@elytracms/studio 0.0.100 → 0.0.102

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.
Files changed (170) hide show
  1. package/convex/analytics-cost.test.ts +1 -1
  2. package/convex/asset-meta-patch.test.ts +7 -27
  3. package/convex/assets.ts +6 -41
  4. package/convex/cms.ts +2 -29
  5. package/convex/delivery.ts +2 -2
  6. package/convex/record-functions.ts +15 -82
  7. package/convex/redirects.ts +11 -116
  8. package/convex/schema.ts +4 -23
  9. package/convex/shared-capture.convex.test.ts +165 -0
  10. package/convex/triggers.ts +7 -14
  11. package/dist/components/ai/ai-chat-panel-lazy.js +7 -2
  12. package/dist/components/ai/ai-chat-panel-lazy.js.map +1 -1
  13. package/dist/components/ai/ai-chat-panel.js +8 -5
  14. package/dist/components/ai/ai-chat-panel.js.map +1 -1
  15. package/dist/components/ai/proposal-card.js +2 -1
  16. package/dist/components/ai/proposal-card.js.map +1 -1
  17. package/dist/components/ai/proposal-unfilled.d.ts +14 -0
  18. package/dist/components/ai/proposal-unfilled.js +33 -0
  19. package/dist/components/ai/proposal-unfilled.js.map +1 -0
  20. package/dist/components/ai/translate-document-action.js +8 -6
  21. package/dist/components/ai/translate-document-action.js.map +1 -1
  22. package/dist/components/ai/translate-skipped.js +1 -0
  23. package/dist/components/ai/translate-skipped.js.map +1 -1
  24. package/dist/components/app-views/studio-config.d.ts +12 -12
  25. package/dist/components/app-views/studio-config.js.map +1 -1
  26. package/dist/components/assets/asset-detail.js +1 -2
  27. package/dist/components/assets/asset-detail.js.map +1 -1
  28. package/dist/components/assets/asset-metadata.d.ts +2 -9
  29. package/dist/components/assets/asset-metadata.js +3 -10
  30. package/dist/components/assets/asset-metadata.js.map +1 -1
  31. package/dist/components/assets/asset-search.js +1 -1
  32. package/dist/components/assets/asset-search.js.map +1 -1
  33. package/dist/components/assets/asset-selection.js +2 -2
  34. package/dist/components/assets/asset-selection.js.map +1 -1
  35. package/dist/components/assets/assets-collection-view-source.d.ts +2 -3
  36. package/dist/components/assets/assets-collection-view-source.js +2 -3
  37. package/dist/components/assets/assets-collection-view-source.js.map +1 -1
  38. package/dist/components/builder/__fixtures__/reuse-race.d.ts +30 -0
  39. package/dist/components/builder/__fixtures__/reuse-race.js +77 -0
  40. package/dist/components/builder/__fixtures__/reuse-race.js.map +1 -0
  41. package/dist/components/builder/builder-store.d.ts +37 -1
  42. package/dist/components/builder/builder-store.js +21 -6
  43. package/dist/components/builder/builder-store.js.map +1 -1
  44. package/dist/components/builder/canvas-chrome-css.d.ts +1 -1
  45. package/dist/components/builder/canvas-chrome-css.js +1 -1
  46. package/dist/components/builder/canvas-chrome-css.js.map +1 -1
  47. package/dist/components/builder/canvas-chrome.d.ts +1 -1
  48. package/dist/components/builder/canvas-chrome.js +1 -1
  49. package/dist/components/builder/canvas-chrome.js.map +1 -1
  50. package/dist/components/builder/canvas-rebase.d.ts +4 -4
  51. package/dist/components/builder/canvas-rebase.js +3 -2
  52. package/dist/components/builder/canvas-rebase.js.map +1 -1
  53. package/dist/components/builder/canvas-render-context.d.ts +1 -1
  54. package/dist/components/builder/canvas-render-context.js +1 -1
  55. package/dist/components/builder/canvas-render-context.js.map +1 -1
  56. package/dist/components/builder/composition-field-editor-lazy.js +2 -1
  57. package/dist/components/builder/composition-field-editor-lazy.js.map +1 -1
  58. package/dist/components/builder/composition-field-editor.js +3 -3
  59. package/dist/components/builder/composition-field-editor.js.map +1 -1
  60. package/dist/components/builder/reuse-context.d.ts +9 -2
  61. package/dist/components/builder/reuse-context.js +46 -6
  62. package/dist/components/builder/reuse-context.js.map +1 -1
  63. package/dist/components/builder/reuse.d.ts +113 -70
  64. package/dist/components/builder/reuse.js +123 -108
  65. package/dist/components/builder/reuse.js.map +1 -1
  66. package/dist/components/cms/asset-field.js +1 -3
  67. package/dist/components/cms/asset-field.js.map +1 -1
  68. package/dist/components/cms/cell.d.ts +2 -2
  69. package/dist/components/cms/cell.js +8 -18
  70. package/dist/components/cms/cell.js.map +1 -1
  71. package/dist/components/cms/cms-store.d.ts +9 -1
  72. package/dist/components/cms/cms-store.js +1 -0
  73. package/dist/components/cms/cms-store.js.map +1 -1
  74. package/dist/components/cms/detail-editor.js +15 -14
  75. package/dist/components/cms/detail-editor.js.map +1 -1
  76. package/dist/components/cms/field-control.js +13 -1
  77. package/dist/components/cms/field-control.js.map +1 -1
  78. package/dist/components/cms/prop-fields.js +1 -1
  79. package/dist/components/cms/prop-fields.js.map +1 -1
  80. package/dist/components/cms/rich-text/lazy.js +2 -1
  81. package/dist/components/cms/rich-text/lazy.js.map +1 -1
  82. package/dist/components/cms/rich-text/toolbar-state.d.ts +1 -1
  83. package/dist/components/cms/rich-text/toolbar-state.js +1 -1
  84. package/dist/components/cms/rich-text/toolbar-state.js.map +1 -1
  85. package/dist/components/cms/use-field-focus.js +3 -3
  86. package/dist/components/cms/use-field-focus.js.map +1 -1
  87. package/dist/components/cms/version-compare-lazy.js +2 -1
  88. package/dist/components/cms/version-compare-lazy.js.map +1 -1
  89. package/dist/components/components/components-index.js +1 -1
  90. package/dist/components/components/components-index.js.map +1 -1
  91. package/dist/components/deployment-setup-gate.d.ts +1 -1
  92. package/dist/components/deployment-setup-gate.js +1 -1
  93. package/dist/components/deployment-setup-gate.js.map +1 -1
  94. package/dist/components/load-failure.d.ts +62 -0
  95. package/dist/components/load-failure.js +110 -0
  96. package/dist/components/load-failure.js.map +1 -0
  97. package/dist/components/notifications/shell-findings.js +3 -3
  98. package/dist/components/notifications/shell-findings.js.map +1 -1
  99. package/dist/customization.d.ts +3 -3
  100. package/dist/customization.js.map +1 -1
  101. package/dist/lib/ai/apply-content.js +99 -28
  102. package/dist/lib/ai/apply-content.js.map +1 -1
  103. package/dist/lib/ai/chat-context.d.ts +77 -7
  104. package/dist/lib/ai/chat-context.js +139 -14
  105. package/dist/lib/ai/chat-context.js.map +1 -1
  106. package/dist/lib/ai/chat-state.d.ts +7 -1
  107. package/dist/lib/ai/chat-state.js.map +1 -1
  108. package/dist/lib/ai/constraints.d.ts +5 -0
  109. package/dist/lib/ai/constraints.js +21 -6
  110. package/dist/lib/ai/constraints.js.map +1 -1
  111. package/dist/lib/ai/fake-provider.d.ts +76 -26
  112. package/dist/lib/ai/fake-provider.js +210 -66
  113. package/dist/lib/ai/fake-provider.js.map +1 -1
  114. package/dist/lib/ai/index.d.ts +1 -0
  115. package/dist/lib/ai/index.js +1 -0
  116. package/dist/lib/ai/index.js.map +1 -1
  117. package/dist/lib/ai/keyless-assistant.d.ts +37 -0
  118. package/dist/lib/ai/keyless-assistant.js +193 -0
  119. package/dist/lib/ai/keyless-assistant.js.map +1 -0
  120. package/dist/lib/ai/knowledge.d.ts +9 -9
  121. package/dist/lib/ai/knowledge.js +22 -0
  122. package/dist/lib/ai/knowledge.js.map +1 -1
  123. package/dist/lib/ai/shape.d.ts +25 -2
  124. package/dist/lib/ai/shape.js +58 -7
  125. package/dist/lib/ai/shape.js.map +1 -1
  126. package/dist/lib/ai/tools.d.ts +18 -1
  127. package/dist/lib/ai/tools.js +74 -11
  128. package/dist/lib/ai/tools.js.map +1 -1
  129. package/dist/lib/ai/translate-document.d.ts +8 -1
  130. package/dist/lib/ai/translate-document.js +25 -9
  131. package/dist/lib/ai/translate-document.js.map +1 -1
  132. package/dist/lib/ai/types.d.ts +22 -7
  133. package/dist/lib/ai/types.js.map +1 -1
  134. package/dist/lib/ai/update-fields.js +13 -17
  135. package/dist/lib/ai/update-fields.js.map +1 -1
  136. package/dist/lib/ai/writer-form.d.ts +72 -3
  137. package/dist/lib/ai/writer-form.js +320 -42
  138. package/dist/lib/ai/writer-form.js.map +1 -1
  139. package/dist/lib/assets/upload.d.ts +1 -2
  140. package/dist/lib/assets/upload.js +1 -2
  141. package/dist/lib/assets/upload.js.map +1 -1
  142. package/dist/lib/auth-errors.d.ts +1 -1
  143. package/dist/lib/auth-errors.js +1 -1
  144. package/dist/lib/auth-errors.js.map +1 -1
  145. package/dist/lib/engine-version.d.ts +1 -1
  146. package/dist/lib/engine-version.js +1 -1
  147. package/dist/lib/engine-version.js.map +1 -1
  148. package/dist/lib/i18n/messages.de.d.ts +1 -1
  149. package/dist/lib/i18n/messages.de.js +32 -8
  150. package/dist/lib/i18n/messages.de.js.map +1 -1
  151. package/dist/lib/i18n/messages.en.d.ts +25 -3
  152. package/dist/lib/i18n/messages.en.js +40 -10
  153. package/dist/lib/i18n/messages.en.js.map +1 -1
  154. package/dist/lib/operations/cms-persistence.d.ts +28 -0
  155. package/dist/lib/operations/cms-persistence.js +55 -0
  156. package/dist/lib/operations/cms-persistence.js.map +1 -1
  157. package/dist/lib/persistence/convex-adapter.js +7 -2
  158. package/dist/lib/persistence/convex-adapter.js.map +1 -1
  159. package/dist/lib/workspace/index.d.ts +3 -3
  160. package/dist/lib/workspace/index.js +3 -3
  161. package/dist/lib/workspace/index.js.map +1 -1
  162. package/dist/router/navigate-once.d.ts +1 -1
  163. package/dist/router/navigate-once.js.map +1 -1
  164. package/dist/router/root.d.ts +3 -2
  165. package/dist/router/root.js +25 -4
  166. package/dist/router/root.js.map +1 -1
  167. package/package.json +3 -3
  168. package/convex/asset-alt-migration.test.ts +0 -127
  169. package/convex/redirect-migration.test.ts +0 -205
  170. package/convex/version-kind-stamping.test.ts +0 -118
