@particle-academy/fancy-cms-ui 0.0.1-dev.8 → 0.0.1-dev.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -111,6 +111,10 @@ function reduce(doc, op, opts = {}) {
111
111
  props: { ...doc.nodes[op.id].props, ...op.patch }
112
112
  });
113
113
  }
114
+ case "set_layout": {
115
+ if (!doc.nodes[op.id]) return invalid("node not found");
116
+ return patchNode(doc, op.id, { layout: op.layout });
117
+ }
114
118
  case "set_style": {
115
119
  const node = doc.nodes[op.id];
116
120
  if (!node) return invalid("node not found");
@@ -197,6 +201,11 @@ function invert(doc, op) {
197
201
  for (const k of Object.keys(op.patch)) prev[k] = node.props[k];
198
202
  return [{ t: "set_props", id: op.id, patch: prev }];
199
203
  }
204
+ case "set_layout": {
205
+ const node = doc.nodes[op.id];
206
+ if (!node) return [];
207
+ return [{ t: "set_layout", id: op.id, layout: node.layout }];
208
+ }
200
209
  case "set_style": {
201
210
  const node = doc.nodes[op.id];
202
211
  if (!node) return [];
@@ -348,5 +357,5 @@ ${inner}
348
357
  }
349
358
 
350
359
  export { DEFAULT_BREAKPOINTS, childrenOf, descendantIds, emitDocCss, emptyDoc, invert, isBinding, reduce, reduceAll };
351
- //# sourceMappingURL=chunk-HYHNMW47.js.map
352
- //# sourceMappingURL=chunk-HYHNMW47.js.map
360
+ //# sourceMappingURL=chunk-3YZMELJJ.js.map
361
+ //# sourceMappingURL=chunk-3YZMELJJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/document/types.ts","../src/document/reduce.ts","../src/render/css.ts"],"names":[],"mappings":"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","file":"chunk-3YZMELJJ.js","sourcesContent":["/**\n * Stages document model — see `fancy-ui/docs/fancy-cms.md` §4.\n *\n * A page is a vertical stack of full-bleed **sections**; inside each, **layers**\n * are positioned by constraints (free + bold stays responsive), with stack/grid\n * containers where structure is wanted. Nodes live in a **flat map** keyed by id\n * (Figma-style) so ops target a stable identity and collab merges stay clean.\n */\n\nexport type NodeId = string;\n\nexport type ScrollMode = \"snap\" | \"smooth\";\n\n/** Ordered, mobile-first. Convention: `\"base\" | \"md\" | \"lg\"`. */\nexport type Breakpoint = string;\n\nexport type Json = string | number | boolean | null | Json[] | { [key: string]: Json };\n\n/** A literal value OR a binding into the page's data context (`{ $bind: \"profile.coins\" }`). */\nexport interface Binding {\n $bind: string;\n}\nexport type Bound<T> = T | Binding;\n\nexport function isBinding(v: unknown): v is Binding {\n return typeof v === \"object\" && v !== null && typeof (v as Binding).$bind === \"string\";\n}\n\n// ── Sizing ────────────────────────────────────────────────────────────────\n\nexport type LengthUnit = \"px\" | \"%\" | \"vw\" | \"vh\" | \"rem\" | \"fr\";\nexport interface Length {\n value: number;\n unit: LengthUnit;\n}\nexport type SizeMode = Length | \"fill\" | \"hug\";\n\nexport interface Constraints {\n /** Pinned offsets within a `free` parent (null = unpinned). */\n left?: Length | null;\n right?: Length | null;\n top?: Length | null;\n bottom?: Length | null;\n centerX?: boolean;\n centerY?: boolean;\n width: SizeMode;\n height: SizeMode;\n}\n\nexport type LayoutMode = \"free\" | \"stack\" | \"grid\";\n\n// ── Style (compiles deterministically to CSS in both emitters) ──────────────\n\nexport interface StyleProps {\n // box\n padding?: Length;\n margin?: Length;\n radius?: Length;\n // color / surface\n background?: string; // color | gradient | url(...)\n color?: string;\n opacity?: number;\n // typography\n fontFamily?: string;\n fontSize?: Length;\n fontWeight?: number;\n lineHeight?: number;\n textAlign?: \"left\" | \"center\" | \"right\" | \"justify\";\n letterSpacing?: Length;\n // border / shadow / effects (static)\n border?: string;\n boxShadow?: string;\n transform?: string;\n filter?: string;\n // container knobs (when layout = stack | grid)\n gap?: Length;\n direction?: \"row\" | \"column\";\n columns?: number;\n align?: \"start\" | \"center\" | \"end\" | \"stretch\";\n justify?: \"start\" | \"center\" | \"end\" | \"between\" | \"around\";\n}\n\n/** Per-breakpoint overrides; `base` is required, others cascade mobile-first. */\nexport type PerBreakpoint<T> = { base: T } & Partial<Record<Breakpoint, Partial<T>>>;\n\n// ── Animation (typed for Phase 2; ignored by the static CSS emitter) ────────\n\nexport type AnimDriver = \"enter\" | \"scroll\" | \"loop\" | \"hover\" | \"click\" | \"drag\";\nexport interface Keyframe {\n at: number; // 0..1\n props: Record<string, number | string>;\n}\nexport interface Anim {\n id: string;\n driver: AnimDriver;\n keyframes: Keyframe[];\n range?: { start: number; end: number };\n ease?: string;\n duration?: number;\n delay?: number;\n stagger?: number;\n once?: boolean;\n emit?: \"css\" | \"js\";\n}\n\n// ── Actions (typed for Phase 3/4) ───────────────────────────────────────────\n\nexport type ActionEvent = \"click\" | \"submit\" | \"change\" | \"load\" | \"timer\" | \"scroll-into\" | \"hover\";\nexport type ActionHandler =\n | { kind: \"navigate\"; href: Bound<string> }\n | { kind: \"submitForm\"; endpoint: string; method?: \"post\" | \"put\" }\n | { kind: \"mutate\"; target: string; value: Bound<Json> }\n | { kind: \"callEndpoint\"; endpoint: string; method?: string }\n | { kind: \"runFlow\"; flow: string }\n | { kind: \"openModal\"; node: NodeId }\n | { kind: \"openDrawer\"; node: NodeId }\n | { kind: \"playTimeline\"; section: NodeId }\n | { kind: \"toggleNode\"; node: NodeId };\nexport interface Action {\n on: ActionEvent;\n do: ActionHandler;\n}\n\n// ── Nodes & document ────────────────────────────────────────────────────────\n\nexport interface Node {\n id: NodeId;\n /** Addon key: `section | text | image | shape | stack | grid | frame | <addon>`. */\n type: string;\n /** `null` = a top-level section. */\n parent: NodeId | null;\n /** Fractional index — collab-safe ordering among siblings. */\n order: string;\n /** Addon-specific props; any value may be a {@link Binding}. */\n props: Record<string, Bound<Json>>;\n /** How this node arranges its children. */\n layout?: LayoutMode;\n /** How this node positions itself within its parent. */\n constraints?: PerBreakpoint<Constraints>;\n style: PerBreakpoint<StyleProps>;\n animations?: Anim[];\n actions?: Action[];\n /** Repeater: render this node once per item of a bound array. */\n repeat?: { each: Binding; as: string };\n /**\n * Host CSS class(es) applied to the node's element. The migration enabler:\n * reuse the host site's existing stylesheet for pixel-perfect reproduction\n * instead of re-deriving every rule through {@link StyleProps}.\n */\n className?: string;\n /** Derived from the addon manifest — interactive/3rd-party ⇒ hydration island. */\n island?: boolean;\n}\n\nexport interface ThemeTokens {\n name?: string;\n [key: string]: Json | undefined;\n}\n\nexport interface PageMeta {\n title: string;\n slug: string;\n scrollMode: ScrollMode;\n seo?: Record<string, string>;\n}\n\nexport interface PageDoc {\n id: string;\n /** Op counter — collab ordering + revision checkpoints. */\n seq: number;\n meta: PageMeta;\n theme: ThemeTokens;\n /** Ordered, mobile-first. */\n breakpoints: Breakpoint[];\n /** Top-level section order. */\n sections: NodeId[];\n /** Flat map — not a nested tree. */\n nodes: Record<NodeId, Node>;\n}\n\n/** Default breakpoint → min-width (px) map. `base` has no media query. */\nexport const DEFAULT_BREAKPOINTS: Record<string, number> = {\n base: 0,\n md: 768,\n lg: 1024,\n};\n\n/** A minimal empty document. */\nexport function emptyDoc(id: string, slug = \"/\"): PageDoc {\n return {\n id,\n seq: 0,\n meta: { title: \"Untitled\", slug, scrollMode: \"smooth\" },\n theme: { name: \"default\" },\n breakpoints: [\"base\", \"md\", \"lg\"],\n sections: [],\n nodes: {},\n };\n}\n","/**\n * The pure, total reducer. `reduce(doc, op)` returns a new document; an invalid\n * op is a **logged no-op** (returns the same doc, `seq` unchanged). That single\n * property is what makes undo, revisions, collaboration, and agent edits all\n * flow through one spine.\n *\n * Ordering: top-level order is the canonical `sections` array; child order is\n * the fractional `order` field among siblings (see `fractional.ts`).\n */\nimport type { Action, Node, NodeId, PageDoc, StyleProps } from \"./types\";\nimport type { PageOp } from \"./ops\";\n\nexport interface ReduceOptions {\n /** Called when an op can't apply. Default: silent. */\n onInvalid?: (op: PageOp, reason: string) => void;\n}\n\n/** Children of a node (or top-level when `parent` is null), in order. */\nexport function childrenOf(doc: PageDoc, parent: NodeId | null): Node[] {\n if (parent === null) {\n return doc.sections.map((id) => doc.nodes[id]).filter((n): n is Node => Boolean(n));\n }\n const out: Node[] = [];\n for (const id of Object.keys(doc.nodes)) {\n const n = doc.nodes[id]!;\n if (n.parent === parent) out.push(n);\n }\n out.sort((a, b) => (a.order < b.order ? -1 : a.order > b.order ? 1 : 0));\n return out;\n}\n\n/** Every transitive descendant id of `id` (excludes `id`). */\nexport function descendantIds(doc: PageDoc, id: NodeId): NodeId[] {\n const out: NodeId[] = [];\n const stack: NodeId[] = [id];\n while (stack.length) {\n const current = stack.pop()!;\n for (const childId of Object.keys(doc.nodes)) {\n const n = doc.nodes[childId]!;\n if (n.parent === current) {\n out.push(childId);\n stack.push(childId);\n }\n }\n }\n return out;\n}\n\nfunction patchNode(doc: PageDoc, id: NodeId, patch: Partial<Node>): PageDoc {\n const node = doc.nodes[id]!;\n return {\n ...doc,\n nodes: { ...doc.nodes, [id]: { ...node, ...patch } },\n seq: doc.seq + 1,\n };\n}\n\nexport function reduce(doc: PageDoc, op: PageOp, opts: ReduceOptions = {}): PageDoc {\n const invalid = (reason: string): PageDoc => {\n opts.onInvalid?.(op, reason);\n return doc;\n };\n\n switch (op.t) {\n case \"insert_node\": {\n if (doc.nodes[op.node.id]) return invalid(\"node id already exists\");\n if (op.node.parent !== null && !doc.nodes[op.node.parent]) return invalid(\"parent not found\");\n return {\n ...doc,\n nodes: { ...doc.nodes, [op.node.id]: op.node },\n sections: op.node.parent === null ? [...doc.sections, op.node.id] : doc.sections,\n seq: doc.seq + 1,\n };\n }\n\n case \"remove_node\": {\n if (!doc.nodes[op.id]) return invalid(\"node not found\");\n const dead = new Set<NodeId>([op.id, ...descendantIds(doc, op.id)]);\n const nodes: Record<NodeId, Node> = {};\n for (const id of Object.keys(doc.nodes)) {\n if (!dead.has(id)) nodes[id] = doc.nodes[id]!;\n }\n return {\n ...doc,\n nodes,\n sections: doc.sections.filter((s) => !dead.has(s)),\n seq: doc.seq + 1,\n };\n }\n\n case \"move_node\": {\n const node = doc.nodes[op.id];\n if (!node) return invalid(\"node not found\");\n if (op.parent === op.id) return invalid(\"cannot parent a node to itself\");\n if (op.parent !== null && !doc.nodes[op.parent]) return invalid(\"new parent not found\");\n if (op.parent !== null && descendantIds(doc, op.id).includes(op.parent)) {\n return invalid(\"move would create a cycle\");\n }\n const wasTop = node.parent === null;\n const nowTop = op.parent === null;\n let sections = doc.sections;\n if (wasTop && !nowTop) sections = sections.filter((s) => s !== op.id);\n else if (!wasTop && nowTop) sections = [...sections, op.id];\n return {\n ...doc,\n nodes: { ...doc.nodes, [op.id]: { ...node, parent: op.parent, order: op.order } },\n sections,\n seq: doc.seq + 1,\n };\n }\n\n case \"set_props\": {\n if (!doc.nodes[op.id]) return invalid(\"node not found\");\n return patchNode(doc, op.id, {\n props: { ...doc.nodes[op.id]!.props, ...op.patch } as unknown as Node[\"props\"],\n });\n }\n\n case \"set_layout\": {\n if (!doc.nodes[op.id]) return invalid(\"node not found\");\n return patchNode(doc, op.id, { layout: op.layout });\n }\n\n case \"set_style\": {\n const node = doc.nodes[op.id];\n if (!node) return invalid(\"node not found\");\n const prev = node.style[op.breakpoint] ?? {};\n return patchNode(doc, op.id, {\n style: { ...node.style, [op.breakpoint]: { ...prev, ...op.patch } } as Node[\"style\"],\n });\n }\n\n case \"set_constraints\": {\n const node = doc.nodes[op.id];\n if (!node) return invalid(\"node not found\");\n const base = node.constraints ?? { base: { width: \"hug\", height: \"hug\" } };\n const prev = base[op.breakpoint] ?? {};\n return patchNode(doc, op.id, {\n constraints: { ...base, [op.breakpoint]: { ...prev, ...op.patch } } as Node[\"constraints\"],\n });\n }\n\n case \"set_animation\": {\n const node = doc.nodes[op.id];\n if (!node) return invalid(\"node not found\");\n const list = node.animations ?? [];\n const idx = list.findIndex((a) => a.id === op.anim.id);\n return patchNode(doc, op.id, {\n animations: idx >= 0 ? list.map((a, i) => (i === idx ? op.anim : a)) : [...list, op.anim],\n });\n }\n\n case \"remove_animation\": {\n const node = doc.nodes[op.id];\n if (!node) return invalid(\"node not found\");\n return patchNode(doc, op.id, {\n animations: (node.animations ?? []).filter((a) => a.id !== op.animId),\n });\n }\n\n case \"set_action\": {\n const node = doc.nodes[op.id];\n if (!node) return invalid(\"node not found\");\n const list = node.actions ?? [];\n let actions: Action[];\n if (op.action === null) actions = list.filter((_, i) => i !== op.index);\n else if (op.index >= list.length) actions = [...list, op.action];\n else actions = list.map((a, i) => (i === op.index ? op.action! : a));\n return patchNode(doc, op.id, { actions });\n }\n\n case \"set_meta\":\n return { ...doc, meta: { ...doc.meta, ...op.patch }, seq: doc.seq + 1 };\n\n case \"set_theme\":\n return { ...doc, theme: { ...doc.theme, ...op.patch }, seq: doc.seq + 1 };\n\n case \"reorder_sections\": {\n const current = new Set(doc.sections);\n const same =\n op.order.length === doc.sections.length && op.order.every((id) => current.has(id));\n if (!same) return invalid(\"reorder must be a permutation of current sections\");\n return { ...doc, sections: [...op.order], seq: doc.seq + 1 };\n }\n\n default:\n return invalid(`unknown op \"${(op as PageOp).t}\"`);\n }\n}\n\n/** Apply a sequence of ops, threading the document. */\nexport function reduceAll(doc: PageDoc, ops: PageOp[], opts?: ReduceOptions): PageDoc {\n return ops.reduce((d, op) => reduce(d, op, opts), doc);\n}\n\n/**\n * Inverse op(s) for undo, computed against the **pre-op** document. Structural\n * ops invert cleanly; merge-style ops (`set_props`/`set_style`/`set_constraints`)\n * restore the previous values of the touched keys (keys that did not previously\n * exist can't be deleted via a merge — a known Phase-0 limitation).\n */\nexport function invert(doc: PageDoc, op: PageOp): PageOp[] {\n switch (op.t) {\n case \"insert_node\":\n return [{ t: \"remove_node\", id: op.node.id }];\n\n case \"remove_node\": {\n const node = doc.nodes[op.id];\n if (!node) return [];\n // Re-insert the subtree parent-first, then restore section order.\n const ids = [op.id, ...descendantIds(doc, op.id)];\n const inserts: PageOp[] = ids.map((id) => ({ t: \"insert_node\", node: doc.nodes[id]! }));\n if (node.parent === null) inserts.push({ t: \"reorder_sections\", order: [...doc.sections] });\n return inserts;\n }\n\n case \"move_node\": {\n const node = doc.nodes[op.id];\n if (!node) return [];\n const back: PageOp[] = [{ t: \"move_node\", id: op.id, parent: node.parent, order: node.order }];\n if (node.parent === null) back.push({ t: \"reorder_sections\", order: [...doc.sections] });\n return back;\n }\n\n case \"set_props\": {\n const node = doc.nodes[op.id];\n if (!node) return [];\n const prev: Record<string, unknown> = {};\n for (const k of Object.keys(op.patch)) prev[k] = node.props[k];\n return [{ t: \"set_props\", id: op.id, patch: prev }];\n }\n\n case \"set_layout\": {\n const node = doc.nodes[op.id];\n if (!node) return [];\n return [{ t: \"set_layout\", id: op.id, layout: node.layout }];\n }\n\n case \"set_style\": {\n const node = doc.nodes[op.id];\n if (!node) return [];\n const prevBp = (node.style[op.breakpoint] ?? {}) as Record<string, unknown>;\n const prev: Partial<StyleProps> = {};\n for (const k of Object.keys(op.patch)) (prev as Record<string, unknown>)[k] = prevBp[k];\n return [{ t: \"set_style\", id: op.id, breakpoint: op.breakpoint, patch: prev }];\n }\n\n case \"set_meta\":\n return [{ t: \"set_meta\", patch: pick(doc.meta, Object.keys(op.patch)) }];\n\n case \"set_theme\":\n return [{ t: \"set_theme\", patch: pick(doc.theme, Object.keys(op.patch)) }];\n\n case \"reorder_sections\":\n return [{ t: \"reorder_sections\", order: [...doc.sections] }];\n\n default:\n return [];\n }\n}\n\nfunction pick<T extends object>(obj: T, keys: string[]): Partial<T> {\n const out: Partial<T> = {};\n for (const k of keys) (out as Record<string, unknown>)[k] = (obj as Record<string, unknown>)[k];\n return out;\n}\n","/**\n * The JS style → CSS emitter (one half of the dual-emitter pair; the PHP\n * `particle-academy/fancy-cms` mirrors it byte-for-byte). Output is\n * **deterministic**: declarations are sorted, selectors and media blocks use a\n * fixed format, and nodes are emitted in sorted-id order — so the parity harness\n * can diff the two engines' CSS exactly.\n *\n * Phase 0 covers the static subset (layout / constraints / style). Motion lands\n * in Phase 2.\n */\nimport {\n DEFAULT_BREAKPOINTS,\n type Constraints,\n type Length,\n type Node,\n type PageDoc,\n type SizeMode,\n type StyleProps,\n} from \"../document/types\";\n\nconst selectorFor = (id: string): string => `[data-cms=\"${id}\"]`;\n\nfunction lengthToCss(len: Length): string {\n return `${len.value}${len.unit}`;\n}\n\nfunction sizeToCss(size: SizeMode): string {\n if (size === \"fill\") return \"100%\";\n if (size === \"hug\") return \"auto\";\n return lengthToCss(size);\n}\n\nconst ALIGN: Record<string, string> = {\n start: \"flex-start\",\n center: \"center\",\n end: \"flex-end\",\n stretch: \"stretch\",\n};\nconst JUSTIFY: Record<string, string> = {\n start: \"flex-start\",\n center: \"center\",\n end: \"flex-end\",\n between: \"space-between\",\n around: \"space-around\",\n};\n\n/** Map StyleProps → CSS declarations (property → value). */\nfunction styleDecls(style: Partial<StyleProps>): Record<string, string> {\n const d: Record<string, string> = {};\n if (style.padding) d[\"padding\"] = lengthToCss(style.padding);\n if (style.margin) d[\"margin\"] = lengthToCss(style.margin);\n if (style.radius) d[\"border-radius\"] = lengthToCss(style.radius);\n if (style.background !== undefined) d[\"background\"] = style.background;\n if (style.color !== undefined) d[\"color\"] = style.color;\n if (style.opacity !== undefined) d[\"opacity\"] = String(style.opacity);\n if (style.fontFamily !== undefined) d[\"font-family\"] = style.fontFamily;\n if (style.fontSize) d[\"font-size\"] = lengthToCss(style.fontSize);\n if (style.fontWeight !== undefined) d[\"font-weight\"] = String(style.fontWeight);\n if (style.lineHeight !== undefined) d[\"line-height\"] = String(style.lineHeight);\n if (style.textAlign !== undefined) d[\"text-align\"] = style.textAlign;\n if (style.letterSpacing) d[\"letter-spacing\"] = lengthToCss(style.letterSpacing);\n if (style.border !== undefined) d[\"border\"] = style.border;\n if (style.boxShadow !== undefined) d[\"box-shadow\"] = style.boxShadow;\n if (style.transform !== undefined) d[\"transform\"] = style.transform;\n if (style.filter !== undefined) d[\"filter\"] = style.filter;\n if (style.gap) d[\"gap\"] = lengthToCss(style.gap);\n if (style.align !== undefined) d[\"align-items\"] = ALIGN[style.align] ?? style.align;\n if (style.justify !== undefined) d[\"justify-content\"] = JUSTIFY[style.justify] ?? style.justify;\n return d;\n}\n\n/** Container behaviour from `node.layout`. */\nfunction layoutDecls(node: Node): Record<string, string> {\n const d: Record<string, string> = {};\n if (node.layout === \"stack\") {\n d[\"display\"] = \"flex\";\n d[\"flex-direction\"] = node.style.base.direction ?? \"column\";\n } else if (node.layout === \"grid\") {\n d[\"display\"] = \"grid\";\n d[\"grid-template-columns\"] = `repeat(${node.style.base.columns ?? 1}, 1fr)`;\n } else if (node.layout === \"free\") {\n d[\"position\"] = \"relative\";\n }\n return d;\n}\n\n/** Self-positioning from constraints (absolute pins only inside a `free` parent). */\nfunction constraintDecls(c: Partial<Constraints>, parentFree: boolean): Record<string, string> {\n const d: Record<string, string> = {};\n const transforms: string[] = [];\n if (parentFree) {\n d[\"position\"] = \"absolute\";\n if (c.left) d[\"left\"] = lengthToCss(c.left);\n if (c.right) d[\"right\"] = lengthToCss(c.right);\n if (c.top) d[\"top\"] = lengthToCss(c.top);\n if (c.bottom) d[\"bottom\"] = lengthToCss(c.bottom);\n if (c.centerX) {\n d[\"left\"] = \"50%\";\n transforms.push(\"translateX(-50%)\");\n }\n if (c.centerY) {\n d[\"top\"] = \"50%\";\n transforms.push(\"translateY(-50%)\");\n }\n if (transforms.length) d[\"transform\"] = transforms.join(\" \");\n }\n if (c.width) d[\"width\"] = sizeToCss(c.width);\n if (c.height) d[\"height\"] = sizeToCss(c.height);\n return d;\n}\n\nfunction isParentFree(doc: PageDoc, node: Node): boolean {\n if (node.parent === null) return false; // sections stack in the page flow\n const parent = doc.nodes[node.parent];\n return !parent || parent.layout === undefined || parent.layout === \"free\";\n}\n\nfunction serializeRule(selector: string, decls: Record<string, string>, indent: string): string {\n const props = Object.keys(decls).sort();\n if (props.length === 0) return \"\";\n const body = props.map((p) => `${indent} ${p}: ${decls[p]};`).join(\"\\n\");\n return `${indent}${selector} {\\n${body}\\n${indent}}`;\n}\n\n/** Emit the full stylesheet for a document. Deterministic. */\nexport function emitDocCss(doc: PageDoc): string {\n const breakpointPx = DEFAULT_BREAKPOINTS;\n const blocks: string[] = [];\n\n for (const id of Object.keys(doc.nodes).sort()) {\n const node = doc.nodes[id]!;\n const selector = selectorFor(id);\n const parentFree = isParentFree(doc, node);\n\n // base = style.base + layout + constraints.base\n const base: Record<string, string> = {\n ...layoutDecls(node),\n ...(node.constraints ? constraintDecls(node.constraints.base, parentFree) : {}),\n ...styleDecls(node.style.base),\n };\n const baseRule = serializeRule(selector, base, \"\");\n if (baseRule) blocks.push(baseRule);\n\n // per-breakpoint overrides (mobile-first, in doc order, skipping base)\n for (const bp of doc.breakpoints) {\n if (bp === \"base\") continue;\n const px = breakpointPx[bp];\n if (px === undefined) continue;\n const decls: Record<string, string> = {\n ...(node.constraints?.[bp] ? constraintDecls(node.constraints[bp]!, parentFree) : {}),\n ...(node.style[bp] ? styleDecls(node.style[bp]!) : {}),\n };\n const inner = serializeRule(selector, decls, \" \");\n if (inner) blocks.push(`@media (min-width: ${px}px) {\\n${inner}\\n}`);\n }\n }\n\n return blocks.join(\"\\n\\n\") + (blocks.length ? \"\\n\" : \"\");\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { childrenOf, emitDocCss, isBinding } from './chunk-HYHNMW47.js';
1
+ import { childrenOf, emitDocCss, isBinding } from './chunk-3YZMELJJ.js';
2
2
  import { jsx, jsxs } from 'react/jsx-runtime';
3
3
  import { Fragment } from 'react';
4
4
 
@@ -74,5 +74,5 @@ function CmsPage({
74
74
  }
75
75
 
76
76
  export { CmsPage, RenderNode, defaultRegistry };
77
- //# sourceMappingURL=chunk-5O6U4VIU.js.map
78
- //# sourceMappingURL=chunk-5O6U4VIU.js.map
77
+ //# sourceMappingURL=chunk-ZV7CV54P.js.map
78
+ //# sourceMappingURL=chunk-ZV7CV54P.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/react/registry.tsx","../src/react/RenderNode.tsx","../src/react/CmsPage.tsx"],"names":["jsx"],"mappings":"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","file":"chunk-5O6U4VIU.js","sourcesContent":["import type { ReactNode } from \"react\";\nimport { isBinding, type Bound, type Json, type Node, type PageDoc } from \"../document/types\";\n\nexport interface ElementContext {\n node: Node;\n doc: PageDoc;\n /** Pre-rendered child nodes, in order. */\n children: ReactNode;\n}\n\n/**\n * Renders the INNER content of a node. The outer `[data-cms]` wrapper is added\n * by {@link RenderNode}, so a renderer only returns what goes inside.\n */\nexport type ElementRenderer = (ctx: ElementContext) => ReactNode;\n\nexport type ElementRegistry = Record<string, ElementRenderer>;\n\n/** Resolve a literal prop to a string (bindings resolve in Phase 3). */\nfunction literal(v: Bound<Json> | undefined): string {\n if (v === undefined || v === null) return \"\";\n if (isBinding(v)) return \"\";\n return String(v);\n}\n\n/** Built-in native elements. Containers render their children; leaves render content. */\nexport const defaultRegistry: ElementRegistry = {\n section: ({ children }) => children,\n stack: ({ children }) => children,\n grid: ({ children }) => children,\n frame: ({ children }) => children,\n shape: ({ children }) => children,\n text: ({ node }) => literal(node.props.content),\n heading: ({ node }) => literal(node.props.content) || \"Heading\",\n button: ({ node }) => {\n const label = literal(node.props.label) || literal(node.props.content) || \"Button\";\n const variant = literal(node.props.variant) || \"primary\";\n return <span data-cms-button={variant} style={buttonStyle(variant)}>{label}</span>;\n },\n image: ({ node }) => (\n <img\n src={literal(node.props.src)}\n alt={literal(node.props.alt)}\n style={{ width: \"100%\", height: \"100%\", objectFit: \"cover\", display: \"block\" }}\n />\n ),\n};\n\n/**\n * A self-contained button look so the `button` primitive renders sensibly with\n * zero host CSS. Hosts that want their own button classes override the `button`\n * renderer in their registry (e.g. the sandbox maps it to `.btn .btn-primary`).\n */\nfunction buttonStyle(variant: string): import(\"react\").CSSProperties {\n const base: import(\"react\").CSSProperties = {\n display: \"inline-flex\",\n alignItems: \"center\",\n gap: 6,\n padding: \"9px 16px\",\n borderRadius: 8,\n fontWeight: 600,\n fontSize: 14,\n lineHeight: 1,\n cursor: \"pointer\",\n userSelect: \"none\",\n };\n if (variant === \"ghost\") return { ...base, background: \"transparent\", color: \"inherit\", border: \"1px solid currentColor\" };\n if (variant === \"outline\") return { ...base, background: \"transparent\", color: \"#7c3aed\", border: \"1px solid #7c3aed\" };\n return { ...base, background: \"#7c3aed\", color: \"#fff\", border: \"1px solid #7c3aed\" };\n}\n","import type { ReactElement } from \"react\";\nimport type { PageDoc } from \"../document/types\";\nimport { childrenOf } from \"../document/reduce\";\nimport { defaultRegistry, type ElementRegistry } from \"./registry\";\n\nexport interface RenderNodeProps {\n doc: PageDoc;\n id: string;\n registry?: ElementRegistry;\n}\n\n/**\n * Render one node (and its subtree) to a `[data-cms]`-tagged element. The CSS\n * emitted by `emitDocCss` targets these `data-cms` handles. Interactive /\n * 3rd-party **islands** with no registered renderer become an empty, sized\n * placeholder the host hydrates client-side — mirroring the PHP renderer.\n */\nexport function RenderNode({ doc, id, registry = defaultRegistry }: RenderNodeProps): ReactElement | null {\n const node = doc.nodes[id];\n if (!node) return null;\n\n const kids = childrenOf(doc, id).map((child) => (\n <RenderNode key={child.id} doc={doc} id={child.id} registry={registry} />\n ));\n\n const renderer = registry[node.type];\n const inner = renderer ? renderer({ node, doc, children: kids }) : node.island ? null : kids;\n\n return (\n <div\n data-cms={node.id}\n data-cms-island={node.island ? node.type : undefined}\n className={node.className}\n >\n {inner}\n </div>\n );\n}\n","import { Fragment, type ReactElement } from \"react\";\nimport type { PageDoc } from \"../document/types\";\nimport { emitDocCss } from \"../render/css\";\nimport { RenderNode } from \"./RenderNode\";\nimport { defaultRegistry, type ElementRegistry } from \"./registry\";\n\nexport interface CmsPageProps {\n doc: PageDoc;\n registry?: ElementRegistry;\n /** Inject the compiled stylesheet. Set false when the host emits CSS itself. */\n includeStyles?: boolean;\n}\n\n/** Render a full page document: the compiled stylesheet + every section. */\nexport function CmsPage({\n doc,\n registry = defaultRegistry,\n includeStyles = true,\n}: CmsPageProps): ReactElement {\n return (\n <Fragment>\n {includeStyles ? <style data-cms-styles=\"\">{emitDocCss(doc)}</style> : null}\n {doc.sections.map((id) => (\n <RenderNode key={id} doc={doc} id={id} registry={registry} />\n ))}\n </Fragment>\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/react/registry.tsx","../src/react/RenderNode.tsx","../src/react/CmsPage.tsx"],"names":["jsx"],"mappings":"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","file":"chunk-ZV7CV54P.js","sourcesContent":["import type { ReactNode } from \"react\";\nimport { isBinding, type Bound, type Json, type Node, type PageDoc } from \"../document/types\";\n\nexport interface ElementContext {\n node: Node;\n doc: PageDoc;\n /** Pre-rendered child nodes, in order. */\n children: ReactNode;\n}\n\n/**\n * Renders the INNER content of a node. The outer `[data-cms]` wrapper is added\n * by {@link RenderNode}, so a renderer only returns what goes inside.\n */\nexport type ElementRenderer = (ctx: ElementContext) => ReactNode;\n\nexport type ElementRegistry = Record<string, ElementRenderer>;\n\n/** Resolve a literal prop to a string (bindings resolve in Phase 3). */\nfunction literal(v: Bound<Json> | undefined): string {\n if (v === undefined || v === null) return \"\";\n if (isBinding(v)) return \"\";\n return String(v);\n}\n\n/** Built-in native elements. Containers render their children; leaves render content. */\nexport const defaultRegistry: ElementRegistry = {\n section: ({ children }) => children,\n stack: ({ children }) => children,\n grid: ({ children }) => children,\n frame: ({ children }) => children,\n shape: ({ children }) => children,\n text: ({ node }) => literal(node.props.content),\n heading: ({ node }) => literal(node.props.content) || \"Heading\",\n button: ({ node }) => {\n const label = literal(node.props.label) || literal(node.props.content) || \"Button\";\n const variant = literal(node.props.variant) || \"primary\";\n return <span data-cms-button={variant} style={buttonStyle(variant)}>{label}</span>;\n },\n image: ({ node }) => (\n <img\n src={literal(node.props.src)}\n alt={literal(node.props.alt)}\n style={{ width: \"100%\", height: \"100%\", objectFit: \"cover\", display: \"block\" }}\n />\n ),\n};\n\n/**\n * A self-contained button look so the `button` primitive renders sensibly with\n * zero host CSS. Hosts that want their own button classes override the `button`\n * renderer in their registry (e.g. the sandbox maps it to `.btn .btn-primary`).\n */\nfunction buttonStyle(variant: string): import(\"react\").CSSProperties {\n const base: import(\"react\").CSSProperties = {\n display: \"inline-flex\",\n alignItems: \"center\",\n gap: 6,\n padding: \"9px 16px\",\n borderRadius: 8,\n fontWeight: 600,\n fontSize: 14,\n lineHeight: 1,\n cursor: \"pointer\",\n userSelect: \"none\",\n };\n if (variant === \"ghost\") return { ...base, background: \"transparent\", color: \"inherit\", border: \"1px solid currentColor\" };\n if (variant === \"outline\") return { ...base, background: \"transparent\", color: \"#7c3aed\", border: \"1px solid #7c3aed\" };\n return { ...base, background: \"#7c3aed\", color: \"#fff\", border: \"1px solid #7c3aed\" };\n}\n","import type { ReactElement } from \"react\";\nimport type { PageDoc } from \"../document/types\";\nimport { childrenOf } from \"../document/reduce\";\nimport { defaultRegistry, type ElementRegistry } from \"./registry\";\n\nexport interface RenderNodeProps {\n doc: PageDoc;\n id: string;\n registry?: ElementRegistry;\n}\n\n/**\n * Render one node (and its subtree) to a `[data-cms]`-tagged element. The CSS\n * emitted by `emitDocCss` targets these `data-cms` handles. Interactive /\n * 3rd-party **islands** with no registered renderer become an empty, sized\n * placeholder the host hydrates client-side — mirroring the PHP renderer.\n */\nexport function RenderNode({ doc, id, registry = defaultRegistry }: RenderNodeProps): ReactElement | null {\n const node = doc.nodes[id];\n if (!node) return null;\n\n const kids = childrenOf(doc, id).map((child) => (\n <RenderNode key={child.id} doc={doc} id={child.id} registry={registry} />\n ));\n\n const renderer = registry[node.type];\n const inner = renderer ? renderer({ node, doc, children: kids }) : node.island ? null : kids;\n\n return (\n <div\n data-cms={node.id}\n data-cms-island={node.island ? node.type : undefined}\n className={node.className}\n >\n {inner}\n </div>\n );\n}\n","import { Fragment, type ReactElement } from \"react\";\nimport type { PageDoc } from \"../document/types\";\nimport { emitDocCss } from \"../render/css\";\nimport { RenderNode } from \"./RenderNode\";\nimport { defaultRegistry, type ElementRegistry } from \"./registry\";\n\nexport interface CmsPageProps {\n doc: PageDoc;\n registry?: ElementRegistry;\n /** Inject the compiled stylesheet. Set false when the host emits CSS itself. */\n includeStyles?: boolean;\n}\n\n/** Render a full page document: the compiled stylesheet + every section. */\nexport function CmsPage({\n doc,\n registry = defaultRegistry,\n includeStyles = true,\n}: CmsPageProps): ReactElement {\n return (\n <Fragment>\n {includeStyles ? <style data-cms-styles=\"\">{emitDocCss(doc)}</style> : null}\n {doc.sections.map((id) => (\n <RenderNode key={id} doc={doc} id={id} registry={registry} />\n ))}\n </Fragment>\n );\n}\n"]}
package/dist/editor.cjs CHANGED
@@ -98,6 +98,10 @@ function reduce(doc, op, opts = {}) {
98
98
  props: { ...doc.nodes[op.id].props, ...op.patch }
99
99
  });
100
100
  }
