diff --git a/apps/app/src/views/SettingsView.appearance-layout.test.tsx b/apps/app/src/views/SettingsView.appearance-layout.test.tsx
new file mode 100644
index 00000000000..51e7ea82c2e
--- /dev/null
+++ b/apps/app/src/views/SettingsView.appearance-layout.test.tsx
@@ -0,0 +1,90 @@
+// @vitest-environment jsdom
+
+import { cleanup, render, screen } from "@testing-library/react";
+import { afterEach, expect, it, vi } from "vitest";
+import { defaultAppTheme } from "@bb/domain";
+import {
+ resetPluginSlotStoreForTest,
+ setPluginSlotRegistrations,
+} from "@/lib/plugin-slots";
+import { makePluginRegistrationSet } from "@/test/fixtures/plugins";
+import { AppearanceSettingsSection } from "./SettingsView";
+
+afterEach(() => {
+ cleanup();
+ window.localStorage.clear();
+ resetPluginSlotStoreForTest();
+});
+
+it("keeps appearance choices first and groups provider controls in Interface", () => {
+ setPluginSlotRegistrations(
+ "fixture",
+ makePluginRegistrationSet({
+ experimentalSidebarHeaders: [
+ { id: "header", title: "Fixture header", component: () => null },
+ ],
+ sourceCodeRenderers: [
+ { id: "source", title: "Fixture source", component: () => null },
+ ],
+ diffRenderers: [
+ { id: "diff", title: "Fixture diff", component: () => null },
+ ],
+ threadLists: [
+ { id: "threads", title: "Fixture threads", component: () => null },
+ ],
+ experimentalSidebarNavigations: [
+ {
+ id: "navigation",
+ title: "Fixture navigation",
+ component: () => null,
+ },
+ ],
+ }),
+ );
+
+ render(
+ ,
+ );
+
+ const labels = (section: HTMLElement) =>
+ Array.from(
+ section.querySelectorAll("[data-control-placement]"),
+ (row) => row.querySelector("p")?.textContent,
+ );
+ const appearance = screen
+ .getByRole("heading", { name: "Appearance" })
+ .closest("section");
+ const interfaceSection = screen
+ .getByRole("heading", { name: "Interface" })
+ .closest("section");
+
+ expect(appearance).not.toBeNull();
+ expect(interfaceSection).not.toBeNull();
+ expect(labels(appearance!)).toEqual([
+ "Theme",
+ "Palette",
+ "Favicon color",
+ "Fade inactive splits",
+ ]);
+ expect(labels(interfaceSection!)).toEqual([
+ "Header",
+ "Source code",
+ "Diffs",
+ "Sidebar",
+ "Navigation",
+ "Sidebar footer",
+ ]);
+});
diff --git a/apps/app/src/views/SettingsView.tsx b/apps/app/src/views/SettingsView.tsx
index 26461d13733..2d274a4ff4e 100644
--- a/apps/app/src/views/SettingsView.tsx
+++ b/apps/app/src/views/SettingsView.tsx
@@ -696,145 +696,154 @@ export function AppearanceSettingsSection({
onAppearanceThemeChange(themeId);
};
return (
-
-
-
-
-
-
-
-
-
-
-
-
- {THEME_PREFERENCE_OPTIONS.map((option) => (
- onThemePreferenceChange(option.value)}
+
+
+
+
+
+
+
-
+
+
+
+ {THEME_PREFERENCE_OPTIONS.map((option) => (
+ onThemePreferenceChange(option.value)}
+ >
+ {option.label}
+
+
+ ))}
+
+
+
-
- {
- if (open) {
- paletteSelectedRef.current = false;
- onAppearanceThemePrefetch([
- ...builtInThemes.map((entry) => entry.id),
- ...customThemes,
- ...pluginThemes.map((theme) => theme.id),
- ]);
- return;
- }
- previewPalette(null);
- }}
+
-
-
-
- {
+ if (open) {
+ paletteSelectedRef.current = false;
+ onAppearanceThemePrefetch([
+ ...builtInThemes.map((entry) => entry.id),
+ ...customThemes,
+ ...pluginThemes.map((theme) => theme.id),
+ ]);
+ return;
+ }
+ previewPalette(null);
+ }}
>
- {builtInThemes.map((entry) => (
-
- {entry.name}
-
- ))}
- {customThemes.map((name) => (
-
- {name}
-
- ))}
- {pluginThemes.map((theme) => (
-
+
- ))}
-
-
-
- Create
-
-
-
-
+
+
+
+
+ {builtInThemes.map((entry) => (
+
+ {entry.name}
+
+ ))}
+ {customThemes.map((name) => (
+
+ {name}
+
+ ))}
+ {pluginThemes.map((theme) => (
+
+ {theme.name}
+
+ ({theme.pluginId})
+
+
+ ))}
+
+
+
+ Create
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
);
}