From 0dc7ff485fb368991d042cf0571ef8a24fdfd58e Mon Sep 17 00:00:00 2001 From: Kjell Tore Guttormsen Date: Mon, 18 May 2026 11:56:44 +0200 Subject: [PATCH] =?UTF-8?q?feat(llm-security):=20playground=20v7.6.2-dev?= =?UTF-8?q?=20=E2=80=94=20katalog=20list-view=20+=20builder-pane=20[skip-d?= =?UTF-8?q?ocs]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - renderCatalogSurface rewritten to list-view (1 rad per kommando), filter-chips (Alle/Rapport/Verktoy + 6 kategori-chips) + sok - Builder-pane (modal) med live-preview: pipeline-strengen oppdateres mens skjema fylles ut. Kopier-knapp er primaer CTA med clipboard API + textarea-fallback for file:// (allerede eksisterende). - Smart prefill fra store.state.shared via 'from: shared' fields i renderCommandForm. Pane-state skriver ikke tilbake til shared (scope 'cat', ingen project-save). Felles-felt markert med 'felles'-badge. - Forstegangsbesok lander pa home (fjernet onboarding auto-redirect). Re-onboard tilgjengelig via topbar. Sesjon 1 av 5 i v7.7.0-lopet. CSS-additioner: catalog-filter-chips, catalog-chip, catalog-list, catalog-row, builder-modal. Tester: 1822/1822 gronne. Static JS-parse OK. Browser-walkthrough gjenstar — verifiseres manuelt for v7.7.0 release. Docs oppdateres ved v7.7.0-release i Sesjon 5 (samlet commit). Co-Authored-By: Claude Opus 4.7 --- .../playground/llm-security-playground.html | 229 +++++++++++++----- 1 file changed, 162 insertions(+), 67 deletions(-) diff --git a/plugins/llm-security/playground/llm-security-playground.html b/plugins/llm-security/playground/llm-security-playground.html index 584473f..807bc4a 100644 --- a/plugins/llm-security/playground/llm-security-playground.html +++ b/plugins/llm-security/playground/llm-security-playground.html @@ -112,7 +112,7 @@ .visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; } /* Catalog */ - .catalog-search { width: 100%; max-width: 480px; margin-bottom: var(--space-5); } + .catalog-search { width: 100%; max-width: 480px; margin-bottom: 0; } .catalog-cards-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: var(--space-3); margin-top: var(--space-3); } .catalog-tool-notice { padding: 8px 12px; background: var(--color-bg-soft); border-left: 3px solid var(--color-state-info, var(--color-primary-300)); font-size: var(--font-size-xs); color: var(--color-text-secondary); border-radius: var(--radius-sm); } /* Expansion-body: playground-markup mangler .expansion__body-inner-wrapping @@ -121,6 +121,42 @@ .expansion__body { padding: 0 var(--space-4) var(--space-4); border-top: 1px solid var(--color-border-subtle); } .expansion[aria-expanded="false"] .expansion__body { display: none; } + /* Catalog v7.6.2: filter-chips + list-view + builder-pane */ + .catalog-filter-chips { display: flex; flex-wrap: wrap; gap: var(--space-2); margin: 0; } + .catalog-chip { font-family: inherit; font-size: var(--font-size-xs); font-weight: var(--font-weight-medium); padding: 6px 12px; border-radius: var(--radius-pill); border: 1px solid var(--color-border-moderate); background: var(--color-surface); color: var(--color-text-secondary); cursor: pointer; transition: background 120ms ease, border-color 120ms ease, color 120ms ease; display: inline-flex; align-items: center; gap: 6px; } + .catalog-chip:hover { border-color: var(--color-border-strong); color: var(--color-text-primary); } + .catalog-chip:focus-visible { outline: 2px solid var(--color-scope-security, var(--color-primary-500)); outline-offset: 2px; } + .catalog-chip--active { background: var(--color-scope-security, var(--color-primary-500)); border-color: var(--color-scope-security, var(--color-primary-500)); color: var(--color-text-on-primary, #fff); } + .catalog-chip--active:hover { color: var(--color-text-on-primary, #fff); } + .catalog-chip__count { font-size: 10px; opacity: 0.85; padding: 1px 6px; border-radius: var(--radius-pill); background: rgba(0,0,0,0.08); } + .catalog-chip--active .catalog-chip__count { background: rgba(255,255,255,0.18); } + + .catalog-list { display: flex; flex-direction: column; gap: 1px; background: var(--color-border-subtle); border: 1px solid var(--color-border-subtle); border-radius: var(--radius-md); overflow: hidden; } + .catalog-row { display: flex; align-items: stretch; gap: var(--space-4); padding: var(--space-3) var(--space-4); background: var(--color-surface); border: 0; text-align: left; font-family: inherit; cursor: pointer; transition: background 120ms ease; width: 100%; } + .catalog-row:hover { background: var(--color-bg-soft); } + .catalog-row:focus-visible { outline: 2px solid var(--color-scope-security, var(--color-primary-500)); outline-offset: -2px; } + .catalog-row__main { display: flex; flex-direction: column; gap: 4px; flex: 1; min-width: 0; } + .catalog-row__head { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-2); } + .catalog-row__id { font-family: var(--font-family-mono); font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold); color: var(--color-text-primary); } + .catalog-row__label { font-size: var(--font-size-sm); color: var(--color-text-secondary); } + .catalog-row__desc { font-size: var(--font-size-xs); color: var(--color-text-tertiary); line-height: var(--line-height-snug); } + .catalog-row__hint { font-family: var(--font-family-mono); font-size: 11px; color: var(--color-text-tertiary); background: var(--color-bg-soft); padding: 1px 6px; border-radius: var(--radius-sm); } + .catalog-row__meta { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; flex-shrink: 0; font-size: var(--font-size-xs); } + .catalog-row__category { font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--color-text-tertiary); font-weight: var(--font-weight-semibold); } + .catalog-row__fields { color: var(--color-text-tertiary); } + .catalog-empty { padding: var(--space-5); text-align: center; color: var(--color-text-tertiary); border: 1px dashed var(--color-border-subtle); border-radius: var(--radius-md); } + + /* Builder-pane: bigger modal, layout-tuned for live-preview workflow */ + .builder-modal { max-width: 880px; } + .builder-modal__lede { color: var(--color-text-secondary); margin: 0; font-size: var(--font-size-sm); } + .builder-modal .form-preview { background: var(--color-bg-soft); border: 1px solid var(--color-border-subtle); } + .builder-modal__hint { font-size: var(--font-size-xs); color: var(--color-text-tertiary); margin: 0; } + + @media (max-width: 720px) { + .catalog-row { flex-direction: column; gap: var(--space-2); } + .catalog-row__meta { flex-direction: row; align-items: center; } + } + /* Modal (playground-only — DS har ikke modal-pattern enda) */ .modal-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; z-index: 100; padding: var(--space-5); } .modal { background: var(--color-surface); border-radius: var(--radius-md); max-width: 720px; width: 100%; max-height: 90vh; overflow: auto; padding: var(--space-5); display: flex; flex-direction: column; gap: var(--space-4); } @@ -5694,10 +5730,9 @@ window.__store = store; window.__persistence = persistence; - // Initial-surface heuristikk - const orgName = store.state.shared && store.state.shared.organization && store.state.shared.organization.name; - if (!orgName) store.state.activeSurface = 'onboarding'; - else if (!store.state.activeSurface) store.state.activeSurface = 'home'; + // Initial-surface heuristikk: førstegangsbesøk lander på home. + // Re-onboard er tilgjengelig via topbar når brukeren ønsker det. + if (!store.state.activeSurface) store.state.activeSurface = 'home'; scheduleRender(); } @@ -6875,6 +6910,7 @@ // CATALOG SURFACE // ============================================================ let catalogSearchQuery = ''; + let catalogFilter = 'all'; // 'all' | 'report' | 'tool' | function catalogMatches(cmd, q) { if (!q) return true; @@ -6882,68 +6918,88 @@ return hay.indexOf(q) >= 0; } - function renderCatalogCardHtml(cmd) { + function categoryLabelById(id) { + const c = (CATALOG.categories || []).find(function (x) { return x.id === id; }); + return c ? c.label : id; + } + + function filteredCatalogCommands() { + const q = catalogSearchQuery.toLowerCase().trim(); + return (CATALOG.commands || []).filter(function (c) { + if (!catalogMatches(c, q)) return false; + if (catalogFilter === 'all') return true; + if (catalogFilter === 'report') return !!c.produces_report; + if (catalogFilter === 'tool') return !c.produces_report; + return c.category === catalogFilter; + }); + } + + function renderCatalogRowHtml(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.
' : ''; + const hintHtml = cmd.argument_hint ? ' ' + escapeHtml(cmd.argument_hint) + '' : ''; + const fieldCount = (cmd.input_fields || []).length; + const fieldLabel = fieldCount + ' felt' + (fieldCount === 1 ? '' : 'er'); return ( - '
' + - '
' + - '
' + - '

