Polish agent configuration shell chrome

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-07-22 15:25:46 +00:00
1 parent ad69cf63e2
commit 0372cccc89
3 files changed
+85 -14

No files matched your search

@@ -1,7 +1,12 @@
import { createElement } from "react";
import { renderToStaticMarkup } from "react-dom/server";
import { describe, expect, it } from "vitest";
import type { Agent } from "@paperclipai/shared";
import { getAgentConfigDirtyDetails } from "./AgentConfigForm";
import {
AgentConfigurationRail,
EffectiveConfigurationStrip,
buildEffectiveConfigurationChips,
filterAgentConfigurationSections,
resolveEffectiveConfiguration,
} from "./agent-configuration-shell";
@@ -43,4 +48,50 @@ describe("agent configuration shell", () => {
sections: ["environment", "runtime", "danger", "schedule"],
});
});
it("builds the effective chips in spec order without a separate Variables chip", () => {
const agent = {
adapterType: "codex_local",
adapterConfig: { model: "gpt-5.6-terra" },
runtimeConfig: { heartbeat: { enabled: false } },
permissions: { trustPreset: "standard" },
defaultEnvironmentId: null,
} as unknown as Agent;
expect(buildEffectiveConfigurationChips(resolveEffectiveConfiguration(agent, 2)).map((chip) => chip.label)).toEqual([
"Adapter",
"Model",
"Cost saver",
"Environment",
"Cadence",
"Trust",
"API keys",
]);
});
it("explains the rail status glyphs", () => {
const markup = renderToStaticMarkup(createElement(AgentConfigurationRail, {
query: "",
onQueryChange: () => undefined,
visibleSections: filterAgentConfigurationSections(""),
dirtySections: new Set(["runtime"]),
}));
expect(markup).toContain(">●</span> = unsaved change in section");
expect(markup).toContain(">⚡</span> = changes apply immediately");
});
it("keeps inherited chip metadata compact and the strip reachable", () => {
const markup = renderToStaticMarkup(createElement(EffectiveConfigurationStrip, {
chips: [
{ label: "Adapter", value: "Codex", section: "runtime" },
{ label: "Model", value: "Adapter default", section: "runtime", inherited: true },
],
}));
expect(markup).toContain("overflow-x-auto");
expect(markup).toContain('tabindex="0"');
expect(markup).toContain('title="Model is inherited"');
expect(markup).toContain('class="sr-only">inherited</span>');
});
});
@@ -69,6 +69,25 @@ export function resolveEffectiveConfiguration(agent: Agent, apiKeyCount = 0) {
};
}
export type EffectiveConfigurationChip = {
label: string;
value: string;
section: AgentConfigurationSectionId;
inherited?: boolean;
};
export function buildEffectiveConfigurationChips(effectiveConfig: ReturnType<typeof resolveEffectiveConfiguration>): EffectiveConfigurationChip[] {
return [
{ label: "Adapter", value: effectiveConfig.adapter, section: "runtime" },
{ label: "Model", value: effectiveConfig.model, section: "runtime", inherited: effectiveConfig.modelInherited },
{ label: "Cost saver", value: effectiveConfig.cheapModel, section: "runtime", inherited: effectiveConfig.cheapInherited },
{ label: "Environment", value: effectiveConfig.environment, section: "environment", inherited: effectiveConfig.environmentInherited },
{ label: "Cadence", value: effectiveConfig.cadence, section: "schedule" },
{ label: "Trust", value: effectiveConfig.trust, section: "access" },
{ label: "API keys", value: String(effectiveConfig.apiKeyCount), section: "keys" },
];
}
export function AgentConfigurationRail({
query,
onQueryChange,
@@ -120,20 +139,29 @@ export function AgentConfigurationRail({
</a>
))}
</nav>
<div className="mt-4 space-y-1 border-t border-border pt-3 text-xs text-muted-foreground" aria-label="Configuration section legend">
<p><span className="text-primary" aria-hidden="true">●</span> = unsaved change in section</p>
<p><span aria-hidden="true">⚡</span> = changes apply immediately</p>
</div>
</aside>
</>
);
}
export function EffectiveConfigurationStrip({ chips }: { chips: Array<{ label: string; value: string; section: AgentConfigurationSectionId; inherited?: boolean }> }) {
export function EffectiveConfigurationStrip({ chips }: { chips: EffectiveConfigurationChip[] }) {
return (
<div className="flex gap-2 overflow-x-auto border-b border-border pb-3" aria-label="Effective configuration">
<div className="flex max-w-full gap-2 overflow-x-auto border-b border-border pb-3 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring" aria-label="Effective configuration" tabIndex={0}>
{chips.map((chip) => (
<a key={chip.label} href={`#config-${chip.section}`} className="shrink-0">
<a
key={chip.label}
href={`#config-${chip.section}`}
className="shrink-0"
title={chip.inherited ? `${chip.label} is inherited` : undefined}
>
<Badge variant="outline" className={cn("gap-1.5 py-1", chip.inherited && "text-muted-foreground")}>
<span>{chip.label}</span>
<span className="font-mono">{chip.value}</span>
{chip.inherited ? <span>inherited</span> : null}
{chip.inherited ? <span className="sr-only">inherited</span> : null}
</Badge>
</a>
))}
+2 -10
View File
@@ -30,6 +30,7 @@ import {
AgentConfigurationRail,
ConfigurationSection,
EffectiveConfigurationStrip,
buildEffectiveConfigurationChips,
filterAgentConfigurationSections,
resolveEffectiveConfiguration,
type AgentConfigurationSectionId,
@@ -1778,16 +1779,7 @@ function AgentConfigurePage({
},
});
const effectiveChips: Array<{ label: string; value: string; section: AgentConfigurationSectionId; inherited?: boolean }> = [
{ label: "Adapter", value: effectiveConfig.adapter, section: "runtime" },
{ label: "Model", value: effectiveConfig.model, section: "runtime", inherited: effectiveConfig.modelInherited },
{ label: "Cost saver", value: effectiveConfig.cheapModel, section: "runtime", inherited: effectiveConfig.cheapInherited },
{ label: "Environment", value: effectiveConfig.environment, section: "environment", inherited: effectiveConfig.environmentInherited },
{ label: "Variables", value: String(effectiveConfig.environmentVariableCount), section: "environment" },
{ label: "Heartbeat", value: effectiveConfig.cadence, section: "schedule" },
{ label: "Trust", value: effectiveConfig.trust, section: "access" },
{ label: "API keys", value: String(effectiveConfig.apiKeyCount), section: "keys" },
];
const effectiveChips = buildEffectiveConfigurationChips(effectiveConfig);
return (
<div className="space-y-5">