mirror of
https://github.com/olehomelchenko/astrolabe.git
synced 2026-08-08 02:02:33 +00:00
Theme Builder: structured color/type controls, scheme catalog, diverging preview
This commit is contained in:
@@ -236,6 +236,30 @@ key-by-key), so a snippet can always override or opt out locally. The
|
||||
that boundary deliberately: merge bakes the selected theme into `spec.config`
|
||||
(spec keys win — rendering unchanged), extract lifts `spec.config` out.
|
||||
|
||||
### Structured controls
|
||||
|
||||
The builder's panels (`ColorControls`; the Type tab's font control) are
|
||||
accelerators over the same `draftConfig`: each reads a value and writes one back
|
||||
through `CustomThemeStore.mutateDraftConfig(fn)` — the single transform path,
|
||||
which reparses, reformats, and updates `draftConfig` so the JSON editor and
|
||||
gallery follow (a parse error disables the controls). The pure transforms live
|
||||
in `core/theme-controls.ts`: immutable config path get/set, the named-scheme
|
||||
catalog (`THEME_SCHEMES`), and `schemeColors` (scheme name → hex swatches, from
|
||||
the `vega-scale` registry — a focused vega sub-package). A color family holds
|
||||
**either** a named scheme string **or** an explicit color array; the picker
|
||||
materializes one to the other. Family by scale: `range.category` (nominal),
|
||||
`range.ramp` (continuous; `range.heatmap` for `rect`), `range.diverging`
|
||||
(continuous color with a `domainMid`).
|
||||
|
||||
- **Do** route structured edits through `mutateDraftConfig` + `setConfigValue`,
|
||||
which sets a value at a path **immutably, preserving sibling keys**, and
|
||||
deletes (pruning emptied ancestors) on `undefined` so a theme stays a diff.
|
||||
- **Don't** rebuild the config from a fixed schema: vega-themes presets carry
|
||||
Vega-_layer_ keys (`symbol`/`shape`/`path`/`group`) absent from the Vega-Lite
|
||||
`Config` schema but forwarded to Vega — a rebuild drops them. Merge in place.
|
||||
- **Do** add a `theme-preview-specs.ts` gallery card for any new color family,
|
||||
so no control ships without a visible mirror.
|
||||
|
||||
### Rules
|
||||
|
||||
- **Do** keep `chartConfigForSelection` as the _only_ place that maps the user's
|
||||
|
||||
@@ -165,7 +165,80 @@ ships, it is an explicit per-font user action, never automatic.
|
||||
**Rejected:** per-snippet theme field (2026-06-12 — `spec.config` + merge/extract covers
|
||||
it without a second mechanism).
|
||||
|
||||
## 5. Status log
|
||||
## 5. Structured controls (slice 4b)
|
||||
|
||||
The builder today is a raw JSON textarea + one font dropdown + the live gallery. Slice 4b
|
||||
adds a strip of structured controls above the editor — accelerators that write into the
|
||||
JSON, never replacing it. The JSON stays the source of truth and the full-power escape
|
||||
hatch; controls cover the common ~80% (color, type, spacing, grid), not all 72 config
|
||||
properties (that is the trap vega-editor deliberately avoids by staying JSON).
|
||||
|
||||
**Hard constraint — the builder must preserve unknown keys.** Verified by compiling: the
|
||||
vega-themes presets carry Vega-_layer_ keys (`symbol`, `shape`, `path`, `group`) that are
|
||||
not in the Vega-Lite `Config` schema, and Vega-Lite forwards the whole config to Vega
|
||||
unchanged — they take effect. So a structured control must **merge into** the existing
|
||||
config (immutable path-set that spreads siblings), never rebuild it from a closed
|
||||
schema-typed model, or it silently drops those keys on a round-trip. Same shape as
|
||||
`applyFontToConfig`, which walks and rewrites rather than reconstructing.
|
||||
|
||||
**Resolved design points:**
|
||||
|
||||
- **Surfacing — inline tab strip** (not popovers, not sub-modals). Tabs (Color / Type /
|
||||
Layout / Axes & grid / Legend) sit between the toolbar and the JSON+gallery, all in the
|
||||
one xlarge modal. No nested overlays/focus traps, no contention with the one-open-popover
|
||||
registry, and panels + JSON + gallery stay visible together.
|
||||
- **Color model — scheme picker that materializes to swatches.** A `range` family takes
|
||||
either an explicit color array or a named Vega scheme string (both verified to compile).
|
||||
Pick a named scheme for the quick path; "materialize" expands it to an editable swatch
|
||||
array for brand tuning. Catalog ships 15 categorical + 24 sequential + 10 diverging
|
||||
schemes; categorical schemes resolve to arrays, continuous ones to interpolators sampled
|
||||
into stops for the gradient preview and the materialize action.
|
||||
- Structured controls are gated on valid JSON (same as the font control): a parse error
|
||||
disables them and the textarea is the fix.
|
||||
- Controls write **minimal** config — clearing a value deletes the key rather than writing
|
||||
a default, so a theme stays a diff against stock, not a full dump.
|
||||
|
||||
**Panels:** Color (`range.category` swatches/scheme, `mark.color`, `range.heatmap`/`ramp`/
|
||||
`diverging`) · Type (base `font`, title/axis/legend size+weight) · Layout (`background` incl.
|
||||
transparent, `padding`, `view.stroke`/`fill`/cornerRadius) · Axes & grid (grid on/off + color
|
||||
|
||||
- dash, domain, label color/angle — base `axis` only; the 25 variants stay JSON) · Legend
|
||||
(orient, label/title color+size, symbol size).
|
||||
|
||||
**Build order:** (a) core foundation — scheme catalog + immutable config path get/set +
|
||||
`schemeColors` materialize, with tests; (b) Color panel (highest payoff); (c) Type, Layout,
|
||||
Axes, Legend panels; (d) wire the tab strip into the modal.
|
||||
|
||||
**Not in slice 4b:** the house style's own gaps — no `mark.color` (single-series charts stay
|
||||
Vega-blue), unset legend/header/padding — are left for a separate house-style redo, not
|
||||
papered over here. Minor cleanup noted: `theme-preview-specs.ts` declares `$schema` v5 while
|
||||
the app standardizes on v6.
|
||||
|
||||
## 6. Status log
|
||||
|
||||
- **2026-06-14 (slice 4b, first increment)** — **structured-control foundation + Color
|
||||
panel.** Core `theme-controls.ts`: immutable config path get/set (preserves siblings —
|
||||
the Vega-layer-key guarantee — and prunes on delete) + the named-scheme catalog (15
|
||||
categorical / 24 sequential / 10 diverging) + `schemeColors` resolution (categorical
|
||||
arrays passthrough, continuous interpolators sampled to hex), all tested. `vega-scale`
|
||||
added as a declared dep (focused sub-package, like `vega-expression`) with a typings
|
||||
shim in `vite-env.d.ts` (its package.json `exports` omits `types`). Store gains the
|
||||
generic `mutateDraftConfig(fn)` write path; `applyDraftFont` refactored onto it. Modal
|
||||
gains an APG tab strip — **Color** (categorical scheme/swatches + materialize, default
|
||||
`mark.color`, sequential/diverging gradient pickers) and **Type** (the relocated font
|
||||
control). Tabpanel gated on valid JSON. From first-use feedback, same day: the modal
|
||||
body is now **controls + JSON on the left, gallery as a full-height right rail** (the
|
||||
previews were starved before); `SelectControl` gained an optional per-option `preview`
|
||||
so the scheme dropdowns show swatch strips (categorical) / gradient bars (continuous);
|
||||
every swatch is a reusable `SwatchRow` (color picker + copyable/editable hex field); and
|
||||
sequential/diverging gained **Materialize → editable stops**, so custom gradient colors
|
||||
are possible, not just named schemes. Second feedback pass: the raw JSON is now a
|
||||
**collapsed disclosure** at the bottom of the controls column (it was eating half the
|
||||
first screen), forced open only on a parse error; the structured controls fill the
|
||||
column. `SelectControl` options gained a `labelStyle`, so the **font dropdown renders
|
||||
each name in its own family** (the type analogue of the color swatches) and its trigger
|
||||
shows the current font in-face. Verified: typecheck, lint, full tests (950).
|
||||
Remaining: Layout / Axes & grid / Legend panels; swatch reorder.
|
||||
|
||||
- **2026-06-12 (slice 4 close-out)** — **custom themes in the §08 envelope.** The
|
||||
workspace export now writes a `themes` array (additive — no format bump; importers
|
||||
|
||||
Reference in New Issue
Block a user