101
+ case "set_layout": {
102
+ if (!doc.nodes[op.id]) return invalid("node not found");
103
+ return patchNode(doc, op.id, { layout: op.layout });
104
+ }
101
105
  case "set_style": {
102
106
  const node = doc.nodes[op.id];
103
107
  if (!node) return invalid("node not found");
@@ -513,31 +517,110 @@ function Canvas({ doc, selection, onSelect, apply }) {
513
517
  function cssEscape(value) {
514
518
  return value.replace(/["\\]/g, "\\$&");
515
519
  }
516
- function LayersPanel({ doc, selection, onSelect }) {
517
- return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { overflow: "auto", padding: 8, fontSize: 13, fontFamily: "system-ui, sans-serif" }, children: doc.sections.map((id) => /* @__PURE__ */ jsxRuntime.jsx(LayerRow, { doc, id, depth: 0, selection, onSelect }, id)) });
520
+
521
+ // src/document/fractional.ts
522
+ var BASE = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
523
+ var RADIX = BASE.length;
524
+ var MID = Math.floor(RADIX / 2);
525
+ function val(c) {
526
+ const i = BASE.indexOf(c);
527
+ return i < 0 ? 0 : i;
518
528
  }
519
- function LayerRow({ doc, id, depth, selection, onSelect }) {
529
+ function keyBetween(a, b) {
530
+ if (a === null) a = "";
531
+ if (b === null) {
532
+ return a + BASE[MID];
533
+ }
534
+ if (a >= b) {
535
+ throw new Error(`keyBetween: lower bound "${a}" is not below upper bound "${b}"`);
536
+ }
537
+ let result = "";
538
+ let i = 0;
539
+ for (; ; ) {
540
+ const ca = i < a.length ? val(a[i]) : 0;
541
+ const cb = i < b.length ? val(b[i]) : RADIX;
542
+ if (cb - ca > 1) {
543
+ result += BASE[ca + Math.floor((cb - ca) / 2)];
544
+ return result;
545
+ }
546
+ if (ca === cb) {
547
+ result += BASE[ca];
548
+ i++;
549
+ continue;
550
+ }
551
+ result += BASE[ca];
552
+ i++;
553
+ for (; ; ) {
554
+ const da = i < a.length ? val(a[i]) : 0;
555
+ if (RADIX - da > 1) {
556
+ result += BASE[da + Math.floor((RADIX - da) / 2)];
557
+ return result;
558
+ }
559
+ result += BASE[da];
560
+ i++;
561
+ }
562
+ }
563
+ }
564
+ var MIME = "application/x-cms-node";
565
+ function isAncestorOrSelf(doc, id, maybeAncestor) {
566
+ for (let cur = id; cur; cur = doc.nodes[cur]?.parent ?? null) {
567
+ if (cur === maybeAncestor) return true;
568
+ }
569
+ return false;
570
+ }
571
+ function LayersPanel({ doc, selection, onSelect, onMove }) {
572
+ const [overId, setOverId] = react.useState(null);
573
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { overflow: "auto", padding: 8, fontSize: 13, fontFamily: "system-ui, sans-serif" }, children: doc.sections.map((id) => /* @__PURE__ */ jsxRuntime.jsx(LayerRow, { doc, id, depth: 0, selection, onSelect, onMove, overId, setOverId }, id)) });
574
+ }
575
+ function LayerRow({ doc, id, depth, selection, onSelect, onMove, overId, setOverId }) {
520
576
  const node = doc.nodes[id];
521
577
  if (!node) return null;
522
578
  const selected = selection === id;
579
+ const draggable = Boolean(onMove);
580
+ const drop = (e) => {
581
+ e.preventDefault();
582
+ e.stopPropagation();
583
+ setOverId(null);
584
+ if (!onMove) return;
585
+ const dragId = e.dataTransfer.getData(MIME);
586
+ if (!dragId || dragId === id || isAncestorOrSelf(doc, id, dragId)) return;
587
+ const siblings = childrenOf(doc, node.parent);
588
+ const idx = siblings.findIndex((n) => n.id === id);
589
+ const next = siblings[idx + 1];
590
+ onMove(dragId, node.parent, keyBetween(node.order, next ? next.order : null));
591
+ };
523
592
  return /* @__PURE__ */ jsxRuntime.jsxs(react.Fragment, { children: [
524
593
  /* @__PURE__ */ jsxRuntime.jsxs(
525
594
  "button",
526
595
  {
527
596
  type: "button",
597
+ draggable,
528
598
  onClick: () => onSelect(id),
599
+ onDragStart: (e) => {
600
+ e.dataTransfer.setData(MIME, id);
601
+ e.dataTransfer.effectAllowed = "move";
602
+ },
603
+ onDragOver: (e) => {
604
+ if (!onMove) return;
605
+ e.preventDefault();
606
+ e.dataTransfer.dropEffect = "move";
607
+ if (overId !== id) setOverId(id);
608
+ },
609
+ onDragLeave: () => overId === id && setOverId(null),
610
+ onDrop: drop,
529
611
  style: {
530
612
  display: "block",
531
613
  width: "100%",
532
614
  textAlign: "left",
533
615
  border: "none",
534
616
  borderRadius: 6,
535
- cursor: "pointer",
617
+ cursor: draggable ? "grab" : "pointer",
536
618
  padding: "4px 8px",
537
619
  paddingLeft: 8 + depth * 14,
538
620
  background: selected ? "#ede9fe" : "transparent",
539
621
  color: selected ? "#5b21b6" : "#334155",
540
- font: "inherit"
622
+ font: "inherit",
623
+ boxShadow: overId === id ? "inset 0 -2px 0 0 #8b5cf6" : "none"
541
624
  },
542
625
  children: [
543
626
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: { opacity: 0.6 }, children: node.type }),
@@ -546,17 +629,7 @@ function LayerRow({ doc, id, depth, selection, onSelect }) {
546
629
  ]
547
630
  }
548
631
  ),
549
- childrenOf(doc, id).map((child) => /* @__PURE__ */ jsxRuntime.jsx(
550
- LayerRow,
551
- {
552
- doc,
553
- id: child.id,
554
- depth: depth + 1,
555
- selection,
556
- onSelect
557
- },
558
- child.id
559
- ))
632
+ childrenOf(doc, id).map((child) => /* @__PURE__ */ jsxRuntime.jsx(LayerRow, { doc, id: child.id, depth: depth + 1, selection, onSelect, onMove, overId, setOverId }, child.id))
560
633
  ] });
561
634
  }
562
635
  var label = {
@@ -688,7 +761,15 @@ function Editor({ defaultValue, onChange }) {
688
761
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: shell, children: [
689
762
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { borderRight: "1px solid #e2e8f0", display: "flex", flexDirection: "column", minHeight: 0 }, children: [
690
763
  /* @__PURE__ */ jsxRuntime.jsx(Toolbar, { ed }),
691
- /* @__PURE__ */ jsxRuntime.jsx(LayersPanel, { doc: ed.state.doc, selection: ed.state.selection, onSelect: ed.select })
764
+ /* @__PURE__ */ jsxRuntime.jsx(
765
+ LayersPanel,
766
+ {
767
+ doc: ed.state.doc,
768
+ selection: ed.state.selection,
769
+ onSelect: ed.select,
770
+ onMove: (id, parent, order) => ed.apply({ t: "move_node", id, parent, order })
771
+ }
772
+ )
692
773
  ] }),
693
774
  /* @__PURE__ */ jsxRuntime.jsx(Canvas, { doc: ed.state.doc, selection: ed.state.selection, onSelect: ed.select, apply: ed.apply }),
694
775
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: { borderLeft: "1px solid #e2e8f0", minHeight: 0 }, children: /* @__PURE__ */ jsxRuntime.jsx(Inspector, { doc: ed.state.doc, selection: ed.state.selection, apply: ed.apply }) })
@@ -721,50 +802,6 @@ function Toolbar({ ed }) {
721
802
  }
