Skip to content

Add ADR for component editor configuration overhaul - #146

Merged
jordanwallwork merged 7 commits into
masterfrom
claude/component-editor-improvements-rxf15d
Jul 20, 2026
Merged

Add ADR for component editor configuration overhaul#146
jordanwallwork merged 7 commits into
masterfrom
claude/component-editor-improvements-rxf15d

Conversation

@jordanwallwork

Copy link
Copy Markdown
Owner

Capture the decisions from the editor review and design interview:
capability-driven property groups, schema normalization without migration,
unset-first nullable properties, edit-session undo batching, unit-aware
length inputs, dual config-panel prototypes, mobile bottom sheet, and
phased canvas/workflow features.

Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01GTSHG3qL5K7qrWx4GXPLkn

claude and others added 7 commits July 17, 2026 23:13
Capture the decisions from the editor review and design interview:
capability-driven property groups, schema normalization without migration,
unset-first nullable properties, edit-session undo batching, unit-aware
length inputs, dual config-panel prototypes, mobile bottom sheet, and
phased canvas/workflow features.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GTSHG3qL5K7qrWx4GXPLkn
Implements Phase 1 of ADR-0001 — invisible foundations for the component
editor configuration overhaul, with no intended UI redesign.

D3 Schema normalization: one Border and one Background model across all
element types. Removed ShapeBorder (shapes use the unified border) and
text's flat backgroundColor (text uses Background). Renamed grid per-cell
styling to cellBackground/cellBorder so it can't be confused with the
grid element's own background/border. No migration (pre-release).

D4 Unset-first defaults: new effectiveDefaults.ts is the single source of
truth for what an unset property means; the element factory (and the
AddElementPopover path) stop stamping cosmetic defaults, so new elements
are nearly-empty objects. Renderers resolve unset values through the
module instead of scattered `|| fallback`.

D7 Store edit-sessions: templateStore.updateElement takes a session key;
consecutive same-key updates collapse into one undo step, sealed on
blur/pointer-up, key change, selection change, or any structural op. The
bespoke saveToHistory/updateElementWithoutHistory workarounds for text
content and drag/resize/rotation are removed; updates stay live so the
canvas previews continuously.

D8 Unit-aware lengths: DimensionInput accepts number|string directly
(number = px), and gap, font size, and shape/grid border widths join the
already unit-aware position/padding/border controls. Canvas drag/resize
preserve an element's existing unit (mm stays mm) via writeBackInExistingUnit.

Adds tests at the store public-API seam (edit-sessions, move/duplicate),
the effective-defaults resolver, and the unit write-back helper.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RmQAu3SECs47Yza2UYyyWR
DimensionInput now accepts number|string directly, so the per-control
number->px string adapters in PositionControls, PaddingControls,
BorderConfig, and BaseElementConfig are dead weight. Pass stored length
values straight through; the object-radius guards are preserved.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RmQAu3SECs47Yza2UYyyWR
Introduces a strongly-typed Dimension working type ({ unit, value } with
px/mm/percent constructors) and consolidates the length parsing/formatting
that was smeared across utils.ts, DimensionInput, and writeBackInExistingUnit
into one pure, well-tested module (dimension.ts).

Storage stays number|string (the serialized convention is unchanged): the
module parses at the boundary (toDimension) and serializes back (toStored),
so Dimension is a domain type, not a schema change. dimensionValue /
dimensionToPx / writeBackInExistingUnit are reimplemented on top of it with
their existing contracts and tests intact.

Two intentional improvements fall out of the seam:
- px canonicalizes to a plain number on serialize (DimensionInput no longer
  emits "10px" strings; onchange widened to number|string|undefined).
- toPx requires an explicit reference for %, so percentage->px can't do
  silent wrong math.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RmQAu3SECs47Yza2UYyyWR
Implements Phase 2 of ADR-0001: the config panel is now driven by a
capability registry, with both UX prototypes coexisting behind a dev
toggle for the owner to evaluate (D5 decision gate — neither deleted).

Capability registry (capabilities.ts): declarative element-type -> property
group matrix (iterator = identity only). Group model (groupModel.ts):
readShared/MIXED, fieldState, set-indicator + summary helpers. All pure
and tested.

Shared group controls (_components/config-groups/): Identity, Position,
Size, Layout, Typography, Background, Border, Effects — each rendering
only the sub-controls meaningful for the element type, with unset
placeholders (from effectiveDefaults), a reset (x) to clear to undefined,
and Mixed-value plumbing (dormant until Phase 3 multi-select). Type-specific
pinned fields per element type preserve merge fields, font tracking,
markdown, conditional visibility.

Previously render-only properties now have editing UI (D3): opacity slider
and a multi-shadow list editor (Effects); background image with
URL/filename/merge-field source + size/repeat/position (Background); margin
and overflow (Layout); z-index and rotation (Position); min/max width/height
(Size).

Two prototypes behind a dev toggle (?panel=a|b or header switch):
AccordionPanel (collapsible groups, pinned identity/type-specific, set-dot +
summary headers, remembered open state) and TabRailPanel (vertical glyph tab
rail, per-tab set dots, remembered active tab). GroupConfigHost binds both to
the store with edit-sessions and read-only guards; empty state when nothing
selected. Mobile: BottomSheet with peek/half/full snap points hosts the panel
so the canvas stays visible; structure tree stays a drawer.

Component-test infra: @testing-library/svelte + jsdom (per-file docblock;
node stays default), $app mocks. Adds 52 tests (pure + component).

Old per-type configuration components remain on disk pending the owner's
prototype decision and cleanup.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RmQAu3SECs47Yza2UYyyWR
Adds a third panel prototype behind the dev toggle, refining Variant B per
owner feedback: the vertical tab rail is now sticky and all granted group
sections render in one long scrollable list. The active tab tracks which
section is in view (IntersectionObserver scroll-spy) and clicking a tab
scrolls to its section. Selectable via ?panel=c or the C header button.

The rail markup mirrors Variant B (already covered by tests); the scroll-spy
depends on a real layout engine + IntersectionObserver, so it is validated by
live evaluation rather than a jsdom unit test. Guarded so the component
degrades gracefully where IntersectionObserver is unavailable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RmQAu3SECs47Yza2UYyyWR
D5 decision gate resolved: the owner chose Variant A (collapsible accordion
groups). Removes the two losing prototypes (Variant B tab rail, Variant C
scroll-spy rail) and the dev toggle; GroupConfigHost now renders the accordion
directly. panelPrefs drops the variant/active-tab machinery, keeping only the
remembered accordion open-state.

Also deletes the now-unused per-type configuration components
(Container/Text/Image/Shape/Grid/Iterator/BaseElementConfig) superseded by the
capability-driven group controls. ComponentConfig (root/component settings) and
PlaceholderModal (used by the new image type-specific fields) are retained.

Records the decision in ADR-0001.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RmQAu3SECs47Yza2UYyyWR
@sonarqubecloud

Copy link
Copy Markdown

This was referenced Jul 19, 2026
@jordanwallwork
jordanwallwork merged commit 8c9d549 into master Jul 20, 2026
3 checks passed
@jordanwallwork
jordanwallwork deleted the claude/component-editor-improvements-rxf15d branch July 20, 2026 12:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants