Align status diagnostics sections

This commit is contained in:
murashit 2026-07-01 18:33:08 +09:00
parent 48ca57ebe8
commit 6575ad7bf3
7 changed files with 53 additions and 74 deletions

View file

@ -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,
}),
);
}

View file

@ -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,

View file

@ -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) },
{

View file

@ -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
</div>
<RateLimitPanel rateLimit={model.rateLimit} />
<DiagnosticSectionsPanel title="Connection diagnostics" sections={model.diagnostics} />
<DiagnosticSectionsPanel title={model.runtimePermissionsTitle} sections={model.runtimePermissions} />
<DiagnosticSectionsPanel title="Permissions & Approvals" sections={model.permissionsAndApprovals} />
<DiagnosticSectionsPanel title="Codex capabilities" sections={model.toolInventory} />
</>
);
@ -252,8 +251,8 @@ function RateLimitPanel({ rateLimit }: { rateLimit: RateLimitSummary | null }):
function DiagnosticSectionsPanel({ title, sections }: { title: string; sections: ToolbarStatusSection[] }): UiNode {
return (
<div className="codex-panel__connection-diagnostics">
<div className="codex-panel__connection-diagnostics-title">{title}</div>
<div className="codex-panel__status-diagnostics">
<div className="codex-panel__status-diagnostics-title">{title}</div>
{sections.map((section) => (
<DiagnosticSection key={section.title} section={section} />
))}
@ -263,7 +262,7 @@ function DiagnosticSectionsPanel({ title, sections }: { title: string; sections:
function DiagnosticRows({ rows }: { rows: ToolbarStatusRow[] }): UiNode {
return (
<dl className="codex-panel__connection-diagnostics-list">
<dl className="codex-panel__status-diagnostics-list">
{rows.map((row) => (
<DiagnosticRow key={`${row.label}:${row.value}:${row.level ?? "normal"}`} row={row} />
))}
@ -274,7 +273,7 @@ function DiagnosticRows({ rows }: { rows: ToolbarStatusRow[] }): UiNode {
function DiagnosticSection({ section }: { section: ToolbarStatusSection }): UiNode {
return (
<>
{section.title ? <div className="codex-panel__connection-diagnostics-section">{section.title}</div> : null}
{section.title ? <div className="codex-panel__status-diagnostics-section">{section.title}</div> : null}
<DiagnosticRows rows={section.rows} />
</>
);
@ -286,10 +285,10 @@ function DiagnosticRow({ row }: { row: ToolbarStatusRow }): UiNode {
<div
className={
level === "error"
? "codex-panel__connection-diagnostics-row codex-panel__connection-diagnostics-row--error"
? "codex-panel__status-diagnostics-row codex-panel__status-diagnostics-row--error"
: level === "warning"
? "codex-panel__connection-diagnostics-row codex-panel__connection-diagnostics-row--warning"
: "codex-panel__connection-diagnostics-row"
? "codex-panel__status-diagnostics-row codex-panel__status-diagnostics-row--warning"
: "codex-panel__status-diagnostics-row"
}
>
<dt>{row.label}</dt>

View file

@ -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);
}

View file

@ -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");

View file

@ -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<HTMLElement>(".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<ToolbarViewModel> = {}): 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,