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,