722
803
  );
723
804
  }
724
-
725
- // src/document/fractional.ts
726
- var BASE = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
727
- var RADIX = BASE.length;
728
- var MID = Math.floor(RADIX / 2);
729
- function val(c) {
730
- const i = BASE.indexOf(c);
731
- return i < 0 ? 0 : i;
732
- }
733
- function keyBetween(a, b) {
734
- if (a === null) a = "";
735
- if (b === null) {
736
- return a + BASE[MID];
737
- }
738
- if (a >= b) {
739
- throw new Error(`keyBetween: lower bound "${a}" is not below upper bound "${b}"`);
740
- }
741
- let result = "";
742
- let i = 0;
743
- for (; ; ) {
744
- const ca = i < a.length ? val(a[i]) : 0;
745
- const cb = i < b.length ? val(b[i]) : RADIX;
746
- if (cb - ca > 1) {
747
- result += BASE[ca + Math.floor((cb - ca) / 2)];
748
- return result;
749
- }
750
- if (ca === cb) {
751
- result += BASE[ca];
752
- i++;
753
- continue;
754
- }
755
- result += BASE[ca];
756
- i++;
757
- for (; ; ) {
758
- const da = i < a.length ? val(a[i]) : 0;
759
- if (RADIX - da > 1) {
760
- result += BASE[da + Math.floor((RADIX - da) / 2)];
761
- return result;
762
- }
763
- result += BASE[da];
764
- i++;
765
- }
766
- }
767
- }
768
805
  var CONTAINER_TYPES = /* @__PURE__ */ new Set(["section", "frame", "stack", "grid", "shape"]);
