ADRs
ADR 0068 — Save artboard box → project Component Library (Copy)
  • Date: 2026-07-14
  • Status: Accepted
  • Builds on: ADR 0066 (artboard persistence). Phase 4 of Studio-in-Workflow.

Context

Artboards now persist (ADR 0066), but a built design-component is still a dead end — you can't reuse it. Arno already has the shape of a component library in Sorgente: sorgente.component_entry + Tweak's "Add to library" dialog + the StudioPalette drag-source + the palette-drop path in StudioDndShell. But that library is maintainer-scoped and stores an HTML snapshot (display-only) — wrong for project-scoped, editable design components.

Decision

A project-scoped Component Library that stores the picked box's ponte LayoutNode subtree (not an HTML snapshot), with Copy semantics: dragging a library component onto an artboard inserts an independent copy (fresh ids). No live master↔instance link — that's a later phase.

Storage (workflow_component, ADR 0066 sibling)

workflow_component
  project_id   text  FK → project.id  ON DELETE CASCADE
  component_id text  (client key "wc-<ts>")
  name         text
  kind         text  (atomic: atom|molecule|organism|template|page)
  tree         jsonb (LayoutNode subtree; typed `unknown`)
  created_at / updated_at  timestamptz
  PRIMARY KEY (project_id, component_id)

API (apps/api/src/workflow-component.ts, mirrors workflow-artboard)

  • GET /api/v1/projects/:projectId/workflow-components{ items }
  • PUT /api/v1/workflow-components → upsert
  • DELETE /api/v1/workflow-components?project=&id=

requireOwnedProject gate, 500k tree cap.

Reuse — the drag-back loop already exists

The Copy insert is the existing paste machinery: freshenIds (id refresh) + splice into the drop target (as in pasteIntoActiveBox). What's new:

  • insertSubtree(root, targetId, subtree, position) in @arno/ponte/tree-ops — clone + regenerate ids + insert (the container/leaf placement rules match insertFromPalette). Pure + unit-tested.
  • StudioDndShell palette-drop learns a second payload: a palette item may carry data.tree (a subtree) instead of data.ref (a single component). When present, drop calls insertSubtree; otherwise the existing insertFromPalette.
  • WorkflowPalette — a workflow-scoped drag-source (mirrors StudioPalette) listing workflow_component rows, grouped by atomic kind, each draggable with data: { source:"palette", tree }.
  • Save action — a small workflow affordance reads the studio-bus picked box, grabs its subtree (findNode), and POSTs it as a workflow_component.

Frontend data

useWorkflowComponents(projectId) — server-backed list + save(name,kind,tree) + remove(id). Mounted in the workflow shell alongside the artboards.

Consequences

  • Build once, reuse anywhere on the project's artboards. Foundation for publish.
  • Copy-only: editing a library component does NOT update already-placed copies. Simple + predictable; master/instance propagation is deferred (needs an instance-ref node type + resolve-at-render + detach — its own ADR).
  • Kind is set at save time (atomic-design bucket); no re-classification UI yet.

Deferred

Master↔instance (live components); rename / re-kind UI; thumbnails/previews in the palette; sharing a component across projects; publish → code.