feat(llm-security): playground v7.6.2-dev — katalog list-view + builder-pane [skip-docs]
- 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 <noreply@anthropic.com>
This commit is contained in:
parent
69610d46bd
commit
0dc7ff485f
1 changed files with 162 additions and 67 deletions
|
|
@ -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' | <category-id>
|
||||
|
||||
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 ? '<span class="card__pill">Verktøy</span>' : '<span class="card__pill">Rapport</span>';
|
||||
const hintHtml = cmd.argument_hint ? '<span class="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>' : '';
|
||||
const hintHtml = cmd.argument_hint ? ' <code class="catalog-row__hint">' + escapeHtml(cmd.argument_hint) + '</code>' : '';
|
||||
const fieldCount = (cmd.input_fields || []).length;
|
||||
const fieldLabel = fieldCount + ' felt' + (fieldCount === 1 ? '' : 'er');
|
||||
return (
|
||||
'<article class="card" data-command-card data-command-id="' + escapeAttr(cmd.id) + '">' +
|
||||
'<div class="card__head">' +
|
||||
'<div>' +
|
||||
'<h3 class="card__title">' + escapeHtml(cmd.label) + '</h3>' +
|
||||
'<p class="card__desc">' + escapeHtml(cmd.description) + '</p>' +
|
||||
'<button type="button" class="catalog-row" data-action="catalog-open-form" data-command="' + escapeAttr(cmd.id) + '" aria-label="Åpne builder for ' + escapeAttr(cmd.label) + '">' +
|
||||
'<span class="catalog-row__main">' +
|
||||
'<span class="catalog-row__head">' +
|
||||
'<span class="catalog-row__id">/security:' + escapeHtml(cmd.id) + '</span>' +
|
||||
'<span class="catalog-row__label">' + escapeHtml(cmd.label) + '</span>' +
|
||||
hintHtml +
|
||||
'</div>' +
|
||||
'<div style="display:flex; flex-direction:column; gap:6px; align-items:flex-end;">' +
|
||||
'<span class="badge badge--scope-security">llm-security</span>' +
|
||||
pill +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
verktoyNotice +
|
||||
'<div class="card__actions">' +
|
||||
'<button type="button" class="btn btn--primary btn--sm" data-action="catalog-open-form" data-command="' + escapeAttr(cmd.id) + '">Åpne skjema</button>' +
|
||||
'<span style="font-size: var(--font-size-xs); color: var(--color-text-tertiary);">' + (cmd.input_fields || []).length + ' felter</span>' +
|
||||
'</div>' +
|
||||
'</article>'
|
||||
'</span>' +
|
||||
'<span class="catalog-row__desc">' + escapeHtml(cmd.description) + '</span>' +
|
||||
'</span>' +
|
||||
'<span class="catalog-row__meta">' +
|
||||
'<span class="catalog-row__category">' + escapeHtml(categoryLabelById(cmd.category)) + '</span>' +
|
||||
pill +
|
||||
'<span class="catalog-row__fields">' + fieldLabel + '</span>' +
|
||||
'</span>' +
|
||||
'</button>'
|
||||
);
|
||||
}
|
||||
|
||||
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
|
||||
? '<div class="catalog-cards-grid">' + cmds.map(renderCatalogCardHtml).join('') + '</div>'
|
||||
: '<p style="color: var(--color-text-tertiary); margin: var(--space-3) 0;">Ingen kommandoer i denne kategorien.</p>';
|
||||
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 (
|
||||
'<div class="expansion" aria-expanded="' + (isOpen ? 'true' : 'false') + '">' +
|
||||
'<button type="button" class="expansion__head" data-action="catalog-toggle-group" data-group="' + escapeAttr(cat.id) + '">' +
|
||||
'<span class="expansion__title">' +
|
||||
'<span class="expansion__title-main">' + escapeHtml(cat.label) + '</span>' +
|
||||
'<span class="expansion__title-sub">' + cmds.length + ' av ' + cat.count + ' kommandoer' + (q ? ' (filtrert)' : '') + '</span>' +
|
||||
'</span>' +
|
||||
'<span class="expansion__chev" aria-hidden="true">▾</span>' +
|
||||
'</button>' +
|
||||
'<div class="expansion__body">' + cardsHtml + '</div>' +
|
||||
'</div>'
|
||||
'<button type="button" class="catalog-chip' + (active ? ' catalog-chip--active' : '') + '" data-action="catalog-filter" data-filter="' + escapeAttr(chip.id) + '" aria-pressed="' + (active ? 'true' : 'false') + '">' +
|
||||
escapeHtml(chip.label) + ' <span class="catalog-chip__count">' + chip.count + '</span>' +
|
||||
'</button>'
|
||||
);
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function renderCatalogListBodyHtml() {
|
||||
const cmds = filteredCatalogCommands();
|
||||
if (cmds.length === 0) {
|
||||
return '<div class="catalog-empty">Ingen treff. Prøv et annet søk eller filter.</div>';
|
||||
}
|
||||
return '<div class="catalog-list">' + cmds.map(renderCatalogRowHtml).join('') + '</div>';
|
||||
}
|
||||
|
||||
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 @@
|
|||
},
|
||||
'<div class="stack-lg">' +
|
||||
'<input type="search" class="input catalog-search" placeholder="Søk i kommandoer (id, label, beskrivelse, argument-hint) …" data-catalog-search value="' + escapeAttr(catalogSearchQuery) + '" aria-label="Søk i kommando-katalogen">' +
|
||||
'<div data-catalog-groups>' + renderCatalogGroupsHtml() + '</div>' +
|
||||
'<div class="catalog-filter-chips" role="group" aria-label="Filtre">' + renderCatalogChipsHtml() + '</div>' +
|
||||
'<div data-catalog-list>' + renderCatalogListBodyHtml() + '</div>' +
|
||||
'</div>'
|
||||
);
|
||||
|
||||
|
|
@ -6966,12 +7023,6 @@
|
|||
renderTopbar('Katalog') +
|
||||
'<div class="app-shell">' + catalogShell + '</div>'
|
||||
);
|
||||
|
||||
// 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
|
||||
? '<p class="builder-modal__hint">Felt merket <span class="field-from-tag" style="cursor:default;">felles</span> er forhåndsutfylt fra onboarding (' + sharedCount + ' av ' + (cmd.input_fields || []).length + '). Endringer her påvirker ikke onboarding-state.</p>'
|
||||
: '<p class="builder-modal__hint">Fyll ut argumenter — pipeline-strengen oppdateres mens du skriver.</p>';
|
||||
return (
|
||||
'<div class="modal" role="dialog" aria-labelledby="cf-title">' +
|
||||
'<div class="modal builder-modal" role="dialog" aria-labelledby="cf-title" data-builder-pane>' +
|
||||
'<div class="modal__head">' +
|
||||
'<h2 id="cf-title" class="modal__title">' + escapeHtml(cmd.label) + '</h2>' +
|
||||
'<div>' +
|
||||
'<h2 id="cf-title" class="modal__title">' + escapeHtml(cmd.label) + ' <span style="font-family: var(--font-family-mono); font-size: var(--font-size-md); color: var(--color-text-tertiary); font-weight: var(--font-weight-regular);">/security:' + escapeHtml(cmd.id) + '</span></h2>' +
|
||||
'</div>' +
|
||||
'<button type="button" class="modal__close" data-action="close-modal" aria-label="Lukk">×</button>' +
|
||||
'</div>' +
|
||||
'<p style="color: var(--color-text-secondary); margin: 0;">' + escapeHtml(cmd.description) + '</p>' +
|
||||
'<p class="builder-modal__lede">' + escapeHtml(cmd.description) + '</p>' +
|
||||
sharedHint +
|
||||
formHtml +
|
||||
'</div>'
|
||||
);
|
||||
|
|
@ -10589,18 +10647,36 @@
|
|||
}
|
||||
|
||||
// Catalog
|
||||
if (action === 'catalog-toggle-group') {
|
||||
const grp = target.dataset.group;
|
||||
const exp = target.closest('.expansion');
|
||||
if (exp) {
|
||||
const open = exp.getAttribute('aria-expanded') === 'true';
|
||||
exp.setAttribute('aria-expanded', open ? 'false' : 'true');
|
||||
if (action === 'catalog-filter') {
|
||||
const f = target.dataset.filter || 'all';
|
||||
if (catalogFilter === f) return;
|
||||
catalogFilter = f;
|
||||
// Re-render in-place: chips (active state) + list body
|
||||
const root = getSurfaceEl('catalog');
|
||||
if (root) {
|
||||
const chipsEl = root.querySelector('.catalog-filter-chips');
|
||||
if (chipsEl) chipsEl.innerHTML = renderCatalogChipsHtml();
|
||||
const listEl = root.querySelector('[data-catalog-list]');
|
||||
if (listEl) listEl.innerHTML = renderCatalogListBodyHtml();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (action === 'catalog-open-form') {
|
||||
const cmd = (CATALOG.commands || []).find(function (c) { return c.id === cmdId; });
|
||||
if (cmd) openModal(renderCatalogFormModal(cmd));
|
||||
if (!cmd) return;
|
||||
openModal(renderCatalogFormModal(cmd));
|
||||
// Initial live-preview: vis pipeline-streng med shared-prefill
|
||||
const formEl = document.querySelector('[data-builder-pane] form.command-form[data-command-form="' + CSS.escape(cmd.id) + '"]');
|
||||
if (formEl) {
|
||||
const data = readCommandFormValues(formEl);
|
||||
const str = buildCommand(cmd.id, data);
|
||||
showCommandPreview(formEl, str);
|
||||
// Auto-fokus første input for keyboard-flow
|
||||
const firstInput = formEl.querySelector('input:not([type="hidden"]), select, textarea');
|
||||
if (firstInput) {
|
||||
try { firstInput.focus(); } catch (e) { /* ignore */ }
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -10677,14 +10753,24 @@
|
|||
if (ev.key === 'Escape') closeModal();
|
||||
});
|
||||
|
||||
// Catalog search
|
||||
// Catalog search + live builder-pane preview
|
||||
document.addEventListener('input', function (ev) {
|
||||
if (ev.target && ev.target.matches && ev.target.matches('[data-catalog-search]')) {
|
||||
catalogSearchQuery = ev.target.value;
|
||||
const groupsEl = document.querySelector('[data-catalog-groups]');
|
||||
if (groupsEl) groupsEl.innerHTML = renderCatalogGroupsHtml();
|
||||
const listEl = document.querySelector('[data-catalog-list]');
|
||||
if (listEl) listEl.innerHTML = renderCatalogListBodyHtml();
|
||||
return;
|
||||
}
|
||||
// Live preview inside builder-pane (catalog modal)
|
||||
if (ev.target && ev.target.matches && ev.target.matches('[data-builder-pane] [data-cf-field]')) {
|
||||
const formEl = ev.target.closest('form.command-form');
|
||||
if (formEl) {
|
||||
const data = readCommandFormValues(formEl);
|
||||
const str = buildCommand(formEl.dataset.commandForm, data);
|
||||
showCommandPreview(formEl, str);
|
||||
}
|
||||
// Fall through to onboarding handling below in case selector overlaps
|
||||
}
|
||||
// Onboarding fields persist on input (debounced via throttledWriter)
|
||||
if (ev.target && ev.target.matches && ev.target.matches('[data-onboarding-field]')) {
|
||||
const path = ev.target.dataset.cfField;
|
||||
|
|
@ -10716,6 +10802,15 @@
|
|||
scheduleRender();
|
||||
}
|
||||
}
|
||||
// Builder-pane: select/checkbox change → live preview
|
||||
if (ev.target && ev.target.matches && ev.target.matches('[data-builder-pane] [data-cf-field]')) {
|
||||
const formEl = ev.target.closest('form.command-form');
|
||||
if (formEl) {
|
||||
const data = readCommandFormValues(formEl);
|
||||
const str = buildCommand(formEl.dataset.commandForm, data);
|
||||
showCommandPreview(formEl, str);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Import file picker
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue