From 7cdb423707d06b2c4b592f5e27e5a4adf3534ea5 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 11 Jun 2026 07:30:34 +0000 Subject: [PATCH 1/8] docs(configurator): propose IA redesign plan (Basic/Advanced/Power tiers) Full proposal for reorganising the configurator's information architecture, inspired by the panel DX of Automatic.css 4.0 adapted to SLASHED's buildless model: - Basic mode becomes a curated per-project checklist: 11 main colors, fluid type/space generators, basic layout/border/shadow options - Quick Knobs (global multipliers) demoted out of Basic into a fenced 'Power knobs' group inside Advanced - Generators write fluid-engine scalars instead of baked clamp() output - Home/checklist screen, per-panel intro copy, friendly labels with raw-token info popovers - Five implementation phases with file-level targets https://claude.ai/code/session_01DCCWK2EPSRdhBDZ7f25NxT --- configurator/IA-REDESIGN.md | 219 ++++++++++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100644 configurator/IA-REDESIGN.md diff --git a/configurator/IA-REDESIGN.md b/configurator/IA-REDESIGN.md new file mode 100644 index 00000000..4a8f993e --- /dev/null +++ b/configurator/IA-REDESIGN.md @@ -0,0 +1,219 @@ +# Configurator IA redesign — Basic / Advanced / Power tiers + +Status: **proposal** (not yet implemented). +Companion docs: [`ROADMAP.md`](ROADMAP.md), [`../docs/architecture.md`](../docs/architecture.md). + +Inspiration: the panel DX of Automatic.css 4.0 — a flat home list of clearly +named categories, sub-pages with a breadcrumb, inline plain-language copy +explaining *what each feature does and whether you should touch it*, and the +dangerous stuff folded away. We copy the **DX**, not the mechanism: ACSS +regenerates a stylesheet via PHP on save; SLASHED is buildless, so every edit +is live `--sf-*` overrides with instant preview and a copy-paste export. That +is our structural advantage and the redesign should make it *feel* like one. + +--- + +## 1. Problem statement + +The current configurator is complete (all 841 tokens reachable) but chaotic +as a first-run experience: + +- **Quick Knobs open every Basic panel.** `--sf-space-scale` drives 45 tokens, + `--sf-shadow-strength` 14, `--sf-contrast-threshold` 11. These are atomic + power tools, not "first thing a new user should drag". They currently sit + *above* the essentials, in Basic mode. +- **Basic mode is a filter, not a curation.** It hides rows but still speaks + raw token names (`--sf-text-display-scale`), shows engine internals, and + renders 12 sidebar domains, most of which a typical project never opens + (Motion, Effects, Misc). +- **No guidance on "change vs leave".** Nothing tells the user that ~11 colors + + two fluid generators + a handful of layout/border/shadow values are the + entire per-project surface, and everything else has good defaults. +- **Generators bake output.** The scale generators write `clamp()` results + per step instead of the four engine scalars, which orphans the live fluid + engine and makes later tweaks harder. + +## 2. Design principles + +1. **Basic = the per-project checklist.** Only options that almost every + project changes from default. Everything else has a good default and is + *reachable*, not *visible*. +2. **Progressive disclosure in three tiers**, not two: + - **Basic** — curated, friendly-labelled forms. + - **Advanced** — the full token catalogue, grouped, raw names visible. + - **Power** — global multipliers and engine internals: present inside + Advanced but visually fenced (collapsed, warning chip, "drives N"). +3. **Friendly labels in Basic, raw tokens in Advanced.** Basic says "Content + width", not `--sf-container-default`; a hover/info affordance reveals the + token name so users graduate naturally. +4. **Defaults are a feature.** Every Basic control shows the framework + default and a one-click reset; untouched = visibly "using default" + (the ACSS toggle metaphor mapped onto our override model: toggle-off + ⇒ clear override, never "disable the token"). +5. **Write engine inputs, not baked outputs.** Generators and presets set + source scalars (`--sf-text-base-min`, ratios…) so the buildless fluid + engine stays live downstream. +6. **Educate in place.** Two sentences of intro copy per panel (ACSS-style), + "drives N" everywhere, and the planned dependency-hover ties the preview + back to tokens. + +## 3. The tier model + +Add a `surface` field per token: `basic | advanced | power` (orthogonal to +the existing PUBLIC / PUBLIC-ADVANCED / INTERNAL tier, which describes API +stability, not UI prominence). + +| Surface | What | Where it renders | +|---|---|---| +| `basic` | ~40 curated controls (below) | Basic mode forms + Advanced list | +| `advanced` | everything else public | Advanced list only | +| `power` | global multipliers, engine scalars, palette-mix %, contrast bias/threshold, per-step sub-properties, bridges, state flags | Advanced, inside a collapsed **Power knobs** group with warning styling | + +Mode toggle stays global (`B`/`A` shortcut preserved); search in Basic +reports "N more matches in Advanced" instead of silently hiding them. + +## 4. Basic mode — exact contents + +Sidebar in Basic shows **six domains + Themes**. Motion, Effects, WCAG, +Misc, Cheatsheet appear only in Advanced (tools stay keyboard-reachable). + +### 4.1 Colors +- The **11 main colors**, as today's BrandColorRow (light value, auto-dark + preview, optional dark override): + - Brand: `primary`, `secondary`, `tertiary`, `action`, `neutral`, `base` + - Status: `success`, `warning`, `error`, `info`, `danger` +- A read-only **derived swatch strip** per family (hover/subtle/shade ramp) + so users see what one color buys them — not editable in Basic. +- *Moved out:* `--sf-contrast-bias`, `--sf-contrast-threshold` → Power; + `--sf-focus-ring-width`, link/selection/mark colors, palette 50–950, + gradients → Advanced. + +### 4.2 Typography +- Font family: body, heading, mono (3 selects with stack presets + free text). +- **Fluid type generator** as the centrepiece: base size min/max, ratio + min/max as a *named-scale dropdown* (Minor third 1.2, Major third 1.25, + Perfect fourth 1.333, Golden 1.618…), live ramp preview. Apply writes the + four engine scalars — never per-step `clamp()`. +- Body line-height (`--sf-leading-normal`), heading weight. +- *Moved out:* `--sf-text-scale`, `--sf-text-display-scale` → Power; weights + table, tracking, per-step sub-properties, prose/heading aliases → Advanced. + +### 4.3 Spacing +- **Fluid space generator** (same pattern: base min/max + named ratio, + preview of the 2xs–4xl ramp, writes engine scalars). +- Section padding (`--sf-section-pad`), content gap, gutter. +- *Moved out:* `--sf-space-scale` (drives 45!), `--sf-section-scale` → Power; + per-step overrides, per-primitive gaps, bridges → Advanced. + +### 4.4 Layout +- Content width (`--sf-container-default`), reading width + (`--sf-container-prose`), narrow / wide containers. +- Header height, touch target. +- *Moved out:* grid mins, primitives' per-token config, ratios, z-index, + sticky offsets → Advanced. + +### 4.5 Borders +- Radius: `s` / `m` / `l` (+ a **style preset** row: Sharp / Subtle / + Rounded / Pill — each just patches the three radius tokens, fully undoable). +- Border width, divider width, border color (light/dark pair like ACSS). +- *Moved out:* `--sf-radius-scale` → Power; full width/outline/divider + catalogue, extra radius steps → Advanced. + +### 4.6 Shadows +- The four elevation steps `s` / `m` / `l` / `xl` (+ a **style preset** row: + None / Subtle / Soft / Strong patching the four tokens). +- *Moved out:* `--sf-shadow-strength` (with its dark-mode `calc()` + encode/decode) → Power; drop/text/glow shadows, shadow colors → Advanced. + +### 4.7 Themes (tool, both modes) +Preset gallery stays — it is the friendliest entry point and demonstrates +what overrides can do. "Save current" doubles as the project hand-off story. + +## 5. Advanced mode + +- All 12 domains, full catalogue, current filters (tier, usage, + modified-only, internal) unchanged. +- Each domain panel gains a **Power knobs** group at the *bottom* (not top): + collapsed by default, ⚡ chip, intro line — e.g. *"`--sf-space-scale` + multiplies all 45 fluid spacing tokens. Prefer the generator above unless + you want a uniform global squeeze."* — and the existing slider+number UI. +- QuickKnobs component is reused as-is inside that group; it stops rendering + in Basic entirely. + +## 6. Panel anatomy (ACSS-grade DX, every panel) + +1. **Header**: icon + name + modified-count badge. +2. **Intro copy**: 1–2 sentences — what this controls, whether typical + projects change it. Stored in `domains.js`. +3. **Groups as accordions** with plain-language names; first group open. +4. **Per-control**: friendly label, default ghost value, reset, "drives N" + badge, info popover revealing the raw token name + one-line description + (from `api-index`). +5. **Footer**: "Reset domain" + link to the relevant `docs/*.md`. + +No "Save" button — we keep live-edit + undo/redo + Output drawer. Instead a +persistent **"N tokens customised — Export CSS"** affordance in the header, +so the ACSS Save mental model maps onto our export step. + +## 7. Home / first-run + +A **Home screen** (Basic mode default landing, like the ACSS root list): +the six Basic domains + Themes as large rows with one-line descriptions and +per-domain customised-count — doubling as a *project setup checklist* +("Colors · 0 customised → start here"). Sidebar remains for switching; +Home is the orientation surface. + +## 8. What we deliberately do NOT copy from ACSS + +- **Save/build cycle** — buildless live preview is the point. +- **Feature on/off toggles that change shipped CSS** — bundle composition is + a loading-time decision (`optional/*.css`), documented, not a configurator + switch. (A future "bundle advisor" note in the Output drawer may *suggest* + dropping `tokens.palette.css` if untouched — suggestion only.) +- **Plugin/builder integration panels** (Bricks, Gutenberg, Class Manager) — + out of scope for a framework configurator. +- **Separate Light/Dark scheme pages** — `light-dark()` + auto-derivation + already collapse this; we keep the light/dark pair-per-row UI. + +## 9. Implementation phases + +### Phase 1 — tier metadata + knob demotion *(small, immediate win)* +- `lib/domains.js`: add `surface` map (curated lists live configurator-side; + no framework churn). Default unknown → `advanced`. +- Render QuickKnobs only in Advanced, moved to bottom "Power knobs" group. +- Basic sidebar filtered to 6 domains + Themes. +- Search across modes with "N more in Advanced" affordance. + +### Phase 2 — curated Basic forms +- `DomainPanel.svelte`: Basic branch renders curated controls with friendly + labels + intro copy; Advanced branch unchanged. +- Info popover (raw token name + description) on every Basic control. +- Colors panel: derived swatch strip per family. + +### Phase 3 — generators & presets +- `ScaleGenerator.svelte`: write engine scalars instead of per-step + `clamp()`; named-ratio dropdown; make it the hero of Typography/Spacing + Basic panels. +- Border & shadow **style preset rows** (token patches via + `patchOverrides`, single undo step). + +### Phase 4 — orientation & education +- Home screen / setup checklist. +- Per-panel intro copy + docs links. +- Dependency-graph hover (already roadmap #1) — closes the loop between + preview and tokens. + +### Phase 5 — sync & docs +- Optionally promote `surface` into `docs/api-index.json` via + `scripts/gen-api-index.js` once the curation stabilises, so docs and + configurator share one source of truth. +- Update `configurator/README.md` + ROADMAP; screenshot refresh. + +## 10. Open questions + +- Should **Motion** earn a Basic slot (single "reduce/disable motion" + switch)? Leaning no — default + `prefers-reduced-motion` already cover it. +- Status colors: collapse `error`/`danger` presentation in Basic (they are + distinct tokens) or show both? Proposal: show both, shared group header. +- Does Basic get the Output drawer diff view, or only "Export CSS"? + Proposal: full drawer in both modes — it is the product. From 91db9d2debce2e6a4c0dfaf913d048bd0c559bf8 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 11 Jun 2026 10:11:30 +0000 Subject: [PATCH 2/8] feat(configurator): demote quick knobs to Advanced, add Basic Home screen Phase 1 of the Basic/Advanced/Power IA restructure: - Quick Knobs no longer render in Basic; they move to a collapsed, warning-styled 'Power knobs' group at the bottom of each Advanced panel, with per-domain powerIntro copy explaining their reach - Basic sidebar shows only the per-project checklist (6 domains + Themes) plus a new Home row - New Home screen: setup checklist with per-domain customised counts and an 'Export CSS' shortcut into the output drawer - Basic search now renders curated-surface hits and offers an 'N more matches in Advanced' switch that preserves the query - Per-domain intro copy on Basic panels - Essentials sync fixes: +--sf-font-weight-heading, +--sf-color-border, --sf-space-content -> --sf-content-gap (canonical knob), --sf-radius-full demoted to Advanced - Output drawer expansion lifted into the shared ui store - domains tests extended: basic ids, intro/powerIntro presence, knob tokens verified against the catalogue https://claude.ai/code/session_01DCCWK2EPSRdhBDZ7f25NxT --- configurator/src/App.svelte | 24 ++- .../src/components/DomainPanel.svelte | 190 ++++++++++++++---- configurator/src/components/Home.svelte | 181 +++++++++++++++++ .../src/components/OutputPanel.svelte | 18 +- configurator/src/components/Sidebar.svelte | 34 +++- configurator/src/lib/domains.js | 43 +++- configurator/src/lib/store.svelte.js | 9 +- configurator/tests/domains.test.js | 46 ++++- 8 files changed, 486 insertions(+), 59 deletions(-) create mode 100644 configurator/src/components/Home.svelte diff --git a/configurator/src/App.svelte b/configurator/src/App.svelte index 8d5ebe20..63e66756 100644 --- a/configurator/src/App.svelte +++ b/configurator/src/App.svelte @@ -19,7 +19,7 @@ * State lives in the shared `ui` store; this component just wires the * active domain to its panel. */ - import { DOMAIN_BY_ID } from './lib/domains.js'; + import { DOMAINS, DOMAIN_BY_ID, BASIC_DOMAIN_IDS } from './lib/domains.js'; import { ui, undo, redo, overrides } from './lib/store.svelte.js'; import { setProbeContext } from './lib/probeHost.js'; import Header from './components/Header.svelte'; @@ -30,10 +30,22 @@ import WcagPanel from './components/WcagPanel.svelte'; import ThemeGallery from './components/ThemeGallery.svelte'; import Cheatsheet from './components/Cheatsheet.svelte'; + import Home from './components/Home.svelte'; + const home = $derived(ui.mode === 'basic' && ui.domain === 'home'); const domain = $derived(DOMAIN_BY_ID.get(ui.domain) ?? DOMAIN_BY_ID.get('colors')); const tool = $derived(domain?.tool ?? ''); + // Keep the active domain valid for the current mode: Home exists only in + // Basic, and Basic hides the non-checklist domains (Motion, Effects, …). + $effect(() => { + if (ui.mode === 'advanced' && ui.domain === 'home') { + ui.domain = 'colors'; + } else if (ui.mode === 'basic' && ui.domain !== 'home' && !BASIC_DOMAIN_IDS.includes(ui.domain)) { + ui.domain = 'home'; + } + }); + // Keep the contrast-probe host in sync with the user's cascade so every // ContrastBadge resolves `var(...)`, `light-dark()` and `oklch(from ...)` // expressions correctly. setProbeContext is internally idempotent. @@ -77,8 +89,10 @@ } else if (e.key === 'a' || e.key === 'A') { ui.mode = 'advanced'; } else if (e.key === '[' || e.key === ']') { - // Cycle non-tool domains. - const ids = ['colors', 'typography', 'spacing', 'layout', 'borders', 'shadows', 'motion', 'effects', 'wcag', 'themes', 'misc', 'cheatsheet']; + // Cycle the domains visible in the current mode. + const ids = ui.mode === 'basic' + ? ['home', ...BASIC_DOMAIN_IDS] + : DOMAINS.map((d) => d.id); const i = ids.indexOf(ui.domain); if (i !== -1) { const next = (i + (e.key === ']' ? 1 : -1) + ids.length) % ids.length; @@ -96,7 +110,9 @@
- {#if tool === 'wcag'} + {#if home} + + {:else if tool === 'wcag'} {:else if tool === 'themes'} diff --git a/configurator/src/components/DomainPanel.svelte b/configurator/src/components/DomainPanel.svelte index 09ca74ef..1807905c 100644 --- a/configurator/src/components/DomainPanel.svelte +++ b/configurator/src/components/DomainPanel.svelte @@ -2,11 +2,13 @@ /** * One domain tab (Colors, Typography, Spacing, …). * - * BASIC mode → curated essential rows + the domain's basic generator - * (e.g. fluid spacing scale). + * BASIC mode → intro copy + curated essential rows + the domain's basic + * generator (e.g. fluid spacing scale). No quick knobs — + * global multipliers are a power tool, not a starting point. * ADVANCED → essentials + every generator + the FULL domain catalogue * (grouped, searchable, tier-filtered, modified-only filter), - * so nothing is ever out of reach. + * capped by a collapsed "Power knobs" group for the global + * multipliers, so nothing is ever out of reach. * * Reuses the existing TokenRow / TokenEditor / OklchPicker / ScaleGenerator, * so each domain is just curation — no bespoke per-field code. @@ -21,7 +23,7 @@ import ScaleGenerator from './ScaleGenerator.svelte'; import QuickKnobs from './QuickKnobs.svelte'; - /** @type {{ domain: { id:string, label:string, icon:string, blurb:string, essentials?:string[], basicGenerators?:string[], advancedGenerators?:string[], brandColors?:boolean } }} */ + /** @type {{ domain: { id:string, label:string, icon:string, blurb:string, intro?:string, powerIntro?:string, essentials?:string[], basicGenerators?:string[], advancedGenerators?:string[], brandColors?:boolean } }} */ let { domain } = $props(); const advanced = $derived(ui.mode === 'advanced'); @@ -51,16 +53,30 @@ ); const grouped = $derived(groupTokens(advancedVisible)); - // Basic mode: when the user is searching, fall back to a flat filtered list - // of *every* domain token (so the search isn't hidden behind the toggle). - const basicSearchHits = $derived.by(() => { - if (!advanced && query) { - return domainTokens.filter((t) => { - if (t.tier === 'INTERNAL' && !ui.showInternal) return false; - return matchesQuery(t, query); - }); + // The curated Basic surface for this domain: essentials plus the brand + // light/dark pair tokens. Basic search renders matches from this set only; + // everything else is one click away via the "more in Advanced" affordance. + const basicSurface = $derived.by(() => { + const s = new Set(domain.essentials ?? []); + if (domain.brandColors) { + for (const { key } of BRAND_COLOR_KEYS) { + s.add(`--sf-color-${key}-light`); + s.add(`--sf-color-${key}-dark`); + } } - return null; + return s; + }); + + // Basic mode + active search: matches across the whole domain, split into + // curated-surface hits (rendered) and the advanced-only remainder (count). + const basicSearch = $derived.by(() => { + if (advanced || !query) return null; + const hits = domainTokens.filter((t) => { + if (t.tier === 'INTERNAL' && !ui.showInternal) return false; + return matchesQuery(t, query); + }); + const shown = hits.filter((t) => basicSurface.has(t.name)); + return { shown, hiddenCount: hits.length - shown.length }; }); // Only label each group's category when more than one is present in this @@ -69,9 +85,11 @@ const basicGenerators = $derived(domain.basicGenerators ?? []); - // Quick knobs for this domain (global multipliers that cascade through - // many derived tokens — see lib/domains.js → KNOBS_BY_DOMAIN). + // Power knobs for this domain (global multipliers that cascade through + // many derived tokens — see lib/domains.js → KNOBS_BY_DOMAIN). Advanced + // only: they sit in a collapsed, visually fenced group below the catalogue. const knobs = $derived(KNOBS_BY_DOMAIN[domain.id] ?? []); + const totalDriven = $derived(knobs.reduce((n, k) => n + (k.driving ?? 1), 0)); // Modified tokens within this domain — surfaced as a collapsible block. const modifiedHere = $derived( @@ -140,35 +158,42 @@
- {#if basicSearchHits} - - {#if basicSearchHits.length === 0} + {#if basicSearch} + + {#if basicSearch.shown.length === 0 && basicSearch.hiddenCount === 0}

No {domain.label.toLowerCase()} tokens match {ui.query}. Try Advanced mode to widen filters.

{:else} -
-
- Search results - {basicSearchHits.length} -
-
- {#each basicSearchHits as token (token.name)} - - {/each} -
-
+ {#if basicSearch.shown.length} +
+
+ Search results + {basicSearch.shown.length} +
+
+ {#each basicSearch.shown as token (token.name)} + + {/each} +
+
+ {/if} + {#if basicSearch.hiddenCount > 0} + + {/if} {/if} {:else} - - {#if knobs.length} - + + {#if !advanced && domain.intro} +

{domain.intro}

{/if} @@ -227,6 +252,22 @@ {/each} {/each} {/if} + + + {#if knobs.length} +
+ + + Power knobs + drives {totalDriven} token{totalDriven === 1 ? '' : 's'} + + {#if domain.powerIntro} +

{domain.powerIntro}

+ {/if} + +
+ {/if} {:else if !domain.brandColors && essentials.length === 0 && basicGenerators.length === 0} @@ -334,6 +375,81 @@ color: var(--cfg-text-faint); } + .panel__intro { + margin: 0; + padding: 10px 14px; + font-size: 13px; + line-height: 1.55; + color: var(--cfg-text-muted); + background: var(--cfg-surface); + border: 1px solid var(--cfg-border); + border-left: 3px solid var(--cfg-accent-strong); + border-radius: var(--cfg-radius-s); + } + + .panel__more { + align-self: center; + padding: 8px 16px; + font-size: 12.5px; + color: var(--cfg-text-muted); + background: var(--cfg-surface-2); + border: 1px dashed var(--cfg-border-strong); + border-radius: var(--cfg-radius-s); + cursor: pointer; + transition: color 0.12s, border-color 0.12s; + } + .panel__more:hover { + color: var(--cfg-text); + border-color: var(--cfg-accent-strong); + } + + /* Power knobs — visually fenced: these multipliers cascade through dozens + of tokens, so the group reads as "handle with intent". */ + .power { + border: 1px solid rgba(240, 173, 78, 0.45); + border-radius: var(--cfg-radius); + background: rgba(240, 173, 78, 0.06); + overflow: clip; + } + .power__summary { + display: flex; + align-items: center; + gap: 10px; + padding: 12px 16px; + cursor: pointer; + list-style: none; + user-select: none; + } + .power__summary::-webkit-details-marker { display: none; } + .power__summary:hover { background: rgba(240, 173, 78, 0.1); } + .power__bolt { font-size: 14px; } + .power__title { + font-size: 12.5px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.06em; + } + .power__chip { + font-family: var(--cfg-mono); + font-size: 10.5px; + color: rgb(240, 173, 78); + border: 1px solid rgba(240, 173, 78, 0.45); + border-radius: 999px; + padding: 1px 8px; + } + .power__intro { + margin: 0; + padding: 0 16px 10px; + font-size: 12.5px; + line-height: 1.55; + color: var(--cfg-text-muted); + } + .power > :global(.knobs) { + border: 0; + border-top: 1px solid rgba(240, 173, 78, 0.25); + border-radius: 0; + } + .panel__empty, .panel__hint { color: var(--cfg-text-faint); diff --git a/configurator/src/components/Home.svelte b/configurator/src/components/Home.svelte new file mode 100644 index 00000000..9833c631 --- /dev/null +++ b/configurator/src/components/Home.svelte @@ -0,0 +1,181 @@ + + +
+
+

Set up your design

+

+ Work through the panels below — they cover everything most projects + change from the framework defaults. Everything else already has a good + default and stays reachable in Advanced mode + (A). +

+ {#if totalMods > 0} + + {/if} +
+ +
    + {#each rows as d, i (d.id)} +
  • + +
  • + {/each} +
+
+ + diff --git a/configurator/src/components/OutputPanel.svelte b/configurator/src/components/OutputPanel.svelte index 42f3aea6..682ba445 100644 --- a/configurator/src/components/OutputPanel.svelte +++ b/configurator/src/components/OutputPanel.svelte @@ -33,7 +33,7 @@ let importOpen = $state(false); let importText = $state(''); let importMsg = $state(''); - let drawerOpen = $state(true); + // Drawer expansion lives in the shared ui store so Home / Header can open it. /** 'css' = generated stylesheet · 'diff' = before/after token list. */ let view = $state('css'); @@ -87,18 +87,18 @@ const cssHtml = $derived(highlight(css || '/* No overrides yet — edit a token to generate CSS. */')); -
+
@@ -144,7 +144,7 @@
- {#if drawerOpen} + {#if ui.outputOpen} {#if importOpen}