feat(ms-ai-architect): playground v3 catalog surface with search + 5 expansion groups [skip-docs]
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
This commit is contained in:
parent
f55a0e9513
commit
3750bee48b
1 changed files with 184 additions and 3 deletions
|
|
@ -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
|
||||
? '<span class="catalog-card__pill">Verktøy</span>'
|
||||
: '<span class="catalog-card__pill">Rapport</span>';
|
||||
const hintHtml = cmd.argument_hint
|
||||
? '<span class="catalog-card__hint">' + escapeHtml(cmd.argument_hint) + '</span>'
|
||||
: '';
|
||||
const verktoyNotice = isVerktoy
|
||||
? '<div class="catalog-tool-notice">Verktøy — ingen rapport-import. Skjema bygger pipeline-streng som kjøres i terminalen.</div>'
|
||||
: '';
|
||||
return (
|
||||
'<article class="catalog-card" data-command-card data-command-id="' + escapeAttr(cmd.id) + '">' +
|
||||
'<div class="catalog-card__head">' +
|
||||
'<div>' +
|
||||
'<h3 class="catalog-card__title">' + escapeHtml(cmd.label) + '</h3>' +
|
||||
'<p class="catalog-card__desc">' + escapeHtml(cmd.description) + '</p>' +
|
||||
'</div>' +
|
||||
pill +
|
||||
'</div>' +
|
||||
'<div class="catalog-card__meta">' +
|
||||
'<span class="command-card__id">/architect:' + escapeHtml(cmd.id) + '</span>' +
|
||||
hintHtml +
|
||||
'</div>' +
|
||||
verktoyNotice +
|
||||
'<div class="catalog-card__actions">' +
|
||||
'<button type="button" class="btn btn--secondary btn--sm" data-action="open-catalog-form" data-command="' + escapeAttr(cmd.id) + '">Åpne skjema</button>' +
|
||||
'</div>' +
|
||||
'</article>'
|
||||
);
|
||||
}
|
||||
|
||||
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
|
||||
? '<div class="catalog-cards">' + cardsHtml + '</div>'
|
||||
: '<p class="command-form__hint" style="padding: var(--space-2) 0;">Ingen treff i denne kategorien.</p>';
|
||||
return (
|
||||
'<section class="expansion" aria-expanded="' + expanded + '" data-catalog-group="' + escapeAttr(cat.id) + '">' +
|
||||
'<button type="button" class="expansion__head" data-action="catalog-toggle-group">' +
|
||||
'<span class="expansion__title">' +
|
||||
'<span class="expansion__title-main">' + escapeHtml(cat.label) + '</span>' +
|
||||
'<span class="expansion__title-sub">' + subLabel + '</span>' +
|
||||
'</span>' +
|
||||
'<span class="expansion__chev" aria-hidden="true">▾</span>' +
|
||||
'</button>' +
|
||||
'<div class="expansion__body">' +
|
||||
'<div class="expansion__body-inner">' + body + '</div>' +
|
||||
'</div>' +
|
||||
'</section>'
|
||||
);
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function renderCatalogSurface() {
|
||||
const root = getSurfaceEl('catalog');
|
||||
if (!root) return;
|
||||
root.innerHTML = renderTopbar('Katalog') + '<div class="app-shell"><p>Command-katalog fylles i Step 9.</p></div>';
|
||||
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') +
|
||||
'<div class="app-shell app-shell--wide">' +
|
||||
'<header class="catalog-header">' +
|
||||
'<h1>Command-katalog</h1>' +
|
||||
'<p>24 commands gruppert i 5 kategorier. Åpne skjema for å bygge en pipeline-streng som kopieres til terminalen og kjøres med Claude Code.</p>' +
|
||||
'</header>' +
|
||||
'<div class="catalog-toolbar">' +
|
||||
'<input type="search" class="input" placeholder="Søk på navn, beskrivelse eller argument-hint…" value="' + escapeAttr(catalogSearchQuery) + '" data-catalog-search aria-label="Søk i katalog">' +
|
||||
'<span class="catalog-toolbar__count" data-catalog-count>' + countText + '</span>' +
|
||||
'</div>' +
|
||||
'<div class="catalog-groups" data-catalog-groups>' + renderCatalogGroupsHtml() + '</div>' +
|
||||
'</div>'
|
||||
);
|
||||
}
|
||||
|
||||
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
|
||||
? (
|
||||
'<div class="guide-panel guide-panel--info">' +
|
||||
'<div class="guide-panel__icon" aria-hidden="true">i</div>' +
|
||||
'<div class="guide-panel__body">' +
|
||||
'<h3 class="guide-panel__title">Verktøy</h3>' +
|
||||
'<p class="guide-panel__text">Dette er et verktøy. Skjemaet bygger en pipeline-streng — ingen rapport-import.</p>' +
|
||||
'</div>' +
|
||||
'</div>'
|
||||
)
|
||||
: '';
|
||||
return (
|
||||
'<div class="modal-backdrop" role="dialog" aria-modal="true" aria-labelledby="cf-modal-title">' +
|
||||
'<div class="modal modal--wide">' +
|
||||
'<div>' +
|
||||
'<h2 class="modal__title" id="cf-modal-title">' + escapeHtml(cmd.label) + '</h2>' +
|
||||
'<p class="catalog-card__desc" style="margin-top: var(--space-2);">' + escapeHtml(cmd.description) + '</p>' +
|
||||
'<span class="command-card__id">/architect:' + escapeHtml(cmd.id) + '</span>' +
|
||||
'</div>' +
|
||||
verktoyBanner +
|
||||
'<div>' + formHtml + '</div>' +
|
||||
'<div class="modal__actions">' +
|
||||
'<button type="button" class="btn btn--ghost" data-action="modal-cancel">Lukk</button>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>'
|
||||
);
|
||||
}
|
||||
|
||||
// ---- 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(); }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue