The browser editor stage — a Phoenix.LiveComponent that renders a scene
as a server-authoritative SVG preview and lets the user select
(single, shift-click, marquee), drag, resize, reorder, and delete
elements on a Fresco-style artboard.
Design: there is one layout engine — the server. The JS hook
(priv/static/open_fresco.js) drives gestures: it hit-tests locally at
pointerdown for instant selection/drag seeding, previews the gesture
with a client-side transform, and commits one operation on release;
the server applies it through OpenFresco.Editor.Ops against the
resolved layout (the same geometry the paint pass used) and
re-renders. So "what you edit is what renders" holds by construction.
The SVG is rendered as separate defs / body / selection dynamics
(OpenFresco.Svg.render_parts/3 with split_images: true), so
LiveView patches after a commit don't retransmit inlined image
data-URLs, and selection changes don't retransmit geometry.
Embedding
<.live_component
module={OpenFresco.Editor} id="og-editor"
scene={@scene} values={@values} globals={@globals} />Assigns:
:scene(required) — theOpenFresco.Scene.:values— slot values, asOpenFresco.render_svg/3.:globals—[[global]]values; the stage resolves them exactly like the PNG path does (one shared substitution pass).:resolver— the resource-resolver fun (seeOpenFresco.render_svg/3); lets the stage display media that's stored as opaque refs instead of the stand-in.:selected— pass to drive selection programmatically (e.g. click a slot chip in the host → highlight its element). Omit to leave selection editor-driven.:fit—true(default) scales the stage to its container (pointer math is transform-safe);falserenders 1:1.:notify— where change messages go: apid(default: the root LiveView viaself()), or{module, id}tosend_update/3into a host LiveComponent (which otherwise could never receive them).
Ownership contract
The scene is host-owned, editor-driven: every committed edit is
notified to the host (persist it; echo it back via the :scene
assign). Echoed scenes that equal the editor's current scene are
accepted without re-rendering, and gestures preview client-side —
a parent re-render can no longer clobber an in-flight gesture. A
different scene from the host always wins (that's programmatic
editing); hosts wanting stricter arbitration can withhold the assign.
On every committed edit the component notifies
{:open_fresco_editor, id, {:scene_changed, scene}} (every message is
a persist-safe commit — previews never reach the server); on selection
{:open_fresco_editor, id, {:selected, id_or_nil}} and — when a
selection set is active — {:selected_ids, [ids]}.
Undo material: apply edits through OpenFresco.Editor.Ops.command/2
in the host when you need an undo stack — each command returns its
inverse.
Keyboard
With the stage focused: Tab/Shift+Tab cycle element selection,
arrows nudge by 1px (Shift = 10px), Delete/Backspace delete the
selection (set-aware), Escape deselects, ]/[ bring to
front / send to back (Alt steps one position).
Requires the optional :phoenix_live_view dependency — without it this
module is not compiled.