Keys section polish (wave-5 chunk A, owner design): the minimal key-value table
The owner called the old section messy; the redesign is theirs:
- MINIMAL TABLE: provider label left, masked status right (…tail ·
chars · env), description hints GONE (they took too much space),
the 'Get a key ↗' link a tiny muted guest on curated rows. Rows
read as clean key-value pairs.
- ENV-OWNED ROWS ARE READ-ONLY (owner ruling): a real environment
variable owns the value — the row dims (graph-view graying) and no
paste field renders, because a paste box on a key the UI cannot
make live would mislead. Clicking does nothing.
- OPENROUTER UN-PINNED (owner ruling): one provider among 33 — a row
only when stored, otherwise it's an option in the Add dropdown like
everyone else. No synthesis, no lifeline description.
- ROW INTERACTION (owner choice): clicking a writable row unfolds a
paste field + Save (+ Delete for a file-sourced value) beneath it;
success folds it back to rest; Esc folds without closing the dialog
(claimed at document CAPTURE phase — the only stopPropagation that
outruns both React handlers and the dialog's window listener; the
React-level attempt leaked and closed the dialog, caught live).
- ADD-PROVIDER = the table's last row, in the app's shared dropdown
language: new ProviderMenuList (fuzzy search + arrow-walk + Enter
over the 33 pi-ai providers, label + envVar rows) in the shared
model-menu.tsx, portaled and fixed-positioned like the role menus.
Picking one unfolds a paste row; Save stores, Cancel/Esc folds.
- Tools & services (Deepgram + the Google pair) keep their own group,
same table look, same unfold interaction.
- Unknown auth.json vars stay read-only dimmed rows.
Tests: classifyKeys re-pinned (openrouter rides the normal provider
classification; addable drops stored keys), keyStatusLine minimal
('not set' for absent, ' · env' marker for environment-owned). Gates:
typecheck, lint, 759/759, build.
Live-verified: add→save→replace→delete round-trips, fold-on-save,
Esc layering both flows, env-owned rows read-only, addable count
31→30→31.
This commit is contained in:
+18
-13
@@ -155,18 +155,20 @@ describe("classifyKeys — the Keys section's two-group projection", () => {
|
||||
{ id: "openrouter", envVar: "OPENROUTER_API_KEY", label: "OpenRouter" },
|
||||
];
|
||||
|
||||
it("OpenRouter is pinned: synthesized when absent (first boot), found when present", () => {
|
||||
it("OpenRouter is UN-pinned (wave-5 owner ruling): no row when absent, a row when stored", () => {
|
||||
const none = classifyKeys([], providers);
|
||||
assert.equal(none.openrouter.id, "OPENROUTER_API_KEY");
|
||||
assert.ok(!none.openrouter.present);
|
||||
assert.equal(none.providerRows.length, 0); // nothing synthesized
|
||||
assert.ok(none.addable.some((p) => p.id === "openrouter")); // it's just an Add option
|
||||
const stored = classifyKeys([synth("OPENROUTER_API_KEY", true)], providers);
|
||||
assert.ok(stored.openrouter.present);
|
||||
assert.equal(stored.providerRows.length, 1);
|
||||
assert.equal(stored.providerRows[0]!.id, "OPENROUTER_API_KEY");
|
||||
assert.ok(!stored.addable.some((p) => p.id === "openrouter"));
|
||||
});
|
||||
|
||||
it("stored provider keys land in providerRows with their provider entry", () => {
|
||||
it("stored provider keys land in providerRows; the Add dropdown drops them", () => {
|
||||
const out = classifyKeys([synth("GROQ_API_KEY", true)], providers);
|
||||
assert.equal(out.providerRows.length, 1);
|
||||
assert.equal(out.providerRows[0]!.provider?.id, "groq");
|
||||
assert.equal(out.providerRows[0]!.id, "GROQ_API_KEY");
|
||||
// the Add dropdown no longer offers groq
|
||||
assert.ok(!out.addable.some((p) => p.id === "groq"));
|
||||
assert.ok(out.addable.some((p) => p.id === "deepseek"));
|
||||
@@ -195,19 +197,22 @@ describe("classifyKeys — the Keys section's two-group projection", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("keyStatusLine — the masked at-a-glance state", () => {
|
||||
it("present file key: tail, chars, source", () => {
|
||||
describe("keyStatusLine — the minimal at-a-glance state", () => {
|
||||
it("present file key: tail + chars, no source suffix", () => {
|
||||
assert.equal(
|
||||
keyStatusLine({ id: "X", info: null, present: true, masked: "…ab12 (64 chars)", source: "file", shadowedByEnv: false }),
|
||||
"…ab12 · 64 chars · saved",
|
||||
"…ab12 · 64",
|
||||
);
|
||||
});
|
||||
|
||||
it("env-sourced key reads 'environment'", () => {
|
||||
assert.ok(keyStatusLine({ id: "X", info: null, present: true, masked: "…ab12 (64 chars)", source: "env", shadowedByEnv: false })?.endsWith("environment"));
|
||||
it("env-owned key carries the 'env' marker (the row renders read-only)", () => {
|
||||
assert.equal(
|
||||
keyStatusLine({ id: "X", info: null, present: true, masked: "…ca10 (73 chars)", source: "env", shadowedByEnv: false }),
|
||||
"…ca10 · 73 · env",
|
||||
);
|
||||
});
|
||||
|
||||
it("absent key has no line", () => {
|
||||
assert.equal(keyStatusLine({ id: "X", info: null, present: false, masked: null, source: "none", shadowedByEnv: false }), null);
|
||||
it("absent key reads 'not set'", () => {
|
||||
assert.equal(keyStatusLine({ id: "X", info: null, present: false, masked: null, source: "none", shadowedByEnv: false }), "not set");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user