diff --git a/CHANGELOG.md b/CHANGELOG.md index be6ad41..c4831aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. The format ### Changed +- [#334](https://github.com/mohanagy/miftah/issues/334) Tightened the Console connection catalog for faster scanning: cards use a compact hierarchy for the connection name, durable default, named-account actions, authentication status, and Manage connection action; medium and narrow layouts collapse before controls can crowd or clip; and long account or connection names wrap without hiding status counts or keyboard focus. Existing authentication, redaction, audit, containment, no-shell, and public API guarantees are unchanged. - [#333](https://github.com/mohanagy/miftah/issues/333) Replaced first-use Console setup vocabulary with task-oriented language: the chooser now leads with **Choose your MCP**, groups built-in and custom options plainly, names custom MCP, local executable, remote HTTPS, browser sign-in, existing-client-entry, and secret-reference paths distinctly, and limits help text to the information needed for the next step. The wording does not claim universal MCP or OAuth compatibility, and existing authentication, redaction, audit, containment, no-shell, and public API guarantees are unchanged. - [#332](https://github.com/mohanagy/miftah/issues/332) Turned Console management navigation into five focused, keyboard-accessible sections: Overview, Accounts, Authentication, Client setup, and Audit. Only the selected task is shown; the non-secret URL fragment restores a valid selection after refresh or reauthentication, while switching tasks leaves in-page form drafts untouched and performs no writes. Destructive account removal remains confined to Accounts, and existing redaction, audit, OAuth, containment, no-shell, and public API guarantees are unchanged. - [#331](https://github.com/mohanagy/miftah/issues/331) Made Console account switching read like a user action: each named account now offers a client-specific `Use in this chat` action, copied-request confirmation names the account and selected client, and the durable default is described as applying to new MCP sessions. Shared guidance appears once instead of on every connection card. The existing `miftah_use_profile` request, supported clients, redaction, audit, credential handling, containment, and public APIs are unchanged. diff --git a/src/console/console-assets.ts b/src/console/console-assets.ts index 4d19991..beef71a 100644 --- a/src/console/console-assets.ts +++ b/src/console/console-assets.ts @@ -525,7 +525,7 @@ p { color: var(--muted); line-height: 1.6; } .gate { display: grid; grid-template-columns: minmax(0, 1fr) minmax(18rem, 1fr); gap: clamp(2rem, 7vw, 6rem); border-top: 1px solid var(--line); padding: 2rem 0 4rem; } .gate h2 { max-width: 31rem; } .field-note { margin: .65rem 0 0; font-size: .8rem; } -.work-section { border-top: 1px solid var(--line); padding: 2rem 0 clamp(3rem, 7vw, 6rem); } +.work-section { border-top: 1px solid var(--line); padding: 1.75rem 0 clamp(2.5rem, 5vw, 4.5rem); } .authentication-guide { margin: 0 0 clamp(3rem, 7vw, 6rem); padding: 0; border: 1px solid var(--line); background: rgb(255 255 255 / 2%); } .authentication-guide > summary { padding: 1.15rem 1.25rem; color: var(--ink); } .authentication-guide[open] > summary { border-bottom: 1px solid var(--line); } @@ -535,7 +535,7 @@ p { color: var(--muted); line-height: 1.6; } .mode-native { box-shadow: inset 0 .2rem 0 var(--key); } .mode-unsupported { box-shadow: inset 0 .2rem 0 var(--danger); } .mode p:last-child { font-size: .88rem; } -.section-heading { display: grid; grid-template-columns: minmax(0, 1fr) minmax(17rem, .75fr); gap: 3rem; align-items: end; margin-bottom: 2rem; } +.section-heading { display: grid; grid-template-columns: minmax(0, 1fr) minmax(17rem, .75fr); gap: 2rem; align-items: end; margin-bottom: 1.5rem; } .form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem; padding: clamp(1.2rem, 3vw, 2rem); border: 1px solid var(--line); background: rgb(21 26 23 / 88%); } .form-grid.compact { margin-top: 1.2rem; } .wide { grid-column: 1 / -1; } @@ -588,24 +588,26 @@ button.danger { color: #ffd7cf; background: transparent; border: 1px solid #7043 .summary span { color: var(--muted); font-size: .8rem; line-height: 1.45; } .restart-note { margin: 1rem 0 4rem; padding: 1rem 1.2rem; border-left: .2rem solid var(--key); background: rgb(239 180 77 / 7%); } .connection-list { display: grid; gap: .8rem; margin-bottom: 1.2rem; } -.configuration-catalog { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .8rem; } -.configuration-catalog-status { margin: 0 0 1rem; padding: .85rem 1rem; border: 1px solid var(--line); background: rgb(255 255 255 / 2%); } +.configuration-catalog { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .65rem; } +.configuration-catalog-status { margin: 0 0 .75rem; padding: .75rem 1rem; border: 1px solid var(--line); background: rgb(255 255 255 / 2%); } .configuration-catalog-status p { margin: 0; } .configuration-catalog-status ul { margin: .55rem 0 0; padding-left: 1.2rem; color: var(--muted); } .configuration-catalog-status ul:empty { display: none; } -.catalog-actions { display: grid; gap: .8rem; justify-items: start; } +.catalog-actions { display: grid; gap: .6rem; justify-items: start; } .catalog-actions p { margin: 0; } .catalog-actions strong { color: var(--ink); } -.catalog-boundary { margin-top: 1rem; } -.configuration-card { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 1rem; align-items: center; padding: 1.15rem 1.25rem; border: 1px solid var(--line); background: var(--panel); } -.configuration-card p { margin: .25rem 0 0; font-size: .82rem; } -.configuration-card .configuration-meta { font: .73rem/1.5 ui-monospace, monospace; } -.configuration-profiles { display: flex; flex-wrap: wrap; gap: .4rem; margin: .75rem 0 0; padding: 0; list-style: none; } -.configuration-profiles li { display: flex; flex-wrap: wrap; align-items: center; gap: .45rem; padding: .28rem .5rem; color: var(--ink); background: var(--ground); border: 1px solid var(--line); font: .72rem/1.35 ui-monospace, monospace; } +.catalog-boundary { margin-top: .75rem; } +.configuration-card { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: .75rem; align-items: start; padding: .9rem 1rem; border: 1px solid var(--line); background: var(--panel); } +.configuration-card > div { min-width: 0; } +.configuration-card h3 { margin: 0; overflow-wrap: anywhere; } +.configuration-card p { margin: .25rem 0 0; overflow-wrap: anywhere; font-size: .82rem; } +.configuration-card .configuration-meta { color: var(--muted); font: .7rem/1.45 ui-monospace, monospace; } +.configuration-profiles { display: flex; flex-wrap: wrap; gap: .35rem; margin: .55rem 0 0; padding: 0; list-style: none; } +.configuration-profiles li { display: flex; min-width: 0; max-width: 100%; flex-wrap: wrap; align-items: center; gap: .4rem; padding: .22rem .4rem; color: var(--ink); background: var(--ground); border: 1px solid var(--line); font: .72rem/1.35 ui-monospace, monospace; } +.configuration-profiles span { min-width: 0; overflow-wrap: anywhere; } .configuration-profiles .configuration-default { border-color: var(--key); } -.configuration-profiles button { min-height: 2.75rem; padding: 0 .65rem; } -.configuration-card .configuration-switch { margin-top: .7rem; color: var(--ink); } -.configuration-card .configuration-switch-technical { color: var(--muted); font-size: .75rem; } +.configuration-profiles button { min-height: 2.75rem; max-width: 100%; padding: 0 .65rem; white-space: normal; overflow-wrap: anywhere; } +.configuration-card > button { align-self: start; white-space: nowrap; } .configuration-card button { min-height: 2.75rem; font-size: .78rem; } .provider-authentication { border-left: .2rem solid var(--safe); padding-left: 1.2rem; background: linear-gradient(90deg, rgb(117 201 154 / 7%), transparent 50%); } .provider-authentication .section-heading { margin-bottom: 0; } @@ -633,8 +635,20 @@ summary { cursor: pointer; font-weight: 700; } .audit-list li { display: grid; grid-template-columns: 10rem 1fr auto; gap: 1rem; padding: .8rem 0; border-bottom: 1px solid var(--line); color: var(--muted); font: .76rem/1.45 ui-monospace, monospace; } .status { position: sticky; bottom: 1rem; min-height: 1.5rem; width: fit-content; max-width: 100%; margin: 1rem 0 0; padding: .7rem 1rem; color: var(--ink); background: #222923; border: 1px solid var(--line); box-shadow: 0 .7rem 2rem rgb(0 0 0 / 35%); } .status:empty { visibility: hidden; } -@media (max-width: 850px) { .mode-grid, .summary, .configuration-catalog { grid-template-columns: repeat(2, 1fr); } .section-heading, .split { grid-template-columns: 1fr; gap: 1rem; } } -@media (max-width: 620px) { .gate, .form-grid, .mode-grid, .summary, .configuration-catalog, .setup-completion-copy { grid-template-columns: 1fr; } .wide, .setup-completion-client { grid-column: 1; } .masthead { flex-direction: column; } .input-row, .connection, .configuration-card { align-items: stretch; flex-direction: column; grid-template-columns: 1fr; } .connection-actions { justify-content: flex-start; } .audit-list li { grid-template-columns: 1fr; gap: .2rem; } } +@media (max-width: 850px) { + .mode-grid, .summary { grid-template-columns: repeat(2, minmax(0, 1fr)); } + .configuration-catalog { grid-template-columns: 1fr; } + .section-heading, .split { grid-template-columns: 1fr; gap: 1rem; } +} +@media (max-width: 620px) { + .gate, .form-grid, .mode-grid, .summary, .configuration-catalog, .setup-completion-copy { grid-template-columns: 1fr; } + .wide, .setup-completion-client { grid-column: 1; } + .masthead { flex-direction: column; } + .input-row, .connection, .configuration-card { align-items: stretch; flex-direction: column; grid-template-columns: 1fr; } + .configuration-card > button { width: 100%; } + .connection-actions { justify-content: flex-start; } + .audit-list li { grid-template-columns: 1fr; gap: .2rem; } +} @media (prefers-reduced-motion: reduce) { *, *::before, *::after { scroll-behavior: auto !important; transition: none !important; animation: none !important; } } `; @@ -1334,13 +1348,15 @@ const script = `(() => { configurationCatalog.replaceChildren(); catalogSwitchButtons = []; catalogSwitchUnavailableCount = 0; - catalog.configurations.forEach((configuration) => { + catalog.configurations.forEach((configuration, index) => { const id = typeof configuration.id === "string" ? configuration.id : ""; const card = document.createElement("article"); card.className = "configuration-card"; const details = document.createElement("div"); - const title = document.createElement("strong"); + const title = document.createElement("h3"); + title.id = "configuration-title-" + index; title.textContent = typeof configuration.name === "string" ? configuration.name : "Unnamed configuration"; + card.setAttribute("aria-labelledby", title.id); const summary = document.createElement("p"); const profileCount = typeof configuration.profileCount === "number" ? configuration.profileCount : 0; const defaultProfile = typeof configuration.defaultProfile === "string" ? configuration.defaultProfile : "unknown"; diff --git a/tests/console-server.test.ts b/tests/console-server.test.ts index e32e145..a7af2b9 100644 --- a/tests/console-server.test.ts +++ b/tests/console-server.test.ts @@ -2996,7 +2996,7 @@ describe("local Console control server", () => { } }); - it("keeps new Console actions keyboard-visible, announced, target-sized, and responsive", async () => { + it("keeps the connection catalog hierarchy keyboard-visible, compact, and responsive", async () => { const server = await startConsoleServer(await writeConfig(), { bootstrapCredential: "test-only-bootstrap-credential" }); @@ -3014,6 +3014,18 @@ describe("local Console control server", () => { expect(css).toContain("button:focus-visible, summary:focus-visible"); expect(css).toContain("#workspace-task-navigation a:focus-visible"); expect(css).toContain(".configuration-profiles button { min-height: 2.75rem;"); + expect(css).toContain(".configuration-card > div { min-width: 0; }"); + expect(css).toContain( + ".configuration-card p { margin: .25rem 0 0; overflow-wrap: anywhere; font-size: .82rem; }" + ); + expect(css).toContain(".configuration-profiles span { min-width: 0; overflow-wrap: anywhere; }"); + expect(css).toMatch( + /@media \(max-width: 850px\)[\s\S]*?\.configuration-catalog \{ grid-template-columns: 1fr; \}/u + ); + expect(css).toMatch( + /@media \(max-width: 620px\)[\s\S]*?\.configuration-card > button \{ width: 100%; \}/u + ); + expect(css).not.toContain(".configuration-switch-technical"); expect(css).toContain(".setup-completion-copy { grid-template-columns: 1fr;"); const script = await fetch(new URL("/app.js", server.url)); @@ -3022,6 +3034,8 @@ describe("local Console control server", () => { expect(javascript).toContain( 'button.setAttribute("aria-label", "Copy request to use " + profile + " in the current " + clientName + " chat")' ); + expect(javascript).toContain('title.id = "configuration-title-" + index;'); + expect(javascript).toContain('card.setAttribute("aria-labelledby", title.id);'); } finally { await server.close(); }