diff --git a/lib/codex-manager/dashboard-display-panel.ts b/lib/codex-manager/dashboard-display-panel.ts new file mode 100644 index 00000000..9c86fb81 --- /dev/null +++ b/lib/codex-manager/dashboard-display-panel.ts @@ -0,0 +1,258 @@ +import { stdin as input, stdout as output } from "node:process"; +import { + type DashboardAccountSortMode, + type DashboardDisplaySettings, + DEFAULT_DASHBOARD_DISPLAY_SETTINGS, +} from "../dashboard-settings.js"; +import type { UI_COPY } from "../ui/copy.js"; +import { getUiRuntimeOptions } from "../ui/runtime.js"; +import { type MenuItem, select } from "../ui/select.js"; + +export type DashboardDisplaySettingKey = + | "menuShowStatusBadge" + | "menuShowCurrentBadge" + | "menuShowLastUsed" + | "menuShowQuotaSummary" + | "menuShowQuotaCooldown" + | "menuShowDetailsForUnselectedRows" + | "menuShowFetchStatus" + | "menuHighlightCurrentRow" + | "menuSortEnabled" + | "menuSortPinCurrent" + | "menuSortQuickSwitchVisibleRow"; + +export interface DashboardDisplaySettingOption { + key: DashboardDisplaySettingKey; + label: string; + description: string; +} + +export type DashboardConfigAction = + | { type: "toggle"; key: DashboardDisplaySettingKey } + | { type: "cycle-sort-mode" } + | { type: "cycle-layout-mode" } + | { type: "reset" } + | { type: "save" } + | { type: "cancel" }; + +export interface DashboardDisplayPanelDeps { + cloneDashboardSettings: ( + settings: DashboardDisplaySettings, + ) => DashboardDisplaySettings; + buildAccountListPreview: ( + settings: DashboardDisplaySettings, + ui: ReturnType, + focusKey: DashboardDisplaySettingKey | "menuSortMode" | "menuLayoutMode", + ) => { label: string; hint?: string }; + formatDashboardSettingState: (enabled: boolean) => string; + formatMenuSortMode: (mode: DashboardAccountSortMode) => string; + resolveMenuLayoutMode: ( + settings: DashboardDisplaySettings, + ) => "compact-details" | "expanded-rows"; + formatMenuLayoutMode: (mode: "compact-details" | "expanded-rows") => string; + applyDashboardDefaultsForKeys: ( + draft: DashboardDisplaySettings, + keys: readonly (keyof DashboardDisplaySettings)[], + ) => DashboardDisplaySettings; + DASHBOARD_DISPLAY_OPTIONS: readonly DashboardDisplaySettingOption[]; + ACCOUNT_LIST_PANEL_KEYS: readonly (keyof DashboardDisplaySettings)[]; + UI_COPY: typeof UI_COPY; +} + +export async function promptDashboardDisplayPanel( + initial: DashboardDisplaySettings, + deps: DashboardDisplayPanelDeps, +): Promise { + if (!input.isTTY || !output.isTTY) return null; + + const ui = getUiRuntimeOptions(); + let draft = deps.cloneDashboardSettings(initial); + let focusKey: DashboardDisplaySettingKey | "menuSortMode" | "menuLayoutMode" = + deps.DASHBOARD_DISPLAY_OPTIONS[0]?.key ?? "menuShowStatusBadge"; + + while (true) { + const preview = deps.buildAccountListPreview(draft, ui, focusKey); + const optionItems: MenuItem[] = + deps.DASHBOARD_DISPLAY_OPTIONS.map((option, index) => { + const enabled = draft[option.key] ?? true; + return { + label: `${deps.formatDashboardSettingState(enabled)} ${index + 1}. ${option.label}`, + hint: option.description, + value: { type: "toggle", key: option.key }, + color: enabled ? "green" : "yellow", + }; + }); + const sortMode = + draft.menuSortMode ?? + DEFAULT_DASHBOARD_DISPLAY_SETTINGS.menuSortMode ?? + "ready-first"; + const sortModeItem: MenuItem = { + label: `Sort mode: ${deps.formatMenuSortMode(sortMode)}`, + hint: "Applies when smart sort is enabled.", + value: { type: "cycle-sort-mode" }, + color: sortMode === "ready-first" ? "green" : "yellow", + }; + const layoutMode = deps.resolveMenuLayoutMode(draft); + const layoutModeItem: MenuItem = { + label: `Layout: ${deps.formatMenuLayoutMode(layoutMode)}`, + hint: "Compact shows one-line rows with a selected details pane.", + value: { type: "cycle-layout-mode" }, + color: layoutMode === "compact-details" ? "green" : "yellow", + }; + const items: MenuItem[] = [ + { + label: deps.UI_COPY.settings.previewHeading, + value: { type: "cancel" }, + kind: "heading", + }, + { + label: preview.label, + hint: preview.hint, + value: { type: "cancel" }, + color: "green", + disabled: true, + hideUnavailableSuffix: true, + }, + { label: "", value: { type: "cancel" }, separator: true }, + { + label: deps.UI_COPY.settings.displayHeading, + value: { type: "cancel" }, + kind: "heading", + }, + ...optionItems, + sortModeItem, + layoutModeItem, + { label: "", value: { type: "cancel" }, separator: true }, + { + label: deps.UI_COPY.settings.resetDefault, + value: { type: "reset" }, + color: "yellow", + }, + { + label: deps.UI_COPY.settings.saveAndBack, + value: { type: "save" }, + color: "green", + }, + { + label: deps.UI_COPY.settings.backNoSave, + value: { type: "cancel" }, + color: "red", + }, + ]; + + const initialCursor = items.findIndex( + (item) => + (item.value.type === "toggle" && item.value.key === focusKey) || + (item.value.type === "cycle-sort-mode" && + focusKey === "menuSortMode") || + (item.value.type === "cycle-layout-mode" && + focusKey === "menuLayoutMode"), + ); + + const updateFocusedPreview = (cursor: number) => { + const focusedItem = items[cursor]; + const focused = + focusedItem?.value.type === "toggle" + ? focusedItem.value.key + : focusedItem?.value.type === "cycle-sort-mode" + ? "menuSortMode" + : focusedItem?.value.type === "cycle-layout-mode" + ? "menuLayoutMode" + : focusKey; + const nextPreview = deps.buildAccountListPreview(draft, ui, focused); + const previewItem = items[1]; + if (!previewItem) return; + previewItem.label = nextPreview.label; + previewItem.hint = nextPreview.hint; + }; + + const result = await select(items, { + message: deps.UI_COPY.settings.accountListTitle, + subtitle: deps.UI_COPY.settings.accountListSubtitle, + help: deps.UI_COPY.settings.accountListHelp, + clearScreen: true, + theme: ui.theme, + selectedEmphasis: "minimal", + initialCursor: initialCursor >= 0 ? initialCursor : undefined, + onCursorChange: ({ cursor }) => { + const focusedItem = items[cursor]; + if (focusedItem?.value.type === "toggle") + focusKey = focusedItem.value.key; + else if (focusedItem?.value.type === "cycle-sort-mode") + focusKey = "menuSortMode"; + else if (focusedItem?.value.type === "cycle-layout-mode") + focusKey = "menuLayoutMode"; + updateFocusedPreview(cursor); + }, + onInput: (raw) => { + const lower = raw.toLowerCase(); + if (lower === "q") return { type: "cancel" }; + if (lower === "s") return { type: "save" }; + if (lower === "r") return { type: "reset" }; + if (lower === "m") return { type: "cycle-sort-mode" }; + if (lower === "l") return { type: "cycle-layout-mode" }; + const parsed = Number.parseInt(raw, 10); + if ( + Number.isFinite(parsed) && + parsed >= 1 && + parsed <= deps.DASHBOARD_DISPLAY_OPTIONS.length + ) { + const target = deps.DASHBOARD_DISPLAY_OPTIONS[parsed - 1]; + if (target) return { type: "toggle", key: target.key }; + } + if (parsed === deps.DASHBOARD_DISPLAY_OPTIONS.length + 1) + return { type: "cycle-sort-mode" }; + if (parsed === deps.DASHBOARD_DISPLAY_OPTIONS.length + 2) + return { type: "cycle-layout-mode" }; + return undefined; + }, + }); + + if (!result || result.type === "cancel") return null; + if (result.type === "save") return draft; + if (result.type === "reset") { + draft = deps.applyDashboardDefaultsForKeys( + draft, + deps.ACCOUNT_LIST_PANEL_KEYS, + ); + focusKey = deps.DASHBOARD_DISPLAY_OPTIONS[0]?.key ?? focusKey; + continue; + } + if (result.type === "cycle-sort-mode") { + const currentMode = + draft.menuSortMode ?? + DEFAULT_DASHBOARD_DISPLAY_SETTINGS.menuSortMode ?? + "ready-first"; + const nextMode: DashboardAccountSortMode = + currentMode === "ready-first" ? "manual" : "ready-first"; + draft = { + ...draft, + menuSortMode: nextMode, + menuSortEnabled: + nextMode === "ready-first" + ? true + : (draft.menuSortEnabled ?? + DEFAULT_DASHBOARD_DISPLAY_SETTINGS.menuSortEnabled ?? + true), + }; + focusKey = "menuSortMode"; + continue; + } + if (result.type === "cycle-layout-mode") { + const currentLayout = deps.resolveMenuLayoutMode(draft); + const nextLayout = + currentLayout === "compact-details" + ? "expanded-rows" + : "compact-details"; + draft = { + ...draft, + menuLayoutMode: nextLayout, + menuShowDetailsForUnselectedRows: nextLayout === "expanded-rows", + }; + focusKey = "menuLayoutMode"; + continue; + } + focusKey = result.key; + draft = { ...draft, [result.key]: !draft[result.key] }; + } +} diff --git a/lib/codex-manager/settings-hub.ts b/lib/codex-manager/settings-hub.ts index b11b21d7..d815ab8d 100644 --- a/lib/codex-manager/settings-hub.ts +++ b/lib/codex-manager/settings-hub.ts @@ -32,27 +32,13 @@ import { type MenuItem, type SelectOptions, select } from "../ui/select.js"; import { getUnifiedSettingsPath } from "../unified-settings.js"; import { sleep } from "../utils.js"; import { promptBehaviorSettingsPanel } from "./behavior-settings-panel.js"; +import { + promptDashboardDisplayPanel, + type DashboardDisplaySettingKey, + type DashboardDisplaySettingOption, +} from "./dashboard-display-panel.js"; import { promptThemeSettingsPanel } from "./theme-settings-panel.js"; -type DashboardDisplaySettingKey = - | "menuShowStatusBadge" - | "menuShowCurrentBadge" - | "menuShowLastUsed" - | "menuShowQuotaSummary" - | "menuShowQuotaCooldown" - | "menuShowDetailsForUnselectedRows" - | "menuShowFetchStatus" - | "menuHighlightCurrentRow" - | "menuSortEnabled" - | "menuSortPinCurrent" - | "menuSortQuickSwitchVisibleRow"; - -interface DashboardDisplaySettingOption { - key: DashboardDisplaySettingKey; - label: string; - description: string; -} - const DASHBOARD_DISPLAY_OPTIONS: DashboardDisplaySettingOption[] = [ { key: "menuShowStatusBadge", @@ -153,14 +139,6 @@ type PreviewFocusKey = | "menuLayoutMode" | null; -type DashboardConfigAction = - | { type: "toggle"; key: DashboardDisplaySettingKey } - | { type: "cycle-sort-mode" } - | { type: "cycle-layout-mode" } - | { type: "reset" } - | { type: "save" } - | { type: "cancel" }; - type StatuslineConfigAction = | { type: "toggle"; key: DashboardStatuslineField } | { type: "move-up"; key: DashboardStatuslineField } @@ -1320,211 +1298,18 @@ const __testOnly = { async function promptDashboardDisplaySettings( initial: DashboardDisplaySettings, ): Promise { - if (!input.isTTY || !output.isTTY) { - return null; - } - - const ui = getUiRuntimeOptions(); - let draft = cloneDashboardSettings(initial); - let focusKey: DashboardDisplaySettingKey | "menuSortMode" | "menuLayoutMode" = - DASHBOARD_DISPLAY_OPTIONS[0]?.key ?? "menuShowStatusBadge"; - while (true) { - const preview = buildAccountListPreview(draft, ui, focusKey); - const optionItems: MenuItem[] = - DASHBOARD_DISPLAY_OPTIONS.map((option, index) => { - const enabled = draft[option.key] ?? true; - const label = `${formatDashboardSettingState(enabled)} ${index + 1}. ${option.label}`; - const color: MenuItem["color"] = enabled - ? "green" - : "yellow"; - return { - label, - hint: option.description, - value: { type: "toggle", key: option.key } as DashboardConfigAction, - color, - }; - }); - const sortMode = - draft.menuSortMode ?? - DEFAULT_DASHBOARD_DISPLAY_SETTINGS.menuSortMode ?? - "ready-first"; - const sortModeItem: MenuItem = { - label: `Sort mode: ${formatMenuSortMode(sortMode)}`, - hint: "Applies when smart sort is enabled.", - value: { type: "cycle-sort-mode" }, - color: sortMode === "ready-first" ? "green" : "yellow", - }; - const layoutMode = resolveMenuLayoutMode(draft); - const layoutModeItem: MenuItem = { - label: `Layout: ${formatMenuLayoutMode(layoutMode)}`, - hint: "Compact shows one-line rows with a selected details pane.", - value: { type: "cycle-layout-mode" }, - color: layoutMode === "compact-details" ? "green" : "yellow", - }; - const items: MenuItem[] = [ - { - label: UI_COPY.settings.previewHeading, - value: { type: "cancel" }, - kind: "heading", - }, - { - label: preview.label, - hint: preview.hint, - value: { type: "cancel" }, - color: "green", - disabled: true, - hideUnavailableSuffix: true, - }, - { label: "", value: { type: "cancel" }, separator: true }, - { - label: UI_COPY.settings.displayHeading, - value: { type: "cancel" }, - kind: "heading", - }, - ...optionItems, - sortModeItem, - layoutModeItem, - { label: "", value: { type: "cancel" }, separator: true }, - { - label: UI_COPY.settings.resetDefault, - value: { type: "reset" }, - color: "yellow", - }, - { - label: UI_COPY.settings.saveAndBack, - value: { type: "save" }, - color: "green", - }, - { - label: UI_COPY.settings.backNoSave, - value: { type: "cancel" }, - color: "red", - }, - ]; - const initialCursor = items.findIndex( - (item) => - (item.value.type === "toggle" && item.value.key === focusKey) || - (item.value.type === "cycle-sort-mode" && - focusKey === "menuSortMode") || - (item.value.type === "cycle-layout-mode" && - focusKey === "menuLayoutMode"), - ); - - const updateFocusedPreview = (cursor: number) => { - const focusedItem = items[cursor]; - const focusedKey = - focusedItem?.value.type === "toggle" - ? focusedItem.value.key - : focusedItem?.value.type === "cycle-sort-mode" - ? "menuSortMode" - : focusedItem?.value.type === "cycle-layout-mode" - ? "menuLayoutMode" - : focusKey; - const nextPreview = buildAccountListPreview(draft, ui, focusedKey); - const previewItem = items[1]; - if (!previewItem) return; - previewItem.label = nextPreview.label; - previewItem.hint = nextPreview.hint; - }; - - const result = await select(items, { - message: UI_COPY.settings.accountListTitle, - subtitle: UI_COPY.settings.accountListSubtitle, - help: UI_COPY.settings.accountListHelp, - clearScreen: true, - theme: ui.theme, - selectedEmphasis: "minimal", - initialCursor: initialCursor >= 0 ? initialCursor : undefined, - onCursorChange: ({ cursor }) => { - const focusedItem = items[cursor]; - if (focusedItem?.value.type === "toggle") { - focusKey = focusedItem.value.key; - } else if (focusedItem?.value.type === "cycle-sort-mode") { - focusKey = "menuSortMode"; - } else if (focusedItem?.value.type === "cycle-layout-mode") { - focusKey = "menuLayoutMode"; - } - updateFocusedPreview(cursor); - }, - onInput: (raw) => { - const lower = raw.toLowerCase(); - if (lower === "q") return { type: "cancel" }; - if (lower === "s") return { type: "save" }; - if (lower === "r") return { type: "reset" }; - if (lower === "m") return { type: "cycle-sort-mode" }; - if (lower === "l") return { type: "cycle-layout-mode" }; - const parsed = Number.parseInt(raw, 10); - if ( - Number.isFinite(parsed) && - parsed >= 1 && - parsed <= DASHBOARD_DISPLAY_OPTIONS.length - ) { - const target = DASHBOARD_DISPLAY_OPTIONS[parsed - 1]; - if (target) { - return { type: "toggle", key: target.key }; - } - } - if (parsed === DASHBOARD_DISPLAY_OPTIONS.length + 1) { - return { type: "cycle-sort-mode" }; - } - if (parsed === DASHBOARD_DISPLAY_OPTIONS.length + 2) { - return { type: "cycle-layout-mode" }; - } - return undefined; - }, - }); - - if (!result || result.type === "cancel") { - return null; - } - if (result.type === "save") { - return draft; - } - if (result.type === "reset") { - draft = applyDashboardDefaultsForKeys(draft, ACCOUNT_LIST_PANEL_KEYS); - focusKey = DASHBOARD_DISPLAY_OPTIONS[0]?.key ?? focusKey; - continue; - } - if (result.type === "cycle-sort-mode") { - const currentMode = - draft.menuSortMode ?? - DEFAULT_DASHBOARD_DISPLAY_SETTINGS.menuSortMode ?? - "ready-first"; - const nextMode: DashboardAccountSortMode = - currentMode === "ready-first" ? "manual" : "ready-first"; - draft = { - ...draft, - menuSortMode: nextMode, - menuSortEnabled: - nextMode === "ready-first" - ? true - : (draft.menuSortEnabled ?? - DEFAULT_DASHBOARD_DISPLAY_SETTINGS.menuSortEnabled ?? - true), - }; - focusKey = "menuSortMode"; - continue; - } - if (result.type === "cycle-layout-mode") { - const currentLayout = resolveMenuLayoutMode(draft); - const nextLayout = - currentLayout === "compact-details" - ? "expanded-rows" - : "compact-details"; - draft = { - ...draft, - menuLayoutMode: nextLayout, - menuShowDetailsForUnselectedRows: nextLayout === "expanded-rows", - }; - focusKey = "menuLayoutMode"; - continue; - } - focusKey = result.key; - draft = { - ...draft, - [result.key]: !draft[result.key], - }; - } + return promptDashboardDisplayPanel(initial, { + cloneDashboardSettings, + buildAccountListPreview, + formatDashboardSettingState, + formatMenuSortMode, + resolveMenuLayoutMode, + formatMenuLayoutMode, + applyDashboardDefaultsForKeys, + DASHBOARD_DISPLAY_OPTIONS, + ACCOUNT_LIST_PANEL_KEYS, + UI_COPY, + }); } async function configureDashboardDisplaySettings( diff --git a/test/dashboard-display-panel.test.ts b/test/dashboard-display-panel.test.ts new file mode 100644 index 00000000..b890b507 --- /dev/null +++ b/test/dashboard-display-panel.test.ts @@ -0,0 +1,226 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { + type DashboardDisplaySettings, + DEFAULT_DASHBOARD_DISPLAY_SETTINGS, +} from "../lib/dashboard-settings.js"; +import { UI_COPY } from "../lib/ui/copy.js"; +import { + type DashboardDisplayPanelDeps, + promptDashboardDisplayPanel, +} from "../lib/codex-manager/dashboard-display-panel.js"; + +const { selectMock, getUiRuntimeOptionsMock } = vi.hoisted(() => ({ + selectMock: vi.fn(), + getUiRuntimeOptionsMock: vi.fn(() => ({ theme: "test-theme" })), +})); + +vi.mock("../lib/ui/select.js", () => ({ + select: selectMock, +})); + +vi.mock("../lib/ui/runtime.js", () => ({ + getUiRuntimeOptions: getUiRuntimeOptionsMock, +})); + +const stdinIsTTYDescriptor = Object.getOwnPropertyDescriptor( + process.stdin, + "isTTY", +); +const stdoutIsTTYDescriptor = Object.getOwnPropertyDescriptor( + process.stdout, + "isTTY", +); + +const DASHBOARD_DISPLAY_OPTIONS: DashboardDisplayPanelDeps["DASHBOARD_DISPLAY_OPTIONS"] = + [ + { + key: "menuShowStatusBadge", + label: "Show Status Badges", + description: "Show [ok], [active], and similar badges.", + }, + { + key: "menuShowCurrentBadge", + label: "Show [current]", + description: "Mark the account active in Codex.", + }, + { + key: "menuShowFetchStatus", + label: "Show Fetch Status", + description: "Show background limit refresh status in the menu subtitle.", + }, + ]; + +const ACCOUNT_LIST_PANEL_KEYS: DashboardDisplayPanelDeps["ACCOUNT_LIST_PANEL_KEYS"] = [ + "menuShowStatusBadge", + "menuShowCurrentBadge", + "menuShowFetchStatus", + "menuSortMode", + "menuSortEnabled", + "menuLayoutMode", + "menuShowDetailsForUnselectedRows", +]; + +function setInteractiveTTY(enabled: boolean): void { + Object.defineProperty(process.stdin, "isTTY", { + value: enabled, + configurable: true, + }); + Object.defineProperty(process.stdout, "isTTY", { + value: enabled, + configurable: true, + }); +} + +function restoreTTYDescriptors(): void { + if (stdinIsTTYDescriptor) { + Object.defineProperty(process.stdin, "isTTY", stdinIsTTYDescriptor); + } else { + delete (process.stdin as unknown as { isTTY?: boolean }).isTTY; + } + + if (stdoutIsTTYDescriptor) { + Object.defineProperty(process.stdout, "isTTY", stdoutIsTTYDescriptor); + } else { + delete (process.stdout as unknown as { isTTY?: boolean }).isTTY; + } +} + +function createSettings( + overrides: Partial = {}, +): DashboardDisplaySettings { + return { + ...DEFAULT_DASHBOARD_DISPLAY_SETTINGS, + ...overrides, + }; +} + +function buildDeps(): DashboardDisplayPanelDeps { + return { + cloneDashboardSettings: (settings) => ({ ...settings }), + buildAccountListPreview: (_settings, _ui, focusKey) => ({ + label: `Preview: ${focusKey}`, + hint: "Preview hint", + }), + formatDashboardSettingState: (enabled) => (enabled ? "[x]" : "[ ]"), + formatMenuSortMode: (mode) => + mode === "ready-first" ? "Ready-First" : "Manual", + resolveMenuLayoutMode: (settings) => { + if (settings.menuLayoutMode === "expanded-rows") { + return "expanded-rows"; + } + if (settings.menuLayoutMode === "compact-details") { + return "compact-details"; + } + return settings.menuShowDetailsForUnselectedRows === true + ? "expanded-rows" + : "compact-details"; + }, + formatMenuLayoutMode: (mode) => + mode === "expanded-rows" + ? "Expanded Rows" + : "Compact + Details Pane", + applyDashboardDefaultsForKeys: (draft, keys) => { + const next = { ...draft }; + for (const key of keys) { + next[key] = DEFAULT_DASHBOARD_DISPLAY_SETTINGS[key]; + } + return next; + }, + DASHBOARD_DISPLAY_OPTIONS, + ACCOUNT_LIST_PANEL_KEYS, + UI_COPY, + }; +} + +describe("promptDashboardDisplayPanel", () => { + beforeEach(() => { + selectMock.mockReset(); + getUiRuntimeOptionsMock.mockClear(); + setInteractiveTTY(true); + }); + + afterEach(() => { + restoreTTYDescriptors(); + }); + + it("returns null without TTY access", async () => { + setInteractiveTTY(false); + + const result = await promptDashboardDisplayPanel(createSettings(), buildDeps()); + + expect(result).toBeNull(); + expect(selectMock).not.toHaveBeenCalled(); + }); + + it("toggles a display flag and saves the draft", async () => { + selectMock + .mockResolvedValueOnce({ + type: "toggle", + key: "menuShowStatusBadge", + }) + .mockResolvedValueOnce({ type: "save" }); + + const result = await promptDashboardDisplayPanel(createSettings(), buildDeps()); + + expect(result?.menuShowStatusBadge).toBe(false); + expect(selectMock).toHaveBeenCalledTimes(2); + }); + + it("resets changed values back to dashboard defaults", async () => { + selectMock + .mockResolvedValueOnce({ + type: "toggle", + key: "menuShowStatusBadge", + }) + .mockResolvedValueOnce({ type: "reset" }) + .mockResolvedValueOnce({ type: "save" }); + + const result = await promptDashboardDisplayPanel(createSettings(), buildDeps()); + + expect(result?.menuShowStatusBadge).toBe( + DEFAULT_DASHBOARD_DISPLAY_SETTINGS.menuShowStatusBadge, + ); + }); + + it("cycles the sort mode and re-enables smart sort when switching to ready-first", async () => { + selectMock + .mockResolvedValueOnce({ type: "cycle-sort-mode" }) + .mockResolvedValueOnce({ type: "save" }); + + const result = await promptDashboardDisplayPanel( + createSettings({ + menuSortMode: "manual", + menuSortEnabled: false, + }), + buildDeps(), + ); + + expect(result?.menuSortMode).toBe("ready-first"); + expect(result?.menuSortEnabled).toBe(true); + }); + + it("cycles the layout mode and syncs the details-pane flag", async () => { + selectMock + .mockResolvedValueOnce({ type: "cycle-layout-mode" }) + .mockResolvedValueOnce({ type: "save" }); + + const result = await promptDashboardDisplayPanel( + createSettings({ + menuLayoutMode: "compact-details", + menuShowDetailsForUnselectedRows: false, + }), + buildDeps(), + ); + + expect(result?.menuLayoutMode).toBe("expanded-rows"); + expect(result?.menuShowDetailsForUnselectedRows).toBe(true); + }); + + it("returns null when the panel is cancelled", async () => { + selectMock.mockResolvedValueOnce({ type: "cancel" }); + + const result = await promptDashboardDisplayPanel(createSettings(), buildDeps()); + + expect(result).toBeNull(); + }); +});