Skip to content

Editing UI & Canvas

The most reused-from-existing-code area of this whole design and, in terms of an actual screen anyone could look at, still the least designed. Real, verified reuse exists for most of it; what's net new is the property panel, the layer list, undo/redo, the promote-selection-to-Element step, and three concrete requirements that surfaced from working through Element mechanics rather than from a wireframe.

Reuse map

Checked directly against the code rather than assumed:

Existing code Generalises into
Template Editor's Library The Element picker
Campaign Editor's sticker mechanism Canvas placement
Template Editor's board Scene sequencing
Template Builder's publish step Publishing
Template Builder's EditStep (minus PSD parsing) plus pieces of Campaign Editor The Element-authoring tool — a new tool, not a repurposed Sticker Builder

A genuine shared core already exists underneath both current canvases — CanvasSelectionOverlay.tsx (hit-testing, per-layer selection boxes, locked-layer handling, and a shipped renderPrimaryManipulator extension seam), the anchor/geometry helpers, the guides overlay — and Composer's canvas can build on it directly. The geometry it draws a ring around comes from the same solveScene / animateScene the renderer uses under the engine, which retires useLayerLayouts's own interpolation copy (Resolver Engine → The Motion Graph) — the only way a flow or pin layer's ring can be right.

Sticker Builder (NEO-1315) is not the base for Element authoring

It parameterises existing SVG artwork (no blank canvas; editing means promoting specific SVG nodes into slots) rather than composing a structured layer tree, and its document schema stores raw SVG as a string — a genuinely incompatible data model to Composer's typed primitives. Its wizard/slot-editing UX is worth borrowing conceptually, nothing more. Its code is still relevant to one narrower question — migrating today's existing sticker definitions into the Element system — assessed only by reputation so far, never actually read.

The Element-authoring tool: extract TemplateBuilder's core

Investigated before assuming this needs building from scratch: the two missing pieces mostly already exist as working code. TemplateBuilder's EditStep already places and configures primitives — canvas, layer list, add-layer menu, layout and timing editors — on the exact layer schema Composer's Elements use; its ManifestBindingsEditor already does the "mark this property exposed, choose its control, label it" workflow Element authoring needs. AddedLayerEditor already lets users place and configure typed primitives directly in a Scene today, so users placing primitives is not new ground.

What's missing is the outer container, not the core: EditStep is built around a whole Template document rather than an Element-scoped one; its save path targets the existing template tables rather than Composer's identity/version tables (Identity, Versions & Schema Versioning); its access today is a blunt route-level super-admin gate rather than a real capability check (Permissions & Enforcement); and there's no existing concept of promoting a selection into one saved, reusable, versioned unit — that promotion step is genuinely new. Everything else is an extraction and retargeting job.

Promotion flattens; it never nests

Because an Element can never contain another Element, promotion cannot simply wrap the selected instances. It flattens them: the selected wrapper Elements' internals are copied into the new Element's own layer list, their layer ids re-scoped to it, their manifest bindings merged into one manifest, and each instance's setInstanceField / setInstanceLayout overrides folded into the layer values they were overriding. A selection containing a genuinely authored (non-wrapper) Element is rejected rather than silently inlined, since flattening it would fork a shared Element's contents away from the Element that owns them. Promotion needs Element create permission, plus the composing rung to have selected the instances in the first place.

Not a separable side project

This is part of the same undesigned canvas/UI work this page flags as needing its own planning session (Blair's call, below), not a parallel track with its own timeline — and it needs the tier design as well as the schema, since it's gated by three real permissions.

What's net new

The property/field panel, the layer list, undo/redo, and the promotion step above. The first three exist today as two separate implementations, not a DRY violation waiting to be deleted: Template Builder's useDocumentHistory (whole-document snapshot) versus Campaign Editor's useRetailStudioStore operation-log; RetailStudioPropertyPanel (tier-gated via useEffectiveLayoutMode) versus EditStep's inline equivalent; BuilderLayerList.tsx versus RetailStudioLayerPanel.tsx. Composer's are realistically a third, purpose-built implementation borrowing the tier-gating pattern from Campaign Editor and the richer transform concepts from Template Builder. Matthew has flagged this category of work as having a rough historical track record for this team.

Decided: undo/redo is an operation log whose entries are Changes

A session-local, uncompacted history of what the editor emitted, stepped backward and forward — not a second, TemplateBuilder-style whole-document-snapshot stack, and not the fork's stored Change[], which is compacted and has no sequence to step through (The Change Model).

Each entry is a { change, inverse } pair captured at emit time, not a bare Change: a setInstanceField carries the new value and not the old one, so the inverse has to be recorded when the editor still knows what it replaced. A compound action — the agent's "swap Element A for B" (AI / Agent Forward-Compatibility) — is one entry holding several pairs, so it undoes in one step; pushFieldBatchUndo in useRetailStudioStore is the existing precedent for exactly that batching.

Three requirements surfaced from Element mechanics

A placement-resize control on the authoring canvas

The authoring canvas needs a resize control that simulates placement-time resizing, independent of whatever default size the Element ships with, since that's the only way to verify its internal layers respond correctly once placed at a size other than the one it was authored against (Elements, Scenes & Projects).

The schema-migration placement dialog

Placing a reference whose schemaVersion.major is behind current needs a confirmation dialog offering migration, and placement must still succeed if the user dismisses it or lacks permission to migrate. The mechanism, the prompt, the minor-behind exemption, and why it never gates are on Identity, Versions & Schema Versioning; what belongs here is only that the dialog is a screen someone has to build.

A standalone migration action outside placement

Whatever view already lets a user browse or manage an Element / Scene / Project's own identity — a catalogue or library screen — needs a "run migration" action reachable there directly, independent of placing anything: the same action, the same fork-and-commit, the same permission as the dialog; the only new UI surface is where it's reachable from. Why it's needed is on Identity, Versions & Schema Versioning.

Deferred: PSD import

Importing a PSD gets harder under the Element model: a Scene is built from Element instances rather than bare primitives, and matching an arbitrary PSD's layers against a whole library of authored Elements is a real recognition problem that doesn't exist today. Not a blocker — anything unmatched can become its own trivial, single-primitive Element via the wrapper mechanism — it just loses Composer's actual point for that layer. Picked back up at a later date.

Design-system consistency

This is Shane's domain. Beyond the reuse/net-new split above, Composer's UI needs clear component/pattern reuse rules so it stays visually and interaction-consistent with Neosframe's established design system — a consistency concern, not just code reuse for engineering convenience.

Decided: the existing Neosframe Component Library

The same Figma library the design-standards gate already references, no separate Composer-specific artifact.

Risks

  • A specific, undiagnosed recurring failure pattern behind the team's historically rough UI work — never explicitly named, only referenced as a pattern to avoid repeating.
  • Whether real usage friction from today's existing sticker feature (which nobody has formally reviewed) already predicts problems Composer's canvas is about to reinherit.

Open questions

  • How much of the underlying system — schema, Change model, permissions, storage — needs to exist before UI work can meaningfully start, versus how much can be explored in parallel against a moving target. Needs a dedicated planning session the team hasn't had yet, and now also determines when the Element-authoring tool can begin. Blair's call, not this brief's.
  • No wireframe or concrete flow exists yet for any Composer-specific screen.
  • The actual UI and permission wiring for user-facing Element authoring, and how the tier design carries the three Element permissions.