769
806
  function NodeInspector({
770
807
  node,
@@ -772,6 +809,7 @@ function NodeInspector({
772
809
  measured,
773
810
  onProps,
774
811
  onStyle,
812
+ onLayout,
775
813
  onTransform,
776
814
  onRemove,
777
815
  onClose
@@ -854,9 +892,44 @@ function NodeInspector({
854
892
  ] }),
855
893
  /* @__PURE__ */ jsxRuntime.jsxs(Row, { children: [
856
894
  /* @__PURE__ */ jsxRuntime.jsx(Num, { label: "Padding", value: base.padding?.value, placeholder: "0", onChange: (v) => onStyle({ padding: v == null ? void 0 : len(v) }) }),
895
+ /* @__PURE__ */ jsxRuntime.jsx(Num, { label: "Margin", value: base.margin?.value, placeholder: "0", onChange: (v) => onStyle({ margin: v == null ? void 0 : len(v) }) })
896
+ ] }),
897
+ /* @__PURE__ */ jsxRuntime.jsx(Row, { children: /* @__PURE__ */ jsxRuntime.jsx(Txt, { label: "Border", value: base.border ?? "", placeholder: "1px solid #e2e8f0", onChange: (v) => onStyle({ border: v || void 0 }) }) })
898
+ ] }),
899
+ CONTAINER_TYPES.has(node.type) ? /* @__PURE__ */ jsxRuntime.jsxs(Group, { label: "Layout", children: [
900
+ /* @__PURE__ */ jsxRuntime.jsxs(Row, { children: [
901
+ /* @__PURE__ */ jsxRuntime.jsx(
902
+ Sel,
903
+ {
904
+ label: "Mode",
905
+ value: node.layout ?? "free",
906
+ options: [["free", "Free"], ["stack", "Stack"], ["grid", "Grid"]],
907
+ onChange: (v) => onLayout?.(v)
908
+ }
909
+ ),
857
910
  /* @__PURE__ */ jsxRuntime.jsx(Num, { label: "Gap", value: base.gap?.value, placeholder: "0", onChange: (v) => onStyle({ gap: v == null ? void 0 : len(v) }) })
911
+ ] }),
912
+ /* @__PURE__ */ jsxRuntime.jsxs(Row, { children: [
913
+ /* @__PURE__ */ jsxRuntime.jsx(
914
+ Sel,
915
+ {
916
+ label: "Align",
917
+ value: base.align ?? "",
918
+ options: [["", "\u2014"], ["start", "Start"], ["center", "Center"], ["end", "End"], ["stretch", "Stretch"]],
919
+ onChange: (v) => onStyle({ align: v || void 0 })
920
+ }
921
+ ),
922
+ /* @__PURE__ */ jsxRuntime.jsx(
923
+ Sel,
924
+ {
925
+ label: "Justify",
926
+ value: base.justify ?? "",
927
+ options: [["", "\u2014"], ["start", "Start"], ["center", "Center"], ["end", "End"], ["between", "Between"], ["around", "Around"]],
928
+ onChange: (v) => onStyle({ justify: v || void 0 })
929
+ }
930
+ )
858
931
  ] })
859
- ] })
932
+ ] }) : null
860
933
  ] })
