mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-29 04:25:52 +01:00
Polish agent configuration shell chrome
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
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>
|
||||
))}
|
||||
|
||||
@@ -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">
|
||||
|
||||
Reference in new issue
Block a user