diff --git a/src/features/chat/chat-state.ts b/src/features/chat/chat-state.ts index f288a84c..15a77a11 100644 --- a/src/features/chat/chat-state.ts +++ b/src/features/chat/chat-state.ts @@ -78,7 +78,6 @@ export interface ChatState { historyCursor: string | null; loadingHistory: boolean; composerDraft: string; - runtimePicker: "model" | "effort" | null; availableModels: readonly Model[]; availableSkills: readonly SkillMetadata[]; reportedLogs: ReadonlySet; @@ -159,7 +158,7 @@ export type ChatAction = } | { type: "ui/panel-set"; - panel: "history" | "status-panel" | "model" | "effort" | null; + panel: "history" | "status-panel" | null; toggle?: boolean; } | { type: "ui/messages-pinned-set"; pinned: boolean } @@ -562,7 +561,6 @@ export function clearConnectionScopedState(state: ChatState): ChatState { availableModels: [], availableSkills: [], appServerDiagnostics: createAppServerDiagnostics(), - runtimePicker: null, }); } @@ -635,9 +633,8 @@ function initialComposerState(): Pick< }; } -function initialPanelUiState(): Pick { +function initialPanelUiState(): Pick { return { - runtimePicker: null, openDetails: new Set(), }; } @@ -723,19 +720,14 @@ function updatedTurnDiffs(turnDiffs: ReadonlyMap, turnId: string return next; } -function setPanelState(state: ChatState, panel: "history" | "status-panel" | "model" | "effort" | null, toggle: boolean): ChatState { - const currentPanel = state.openDetails.has("history") - ? "history" - : state.openDetails.has("status-panel") - ? "status-panel" - : state.runtimePicker; +function setPanelState(state: ChatState, panel: "history" | "status-panel" | null, toggle: boolean): ChatState { + const currentPanel = state.openDetails.has("history") ? "history" : state.openDetails.has("status-panel") ? "status-panel" : null; const nextPanel = toggle && currentPanel === panel ? null : panel; return patchChatState(state, { openDetails: nextPanel === "history" || nextPanel === "status-panel" ? new Set([nextPanel]) : new Set([...state.openDetails].filter((key) => key !== "history" && key !== "status-panel")), - runtimePicker: nextPanel === "model" || nextPanel === "effort" ? nextPanel : null, }); } diff --git a/src/features/chat/toolbar-model.ts b/src/features/chat/toolbar-model.ts index 3db5ba1e..2416d67a 100644 --- a/src/features/chat/toolbar-model.ts +++ b/src/features/chat/toolbar-model.ts @@ -1,14 +1,6 @@ import type { EffectiveConfigSection, RateLimitSummary } from "../../runtime/view"; -export type ToolbarPanelKind = "history" | "status" | "runtime"; - -export interface ToolbarChoice { - label: string; - selected?: boolean; - disabled?: boolean; - meta?: string; - onClick: () => void; -} +export type ToolbarPanelKind = "history" | "status"; export interface ToolbarThreadRow { title: string; @@ -38,16 +30,10 @@ export interface ToolbarViewModel { newChatDisabled: boolean; historyOpen: boolean; statusPanelOpen: boolean; - runtimeOpen: boolean; - planActive: boolean; - autoReviewActive: boolean; - fastActive: boolean; rateLimit: RateLimitSummary | null; configSections: EffectiveConfigSection[]; openPanel: ToolbarPanelKind | null; threads: ToolbarThreadRow[]; - modelChoices: ToolbarChoice[]; - effortChoices: ToolbarChoice[]; connectLabel: string; diagnostics: ToolbarDiagnosticSection[]; } diff --git a/src/features/chat/toolbar-panel-controller.ts b/src/features/chat/toolbar-panel-controller.ts index f5df2d98..71881625 100644 --- a/src/features/chat/toolbar-panel-controller.ts +++ b/src/features/chat/toolbar-panel-controller.ts @@ -44,11 +44,6 @@ export class ToolbarPanelController { this.host.scheduleRender(); } - toggleRuntime(picker: NonNullable): void { - this.dispatch({ type: "ui/panel-set", panel: picker, toggle: true }); - this.host.scheduleRender(); - } - closeForThreadSelection(): void { this.archiveConfirmThreadId = null; } @@ -90,7 +85,7 @@ export class ToolbarPanelController { } private hasOpenPanel(): boolean { - return this.state.openDetails.has("history") || this.state.openDetails.has("status-panel") || this.state.runtimePicker !== null; + return this.state.openDetails.has("history") || this.state.openDetails.has("status-panel"); } private close(): void { diff --git a/src/features/chat/ui/composer.tsx b/src/features/chat/ui/composer.tsx index 08d6185f..f70a5fb2 100644 --- a/src/features/chat/ui/composer.tsx +++ b/src/features/chat/ui/composer.tsx @@ -3,8 +3,7 @@ import type { ButtonHTMLAttributes, ComponentChild as UiNode, Ref } from "preact import { useLayoutEffect, useRef, useState } from "preact/hooks"; import type { ComposerSuggestion } from "../composer/suggestions"; -import type { ComposerMetaViewModel } from "../view-model"; -import type { ToolbarChoice } from "../toolbar-model"; +import type { ComposerMetaViewModel, RuntimeChoice } from "../view-model"; import { IconButton } from "../../../shared/ui/components"; import { renderUiRoot } from "../../../shared/ui/ui-root"; import { syncTextareaHeight } from "../../../shared/ui/textarea-autogrow"; @@ -421,7 +420,7 @@ function ComposerMetaChoicePopover({ onClose, }: { kind: ComposerMetaPickerKind; - choices: ToolbarChoice[]; + choices: RuntimeChoice[]; left: number; onClose: () => void; }): UiNode { @@ -437,7 +436,7 @@ function ComposerMetaChoicePopover({ ); } -function ComposerMetaChoice({ choice, onClose }: { choice: ToolbarChoice; onClose: () => void }): UiNode { +function ComposerMetaChoice({ choice, onClose }: { choice: RuntimeChoice; onClose: () => void }): UiNode { const onSelect = () => { if (choice.disabled) return; choice.onClick(); diff --git a/src/features/chat/ui/toolbar.tsx b/src/features/chat/ui/toolbar.tsx index e4bd6de8..0d9ccbf4 100644 --- a/src/features/chat/ui/toolbar.tsx +++ b/src/features/chat/ui/toolbar.tsx @@ -13,11 +13,7 @@ type ButtonProps = ButtonHTMLAttributes & { export interface ToolbarActions { startNewThread: () => void; toggleHistory: () => void; - toggleAutoReview: () => void; toggleStatusPanel: () => void; - togglePlan: () => void; - toggleFast: () => void; - toggleRuntime: () => void; connect: () => void; refreshStatus: () => void; resumeThread: (threadId: string) => void; @@ -53,7 +49,6 @@ function Toolbar({ model, actions }: { model: ToolbarViewModel; actions: Toolbar disabled={model.newChatDisabled} onClick={actions.startNewThread} /> - @@ -82,60 +77,6 @@ function ToolbarIconButton({ ); } -function RuntimeButtons({ model, actions }: { model: ToolbarViewModel; actions: ToolbarActions }): UiNode { - return ( - <> - - - - - - ); -} - -function RuntimeIcon({ - icon, - label, - className, - active, - onClick, -}: { - icon: string; - label: string; - className?: string; - active: boolean; - onClick: () => void; -}): UiNode { - return ( - - ); -} - function StatusButton({ model, actions }: { model: ToolbarViewModel; actions: ToolbarActions }): UiNode { return ( {model.openPanel === "history" ? : null} - {model.openPanel === "runtime" ? : null} {model.openPanel === "status" ? : null} ); @@ -267,39 +207,6 @@ function FragmentedConfigSection({ section }: { section: EffectiveConfigSection ); } -function RuntimePicker({ model }: { model: ToolbarViewModel }): UiNode { - return ( -
-
Reasoning effort
- {model.effortChoices.map((choice) => ( - - ))} -
Model
- {model.modelChoices.map((choice) => ( - - ))} -
- ); -} - function ThreadList({ threads, actions }: { threads: ToolbarThreadRow[]; actions: ToolbarActions }): UiNode { if (threads.length === 0) { return ( diff --git a/src/features/chat/view-model.ts b/src/features/chat/view-model.ts index 8ad811d7..3c6154cc 100644 --- a/src/features/chat/view-model.ts +++ b/src/features/chat/view-model.ts @@ -18,7 +18,7 @@ import { codexPanelDisplayTitle, explicitThreadName, getThreadTitle } from "../. import { connectionDiagnosticSections } from "./diagnostics"; import type { ChatState } from "./chat-state"; import { statusValue, usageLimitStatusLines } from "./status-lines"; -import type { ToolbarChoice, ToolbarThreadRow, ToolbarViewModel } from "./toolbar-model"; +import type { ToolbarThreadRow, ToolbarViewModel } from "./toolbar-model"; export interface RuntimeSnapshotInput { state: ChatState; @@ -33,8 +33,16 @@ export interface ComposerMetaViewModel { planActive: boolean; autoReviewActive: boolean; fastActive: boolean; - modelChoices?: ToolbarChoice[]; - effortChoices?: ToolbarChoice[]; + modelChoices?: RuntimeChoice[]; + effortChoices?: RuntimeChoice[]; +} + +export interface RuntimeChoice { + label: string; + selected?: boolean; + disabled?: boolean; + meta?: string; + onClick: () => void; } export interface ComposerContextMeterCellViewModel { @@ -56,8 +64,6 @@ export interface ToolbarViewModelInput { configuredCommand: string; archiveConfirmThreadId: string | null; archiveExportEnabled: boolean; - modelChoices: ToolbarChoice[]; - effortChoices: ToolbarChoice[]; renameState: (threadId: string) => ToolbarThreadRow["rename"]; } @@ -67,7 +73,7 @@ export interface ConnectionDiagnosticsModelInput { configuredCommand: string; } -export interface RuntimeToolbarChoicesInput { +export interface RuntimeComposerChoicesInput { state: ChatState; snapshot: RuntimeSnapshot; setRequestedModel: (model: string | null) => void; @@ -103,11 +109,14 @@ export function runtimeSnapshotForChatState({ state }: RuntimeSnapshotInput): Ru }; } -export function runtimeToolbarChoices(input: RuntimeToolbarChoicesInput): Pick { +export function runtimeComposerChoices(input: RuntimeComposerChoicesInput): { + modelChoices: RuntimeChoice[]; + effortChoices: RuntimeChoice[]; +} { const config = readRuntimeConfig(input.state.effectiveConfig); const activeModel = currentModel(input.snapshot, config); const models = sortedAvailableModels(input.state.availableModels); - const modelChoices: ToolbarChoice[] = models.slice(0, 12).map((model) => ({ + const modelChoices: RuntimeChoice[] = models.slice(0, 12).map((model) => ({ label: model.model, selected: activeModel === model.model, onClick: () => { @@ -123,7 +132,7 @@ export function runtimeToolbarChoices(input: RuntimeToolbarChoicesInput): Pick ({ + const effortChoices: RuntimeChoice[] = supportedReasoningEfforts(input.snapshot).map((effort) => ({ label: effort, selected: activeEffort === effort, onClick: () => { @@ -225,22 +234,16 @@ function onOffLabel(active: boolean): string { export function toolbarViewModel(input: ToolbarViewModelInput): ToolbarViewModel { const { state, snapshot } = input; - const config = readRuntimeConfig(state.effectiveConfig); const limit = rateLimitSummary(snapshot); const historyOpen = state.openDetails.has("history"); const statusPanelOpen = state.openDetails.has("status-panel"); - const runtimeOpen = state.runtimePicker !== null; return { newChatDisabled: input.turnBusy, historyOpen, statusPanelOpen, - runtimeOpen, - planActive: state.selectedCollaborationMode === "plan", - autoReviewActive: autoReviewActive(snapshot, config), - fastActive: fastModeActive(snapshot, config), rateLimit: limit, configSections: effectiveConfigSections(snapshot, input.vaultPath), - openPanel: historyOpen ? "history" : runtimeOpen ? "runtime" : statusPanelOpen ? "status" : null, + openPanel: historyOpen ? "history" : statusPanelOpen ? "status" : null, threads: toolbarThreadRows({ threads: state.listedThreads, activeThreadId: state.activeThreadId, @@ -249,8 +252,6 @@ export function toolbarViewModel(input: ToolbarViewModelInput): ToolbarViewModel archiveExportEnabled: input.archiveExportEnabled, renameState: input.renameState, }), - modelChoices: input.modelChoices, - effortChoices: input.effortChoices, connectLabel: input.connected ? "Reconnect" : "Connect", diagnostics: connectionDiagnosticsModel({ state, diff --git a/src/features/chat/view-snapshot.ts b/src/features/chat/view-snapshot.ts index d2d56ab4..94335ca4 100644 --- a/src/features/chat/view-snapshot.ts +++ b/src/features/chat/view-snapshot.ts @@ -38,7 +38,6 @@ export function toolbarSlotSnapshot(state: ChatState, connected: boolean): ChatP state.requestedApprovalsReviewer, state.requestedModel, state.requestedReasoningEffort, - state.runtimePicker, openDetailsSignature(state.openDetails), state.threadsLoaded, threadListSignature(state.listedThreads), diff --git a/src/features/chat/view.ts b/src/features/chat/view.ts index 681de71f..96f05e76 100644 --- a/src/features/chat/view.ts +++ b/src/features/chat/view.ts @@ -26,7 +26,7 @@ import { composerPlaceholder as buildComposerPlaceholder, effortStatusLines as buildEffortStatusLines, modelStatusLines as buildModelStatusLines, - runtimeToolbarChoices, + runtimeComposerChoices, runtimeSnapshotForChatState, statusSummaryLines as buildStatusSummaryLines, toolbarViewModel as buildToolbarViewModel, @@ -558,7 +558,7 @@ export class CodexChatView extends ItemView { private composerMetaViewModel() { return { ...buildComposerMetaViewModel(this.state, this.runtimeSnapshot()), - ...runtimeToolbarChoices({ + ...runtimeComposerChoices({ state: this.state, snapshot: this.runtimeSnapshot(), setRequestedModel: (model) => void this.setRequestedModelFromUi(model), @@ -587,15 +587,9 @@ export class CodexChatView extends ItemView { toggleHistory: () => { this.toolbarPanels.toggleHistory(); }, - toggleAutoReview: () => void this.runtimeSettings.toggleAutoReview(), toggleStatusPanel: () => { this.toolbarPanels.toggleStatus(); }, - togglePlan: () => void this.runtimeSettings.toggleCollaborationMode(), - toggleFast: () => void this.runtimeSettings.toggleFastMode(), - toggleRuntime: () => { - this.toolbarPanels.toggleRuntime("model"); - }, connect: () => void this.reconnectActions.reconnectFromToolbar(), refreshStatus: () => void this.refreshStatusPanel(), resumeThread: (threadId) => void this.threadSelection.selectThreadFromToolbar(threadId), @@ -627,12 +621,6 @@ export class CodexChatView extends ItemView { configuredCommand: this.plugin.settings.codexPath, archiveConfirmThreadId: this.toolbarPanels.archiveConfirmId(), archiveExportEnabled: this.plugin.settings.archiveExportEnabled, - ...runtimeToolbarChoices({ - state: this.state, - snapshot: this.runtimeSnapshot(), - setRequestedModel: (model) => void this.setRequestedModelFromUi(model), - setRequestedReasoningEffort: (effort) => void this.setRequestedReasoningEffortFromUi(effort), - }), renameState: (threadId) => this.threadRename.editState(threadId), }); } diff --git a/src/styles/20-chat-toolbar.css b/src/styles/20-chat-toolbar.css index 7af9d824..f9718f53 100644 --- a/src/styles/20-chat-toolbar.css +++ b/src/styles/20-chat-toolbar.css @@ -16,16 +16,6 @@ white-space: nowrap; } -.codex-panel__runtime-icon { - flex: 0 0 auto; -} - -.codex-panel__runtime-picker { - display: flex; - flex-direction: column; - gap: var(--nav-item-margin-bottom, var(--codex-panel-panel-gap)); -} - .codex-panel__toolbar-panel-item { display: flex; align-items: center; @@ -76,10 +66,3 @@ box-shadow: none; outline: none; } - -.codex-panel__runtime-picker-label { - padding: var(--size-4-1) var(--size-4-2) var(--size-2-1); - color: var(--text-faint); - font-size: var(--codex-panel-toolbar-meta-size); - font-weight: var(--font-medium); -} diff --git a/tests/features/chat/chat-state-reducer.test.ts b/tests/features/chat/chat-state-reducer.test.ts index 5beb0d7c..63c0ff1e 100644 --- a/tests/features/chat/chat-state-reducer.test.ts +++ b/tests/features/chat/chat-state-reducer.test.ts @@ -290,13 +290,11 @@ describe("chatReducer", () => { let state = createChatState(); state = chatReducer(state, { type: "ui/panel-set", panel: "history" }); - state = chatReducer(state, { type: "ui/panel-set", panel: "model" }); - expect(state.openDetails.size).toBe(0); - expect(state.runtimePicker).toBe("model"); + expect(state.openDetails.has("history")).toBe(true); state = chatReducer(state, { type: "ui/panel-set", panel: "status-panel" }); + expect(state.openDetails.has("history")).toBe(false); expect(state.openDetails.has("status-panel")).toBe(true); - expect(state.runtimePicker).toBeNull(); }); it("updates remembered details and user input drafts through typed UI actions", () => { diff --git a/tests/features/chat/controllers/connection/connection-controller.test.ts b/tests/features/chat/controllers/connection/connection-controller.test.ts index 89379ef2..cdbcfa7a 100644 --- a/tests/features/chat/controllers/connection/connection-controller.test.ts +++ b/tests/features/chat/controllers/connection/connection-controller.test.ts @@ -116,7 +116,6 @@ describe("ChatConnectionController", () => { threadsLoaded: false, availableModels: [], availableSkills: [], - runtimePicker: null, }); }); diff --git a/tests/features/chat/controllers/submission/plan-implementation-controller.test.ts b/tests/features/chat/controllers/submission/plan-implementation-controller.test.ts index b07045c6..4973fce6 100644 --- a/tests/features/chat/controllers/submission/plan-implementation-controller.test.ts +++ b/tests/features/chat/controllers/submission/plan-implementation-controller.test.ts @@ -70,13 +70,13 @@ describe("PlanImplementationController", () => { const { controller, ensureConnected, sendTurnText, stateStore } = createController(); const plan = planItem("plan"); resumeThread(stateStore, [plan]); - stateStore.dispatch({ type: "ui/panel-set", panel: "model" }); + stateStore.dispatch({ type: "ui/panel-set", panel: "status-panel" }); await controller.implement(plan); expect(ensureConnected).toHaveBeenCalledOnce(); expect(stateStore.getState().selectedCollaborationMode).toBe("default"); - expect(stateStore.getState().runtimePicker).toBeNull(); + expect(stateStore.getState().openDetails.has("status-panel")).toBe(false); expect(sendTurnText).toHaveBeenCalledWith("Please implement this plan."); }); diff --git a/tests/features/chat/ui/renderers/toolbar.test.ts b/tests/features/chat/ui/renderers/toolbar.test.ts index 95f38729..9f3ec944 100644 --- a/tests/features/chat/ui/renderers/toolbar.test.ts +++ b/tests/features/chat/ui/renderers/toolbar.test.ts @@ -18,10 +18,9 @@ describe("toolbar renderer decisions", () => { const parent = document.createElement("div"); const startNewThread = vi.fn(); const toggleHistory = vi.fn(); - const toggleAutoReview = vi.fn(); const baseModel = toolbarModel(); - renderToolbar(parent, baseModel, toolbarActions({ startNewThread, toggleHistory, toggleAutoReview })); + renderToolbar(parent, baseModel, toolbarActions({ startNewThread, toggleHistory })); const navHeader = parent.querySelector(".codex-panel__toolbar-primary"); expect(navHeader?.classList.contains("nav-header")).toBe(true); @@ -32,12 +31,11 @@ describe("toolbar renderer decisions", () => { expect([...expectPresent(navButtons).children].map((button) => button.getAttribute("aria-label"))).toEqual([ "Show thread list", "Start new chat", - "Toggle plan mode", - "Toggle auto-review", - "Toggle fast mode", - "Change model and reasoning effort", "Show panel menu", ]); + expect(parent.querySelector(".codex-panel__plan-toggle")).toBeNull(); + expect(parent.querySelector(".codex-panel__auto-review-toggle")).toBeNull(); + expect(parent.querySelector(".codex-panel__runtime-model")).toBeNull(); const newChatButton = parent.querySelector(".codex-panel__new-chat"); expect(newChatButton?.getAttribute("aria-label")).toBe("Start new chat"); expect(newChatButton?.dataset["icon"]).toBe("messages-square"); @@ -59,27 +57,6 @@ describe("toolbar renderer decisions", () => { expect(historyButton?.classList.contains("clickable-icon")).toBe(true); historyButton?.click(); expect(toggleHistory).toHaveBeenCalled(); - const planButton = parent.querySelector(".codex-panel__plan-toggle"); - expect(planButton?.getAttribute("aria-label")).toBe("Toggle plan mode"); - expect(planButton?.dataset["icon"]).toBe("list-todo"); - expect(planButton?.getAttribute("aria-pressed")).toBe("false"); - expect(planButton?.classList.contains("codex-panel__runtime-icon")).toBe(true); - const autoReviewButton = parent.querySelector(".codex-panel__auto-review-toggle"); - expect(autoReviewButton?.getAttribute("aria-label")).toBe("Toggle auto-review"); - expect(autoReviewButton?.getAttribute("aria-pressed")).toBe("false"); - expect(autoReviewButton?.classList.contains("codex-panel__runtime-icon")).toBe(true); - expect(autoReviewButton?.classList.contains("nav-action-button")).toBe(true); - expect(autoReviewButton?.classList.contains("codex-panel-ui__toolbar-action")).toBe(true); - expect(autoReviewButton?.classList.contains("codex-panel-ui__toolbar-control")).toBe(false); - expect(autoReviewButton?.classList.contains("clickable-icon")).toBe(true); - autoReviewButton?.click(); - expect(toggleAutoReview).toHaveBeenCalled(); - - parent.empty(); - renderToolbar(parent, toolbarModel({ autoReviewActive: true }), toolbarActions()); - expect(parent.querySelector(".codex-panel__status-menu-toggle")?.getAttribute("aria-label")).toBe("Show panel menu"); - expect(parent.querySelector(".codex-panel__auto-review-toggle")?.getAttribute("aria-pressed")).toBe("true"); - parent.empty(); renderToolbar(parent, toolbarModel({ newChatDisabled: true }), toolbarActions()); expect(parent.querySelector(".codex-panel__new-chat")?.disabled).toBe(true); @@ -90,36 +67,6 @@ describe("toolbar renderer decisions", () => { expect(parent.querySelector(".codex-panel__history-toggle")?.classList.contains("is-active")).toBe(true); expect(parent.querySelector(".codex-panel__status-menu-toggle")?.getAttribute("aria-label")).toBe("Hide panel menu"); expect(parent.querySelector(".codex-panel__status-menu-toggle")?.classList.contains("is-active")).toBe(true); - expect(parent.querySelector(".codex-panel__runtime-model")?.getAttribute("aria-label")).toBe("Change model and reasoning effort"); - expect(parent.querySelector(".codex-panel__runtime-model")?.dataset["icon"]).toBe("bot"); - expect(parent.querySelector(".codex-panel__runtime-model")?.classList.contains("is-active")).toBe(true); - expect(parent.querySelector(".codex-panel__runtime-model")?.classList.contains("nav-action-button")).toBe(true); - }); - - it("keeps frequently changed effort choices first inside the runtime menu", () => { - const parent = document.createElement("div"); - - renderToolbar( - parent, - toolbarModel({ - modelChoices: [{ label: "gpt-5.5", selected: true, onClick: vi.fn() }], - effortChoices: [{ label: "high", selected: true, onClick: vi.fn() }], - }), - toolbarActions(), - ); - - expect([...parent.querySelectorAll(".codex-panel__runtime-picker-label")].map((label) => label.textContent)).toEqual([ - "Reasoning effort", - "Model", - ]); - expect([...parent.querySelectorAll(".codex-panel__runtime-choice")].map((choice) => choice.textContent)).toEqual(["high", "gpt-5.5"]); - for (const choice of parent.querySelectorAll(".codex-panel__runtime-choice")) { - expect(choice.getAttribute("role")).toBe("option"); - expect(choice.getAttribute("aria-selected")).toBe("true"); - expect(choice.querySelector(".codex-panel__toolbar-panel-check")).toBeNull(); - expect(choice.classList.contains("selected")).toBe(false); - expect(choice.classList.contains("is-selected")).toBe(true); - } }); it("keeps context out of the toolbar and Codex limits inside the status menu", () => { @@ -381,16 +328,10 @@ function toolbarModel(overrides: Partial = {}): ToolbarViewMod newChatDisabled: false, historyOpen: false, statusPanelOpen: false, - runtimeOpen: true, - planActive: false, - autoReviewActive: false, - fastActive: false, rateLimit: null, configSections: [], - openPanel: "runtime", + openPanel: null, threads: [{ title: "Thread", threadId: "thread", selected: true, disabled: false, canArchive: true, rename: null }], - modelChoices: [{ label: "Default", selected: true, onClick: vi.fn() }], - effortChoices: [{ label: "Default", selected: true, onClick: vi.fn() }], connectLabel: "Reconnect", diagnostics: [{ title: "Process", rows: [{ label: "Codex App Server", value: "codex-cli/test" }] }], ...overrides, @@ -401,11 +342,7 @@ function toolbarActions(overrides: Partial[2]> return { toggleHistory: vi.fn(), startNewThread: vi.fn(), - toggleAutoReview: vi.fn(), toggleStatusPanel: vi.fn(), - togglePlan: vi.fn(), - toggleFast: vi.fn(), - toggleRuntime: vi.fn(), connect: vi.fn(), refreshStatus: vi.fn(), resumeThread: vi.fn(), diff --git a/tests/features/chat/ui/shell.test.tsx b/tests/features/chat/ui/shell.test.tsx index e3e2b03d..01d8d229 100644 --- a/tests/features/chat/ui/shell.test.tsx +++ b/tests/features/chat/ui/shell.test.tsx @@ -70,7 +70,7 @@ describe("ChatPanelShell", () => { await act(async () => { store.dispatch({ type: "status/set", status: "Working" }); - store.dispatch({ type: "ui/panel-set", panel: "model" }); + store.dispatch({ type: "ui/panel-set", panel: "status-panel" }); store.dispatch({ type: "system/message-added", item: { id: "system-1", kind: "system", role: "system", text: "Model set." } }); await settleShellEffects(); }); diff --git a/tests/features/chat/view-model.test.ts b/tests/features/chat/view-model.test.ts index f6ec61b0..71f58cae 100644 --- a/tests/features/chat/view-model.test.ts +++ b/tests/features/chat/view-model.test.ts @@ -9,7 +9,7 @@ import { composerMetaViewModel, composerPlaceholder, effortStatusLines, - runtimeToolbarChoices, + runtimeComposerChoices, modelStatusLines, runtimeSnapshotForChatState, statusSummaryLines, @@ -38,8 +38,6 @@ describe("chat view model", () => { configuredCommand: "codex", archiveConfirmThreadId: "thread-2", archiveExportEnabled: true, - modelChoices: [{ label: "gpt-5.5", selected: true, onClick: () => undefined }], - effortChoices: [{ label: "high", selected: true, onClick: () => undefined }], renameState: (threadId) => (threadId === "thread-1" ? { draft: "Active", generating: false } : null), }); @@ -49,32 +47,6 @@ describe("chat view model", () => { { threadId: "thread-1", title: "Active", selected: true, disabled: false, rename: { draft: "Active" } }, { threadId: "thread-2", title: "Other", selected: false, disabled: true, archiveConfirm: { active: true } }, ]); - expect(model.modelChoices).toHaveLength(1); - }); - - it("marks fast active for the catalog Fast service tier id", () => { - const state = createChatState(); - state.activeModel = "gpt-5.5"; - state.activeServiceTier = "priority"; - state.effectiveConfig = effectiveConfigFixture({ model: "gpt-5.5", model_reasoning_effort: "high" }); - state.availableModels = [modelFixture("gpt-5.5", "priority")]; - - const model = toolbarViewModel({ - state, - snapshot: runtimeSnapshotForChatState({ state }), - connected: true, - turnBusy: false, - vaultPath: "/vault", - configuredCommand: "codex", - archiveConfirmThreadId: null, - archiveExportEnabled: true, - modelChoices: [], - effortChoices: [], - renameState: () => null, - }); - - expect(model.fastActive).toBe(true); - expect(model.newChatDisabled).toBe(false); }); it("builds composer meta from context and runtime state", () => { @@ -211,14 +183,14 @@ describe("chat view model", () => { expect(effortStatusLines(state, snapshot)).toContain("Supported: high"); }); - it("builds runtime toolbar choices from immutable chat state snapshots", () => { + it("builds runtime composer choices from immutable chat state snapshots", () => { const state = createChatState(); state.effectiveConfig = effectiveConfigFixture({ model: "gpt-5.5", model_reasoning_effort: "high" }); state.availableModels = [modelFixture("gpt-5.5"), modelFixture("gpt-5-mini")]; const selectedModels: (string | null)[] = []; const selectedEfforts: string[] = []; - const choices = runtimeToolbarChoices({ + const choices = runtimeComposerChoices({ state, snapshot: runtimeSnapshotForChatState({ state }), setRequestedModel: (model) => { diff --git a/tests/styles.test.ts b/tests/styles.test.ts index 13e13de8..77d4be7b 100644 --- a/tests/styles.test.ts +++ b/tests/styles.test.ts @@ -98,11 +98,9 @@ describe("chat toolbar CSS", () => { it("keeps chat thread row actions inset from the row edge", () => { const threadList = /\.codex-panel__threads \{(?[^}]+)\}/.exec(styles)?.groups?.["body"] ?? ""; const navRow = /\.codex-panel-ui__nav-row \{(?[^}]+)\}/.exec(styles)?.groups?.["body"] ?? ""; - const runtimePicker = /\.codex-panel__runtime-picker \{(?[^}]+)\}/.exec(styles)?.groups?.["body"] ?? ""; const statusPanelItems = /\.codex-panel__status-panel-items \{(?[^}]+)\}/.exec(styles)?.groups?.["body"] ?? ""; expect(threadList).toContain("gap: var(--nav-item-margin-bottom, var(--codex-panel-panel-gap))"); - expect(runtimePicker).toContain("gap: var(--nav-item-margin-bottom, var(--codex-panel-panel-gap))"); expect(statusPanelItems).toContain("gap: var(--nav-item-margin-bottom, var(--codex-panel-panel-gap))"); expect(navRow).toContain("--codex-panel-nav-item-background-hover: transparent"); expect(navRow).toContain("padding-inline-end: var(--size-4-2)");