diff --git a/src/features/chat/panel/runtime-status-projection.ts b/src/features/chat/panel/runtime-status-projection.ts index af8bd35d..e15c8e5a 100644 --- a/src/features/chat/panel/runtime-status-projection.ts +++ b/src/features/chat/panel/runtime-status-projection.ts @@ -4,7 +4,7 @@ import type { MessageStreamNoticeSection } from "../domain/message-stream/items" import { collaborationModeLabel as formatCollaborationModeLabel } from "../domain/runtime/labels"; import type { RuntimeSnapshot } from "../domain/runtime/snapshot"; import { appServerDiagnosticSections } from "../presentation/runtime/diagnostic-sections"; -import { runtimePermissionDetails } from "../presentation/runtime/permission-sections"; +import { runtimePermissionSections } from "../presentation/runtime/permission-sections"; import { effortStatusLines as buildEffortStatusLines, modelStatusLines as buildModelStatusLines, @@ -86,10 +86,10 @@ function toolInventoryDetails(input: ChatPanelRuntimeProjectionInput): MessageSt function permissionDetails(input: ChatPanelRuntimeProjectionInput): MessageStreamNoticeSection[] { const state = input.state(); return noticeSectionsFromDiagnostics( - runtimePermissionDetails({ + runtimePermissionSections({ snapshot: runtimeSnapshot(state), vaultPath: input.vaultPath(), - }).sections, + }), ); } diff --git a/src/features/chat/panel/surface/toolbar-projection.tsx b/src/features/chat/panel/surface/toolbar-projection.tsx index 8c4a37e7..383f874c 100644 --- a/src/features/chat/panel/surface/toolbar-projection.tsx +++ b/src/features/chat/panel/surface/toolbar-projection.tsx @@ -6,7 +6,7 @@ import type { Thread } from "../../../../domain/threads/model"; import { threadRowCoreProjection } from "../../../threads/list/row-projection"; import type { RuntimeSnapshot } from "../../domain/runtime/snapshot"; import { appServerDiagnosticSections } from "../../presentation/runtime/diagnostic-sections"; -import { runtimePermissionDetails } from "../../presentation/runtime/permission-sections"; +import { runtimePermissionSections } from "../../presentation/runtime/permission-sections"; import { rateLimitSummary } from "../../presentation/runtime/status"; import { toolInventoryDiagnosticSections } from "../../presentation/runtime/tool-inventory-diagnostic-sections"; import { Toolbar, type ToolbarActions, type ToolbarThreadRow, type ToolbarViewModel } from "../../ui/toolbar"; @@ -76,7 +76,7 @@ function chatPanelToolbarProjection(input: ToolbarViewModelInput): ToolbarViewMo const projection = toolbarStateProjection(input); const limit = rateLimitSummary(snapshot, input.nowMs); const diagnostics = model.diagnostics.value; - const permissions = runtimePermissionDetails({ + const permissions = runtimePermissionSections({ snapshot, vaultPath: input.vaultPath, }); @@ -90,8 +90,7 @@ function chatPanelToolbarProjection(input: ToolbarViewModelInput): ToolbarViewMo openPanel: projection.openPanel, threads: projection.threads, connectLabel: input.connected ? "Reconnect" : "Connect", - runtimePermissionsTitle: permissions.title, - runtimePermissions: permissions.sections, + permissionsAndApprovals: permissions, diagnostics: appServerDiagnosticSections({ connected: input.connected, configuredCommand: input.configuredCommand, diff --git a/src/features/chat/presentation/runtime/permission-sections.ts b/src/features/chat/presentation/runtime/permission-sections.ts index f538af1e..741ba74b 100644 --- a/src/features/chat/presentation/runtime/permission-sections.ts +++ b/src/features/chat/presentation/runtime/permission-sections.ts @@ -2,46 +2,29 @@ import { runtimeConfigOrDefault } from "../../../../domain/runtime/config"; import type { RuntimePermissionState, RuntimeSandboxPolicy } from "../../../../domain/runtime/permissions"; import { resolveRuntimeControls } from "../../domain/runtime/resolution"; import type { RuntimeSnapshot } from "../../domain/runtime/snapshot"; - -interface RuntimePermissionRow { - label: string; - value: string; -} - -interface RuntimePermissionSection { - title: string; - rows: RuntimePermissionRow[]; -} - -interface RuntimePermissionDetails { - title: string; - sections: RuntimePermissionSection[]; -} +import type { DiagnosticRow, DiagnosticSection } from "./diagnostic-sections"; interface RuntimePermissionSectionsInput { snapshot: RuntimeSnapshot; vaultPath: string; } -export function runtimePermissionDetails(input: RuntimePermissionSectionsInput): RuntimePermissionDetails { +export function runtimePermissionSections(input: RuntimePermissionSectionsInput): DiagnosticSection[] { const config = runtimeConfigOrDefault(input.snapshot.runtimeConfig); const resolution = resolveRuntimeControls(input.snapshot, config); - return { - title: "Permissions & Approvals", - sections: [ - { - title: "Permissions", - rows: accessRows(resolution.permissions.effective, input.vaultPath), - }, - { - title: "Approvals", - rows: approvalRows(resolution.permissions.effective, resolution.approvalsReviewer.effective), - }, - ], - }; + return [ + { + title: "Permissions", + rows: accessRows(resolution.permissions.effective, input.vaultPath), + }, + { + title: "Approvals", + rows: approvalRows(resolution.permissions.effective, resolution.approvalsReviewer.effective), + }, + ]; } -function accessRows(permissions: RuntimePermissionState, vaultPath: string): RuntimePermissionRow[] { +function accessRows(permissions: RuntimePermissionState, vaultPath: string): DiagnosticRow[] { return [ { label: "Profile", value: profileLabel(permissions) }, { label: "Sandbox", value: sandboxLabel(permissions.sandboxPolicy) }, @@ -50,7 +33,7 @@ function accessRows(permissions: RuntimePermissionState, vaultPath: string): Run ]; } -function approvalRows(permissions: RuntimePermissionState, reviewer: string | null): RuntimePermissionRow[] { +function approvalRows(permissions: RuntimePermissionState, reviewer: string | null): DiagnosticRow[] { return [ { label: "Approval policy", value: approvalPolicyLabel(permissions.approvalPolicy) }, { diff --git a/src/features/chat/ui/toolbar.tsx b/src/features/chat/ui/toolbar.tsx index ca0d5e52..b11c0673 100644 --- a/src/features/chat/ui/toolbar.tsx +++ b/src/features/chat/ui/toolbar.tsx @@ -42,8 +42,7 @@ export interface ToolbarViewModel { openPanel: "history" | "chat-actions" | "status" | null; threads: ToolbarThreadRow[]; connectLabel: string; - runtimePermissionsTitle: string; - runtimePermissions: ToolbarStatusSection[]; + permissionsAndApprovals: ToolbarStatusSection[]; diagnostics: ToolbarStatusSection[]; toolInventory: ToolbarStatusSection[]; } @@ -196,7 +195,7 @@ function StatusPanel({ model, actions }: { model: ToolbarViewModel; actions: Too - + ); @@ -252,8 +251,8 @@ function RateLimitPanel({ rateLimit }: { rateLimit: RateLimitSummary | null }): function DiagnosticSectionsPanel({ title, sections }: { title: string; sections: ToolbarStatusSection[] }): UiNode { return ( -
-
{title}
+
+
{title}
{sections.map((section) => ( ))} @@ -263,7 +262,7 @@ function DiagnosticSectionsPanel({ title, sections }: { title: string; sections: function DiagnosticRows({ rows }: { rows: ToolbarStatusRow[] }): UiNode { return ( -
+
{rows.map((row) => ( ))} @@ -274,7 +273,7 @@ function DiagnosticRows({ rows }: { rows: ToolbarStatusRow[] }): UiNode { function DiagnosticSection({ section }: { section: ToolbarStatusSection }): UiNode { return ( <> - {section.title ?
{section.title}
: null} + {section.title ?
{section.title}
: null} ); @@ -286,10 +285,10 @@ function DiagnosticRow({ row }: { row: ToolbarStatusRow }): UiNode {
{row.label}
diff --git a/src/styles/21-chat-toolbar.css b/src/styles/21-chat-toolbar.css index 49ebde69..f433ee5c 100644 --- a/src/styles/21-chat-toolbar.css +++ b/src/styles/21-chat-toolbar.css @@ -217,12 +217,12 @@ color: var(--codex-panel-color-danger); } -.codex-panel__connection-diagnostics { +.codex-panel__status-diagnostics { margin-top: var(--codex-panel-panel-gap); padding-top: var(--codex-panel-item-gap); } -.codex-panel__connection-diagnostics-title { +.codex-panel__status-diagnostics-title { padding: var(--codex-panel-section-label-padding); color: var(--codex-panel-section-label-color); font-size: var(--codex-panel-section-label-size); @@ -230,7 +230,7 @@ line-height: var(--codex-panel-section-label-line-height); } -.codex-panel__connection-diagnostics-section { +.codex-panel__status-diagnostics-section { padding: var(--codex-panel-section-label-block-padding); color: var(--codex-panel-section-label-color); font-size: var(--codex-panel-section-label-size); @@ -238,37 +238,37 @@ line-height: var(--codex-panel-section-label-line-height); } -.codex-panel__connection-diagnostics-title + .codex-panel__connection-diagnostics-section { +.codex-panel__status-diagnostics-title + .codex-panel__status-diagnostics-section { padding-top: 0; } -.codex-panel__connection-diagnostics-list { +.codex-panel__status-diagnostics-list { margin: 0; cursor: text; user-select: text; } -.codex-panel__connection-diagnostics-row { +.codex-panel__status-diagnostics-row { display: grid; grid-template-columns: minmax(0, 36%) minmax(0, 1fr); gap: var(--codex-panel-section-gap); padding: var(--codex-panel-control-gap) var(--codex-panel-section-gap); } -.codex-panel__connection-diagnostics-row dt, -.codex-panel__connection-diagnostics-row dd { +.codex-panel__status-diagnostics-row dt, +.codex-panel__status-diagnostics-row dd { margin: 0; min-width: 0; font-size: var(--font-ui-smaller); line-height: var(--line-height-tight); } -.codex-panel__connection-diagnostics-row dt { +.codex-panel__status-diagnostics-row dt { color: var(--codex-panel-text-faint); overflow-wrap: anywhere; } -.codex-panel__connection-diagnostics-row dd { +.codex-panel__status-diagnostics-row dd { color: var(--codex-panel-text-muted); overflow-wrap: anywhere; cursor: text; @@ -276,11 +276,11 @@ white-space: pre-wrap; } -.codex-panel__connection-diagnostics-row--warning dd { +.codex-panel__status-diagnostics-row--warning dd { color: var(--codex-panel-color-warning); } -.codex-panel__connection-diagnostics-row--error dd { +.codex-panel__status-diagnostics-row--error dd { color: var(--codex-panel-color-danger); } diff --git a/tests/features/chat/panel/surface/projections.test.ts b/tests/features/chat/panel/surface/projections.test.ts index d154b018..7740f3f1 100644 --- a/tests/features/chat/panel/surface/projections.test.ts +++ b/tests/features/chat/panel/surface/projections.test.ts @@ -144,9 +144,9 @@ describe("chat panel surface projections", () => { expect(parent.textContent).toContain("Permissions & Approvals"); expect(parent.textContent).toContain("Permissions"); expect(parent.textContent).toContain("Approvals"); - expect( - [...parent.querySelectorAll(".codex-panel__connection-diagnostics-section")].map((section) => section.textContent), - ).not.toContain("New thread"); + expect([...parent.querySelectorAll(".codex-panel__status-diagnostics-section")].map((section) => section.textContent)).not.toContain( + "New thread", + ); expect(parent.textContent).toContain(":workspace"); expect(parent.textContent).toContain("on-request"); expect(parent.textContent).toContain("auto_review"); diff --git a/tests/features/chat/ui/toolbar.test.ts b/tests/features/chat/ui/toolbar.test.ts index 9496c9bc..f0c4c7ea 100644 --- a/tests/features/chat/ui/toolbar.test.ts +++ b/tests/features/chat/ui/toolbar.test.ts @@ -167,7 +167,7 @@ describe("Toolbar decisions", () => { toolbarActions({ refreshStatus }), ); - expect([...parent.querySelectorAll(".codex-panel__connection-diagnostics-title")].map((title) => title.textContent)).toEqual([ + expect([...parent.querySelectorAll(".codex-panel__status-diagnostics-title")].map((title) => title.textContent)).toEqual([ "Connection diagnostics", "Permissions & Approvals", "Codex capabilities", @@ -179,7 +179,7 @@ describe("Toolbar decisions", () => { expect(parent.textContent).toContain("Copy debug details"); expect(parent.textContent).toContain("Refresh"); expect(parent.textContent).toContain("codex-cli/1.2.3"); - expect(parent.querySelector(".codex-panel__connection-diagnostics-row--error")?.textContent).toContain("model/list failed"); + expect(parent.querySelector(".codex-panel__status-diagnostics-row--error")?.textContent).toContain("model/list failed"); const statusItems = [...parent.querySelectorAll(".codex-panel__status-panel-item")]; expect(parent.querySelector(".codex-panel__status-panel-items")?.tagName).toBe("DIV"); expect(parent.querySelector(".codex-panel__status-panel-items")?.getAttribute("aria-label")).toBeNull(); @@ -198,8 +198,7 @@ describe("Toolbar decisions", () => { toolbarModel({ statusPanelOpen: true, openPanel: "status", - runtimePermissionsTitle: "Permissions & Approvals", - runtimePermissions: [ + permissionsAndApprovals: [ { title: "Permissions", rows: [ @@ -225,14 +224,14 @@ describe("Toolbar decisions", () => { expect(parent.textContent).toContain("Permissions & Approvals"); expect(parent.textContent).toContain("Permissions"); expect(parent.textContent).toContain("Approvals"); - expect( - [...parent.querySelectorAll(".codex-panel__connection-diagnostics-section")].map((section) => section.textContent), - ).not.toContain("Current thread"); + expect([...parent.querySelectorAll(".codex-panel__status-diagnostics-section")].map((section) => section.textContent)).not.toContain( + "Current thread", + ); expect(parent.textContent).toContain(":workspace"); expect(parent.textContent).toContain("workspace-write"); expect(parent.textContent).toContain("auto_review"); - expect(parent.querySelector(".codex-panel__connection-diagnostics-row--warning")).toBeNull(); - expect(parent.querySelector(".codex-panel__connection-diagnostics-row--error")).toBeNull(); + expect(parent.querySelector(".codex-panel__status-diagnostics-row--warning")).toBeNull(); + expect(parent.querySelector(".codex-panel__status-diagnostics-row--error")).toBeNull(); }); it("copies raw debug details from the status menu", () => { @@ -411,8 +410,7 @@ function toolbarModel(overrides: Partial = {}): ToolbarViewMod openPanel: null, threads: [{ title: "Thread", threadId: "thread", selected: true, disabled: false, canArchive: true, rename: null }], connectLabel: "Reconnect", - runtimePermissionsTitle: "Permissions & Approvals", - runtimePermissions: [{ title: "Permissions", rows: [{ label: "Thread", value: "(none)" }] }], + permissionsAndApprovals: [{ title: "Permissions", rows: [{ label: "Thread", value: "(none)" }] }], diagnostics: [{ title: "Process", rows: [{ label: "Codex App Server", value: "codex-cli/test" }] }], toolInventory: [{ title: "Tool providers", rows: [{ label: "Tool providers", value: "not loaded", level: "warning" }] }], ...overrides,