861
934
  ] });
862
935
  }
@@ -911,6 +984,12 @@ function Num({ label: label2, value, placeholder, onChange, step, min, max }) {
911
984
  )
912
985
  ] });
913
986
  }
987
+ function Txt({ label: label2, value, placeholder, onChange }) {
988
+ return /* @__PURE__ */ jsxRuntime.jsxs("label", { style: field, children: [
989
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: fieldLabel, children: label2 }),
990
+ /* @__PURE__ */ jsxRuntime.jsx("input", { style: input2, type: "text", value, placeholder, onChange: (e) => onChange(e.target.value) })
991
+ ] });
992
+ }
914
993
  function Color({ label: label2, value, onChange }) {
915
994
  return /* @__PURE__ */ jsxRuntime.jsxs("label", { style: field, children: [
916
995
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: fieldLabel, children: label2 }),
@@ -1177,6 +1256,7 @@ function EditablePage({
1177
1256
  );
1178
1257
  const setStyle = (patch) => selection && ed.apply({ t: "set_style", id: selection, breakpoint: "base", patch });
1179
1258
  const setProps = (patch) => selection && ed.apply({ t: "set_props", id: selection, patch });
1259
+ const setLayout = (layout) => selection && ed.apply({ t: "set_layout", id: selection, layout });
1180
1260
  const removeSelected = () => {
1181
1261
  if (!selection) return;
1182
1262
  ed.apply({ t: "remove_node", id: selection });
@@ -1201,6 +1281,8 @@ function EditablePage({
1201
1281
  ed.select(id);
1202
1282
  requestAnimationFrame(() => pageRef.current?.querySelector(`[data-cms="${cssEscape2(id)}"]`)?.focus());
1203
1283
  };
1284
+ const nudgeRef = react.useRef({ transforms, onNodeTransform });
1285
+ nudgeRef.current = { transforms, onNodeTransform };
1204
1286
  react.useEffect(() => {
1205
1287
  if (!editing) return;
1206
1288
  const onKey = (e) => {
@@ -1219,6 +1301,15 @@ function EditablePage({
1219
1301
  } else if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "v") {
1220
1302
  e.preventDefault();
1221
1303
  paste(selection);
1304
+ } else if (selection && /^Arrow(Left|Right|Up|Down)$/.test(e.key)) {
1305
+ const { transforms: tf, onNodeTransform: commit } = nudgeRef.current;
1306
+ if (!commit) return;
1307
+ e.preventDefault();
1308
+ const step = e.shiftKey ? 10 : 1;
1309
+ const dx = e.key === "ArrowLeft" ? -step : e.key === "ArrowRight" ? step : 0;
1310
+ const dy = e.key === "ArrowUp" ? -step : e.key === "ArrowDown" ? step : 0;
1311
+ const cur = tf?.[selection] ?? {};
1312
+ commit(selection, { ...cur, x: (cur.x ?? 0) + dx, y: (cur.y ?? 0) + dy });
1222
1313
  }
1223
1314
  };
1224
1315
  window.addEventListener("keydown", onKey);
@@ -1272,6 +1363,7 @@ function EditablePage({
1272
1363
  measured: { w: box.w, h: box.h },
1273
1364
  onProps: setProps,
1274
1365
  onStyle: setStyle,
1366
+ onLayout: setLayout,
1275
1367
  onTransform: (t) => selection && onNodeTransform?.(selection, t),
1276
1368
  onRemove: removeSelected,
1277
1369
  onClose: () => ed.select(null)