' + escapeHtml(cmd.label) + '

' + - '

' + escapeHtml(cmd.description) + '

' + + '
' + - '
' + - 'llm-security' + - pill + - '
' + - '
' + - verktoyNotice + - '
' + - '' + - '' + (cmd.input_fields || []).length + ' felter' + - '
' + - '
' + '' + + '' + escapeHtml(cmd.description) + '' + + '' + + '' + + '' + escapeHtml(categoryLabelById(cmd.category)) + '' + + pill + + '' + fieldLabel + '' + + '' + + '' ); } - function renderCatalogGroupsHtml() { - const q = catalogSearchQuery.toLowerCase().trim(); - return CATALOG.categories.map(function (cat) { - const cmds = CATALOG.commands.filter(function (c) { return c.category === cat.id && catalogMatches(c, q); }); - if (cmds.length === 0 && q) return ''; // skjul tomme grupper ved aktiv søk - const isOpen = q !== '' || cat.id === 'discover'; // discover åpen som default - const cardsHtml = cmds.length > 0 - ? '
' + cmds.map(renderCatalogCardHtml).join('') + '
' - : '

Ingen kommandoer i denne kategorien.

'; + function renderCatalogChipsHtml() { + const total = (CATALOG.commands || []).length; + const reportCount = (CATALOG.commands || []).filter(function (c) { return c.produces_report; }).length; + const toolCount = total - reportCount; + const baseChips = [ + { id: 'all', label: 'Alle', count: total }, + { id: 'report', label: 'Rapport-produserende', count: reportCount }, + { id: 'tool', label: 'Verktøy', count: toolCount } + ]; + const categoryChips = (CATALOG.categories || []).map(function (cat) { + return { id: cat.id, label: cat.label, count: cat.count }; + }); + return baseChips.concat(categoryChips).map(function (chip) { + const active = catalogFilter === chip.id; return ( - '
' + - '' + - '
' + cardsHtml + '
' + - '
' + '' ); }).join(''); } + function renderCatalogListBodyHtml() { + const cmds = filteredCatalogCommands(); + if (cmds.length === 0) { + return '
Ingen treff. Prøv et annet søk eller filter.
'; + } + return '
' + cmds.map(renderCatalogRowHtml).join('') + '
'; + } + function renderCatalogSurface() { const root = getSurfaceEl('catalog'); if (!root) return; - const total = CATALOG.commands.length; - const reportCount = CATALOG.commands.filter(function (c) { return c.produces_report; }).length; + const total = (CATALOG.commands || []).length; + const reportCount = (CATALOG.commands || []).filter(function (c) { return c.produces_report; }).length; const toolCount = total - reportCount; const catalogShell = renderPageShell({ eyebrow: 'KATALOG', title: 'Command-katalog', - lede: 'Alle ' + total + ' kommandoer gruppert på kategori. Bygg pipeline-strenger uten et aktivt prosjekt.', + lede: 'Alle ' + total + ' kommandoer. Søk, filtrer, klikk en rad for å bygge kommandostrengen.', verdict: 'n-a', meta: [ total + ' kommandoer', @@ -6958,7 +7014,8 @@ }, '
' + '' + - '
' + renderCatalogGroupsHtml() + '
' + + '
' + renderCatalogChipsHtml() + '
' + + '
' + renderCatalogListBodyHtml() + '
' + '
' ); @@ -6966,12 +7023,6 @@ renderTopbar('Katalog') + '
' + catalogShell + '
' ); - - // Bevarer fokus i søkefeltet under re-render - const searchEl = root.querySelector('[data-catalog-search]'); - if (searchEl && document.activeElement !== searchEl && catalogSearchQuery) { - // Ikke stjel fokus med mindre brukeren akkurat skrev — håndteres i action handler - } } // ============================================================ @@ -10406,13 +10457,20 @@ function renderCatalogFormModal(cmd) { const formHtml = renderCommandForm(cmd.id, { scope: 'cat' }); + const sharedCount = (cmd.input_fields || []).filter(function (f) { return f.from === 'shared'; }).length; + const sharedHint = sharedCount > 0 + ? '

Felt merket felles er forhåndsutfylt fra onboarding (' + sharedCount + ' av ' + (cmd.input_fields || []).length + '). Endringer her påvirker ikke onboarding-state.

' + : '

Fyll ut argumenter — pipeline-strengen oppdateres mens du skriver.

'; return ( - '