From 3750bee48b6d446b675998b1465b738dd653c211 Mon Sep 17 00:00:00 2001 From: Kjell Tore Guttormsen Date: Sun, 3 May 2026 18:35:44 +0200 Subject: [PATCH] feat(ms-ai-architect): playground v3 catalog surface with search + 5 expansion groups [skip-docs] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Step 9 of v3 plan. Replaces renderCatalogStub with full renderCatalogSurface — search-input + 5 .expansion-grupper (en per CATALOG.categories) + per-command-card with "Åpne skjema"-button. Klikk åpner modal med renderCommandForm (samme generic renderer som prosjekt-detalj fra Step 8). Søk: input-event oppdaterer modul-lokal catalogSearchQuery og kaller refreshCatalogResults() som re-rendrer kun groups-containeren — bevarer fokus + cursor i søkefeltet (full re-render ville flyttet caret). Filtrerer på id+label+description+argument_hint. Når query er aktiv forces alle expansions med treff åpne; ellers er 'regulatory' åpen som default (mest brukt entry-point). Verktøy-commands får .catalog-card__pill="Verktøy" + .catalog-tool-notice ("Verktøy — ingen rapport-import"). Modalen viser samme advarsel via .guide-panel--info-banner. Rapport-produserende får "Rapport"-pill. Verifisert via vm-sandbox med activeSurface='catalog': - data-command-card === 24 (Step 9 verify-assert ✓) - 5 expansion-grupper (data-catalog-group) - 24 open-catalog-form-knapper - 17 Rapport-pills + 7 Verktøy-notices (matcher CATALOG.commands.filter produces_report) - refreshCatalogResults() med query='classify' kjører feilfritt --- .../playground/ms-ai-architect-v3.html | 187 +++++++++++++++++- 1 file changed, 184 insertions(+), 3 deletions(-) diff --git a/plugins/ms-ai-architect/playground/ms-ai-architect-v3.html b/plugins/ms-ai-architect/playground/ms-ai-architect-v3.html index 7f70b13..660ad56 100644 --- a/plugins/ms-ai-architect/playground/ms-ai-architect-v3.html +++ b/plugins/ms-ai-architect/playground/ms-ai-architect-v3.html @@ -1440,7 +1440,7 @@ if (active === 'onboarding') renderOnboardingSurface(); else if (active === 'home') renderHomeSurface(); else if (active === 'project') renderProjectSurface(); - else if (active === 'catalog') renderCatalogStub(); + else if (active === 'catalog') renderCatalogSurface(); } function navigate(surface) { @@ -1920,10 +1920,164 @@ ); } - function renderCatalogStub() { + // ============================================================ + // CATALOG SURFACE (Step 9) + // ============================================================ + // + // 24 commands gruppert i 5 .expansion-grupper (CATALOG.categories) med + // søke-input som filtrerer på id+label+description+argument_hint. + // Hver kategori-expansion rendrer en .catalog-cards-grid med kort. + // "Åpne skjema" på et kort åpner renderCommandForm() i modal. + // + // Søk: input-event oppdaterer modul-lokal catalogSearchQuery og + // re-rendrer kun groups-containeren (bevarer fokus/cursor i søkefeltet). + // Når query er ikke-tom forces alle expansions åpne. I tom-state er + // 'regulatory' åpen som standard (mest brukt entry-point). + // + // Verktøy-commands får .catalog-tool-notice "Verktøy"-pill + samme + // skjema-modal — ingen rapport-import (parser/renderer hopper dem over). + + let catalogSearchQuery = ''; + + function catalogMatches(cmd, q) { + if (!q) return true; + const hay = ( + (cmd.id || '') + ' ' + + (cmd.label || '') + ' ' + + (cmd.description || '') + ' ' + + (cmd.argument_hint || '') + ).toLowerCase(); + return hay.indexOf(q) >= 0; + } + + function renderCatalogCardHtml(cmd) { + const isVerktoy = !cmd.produces_report; + const pill = isVerktoy + ? 'Verktøy' + : 'Rapport'; + const hintHtml = cmd.argument_hint + ? '' + escapeHtml(cmd.argument_hint) + '' + : ''; + const verktoyNotice = isVerktoy + ? '
Verktøy — ingen rapport-import. Skjema bygger pipeline-streng som kjøres i terminalen.
' + : ''; + return ( + '
' + + '
' + + '
' + + '

' + escapeHtml(cmd.label) + '

' + + '

' + escapeHtml(cmd.description) + '

' + + '
' + + pill + + '
' + + '
' + + '/architect:' + escapeHtml(cmd.id) + '' + + hintHtml + + '
' + + verktoyNotice + + '
' + + '' + + '
' + + '
' + ); + } + + function renderCatalogGroupsHtml() { + const q = (catalogSearchQuery || '').trim().toLowerCase(); + return CATALOG.categories.map(function (cat) { + const cmds = CATALOG.commands.filter(function (c) { + return c.category === cat.id && catalogMatches(c, q); + }); + const cardsHtml = cmds.map(renderCatalogCardHtml).join(''); + // Force-open når aktiv søk-query har treff. Ellers: 'regulatory' åpen som default. + const expanded = q ? (cmds.length > 0 ? 'true' : 'false') : (cat.id === 'regulatory' ? 'true' : 'false'); + const subLabel = cmds.length === cat.count + ? cat.count + ' commands' + : cmds.length + ' / ' + cat.count + ' commands'; + const body = cmds.length > 0 + ? '
' + cardsHtml + '
' + : '

Ingen treff i denne kategorien.

'; + return ( + '
' + + '' + + '
' + + '
' + body + '
' + + '
' + + '
' + ); + }).join(''); + } + + function renderCatalogSurface() { const root = getSurfaceEl('catalog'); if (!root) return; - root.innerHTML = renderTopbar('Katalog') + '

Command-katalog fylles i Step 9.

'; + const q = (catalogSearchQuery || '').trim().toLowerCase(); + const totalMatches = CATALOG.commands.filter(function (c) { return catalogMatches(c, q); }).length; + const countText = totalMatches + ' av ' + CATALOG.commands.length + ' treff' + (q ? ' for «' + escapeHtml(catalogSearchQuery) + '»' : ''); + root.innerHTML = ( + renderTopbar('Katalog') + + '
' + + '
' + + '

Command-katalog

' + + '

24 commands gruppert i 5 kategorier. Åpne skjema for å bygge en pipeline-streng som kopieres til terminalen og kjøres med Claude Code.

' + + '
' + + '
' + + '' + + '' + countText + '' + + '
' + + '
' + renderCatalogGroupsHtml() + '
' + + '
' + ); + } + + function refreshCatalogResults() { + const root = getSurfaceEl('catalog'); + if (!root) return; + const groupsEl = root.querySelector('[data-catalog-groups]'); + if (groupsEl) groupsEl.innerHTML = renderCatalogGroupsHtml(); + const countEl = root.querySelector('[data-catalog-count]'); + if (countEl) { + const q = (catalogSearchQuery || '').trim().toLowerCase(); + const totalMatches = CATALOG.commands.filter(function (c) { return catalogMatches(c, q); }).length; + countEl.textContent = totalMatches + ' av ' + CATALOG.commands.length + ' treff' + (q ? ' for «' + catalogSearchQuery + '»' : ''); + } + } + + function renderCatalogFormModalHtml(cmd) { + const formHtml = renderCommandForm(cmd.id, { context: 'modal', scope: 'm' }); + const verktoyBanner = !cmd.produces_report + ? ( + '
' + + '' + + '
' + + '

Verktøy

' + + '

Dette er et verktøy. Skjemaet bygger en pipeline-streng — ingen rapport-import.

' + + '
' + + '
' + ) + : ''; + return ( + '' + ); } // ---- Paste-import stub (Step 12 erstatter med faktisk routing) ---- @@ -2457,6 +2611,31 @@ showCommandPreview(formEl, buildCommand(commandId, data)); }; + // ---- Step 9: catalog actions ---- + + ACTIONS['open-catalog-form'] = function (ev, el) { + const commandId = el.dataset.command; + if (!commandId) return; + const cmd = (CATALOG.commands || []).find(function (c) { return c.id === commandId; }); + if (!cmd) return; + mountModal(renderCatalogFormModalHtml(cmd)); + }; + + ACTIONS['catalog-toggle-group'] = function (ev, el) { + const exp = el.closest('.expansion'); + if (!exp) return; + const open = exp.getAttribute('aria-expanded') === 'true'; + exp.setAttribute('aria-expanded', open ? 'false' : 'true'); + }; + + // Søk-input: input-event oppdaterer query og re-rendrer kun groups-containeren + // (bevarer fokus/cursor i selve søke-feltet — full re-render ville flyttet caret). + document.addEventListener('input', function (ev) { + if (!ev.target.matches || !ev.target.matches('[data-catalog-search]')) return; + catalogSearchQuery = ev.target.value || ''; + refreshCatalogResults(); + }); + // Eksponer for Verify-asserts og Step 8/9/12. window.__SCENARIOS = SCENARIOS; window.__createProject = createProject; @@ -2468,6 +2647,8 @@ window.__renderCommandForm = renderCommandForm; window.__readCommandFormValues = readCommandFormValues; window.__resolveSharedPath = resolveSharedPath; + window.__renderCatalogSurface = renderCatalogSurface; + window.__refreshCatalogResults = refreshCatalogResults; ACTIONS['export-state'] = function () { try { exportState(); }