@@ -22,7 +22,7 @@ function commit(state, root, opts = {}) {
22
22
  const normalized = normalizeContainers(root, state.layout);
23
23
  const present = normalized;
24
24
  const preserved = opts.preserved ?? state.preserved;
25
- const coalesce = opts.coalesceKey != null && opts.coalesceKey === state.lastCoalesceKey;
25
+ const coalesce = opts.effect === undefined && opts.coalesceKey != null && opts.coalesceKey === state.lastCoalesceKey;
26
26
  // Normalization can dissolve the node a caller wanted selected (e.g. the
27
27
  // parent container of a removed child) — selection never points at a ghost.
28
28
  const desired = opts.selectedId !== undefined ? opts.selectedId : state.selectedId;
@@ -30,7 +30,7 @@ function commit(state, root, opts = {}) {
30
30
  const desiredRun = opts.selectedRun !== undefined ? opts.selectedRun : state.selectedRun;
31
31
  return {
32
32
  ...state,
33
- past: coalesce ? state.past : [...state.past, snapshotOf(state)].slice(-HISTORY_LIMIT),
33
+ past: coalesce ? state.past : [...state.past, snapshotOf(state, opts.effect)].slice(-HISTORY_LIMIT),
34
34
  present,
35
35
  preserved,
36
36
  future: [],
@@ -40,8 +40,14 @@ function commit(state, root, opts = {}) {
40
40
  refusal: null,
41
41
  };
42
42
  }
43
- function snapshotOf(state) {
44
- return { root: state.present, preserved: state.preserved };
43
+ function snapshotOf(state, effect) {
44
+ return { root: state.present, preserved: state.preserved, ...(effect ? { effect } : {}) };
45
+ }
46
+ /** EC-728 — the crossing to report when an Undo/Redo moves across a step carrying `effect`. */
47
+ function crossing(state, direction, effect) {
48
+ if (!effect)
49
+ return state.historyEffect;
50
+ return { seq: (state.historyEffect?.seq ?? 0) + 1, direction, effect };
45
51
  }
46
52
  /** The run key if that run still exists in this state, else `null` (never a ghost selection). */
47
53
  function liveRun(root, preserved, run) {
@@ -130,13 +136,20 @@ function transition(state, action, placement) {
130
136
  const prev = state.past.at(-1);
131
137
  if (!prev)
132
138
  return state;
133
- return restore(state, prev, state.past.slice(0, -1), [snapshotOf(state), ...state.future]);
139
+ // EC-728: the step's effect moves with it — onto the redo entry that repeats it.
140
+ return {
141
+ ...restore(state, prev, state.past.slice(0, -1), [snapshotOf(state, prev.effect), ...state.future]),
142
+ historyEffect: crossing(state, 'undo', prev.effect),
143
+ };
134
144
  }
135
145
  case 'redo': {
136
146
  const next = state.future[0];
137
147
  if (!next)
138
148
  return state;
139
- return restore(state, next, [...state.past, snapshotOf(state)], state.future.slice(1));
149
+ return {
150
+ ...restore(state, next, [...state.past, snapshotOf(state, next.effect)], state.future.slice(1)),
151
+ historyEffect: crossing(state, 'redo', next.effect),
152
+ };
140
153
  }
141
154
  case 'rebase': {
142
155
  // EC-630: undo never reverts the external change — every snapshot absorbs it. The
@@ -216,6 +229,7 @@ function transition(state, action, placement) {
216
229
  return state; // root or absent id — no-op
217
230
  return commit(state, next, {
218
231
  selectedId: action.select === false ? state.selectedId : (cloned[0]?.id ?? state.selectedId),
232
+ ...(action.effect ? { effect: action.effect } : {}),
219
233
  });
220
234
  }
221
235
  case 'wrap': {
@@ -298,6 +312,7 @@ preserved = []) {
298
312
  layout,
299
313
  refusal: null,
300
314
  rebased: 0,
315
+ historyEffect: null,
301
316
  };
302
317
  }
303
318
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"builder-store.js","sourceRoot":"","sources":["../../../src/components/builder/builder-store.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useMemo, useReducer, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport {\n DEFAULT_SLOT,\n cloneWithNewIds,\n duplicateNode,\n getNode,\n getParentInfo,\n gridContainerNode,\n insertNode,\n moveNode,\n normalizeContainers,\n removeNode,\n removeNodeProp,\n reorderNode,\n replaceNode,\n replacePlacementRefusal,\n setNodeProp,\n subtreePlacementRefusal,\n} from '@elytracms/core/project-graph'\nimport type {\n AllowList,\n ComponentLookup,\n ComponentNode,\n InsertTarget,\n LayoutContainerIds,\n PlacementScope,\n PlacementVerdict,\n PropValue,\n} from '@elytracms/core/project-graph'\nimport { createUuidIdFactory } from '@elytracms/core/persistence'\nimport type { IdFactory } from '@elytracms/core/persistence'\nimport { WRAP_GRID_OPTIONS, applyWrap } from '../../lib/dnd/drop-rules'\nimport { replaceRun, runByKey } from './canvas-runs'\nimport type { PreservedItem } from './canvas-runs'\nimport { rebaseCanvas } from './canvas-rebase'\nimport { createRequiredContext } from '../../lib/required-context'\n\nconst HISTORY_LIMIT = 100\n\n/**\n * A canvas preview-width key (EC-250): `'full'` (unconstrained) or a configured\n * breakpoint name (`'sm'`, `'md'`, …). Dynamic because the set is sourced from\n * the consuming site's breakpoints, so it is a string, not a fixed union — the\n * switcher resolves the available options via {@link resolveCanvasViewports}.\n */\nexport type Viewport = string\n\n/**\n * The builder store's action union. EC-287 retired the DB-resident project graph\n * and its persistence mirror, so these actions are pure in-memory editor state —\n * no action maps to a persisted `graph.*` command anymore. The block-field editor\n * lifts committed edits into the CMS document via `onChange`\n * (`composition-field-editor`); nothing persists a graph.\n */\nexport type BuilderAction =\n | { type: 'select'; id: string | null }\n | { type: 'hover'; id: string | null }\n | { type: 'viewport'; viewport: Viewport }\n | { type: 'undo' }\n | { type: 'redo' }\n /**\n * Insert a node (its subtree re-id'd from the store's id factory). `minted` — the\n * node ALREADY carries fresh ids from the same factory (the paste path mints first\n * so its issue deep links name exactly the ids that land); the reducer then inserts\n * it as given.\n */\n | { type: 'insert'; target: InsertTarget; node: ComponentNode; select?: boolean; minted?: true }\n | { type: 'remove'; id: string }\n | { type: 'duplicate'; id: string }\n | { type: 'reorder'; id: string; delta: -1 | 1 }\n | { type: 'move'; id: string; target: InsertTarget }\n /**\n * EC-282 reuse gesture: replace a node with zero or more nodes at its\n * position (clone-with-fresh-ids, like `insert`). Promote → 1 embed node;\n * detach → the referenced element's inline blocks. One atomic structural\n * edit, one undo step.\n */\n | { type: 'replace'; id: string; nodes: ComponentNode[]; select?: boolean }\n /**\n * EC-162 layout gesture: wrap the reference node and the dragged subject in\n * a two-column Grid container as ONE atomic structural edit (one undo step).\n */\n | {\n type: 'wrap'\n referenceId: string\n side: 'left' | 'right'\n subject: { kind: 'node'; nodeId: string } | { kind: 'component'; node: ComponentNode }\n }\n | { type: 'setProp'; id: string; name: string; value: PropValue; coalesceKey?: string }\n | { type: 'removeProp'; id: string; name: string }\n /** EC-584: the editor dismissed the visible refusal ({@link BuilderState.refusal}). */\n | { type: 'clearRefusal' }\n /**\n * EC-549 — select a typography RUN by its key (`canvas-runs.ts`: the index of its\n * first preserved item); `null` clears it. A run and a node are never both selected.\n */\n | { type: 'selectRun'; run: number | null }\n /** EC-549 — delete a typography run: a real, undoable edit of the preserved list. */\n | { type: 'removeRun'; run: number }\n /**\n * EC-549 — replace a run's typography with `content` (the inspector's rich-text\n * editor on that run; consecutive same-key edits coalesce into one undo step). Empty\n * `content` deletes the run.\n */\n | { type: 'setRun'; run: number; content: readonly unknown[]; coalesceKey?: string }\n /**\n * EC-630 — the field's value changed UNDERNEATH the canvas (a colleague's patch by\n * subscription, a write's canonical row, a resolved conflict), from `from` to `to` (the\n * draft's values before and after): the present absorbs the change and the history and\n * selection rebase onto it (`canvas-rebase.ts`) — never a remount. Not an edit: it adds\n * no history entry, and the canvas writes nothing for it.\n */\n | { type: 'rebase'; from: unknown; to: unknown }\n\n/** The structural actions the placement judge rules on (EC-584): every node that enters or moves. */\nexport type PlacingAction = Extract<BuilderAction, { type: 'insert' | 'replace' | 'move' | 'wrap' | 'duplicate' }>\n\n/** A refused placement (EC-584): the judge's verdict and the action it refused. */\nexport interface PlacementRefusal {\n action: PlacingAction['type']\n verdict: PlacementVerdict\n}\n\n/**\n * One history entry (EC-549): the component tree AND the preserved typography — so a\n * run's deletion or edit is undone exactly like a block's.\n */\nexport interface CanvasSnapshot {\n root: ComponentNode\n preserved: readonly PreservedItem[]\n}\n\nexport interface BuilderState {\n /**\n * The edited composition: one root-bearing component tree (EC-287). The canvas\n * wraps a page document's `body` blocks into a synthetic root and edits it\n * directly — no page/layout wrapper. Undo/redo snapshot it with the preserved\n * typography ({@link CanvasSnapshot}).\n */\n past: CanvasSnapshot[]\n present: ComponentNode\n future: CanvasSnapshot[]\n /**\n * EC-549 — the field value's typography items (no ids, EC-500 D1), each anchored by\n * how many component roots preceded it (`componentPrefix`); the field value is\n * `mergeBlockRoots(present's roots, preserved)`. In the store, so selecting,\n * deleting and editing a run is a real action with truthful undo.\n */\n preserved: readonly PreservedItem[]\n selectedId: string | null\n /** EC-549 — the selected typography run's key, or `null` (exclusive with {@link selectedId}). */\n selectedRun: number | null\n hoveredId: string | null\n viewport: Viewport\n /**\n * EC-578 / EC-595 A2 — THE node-id factory (injected; UUIDs in the studio, a\n * sequential factory in fixtures). It replaced the per-session `node-<n>` counter,\n * which two sessions editing one page advanced identically — both minted the same\n * next id, a `duplicate-node-id` on the second save. Stored ids are untouched.\n */\n newId: IdFactory\n /** Coalescing key of the last edit (consecutive same-key edits don't stack history). */\n lastCoalesceKey: string | null\n /**\n * The components filling the layout roles (EC-591, `ComponentRegistry.layout`),\n * fixed for the store's lifetime: which nodes container normalization treats as\n * Stack/Grid, and the Grid the wrap gesture builds. No hardcoded engine id.\n */\n layout: LayoutContainerIds\n /**\n * EC-584 — the last REFUSED placement, with its reason: the reducer judged an\n * `insert`/`replace`/`move`/`wrap`/`duplicate` against the field and slot\n * vocabularies and canvas eligibility (the core placement judge) and left the\n * composition unchanged. Transient — the next edit, selection, undo or redo clears\n * it — and never silent: the canvas shows it (`PlacementRefusalNotice`). A refused\n * action changes no tree, so it adds no history entry and emits no canvas patch.\n */\n refusal: PlacementRefusal | null\n /**\n * EC-630 — how many `rebase` actions the store has applied (external changes of its\n * field). The canvas sync reads it to tell a store value from before a rebase it\n * dispatched from one after it (`createCanvasSync`).\n */\n rebased: number\n}\n\ntype Action = BuilderAction\n\n/**\n * Push a new graph onto history, coalescing consecutive edits with the same key.\n *\n * Every commit normalizes layout containers (EC-162 ledger fix): the\n * operations layer normalizes every persisted `graph.*` write (EC-161), so\n * the optimistic working graph must apply the same `normalizeContainers` pass\n * or the UI state diverges from the persisted revision after\n * container-emptying moves/removes. The mirrored command stays un-normalized\n * (`graphInvocationFor` sees the raw action); both sides normalize\n * identically, so they land on the same graph.\n */\nfunction commit(\n state: BuilderState,\n root: ComponentNode,\n opts: {\n coalesceKey?: string\n selectedId?: string | null\n preserved?: readonly PreservedItem[]\n selectedRun?: number | null\n } = {},\n): BuilderState {\n const normalized = normalizeContainers(root, state.layout)\n const present = normalized\n const preserved = opts.preserved ?? state.preserved\n const coalesce = opts.coalesceKey != null && opts.coalesceKey === state.lastCoalesceKey\n // Normalization can dissolve the node a caller wanted selected (e.g. the\n // parent container of a removed child) — selection never points at a ghost.\n const desired = opts.selectedId !== undefined ? opts.selectedId : state.selectedId\n const selectedId = desired !== null && getNode(normalized, desired) ? desired : null\n const desiredRun = opts.selectedRun !== undefined ? opts.selectedRun : state.selectedRun\n return {\n ...state,\n past: coalesce ? state.past : [...state.past, snapshotOf(state)].slice(-HISTORY_LIMIT),\n present,\n preserved,\n future: [],\n lastCoalesceKey: opts.coalesceKey ?? null,\n selectedId,\n selectedRun: liveRun(present, preserved, desiredRun),\n refusal: null,\n }\n}\n\nfunction snapshotOf(state: BuilderState): CanvasSnapshot {\n return { root: state.present, preserved: state.preserved }\n}\n\n/** The run key if that run still exists in this state, else `null` (never a ghost selection). */\nfunction liveRun(root: ComponentNode, preserved: readonly PreservedItem[], run: number | null): number | null {\n if (run === null) return null\n return runByKey(rootCount(root), preserved, run) ? run : null\n}\n\n/** How many component roots the composition has (the synthetic root's children). */\nfunction rootCount(root: ComponentNode): number {\n return root.slots?.[DEFAULT_SLOT]?.length ?? 0\n}\n\n/** Restore a history entry (undo/redo), keeping a selection only where it still exists. */\nfunction restore(state: BuilderState, to: CanvasSnapshot, past: CanvasSnapshot[], future: CanvasSnapshot[]): BuilderState {\n return {\n ...state,\n past,\n present: to.root,\n preserved: to.preserved,\n future,\n lastCoalesceKey: null,\n selectedId: getNode(to.root, state.selectedId ?? '') ? state.selectedId : null,\n selectedRun: liveRun(to.root, to.preserved, state.selectedRun),\n refusal: null,\n }\n}\n\n/** Leave the composition untouched and say why (EC-584 — never a silent no-op). */\nfunction refuse(state: BuilderState, action: PlacingAction, verdict: PlacementVerdict): BuilderState {\n return { ...state, refusal: { action: action.type, verdict } }\n}\n\n/**\n * The builder's pure state transition, CONSTRUCTED with the placement scope it judges\n * by (EC-584, owner-ratified 4a): the field's effective top-level vocabulary and the\n * manifest lookup. The scope is static per store and deliberately not reducer STATE\n * (it is the declaration the tree is judged against, not part of the tree) — the\n * reducer RECEIVES it, so there is no `(state, action)` to call without it.\n *\n * Every structural action that places a node — `insert`, `replace`, `move`, `wrap`,\n * `duplicate` — runs the ONE core judge (`subtreePlacementRefusal`) over every node\n * that enters or moves, BEFORE committing. A refusal returns the composition\n * unchanged, with the verdict in {@link BuilderState.refusal}: so no gesture — a\n * forgotten pre-check, promote, detach, a fixture's `slotChildren`, a server-only\n * paste — can place what the field, a slot or canvas eligibility forbids, and the\n * publish gate (which calls the same judge) cannot disagree with the canvas.\n *\n * Exported (with `initialBuilderState`) for unit tests — gesture determinism and\n * normalization lockstep are reducer-level properties, not render behavior.\n */\nexport function createBuilderReducer(\n placement: PlacementScope,\n): (state: BuilderState, action: Action) => BuilderState {\n return (state, action) => transition(state, action, placement)\n}\n\nfunction transition(state: BuilderState, action: Action, placement: PlacementScope): BuilderState {\n const root = state.present\n switch (action.type) {\n case 'select':\n return { ...state, selectedId: action.id, selectedRun: action.id === null ? state.selectedRun : null, refusal: null }\n case 'selectRun':\n return {\n ...state,\n selectedRun: liveRun(root, state.preserved, action.run),\n selectedId: action.run === null ? state.selectedId : null,\n refusal: null,\n }\n case 'removeRun': {\n const run = runByKey(rootCount(root), state.preserved, action.run)\n if (!run) return state\n return commit(state, root, { preserved: replaceRun(state.preserved, run, []), selectedRun: null })\n }\n case 'setRun': {\n const run = runByKey(rootCount(root), state.preserved, action.run)\n if (!run) return state\n return commit(state, root, {\n preserved: replaceRun(state.preserved, run, action.content),\n coalesceKey: action.coalesceKey,\n selectedRun: action.content.length > 0 ? action.run : null,\n })\n }\n case 'hover':\n return state.hoveredId === action.id ? state : { ...state, hoveredId: action.id }\n case 'viewport':\n return { ...state, viewport: action.viewport }\n case 'clearRefusal':\n return state.refusal === null ? state : { ...state, refusal: null }\n\n case 'undo': {\n const prev = state.past.at(-1)\n if (!prev) return state\n return restore(state, prev, state.past.slice(0, -1), [snapshotOf(state), ...state.future])\n }\n case 'redo': {\n const next = state.future[0]\n if (!next) return state\n return restore(state, next, [...state.past, snapshotOf(state)], state.future.slice(1))\n }\n case 'rebase': {\n // EC-630: undo never reverts the external change — every snapshot absorbs it. The\n // next edit starts a new undo step (the step it would coalesce into may have been\n // dropped by the rebase, and an edit must never land in a step it cannot undo).\n const rebased = rebaseCanvas(state, action.from, action.to)\n // Counted even when it changes nothing: the canvas sync waits for every rebase it dispatched.\n if (!rebased) return { ...state, rebased: state.rebased + 1 }\n return {\n ...state,\n ...rebased,\n past: [...rebased.past],\n future: [...rebased.future],\n lastCoalesceKey: null,\n rebased: state.rebased + 1,\n }\n }\n\n case 'insert': {\n const node = action.minted ? action.node : cloneWithNewIds(action.node, state.newId)\n const verdict = subtreePlacementRefusal(root, node, landingOf(action.target), placement)\n if (verdict) return refuse(state, action, verdict)\n const next = insertNode(root, action.target, node)\n return commit(state, next, {\n selectedId: action.select === false ? state.selectedId : node.id,\n })\n }\n case 'remove': {\n const parent = getParentInfo(root, action.id)\n const next = removeNode(root, action.id)\n const selectedId =\n state.selectedId === action.id ? (parent?.parent.id ?? null) : state.selectedId\n return commit(state, next, { selectedId })\n }\n case 'duplicate': {\n // The copy (and every node of its subtree) ENTERS beside the original.\n const info = getParentInfo(root, action.id)\n const source = getNode(root, action.id)\n if (info && source) {\n const verdict = subtreePlacementRefusal(root, source, { parentId: info.parent.id, slot: info.slot }, placement)\n if (verdict) return refuse(state, action, verdict)\n }\n const { root: next, newId } = duplicateNode(root, action.id, state.newId)\n return commit(state, next, { selectedId: newId ?? state.selectedId })\n }\n case 'reorder':\n // Same parent, same slot: the placement does not change — nothing to judge.\n return commit(state, reorderNode(root, action.id, action.delta))\n case 'move': {\n // A node that changes parent or slot lands somewhere new (its descendants keep\n // their own slots, so only it is judged); a move within its own slot is a\n // reorder and leaves the placement as it was.\n const info = getParentInfo(root, action.id)\n const node = getNode(root, action.id)\n const relocates = !info || info.parent.id !== action.target.parentId || info.slot !== action.target.slot\n if (node && relocates) {\n const verdict = subtreePlacementRefusal(root, withoutSlots(node), landingOf(action.target), placement)\n if (verdict) return refuse(state, action, verdict)\n }\n return commit(state, moveNode(root, action.id, action.target))\n }\n case 'replace': {\n // Mint fresh ids for the incoming nodes (page-local namespace) so a detached\n // copy never collides with the page, and a promoted block's source subtree\n // (kept for the new reusable document) stays untouched.\n const cloned = action.nodes.map((n) => cloneWithNewIds(n, state.newId))\n // EC-560: promote (one embed) and detach (the reusable's content) land where the\n // replaced node was — judged there like any insert (the reuse preflight asks the\n // same `replacePlacementRefusal` before any side effect).\n const verdict = replacePlacementRefusal(root, action.id, cloned, placement)\n if (verdict) return refuse(state, action, verdict)\n const next = replaceNode(root, action.id, cloned)\n if (next === root) return state // root or absent id — no-op\n return commit(state, next, {\n selectedId: action.select === false ? state.selectedId : (cloned[0]?.id ?? state.selectedId),\n })\n }\n case 'wrap': {\n // Layout gesture (EC-162): the reference and the dragged subject become\n // the two columns of a new Grid container — ONE structural edit, one\n // history entry, one mirrored command, so a single undo dissolves the\n // whole gesture.\n const partner =\n action.subject.kind === 'node'\n ? { kind: 'node' as const, nodeId: action.subject.nodeId }\n : { kind: 'subtree' as const, node: cloneWithNewIds(action.subject.node, state.newId) }\n // EC-591: the gesture builds the `layout.grid` component; with none, it is off\n // (the drop rules refuse it before dispatch — this guard keeps the reducer total).\n const gridComponentId = state.layout.grid\n if (gridComponentId === undefined) return state\n // EC-584: the Grid lands where the reference is; the reference and the partner\n // land in the Grid's column slot. A moved partner keeps its own subtree (only it\n // relocates); a new one (a blocks-panel tile) enters whole.\n const reference = getNode(root, action.referenceId)\n const referenceInfo = getParentInfo(root, action.referenceId)\n const partnerNode = partner.kind === 'node' ? getNode(root, partner.nodeId) : partner.node\n if (reference && referenceInfo && partnerNode) {\n const columns = [withoutSlots(reference), partner.kind === 'node' ? withoutSlots(partnerNode) : partnerNode]\n const probe = gridContainerNode(gridComponentId, 'wrap', WRAP_GRID_OPTIONS, columns)\n const verdict = subtreePlacementRefusal(\n root,\n probe,\n { parentId: referenceInfo.parent.id, slot: referenceInfo.slot },\n placement,\n )\n if (verdict) return refuse(state, action, verdict)\n }\n const containerId = state.newId()\n const wrapped = applyWrap({\n root,\n gridComponentId,\n referenceId: action.referenceId,\n side: action.side,\n partner,\n containerId,\n })\n if (!wrapped) return state\n return commit(state, wrapped.root, { selectedId: containerId })\n }\n case 'setProp':\n return commit(state, setNodeProp(root, action.id, action.name, action.value), {\n coalesceKey: action.coalesceKey,\n })\n case 'removeProp':\n return commit(state, removeNodeProp(root, action.id, action.name))\n default:\n return state\n }\n}\n\nfunction landingOf(target: InsertTarget): { parentId: string; slot?: string } {\n return { parentId: target.parentId, slot: target.slot }\n}\n\n/** The node alone — a move relocates it; its descendants keep their own slots. */\nfunction withoutSlots(node: ComponentNode): ComponentNode {\n const { slots: _slots, ...rest } = node\n return rest\n}\n\n/**\n * The composition FIELD whose value the store edits (EC-554). Static for the\n * store's lifetime and deliberately NOT reducer state: it is the declaration the\n * edited tree is judged against, not part of the tree. It travels WITH the store\n * so every placement surface under the provider — the canvas and page-tree\n * drag-and-drop, paste (canvas, tree, keyboard), and the insert offer — reads the\n * same value from the one context it already requires; none can be mounted\n * without it, and `BuilderProvider` cannot be mounted without declaring it.\n */\nexport interface CompositionFieldScope {\n /** The field's display label — what a refusal notice names (e.g. \"Canvas\"). */\n label: string\n /**\n * The field's EFFECTIVE top-level vocabulary (EC-188; EC-584 — `fieldAllowList(field)`:\n * its `allow`, else the `blocks` type's default). The judge applies it only to\n * direct children of the composition root (`allowAt`, core `placement.ts`).\n */\n allow: AllowList\n}\n\nexport interface BuilderStore extends BuilderState {\n canUndo: boolean\n canRedo: boolean\n selectedNode: ComponentNode | null\n /** The composition field being edited (EC-554) — see {@link CompositionFieldScope}. */\n field: CompositionFieldScope\n /**\n * EC-584 — what the reducer judges every placement by: the manifest lookup and the\n * field's vocabulary. The gestures' own pre-checks (drag-and-drop, paste, the insert\n * offer, the reuse preflight) read the SAME scope, so an indicator and the sink agree.\n */\n placement: PlacementScope\n dispatch: (action: Action) => void\n}\n\nconst { Context: BuilderContext, useRequired: useBuilder } =\n createRequiredContext<BuilderStore>('Builder')\n\nexport function initialBuilderState(\n composition: ComponentNode,\n layout: LayoutContainerIds,\n newId: IdFactory,\n /** EC-549 — the field value's typography items with their anchors (`splitBlockRoots`). */\n preserved: readonly PreservedItem[] = [],\n): BuilderState {\n // Start container-normal (idempotent on healthy trees) so the session and the\n // lifted baseline agree from the first edit.\n const present = normalizeContainers(composition, layout)\n return {\n past: [],\n present,\n future: [],\n preserved,\n selectedId: present.id,\n selectedRun: null,\n hoveredId: null,\n viewport: 'full',\n // EC-578: fresh ids come from the injected factory — never a counter seeded past\n // the tree's `node-<n>` ids (EC-203), which two sessions advanced identically.\n newId,\n lastCoalesceKey: null,\n layout,\n refusal: null,\n rebased: 0,\n }\n}\n\n/**\n * Undo/redo design (EC-141): the history stack is *UI state* — an in-memory\n * stack of composition-root snapshots. EC-287 retired the DB-resident project\n * graph and its append-only revision log, so the store no longer mirrors edits onto a\n * persistence layer: it is a pure in-memory editor. The block-field editor lifts\n * its committed composition into the open CMS document via `onChange`\n * (`composition-field-editor`); the document's own Save persists it as content.\n * EC-630: a change of the field made underneath the store (a colleague's patch) is\n * absorbed by the present AND every snapshot (`rebase`, `canvas-rebase.ts`), so an undo\n * never reverts it.\n */\nexport function BuilderProvider({\n composition,\n preserved,\n field,\n components,\n layout,\n idFactory,\n children,\n}: {\n /** The composition root to edit (the composition-field editor's synthetic root). */\n composition: ComponentNode\n /** EC-549 — the field value's typography items (`splitBlockRoots(value).preserved`). */\n preserved?: readonly PreservedItem[]\n /** The field the composition is the value of (EC-554) — required, never defaulted. */\n field: CompositionFieldScope\n /** The manifest lookup the placement judge reads (EC-584, `registry.lookup`) — required. */\n components: ComponentLookup\n /** The layout-role component ids (EC-591, `ComponentRegistry.layout`) — required. */\n layout: LayoutContainerIds\n /**\n * EC-578 — the node-id factory. Defaults to UUIDs (`createUuidIdFactory`, one per\n * store); fixtures inject a deterministic factory.\n */\n idFactory?: IdFactory\n children: ReactNode\n}) {\n const [newId] = useState<IdFactory>(() => idFactory ?? createUuidIdFactory())\n // EC-584: the reducer is constructed ONCE with the scope it judges by — the field\n // and the lookup are static for the store's lifetime (a remount re-reads them).\n const [placement] = useState<PlacementScope>(() => ({ components, fieldAllow: field.allow }))\n const [reducer] = useState(() => createBuilderReducer(placement))\n // EC-287: nothing persists a graph — committed edits are lifted into the CMS\n // document by the composition-field editor's `onChange`. `useReducer` drives the\n // pure reducer (consecutive dispatches in one gesture apply in sequence); the\n // store below decorates the state with the derived undo/redo + selection flags.\n const [state, dispatch] = useReducer(reducer, { composition, layout, newId, preserved }, (init) =>\n initialBuilderState(init.composition, init.layout, init.newId, init.preserved),\n )\n\n const store = useMemo<BuilderStore>(() => {\n const selectedNode = state.selectedId\n ? (getNode(state.present, state.selectedId) ?? null)\n : null\n return {\n ...state,\n canUndo: state.past.length > 0,\n canRedo: state.future.length > 0,\n selectedNode,\n field,\n placement,\n dispatch,\n }\n }, [state, field, placement, dispatch])\n\n return <BuilderContext.Provider value={store}>{children}</BuilderContext.Provider>\n}\n\nexport { useBuilder }\n"]}
1
+ {"version":3,"file":"builder-store.js","sourceRoot":"","sources":["../../../src/components/builder/builder-store.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useMemo, useReducer, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport {\n DEFAULT_SLOT,\n cloneWithNewIds,\n duplicateNode,\n getNode,\n getParentInfo,\n gridContainerNode,\n insertNode,\n moveNode,\n normalizeContainers,\n removeNode,\n removeNodeProp,\n reorderNode,\n replaceNode,\n replacePlacementRefusal,\n setNodeProp,\n subtreePlacementRefusal,\n} from '@elytracms/core/project-graph'\nimport type {\n AllowList,\n ComponentLookup,\n ComponentNode,\n InsertTarget,\n LayoutContainerIds,\n PlacementScope,\n PlacementVerdict,\n PropValue,\n} from '@elytracms/core/project-graph'\nimport { createUuidIdFactory } from '@elytracms/core/persistence'\nimport type { IdFactory } from '@elytracms/core/persistence'\nimport type { CmsDocument, DocumentRef } from '@elytracms/core/cms-core'\nimport { WRAP_GRID_OPTIONS, applyWrap } from '../../lib/dnd/drop-rules'\nimport { replaceRun, runByKey } from './canvas-runs'\nimport type { PreservedItem } from './canvas-runs'\nimport { rebaseCanvas } from './canvas-rebase'\nimport { createRequiredContext } from '../../lib/required-context'\n\nconst HISTORY_LIMIT = 100\n\n/**\n * A canvas preview-width key (EC-250): `'full'` (unconstrained) or a configured\n * breakpoint name (`'sm'`, `'md'`, …). Dynamic because the set is sourced from\n * the consuming site's breakpoints, so it is a string, not a fixed union — the\n * switcher resolves the available options via {@link resolveCanvasViewports}.\n */\nexport type Viewport = string\n\n/**\n * The builder store's action union. EC-287 retired the DB-resident project graph\n * and its persistence mirror, so these actions are pure in-memory editor state —\n * no action maps to a persisted `graph.*` command anymore. The block-field editor\n * lifts committed edits into the CMS document via `onChange`\n * (`composition-field-editor`); nothing persists a graph.\n */\nexport type BuilderAction =\n | { type: 'select'; id: string | null }\n | { type: 'hover'; id: string | null }\n | { type: 'viewport'; viewport: Viewport }\n | { type: 'undo' }\n | { type: 'redo' }\n /**\n * Insert a node (its subtree re-id'd from the store's id factory). `minted` — the\n * node ALREADY carries fresh ids from the same factory (the paste path mints first\n * so its issue deep links name exactly the ids that land); the reducer then inserts\n * it as given.\n */\n | { type: 'insert'; target: InsertTarget; node: ComponentNode; select?: boolean; minted?: true }\n | { type: 'remove'; id: string }\n | { type: 'duplicate'; id: string }\n | { type: 'reorder'; id: string; delta: -1 | 1 }\n | { type: 'move'; id: string; target: InsertTarget }\n /**\n * EC-282 reuse gesture: replace a node with zero or more nodes at its\n * position (clone-with-fresh-ids, like `insert`). Promote → 1 embed node;\n * detach → the referenced element's inline blocks. One atomic structural\n * edit, one undo step. EC-728: `effect` — what the step did OUTSIDE the canvas\n * (promote created a reusable document), recorded on that one history entry.\n */\n | { type: 'replace'; id: string; nodes: ComponentNode[]; select?: boolean; effect?: CanvasHistoryEffect }\n /**\n * EC-162 layout gesture: wrap the reference node and the dragged subject in\n * a two-column Grid container as ONE atomic structural edit (one undo step).\n */\n | {\n type: 'wrap'\n referenceId: string\n side: 'left' | 'right'\n subject: { kind: 'node'; nodeId: string } | { kind: 'component'; node: ComponentNode }\n }\n | { type: 'setProp'; id: string; name: string; value: PropValue; coalesceKey?: string }\n | { type: 'removeProp'; id: string; name: string }\n /** EC-584: the editor dismissed the visible refusal ({@link BuilderState.refusal}). */\n | { type: 'clearRefusal' }\n /**\n * EC-549 — select a typography RUN by its key (`canvas-runs.ts`: the index of its\n * first preserved item); `null` clears it. A run and a node are never both selected.\n */\n | { type: 'selectRun'; run: number | null }\n /** EC-549 — delete a typography run: a real, undoable edit of the preserved list. */\n | { type: 'removeRun'; run: number }\n /**\n * EC-549 — replace a run's typography with `content` (the inspector's rich-text\n * editor on that run; consecutive same-key edits coalesce into one undo step). Empty\n * `content` deletes the run.\n */\n | { type: 'setRun'; run: number; content: readonly unknown[]; coalesceKey?: string }\n /**\n * EC-630 — the field's value changed UNDERNEATH the canvas (a colleague's patch by\n * subscription, a write's canonical row, a resolved conflict), from `from` to `to` (the\n * draft's values before and after): the present absorbs the change and the history and\n * selection rebase onto it (`canvas-rebase.ts`) — never a remount. Not an edit: it adds\n * no history entry, and the canvas writes nothing for it.\n */\n | { type: 'rebase'; from: unknown; to: unknown }\n\n/** The structural actions the placement judge rules on (EC-584): every node that enters or moves. */\nexport type PlacingAction = Extract<BuilderAction, { type: 'insert' | 'replace' | 'move' | 'wrap' | 'duplicate' }>\n\n/** A refused placement (EC-584): the judge's verdict and the action it refused. */\nexport interface PlacementRefusal {\n action: PlacingAction['type']\n verdict: PlacementVerdict\n}\n\n/**\n * EC-728 — what a canvas step did OUTSIDE the canvas, so Undo can reverse it and Redo repeat\n * it: *Make reusable* created a reusable document (the canvas half of the gesture — the block\n * swapped for its embed — is the entry's snapshot like any edit's). The store only CARRIES it:\n * the reuse provider performs the document side when an Undo or Redo crosses the step\n * ({@link BuilderState.historyEffect}).\n */\nexport interface CanvasHistoryEffect {\n readonly kind: 'reusable-created'\n readonly ref: DocumentRef\n /** The name the editor gave it — what a notice names. */\n readonly name: string\n /** The document as created — what Redo re-inserts (the same id, insert-if-absent). */\n readonly document: CmsDocument\n}\n\n/**\n * One history entry (EC-549): the component tree AND the preserved typography — so a\n * run's deletion or edit is undone exactly like a block's.\n *\n * EC-728 — `effect` belongs to the STEP between this entry and its neighbour toward the\n * present: on an undo entry (`past`), the step forward from it; on a redo entry (`future`),\n * the step forward to it. Undo and Redo move it with the step, so it is reversed and\n * repeated exactly once per crossing.\n */\nexport interface CanvasSnapshot {\n root: ComponentNode\n preserved: readonly PreservedItem[]\n effect?: CanvasHistoryEffect\n}\n\n/**\n * EC-728 — the last Undo or Redo that crossed a step with an effect outside the canvas:\n * which way, and the effect. `seq` increases with every such crossing, so a consumer handles\n * each exactly once.\n */\nexport interface HistoryEffectStep {\n readonly seq: number\n readonly direction: 'undo' | 'redo'\n readonly effect: CanvasHistoryEffect\n}\n\nexport interface BuilderState {\n /**\n * The edited composition: one root-bearing component tree (EC-287). The canvas\n * wraps a page document's `body` blocks into a synthetic root and edits it\n * directly — no page/layout wrapper. Undo/redo snapshot it with the preserved\n * typography ({@link CanvasSnapshot}).\n */\n past: CanvasSnapshot[]\n present: ComponentNode\n future: CanvasSnapshot[]\n /**\n * EC-549 — the field value's typography items (no ids, EC-500 D1), each anchored by\n * how many component roots preceded it (`componentPrefix`); the field value is\n * `mergeBlockRoots(present's roots, preserved)`. In the store, so selecting,\n * deleting and editing a run is a real action with truthful undo.\n */\n preserved: readonly PreservedItem[]\n selectedId: string | null\n /** EC-549 — the selected typography run's key, or `null` (exclusive with {@link selectedId}). */\n selectedRun: number | null\n hoveredId: string | null\n viewport: Viewport\n /**\n * EC-578 / EC-595 A2 — THE node-id factory (injected; UUIDs in the studio, a\n * sequential factory in fixtures). It replaced the per-session `node-<n>` counter,\n * which two sessions editing one page advanced identically — both minted the same\n * next id, a `duplicate-node-id` on the second save. Stored ids are untouched.\n */\n newId: IdFactory\n /** Coalescing key of the last edit (consecutive same-key edits don't stack history). */\n lastCoalesceKey: string | null\n /**\n * The components filling the layout roles (EC-591, `ComponentRegistry.layout`),\n * fixed for the store's lifetime: which nodes container normalization treats as\n * Stack/Grid, and the Grid the wrap gesture builds. No hardcoded engine id.\n */\n layout: LayoutContainerIds\n /**\n * EC-584 — the last REFUSED placement, with its reason: the reducer judged an\n * `insert`/`replace`/`move`/`wrap`/`duplicate` against the field and slot\n * vocabularies and canvas eligibility (the core placement judge) and left the\n * composition unchanged. Transient — the next edit, selection, undo or redo clears\n * it — and never silent: the canvas shows it (`PlacementRefusalNotice`). A refused\n * action changes no tree, so it adds no history entry and emits no canvas patch.\n */\n refusal: PlacementRefusal | null\n /**\n * EC-630 — how many `rebase` actions the store has applied (external changes of its\n * field). The canvas sync reads it to tell a store value from before a rebase it\n * dispatched from one after it (`createCanvasSync`).\n */\n rebased: number\n /** EC-728 — see {@link HistoryEffectStep}; `null` until an Undo or Redo crosses an effectful step. */\n historyEffect: HistoryEffectStep | null\n}\n\ntype Action = BuilderAction\n\n/**\n * Push a new graph onto history, coalescing consecutive edits with the same key.\n *\n * Every commit normalizes layout containers (EC-162 ledger fix): the\n * operations layer normalizes every persisted `graph.*` write (EC-161), so\n * the optimistic working graph must apply the same `normalizeContainers` pass\n * or the UI state diverges from the persisted revision after\n * container-emptying moves/removes. The mirrored command stays un-normalized\n * (`graphInvocationFor` sees the raw action); both sides normalize\n * identically, so they land on the same graph.\n */\nfunction commit(\n state: BuilderState,\n root: ComponentNode,\n opts: {\n coalesceKey?: string\n selectedId?: string | null\n preserved?: readonly PreservedItem[]\n selectedRun?: number | null\n /** EC-728 — the step's effect outside the canvas (never coalesced: it is its own step). */\n effect?: CanvasHistoryEffect\n } = {},\n): BuilderState {\n const normalized = normalizeContainers(root, state.layout)\n const present = normalized\n const preserved = opts.preserved ?? state.preserved\n const coalesce = opts.effect === undefined && opts.coalesceKey != null && opts.coalesceKey === state.lastCoalesceKey\n // Normalization can dissolve the node a caller wanted selected (e.g. the\n // parent container of a removed child) — selection never points at a ghost.\n const desired = opts.selectedId !== undefined ? opts.selectedId : state.selectedId\n const selectedId = desired !== null && getNode(normalized, desired) ? desired : null\n const desiredRun = opts.selectedRun !== undefined ? opts.selectedRun : state.selectedRun\n return {\n ...state,\n past: coalesce ? state.past : [...state.past, snapshotOf(state, opts.effect)].slice(-HISTORY_LIMIT),\n present,\n preserved,\n future: [],\n lastCoalesceKey: opts.coalesceKey ?? null,\n selectedId,\n selectedRun: liveRun(present, preserved, desiredRun),\n refusal: null,\n }\n}\n\nfunction snapshotOf(state: BuilderState, effect?: CanvasHistoryEffect): CanvasSnapshot {\n return { root: state.present, preserved: state.preserved, ...(effect ? { effect } : {}) }\n}\n\n/** EC-728 — the crossing to report when an Undo/Redo moves across a step carrying `effect`. */\nfunction crossing(\n state: BuilderState,\n direction: HistoryEffectStep['direction'],\n effect: CanvasHistoryEffect | undefined,\n): HistoryEffectStep | null {\n if (!effect) return state.historyEffect\n return { seq: (state.historyEffect?.seq ?? 0) + 1, direction, effect }\n}\n\n/** The run key if that run still exists in this state, else `null` (never a ghost selection). */\nfunction liveRun(root: ComponentNode, preserved: readonly PreservedItem[], run: number | null): number | null {\n if (run === null) return null\n return runByKey(rootCount(root), preserved, run) ? run : null\n}\n\n/** How many component roots the composition has (the synthetic root's children). */\nfunction rootCount(root: ComponentNode): number {\n return root.slots?.[DEFAULT_SLOT]?.length ?? 0\n}\n\n/** Restore a history entry (undo/redo), keeping a selection only where it still exists. */\nfunction restore(state: BuilderState, to: CanvasSnapshot, past: CanvasSnapshot[], future: CanvasSnapshot[]): BuilderState {\n return {\n ...state,\n past,\n present: to.root,\n preserved: to.preserved,\n future,\n lastCoalesceKey: null,\n selectedId: getNode(to.root, state.selectedId ?? '') ? state.selectedId : null,\n selectedRun: liveRun(to.root, to.preserved, state.selectedRun),\n refusal: null,\n }\n}\n\n/** Leave the composition untouched and say why (EC-584 — never a silent no-op). */\nfunction refuse(state: BuilderState, action: PlacingAction, verdict: PlacementVerdict): BuilderState {\n return { ...state, refusal: { action: action.type, verdict } }\n}\n\n/**\n * The builder's pure state transition, CONSTRUCTED with the placement scope it judges\n * by (EC-584, owner-ratified 4a): the field's effective top-level vocabulary and the\n * manifest lookup. The scope is static per store and deliberately not reducer STATE\n * (it is the declaration the tree is judged against, not part of the tree) — the\n * reducer RECEIVES it, so there is no `(state, action)` to call without it.\n *\n * Every structural action that places a node — `insert`, `replace`, `move`, `wrap`,\n * `duplicate` — runs the ONE core judge (`subtreePlacementRefusal`) over every node\n * that enters or moves, BEFORE committing. A refusal returns the composition\n * unchanged, with the verdict in {@link BuilderState.refusal}: so no gesture — a\n * forgotten pre-check, promote, detach, a fixture's `slotChildren`, a server-only\n * paste — can place what the field, a slot or canvas eligibility forbids, and the\n * publish gate (which calls the same judge) cannot disagree with the canvas.\n *\n * Exported (with `initialBuilderState`) for unit tests — gesture determinism and\n * normalization lockstep are reducer-level properties, not render behavior.\n */\nexport function createBuilderReducer(\n placement: PlacementScope,\n): (state: BuilderState, action: Action) => BuilderState {\n return (state, action) => transition(state, action, placement)\n}\n\nfunction transition(state: BuilderState, action: Action, placement: PlacementScope): BuilderState {\n const root = state.present\n switch (action.type) {\n case 'select':\n return { ...state, selectedId: action.id, selectedRun: action.id === null ? state.selectedRun : null, refusal: null }\n case 'selectRun':\n return {\n ...state,\n selectedRun: liveRun(root, state.preserved, action.run),\n selectedId: action.run === null ? state.selectedId : null,\n refusal: null,\n }\n case 'removeRun': {\n const run = runByKey(rootCount(root), state.preserved, action.run)\n if (!run) return state\n return commit(state, root, { preserved: replaceRun(state.preserved, run, []), selectedRun: null })\n }\n case 'setRun': {\n const run = runByKey(rootCount(root), state.preserved, action.run)\n if (!run) return state\n return commit(state, root, {\n preserved: replaceRun(state.preserved, run, action.content),\n coalesceKey: action.coalesceKey,\n selectedRun: action.content.length > 0 ? action.run : null,\n })\n }\n case 'hover':\n return state.hoveredId === action.id ? state : { ...state, hoveredId: action.id }\n case 'viewport':\n return { ...state, viewport: action.viewport }\n case 'clearRefusal':\n return state.refusal === null ? state : { ...state, refusal: null }\n\n case 'undo': {\n const prev = state.past.at(-1)\n if (!prev) return state\n // EC-728: the step's effect moves with it — onto the redo entry that repeats it.\n return {\n ...restore(state, prev, state.past.slice(0, -1), [snapshotOf(state, prev.effect), ...state.future]),\n historyEffect: crossing(state, 'undo', prev.effect),\n }\n }\n case 'redo': {\n const next = state.future[0]\n if (!next) return state\n return {\n ...restore(state, next, [...state.past, snapshotOf(state, next.effect)], state.future.slice(1)),\n historyEffect: crossing(state, 'redo', next.effect),\n }\n }\n case 'rebase': {\n // EC-630: undo never reverts the external change — every snapshot absorbs it. The\n // next edit starts a new undo step (the step it would coalesce into may have been\n // dropped by the rebase, and an edit must never land in a step it cannot undo).\n const rebased = rebaseCanvas(state, action.from, action.to)\n // Counted even when it changes nothing: the canvas sync waits for every rebase it dispatched.\n if (!rebased) return { ...state, rebased: state.rebased + 1 }\n return {\n ...state,\n ...rebased,\n past: [...rebased.past],\n future: [...rebased.future],\n lastCoalesceKey: null,\n rebased: state.rebased + 1,\n }\n }\n\n case 'insert': {\n const node = action.minted ? action.node : cloneWithNewIds(action.node, state.newId)\n const verdict = subtreePlacementRefusal(root, node, landingOf(action.target), placement)\n if (verdict) return refuse(state, action, verdict)\n const next = insertNode(root, action.target, node)\n return commit(state, next, {\n selectedId: action.select === false ? state.selectedId : node.id,\n })\n }\n case 'remove': {\n const parent = getParentInfo(root, action.id)\n const next = removeNode(root, action.id)\n const selectedId =\n state.selectedId === action.id ? (parent?.parent.id ?? null) : state.selectedId\n return commit(state, next, { selectedId })\n }\n case 'duplicate': {\n // The copy (and every node of its subtree) ENTERS beside the original.\n const info = getParentInfo(root, action.id)\n const source = getNode(root, action.id)\n if (info && source) {\n const verdict = subtreePlacementRefusal(root, source, { parentId: info.parent.id, slot: info.slot }, placement)\n if (verdict) return refuse(state, action, verdict)\n }\n const { root: next, newId } = duplicateNode(root, action.id, state.newId)\n return commit(state, next, { selectedId: newId ?? state.selectedId })\n }\n case 'reorder':\n // Same parent, same slot: the placement does not change — nothing to judge.\n return commit(state, reorderNode(root, action.id, action.delta))\n case 'move': {\n // A node that changes parent or slot lands somewhere new (its descendants keep\n // their own slots, so only it is judged); a move within its own slot is a\n // reorder and leaves the placement as it was.\n const info = getParentInfo(root, action.id)\n const node = getNode(root, action.id)\n const relocates = !info || info.parent.id !== action.target.parentId || info.slot !== action.target.slot\n if (node && relocates) {\n const verdict = subtreePlacementRefusal(root, withoutSlots(node), landingOf(action.target), placement)\n if (verdict) return refuse(state, action, verdict)\n }\n return commit(state, moveNode(root, action.id, action.target))\n }\n case 'replace': {\n // Mint fresh ids for the incoming nodes (page-local namespace) so a detached\n // copy never collides with the page, and a promoted block's source subtree\n // (kept for the new reusable document) stays untouched.\n const cloned = action.nodes.map((n) => cloneWithNewIds(n, state.newId))\n // EC-560: promote (one embed) and detach (the reusable's content) land where the\n // replaced node was — judged there like any insert (the reuse preflight asks the\n // same `replacePlacementRefusal` before any side effect).\n const verdict = replacePlacementRefusal(root, action.id, cloned, placement)\n if (verdict) return refuse(state, action, verdict)\n const next = replaceNode(root, action.id, cloned)\n if (next === root) return state // root or absent id — no-op\n return commit(state, next, {\n selectedId: action.select === false ? state.selectedId : (cloned[0]?.id ?? state.selectedId),\n ...(action.effect ? { effect: action.effect } : {}),\n })\n }\n case 'wrap': {\n // Layout gesture (EC-162): the reference and the dragged subject become\n // the two columns of a new Grid container — ONE structural edit, one\n // history entry, one mirrored command, so a single undo dissolves the\n // whole gesture.\n const partner =\n action.subject.kind === 'node'\n ? { kind: 'node' as const, nodeId: action.subject.nodeId }\n : { kind: 'subtree' as const, node: cloneWithNewIds(action.subject.node, state.newId) }\n // EC-591: the gesture builds the `layout.grid` component; with none, it is off\n // (the drop rules refuse it before dispatch — this guard keeps the reducer total).\n const gridComponentId = state.layout.grid\n if (gridComponentId === undefined) return state\n // EC-584: the Grid lands where the reference is; the reference and the partner\n // land in the Grid's column slot. A moved partner keeps its own subtree (only it\n // relocates); a new one (a blocks-panel tile) enters whole.\n const reference = getNode(root, action.referenceId)\n const referenceInfo = getParentInfo(root, action.referenceId)\n const partnerNode = partner.kind === 'node' ? getNode(root, partner.nodeId) : partner.node\n if (reference && referenceInfo && partnerNode) {\n const columns = [withoutSlots(reference), partner.kind === 'node' ? withoutSlots(partnerNode) : partnerNode]\n const probe = gridContainerNode(gridComponentId, 'wrap', WRAP_GRID_OPTIONS, columns)\n const verdict = subtreePlacementRefusal(\n root,\n probe,\n { parentId: referenceInfo.parent.id, slot: referenceInfo.slot },\n placement,\n )\n if (verdict) return refuse(state, action, verdict)\n }\n const containerId = state.newId()\n const wrapped = applyWrap({\n root,\n gridComponentId,\n referenceId: action.referenceId,\n side: action.side,\n partner,\n containerId,\n })\n if (!wrapped) return state\n return commit(state, wrapped.root, { selectedId: containerId })\n }\n case 'setProp':\n return commit(state, setNodeProp(root, action.id, action.name, action.value), {\n coalesceKey: action.coalesceKey,\n })\n case 'removeProp':\n return commit(state, removeNodeProp(root, action.id, action.name))\n default:\n return state\n }\n}\n\nfunction landingOf(target: InsertTarget): { parentId: string; slot?: string } {\n return { parentId: target.parentId, slot: target.slot }\n}\n\n/** The node alone — a move relocates it; its descendants keep their own slots. */\nfunction withoutSlots(node: ComponentNode): ComponentNode {\n const { slots: _slots, ...rest } = node\n return rest\n}\n\n/**\n * The composition FIELD whose value the store edits (EC-554). Static for the\n * store's lifetime and deliberately NOT reducer state: it is the declaration the\n * edited tree is judged against, not part of the tree. It travels WITH the store\n * so every placement surface under the provider — the canvas and page-tree\n * drag-and-drop, paste (canvas, tree, keyboard), and the insert offer — reads the\n * same value from the one context it already requires; none can be mounted\n * without it, and `BuilderProvider` cannot be mounted without declaring it.\n */\nexport interface CompositionFieldScope {\n /** The field's display label — what a refusal notice names (e.g. \"Canvas\"). */\n label: string\n /**\n * The field's EFFECTIVE top-level vocabulary (EC-188; EC-584 — `fieldAllowList(field)`:\n * its `allow`, else the `blocks` type's default). The judge applies it only to\n * direct children of the composition root (`allowAt`, core `placement.ts`).\n */\n allow: AllowList\n}\n\nexport interface BuilderStore extends BuilderState {\n canUndo: boolean\n canRedo: boolean\n selectedNode: ComponentNode | null\n /** The composition field being edited (EC-554) — see {@link CompositionFieldScope}. */\n field: CompositionFieldScope\n /**\n * EC-584 — what the reducer judges every placement by: the manifest lookup and the\n * field's vocabulary. The gestures' own pre-checks (drag-and-drop, paste, the insert\n * offer, the reuse preflight) read the SAME scope, so an indicator and the sink agree.\n */\n placement: PlacementScope\n dispatch: (action: Action) => void\n}\n\nconst { Context: BuilderContext, useRequired: useBuilder } =\n createRequiredContext<BuilderStore>('Builder')\n\nexport function initialBuilderState(\n composition: ComponentNode,\n layout: LayoutContainerIds,\n newId: IdFactory,\n /** EC-549 — the field value's typography items with their anchors (`splitBlockRoots`). */\n preserved: readonly PreservedItem[] = [],\n): BuilderState {\n // Start container-normal (idempotent on healthy trees) so the session and the\n // lifted baseline agree from the first edit.\n const present = normalizeContainers(composition, layout)\n return {\n past: [],\n present,\n future: [],\n preserved,\n selectedId: present.id,\n selectedRun: null,\n hoveredId: null,\n viewport: 'full',\n // EC-578: fresh ids come from the injected factory — never a counter seeded past\n // the tree's `node-<n>` ids (EC-203), which two sessions advanced identically.\n newId,\n lastCoalesceKey: null,\n layout,\n refusal: null,\n rebased: 0,\n historyEffect: null,\n }\n}\n\n/**\n * Undo/redo design (EC-141): the history stack is *UI state* — an in-memory\n * stack of composition-root snapshots. EC-287 retired the DB-resident project\n * graph and its append-only revision log, so the store no longer mirrors edits onto a\n * persistence layer: it is a pure in-memory editor. The block-field editor lifts\n * its committed composition into the open CMS document via `onChange`\n * (`composition-field-editor`); the document's own Save persists it as content.\n * EC-630: a change of the field made underneath the store (a colleague's patch) is\n * absorbed by the present AND every snapshot (`rebase`, `canvas-rebase.ts`), so an undo\n * never reverts it.\n */\nexport function BuilderProvider({\n composition,\n preserved,\n field,\n components,\n layout,\n idFactory,\n children,\n}: {\n /** The composition root to edit (the composition-field editor's synthetic root). */\n composition: ComponentNode\n /** EC-549 — the field value's typography items (`splitBlockRoots(value).preserved`). */\n preserved?: readonly PreservedItem[]\n /** The field the composition is the value of (EC-554) — required, never defaulted. */\n field: CompositionFieldScope\n /** The manifest lookup the placement judge reads (EC-584, `registry.lookup`) — required. */\n components: ComponentLookup\n /** The layout-role component ids (EC-591, `ComponentRegistry.layout`) — required. */\n layout: LayoutContainerIds\n /**\n * EC-578 — the node-id factory. Defaults to UUIDs (`createUuidIdFactory`, one per\n * store); fixtures inject a deterministic factory.\n */\n idFactory?: IdFactory\n children: ReactNode\n}) {\n const [newId] = useState<IdFactory>(() => idFactory ?? createUuidIdFactory())\n // EC-584: the reducer is constructed ONCE with the scope it judges by — the field\n // and the lookup are static for the store's lifetime (a remount re-reads them).\n const [placement] = useState<PlacementScope>(() => ({ components, fieldAllow: field.allow }))\n const [reducer] = useState(() => createBuilderReducer(placement))\n // EC-287: nothing persists a graph — committed edits are lifted into the CMS\n // document by the composition-field editor's `onChange`. `useReducer` drives the\n // pure reducer (consecutive dispatches in one gesture apply in sequence); the\n // store below decorates the state with the derived undo/redo + selection flags.\n const [state, dispatch] = useReducer(reducer, { composition, layout, newId, preserved }, (init) =>\n initialBuilderState(init.composition, init.layout, init.newId, init.preserved),\n )\n\n const store = useMemo<BuilderStore>(() => {\n const selectedNode = state.selectedId\n ? (getNode(state.present, state.selectedId) ?? null)\n : null\n return {\n ...state,\n canUndo: state.past.length > 0,\n canRedo: state.future.length > 0,\n selectedNode,\n field,\n placement,\n dispatch,\n }\n }, [state, field, placement, dispatch])\n\n return <BuilderContext.Provider value={store}>{children}</BuilderContext.Provider>\n}\n\nexport { useBuilder }\n"]}
@@ -43,7 +43,7 @@ export type CanvasWorld = 'legacy' | 'new';
43
43
  * ships — `@layer base` body rules, unlayered rules, later layers — and only
44
44
  * paint when the consumer paints nothing. (An unlayered backdrop would ALWAYS
45
45
  * beat the consumer's layered `@layer base body { background }` — the exact
46
- * NK-004 mechanism this task retires.) Inert in the legacy world: the legacy
46
+ * consumer-reported mechanism this task retires.) Inert in the legacy world: the legacy
47
47
  * chrome CSS puts nothing in the layer.
48
48
  */
49
49
  export declare const CANVAS_BACKDROP_LAYER_STATEMENT = "@layer ec-canvas-backdrop;";
@@ -7,7 +7,7 @@ import { PRESERVE_NODE_ATTR } from '@elytracms/core/rich-text';
7
7
  * ships — `@layer base` body rules, unlayered rules, later layers — and only
8
8
  * paint when the consumer paints nothing. (An unlayered backdrop would ALWAYS
9
9
  * beat the consumer's layered `@layer base body { background }` — the exact
10
- * NK-004 mechanism this task retires.) Inert in the legacy world: the legacy
10
+ * consumer-reported mechanism this task retires.) Inert in the legacy world: the legacy
11
11
  * chrome CSS puts nothing in the layer.
12
12
  */
13
13
  export const CANVAS_BACKDROP_LAYER_STATEMENT = '@layer ec-canvas-backdrop;';
@@ -1 +1 @@
1
- {"version":3,"file":"canvas-chrome-css.js","sourceRoot":"","sources":["../../../src/components/builder/canvas-chrome-css.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAyC9D;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,4BAA4B,CAAA;AAE3E;;;;;;;;GAQG;AACH,MAAM,sBAAsB,GAAG;;;;;CAK9B,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,mBAAmB,GAAG;;CAE3B,CAAA;AAED;;;;;GAKG;AACH,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wCAmJkB,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuCzD,CAAA;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,KAAkB;IAChD,OAAO,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,sBAAsB,CAAC,GAAG,aAAa,CAAA;AAC5F,CAAC","sourcesContent":["import { PRESERVE_NODE_ATTR } from '@elytracms/core/rich-text'\n\n/**\n * The canvas chrome STYLESHEET (EC-414 / AD-B) — the studio's complete in-frame\n * styling, hand-written. This is deliberately NOT a second Tailwind world: the\n * chrome inside the canvas iframe styles itself with `ec-*` classes, `data-ec-*`\n * attributes, and inline styles only, so the consumer's theme is the ONLY design\n * system in the canvas document and the canvas resolves exactly what delivery\n * resolves (AD-A).\n *\n * The chrome contract (binding on ALL in-frame studio UI — EC-404/405/408 build\n * on this):\n *\n * 1. Only `ec-*` class names / `data-ec-*` attributes / inline styles — never\n * studio utility classes, never studio React UI primitives.\n * 2. Chrome roots carry the containment recipe (`.ec-chrome`): `all: initial`\n * plus explicit font/color/line-height/color-scheme re-establishment, so\n * consumer element rules and inherited typography cannot restyle chrome.\n * NOTE (verified, MDN): `all: initial` does NOT stop custom-property\n * inheritance — the real protection is that chrome never CONSUMES a var()\n * name a consumer could define (this file uses no var() at all; the\n * contract test enforces it).\n * 3. Where a consumer ELEMENT selector could still match chrome descendants\n * (`div`, `span`, `button`, `input` rules in a site theme), chrome rules\n * use `!important` deliberately — a small, documented surface: the\n * selection/DnD indicators, the rich-text toolbar, and the locale chip.\n * The `.ec-chrome` recipe itself stays normal-strength on purpose: inline\n * styles are the top styling layer for chrome-owned looks (placeholders,\n * spacing handles) and stylesheet-`!important` would beat them.\n * 4. Escape hatch (documented only, NOT built): if chrome ever outgrows\n * hand-written CSS, the verified prefixed-bundle recipe is the upgrade\n * path — granular Tailwind imports `theme.css layer(theme) prefix(ec)` +\n * `utilities.css layer(utilities) prefix(ec) important source(none)`\n * compiled into an engine-provided entry (compiles clean on 4.3.2).\n */\n\n/** The two canvas stylesheet worlds (AD-C): `legacy` = the pre-EC-414 merged\n * canvas.css (studio design system + consumer theme in one build); `new` = the\n * consumer's standalone full-Tailwind theme entry, studio-free. */\nexport type CanvasWorld = 'legacy' | 'new'\n\n/**\n * Injected FIRST into the canvas head (before the `ec-app-css` link): a bare\n * layer STATEMENT that pins `ec-canvas-backdrop` to the very bottom of the\n * document's layer order (layer order is first-occurrence order). The new-world\n * backdrop rules live in this layer, so they lose to EVERYTHING the consumer\n * ships — `@layer base` body rules, unlayered rules, later layers — and only\n * paint when the consumer paints nothing. (An unlayered backdrop would ALWAYS\n * beat the consumer's layered `@layer base body { background }` — the exact\n * NK-004 mechanism this task retires.) Inert in the legacy world: the legacy\n * chrome CSS puts nothing in the layer.\n */\nexport const CANVAS_BACKDROP_LAYER_STATEMENT = '@layer ec-canvas-backdrop;'\n\n/**\n * NEW-world backdrop (AD-B item 4): the studio-utility body-class default is\n * retired; these neutral hand-written rules give the canvas a light, site-like\n * page when the consumer theme paints nothing, in the weakest possible cascade\n * position (see {@link CANVAS_BACKDROP_LAYER_STATEMENT}). Background sits on\n * BODY (not html) so a consumer `body { background }` keeps its normal\n * canvas-propagation behavior — short pages paint edge-to-edge exactly as the\n * delivery build does.\n */\nconst NEW_WORLD_BACKDROP_CSS = `\n @layer ec-canvas-backdrop {\n html { color-scheme: light; }\n body { background-color: #ffffff; }\n }\n`\n\n/**\n * LEGACY-world backdrop — today's rule, verbatim and unlayered (EC-389-era\n * behavior applies EXACTLY in the legacy world, per AD-C): the canvas backdrop\n * is deliberately always light/site-like, so the native form-control/scrollbar\n * theme is forced to match and a page with real inputs never shows dark native\n * chrome floating on a light page. The light page itself comes from the studio\n * body-class default (`DEFAULT_CANVAS_BODY_CLASS`) resolving against the merged\n * stylesheet's studio tokens.\n */\nconst LEGACY_BACKDROP_CSS = `\n html { color-scheme: light; }\n`\n\n/**\n * The chrome UI rules — identical in both worlds. Sections:\n * page frame, containment recipe, editable wrappers, selection/hover overlay\n * (EC-404), inline editing, rich-text toolbar, locale-fallback chips, drag &\n * drop indicators.\n */\nconst CHROME_UI_CSS = `\n /* ── Page frame ─────────────────────────────────────────────────────────\n The canvas gutter — unlayered on purpose (identical in both worlds, and\n it must beat a consumer body margin: the 16px frame is editor ergonomics,\n not page styling). */\n body { margin: 0; padding: 16px; }\n\n /* ── Containment recipe (chrome contract item 2) ────────────────────────\n Every chrome ROOT (toolbar, spacing handles, in-canvas placeholders)\n carries .ec-chrome: reset everything, then re-establish the chrome's own\n typography and color-scheme explicitly. Inherited custom properties pierce\n 'all: initial' (and even shadow DOM) — that is why chrome never consumes\n var() names (contract-tested), not something this recipe could fix.\n The :where(div, span, p) guard re-links descendant text to the chrome\n root's typography with class-level specificity, so a consumer's bare\n 'div, span { color: … }' element rules cannot restyle chrome text; chrome\n elements styled inline stay inline-styled (inline always wins here). */\n .ec-chrome {\n all: initial;\n display: block;\n box-sizing: border-box;\n font-family: ui-sans-serif, system-ui, sans-serif;\n font-size: 13px;\n line-height: 1.5;\n color: rgb(30, 35, 45);\n color-scheme: light;\n }\n .ec-chrome *,\n .ec-chrome *::before,\n .ec-chrome *::after { box-sizing: border-box; }\n .ec-chrome :where(div, span, p) { color: inherit; font: inherit; }\n\n /* ── Editable wrappers ──────────────────────────────────────────────────\n Scoped to the per-node editable wrappers. The wrappers WRAP consumer\n content, so they are deliberately NOT contained (no .ec-chrome — they must\n stay layout-transparent). EC-404 RETIRED the in-document selection/hover\n outlines (data-selected / data-hovered): an outline paints in the\n element's own stacking order and any full-bleed background or higher\n z-index sibling paints over it — a structural occlusion, not a z-index bug.\n Selection + hover are now drawn by the topmost .ec-overlay layer below.\n The outline-offset stays for the editing outline (and DnD 'into' overrides\n it); its !important (contract item 3) keeps a site theme's element rules\n from hiding those states. */\n .ec-editable { outline-offset: -1px !important; cursor: default; }\n /* EC-549: a typography RUN of the field value (loose prose between blocks) —\n selectable and deletable by its path. Prose flows in the page, so the selected\n run carries its own outline (a run has no frame bar: it is edited and deleted\n in the inspector, or with the Delete key). */\n .ec-run { cursor: default; }\n .ec-run:hover { outline: 1px dashed #93b4ff !important; outline-offset: 2px !important; }\n .ec-run[data-selected] { outline: 2px solid #5b8cff !important; outline-offset: 2px !important; }\n\n /* ── Selection/hover overlay (EC-404) ───────────────────────────────────\n The topmost editing layer, portaled into the iframe body as a sibling of\n the page composition and pinned to the maximum stacking level, so NOTHING\n the page paints can occlude the selection frame. The root carries the\n .ec-chrome containment recipe; geometry (left/top/width/height) is inline,\n everything visual lives here. Element-matching declarations carry\n !important (contract item 3): the frame boxes are <div>s and the controls\n are <button>s a site theme's element rules would otherwise restyle. Frames\n are click-through (pointer-events:none); only the bar re-enables events. */\n .ec-overlay {\n position: absolute !important; top: 0 !important; left: 0 !important;\n z-index: 2147483647 !important; pointer-events: none !important;\n width: 0 !important; height: 0 !important;\n }\n .ec-overlay-frame {\n position: absolute !important; box-sizing: border-box !important;\n pointer-events: none !important; border-radius: 2px !important;\n }\n /* Hover frame (EC-435 AD-3): accent-leaning and heavier than the old 1px\n grey-blue dash (easy to overlook over imagery; live-tuned on light AND\n busy sections — 1.5px collapses to 1px at 1x DPR, so 2px is the smallest\n deterministic bump). Still clearly secondary to the SELECTED frame: the\n same weight but dashed and translucent vs. solid opaque. */\n .ec-overlay-frame[data-ec-hover] { z-index: 1 !important; border: 2px dashed rgba(91,140,255,0.8) !important; }\n .ec-overlay-frame[data-ec-selected] { z-index: 2 !important; border: 2px solid #5b8cff !important; }\n /* ── Prop highlight (EC-408 reverse: field focus → sub-part) ─────────────\n A quiet FILLED tint (no hard border) on the focused prop's element,\n deliberately distinct from the selection frame (solid border) and the\n hover frame (dashed border). Sits below both frames so their borders stay\n crisp; click-through like the frames. */\n .ec-overlay-prop {\n position: absolute !important; z-index: 1 !important; box-sizing: border-box !important;\n pointer-events: none !important; border-radius: 3px !important;\n background: rgba(91,140,255,0.16) !important;\n box-shadow: inset 0 0 0 1px rgba(91,140,255,0.45) !important;\n }\n .ec-overlay-bar {\n position: absolute !important; z-index: 3 !important; pointer-events: auto !important;\n display: flex !important; align-items: center !important; gap: 1px !important;\n height: 22px !important; max-width: 320px !important; padding: 0 2px 0 6px !important;\n border-radius: 6px !important; background: #5b8cff !important;\n box-shadow: 0 2px 8px rgba(8,12,24,0.35) !important;\n font: 600 11px/1 ui-sans-serif, system-ui, sans-serif !important;\n color: #ffffff !important; white-space: nowrap !important;\n }\n .ec-overlay-label {\n overflow: hidden !important; text-overflow: ellipsis !important; white-space: nowrap !important;\n max-width: 180px !important; padding-right: 2px !important;\n font: inherit !important; color: #ffffff !important;\n }\n .ec-overlay-sep {\n width: 1px !important; height: 12px !important; margin: 0 3px !important;\n background: rgba(255,255,255,0.4) !important;\n }\n .ec-overlay-btn {\n display: inline-flex !important; align-items: center !important; justify-content: center !important;\n width: 20px !important; height: 18px !important; padding: 0 !important; margin: 0 !important;\n border: 0 !important; border-radius: 4px !important; background: transparent !important;\n color: #ffffff !important; cursor: pointer !important; -webkit-appearance: none !important; appearance: none !important;\n }\n .ec-overlay-btn:hover { background: rgba(255,255,255,0.22) !important; }\n .ec-overlay-btn:disabled { opacity: 0.4 !important; cursor: default !important; }\n\n /* ── Insertion affordances (EC-405) ─────────────────────────────────────\n A \"+\" button centered ON a block boundary (before/after edge, or the center\n of an empty container) — the geometry is inline (the anchor point), the\n translate here centers the button on it. Appears only on hover/selection\n (never during a drag or inline edit); pointer-events re-enabled so it is\n clickable while the overlay root stays click-through. Element-matching\n declarations !important (contract item 3): it is a <button> a site theme's\n element rules would otherwise restyle. */\n .ec-overlay-add {\n position: absolute !important; z-index: 4 !important; pointer-events: auto !important;\n transform: translate(-50%, -50%) !important;\n display: inline-flex !important; align-items: center !important; justify-content: center !important;\n width: 20px !important; height: 20px !important; padding: 0 !important; margin: 0 !important;\n border: 1.5px solid #ffffff !important; border-radius: 999px !important;\n background: #5b8cff !important; color: #ffffff !important; cursor: pointer !important;\n box-shadow: 0 1px 5px rgba(8,12,24,0.4) !important;\n -webkit-appearance: none !important; appearance: none !important;\n }\n .ec-overlay-add:hover { background: #3f74f5 !important; }\n\n /* ── Inline editing (EC-158 text, EC-159 rich text) ─────────────────────\n The rendered element itself becomes the editor — no overlay, no\n replacement input — so typography, alignment, and layout are\n pixel-identical between edit and render states. The wrapper keeps the\n selection outline; the host only gets a caret. pre-wrap applies to PLAIN\n text hosts only — rich-text markup must keep its rendered whitespace\n handling (no layout shift). */\n .ec-editable[data-ec-editing] { outline: 2px solid #5b8cff !important; }\n .ec-editable [data-ec-edit] { outline: none; cursor: text; }\n .ec-editable [data-ec-edit='text'] { white-space: pre-wrap; }\n /* Preserved atoms during a rich-text session (custom blocks, unknown nodes,\n images, embeds): visible and rendered, but not text-editable. */\n .ec-editable [data-ec-edit='rich'] [${PRESERVE_NODE_ATTR}] { user-select: none; }\n\n /* ── Floating mark toolbar (EC-159), portaled into the iframe ───────────\n All visual declarations !important (contract item 3): the toolbar is\n stylesheet-styled chrome whose button/input elements a site theme's\n element rules would otherwise match. */\n .ec-rt-toolbar { color-scheme: dark !important; }\n .ec-rt-toolbar-row { display: flex !important; align-items: center !important; gap: 2px !important;\n padding: 3px !important; margin: 0 !important;\n border: 1px solid rgba(130,150,190,0.4) !important; border-radius: 8px !important;\n background: rgb(24,26,32) !important; box-shadow: 0 4px 16px rgba(0,0,0,0.4) !important; }\n .ec-rt-toolbar-btn { min-width: 26px !important; height: 24px !important; padding: 0 7px !important;\n border: 0 !important; border-radius: 5px !important;\n background: transparent !important; color: rgb(225,228,236) !important;\n font: 600 12px/1 ui-sans-serif, system-ui, sans-serif !important;\n cursor: pointer !important; }\n .ec-rt-toolbar-btn:hover { background: rgba(130,150,190,0.25) !important; }\n .ec-rt-toolbar-btn[data-active] { background: #5b8cff !important; color: white !important; }\n .ec-rt-toolbar-input { width: 200px !important; height: 24px !important; padding: 0 7px !important;\n border-radius: 5px !important;\n border: 1px solid rgba(130,150,190,0.4) !important; background: rgb(16,18,22) !important;\n color: rgb(225,228,236) !important;\n font: 12px/1 ui-sans-serif, system-ui, sans-serif !important; outline: none !important; }\n\n /* ── Drag & drop states (EC-160/EC-162) ─────────────────────────────────\n Eligibility comes from the shared pure rules (lib/dnd/drop-rules); these\n classes only *show* the resolution. Rejection is explicit and visible\n during the drag — never a silent failed drop. Before/after bars follow the\n parent's flow axis (data-ec-drop-axis); left/right are the\n wrap-into-columns gesture (violet, paired highlight). Indicator\n declarations !important (contract item 3). */\n .ec-editable[data-ec-drop='into'] { outline: 2px solid #34d399 !important; outline-offset: -2px !important; background-color: rgba(52, 211, 153, 0.08) !important; }\n .ec-editable[data-ec-drop='before'] { box-shadow: 0 -3px 0 0 #38bdf8 !important; }\n .ec-editable[data-ec-drop='after'] { box-shadow: 0 3px 0 0 #38bdf8 !important; }\n .ec-editable[data-ec-drop='before'][data-ec-drop-axis='x'] { box-shadow: -3px 0 0 0 #38bdf8 !important; }\n .ec-editable[data-ec-drop='after'][data-ec-drop-axis='x'] { box-shadow: 3px 0 0 0 #38bdf8 !important; }\n .ec-editable[data-ec-drop='left'] { box-shadow: -3px 0 0 0 #a78bfa !important; background-color: rgba(167, 139, 250, 0.1) !important; }\n .ec-editable[data-ec-drop='right'] { box-shadow: 3px 0 0 0 #a78bfa !important; background-color: rgba(167, 139, 250, 0.1) !important; }\n .ec-editable[data-ec-drop='blocked'] { outline: 2px solid #ef4444 !important; outline-offset: -2px !important; background-color: rgba(239, 68, 68, 0.08) !important; cursor: not-allowed !important; }\n`\n\n/**\n * The complete chrome stylesheet for a world — the `ec-outline-css` element's\n * content (id kept from EC-389; the cascade position is pinned by test).\n */\nexport function canvasChromeCss(world: CanvasWorld): string {\n return (world === 'legacy' ? LEGACY_BACKDROP_CSS : NEW_WORLD_BACKDROP_CSS) + CHROME_UI_CSS\n}\n"]}
1
+ {"version":3,"file":"canvas-chrome-css.js","sourceRoot":"","sources":["../../../src/components/builder/canvas-chrome-css.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAyC9D;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,4BAA4B,CAAA;AAE3E;;;;;;;;GAQG;AACH,MAAM,sBAAsB,GAAG;;;;;CAK9B,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,mBAAmB,GAAG;;CAE3B,CAAA;AAED;;;;;GAKG;AACH,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wCAmJkB,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuCzD,CAAA;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,KAAkB;IAChD,OAAO,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,sBAAsB,CAAC,GAAG,aAAa,CAAA;AAC5F,CAAC","sourcesContent":["import { PRESERVE_NODE_ATTR } from '@elytracms/core/rich-text'\n\n/**\n * The canvas chrome STYLESHEET (EC-414 / AD-B) — the studio's complete in-frame\n * styling, hand-written. This is deliberately NOT a second Tailwind world: the\n * chrome inside the canvas iframe styles itself with `ec-*` classes, `data-ec-*`\n * attributes, and inline styles only, so the consumer's theme is the ONLY design\n * system in the canvas document and the canvas resolves exactly what delivery\n * resolves (AD-A).\n *\n * The chrome contract (binding on ALL in-frame studio UI — EC-404/405/408 build\n * on this):\n *\n * 1. Only `ec-*` class names / `data-ec-*` attributes / inline styles — never\n * studio utility classes, never studio React UI primitives.\n * 2. Chrome roots carry the containment recipe (`.ec-chrome`): `all: initial`\n * plus explicit font/color/line-height/color-scheme re-establishment, so\n * consumer element rules and inherited typography cannot restyle chrome.\n * NOTE (verified, MDN): `all: initial` does NOT stop custom-property\n * inheritance — the real protection is that chrome never CONSUMES a var()\n * name a consumer could define (this file uses no var() at all; the\n * contract test enforces it).\n * 3. Where a consumer ELEMENT selector could still match chrome descendants\n * (`div`, `span`, `button`, `input` rules in a site theme), chrome rules\n * use `!important` deliberately — a small, documented surface: the\n * selection/DnD indicators, the rich-text toolbar, and the locale chip.\n * The `.ec-chrome` recipe itself stays normal-strength on purpose: inline\n * styles are the top styling layer for chrome-owned looks (placeholders,\n * spacing handles) and stylesheet-`!important` would beat them.\n * 4. Escape hatch (documented only, NOT built): if chrome ever outgrows\n * hand-written CSS, the verified prefixed-bundle recipe is the upgrade\n * path — granular Tailwind imports `theme.css layer(theme) prefix(ec)` +\n * `utilities.css layer(utilities) prefix(ec) important source(none)`\n * compiled into an engine-provided entry (compiles clean on 4.3.2).\n */\n\n/** The two canvas stylesheet worlds (AD-C): `legacy` = the pre-EC-414 merged\n * canvas.css (studio design system + consumer theme in one build); `new` = the\n * consumer's standalone full-Tailwind theme entry, studio-free. */\nexport type CanvasWorld = 'legacy' | 'new'\n\n/**\n * Injected FIRST into the canvas head (before the `ec-app-css` link): a bare\n * layer STATEMENT that pins `ec-canvas-backdrop` to the very bottom of the\n * document's layer order (layer order is first-occurrence order). The new-world\n * backdrop rules live in this layer, so they lose to EVERYTHING the consumer\n * ships — `@layer base` body rules, unlayered rules, later layers — and only\n * paint when the consumer paints nothing. (An unlayered backdrop would ALWAYS\n * beat the consumer's layered `@layer base body { background }` — the exact\n * consumer-reported mechanism this task retires.) Inert in the legacy world: the legacy\n * chrome CSS puts nothing in the layer.\n */\nexport const CANVAS_BACKDROP_LAYER_STATEMENT = '@layer ec-canvas-backdrop;'\n\n/**\n * NEW-world backdrop (AD-B item 4): the studio-utility body-class default is\n * retired; these neutral hand-written rules give the canvas a light, site-like\n * page when the consumer theme paints nothing, in the weakest possible cascade\n * position (see {@link CANVAS_BACKDROP_LAYER_STATEMENT}). Background sits on\n * BODY (not html) so a consumer `body { background }` keeps its normal\n * canvas-propagation behavior — short pages paint edge-to-edge exactly as the\n * delivery build does.\n */\nconst NEW_WORLD_BACKDROP_CSS = `\n @layer ec-canvas-backdrop {\n html { color-scheme: light; }\n body { background-color: #ffffff; }\n }\n`\n\n/**\n * LEGACY-world backdrop — today's rule, verbatim and unlayered (EC-389-era\n * behavior applies EXACTLY in the legacy world, per AD-C): the canvas backdrop\n * is deliberately always light/site-like, so the native form-control/scrollbar\n * theme is forced to match and a page with real inputs never shows dark native\n * chrome floating on a light page. The light page itself comes from the studio\n * body-class default (`DEFAULT_CANVAS_BODY_CLASS`) resolving against the merged\n * stylesheet's studio tokens.\n */\nconst LEGACY_BACKDROP_CSS = `\n html { color-scheme: light; }\n`\n\n/**\n * The chrome UI rules — identical in both worlds. Sections:\n * page frame, containment recipe, editable wrappers, selection/hover overlay\n * (EC-404), inline editing, rich-text toolbar, locale-fallback chips, drag &\n * drop indicators.\n */\nconst CHROME_UI_CSS = `\n /* ── Page frame ─────────────────────────────────────────────────────────\n The canvas gutter — unlayered on purpose (identical in both worlds, and\n it must beat a consumer body margin: the 16px frame is editor ergonomics,\n not page styling). */\n body { margin: 0; padding: 16px; }\n\n /* ── Containment recipe (chrome contract item 2) ────────────────────────\n Every chrome ROOT (toolbar, spacing handles, in-canvas placeholders)\n carries .ec-chrome: reset everything, then re-establish the chrome's own\n typography and color-scheme explicitly. Inherited custom properties pierce\n 'all: initial' (and even shadow DOM) — that is why chrome never consumes\n var() names (contract-tested), not something this recipe could fix.\n The :where(div, span, p) guard re-links descendant text to the chrome\n root's typography with class-level specificity, so a consumer's bare\n 'div, span { color: … }' element rules cannot restyle chrome text; chrome\n elements styled inline stay inline-styled (inline always wins here). */\n .ec-chrome {\n all: initial;\n display: block;\n box-sizing: border-box;\n font-family: ui-sans-serif, system-ui, sans-serif;\n font-size: 13px;\n line-height: 1.5;\n color: rgb(30, 35, 45);\n color-scheme: light;\n }\n .ec-chrome *,\n .ec-chrome *::before,\n .ec-chrome *::after { box-sizing: border-box; }\n .ec-chrome :where(div, span, p) { color: inherit; font: inherit; }\n\n /* ── Editable wrappers ──────────────────────────────────────────────────\n Scoped to the per-node editable wrappers. The wrappers WRAP consumer\n content, so they are deliberately NOT contained (no .ec-chrome — they must\n stay layout-transparent). EC-404 RETIRED the in-document selection/hover\n outlines (data-selected / data-hovered): an outline paints in the\n element's own stacking order and any full-bleed background or higher\n z-index sibling paints over it — a structural occlusion, not a z-index bug.\n Selection + hover are now drawn by the topmost .ec-overlay layer below.\n The outline-offset stays for the editing outline (and DnD 'into' overrides\n it); its !important (contract item 3) keeps a site theme's element rules\n from hiding those states. */\n .ec-editable { outline-offset: -1px !important; cursor: default; }\n /* EC-549: a typography RUN of the field value (loose prose between blocks) —\n selectable and deletable by its path. Prose flows in the page, so the selected\n run carries its own outline (a run has no frame bar: it is edited and deleted\n in the inspector, or with the Delete key). */\n .ec-run { cursor: default; }\n .ec-run:hover { outline: 1px dashed #93b4ff !important; outline-offset: 2px !important; }\n .ec-run[data-selected] { outline: 2px solid #5b8cff !important; outline-offset: 2px !important; }\n\n /* ── Selection/hover overlay (EC-404) ───────────────────────────────────\n The topmost editing layer, portaled into the iframe body as a sibling of\n the page composition and pinned to the maximum stacking level, so NOTHING\n the page paints can occlude the selection frame. The root carries the\n .ec-chrome containment recipe; geometry (left/top/width/height) is inline,\n everything visual lives here. Element-matching declarations carry\n !important (contract item 3): the frame boxes are <div>s and the controls\n are <button>s a site theme's element rules would otherwise restyle. Frames\n are click-through (pointer-events:none); only the bar re-enables events. */\n .ec-overlay {\n position: absolute !important; top: 0 !important; left: 0 !important;\n z-index: 2147483647 !important; pointer-events: none !important;\n width: 0 !important; height: 0 !important;\n }\n .ec-overlay-frame {\n position: absolute !important; box-sizing: border-box !important;\n pointer-events: none !important; border-radius: 2px !important;\n }\n /* Hover frame (EC-435 AD-3): accent-leaning and heavier than the old 1px\n grey-blue dash (easy to overlook over imagery; live-tuned on light AND\n busy sections — 1.5px collapses to 1px at 1x DPR, so 2px is the smallest\n deterministic bump). Still clearly secondary to the SELECTED frame: the\n same weight but dashed and translucent vs. solid opaque. */\n .ec-overlay-frame[data-ec-hover] { z-index: 1 !important; border: 2px dashed rgba(91,140,255,0.8) !important; }\n .ec-overlay-frame[data-ec-selected] { z-index: 2 !important; border: 2px solid #5b8cff !important; }\n /* ── Prop highlight (EC-408 reverse: field focus → sub-part) ─────────────\n A quiet FILLED tint (no hard border) on the focused prop's element,\n deliberately distinct from the selection frame (solid border) and the\n hover frame (dashed border). Sits below both frames so their borders stay\n crisp; click-through like the frames. */\n .ec-overlay-prop {\n position: absolute !important; z-index: 1 !important; box-sizing: border-box !important;\n pointer-events: none !important; border-radius: 3px !important;\n background: rgba(91,140,255,0.16) !important;\n box-shadow: inset 0 0 0 1px rgba(91,140,255,0.45) !important;\n }\n .ec-overlay-bar {\n position: absolute !important; z-index: 3 !important; pointer-events: auto !important;\n display: flex !important; align-items: center !important; gap: 1px !important;\n height: 22px !important; max-width: 320px !important; padding: 0 2px 0 6px !important;\n border-radius: 6px !important; background: #5b8cff !important;\n box-shadow: 0 2px 8px rgba(8,12,24,0.35) !important;\n font: 600 11px/1 ui-sans-serif, system-ui, sans-serif !important;\n color: #ffffff !important; white-space: nowrap !important;\n }\n .ec-overlay-label {\n overflow: hidden !important; text-overflow: ellipsis !important; white-space: nowrap !important;\n max-width: 180px !important; padding-right: 2px !important;\n font: inherit !important; color: #ffffff !important;\n }\n .ec-overlay-sep {\n width: 1px !important; height: 12px !important; margin: 0 3px !important;\n background: rgba(255,255,255,0.4) !important;\n }\n .ec-overlay-btn {\n display: inline-flex !important; align-items: center !important; justify-content: center !important;\n width: 20px !important; height: 18px !important; padding: 0 !important; margin: 0 !important;\n border: 0 !important; border-radius: 4px !important; background: transparent !important;\n color: #ffffff !important; cursor: pointer !important; -webkit-appearance: none !important; appearance: none !important;\n }\n .ec-overlay-btn:hover { background: rgba(255,255,255,0.22) !important; }\n .ec-overlay-btn:disabled { opacity: 0.4 !important; cursor: default !important; }\n\n /* ── Insertion affordances (EC-405) ─────────────────────────────────────\n A \"+\" button centered ON a block boundary (before/after edge, or the center\n of an empty container) — the geometry is inline (the anchor point), the\n translate here centers the button on it. Appears only on hover/selection\n (never during a drag or inline edit); pointer-events re-enabled so it is\n clickable while the overlay root stays click-through. Element-matching\n declarations !important (contract item 3): it is a <button> a site theme's\n element rules would otherwise restyle. */\n .ec-overlay-add {\n position: absolute !important; z-index: 4 !important; pointer-events: auto !important;\n transform: translate(-50%, -50%) !important;\n display: inline-flex !important; align-items: center !important; justify-content: center !important;\n width: 20px !important; height: 20px !important; padding: 0 !important; margin: 0 !important;\n border: 1.5px solid #ffffff !important; border-radius: 999px !important;\n background: #5b8cff !important; color: #ffffff !important; cursor: pointer !important;\n box-shadow: 0 1px 5px rgba(8,12,24,0.4) !important;\n -webkit-appearance: none !important; appearance: none !important;\n }\n .ec-overlay-add:hover { background: #3f74f5 !important; }\n\n /* ── Inline editing (EC-158 text, EC-159 rich text) ─────────────────────\n The rendered element itself becomes the editor — no overlay, no\n replacement input — so typography, alignment, and layout are\n pixel-identical between edit and render states. The wrapper keeps the\n selection outline; the host only gets a caret. pre-wrap applies to PLAIN\n text hosts only — rich-text markup must keep its rendered whitespace\n handling (no layout shift). */\n .ec-editable[data-ec-editing] { outline: 2px solid #5b8cff !important; }\n .ec-editable [data-ec-edit] { outline: none; cursor: text; }\n .ec-editable [data-ec-edit='text'] { white-space: pre-wrap; }\n /* Preserved atoms during a rich-text session (custom blocks, unknown nodes,\n images, embeds): visible and rendered, but not text-editable. */\n .ec-editable [data-ec-edit='rich'] [${PRESERVE_NODE_ATTR}] { user-select: none; }\n\n /* ── Floating mark toolbar (EC-159), portaled into the iframe ───────────\n All visual declarations !important (contract item 3): the toolbar is\n stylesheet-styled chrome whose button/input elements a site theme's\n element rules would otherwise match. */\n .ec-rt-toolbar { color-scheme: dark !important; }\n .ec-rt-toolbar-row { display: flex !important; align-items: center !important; gap: 2px !important;\n padding: 3px !important; margin: 0 !important;\n border: 1px solid rgba(130,150,190,0.4) !important; border-radius: 8px !important;\n background: rgb(24,26,32) !important; box-shadow: 0 4px 16px rgba(0,0,0,0.4) !important; }\n .ec-rt-toolbar-btn { min-width: 26px !important; height: 24px !important; padding: 0 7px !important;\n border: 0 !important; border-radius: 5px !important;\n background: transparent !important; color: rgb(225,228,236) !important;\n font: 600 12px/1 ui-sans-serif, system-ui, sans-serif !important;\n cursor: pointer !important; }\n .ec-rt-toolbar-btn:hover { background: rgba(130,150,190,0.25) !important; }\n .ec-rt-toolbar-btn[data-active] { background: #5b8cff !important; color: white !important; }\n .ec-rt-toolbar-input { width: 200px !important; height: 24px !important; padding: 0 7px !important;\n border-radius: 5px !important;\n border: 1px solid rgba(130,150,190,0.4) !important; background: rgb(16,18,22) !important;\n color: rgb(225,228,236) !important;\n font: 12px/1 ui-sans-serif, system-ui, sans-serif !important; outline: none !important; }\n\n /* ── Drag & drop states (EC-160/EC-162) ─────────────────────────────────\n Eligibility comes from the shared pure rules (lib/dnd/drop-rules); these\n classes only *show* the resolution. Rejection is explicit and visible\n during the drag — never a silent failed drop. Before/after bars follow the\n parent's flow axis (data-ec-drop-axis); left/right are the\n wrap-into-columns gesture (violet, paired highlight). Indicator\n declarations !important (contract item 3). */\n .ec-editable[data-ec-drop='into'] { outline: 2px solid #34d399 !important; outline-offset: -2px !important; background-color: rgba(52, 211, 153, 0.08) !important; }\n .ec-editable[data-ec-drop='before'] { box-shadow: 0 -3px 0 0 #38bdf8 !important; }\n .ec-editable[data-ec-drop='after'] { box-shadow: 0 3px 0 0 #38bdf8 !important; }\n .ec-editable[data-ec-drop='before'][data-ec-drop-axis='x'] { box-shadow: -3px 0 0 0 #38bdf8 !important; }\n .ec-editable[data-ec-drop='after'][data-ec-drop-axis='x'] { box-shadow: 3px 0 0 0 #38bdf8 !important; }\n .ec-editable[data-ec-drop='left'] { box-shadow: -3px 0 0 0 #a78bfa !important; background-color: rgba(167, 139, 250, 0.1) !important; }\n .ec-editable[data-ec-drop='right'] { box-shadow: 3px 0 0 0 #a78bfa !important; background-color: rgba(167, 139, 250, 0.1) !important; }\n .ec-editable[data-ec-drop='blocked'] { outline: 2px solid #ef4444 !important; outline-offset: -2px !important; background-color: rgba(239, 68, 68, 0.08) !important; cursor: not-allowed !important; }\n`\n\n/**\n * The complete chrome stylesheet for a world — the `ec-outline-css` element's\n * content (id kept from EC-389; the cascade position is pinned by test).\n */\nexport function canvasChromeCss(world: CanvasWorld): string {\n return (world === 'legacy' ? LEGACY_BACKDROP_CSS : NEW_WORLD_BACKDROP_CSS) + CHROME_UI_CSS\n}\n"]}
@@ -84,7 +84,7 @@ export declare function probeCanvasTheme(doc: Document, loadFailed?: boolean): C
84
84
  * a consumer's `defineStudio({ canvas: { bodyClassName } })` REPLACES it
85
85
  * wholesale (including `''` to drop it), because these utility classes
86
86
  * out-cascade a site theme's `@layer base body` rules — keeping them additive
87
- * would make the site's own page background unreachable (the NK-004 bug). In
87
+ * would make the site's own page background unreachable (the bug a consumer reported). In
88
88
  * the NEW world the default retires entirely (the utilities don't exist there);
89
89
  * the chrome stylesheet's neutral backdrop takes over.
90
90
  */
@@ -74,7 +74,7 @@ export function probeCanvasTheme(doc, loadFailed = false) {
74
74
  * a consumer's `defineStudio({ canvas: { bodyClassName } })` REPLACES it
75
75
  * wholesale (including `''` to drop it), because these utility classes
76
76
  * out-cascade a site theme's `@layer base body` rules — keeping them additive
77
- * would make the site's own page background unreachable (the NK-004 bug). In
77
+ * would make the site's own page background unreachable (the bug a consumer reported). In
78
78
  * the NEW world the default retires entirely (the utilities don't exist there);
79
79
  * the chrome stylesheet's neutral backdrop takes over.
80
80
  */
@@ -1 +1 @@
1
- {"version":3,"file":"canvas-chrome.js","sourceRoot":"","sources":["../../../src/components/builder/canvas-chrome.ts"],"names":[],"mappings":"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","sourcesContent":["import type { StudioCanvasTheme } from '../app-views/studio-config'\nimport { CANVAS_BACKDROP_LAYER_STATEMENT, canvasChromeCss } from './canvas-chrome-css'\nimport type { CanvasWorld } from './canvas-chrome-css'\n\nexport { canvasChromeCss, CANVAS_BACKDROP_LAYER_STATEMENT } from './canvas-chrome-css'\nexport type { CanvasWorld } from './canvas-chrome-css'\n\n/**\n * The canvas iframe's document chrome (EC-389, reshaped by EC-414) — the\n * framework-free half of `BuilderCanvas`'s `wireUp` (house pattern, EC-330):\n * everything injected into the preview document lives here, in ONE pinned\n * cascade order, unit-testable without jsdom.\n *\n * EC-414 (AD-A/AD-C): the studio design system is leaving the canvas document.\n * Which world a project lives in is decided by a RUNTIME PROBE of the loaded\n * canvas stylesheet — no flag, no config, zero consumer breakage:\n *\n * - `legacy` (marker tokens present): the merged pre-EC-414 canvas.css that\n * bundles `@elytracms/studio/styles.css` with the consumer theme. The\n * EC-389 body-class default and today's behavior apply EXACTLY.\n * - `new` (markers absent): the consumer's standalone full-Tailwind theme\n * entry. No studio utilities exist in the document, so the body-class\n * default retires and the neutral hand-written backdrop applies instead.\n */\n\n/**\n * The studio design system's marker tokens (AD-C probe). These are the plain\n * `:root` custom properties from the studio's generated `tokens.css`, which any\n * merged legacy build carries VERBATIM — unlike `@theme inline` variables\n * (e.g. `--color-panel`), which Tailwind v4 inlines into utilities and never\n * emits as custom properties (verified against the repo's Tailwind 4.3.2; the\n * task file's `--color-panel` example probe would classify every legacy build\n * as new). Two tokens ANDed so a consumer theme that happens to define one\n * generic name (`--panel`) cannot be mistaken for the studio design system.\n */\nexport const CANVAS_LEGACY_MARKER_TOKENS = ['--panel', '--grid-line'] as const\n\n/**\n * A Tailwind v4 build unconditionally emits `--default-font-family` on\n * `:root, :host` (preflight consumes it) — its absence after a successful\n * stylesheet load means the canvas stylesheet is not a Tailwind theme entry at\n * all (AD-D item 2's \"defines no @theme tokens\" invalid state).\n */\nexport const CANVAS_TAILWIND_MARKER_TOKEN = '--default-font-family'\n\n/** The settled classification of the canvas stylesheet (AD-C + AD-D item 2). */\nexport type CanvasThemeStatus =\n /** Pre-EC-414 merged canvas.css — studio design system + consumer theme. */\n | { kind: 'legacy' }\n /** The EC-414 shape: a standalone consumer Tailwind theme entry. */\n | { kind: 'new' }\n /** The app never provided a canvas stylesheet (`setCanvasStylesheetUrl`). */\n | { kind: 'no-stylesheet' }\n /** The stylesheet `<link>` failed to load. */\n | { kind: 'stylesheet-error' }\n /** Loaded, but defines no Tailwind theme tokens at all. */\n | { kind: 'no-theme-tokens' }\n\n/** The raw computed-style sample the pure classifier decides from. */\nexport interface CanvasThemeProbeSample {\n /** The `ec-app-css` link fired `error` (or never resolved). */\n loadFailed: boolean\n /** Computed values of {@link CANVAS_LEGACY_MARKER_TOKENS}, in order. */\n legacyMarkers: readonly string[]\n /** Computed value of {@link CANVAS_TAILWIND_MARKER_TOKEN}. */\n tailwindMarker: string\n}\n\nconst present = (value: string | null | undefined): boolean =>\n typeof value === 'string' && value.trim() !== ''\n\n/**\n * AD-C pure core: classify the canvas stylesheet world from computed-token\n * presence. Legacy needs EVERY legacy marker; a loaded stylesheet with no\n * Tailwind marker is the explicit `no-theme-tokens` invalid state.\n */\nexport function classifyCanvasTheme(sample: CanvasThemeProbeSample): CanvasThemeStatus {\n if (sample.loadFailed) return { kind: 'stylesheet-error' }\n if (sample.legacyMarkers.length > 0 && sample.legacyMarkers.every(present)) {\n return { kind: 'legacy' }\n }\n if (present(sample.tailwindMarker)) return { kind: 'new' }\n return { kind: 'no-theme-tokens' }\n}\n\n/** The world a status renders as: only the legacy merged stylesheet keeps the\n * EC-389 body-class default; every other state gets the neutral backdrop. */\nexport function canvasWorldOf(status: CanvasThemeStatus): CanvasWorld {\n return status.kind === 'legacy' ? 'legacy' : 'new'\n}\n\n/** Sample the live document's computed root styles for {@link classifyCanvasTheme}. */\nexport function probeCanvasTheme(doc: Document, loadFailed = false): CanvasThemeStatus {\n const root = doc.documentElement\n const win = doc.defaultView\n if (!root || !win) return { kind: 'new' }\n const computed = win.getComputedStyle(root)\n return classifyCanvasTheme({\n loadFailed,\n legacyMarkers: CANVAS_LEGACY_MARKER_TOKENS.map((token) => computed.getPropertyValue(token)),\n tailwindMarker: computed.getPropertyValue(CANVAS_TAILWIND_MARKER_TOKEN),\n })\n}\n\n/**\n * The studio's DEFAULT canvas body classes — LEGACY world only (EC-389 → EC-414):\n * a consumer's `defineStudio({ canvas: { bodyClassName } })` REPLACES it\n * wholesale (including `''` to drop it), because these utility classes\n * out-cascade a site theme's `@layer base body` rules — keeping them additive\n * would make the site's own page background unreachable (the NK-004 bug). In\n * the NEW world the default retires entirely (the utilities don't exist there);\n * the chrome stylesheet's neutral backdrop takes over.\n */\nexport const DEFAULT_CANVAS_BODY_CLASS = 'bg-background text-foreground font-sans'\n\n/** Everything injected into the canvas preview document. */\nexport interface CanvasChromeInputs {\n /** The consuming app's compiled canvas stylesheet URL; `null` → no link (tests/SSR). */\n stylesheetUrl: string | null\n /** Which stylesheet world the document is in (AD-C probe; see `wireCanvasChrome`). */\n world: CanvasWorld\n /** The consumer's canvas theme from `defineStudio({ canvas })` (EC-389). */\n theme?: StudioCanvasTheme\n}\n\n/** The head element surface the chrome creates (link/style). */\nexport interface CanvasChromeElement {\n id: string\n rel?: string\n href?: string\n textContent: string | null\n remove(): void\n}\n\n/**\n * The document surface `applyCanvasChrome` needs (the `canvas-dnd` house\n * pattern: a narrow structural type so the core is unit-testable with a\n * hand-rolled fake — no jsdom). A real iframe `Document` satisfies it.\n */\nexport interface CanvasChromeDocument {\n getElementById(id: string): CanvasChromeElement | null\n createElement(tag: string): CanvasChromeElement\n // `object` (not CanvasChromeElement): the real DOM `appendChild<T extends Node>`\n // is only bivariance-compatible with a parameter every Node satisfies.\n head: { appendChild(node: object): void }\n body: { className: string }\n}\n\n/**\n * Apply the canvas document chrome in the PINNED cascade order (EC-389/EC-414):\n *\n * 1. `ec-layer-order` — the backdrop layer STATEMENT, pinning the chrome's\n * `ec-canvas-backdrop` layer to the bottom of the layer order (it must\n * precede the consumer stylesheet in document order to lose to it);\n * 2. `ec-app-css` — the canvas stylesheet `<link>` (the consumer theme; in\n * the legacy world also the studio design system);\n * 3. `ec-outline-css` — the studio's editing chrome (world-composed:\n * backdrop + selection/DnD/toolbar/containment styles);\n * 4. `ec-canvas-theme` — the consumer's `canvas.tokensCss` (consumer overrides).\n * (An `ec-kit-tokens` layer used to sit between 3 and 4 for an instantiated\n * starter kit's palette; the kits went in EC-574.)\n *\n * Later elements win the cascade at equal specificity, so studio defaults <\n * consumer config — the customization-seam ordering. The body class is the\n * consumer's `bodyClassName` when given (even `''`), else the legacy default in\n * the legacy world and empty in the new world (AD-C). Safe to run repeatedly\n * (iframe onLoad + the already-loaded fast path + the probe settling):\n * links/styles are deduped by id, the chrome CSS is re-composed in place when\n * the world flips, and the theme style is re-appended so a re-wire never\n * changes who wins.\n */\nexport function applyCanvasChrome(d: CanvasChromeDocument, inputs: CanvasChromeInputs): void {\n if (!d.getElementById('ec-layer-order')) {\n const style = d.createElement('style')\n style.id = 'ec-layer-order'\n style.textContent = CANVAS_BACKDROP_LAYER_STATEMENT\n d.head.appendChild(style)\n }\n if (inputs.stylesheetUrl && !d.getElementById('ec-app-css')) {\n const link = d.createElement('link')\n link.id = 'ec-app-css'\n link.rel = 'stylesheet'\n link.href = inputs.stylesheetUrl\n d.head.appendChild(link)\n }\n const chromeCss = canvasChromeCss(inputs.world)\n const outline = d.getElementById('ec-outline-css')\n if (outline) {\n // Probe settled on the other world: re-compose in place (position stable).\n if (outline.textContent !== chromeCss) outline.textContent = chromeCss\n } else {\n const style = d.createElement('style')\n style.id = 'ec-outline-css'\n style.textContent = chromeCss\n d.head.appendChild(style)\n }\n // The theme layer re-appends (remove + add) on every wire, so it always lands\n // last — after a re-composed chrome layer too.\n appendCssLayer(d, 'ec-canvas-theme', inputs.theme?.tokensCss)\n d.body.className =\n inputs.theme?.bodyClassName ?? (inputs.world === 'legacy' ? DEFAULT_CANVAS_BODY_CLASS : '')\n}\n\n/** Replace-append one identified `<style>` layer; absent CSS removes a stale element. */\nfunction appendCssLayer(d: CanvasChromeDocument, id: string, css: string | undefined): void {\n const existing = d.getElementById(id)\n if (existing) existing.remove()\n if (css === undefined || css === '') return\n const style = d.createElement('style')\n style.id = id\n style.textContent = css\n d.head.appendChild(style)\n}\n\n/** `applyCanvasChrome` inputs minus the probe-owned `world`, plus the settle callback. */\nexport interface WireCanvasChromeOptions {\n stylesheetUrl: string | null\n theme?: StudioCanvasTheme\n /**\n * Fired when the classification SETTLES — the stylesheet finished loading\n * (or failing), or there is no stylesheet to wait for. Feeds the AD-D dev\n * notice; may fire again on re-wires (callers dedupe by kind).\n */\n onStatus?: (status: CanvasThemeStatus) => void\n}\n\n/** Links whose settle listeners are already attached (re-wires must not stack them). */\nconst wiredLinks = new WeakSet<object>()\n\n/**\n * The DOM-level wiring around {@link applyCanvasChrome}: applies the chrome\n * immediately with the CURRENT classification, then re-applies once the\n * `ec-app-css` link settles (the AD-C probe reads computed custom properties,\n * which exist only after the stylesheet loads). Before the stylesheet arrives\n * neither world's styling is resolvable anyway (no consumer CSS yet), so the\n * pre-settle pass is visually neutral in both worlds.\n */\nexport function wireCanvasChrome(doc: Document, options: WireCanvasChromeOptions): void {\n const apply = (settled: boolean, loadFailed: boolean) => {\n const status: CanvasThemeStatus = options.stylesheetUrl\n ? probeCanvasTheme(doc, loadFailed)\n : { kind: 'no-stylesheet' }\n applyCanvasChrome(doc, {\n stylesheetUrl: options.stylesheetUrl,\n world: canvasWorldOf(status),\n ...(options.theme !== undefined ? { theme: options.theme } : {}),\n })\n if (settled) options.onStatus?.(status)\n }\n\n if (!options.stylesheetUrl) {\n apply(true, false)\n return\n }\n // First pass injects the link (and everything else) with the current sample.\n const already = doc.getElementById('ec-app-css') as HTMLLinkElement | null\n const loaded = already?.sheet != null\n apply(loaded, false)\n if (loaded) return\n const link = doc.getElementById('ec-app-css') as HTMLLinkElement | null\n if (!link || wiredLinks.has(link)) return\n wiredLinks.add(link)\n link.addEventListener('load', () => apply(true, false), { once: true })\n link.addEventListener('error', () => apply(true, true), { once: true })\n}\n"]}
1
+ {"version":3,"file":"canvas-chrome.js","sourceRoot":"","sources":["../../../src/components/builder/canvas-chrome.ts"],"names":[],"mappings":"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","sourcesContent":["import type { StudioCanvasTheme } from '../app-views/studio-config'\nimport { CANVAS_BACKDROP_LAYER_STATEMENT, canvasChromeCss } from './canvas-chrome-css'\nimport type { CanvasWorld } from './canvas-chrome-css'\n\nexport { canvasChromeCss, CANVAS_BACKDROP_LAYER_STATEMENT } from './canvas-chrome-css'\nexport type { CanvasWorld } from './canvas-chrome-css'\n\n/**\n * The canvas iframe's document chrome (EC-389, reshaped by EC-414) — the\n * framework-free half of `BuilderCanvas`'s `wireUp` (house pattern, EC-330):\n * everything injected into the preview document lives here, in ONE pinned\n * cascade order, unit-testable without jsdom.\n *\n * EC-414 (AD-A/AD-C): the studio design system is leaving the canvas document.\n * Which world a project lives in is decided by a RUNTIME PROBE of the loaded\n * canvas stylesheet — no flag, no config, zero consumer breakage:\n *\n * - `legacy` (marker tokens present): the merged pre-EC-414 canvas.css that\n * bundles `@elytracms/studio/styles.css` with the consumer theme. The\n * EC-389 body-class default and today's behavior apply EXACTLY.\n * - `new` (markers absent): the consumer's standalone full-Tailwind theme\n * entry. No studio utilities exist in the document, so the body-class\n * default retires and the neutral hand-written backdrop applies instead.\n */\n\n/**\n * The studio design system's marker tokens (AD-C probe). These are the plain\n * `:root` custom properties from the studio's generated `tokens.css`, which any\n * merged legacy build carries VERBATIM — unlike `@theme inline` variables\n * (e.g. `--color-panel`), which Tailwind v4 inlines into utilities and never\n * emits as custom properties (verified against the repo's Tailwind 4.3.2; the\n * task file's `--color-panel` example probe would classify every legacy build\n * as new). Two tokens ANDed so a consumer theme that happens to define one\n * generic name (`--panel`) cannot be mistaken for the studio design system.\n */\nexport const CANVAS_LEGACY_MARKER_TOKENS = ['--panel', '--grid-line'] as const\n\n/**\n * A Tailwind v4 build unconditionally emits `--default-font-family` on\n * `:root, :host` (preflight consumes it) — its absence after a successful\n * stylesheet load means the canvas stylesheet is not a Tailwind theme entry at\n * all (AD-D item 2's \"defines no @theme tokens\" invalid state).\n */\nexport const CANVAS_TAILWIND_MARKER_TOKEN = '--default-font-family'\n\n/** The settled classification of the canvas stylesheet (AD-C + AD-D item 2). */\nexport type CanvasThemeStatus =\n /** Pre-EC-414 merged canvas.css — studio design system + consumer theme. */\n | { kind: 'legacy' }\n /** The EC-414 shape: a standalone consumer Tailwind theme entry. */\n | { kind: 'new' }\n /** The app never provided a canvas stylesheet (`setCanvasStylesheetUrl`). */\n | { kind: 'no-stylesheet' }\n /** The stylesheet `<link>` failed to load. */\n | { kind: 'stylesheet-error' }\n /** Loaded, but defines no Tailwind theme tokens at all. */\n | { kind: 'no-theme-tokens' }\n\n/** The raw computed-style sample the pure classifier decides from. */\nexport interface CanvasThemeProbeSample {\n /** The `ec-app-css` link fired `error` (or never resolved). */\n loadFailed: boolean\n /** Computed values of {@link CANVAS_LEGACY_MARKER_TOKENS}, in order. */\n legacyMarkers: readonly string[]\n /** Computed value of {@link CANVAS_TAILWIND_MARKER_TOKEN}. */\n tailwindMarker: string\n}\n\nconst present = (value: string | null | undefined): boolean =>\n typeof value === 'string' && value.trim() !== ''\n\n/**\n * AD-C pure core: classify the canvas stylesheet world from computed-token\n * presence. Legacy needs EVERY legacy marker; a loaded stylesheet with no\n * Tailwind marker is the explicit `no-theme-tokens` invalid state.\n */\nexport function classifyCanvasTheme(sample: CanvasThemeProbeSample): CanvasThemeStatus {\n if (sample.loadFailed) return { kind: 'stylesheet-error' }\n if (sample.legacyMarkers.length > 0 && sample.legacyMarkers.every(present)) {\n return { kind: 'legacy' }\n }\n if (present(sample.tailwindMarker)) return { kind: 'new' }\n return { kind: 'no-theme-tokens' }\n}\n\n/** The world a status renders as: only the legacy merged stylesheet keeps the\n * EC-389 body-class default; every other state gets the neutral backdrop. */\nexport function canvasWorldOf(status: CanvasThemeStatus): CanvasWorld {\n return status.kind === 'legacy' ? 'legacy' : 'new'\n}\n\n/** Sample the live document's computed root styles for {@link classifyCanvasTheme}. */\nexport function probeCanvasTheme(doc: Document, loadFailed = false): CanvasThemeStatus {\n const root = doc.documentElement\n const win = doc.defaultView\n if (!root || !win) return { kind: 'new' }\n const computed = win.getComputedStyle(root)\n return classifyCanvasTheme({\n loadFailed,\n legacyMarkers: CANVAS_LEGACY_MARKER_TOKENS.map((token) => computed.getPropertyValue(token)),\n tailwindMarker: computed.getPropertyValue(CANVAS_TAILWIND_MARKER_TOKEN),\n })\n}\n\n/**\n * The studio's DEFAULT canvas body classes — LEGACY world only (EC-389 → EC-414):\n * a consumer's `defineStudio({ canvas: { bodyClassName } })` REPLACES it\n * wholesale (including `''` to drop it), because these utility classes\n * out-cascade a site theme's `@layer base body` rules — keeping them additive\n * would make the site's own page background unreachable (the bug a consumer reported). In\n * the NEW world the default retires entirely (the utilities don't exist there);\n * the chrome stylesheet's neutral backdrop takes over.\n */\nexport const DEFAULT_CANVAS_BODY_CLASS = 'bg-background text-foreground font-sans'\n\n/** Everything injected into the canvas preview document. */\nexport interface CanvasChromeInputs {\n /** The consuming app's compiled canvas stylesheet URL; `null` → no link (tests/SSR). */\n stylesheetUrl: string | null\n /** Which stylesheet world the document is in (AD-C probe; see `wireCanvasChrome`). */\n world: CanvasWorld\n /** The consumer's canvas theme from `defineStudio({ canvas })` (EC-389). */\n theme?: StudioCanvasTheme\n}\n\n/** The head element surface the chrome creates (link/style). */\nexport interface CanvasChromeElement {\n id: string\n rel?: string\n href?: string\n textContent: string | null\n remove(): void\n}\n\n/**\n * The document surface `applyCanvasChrome` needs (the `canvas-dnd` house\n * pattern: a narrow structural type so the core is unit-testable with a\n * hand-rolled fake — no jsdom). A real iframe `Document` satisfies it.\n */\nexport interface CanvasChromeDocument {\n getElementById(id: string): CanvasChromeElement | null\n createElement(tag: string): CanvasChromeElement\n // `object` (not CanvasChromeElement): the real DOM `appendChild<T extends Node>`\n // is only bivariance-compatible with a parameter every Node satisfies.\n head: { appendChild(node: object): void }\n body: { className: string }\n}\n\n/**\n * Apply the canvas document chrome in the PINNED cascade order (EC-389/EC-414):\n *\n * 1. `ec-layer-order` — the backdrop layer STATEMENT, pinning the chrome's\n * `ec-canvas-backdrop` layer to the bottom of the layer order (it must\n * precede the consumer stylesheet in document order to lose to it);\n * 2. `ec-app-css` — the canvas stylesheet `<link>` (the consumer theme; in\n * the legacy world also the studio design system);\n * 3. `ec-outline-css` — the studio's editing chrome (world-composed:\n * backdrop + selection/DnD/toolbar/containment styles);\n * 4. `ec-canvas-theme` — the consumer's `canvas.tokensCss` (consumer overrides).\n * (An `ec-kit-tokens` layer used to sit between 3 and 4 for an instantiated\n * starter kit's palette; the kits went in EC-574.)\n *\n * Later elements win the cascade at equal specificity, so studio defaults <\n * consumer config — the customization-seam ordering. The body class is the\n * consumer's `bodyClassName` when given (even `''`), else the legacy default in\n * the legacy world and empty in the new world (AD-C). Safe to run repeatedly\n * (iframe onLoad + the already-loaded fast path + the probe settling):\n * links/styles are deduped by id, the chrome CSS is re-composed in place when\n * the world flips, and the theme style is re-appended so a re-wire never\n * changes who wins.\n */\nexport function applyCanvasChrome(d: CanvasChromeDocument, inputs: CanvasChromeInputs): void {\n if (!d.getElementById('ec-layer-order')) {\n const style = d.createElement('style')\n style.id = 'ec-layer-order'\n style.textContent = CANVAS_BACKDROP_LAYER_STATEMENT\n d.head.appendChild(style)\n }\n if (inputs.stylesheetUrl && !d.getElementById('ec-app-css')) {\n const link = d.createElement('link')\n link.id = 'ec-app-css'\n link.rel = 'stylesheet'\n link.href = inputs.stylesheetUrl\n d.head.appendChild(link)\n }\n const chromeCss = canvasChromeCss(inputs.world)\n const outline = d.getElementById('ec-outline-css')\n if (outline) {\n // Probe settled on the other world: re-compose in place (position stable).\n if (outline.textContent !== chromeCss) outline.textContent = chromeCss\n } else {\n const style = d.createElement('style')\n style.id = 'ec-outline-css'\n style.textContent = chromeCss\n d.head.appendChild(style)\n }\n // The theme layer re-appends (remove + add) on every wire, so it always lands\n // last — after a re-composed chrome layer too.\n appendCssLayer(d, 'ec-canvas-theme', inputs.theme?.tokensCss)\n d.body.className =\n inputs.theme?.bodyClassName ?? (inputs.world === 'legacy' ? DEFAULT_CANVAS_BODY_CLASS : '')\n}\n\n/** Replace-append one identified `<style>` layer; absent CSS removes a stale element. */\nfunction appendCssLayer(d: CanvasChromeDocument, id: string, css: string | undefined): void {\n const existing = d.getElementById(id)\n if (existing) existing.remove()\n if (css === undefined || css === '') return\n const style = d.createElement('style')\n style.id = id\n style.textContent = css\n d.head.appendChild(style)\n}\n\n/** `applyCanvasChrome` inputs minus the probe-owned `world`, plus the settle callback. */\nexport interface WireCanvasChromeOptions {\n stylesheetUrl: string | null\n theme?: StudioCanvasTheme\n /**\n * Fired when the classification SETTLES — the stylesheet finished loading\n * (or failing), or there is no stylesheet to wait for. Feeds the AD-D dev\n * notice; may fire again on re-wires (callers dedupe by kind).\n */\n onStatus?: (status: CanvasThemeStatus) => void\n}\n\n/** Links whose settle listeners are already attached (re-wires must not stack them). */\nconst wiredLinks = new WeakSet<object>()\n\n/**\n * The DOM-level wiring around {@link applyCanvasChrome}: applies the chrome\n * immediately with the CURRENT classification, then re-applies once the\n * `ec-app-css` link settles (the AD-C probe reads computed custom properties,\n * which exist only after the stylesheet loads). Before the stylesheet arrives\n * neither world's styling is resolvable anyway (no consumer CSS yet), so the\n * pre-settle pass is visually neutral in both worlds.\n */\nexport function wireCanvasChrome(doc: Document, options: WireCanvasChromeOptions): void {\n const apply = (settled: boolean, loadFailed: boolean) => {\n const status: CanvasThemeStatus = options.stylesheetUrl\n ? probeCanvasTheme(doc, loadFailed)\n : { kind: 'no-stylesheet' }\n applyCanvasChrome(doc, {\n stylesheetUrl: options.stylesheetUrl,\n world: canvasWorldOf(status),\n ...(options.theme !== undefined ? { theme: options.theme } : {}),\n })\n if (settled) options.onStatus?.(status)\n }\n\n if (!options.stylesheetUrl) {\n apply(true, false)\n return\n }\n // First pass injects the link (and everything else) with the current sample.\n const already = doc.getElementById('ec-app-css') as HTMLLinkElement | null\n const loaded = already?.sheet != null\n apply(loaded, false)\n if (loaded) return\n const link = doc.getElementById('ec-app-css') as HTMLLinkElement | null\n if (!link || wiredLinks.has(link)) return\n wiredLinks.add(link)\n link.addEventListener('load', () => apply(true, false), { once: true })\n link.addEventListener('error', () => apply(true, true), { once: true })\n}\n"]}
@@ -17,13 +17,13 @@ export declare function absorbIntoValue(value: unknown, change: readonly Documen
17
17
  /** One composition state with the change absorbed, or `undefined` when it cannot take it. */
18
18
  export declare function absorbChange(state: CanvasValueState, change: readonly DocumentPatch[]): CanvasValueState | undefined;
19
19
  /** The editor state a rebase reads and writes (the builder state's composition, history and selection). */
20
- export interface RebasableState {
20
+ export interface RebasableState<Entry extends CanvasValueState = CanvasValueState> {
21
21
  readonly present: ComponentNode;
22
22
  readonly preserved: readonly PreservedItem[];
23
23
  /** Oldest first. */
24
- readonly past: readonly CanvasValueState[];
24
+ readonly past: readonly Entry[];
25
25
  /** Next redo first. */
26
- readonly future: readonly CanvasValueState[];
26
+ readonly future: readonly Entry[];
27
27
  readonly selectedId: string | null;
28
28
  readonly selectedRun: number | null;
29
29
  readonly hoveredId: string | null;
@@ -32,7 +32,7 @@ export interface RebasableState {
32
32
  * Rebase the editor onto the field's change `from` → `to` (see the module doc). Returns
33
33
  * `undefined` when there is nothing to change.
34
34
  */
35
- export declare function rebaseCanvas(state: RebasableState, from: unknown, to: unknown): RebasableState | undefined;
35
+ export declare function rebaseCanvas<Entry extends CanvasValueState>(state: RebasableState<Entry>, from: unknown, to: unknown): RebasableState<Entry> | undefined;
36
36
  /**
37
37
  * A change of the canvas field's value made UNDERNEATH the canvas, as the document draft
38
38
  * saw it happen: the field's value before and after the session's document moved, a
@@ -87,7 +87,8 @@ export function absorbChange(state, change) {
87
87
  * Rebase one direction of the history (`chain` ordered from the present outward — the
88
88
  * newest undo step first, or the next redo step first): each entry absorbs the change,
89
89
  * the first that cannot ends the chain, and an entry equal to its neighbour toward the
90
- * present is dropped. Returns the chain in the same order.
90
+ * present is dropped. Returns the chain in the same order. An entry keeps what it carries
91
+ * beside its value (EC-728: the step's effect outside the canvas); a dropped entry drops it.
91
92
  */
92
93
  function rebaseChain(present, chain, change) {
93
94
  const out = [];
@@ -99,7 +100,7 @@ function rebaseChain(present, chain, change) {
99
100
  const value = canvasValueOf(absorbed);
100
101
  if (semanticValueEqual(value, neighbour))
101
102
  continue;
102
- out.push(absorbed);
103
+ out.push({ ...entry, ...absorbed });
103
104
  neighbour = value;
104
105
  }
105
106
  return out;