@elytracms/studio 0.0.35 → 0.0.36

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 (56) hide show
  1. package/dist/components/assets/asset-selection.d.ts +1 -1
  2. package/dist/components/assets/asset-selection.js +2 -2
  3. package/dist/components/assets/asset-selection.js.map +1 -1
  4. package/dist/components/builder/clipboard-actions.js +1 -1
  5. package/dist/components/builder/clipboard-actions.js.map +1 -1
  6. package/dist/components/builder/composition-field-editor.js +7 -7
  7. package/dist/components/builder/composition-field-editor.js.map +1 -1
  8. package/dist/components/builder/inline-edit.js +4 -3
  9. package/dist/components/builder/inline-edit.js.map +1 -1
  10. package/dist/components/builder/insert-context.js +1 -1
  11. package/dist/components/builder/insert-context.js.map +1 -1
  12. package/dist/components/builder/layout-inspector.js +2 -2
  13. package/dist/components/builder/layout-inspector.js.map +1 -1
  14. package/dist/components/builder/node-factory.js +7 -7
  15. package/dist/components/builder/node-factory.js.map +1 -1
  16. package/dist/components/builder/page-tree.js +1 -1
  17. package/dist/components/builder/page-tree.js.map +1 -1
  18. package/dist/components/builder/property-inspector.js +2 -2
  19. package/dist/components/builder/property-inspector.js.map +1 -1
  20. package/dist/components/builder/reuse-context.js +2 -2
  21. package/dist/components/builder/reuse-context.js.map +1 -1
  22. package/dist/components/builder/reuse.js +5 -4
  23. package/dist/components/builder/reuse.js.map +1 -1
  24. package/dist/components/cms/cell.js +1 -1
  25. package/dist/components/cms/cell.js.map +1 -1
  26. package/dist/components/cms/editable-cell.js +4 -4
  27. package/dist/components/cms/editable-cell.js.map +1 -1
  28. package/dist/components/cms/field-control.js +9 -9
  29. package/dist/components/cms/field-control.js.map +1 -1
  30. package/dist/components/cms/relation-picker.d.ts +2 -2
  31. package/dist/components/cms/relation-picker.js +5 -5
  32. package/dist/components/cms/relation-picker.js.map +1 -1
  33. package/dist/components/components/component-detail.js +1 -1
  34. package/dist/components/components/component-detail.js.map +1 -1
  35. package/dist/components/components/components-index.js +1 -1
  36. package/dist/components/components/components-index.js.map +1 -1
  37. package/dist/components/components/registry-browser.js +2 -2
  38. package/dist/components/components/registry-browser.js.map +1 -1
  39. package/dist/components/extensions/extensions-view.js +1 -1
  40. package/dist/components/extensions/extensions-view.js.map +1 -1
  41. package/dist/lib/clipboard/paste-target.js +1 -1
  42. package/dist/lib/clipboard/paste-target.js.map +1 -1
  43. package/dist/lib/dnd/drop-rules.js +2 -2
  44. package/dist/lib/dnd/drop-rules.js.map +1 -1
  45. package/dist/lib/dnd/gap-drag.js +2 -2
  46. package/dist/lib/dnd/gap-drag.js.map +1 -1
  47. package/dist/lib/persona/labels.d.ts +2 -2
  48. package/dist/lib/persona/labels.js +3 -3
  49. package/dist/lib/persona/labels.js.map +1 -1
  50. package/dist/lib/references/reference-model.js +1 -1
  51. package/dist/lib/references/reference-model.js.map +1 -1
  52. package/dist/lib/registry/preview.js +15 -15
  53. package/dist/lib/registry/preview.js.map +1 -1
  54. package/dist/lib/registry/project-components.js +6 -13
  55. package/dist/lib/registry/project-components.js.map +1 -1
  56. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"drop-rules.js","sourceRoot":"","sources":["../../../src/lib/dnd/drop-rules.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n DEFAULT_SLOT,\n GRID_COMPONENT_ID,\n OUTLET_SLOT,\n STACK_COMPONENT_ID,\n getNode,\n getParentInfo,\n gridContainerNode,\n isDescendantOf,\n removeNode,\n updateNode,\n} from '@elytracms/core/project-graph'\nimport type { ComponentNode, GridContainerOptions, InsertTarget } from '@elytracms/core/project-graph'\n\n/**\n * Drag & drop eligibility rules (EC-160 + EC-162, vision §3.1) — pure\n * graph-level functions with **no DOM or UI dependencies**. The canvas and the\n * page tree both resolve drops through `resolveDrop`, so the two surfaces can\n * never disagree about what a manifest slot allow-list permits, which children\n * a component accepts, or whether a move would create a cycle.\n *\n * The drop-zone model is position-based: `into` a slot, `before`/`after` a\n * sibling along the parent's flow axis, and the EC-162 gesture positions\n * `left`/`right` — dropping *beside* an element wraps both in a two-column\n * `Grid` container (see `applyWrap`).\n */\n\n// ---------------------------------------------------------------------------\n// Inputs\n// ---------------------------------------------------------------------------\n\n/**\n * The manifest slice the rules evaluate. Structural (not the full\n * `ComponentManifest`) so tests and future callers can feed minimal lookups.\n */\nexport interface SlotRule {\n name: string\n /** Restrict which component ids may be inserted (omit to allow any). */\n allow?: string[]\n}\n\nexport interface ManifestRules {\n slots: SlotRule[]\n}\n\n/** Resolve a component id to its manifest slot rules (undefined = unknown component). */\nexport type ManifestLookup = (componentId: string) => ManifestRules | undefined\n\n/** What is being dragged. */\nexport type DragSubject =\n /** An existing node of the current page — a completed drop is a `move`. */\n | { kind: 'node'; nodeId: string; componentId: string }\n /** A new subtree (blocks-panel tile) — a completed drop is an `insert`. */\n | { kind: 'component'; componentId: string }\n\n/**\n * Where, relative to a reference node, the drop would land. `into`, `before`\n * and `after` are the EC-160 positions; `left`/`right` are the EC-162 gesture\n * positions — \"beside\" the reference, wrapping both elements in a two-column\n * `Grid` container.\n */\nexport type DropPosition = 'into' | 'before' | 'after' | 'left' | 'right'\n\n/** The side of the reference an EC-162 wrap gesture drops on. */\nexport type WrapSide = 'left' | 'right'\n\nexport interface DropLocation {\n /** The reference node id (the hovered node/row). */\n nodeId: string\n position: DropPosition\n}\n\n// ---------------------------------------------------------------------------\n// Outcomes\n// ---------------------------------------------------------------------------\n\nexport type DropRejectionCode =\n /** The reference node does not exist in the tree. */\n | 'missing-target'\n /** `into` on a component that declares no insertable slot. */\n | 'no-slot'\n /** The target slot's manifest allow-list rejects the dragged component. */\n | 'not-allowed'\n /** Moving a node into itself or its own subtree (cycle). */\n | 'own-subtree'\n /** The page root cannot be moved, and nothing can drop beside it. */\n | 'root'\n /** Structurally valid but changes nothing (dropping a node onto its own position). */\n | 'no-op'\n\nexport interface DropRejection {\n eligible: false\n code: DropRejectionCode\n reason: string\n}\n\n/** An eligible drop: exactly one builder action (move, insert, or wrap) commits it. */\nexport type DropPlan =\n | { eligible: true; kind: 'move'; nodeId: string; target: InsertTarget }\n | { eligible: true; kind: 'insert'; target: InsertTarget }\n /**\n * EC-162 wrap gesture: the reference node and the dragged subject become the\n * two columns of a new `Grid` container, created atomically (`applyWrap`).\n * `nodeId` is present when the subject is an existing node (a move-wrap);\n * absent for blocks-panel components (an insert-wrap via `buildNode`).\n */\n | { eligible: true; kind: 'wrap'; referenceId: string; side: WrapSide; nodeId?: string }\n\nexport type DropResolution = DropPlan | DropRejection\n\nfunction rejected(code: DropRejectionCode, reason: string): DropRejection {\n return { eligible: false, code, reason }\n}\n\n// ---------------------------------------------------------------------------\n// Slot rules\n// ---------------------------------------------------------------------------\n\n/**\n * The slot an `into` drop inserts into: the default `children` slot when\n * declared, else the first authored slot that is not the layout `outlet`.\n * Mirrors the shell's insert-context resolution so click-insert and\n * drag-insert land in the same place.\n */\nexport function primaryInsertSlot(manifest: ManifestRules | undefined): SlotRule | undefined {\n if (!manifest) return undefined\n return (\n manifest.slots.find((s) => s.name === DEFAULT_SLOT) ??\n manifest.slots.find((s) => s.name !== OUTLET_SLOT)\n )\n}\n\n/** True when a component can receive `into` drops at all (declares an insertable slot). */\nexport function canNestInto(getManifest: ManifestLookup, componentId: string): boolean {\n return primaryInsertSlot(getManifest(componentId)) !== undefined\n}\n\n/** True when a slot's allow-list (if any) permits the component id. */\nexport function slotAllows(slot: SlotRule | undefined, componentId: string): boolean {\n if (!slot?.allow) return true\n return slot.allow.includes(componentId)\n}\n\n// ---------------------------------------------------------------------------\n// Resolution\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve a prospective drop into either an executable plan (one `move` or\n * `insert` builder action → one operations command → one undo step) or a\n * structured rejection the UI shows *during* the drag. Pure: same inputs,\n * same answer, on both surfaces.\n */\nexport function resolveDrop(args: {\n root: ComponentNode\n getManifest: ManifestLookup\n subject: DragSubject\n location: DropLocation\n}): DropResolution {\n const { root, getManifest, subject, location } = args\n\n if (subject.kind === 'node' && subject.nodeId === root.id) {\n return rejected('root', 'The page root cannot be moved.')\n }\n\n const reference = getNode(root, location.nodeId)\n if (!reference) {\n return rejected('missing-target', `Node \"${location.nodeId}\" is not on this page.`)\n }\n\n // EC-162: dropping beside the reference wraps both in a two-column Grid.\n if (location.position === 'left' || location.position === 'right') {\n return resolveWrap({ root, getManifest, subject, reference, side: location.position })\n }\n\n // Resolve the concrete insertion point: parent node, slot, index.\n let parentId: string\n let slotRule: SlotRule | undefined\n let slotName: string\n let index: number\n\n if (location.position === 'into') {\n const manifest = getManifest(reference.componentId)\n const slot = primaryInsertSlot(manifest)\n if (!slot) {\n return rejected('no-slot', `\"${reference.componentId}\" does not accept children.`)\n }\n parentId = reference.id\n slotRule = slot\n slotName = slot.name\n index = (reference.slots?.[slot.name] ?? []).length\n } else {\n const info = getParentInfo(root, reference.id)\n if (!info) {\n return rejected('root', 'Nothing can be dropped beside the page root.')\n }\n parentId = info.parent.id\n slotName = info.slot\n // Unknown parent manifests impose no allow-list; validation owns that issue.\n slotRule = getManifest(info.parent.componentId)?.slots.find((s) => s.name === info.slot)\n index = info.index + (location.position === 'after' ? 1 : 0)\n }\n\n if (!slotAllows(slotRule, subject.componentId)) {\n return rejected(\n 'not-allowed',\n `Slot \"${slotName}\" does not allow \"${subject.componentId}\".`,\n )\n }\n\n const target: InsertTarget = { parentId, slot: slotName, index }\n\n if (subject.kind === 'component') {\n return { eligible: true, kind: 'insert', target }\n }\n\n // Cycle prevention: a node can never land inside its own subtree.\n if (subject.nodeId === parentId || isDescendantOf(root, subject.nodeId, parentId)) {\n return rejected('own-subtree', 'A node cannot be dropped into its own subtree.')\n }\n\n // No-op detection: the final position (after same-slot removal compensation,\n // matching `moveNode`) equals the node's current position.\n const current = getParentInfo(root, subject.nodeId)\n if (current && current.parent.id === parentId && current.slot === slotName) {\n const finalIndex = index > current.index ? index - 1 : index\n if (finalIndex === current.index) {\n return rejected('no-op', 'Dropping here would not move the node.')\n }\n }\n\n return { eligible: true, kind: 'move', nodeId: subject.nodeId, target }\n}\n\n// ---------------------------------------------------------------------------\n// Wrap gesture (EC-162): drop beside → two-column Grid container\n// ---------------------------------------------------------------------------\n\n/**\n * The container a \"drop beside\" gesture creates. Canonical choice (EC-161\n * semantics): a `Grid` with two columns, **not** a horizontal `Stack` —\n * EC-161 gives `Grid` the canonical column vocabulary (`columns` is a\n * validated graph value the contextual inspector refines from 2 → n), while\n * a horizontal Stack expresses flow order, not column tracks. The values\n * mirror the Grid manifest defaults (`columns: 2`, `gap: '4'`), so the\n * gesture's graph is exactly what composing the same layout manually would\n * produce.\n */\nexport const WRAP_GRID_OPTIONS: GridContainerOptions = { columns: 2, gap: '4' }\n\n/** Eligibility for a `left`/`right` (wrap-beside) drop. */\nfunction resolveWrap(args: {\n root: ComponentNode\n getManifest: ManifestLookup\n subject: DragSubject\n reference: ComponentNode\n side: WrapSide\n}): DropResolution {\n const { root, getManifest, subject, reference, side } = args\n\n // The Grid takes the reference's place in its parent slot — so the\n // reference needs a parent (the root is the page anchor, never wrapped)\n // and that slot's allow-list must accept a Grid.\n const info = getParentInfo(root, reference.id)\n if (!info) {\n return rejected('root', 'The page root cannot be wrapped in a container.')\n }\n const parentSlotRule = getManifest(info.parent.componentId)?.slots.find(\n (s) => s.name === info.slot,\n )\n if (!slotAllows(parentSlotRule, GRID_COMPONENT_ID)) {\n return rejected('not-allowed', `Slot \"${info.slot}\" does not allow a Grid container.`)\n }\n\n // Both columns land in the Grid's children slot.\n const gridSlot = primaryInsertSlot(getManifest(GRID_COMPONENT_ID))\n for (const componentId of [reference.componentId, subject.componentId]) {\n if (!slotAllows(gridSlot, componentId)) {\n return rejected('not-allowed', `A Grid container does not allow \"${componentId}\".`)\n }\n }\n\n if (subject.kind === 'component') {\n return { eligible: true, kind: 'wrap', referenceId: reference.id, side }\n }\n if (subject.nodeId === reference.id) {\n return rejected('no-op', 'A node cannot be placed beside itself.')\n }\n // The reference must not live inside the dragged subtree (the subject is\n // lifted out first — taking the reference with it would orphan the wrap).\n if (isDescendantOf(root, subject.nodeId, reference.id)) {\n return rejected('own-subtree', 'A node cannot wrap an element of its own subtree.')\n }\n return { eligible: true, kind: 'wrap', referenceId: reference.id, side, nodeId: subject.nodeId }\n}\n\n/** What lands beside the reference: an existing node (moved) or a built subtree (inserted). */\nexport type WrapPartner =\n | { kind: 'node'; nodeId: string }\n | { kind: 'subtree'; node: ComponentNode }\n\nexport interface WrapResult {\n root: ComponentNode\n /** The id of the created Grid container. */\n containerId: string\n}\n\n/**\n * Execute a wrap plan as **one** structural edit: lift the partner out of the\n * tree (when it is an existing node) and replace the reference with\n * `gridContainerNode(containerId, WRAP_GRID_OPTIONS, [left, right])` in a\n * single pass. The caller commits the result as one builder action → one\n * operations command → one undo step; EC-161 normalization then runs on the\n * *committed* tree, so the two-child container can never be observed (or\n * persisted) in a half-built state. Returns `null` for structurally invalid\n * input (missing nodes, root reference, reference inside the partner).\n *\n * Determinism (EC-162): the produced container subtree is exactly the\n * `gridContainerNode` constructor output — identical to manual composition.\n */\nexport function applyWrap(args: {\n root: ComponentNode\n referenceId: string\n side: WrapSide\n partner: WrapPartner\n containerId: string\n}): WrapResult | null {\n const { root, referenceId, side, partner, containerId } = args\n if (referenceId === root.id || !getNode(root, referenceId)) return null\n\n let working = root\n let partnerNode: ComponentNode\n if (partner.kind === 'node') {\n if (partner.nodeId === referenceId || partner.nodeId === root.id) return null\n const node = getNode(root, partner.nodeId)\n if (!node || isDescendantOf(root, partner.nodeId, referenceId)) return null\n partnerNode = node\n working = removeNode(root, partner.nodeId)\n } else {\n partnerNode = partner.node\n }\n\n const next = updateNode(working, referenceId, (reference) =>\n gridContainerNode(\n containerId,\n WRAP_GRID_OPTIONS,\n side === 'left' ? [partnerNode, reference] : [reference, partnerNode],\n ),\n )\n return { root: next, containerId }\n}\n\n// ---------------------------------------------------------------------------\n// Pointer → position bands (shared hit-testing model)\n// ---------------------------------------------------------------------------\n\n/** The axis along which a container lays out its children. */\nexport type FlowAxis = 'vertical' | 'horizontal'\n\nfunction staticPropValue(node: ComponentNode, name: string): unknown {\n const prop = node.props?.[name]\n return prop?.kind === 'static' ? prop.value : undefined\n}\n\n/**\n * The flow axis of a container — the axis its children stack along, and\n * therefore the hit-band axis for drops over those children (EC-162 ledger\n * fix: bands follow the parent's flow direction, on both surfaces). Mirrors\n * the renderer: a horizontal Stack flows horizontally; a Grid flows\n * horizontally when it actually has ≥ 2 column tracks (an absent/invalid\n * `columns` renders one column). Everything else is block flow (vertical).\n */\nexport function containerFlowAxis(node: ComponentNode | undefined): FlowAxis {\n if (!node) return 'vertical'\n if (node.componentId === STACK_COMPONENT_ID) {\n return staticPropValue(node, 'direction') === 'horizontal' ? 'horizontal' : 'vertical'\n }\n if (node.componentId === GRID_COMPONENT_ID) {\n const columns = staticPropValue(node, 'columns')\n return typeof columns === 'number' && columns >= 2 ? 'horizontal' : 'vertical'\n }\n return 'vertical'\n}\n\n/** Cross-axis side-band width (fraction of the box) that triggers the wrap gesture. */\nconst WRAP_BAND = 0.18\n\n/**\n * Map a 2D pointer offset within a drop target's box onto a `DropPosition`\n * (EC-162). `flow` is the flow axis of the target's *parent* — before/after\n * bands run along it. On vertically flowing targets, narrow side bands on the\n * cross axis produce `left`/`right` (the wrap-into-columns gesture, when\n * `wrappable`); children of horizontal containers get left/right bands for\n * plain reorder instead, so \"beside\" already means before/after there.\n */\nexport function dropPositionForPoint(args: {\n /** Pointer offset across the target's width, 0..1. */\n x: number\n /** Pointer offset across the target's height, 0..1. */\n y: number\n /** Flow axis of the target's parent (the axis siblings stack along). */\n flow: FlowAxis\n nestable: boolean\n edges: boolean\n /** Enable the wrap side-bands (the gesture surface opts in). */\n wrappable?: boolean\n}): DropPosition {\n const { x, y, flow, nestable, edges, wrappable = false } = args\n if (!edges) return 'into'\n if (wrappable && flow === 'vertical') {\n if (x < WRAP_BAND) return 'left'\n if (x > 1 - WRAP_BAND) return 'right'\n }\n return dropPositionForPointer({ ratio: flow === 'horizontal' ? x : y, nestable, edges })\n}\n\n/**\n * Map a pointer offset within a drop target's box onto a `DropPosition`.\n * `ratio` is the pointer's offset along the target's main axis, 0..1 — the\n * caller picks the axis (the parent's flow axis; see `containerFlowAxis`).\n *\n * - nestable targets with siblings: edge quarters insert before/after, the\n * middle half nests into the slot\n * - leaf targets: top/bottom halves insert before/after\n * - targets without siblings (the root): always `into`\n */\nexport function dropPositionForPointer(args: {\n ratio: number\n /** Whether `into` is structurally possible (target declares an insert slot). */\n nestable: boolean\n /** Whether before/after exist (target has a parent slot). */\n edges: boolean\n}): DropPosition {\n const { ratio, nestable, edges } = args\n if (!edges) return 'into'\n if (nestable) {\n if (ratio < 0.25) return 'before'\n if (ratio > 0.75) return 'after'\n return 'into'\n }\n return ratio < 0.5 ? 'before' : 'after'\n}\n\n// ---------------------------------------------------------------------------\n// Indicator state (shared visual contract)\n// ---------------------------------------------------------------------------\n\n/**\n * The visual state a surface shows for the current resolution. `blocked` is\n * the explicit rejection state (never a silent failed drop); `null` means show\n * nothing (a no-op drop is inert, not an error).\n */\nexport type DropIndicator = DropPosition | 'blocked' | null\n\nexport function dropIndicatorFor(\n resolution: DropResolution,\n position: DropPosition,\n): DropIndicator {\n if (resolution.eligible) return position\n return resolution.code === 'no-op' ? null : 'blocked'\n}\n"]}
1
+ {"version":3,"file":"drop-rules.js","sourceRoot":"","sources":["../../../src/lib/dnd/drop-rules.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n DEFAULT_SLOT,\n GRID_COMPONENT_ID,\n OUTLET_SLOT,\n STACK_COMPONENT_ID,\n getNode,\n getParentInfo,\n gridContainerNode,\n isDescendantOf,\n isStaticProp,\n removeNode,\n updateNode,\n} from '@elytracms/core/project-graph'\nimport type { ComponentNode, GridContainerOptions, InsertTarget } from '@elytracms/core/project-graph'\n\n/**\n * Drag & drop eligibility rules (EC-160 + EC-162, vision §3.1) — pure\n * graph-level functions with **no DOM or UI dependencies**. The canvas and the\n * page tree both resolve drops through `resolveDrop`, so the two surfaces can\n * never disagree about what a manifest slot allow-list permits, which children\n * a component accepts, or whether a move would create a cycle.\n *\n * The drop-zone model is position-based: `into` a slot, `before`/`after` a\n * sibling along the parent's flow axis, and the EC-162 gesture positions\n * `left`/`right` — dropping *beside* an element wraps both in a two-column\n * `Grid` container (see `applyWrap`).\n */\n\n// ---------------------------------------------------------------------------\n// Inputs\n// ---------------------------------------------------------------------------\n\n/**\n * The manifest slice the rules evaluate. Structural (not the full\n * `ComponentManifest`) so tests and future callers can feed minimal lookups.\n */\nexport interface SlotRule {\n name: string\n /** Restrict which component ids may be inserted (omit to allow any). */\n allow?: string[]\n}\n\nexport interface ManifestRules {\n slots: SlotRule[]\n}\n\n/** Resolve a component id to its manifest slot rules (undefined = unknown component). */\nexport type ManifestLookup = (componentId: string) => ManifestRules | undefined\n\n/** What is being dragged. */\nexport type DragSubject =\n /** An existing node of the current page — a completed drop is a `move`. */\n | { kind: 'node'; nodeId: string; componentId: string }\n /** A new subtree (blocks-panel tile) — a completed drop is an `insert`. */\n | { kind: 'component'; componentId: string }\n\n/**\n * Where, relative to a reference node, the drop would land. `into`, `before`\n * and `after` are the EC-160 positions; `left`/`right` are the EC-162 gesture\n * positions — \"beside\" the reference, wrapping both elements in a two-column\n * `Grid` container.\n */\nexport type DropPosition = 'into' | 'before' | 'after' | 'left' | 'right'\n\n/** The side of the reference an EC-162 wrap gesture drops on. */\nexport type WrapSide = 'left' | 'right'\n\nexport interface DropLocation {\n /** The reference node id (the hovered node/row). */\n nodeId: string\n position: DropPosition\n}\n\n// ---------------------------------------------------------------------------\n// Outcomes\n// ---------------------------------------------------------------------------\n\nexport type DropRejectionCode =\n /** The reference node does not exist in the tree. */\n | 'missing-target'\n /** `into` on a component that declares no insertable slot. */\n | 'no-slot'\n /** The target slot's manifest allow-list rejects the dragged component. */\n | 'not-allowed'\n /** Moving a node into itself or its own subtree (cycle). */\n | 'own-subtree'\n /** The page root cannot be moved, and nothing can drop beside it. */\n | 'root'\n /** Structurally valid but changes nothing (dropping a node onto its own position). */\n | 'no-op'\n\nexport interface DropRejection {\n eligible: false\n code: DropRejectionCode\n reason: string\n}\n\n/** An eligible drop: exactly one builder action (move, insert, or wrap) commits it. */\nexport type DropPlan =\n | { eligible: true; kind: 'move'; nodeId: string; target: InsertTarget }\n | { eligible: true; kind: 'insert'; target: InsertTarget }\n /**\n * EC-162 wrap gesture: the reference node and the dragged subject become the\n * two columns of a new `Grid` container, created atomically (`applyWrap`).\n * `nodeId` is present when the subject is an existing node (a move-wrap);\n * absent for blocks-panel components (an insert-wrap via `buildNode`).\n */\n | { eligible: true; kind: 'wrap'; referenceId: string; side: WrapSide; nodeId?: string }\n\nexport type DropResolution = DropPlan | DropRejection\n\nfunction rejected(code: DropRejectionCode, reason: string): DropRejection {\n return { eligible: false, code, reason }\n}\n\n// ---------------------------------------------------------------------------\n// Slot rules\n// ---------------------------------------------------------------------------\n\n/**\n * The slot an `into` drop inserts into: the default `children` slot when\n * declared, else the first authored slot that is not the layout `outlet`.\n * Mirrors the shell's insert-context resolution so click-insert and\n * drag-insert land in the same place.\n */\nexport function primaryInsertSlot(manifest: ManifestRules | undefined): SlotRule | undefined {\n if (!manifest) return undefined\n return (\n manifest.slots.find((s) => s.name === DEFAULT_SLOT) ??\n manifest.slots.find((s) => s.name !== OUTLET_SLOT)\n )\n}\n\n/** True when a component can receive `into` drops at all (declares an insertable slot). */\nexport function canNestInto(getManifest: ManifestLookup, componentId: string): boolean {\n return primaryInsertSlot(getManifest(componentId)) !== undefined\n}\n\n/** True when a slot's allow-list (if any) permits the component id. */\nexport function slotAllows(slot: SlotRule | undefined, componentId: string): boolean {\n if (!slot?.allow) return true\n return slot.allow.includes(componentId)\n}\n\n// ---------------------------------------------------------------------------\n// Resolution\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve a prospective drop into either an executable plan (one `move` or\n * `insert` builder action → one operations command → one undo step) or a\n * structured rejection the UI shows *during* the drag. Pure: same inputs,\n * same answer, on both surfaces.\n */\nexport function resolveDrop(args: {\n root: ComponentNode\n getManifest: ManifestLookup\n subject: DragSubject\n location: DropLocation\n}): DropResolution {\n const { root, getManifest, subject, location } = args\n\n if (subject.kind === 'node' && subject.nodeId === root.id) {\n return rejected('root', 'The page root cannot be moved.')\n }\n\n const reference = getNode(root, location.nodeId)\n if (!reference) {\n return rejected('missing-target', `Node \"${location.nodeId}\" is not on this page.`)\n }\n\n // EC-162: dropping beside the reference wraps both in a two-column Grid.\n if (location.position === 'left' || location.position === 'right') {\n return resolveWrap({ root, getManifest, subject, reference, side: location.position })\n }\n\n // Resolve the concrete insertion point: parent node, slot, index.\n let parentId: string\n let slotRule: SlotRule | undefined\n let slotName: string\n let index: number\n\n if (location.position === 'into') {\n const manifest = getManifest(reference.componentId)\n const slot = primaryInsertSlot(manifest)\n if (!slot) {\n return rejected('no-slot', `\"${reference.componentId}\" does not accept children.`)\n }\n parentId = reference.id\n slotRule = slot\n slotName = slot.name\n index = (reference.slots?.[slot.name] ?? []).length\n } else {\n const info = getParentInfo(root, reference.id)\n if (!info) {\n return rejected('root', 'Nothing can be dropped beside the page root.')\n }\n parentId = info.parent.id\n slotName = info.slot\n // Unknown parent manifests impose no allow-list; validation owns that issue.\n slotRule = getManifest(info.parent.componentId)?.slots.find((s) => s.name === info.slot)\n index = info.index + (location.position === 'after' ? 1 : 0)\n }\n\n if (!slotAllows(slotRule, subject.componentId)) {\n return rejected(\n 'not-allowed',\n `Slot \"${slotName}\" does not allow \"${subject.componentId}\".`,\n )\n }\n\n const target: InsertTarget = { parentId, slot: slotName, index }\n\n if (subject.kind === 'component') {\n return { eligible: true, kind: 'insert', target }\n }\n\n // Cycle prevention: a node can never land inside its own subtree.\n if (subject.nodeId === parentId || isDescendantOf(root, subject.nodeId, parentId)) {\n return rejected('own-subtree', 'A node cannot be dropped into its own subtree.')\n }\n\n // No-op detection: the final position (after same-slot removal compensation,\n // matching `moveNode`) equals the node's current position.\n const current = getParentInfo(root, subject.nodeId)\n if (current && current.parent.id === parentId && current.slot === slotName) {\n const finalIndex = index > current.index ? index - 1 : index\n if (finalIndex === current.index) {\n return rejected('no-op', 'Dropping here would not move the node.')\n }\n }\n\n return { eligible: true, kind: 'move', nodeId: subject.nodeId, target }\n}\n\n// ---------------------------------------------------------------------------\n// Wrap gesture (EC-162): drop beside → two-column Grid container\n// ---------------------------------------------------------------------------\n\n/**\n * The container a \"drop beside\" gesture creates. Canonical choice (EC-161\n * semantics): a `Grid` with two columns, **not** a horizontal `Stack` —\n * EC-161 gives `Grid` the canonical column vocabulary (`columns` is a\n * validated graph value the contextual inspector refines from 2 → n), while\n * a horizontal Stack expresses flow order, not column tracks. The values\n * mirror the Grid manifest defaults (`columns: 2`, `gap: '4'`), so the\n * gesture's graph is exactly what composing the same layout manually would\n * produce.\n */\nexport const WRAP_GRID_OPTIONS: GridContainerOptions = { columns: 2, gap: '4' }\n\n/** Eligibility for a `left`/`right` (wrap-beside) drop. */\nfunction resolveWrap(args: {\n root: ComponentNode\n getManifest: ManifestLookup\n subject: DragSubject\n reference: ComponentNode\n side: WrapSide\n}): DropResolution {\n const { root, getManifest, subject, reference, side } = args\n\n // The Grid takes the reference's place in its parent slot — so the\n // reference needs a parent (the root is the page anchor, never wrapped)\n // and that slot's allow-list must accept a Grid.\n const info = getParentInfo(root, reference.id)\n if (!info) {\n return rejected('root', 'The page root cannot be wrapped in a container.')\n }\n const parentSlotRule = getManifest(info.parent.componentId)?.slots.find(\n (s) => s.name === info.slot,\n )\n if (!slotAllows(parentSlotRule, GRID_COMPONENT_ID)) {\n return rejected('not-allowed', `Slot \"${info.slot}\" does not allow a Grid container.`)\n }\n\n // Both columns land in the Grid's children slot.\n const gridSlot = primaryInsertSlot(getManifest(GRID_COMPONENT_ID))\n for (const componentId of [reference.componentId, subject.componentId]) {\n if (!slotAllows(gridSlot, componentId)) {\n return rejected('not-allowed', `A Grid container does not allow \"${componentId}\".`)\n }\n }\n\n if (subject.kind === 'component') {\n return { eligible: true, kind: 'wrap', referenceId: reference.id, side }\n }\n if (subject.nodeId === reference.id) {\n return rejected('no-op', 'A node cannot be placed beside itself.')\n }\n // The reference must not live inside the dragged subtree (the subject is\n // lifted out first — taking the reference with it would orphan the wrap).\n if (isDescendantOf(root, subject.nodeId, reference.id)) {\n return rejected('own-subtree', 'A node cannot wrap an element of its own subtree.')\n }\n return { eligible: true, kind: 'wrap', referenceId: reference.id, side, nodeId: subject.nodeId }\n}\n\n/** What lands beside the reference: an existing node (moved) or a built subtree (inserted). */\nexport type WrapPartner =\n | { kind: 'node'; nodeId: string }\n | { kind: 'subtree'; node: ComponentNode }\n\nexport interface WrapResult {\n root: ComponentNode\n /** The id of the created Grid container. */\n containerId: string\n}\n\n/**\n * Execute a wrap plan as **one** structural edit: lift the partner out of the\n * tree (when it is an existing node) and replace the reference with\n * `gridContainerNode(containerId, WRAP_GRID_OPTIONS, [left, right])` in a\n * single pass. The caller commits the result as one builder action → one\n * operations command → one undo step; EC-161 normalization then runs on the\n * *committed* tree, so the two-child container can never be observed (or\n * persisted) in a half-built state. Returns `null` for structurally invalid\n * input (missing nodes, root reference, reference inside the partner).\n *\n * Determinism (EC-162): the produced container subtree is exactly the\n * `gridContainerNode` constructor output — identical to manual composition.\n */\nexport function applyWrap(args: {\n root: ComponentNode\n referenceId: string\n side: WrapSide\n partner: WrapPartner\n containerId: string\n}): WrapResult | null {\n const { root, referenceId, side, partner, containerId } = args\n if (referenceId === root.id || !getNode(root, referenceId)) return null\n\n let working = root\n let partnerNode: ComponentNode\n if (partner.kind === 'node') {\n if (partner.nodeId === referenceId || partner.nodeId === root.id) return null\n const node = getNode(root, partner.nodeId)\n if (!node || isDescendantOf(root, partner.nodeId, referenceId)) return null\n partnerNode = node\n working = removeNode(root, partner.nodeId)\n } else {\n partnerNode = partner.node\n }\n\n const next = updateNode(working, referenceId, (reference) =>\n gridContainerNode(\n containerId,\n WRAP_GRID_OPTIONS,\n side === 'left' ? [partnerNode, reference] : [reference, partnerNode],\n ),\n )\n return { root: next, containerId }\n}\n\n// ---------------------------------------------------------------------------\n// Pointer → position bands (shared hit-testing model)\n// ---------------------------------------------------------------------------\n\n/** The axis along which a container lays out its children. */\nexport type FlowAxis = 'vertical' | 'horizontal'\n\nfunction staticPropValue(node: ComponentNode, name: string): unknown {\n const prop = node.props?.[name]\n return isStaticProp(prop) ? prop.value : undefined\n}\n\n/**\n * The flow axis of a container — the axis its children stack along, and\n * therefore the hit-band axis for drops over those children (EC-162 ledger\n * fix: bands follow the parent's flow direction, on both surfaces). Mirrors\n * the renderer: a horizontal Stack flows horizontally; a Grid flows\n * horizontally when it actually has ≥ 2 column tracks (an absent/invalid\n * `columns` renders one column). Everything else is block flow (vertical).\n */\nexport function containerFlowAxis(node: ComponentNode | undefined): FlowAxis {\n if (!node) return 'vertical'\n if (node.componentId === STACK_COMPONENT_ID) {\n return staticPropValue(node, 'direction') === 'horizontal' ? 'horizontal' : 'vertical'\n }\n if (node.componentId === GRID_COMPONENT_ID) {\n const columns = staticPropValue(node, 'columns')\n return typeof columns === 'number' && columns >= 2 ? 'horizontal' : 'vertical'\n }\n return 'vertical'\n}\n\n/** Cross-axis side-band width (fraction of the box) that triggers the wrap gesture. */\nconst WRAP_BAND = 0.18\n\n/**\n * Map a 2D pointer offset within a drop target's box onto a `DropPosition`\n * (EC-162). `flow` is the flow axis of the target's *parent* — before/after\n * bands run along it. On vertically flowing targets, narrow side bands on the\n * cross axis produce `left`/`right` (the wrap-into-columns gesture, when\n * `wrappable`); children of horizontal containers get left/right bands for\n * plain reorder instead, so \"beside\" already means before/after there.\n */\nexport function dropPositionForPoint(args: {\n /** Pointer offset across the target's width, 0..1. */\n x: number\n /** Pointer offset across the target's height, 0..1. */\n y: number\n /** Flow axis of the target's parent (the axis siblings stack along). */\n flow: FlowAxis\n nestable: boolean\n edges: boolean\n /** Enable the wrap side-bands (the gesture surface opts in). */\n wrappable?: boolean\n}): DropPosition {\n const { x, y, flow, nestable, edges, wrappable = false } = args\n if (!edges) return 'into'\n if (wrappable && flow === 'vertical') {\n if (x < WRAP_BAND) return 'left'\n if (x > 1 - WRAP_BAND) return 'right'\n }\n return dropPositionForPointer({ ratio: flow === 'horizontal' ? x : y, nestable, edges })\n}\n\n/**\n * Map a pointer offset within a drop target's box onto a `DropPosition`.\n * `ratio` is the pointer's offset along the target's main axis, 0..1 — the\n * caller picks the axis (the parent's flow axis; see `containerFlowAxis`).\n *\n * - nestable targets with siblings: edge quarters insert before/after, the\n * middle half nests into the slot\n * - leaf targets: top/bottom halves insert before/after\n * - targets without siblings (the root): always `into`\n */\nexport function dropPositionForPointer(args: {\n ratio: number\n /** Whether `into` is structurally possible (target declares an insert slot). */\n nestable: boolean\n /** Whether before/after exist (target has a parent slot). */\n edges: boolean\n}): DropPosition {\n const { ratio, nestable, edges } = args\n if (!edges) return 'into'\n if (nestable) {\n if (ratio < 0.25) return 'before'\n if (ratio > 0.75) return 'after'\n return 'into'\n }\n return ratio < 0.5 ? 'before' : 'after'\n}\n\n// ---------------------------------------------------------------------------\n// Indicator state (shared visual contract)\n// ---------------------------------------------------------------------------\n\n/**\n * The visual state a surface shows for the current resolution. `blocked` is\n * the explicit rejection state (never a silent failed drop); `null` means show\n * nothing (a no-op drop is inert, not an error).\n */\nexport type DropIndicator = DropPosition | 'blocked' | null\n\nexport function dropIndicatorFor(\n resolution: DropResolution,\n position: DropPosition,\n): DropIndicator {\n if (resolution.eligible) return position\n return resolution.code === 'no-op' ? null : 'blocked'\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { SPACING_TOKEN_CSS, SPACING_TOKEN_NAMES, isSpacingTokenName, spacingTokenFromLegacyGap, } from '@elytracms/core/project-graph';
1
+ import { SPACING_TOKEN_CSS, SPACING_TOKEN_NAMES, isSpacingTokenName, isStaticProp, spacingTokenFromLegacyGap, } from '@elytracms/core/project-graph';
2
2
  /** The full drag range, smallest to largest: none, then the EC-161 token scale. */
3
3
  export const GAP_STEPS = [null, ...SPACING_TOKEN_NAMES];
4
4
  /** Canvas root font size — the deterministic rem basis for token CSS lengths. */
@@ -45,7 +45,7 @@ export function gapStepFromDrag(start, deltaPx) {
45
45
  */
46
46
  export function containerGapStep(node) {
47
47
  const prop = node.props?.['gap'];
48
- if (prop?.kind !== 'static')
48
+ if (!isStaticProp(prop))
49
49
  return null;
50
50
  if (typeof prop.value === 'string' && isSpacingTokenName(prop.value))
51
51
  return prop.value;
@@ -1 +1 @@
1
- {"version":3,"file":"gap-drag.js","sourceRoot":"","sources":["../../../src/lib/dnd/gap-drag.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n SPACING_TOKEN_CSS,\n SPACING_TOKEN_NAMES,\n isSpacingTokenName,\n spacingTokenFromLegacyGap,\n} from '@elytracms/core/project-graph'\nimport type { ComponentNode, SpacingTokenName } from '@elytracms/core/project-graph'\nimport type { FlowAxis } from './drop-rules'\n\n/**\n * Tokenized gap-drag math (EC-163, vision §3.1 / AD-10) — pure functions with\n * no DOM dependencies, shared by the canvas spacing handles. The drag model is\n * deliberately token-quantized: a pointer delta moves the gap through the\n * EC-161 spacing-token *steps*, and the only values that can ever be produced\n * are a `SpacingTokenName` or `null` (\"no gap\" — the prop is removed, exactly\n * what the layout inspector's \"None\" choice does). No freeform pixel value can\n * leave this module, so none can enter the graph from the canvas.\n */\n\n/** A gap step the drag can land on: a spacing token, or `null` for \"no gap\". */\nexport type GapStep = SpacingTokenName | null\n\n/** The full drag range, smallest to largest: none, then the EC-161 token scale. */\nexport const GAP_STEPS: readonly GapStep[] = [null, ...SPACING_TOKEN_NAMES]\n\n/** Canvas root font size — the deterministic rem basis for token CSS lengths. */\nexport const GAP_REM_PX = 16\n\n/** The pixel length of a gap step at the canvas rem basis. */\nexport function gapStepPx(step: GapStep): number {\n if (step === null) return 0\n const css = SPACING_TOKEN_CSS[step]\n const value = Number.parseFloat(css)\n return css.endsWith('rem') ? value * GAP_REM_PX : value\n}\n\n/** The CSS length a previewed step renders with (the same length the renderer emits). */\nexport function gapStepCss(step: GapStep): string {\n return step === null ? '0px' : SPACING_TOKEN_CSS[step]\n}\n\n/** Snap a raw pixel gap onto the nearest step of the token scale. */\nexport function nearestGapStep(px: number): GapStep {\n let best: GapStep = null\n let bestDistance = Number.POSITIVE_INFINITY\n for (const step of GAP_STEPS) {\n const distance = Math.abs(gapStepPx(step) - px)\n if (distance < bestDistance) {\n bestDistance = distance\n best = step\n }\n }\n return best\n}\n\n/**\n * The step a drag lands on: the starting step's pixel length plus the pointer\n * delta along the container's flow axis, snapped to the scale. Monotonic and\n * deterministic — the same delta from the same start always lands on the same\n * token.\n */\nexport function gapStepFromDrag(start: GapStep, deltaPx: number): GapStep {\n return nearestGapStep(gapStepPx(start) + deltaPx)\n}\n\n/**\n * The current gap step of a container node: a token name verbatim, a legacy\n * on-scale integer mapped to its token, anything else (absent, off-scale,\n * bound) as `null` — mirroring how the layout inspector reads the same prop,\n * so handle and inspector always start from the same value.\n */\nexport function containerGapStep(node: ComponentNode): GapStep {\n const prop = node.props?.['gap']\n if (prop?.kind !== 'static') return null\n if (typeof prop.value === 'string' && isSpacingTokenName(prop.value)) return prop.value\n if (typeof prop.value === 'number') {\n const token = spacingTokenFromLegacyGap(prop.value)\n if (token) return token\n }\n return null\n}\n\n// ---------------------------------------------------------------------------\n// Handle geometry — where the between-children handles sit\n// ---------------------------------------------------------------------------\n\n/** A minimal rectangle (client or document coordinates — caller's choice). */\nexport interface GapRect {\n left: number\n top: number\n width: number\n height: number\n}\n\n/**\n * One draggable spot between two adjacent children. `x`/`y` is the gap's\n * center point; `crossStart`/`crossLength` span the children's shared extent\n * on the cross axis (the handle's visible length).\n */\nexport interface GapHandleSpot {\n /** Index of the leading child of the pair. */\n index: number\n x: number\n y: number\n crossStart: number\n crossLength: number\n}\n\n/**\n * The handle spots between consecutive child rects along the container's flow\n * axis. Pairs that wrap (grid rows: the next rect starts before the previous\n * ends along the axis) or that do not overlap on the cross axis yield no\n * handle — handles only ever sit in real gaps between visual neighbours.\n * Works at gap 0 too: the handle sits on the shared boundary so a closed gap\n * can be dragged open.\n */\nexport function gapHandleSpots(rects: readonly GapRect[], axis: FlowAxis): GapHandleSpot[] {\n const spots: GapHandleSpot[] = []\n for (let i = 0; i < rects.length - 1; i += 1) {\n const a = rects[i]!\n const b = rects[i + 1]!\n const aEnd = axis === 'horizontal' ? a.left + a.width : a.top + a.height\n const bStart = axis === 'horizontal' ? b.left : b.top\n // Wrapped to the next row/column: not visual neighbours along the axis.\n // A small tolerance keeps sub-pixel layout from dropping legitimate pairs.\n if (bStart < aEnd - 0.5) continue\n const crossStart = axis === 'horizontal' ? Math.max(a.top, b.top) : Math.max(a.left, b.left)\n const crossEnd =\n axis === 'horizontal'\n ? Math.min(a.top + a.height, b.top + b.height)\n : Math.min(a.left + a.width, b.left + b.width)\n if (crossEnd <= crossStart) continue\n const mid = (aEnd + bStart) / 2\n spots.push({\n index: i,\n x: axis === 'horizontal' ? mid : crossStart + (crossEnd - crossStart) / 2,\n y: axis === 'horizontal' ? crossStart + (crossEnd - crossStart) / 2 : mid,\n crossStart,\n crossLength: crossEnd - crossStart,\n })\n }\n return spots\n}\n"]}
1
+ {"version":3,"file":"gap-drag.js","sourceRoot":"","sources":["../../../src/lib/dnd/gap-drag.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n SPACING_TOKEN_CSS,\n SPACING_TOKEN_NAMES,\n isSpacingTokenName,\n isStaticProp,\n spacingTokenFromLegacyGap,\n} from '@elytracms/core/project-graph'\nimport type { ComponentNode, SpacingTokenName } from '@elytracms/core/project-graph'\nimport type { FlowAxis } from './drop-rules'\n\n/**\n * Tokenized gap-drag math (EC-163, vision §3.1 / AD-10) — pure functions with\n * no DOM dependencies, shared by the canvas spacing handles. The drag model is\n * deliberately token-quantized: a pointer delta moves the gap through the\n * EC-161 spacing-token *steps*, and the only values that can ever be produced\n * are a `SpacingTokenName` or `null` (\"no gap\" — the prop is removed, exactly\n * what the layout inspector's \"None\" choice does). No freeform pixel value can\n * leave this module, so none can enter the graph from the canvas.\n */\n\n/** A gap step the drag can land on: a spacing token, or `null` for \"no gap\". */\nexport type GapStep = SpacingTokenName | null\n\n/** The full drag range, smallest to largest: none, then the EC-161 token scale. */\nexport const GAP_STEPS: readonly GapStep[] = [null, ...SPACING_TOKEN_NAMES]\n\n/** Canvas root font size — the deterministic rem basis for token CSS lengths. */\nexport const GAP_REM_PX = 16\n\n/** The pixel length of a gap step at the canvas rem basis. */\nexport function gapStepPx(step: GapStep): number {\n if (step === null) return 0\n const css = SPACING_TOKEN_CSS[step]\n const value = Number.parseFloat(css)\n return css.endsWith('rem') ? value * GAP_REM_PX : value\n}\n\n/** The CSS length a previewed step renders with (the same length the renderer emits). */\nexport function gapStepCss(step: GapStep): string {\n return step === null ? '0px' : SPACING_TOKEN_CSS[step]\n}\n\n/** Snap a raw pixel gap onto the nearest step of the token scale. */\nexport function nearestGapStep(px: number): GapStep {\n let best: GapStep = null\n let bestDistance = Number.POSITIVE_INFINITY\n for (const step of GAP_STEPS) {\n const distance = Math.abs(gapStepPx(step) - px)\n if (distance < bestDistance) {\n bestDistance = distance\n best = step\n }\n }\n return best\n}\n\n/**\n * The step a drag lands on: the starting step's pixel length plus the pointer\n * delta along the container's flow axis, snapped to the scale. Monotonic and\n * deterministic — the same delta from the same start always lands on the same\n * token.\n */\nexport function gapStepFromDrag(start: GapStep, deltaPx: number): GapStep {\n return nearestGapStep(gapStepPx(start) + deltaPx)\n}\n\n/**\n * The current gap step of a container node: a token name verbatim, a legacy\n * on-scale integer mapped to its token, anything else (absent, off-scale,\n * bound) as `null` — mirroring how the layout inspector reads the same prop,\n * so handle and inspector always start from the same value.\n */\nexport function containerGapStep(node: ComponentNode): GapStep {\n const prop = node.props?.['gap']\n if (!isStaticProp(prop)) return null\n if (typeof prop.value === 'string' && isSpacingTokenName(prop.value)) return prop.value\n if (typeof prop.value === 'number') {\n const token = spacingTokenFromLegacyGap(prop.value)\n if (token) return token\n }\n return null\n}\n\n// ---------------------------------------------------------------------------\n// Handle geometry — where the between-children handles sit\n// ---------------------------------------------------------------------------\n\n/** A minimal rectangle (client or document coordinates — caller's choice). */\nexport interface GapRect {\n left: number\n top: number\n width: number\n height: number\n}\n\n/**\n * One draggable spot between two adjacent children. `x`/`y` is the gap's\n * center point; `crossStart`/`crossLength` span the children's shared extent\n * on the cross axis (the handle's visible length).\n */\nexport interface GapHandleSpot {\n /** Index of the leading child of the pair. */\n index: number\n x: number\n y: number\n crossStart: number\n crossLength: number\n}\n\n/**\n * The handle spots between consecutive child rects along the container's flow\n * axis. Pairs that wrap (grid rows: the next rect starts before the previous\n * ends along the axis) or that do not overlap on the cross axis yield no\n * handle — handles only ever sit in real gaps between visual neighbours.\n * Works at gap 0 too: the handle sits on the shared boundary so a closed gap\n * can be dragged open.\n */\nexport function gapHandleSpots(rects: readonly GapRect[], axis: FlowAxis): GapHandleSpot[] {\n const spots: GapHandleSpot[] = []\n for (let i = 0; i < rects.length - 1; i += 1) {\n const a = rects[i]!\n const b = rects[i + 1]!\n const aEnd = axis === 'horizontal' ? a.left + a.width : a.top + a.height\n const bStart = axis === 'horizontal' ? b.left : b.top\n // Wrapped to the next row/column: not visual neighbours along the axis.\n // A small tolerance keeps sub-pixel layout from dropping legitimate pairs.\n if (bStart < aEnd - 0.5) continue\n const crossStart = axis === 'horizontal' ? Math.max(a.top, b.top) : Math.max(a.left, b.left)\n const crossEnd =\n axis === 'horizontal'\n ? Math.min(a.top + a.height, b.top + b.height)\n : Math.min(a.left + a.width, b.left + b.width)\n if (crossEnd <= crossStart) continue\n const mid = (aEnd + bStart) / 2\n spots.push({\n index: i,\n x: axis === 'horizontal' ? mid : crossStart + (crossEnd - crossStart) / 2,\n y: axis === 'horizontal' ? crossStart + (crossEnd - crossStart) / 2 : mid,\n crossStart,\n crossLength: crossEnd - crossStart,\n })\n }\n return spots\n}\n"]}
@@ -12,12 +12,12 @@ export declare function operationErrorLabel(code: OperationErrorCode): string;
12
12
  export declare function exportTargetLabel(target: string): string;
13
13
  /** Plain-language label for a condition operator (`gte` → `At least`). */
14
14
  export declare function conditionOperatorLabel(operator: ConditionOperator): string;
15
- /** The leaf of a component id, namespace stripped (`base.primitives.Heading` → `Heading`). */
15
+ /** The leaf of a component id, namespace stripped (`base.Heading` → `Heading`). */
16
16
  export declare function componentLeafId(componentId: string): string;
17
17
  /**
18
18
  * The human display name of a component for editor-facing surfaces: the
19
19
  * manifest's authored `title` when present, else the humanized leaf of its id
20
- * (`base.primitives.Heading` → `Heading`, `project.MarketingHero` → `Marketing hero`).
20
+ * (`base.Heading` → `Heading`, `project.MarketingHero` → `Marketing hero`).
21
21
  * Never the raw namespaced id.
22
22
  */
23
23
  export declare function componentDisplayName(componentId: string, manifest?: ComponentManifest | undefined): string;
@@ -5,7 +5,7 @@ import { humanizeName } from '../utils';
5
5
  * A non-technical editor must never see a manifest/binding/path-token/validation
6
6
  * identifier on — or one click off — the editor path. The engine speaks in stable
7
7
  * machine codes (`issueCodeSchema`, `cmsIssueCodeSchema`, `operationErrorCodeSchema`)
8
- * and machine ids (`base.primitives.Heading`, `r-blog-slug-en`); this module is the
8
+ * and machine ids (`base.Heading`, `r-blog-slug-en`); this module is the
9
9
  * single translation layer the editor-facing surfaces route through.
10
10
  *
11
11
  * The maps are exhaustive `Record<…Code, string>` over the Zod enums, so adding a
@@ -108,7 +108,7 @@ export function exportTargetLabel(target) {
108
108
  export function conditionOperatorLabel(operator) {
109
109
  return CONDITION_OPERATOR_LABELS[operator];
110
110
  }
111
- /** The leaf of a component id, namespace stripped (`base.primitives.Heading` → `Heading`). */
111
+ /** The leaf of a component id, namespace stripped (`base.Heading` → `Heading`). */
112
112
  export function componentLeafId(componentId) {
113
113
  const dot = componentId.lastIndexOf('.');
114
114
  return dot === -1 ? componentId : componentId.slice(dot + 1);
@@ -116,7 +116,7 @@ export function componentLeafId(componentId) {
116
116
  /**
117
117
  * The human display name of a component for editor-facing surfaces: the
118
118
  * manifest's authored `title` when present, else the humanized leaf of its id
119
- * (`base.primitives.Heading` → `Heading`, `project.MarketingHero` → `Marketing hero`).
119
+ * (`base.Heading` → `Heading`, `project.MarketingHero` → `Marketing hero`).
120
120
  * Never the raw namespaced id.
121
121
  */
122
122
  export function componentDisplayName(componentId, manifest) {
@@ -1 +1 @@
1
- {"version":3,"file":"labels.js","sourceRoot":"","sources":["../../../src/lib/persona/labels.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IssueCode, ConditionOperator } from '@elytracms/core/project-graph'\nimport type { CmsIssueCode } from '@elytracms/core/cms-core'\nimport type { OperationErrorCode } from '@elytracms/core/operations'\nimport type { ComponentManifest } from '@elytracms/core/component-registry'\nimport { humanizeName } from '../utils'\n\n/**\n * Persona-aware labels (EC-192 item 2, Milestone B promise).\n *\n * A non-technical editor must never see a manifest/binding/path-token/validation\n * identifier on — or one click off — the editor path. The engine speaks in stable\n * machine codes (`issueCodeSchema`, `cmsIssueCodeSchema`, `operationErrorCodeSchema`)\n * and machine ids (`base.primitives.Heading`, `r-blog-slug-en`); this module is the\n * single translation layer the editor-facing surfaces route through.\n *\n * The maps are exhaustive `Record<…Code, string>` over the Zod enums, so adding a\n * code in `@elytracms/core/project-graph`, `@elytracms/core/cms-core`, or `@elytracms/core/operations`\n * fails typecheck here until it is given an editor-facing label — the persona\n * boundary cannot silently regress. Engineers keep the raw code: every surface that\n * shows a label also carries the code in a `title=`/`data-*` attribute (inspectable\n * via hover/devtools, never rendered as visible copy).\n */\n\nconst GRAPH_ISSUE_LABELS: Record<IssueCode, string> = {\n 'invalid-component-name': 'Invalid component',\n 'unknown-component': 'Unknown component',\n 'duplicate-node-id': 'Duplicate item',\n 'unknown-prop': 'Unknown setting',\n 'invalid-prop': 'Invalid setting',\n 'binding-not-allowed': 'Data not allowed here',\n 'unresolved-binding': 'Missing data',\n 'missing-required-slot': 'Missing content',\n 'unknown-slot': 'Unknown content area',\n 'forbidden-component': 'Not allowed here',\n 'server-only-component': 'Not available in the editor',\n 'invalid-container-direction': 'Invalid layout',\n 'invalid-container-alignment': 'Invalid alignment',\n 'invalid-container-gap': 'Invalid spacing',\n 'invalid-container-columns': 'Invalid columns',\n 'invalid-container-responsive': 'Invalid responsive setting',\n}\n\nconst CMS_ISSUE_LABELS: Record<CmsIssueCode, string> = {\n 'duplicate-collection': 'Duplicate collection',\n 'duplicate-field': 'Duplicate field',\n 'unknown-collection': 'Unknown collection',\n 'invalid-collection-config': 'Invalid collection setup',\n 'invalid-field-config': 'Invalid field setup',\n 'reserved-field-name': 'Reserved field name',\n 'missing-required-field': 'Missing required field',\n 'invalid-field-value': 'Invalid value',\n 'unknown-relation-target': 'Missing reference',\n 'unpublished-relation-target': 'Unpublished reference',\n 'cardinality-violation': 'Wrong number of values',\n 'unknown-asset': 'Missing asset',\n 'duplicate-document': 'Duplicate entry',\n 'unknown-locale': 'Unknown language',\n 'missing-localized-value': 'Missing translation',\n 'route-conflict': 'Conflicting URL',\n 'redirect-loop': 'Redirect loop',\n 'unknown-route-target': 'Missing page',\n 'hierarchy-cycle': 'Page nesting loop',\n 'document-nesting-depth': 'Content nested too deeply',\n 'singleton-document-drift': 'More than one document',\n 'duplicate-alias': 'Slot used twice',\n 'unknown-alias': 'Unknown slot',\n 'unfilled-slot': 'Empty slot',\n 'custom-validation': 'Validation rule',\n 'unknown-version': 'Unknown version',\n}\n\nconst OPERATION_ERROR_LABELS: Record<OperationErrorCode, string> = {\n validation: \"Couldn't save\",\n 'not-found': 'Not found',\n conflict: 'Already exists',\n 'backend-unavailable': 'Connection problem',\n unauthorized: 'Not allowed',\n unknown: 'Something went wrong',\n}\n\nconst EXPORT_TARGET_LABELS: Record<string, string> = {\n nextjs: 'Next.js',\n}\n\n/**\n * Plain-language labels for condition operators. Exhaustive over\n * `conditionOperatorSchema` — a new operator fails typecheck here until it has an\n * editor-facing label. The raw operator (`eq`, `gte`, …) is a machine token an\n * editor must never read in the inspector.\n */\nconst CONDITION_OPERATOR_LABELS: Record<ConditionOperator, string> = {\n truthy: 'Is set',\n exists: 'Exists',\n eq: 'Equals',\n neq: 'Does not equal',\n gt: 'Greater than',\n gte: 'At least',\n lt: 'Less than',\n lte: 'At most',\n}\n\n/** Plain-language title for a graph validation issue code. */\nexport function graphIssueLabel(code: IssueCode): string {\n return GRAPH_ISSUE_LABELS[code]\n}\n\n/** Plain-language title for a CMS validation issue code. */\nexport function cmsIssueLabel(code: CmsIssueCode): string {\n return CMS_ISSUE_LABELS[code]\n}\n\n/** Plain-language title for an operation failure code. */\nexport function operationErrorLabel(code: OperationErrorCode): string {\n return OPERATION_ERROR_LABELS[code] ?? OPERATION_ERROR_LABELS.unknown\n}\n\n/** Friendly name for a project export target (`nextjs` → `Next.js`). */\nexport function exportTargetLabel(target: string): string {\n return EXPORT_TARGET_LABELS[target] ?? humanizeName(target)\n}\n\n/** Plain-language label for a condition operator (`gte` → `At least`). */\nexport function conditionOperatorLabel(operator: ConditionOperator): string {\n return CONDITION_OPERATOR_LABELS[operator]\n}\n\n/** The leaf of a component id, namespace stripped (`base.primitives.Heading` → `Heading`). */\nexport function componentLeafId(componentId: string): string {\n const dot = componentId.lastIndexOf('.')\n return dot === -1 ? componentId : componentId.slice(dot + 1)\n}\n\n/**\n * The human display name of a component for editor-facing surfaces: the\n * manifest's authored `title` when present, else the humanized leaf of its id\n * (`base.primitives.Heading` → `Heading`, `project.MarketingHero` → `Marketing hero`).\n * Never the raw namespaced id.\n */\nexport function componentDisplayName(\n componentId: string,\n manifest?: ComponentManifest | undefined,\n): string {\n if (manifest?.title) return manifest.title\n return humanizeName(componentLeafId(componentId))\n}\n"]}
1
+ {"version":3,"file":"labels.js","sourceRoot":"","sources":["../../../src/lib/persona/labels.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IssueCode, ConditionOperator } from '@elytracms/core/project-graph'\nimport type { CmsIssueCode } from '@elytracms/core/cms-core'\nimport type { OperationErrorCode } from '@elytracms/core/operations'\nimport type { ComponentManifest } from '@elytracms/core/component-registry'\nimport { humanizeName } from '../utils'\n\n/**\n * Persona-aware labels (EC-192 item 2, Milestone B promise).\n *\n * A non-technical editor must never see a manifest/binding/path-token/validation\n * identifier on — or one click off — the editor path. The engine speaks in stable\n * machine codes (`issueCodeSchema`, `cmsIssueCodeSchema`, `operationErrorCodeSchema`)\n * and machine ids (`base.Heading`, `r-blog-slug-en`); this module is the\n * single translation layer the editor-facing surfaces route through.\n *\n * The maps are exhaustive `Record<…Code, string>` over the Zod enums, so adding a\n * code in `@elytracms/core/project-graph`, `@elytracms/core/cms-core`, or `@elytracms/core/operations`\n * fails typecheck here until it is given an editor-facing label — the persona\n * boundary cannot silently regress. Engineers keep the raw code: every surface that\n * shows a label also carries the code in a `title=`/`data-*` attribute (inspectable\n * via hover/devtools, never rendered as visible copy).\n */\n\nconst GRAPH_ISSUE_LABELS: Record<IssueCode, string> = {\n 'invalid-component-name': 'Invalid component',\n 'unknown-component': 'Unknown component',\n 'duplicate-node-id': 'Duplicate item',\n 'unknown-prop': 'Unknown setting',\n 'invalid-prop': 'Invalid setting',\n 'binding-not-allowed': 'Data not allowed here',\n 'unresolved-binding': 'Missing data',\n 'missing-required-slot': 'Missing content',\n 'unknown-slot': 'Unknown content area',\n 'forbidden-component': 'Not allowed here',\n 'server-only-component': 'Not available in the editor',\n 'invalid-container-direction': 'Invalid layout',\n 'invalid-container-alignment': 'Invalid alignment',\n 'invalid-container-gap': 'Invalid spacing',\n 'invalid-container-columns': 'Invalid columns',\n 'invalid-container-responsive': 'Invalid responsive setting',\n}\n\nconst CMS_ISSUE_LABELS: Record<CmsIssueCode, string> = {\n 'duplicate-collection': 'Duplicate collection',\n 'duplicate-field': 'Duplicate field',\n 'unknown-collection': 'Unknown collection',\n 'invalid-collection-config': 'Invalid collection setup',\n 'invalid-field-config': 'Invalid field setup',\n 'reserved-field-name': 'Reserved field name',\n 'missing-required-field': 'Missing required field',\n 'invalid-field-value': 'Invalid value',\n 'unknown-relation-target': 'Missing reference',\n 'unpublished-relation-target': 'Unpublished reference',\n 'cardinality-violation': 'Wrong number of values',\n 'unknown-asset': 'Missing asset',\n 'duplicate-document': 'Duplicate entry',\n 'unknown-locale': 'Unknown language',\n 'missing-localized-value': 'Missing translation',\n 'route-conflict': 'Conflicting URL',\n 'redirect-loop': 'Redirect loop',\n 'unknown-route-target': 'Missing page',\n 'hierarchy-cycle': 'Page nesting loop',\n 'document-nesting-depth': 'Content nested too deeply',\n 'singleton-document-drift': 'More than one document',\n 'duplicate-alias': 'Slot used twice',\n 'unknown-alias': 'Unknown slot',\n 'unfilled-slot': 'Empty slot',\n 'custom-validation': 'Validation rule',\n 'unknown-version': 'Unknown version',\n}\n\nconst OPERATION_ERROR_LABELS: Record<OperationErrorCode, string> = {\n validation: \"Couldn't save\",\n 'not-found': 'Not found',\n conflict: 'Already exists',\n 'backend-unavailable': 'Connection problem',\n unauthorized: 'Not allowed',\n unknown: 'Something went wrong',\n}\n\nconst EXPORT_TARGET_LABELS: Record<string, string> = {\n nextjs: 'Next.js',\n}\n\n/**\n * Plain-language labels for condition operators. Exhaustive over\n * `conditionOperatorSchema` — a new operator fails typecheck here until it has an\n * editor-facing label. The raw operator (`eq`, `gte`, …) is a machine token an\n * editor must never read in the inspector.\n */\nconst CONDITION_OPERATOR_LABELS: Record<ConditionOperator, string> = {\n truthy: 'Is set',\n exists: 'Exists',\n eq: 'Equals',\n neq: 'Does not equal',\n gt: 'Greater than',\n gte: 'At least',\n lt: 'Less than',\n lte: 'At most',\n}\n\n/** Plain-language title for a graph validation issue code. */\nexport function graphIssueLabel(code: IssueCode): string {\n return GRAPH_ISSUE_LABELS[code]\n}\n\n/** Plain-language title for a CMS validation issue code. */\nexport function cmsIssueLabel(code: CmsIssueCode): string {\n return CMS_ISSUE_LABELS[code]\n}\n\n/** Plain-language title for an operation failure code. */\nexport function operationErrorLabel(code: OperationErrorCode): string {\n return OPERATION_ERROR_LABELS[code] ?? OPERATION_ERROR_LABELS.unknown\n}\n\n/** Friendly name for a project export target (`nextjs` → `Next.js`). */\nexport function exportTargetLabel(target: string): string {\n return EXPORT_TARGET_LABELS[target] ?? humanizeName(target)\n}\n\n/** Plain-language label for a condition operator (`gte` → `At least`). */\nexport function conditionOperatorLabel(operator: ConditionOperator): string {\n return CONDITION_OPERATOR_LABELS[operator]\n}\n\n/** The leaf of a component id, namespace stripped (`base.Heading` → `Heading`). */\nexport function componentLeafId(componentId: string): string {\n const dot = componentId.lastIndexOf('.')\n return dot === -1 ? componentId : componentId.slice(dot + 1)\n}\n\n/**\n * The human display name of a component for editor-facing surfaces: the\n * manifest's authored `title` when present, else the humanized leaf of its id\n * (`base.Heading` → `Heading`, `project.MarketingHero` → `Marketing hero`).\n * Never the raw namespaced id.\n */\nexport function componentDisplayName(\n componentId: string,\n manifest?: ComponentManifest | undefined,\n): string {\n if (manifest?.title) return manifest.title\n return humanizeName(componentLeafId(componentId))\n}\n"]}
@@ -107,7 +107,7 @@ export function documentTitleOf(collection, document, fallback) {
107
107
  }
108
108
  /** Strip the namespace off a component id for compact labels. */
109
109
  export function shortComponentId(componentId) {
110
- return componentId.replace('base.primitives.', '').replace('project.', '');
110
+ return componentId.replace('base.', '').replace('project.', '');
111
111
  }
112
112
  function documentParts(context, key) {
113
113
  const ref = parseDocumentKey(key);
@@ -1 +1 @@
1
- {"version":3,"file":"reference-model.js","sourceRoot":"","sources":["../../../src/lib/references/reference-model.ts"],"names":[],"mappings":"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","sourcesContent":["import { resolveDocumentTitle } from '@elytracms/core/cms-core'\nimport { localizedLabel } from '@elytracms/studio/lib/i18n'\nimport type { CmsDocument, CollectionDef } from '@elytracms/core/cms-core'\nimport type {\n ReferenceEntryRecord,\n ReferenceKind,\n ReferenceSource,\n ReferenceTarget,\n} from '@elytracms/core/persistence'\n\n/**\n * Pure presentation model for the EC-172 references panels.\n *\n * The EC-155 index stores entries as `(source, path, target, kind)` per\n * variant; this module turns those records into renderable rows: variants\n * merged onto one row (draft/published badges), human-readable source/\n * location/target labels, and deep-link descriptors that pinpoint the exact\n * field (CMS) or graph node (builder) a path identifies. No data fetching, no\n * React — everything here is a deterministic function of its inputs, so it is\n * fully testable in the node environment.\n */\n\n// --- Path parsing -------------------------------------------------------------\n\n/** Split a `collection:id` document key (the index's document source/target id). */\nexport function parseDocumentKey(key: string): { collection: string; id: string } | null {\n const separator = key.indexOf(':')\n if (separator <= 0 || separator === key.length - 1) return null\n return { collection: key.slice(0, separator), id: key.slice(separator + 1) }\n}\n\nexport interface DocumentFieldPath {\n /** Present for `localized/<locale>/<field>` paths. */\n locale?: string\n field: string\n /** Tokens below the field value (array indices, rich-text node paths). */\n rest: readonly string[]\n}\n\n/**\n * Parse a document index path (`values/<field>/…` or\n * `localized/<locale>/<field>/…`) onto the owning field.\n */\nexport function parseDocumentFieldPath(path: string): DocumentFieldPath | null {\n const tokens = path.split('/')\n if (tokens[0] === 'values' && tokens[1]) {\n return { field: tokens[1], rest: tokens.slice(2) }\n }\n if (tokens[0] === 'localized' && tokens[1] && tokens[2]) {\n return { locale: tokens[1], field: tokens[2], rest: tokens.slice(3) }\n }\n return null\n}\n\n// --- Variant merging ----------------------------------------------------------\n\n/** One panel row: the same logical entry across both variants, with flags. */\nexport interface MergedReferenceEntry {\n /** Stable row key (source + path + target + kind). */\n key: string\n source: ReferenceSource\n path: string\n target: ReferenceTarget\n kind: ReferenceKind\n /** Present in the draft variant of the index. */\n draft: boolean\n /** Present in the published variant (a live reference). */\n published: boolean\n}\n\n/**\n * Merge per-variant index records onto one row per logical entry. A\n * draft-only row (`draft && !published`) is the Sanity hidden-draft-reference\n * case the panel must make visible. Order is deterministic:\n * source type → source id → path → target type → target id → kind.\n */\nexport function mergeReferenceVariants(\n records: readonly ReferenceEntryRecord[],\n): MergedReferenceEntry[] {\n const byKey = new Map<string, MergedReferenceEntry>()\n for (const record of records) {\n const key = `${record.source.type} ${record.source.id} ${record.path} ${record.target.type} ${record.target.id} ${record.kind}`\n const existing = byKey.get(key)\n if (existing) {\n if (record.variant === 'draft') existing.draft = true\n else existing.published = true\n continue\n }\n byKey.set(key, {\n key,\n source: record.source,\n path: record.path,\n target: record.target,\n kind: record.kind,\n draft: record.variant === 'draft',\n published: record.variant === 'published',\n })\n }\n return [...byKey.values()].sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0))\n}\n\n// --- Labels & deep links --------------------------------------------------------\n\n/** Human label per reference kind (entry-row badge). */\nexport const REFERENCE_KIND_LABELS: Record<ReferenceKind, string> = {\n 'relation-field': 'relation',\n 'asset-field': 'asset field',\n 'rich-text-link': 'rich-text link',\n 'rich-text-node': 'rich-text node',\n 'component-usage': 'component usage',\n}\n\n/**\n * A deep-link descriptor the panel renders as a router link. `cms-field`\n * carries the full index path so the document editor can highlight the exact\n * field control; `builder-node` carries the resolved node id so the builder\n * can select + scroll to the node.\n */\nexport type ReferenceDeepLink =\n | { kind: 'cms-field'; collectionId: string; documentId: string; fieldPath?: string }\n | { kind: 'builder-node'; pageId?: string; nodeId?: string }\n | { kind: 'component'; componentId: string }\n | { kind: 'asset'; assetId: string }\n\n/** Everything the model needs to label entries (loaded once per panel). */\nexport interface ReferenceLabelContext {\n collections: readonly CollectionDef[]\n /** Live documents keyed by `collection:id`. */\n documents: ReadonlyMap<string, CmsDocument>\n /** Asset metadata keyed by asset id. */\n assets: ReadonlyMap<string, { filename: string; title?: string | undefined }>\n /** Resolves a component id to its manifest title (live registry lookup). */\n componentTitle: (componentId: string) => string | undefined\n}\n\n/** One fully-labelled panel row. */\nexport interface ReferenceEntryView extends MergedReferenceEntry {\n kindLabel: string\n /** The holding entity, e.g. `Post · Hello World` or `Page · Home`. */\n sourceLabel: string\n /** The exact location inside the source, e.g. `Author` or `Text (n-post-title)`. */\n locationLabel: string\n /** The referenced entity, e.g. `Ada Lovelace` or `cover.jpg`. */\n targetLabel: string\n /** Deep link to the exact source location (field control / graph node). */\n sourceLink: ReferenceDeepLink | null\n /** Link to the referenced entity's surface. */\n targetLink: ReferenceDeepLink | null\n}\n\nfunction fieldLabelOf(collection: CollectionDef | undefined, fieldName: string): string {\n const field = collection?.fields.find((f) => f.name === fieldName)\n // Non-hook `localizedLabel` reads the active UI locale (safe anywhere), so a\n // `{ en, de }` label resolves to the editor's language here too.\n return localizedLabel(field?.form?.label, fieldName)\n}\n\nfunction collectionLabelOf(collection: CollectionDef | undefined, collectionId: string): string {\n return (\n localizedLabel(collection?.form?.labelSingular) || localizedLabel(collection?.form?.label, collectionId)\n )\n}\n\n/** Resolve a document's display title (titleField value, default locale first). */\nexport function documentTitleOf(\n collection: CollectionDef | undefined,\n document: CmsDocument | undefined,\n fallback: string,\n): string {\n if (!collection || !document) return fallback\n // EC-242: a function `titleField` (title pattern) has no single source field —\n // resolve it from the base values via the shared identity resolver.\n if (typeof collection.titleField === 'function') {\n return resolveDocumentTitle(collection, document.values, fallback)\n }\n const field = typeof collection.titleField === 'string' ? collection.titleField : 'title'\n const direct = document.values[field]\n if (typeof direct === 'string' && direct.length > 0) return direct\n const locales = Object.keys(document.localized ?? {}).sort()\n const preferred = document.defaultLocale\n for (const locale of [preferred, ...locales]) {\n if (!locale) continue\n const value = document.localized?.[locale]?.[field]\n if (typeof value === 'string' && value.length > 0) return value\n }\n return fallback\n}\n\n/** Strip the namespace off a component id for compact labels. */\nexport function shortComponentId(componentId: string): string {\n return componentId.replace('base.primitives.', '').replace('project.', '')\n}\n\ninterface DocumentParts {\n collection: CollectionDef | undefined\n collectionId: string\n documentId: string\n title: string\n}\n\nfunction documentParts(context: ReferenceLabelContext, key: string): DocumentParts | null {\n const ref = parseDocumentKey(key)\n if (!ref) return null\n const collection = context.collections.find((c) => c.id === ref.collection)\n return {\n collection,\n collectionId: ref.collection,\n documentId: ref.id,\n title: documentTitleOf(collection, context.documents.get(key), ref.id),\n }\n}\n\nfunction describeDocumentSource(\n context: ReferenceLabelContext,\n entry: MergedReferenceEntry,\n): { sourceLabel: string; locationLabel: string; sourceLink: ReferenceDeepLink | null } {\n const parts = documentParts(context, entry.source.id)\n if (!parts) {\n return { sourceLabel: entry.source.id, locationLabel: entry.path, sourceLink: null }\n }\n const parsed = parseDocumentFieldPath(entry.path)\n const fieldLabel = parsed ? fieldLabelOf(parts.collection, parsed.field) : entry.path\n const qualifiers: string[] = []\n if (parsed?.locale) qualifiers.push(parsed.locale)\n if (entry.kind === 'rich-text-link') qualifiers.push('link')\n if (entry.kind === 'rich-text-node') qualifiers.push('embedded node')\n if (\n entry.kind === 'relation-field' &&\n parsed &&\n parsed.rest.length === 1 &&\n /^\\d+$/.test(parsed.rest[0]!)\n ) {\n qualifiers.push(`item ${parsed.rest[0]}`)\n }\n return {\n sourceLabel: `${collectionLabelOf(parts.collection, parts.collectionId)} · ${parts.title}`,\n locationLabel: qualifiers.length > 0 ? `${fieldLabel} (${qualifiers.join(', ')})` : fieldLabel,\n sourceLink: {\n kind: 'cms-field',\n collectionId: parts.collectionId,\n documentId: parts.documentId,\n fieldPath: entry.path,\n },\n }\n}\n\n\nfunction describeTarget(\n context: ReferenceLabelContext,\n target: ReferenceTarget,\n): { targetLabel: string; targetLink: ReferenceDeepLink | null } {\n switch (target.type) {\n case 'document': {\n const parts = documentParts(context, target.id)\n if (!parts) return { targetLabel: target.id, targetLink: null }\n return {\n targetLabel: `${collectionLabelOf(parts.collection, parts.collectionId)} · ${parts.title}`,\n targetLink: {\n kind: 'cms-field',\n collectionId: parts.collectionId,\n documentId: parts.documentId,\n },\n }\n }\n case 'component':\n return {\n targetLabel: context.componentTitle(target.id) ?? shortComponentId(target.id),\n targetLink: { kind: 'component', componentId: target.id },\n }\n case 'asset': {\n const asset = context.assets.get(target.id)\n return {\n targetLabel: asset ? (asset.title ?? asset.filename) : target.id,\n targetLink: { kind: 'asset', assetId: target.id },\n }\n }\n }\n}\n\n/** Label + link one merged entry (see {@link buildReferenceEntryViews}). */\nexport function buildReferenceEntryView(\n entry: MergedReferenceEntry,\n context: ReferenceLabelContext,\n): ReferenceEntryView {\n // EC-287: a reference source is only ever a CMS document (structure is code).\n const sourceParts = describeDocumentSource(context, entry)\n return {\n ...entry,\n kindLabel: REFERENCE_KIND_LABELS[entry.kind],\n ...sourceParts,\n ...describeTarget(context, entry.target),\n }\n}\n\n/** Merge variants and label every record — the panel's full render model. */\nexport function buildReferenceEntryViews(\n records: readonly ReferenceEntryRecord[],\n context: ReferenceLabelContext,\n): ReferenceEntryView[] {\n return mergeReferenceVariants(records).map((entry) => buildReferenceEntryView(entry, context))\n}\n"]}
1
+ {"version":3,"file":"reference-model.js","sourceRoot":"","sources":["../../../src/lib/references/reference-model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAA;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAA;AAS3D;;;;;;;;;;GAUG;AAEH,iFAAiF;AAEjF,oFAAoF;AACpF,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IAClC,IAAI,SAAS,IAAI,CAAC,IAAI,SAAS,KAAK,GAAG,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAA;IAC/D,OAAO,EAAE,UAAU,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC,EAAE,CAAA;AAC9E,CAAC;AAUD;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC9B,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QACxC,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAA;IACpD,CAAC;IACD,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,WAAW,IAAI,MAAM,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QACxD,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAA;IACvE,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAkBD;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CACpC,OAAwC;IAExC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAgC,CAAA;IACrD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,GAAG,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,MAAM,CAAC,IAAI,EAAE,CAAA;QAC/H,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAC/B,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,MAAM,CAAC,OAAO,KAAK,OAAO;gBAAE,QAAQ,CAAC,KAAK,GAAG,IAAI,CAAA;;gBAChD,QAAQ,CAAC,SAAS,GAAG,IAAI,CAAA;YAC9B,SAAQ;QACV,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE;YACb,GAAG;YACH,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,KAAK,EAAE,MAAM,CAAC,OAAO,KAAK,OAAO;YACjC,SAAS,EAAE,MAAM,CAAC,OAAO,KAAK,WAAW;SAC1C,CAAC,CAAA;IACJ,CAAC;IACD,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AACzF,CAAC;AAED,mFAAmF;AAEnF,wDAAwD;AACxD,MAAM,CAAC,MAAM,qBAAqB,GAAkC;IAClE,gBAAgB,EAAE,UAAU;IAC5B,aAAa,EAAE,aAAa;IAC5B,gBAAgB,EAAE,gBAAgB;IAClC,gBAAgB,EAAE,gBAAgB;IAClC,iBAAiB,EAAE,iBAAiB;CACrC,CAAA;AAwCD,SAAS,YAAY,CAAC,UAAqC,EAAE,SAAiB;IAC5E,MAAM,KAAK,GAAG,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAA;IAClE,6EAA6E;IAC7E,iEAAiE;IACjE,OAAO,cAAc,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,CAAC,CAAA;AACtD,CAAC;AAED,SAAS,iBAAiB,CAAC,UAAqC,EAAE,YAAoB;IACpF,OAAO,CACL,cAAc,CAAC,UAAU,EAAE,IAAI,EAAE,aAAa,CAAC,IAAI,cAAc,CAAC,UAAU,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,CAAC,CACzG,CAAA;AACH,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,eAAe,CAC7B,UAAqC,EACrC,QAAiC,EACjC,QAAgB;IAEhB,IAAI,CAAC,UAAU,IAAI,CAAC,QAAQ;QAAE,OAAO,QAAQ,CAAA;IAC7C,+EAA+E;IAC/E,oEAAoE;IACpE,IAAI,OAAO,UAAU,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;QAChD,OAAO,oBAAoB,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;IACpE,CAAC;IACD,MAAM,KAAK,GAAG,OAAO,UAAU,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAA;IACzF,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IACrC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,MAAM,CAAA;IAClE,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;IAC5D,MAAM,SAAS,GAAG,QAAQ,CAAC,aAAa,CAAA;IACxC,KAAK,MAAM,MAAM,IAAI,CAAC,SAAS,EAAE,GAAG,OAAO,CAAC,EAAE,CAAC;QAC7C,IAAI,CAAC,MAAM;YAAE,SAAQ;QACrB,MAAM,KAAK,GAAG,QAAQ,CAAC,SAAS,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAA;QACnD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,KAAK,CAAA;IACjE,CAAC;IACD,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,gBAAgB,CAAC,WAAmB;IAClD,OAAO,WAAW,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAA;AACjE,CAAC;AASD,SAAS,aAAa,CAAC,OAA8B,EAAE,GAAW;IAChE,MAAM,GAAG,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAA;IACjC,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAA;IACrB,MAAM,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,UAAU,CAAC,CAAA;IAC3E,OAAO;QACL,UAAU;QACV,YAAY,EAAE,GAAG,CAAC,UAAU;QAC5B,UAAU,EAAE,GAAG,CAAC,EAAE;QAClB,KAAK,EAAE,eAAe,CAAC,UAAU,EAAE,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;KACvE,CAAA;AACH,CAAC;AAED,SAAS,sBAAsB,CAC7B,OAA8B,EAC9B,KAA2B;IAE3B,MAAM,KAAK,GAAG,aAAa,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;IACrD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,EAAE,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,aAAa,EAAE,KAAK,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,CAAA;IACtF,CAAC;IACD,MAAM,MAAM,GAAG,sBAAsB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IACjD,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,UAAU,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAA;IACrF,MAAM,UAAU,GAAa,EAAE,CAAA;IAC/B,IAAI,MAAM,EAAE,MAAM;QAAE,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IAClD,IAAI,KAAK,CAAC,IAAI,KAAK,gBAAgB;QAAE,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IAC5D,IAAI,KAAK,CAAC,IAAI,KAAK,gBAAgB;QAAE,UAAU,CAAC,IAAI,CAAC,eAAe,CAAC,CAAA;IACrE,IACE,KAAK,CAAC,IAAI,KAAK,gBAAgB;QAC/B,MAAM;QACN,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC;QACxB,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,EAC7B,CAAC;QACD,UAAU,CAAC,IAAI,CAAC,QAAQ,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IAC3C,CAAC;IACD,OAAO;QACL,WAAW,EAAE,GAAG,iBAAiB,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC,KAAK,EAAE;QAC1F,aAAa,EAAE,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,UAAU,KAAK,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU;QAC9F,UAAU,EAAE;YACV,IAAI,EAAE,WAAW;YACjB,YAAY,EAAE,KAAK,CAAC,YAAY;YAChC,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,SAAS,EAAE,KAAK,CAAC,IAAI;SACtB;KACF,CAAA;AACH,CAAC;AAGD,SAAS,cAAc,CACrB,OAA8B,EAC9B,MAAuB;IAEvB,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,UAAU,CAAC,CAAC,CAAC;YAChB,MAAM,KAAK,GAAG,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC,CAAA;YAC/C,IAAI,CAAC,KAAK;gBAAE,OAAO,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,CAAA;YAC/D,OAAO;gBACL,WAAW,EAAE,GAAG,iBAAiB,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC,KAAK,EAAE;gBAC1F,UAAU,EAAE;oBACV,IAAI,EAAE,WAAW;oBACjB,YAAY,EAAE,KAAK,CAAC,YAAY;oBAChC,UAAU,EAAE,KAAK,CAAC,UAAU;iBAC7B;aACF,CAAA;QACH,CAAC;QACD,KAAK,WAAW;YACd,OAAO;gBACL,WAAW,EAAE,OAAO,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,gBAAgB,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC7E,UAAU,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,EAAE;aAC1D,CAAA;QACH,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;YAC3C,OAAO;gBACL,WAAW,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE;gBAChE,UAAU,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,EAAE;aAClD,CAAA;QACH,CAAC;IACH,CAAC;AACH,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,uBAAuB,CACrC,KAA2B,EAC3B,OAA8B;IAE9B,8EAA8E;IAC9E,MAAM,WAAW,GAAG,sBAAsB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;IAC1D,OAAO;QACL,GAAG,KAAK;QACR,SAAS,EAAE,qBAAqB,CAAC,KAAK,CAAC,IAAI,CAAC;QAC5C,GAAG,WAAW;QACd,GAAG,cAAc,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC;KACzC,CAAA;AACH,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,wBAAwB,CACtC,OAAwC,EACxC,OAA8B;IAE9B,OAAO,sBAAsB,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,uBAAuB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAA;AAChG,CAAC","sourcesContent":["import { resolveDocumentTitle } from '@elytracms/core/cms-core'\nimport { localizedLabel } from '@elytracms/studio/lib/i18n'\nimport type { CmsDocument, CollectionDef } from '@elytracms/core/cms-core'\nimport type {\n ReferenceEntryRecord,\n ReferenceKind,\n ReferenceSource,\n ReferenceTarget,\n} from '@elytracms/core/persistence'\n\n/**\n * Pure presentation model for the EC-172 references panels.\n *\n * The EC-155 index stores entries as `(source, path, target, kind)` per\n * variant; this module turns those records into renderable rows: variants\n * merged onto one row (draft/published badges), human-readable source/\n * location/target labels, and deep-link descriptors that pinpoint the exact\n * field (CMS) or graph node (builder) a path identifies. No data fetching, no\n * React — everything here is a deterministic function of its inputs, so it is\n * fully testable in the node environment.\n */\n\n// --- Path parsing -------------------------------------------------------------\n\n/** Split a `collection:id` document key (the index's document source/target id). */\nexport function parseDocumentKey(key: string): { collection: string; id: string } | null {\n const separator = key.indexOf(':')\n if (separator <= 0 || separator === key.length - 1) return null\n return { collection: key.slice(0, separator), id: key.slice(separator + 1) }\n}\n\nexport interface DocumentFieldPath {\n /** Present for `localized/<locale>/<field>` paths. */\n locale?: string\n field: string\n /** Tokens below the field value (array indices, rich-text node paths). */\n rest: readonly string[]\n}\n\n/**\n * Parse a document index path (`values/<field>/…` or\n * `localized/<locale>/<field>/…`) onto the owning field.\n */\nexport function parseDocumentFieldPath(path: string): DocumentFieldPath | null {\n const tokens = path.split('/')\n if (tokens[0] === 'values' && tokens[1]) {\n return { field: tokens[1], rest: tokens.slice(2) }\n }\n if (tokens[0] === 'localized' && tokens[1] && tokens[2]) {\n return { locale: tokens[1], field: tokens[2], rest: tokens.slice(3) }\n }\n return null\n}\n\n// --- Variant merging ----------------------------------------------------------\n\n/** One panel row: the same logical entry across both variants, with flags. */\nexport interface MergedReferenceEntry {\n /** Stable row key (source + path + target + kind). */\n key: string\n source: ReferenceSource\n path: string\n target: ReferenceTarget\n kind: ReferenceKind\n /** Present in the draft variant of the index. */\n draft: boolean\n /** Present in the published variant (a live reference). */\n published: boolean\n}\n\n/**\n * Merge per-variant index records onto one row per logical entry. A\n * draft-only row (`draft && !published`) is the Sanity hidden-draft-reference\n * case the panel must make visible. Order is deterministic:\n * source type → source id → path → target type → target id → kind.\n */\nexport function mergeReferenceVariants(\n records: readonly ReferenceEntryRecord[],\n): MergedReferenceEntry[] {\n const byKey = new Map<string, MergedReferenceEntry>()\n for (const record of records) {\n const key = `${record.source.type} ${record.source.id} ${record.path} ${record.target.type} ${record.target.id} ${record.kind}`\n const existing = byKey.get(key)\n if (existing) {\n if (record.variant === 'draft') existing.draft = true\n else existing.published = true\n continue\n }\n byKey.set(key, {\n key,\n source: record.source,\n path: record.path,\n target: record.target,\n kind: record.kind,\n draft: record.variant === 'draft',\n published: record.variant === 'published',\n })\n }\n return [...byKey.values()].sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0))\n}\n\n// --- Labels & deep links --------------------------------------------------------\n\n/** Human label per reference kind (entry-row badge). */\nexport const REFERENCE_KIND_LABELS: Record<ReferenceKind, string> = {\n 'relation-field': 'relation',\n 'asset-field': 'asset field',\n 'rich-text-link': 'rich-text link',\n 'rich-text-node': 'rich-text node',\n 'component-usage': 'component usage',\n}\n\n/**\n * A deep-link descriptor the panel renders as a router link. `cms-field`\n * carries the full index path so the document editor can highlight the exact\n * field control; `builder-node` carries the resolved node id so the builder\n * can select + scroll to the node.\n */\nexport type ReferenceDeepLink =\n | { kind: 'cms-field'; collectionId: string; documentId: string; fieldPath?: string }\n | { kind: 'builder-node'; pageId?: string; nodeId?: string }\n | { kind: 'component'; componentId: string }\n | { kind: 'asset'; assetId: string }\n\n/** Everything the model needs to label entries (loaded once per panel). */\nexport interface ReferenceLabelContext {\n collections: readonly CollectionDef[]\n /** Live documents keyed by `collection:id`. */\n documents: ReadonlyMap<string, CmsDocument>\n /** Asset metadata keyed by asset id. */\n assets: ReadonlyMap<string, { filename: string; title?: string | undefined }>\n /** Resolves a component id to its manifest title (live registry lookup). */\n componentTitle: (componentId: string) => string | undefined\n}\n\n/** One fully-labelled panel row. */\nexport interface ReferenceEntryView extends MergedReferenceEntry {\n kindLabel: string\n /** The holding entity, e.g. `Post · Hello World` or `Page · Home`. */\n sourceLabel: string\n /** The exact location inside the source, e.g. `Author` or `Text (n-post-title)`. */\n locationLabel: string\n /** The referenced entity, e.g. `Ada Lovelace` or `cover.jpg`. */\n targetLabel: string\n /** Deep link to the exact source location (field control / graph node). */\n sourceLink: ReferenceDeepLink | null\n /** Link to the referenced entity's surface. */\n targetLink: ReferenceDeepLink | null\n}\n\nfunction fieldLabelOf(collection: CollectionDef | undefined, fieldName: string): string {\n const field = collection?.fields.find((f) => f.name === fieldName)\n // Non-hook `localizedLabel` reads the active UI locale (safe anywhere), so a\n // `{ en, de }` label resolves to the editor's language here too.\n return localizedLabel(field?.form?.label, fieldName)\n}\n\nfunction collectionLabelOf(collection: CollectionDef | undefined, collectionId: string): string {\n return (\n localizedLabel(collection?.form?.labelSingular) || localizedLabel(collection?.form?.label, collectionId)\n )\n}\n\n/** Resolve a document's display title (titleField value, default locale first). */\nexport function documentTitleOf(\n collection: CollectionDef | undefined,\n document: CmsDocument | undefined,\n fallback: string,\n): string {\n if (!collection || !document) return fallback\n // EC-242: a function `titleField` (title pattern) has no single source field —\n // resolve it from the base values via the shared identity resolver.\n if (typeof collection.titleField === 'function') {\n return resolveDocumentTitle(collection, document.values, fallback)\n }\n const field = typeof collection.titleField === 'string' ? collection.titleField : 'title'\n const direct = document.values[field]\n if (typeof direct === 'string' && direct.length > 0) return direct\n const locales = Object.keys(document.localized ?? {}).sort()\n const preferred = document.defaultLocale\n for (const locale of [preferred, ...locales]) {\n if (!locale) continue\n const value = document.localized?.[locale]?.[field]\n if (typeof value === 'string' && value.length > 0) return value\n }\n return fallback\n}\n\n/** Strip the namespace off a component id for compact labels. */\nexport function shortComponentId(componentId: string): string {\n return componentId.replace('base.', '').replace('project.', '')\n}\n\ninterface DocumentParts {\n collection: CollectionDef | undefined\n collectionId: string\n documentId: string\n title: string\n}\n\nfunction documentParts(context: ReferenceLabelContext, key: string): DocumentParts | null {\n const ref = parseDocumentKey(key)\n if (!ref) return null\n const collection = context.collections.find((c) => c.id === ref.collection)\n return {\n collection,\n collectionId: ref.collection,\n documentId: ref.id,\n title: documentTitleOf(collection, context.documents.get(key), ref.id),\n }\n}\n\nfunction describeDocumentSource(\n context: ReferenceLabelContext,\n entry: MergedReferenceEntry,\n): { sourceLabel: string; locationLabel: string; sourceLink: ReferenceDeepLink | null } {\n const parts = documentParts(context, entry.source.id)\n if (!parts) {\n return { sourceLabel: entry.source.id, locationLabel: entry.path, sourceLink: null }\n }\n const parsed = parseDocumentFieldPath(entry.path)\n const fieldLabel = parsed ? fieldLabelOf(parts.collection, parsed.field) : entry.path\n const qualifiers: string[] = []\n if (parsed?.locale) qualifiers.push(parsed.locale)\n if (entry.kind === 'rich-text-link') qualifiers.push('link')\n if (entry.kind === 'rich-text-node') qualifiers.push('embedded node')\n if (\n entry.kind === 'relation-field' &&\n parsed &&\n parsed.rest.length === 1 &&\n /^\\d+$/.test(parsed.rest[0]!)\n ) {\n qualifiers.push(`item ${parsed.rest[0]}`)\n }\n return {\n sourceLabel: `${collectionLabelOf(parts.collection, parts.collectionId)} · ${parts.title}`,\n locationLabel: qualifiers.length > 0 ? `${fieldLabel} (${qualifiers.join(', ')})` : fieldLabel,\n sourceLink: {\n kind: 'cms-field',\n collectionId: parts.collectionId,\n documentId: parts.documentId,\n fieldPath: entry.path,\n },\n }\n}\n\n\nfunction describeTarget(\n context: ReferenceLabelContext,\n target: ReferenceTarget,\n): { targetLabel: string; targetLink: ReferenceDeepLink | null } {\n switch (target.type) {\n case 'document': {\n const parts = documentParts(context, target.id)\n if (!parts) return { targetLabel: target.id, targetLink: null }\n return {\n targetLabel: `${collectionLabelOf(parts.collection, parts.collectionId)} · ${parts.title}`,\n targetLink: {\n kind: 'cms-field',\n collectionId: parts.collectionId,\n documentId: parts.documentId,\n },\n }\n }\n case 'component':\n return {\n targetLabel: context.componentTitle(target.id) ?? shortComponentId(target.id),\n targetLink: { kind: 'component', componentId: target.id },\n }\n case 'asset': {\n const asset = context.assets.get(target.id)\n return {\n targetLabel: asset ? (asset.title ?? asset.filename) : target.id,\n targetLink: { kind: 'asset', assetId: target.id },\n }\n }\n }\n}\n\n/** Label + link one merged entry (see {@link buildReferenceEntryViews}). */\nexport function buildReferenceEntryView(\n entry: MergedReferenceEntry,\n context: ReferenceLabelContext,\n): ReferenceEntryView {\n // EC-287: a reference source is only ever a CMS document (structure is code).\n const sourceParts = describeDocumentSource(context, entry)\n return {\n ...entry,\n kindLabel: REFERENCE_KIND_LABELS[entry.kind],\n ...sourceParts,\n ...describeTarget(context, entry.target),\n }\n}\n\n/** Merge variants and label every record — the panel's full render model. */\nexport function buildReferenceEntryViews(\n records: readonly ReferenceEntryRecord[],\n context: ReferenceLabelContext,\n): ReferenceEntryView[] {\n return mergeReferenceVariants(records).map((entry) => buildReferenceEntryView(entry, context))\n}\n"]}
@@ -5,7 +5,7 @@ import { staticProp } from '@elytracms/core/project-graph';
5
5
  * and reproducible across runs — no module-level counter, no time/random.
6
6
  */
7
7
  function text(value, id) {
8
- return { id, componentId: 'base.primitives.Text', props: { value: staticProp(value) } };
8
+ return { id, componentId: 'base.Text', props: { value: staticProp(value) } };
9
9
  }
10
10
  /** Build a node from a manifest's defaults, overlaying any provided prop values. */
11
11
  export function nodeFromProps(manifest, overrides, id) {
@@ -29,22 +29,22 @@ function nodeFromPropsRaw(componentId, props, id) {
29
29
  * Each builder receives a deterministic `base` id prefix.
30
30
  */
31
31
  const PRIMITIVE_PREVIEWS = {
32
- 'base.primitives.Container': (base) => [
32
+ 'base.Container': (base) => [
33
33
  {
34
34
  name: 'With content',
35
35
  node: {
36
36
  id: base,
37
- componentId: 'base.primitives.Container',
37
+ componentId: 'base.Container',
38
38
  slots: { children: [text('Container content', `${base}-c0`)] },
39
39
  },
40
40
  },
41
41
  ],
42
- 'base.primitives.Stack': (base) => [
42
+ 'base.Stack': (base) => [
43
43
  {
44
44
  name: 'Gap 4',
45
45
  node: {
46
46
  id: base,
47
- componentId: 'base.primitives.Stack',
47
+ componentId: 'base.Stack',
48
48
  props: { gap: staticProp(4) },
49
49
  slots: {
50
50
  children: [
@@ -56,12 +56,12 @@ const PRIMITIVE_PREVIEWS = {
56
56
  },
57
57
  },
58
58
  ],
59
- 'base.primitives.Grid': (base) => [
59
+ 'base.Grid': (base) => [
60
60
  {
61
61
  name: '2 columns',
62
62
  node: {
63
63
  id: base,
64
- componentId: 'base.primitives.Grid',
64
+ componentId: 'base.Grid',
65
65
  props: { columns: staticProp(2), gap: staticProp(3) },
66
66
  slots: {
67
67
  children: [
@@ -74,20 +74,20 @@ const PRIMITIVE_PREVIEWS = {
74
74
  },
75
75
  },
76
76
  ],
77
- 'base.primitives.Text': (base) => [
78
- { name: 'Sample', node: nodeFromPropsRaw('base.primitives.Text', { value: 'The quick brown fox.' }, base) },
77
+ 'base.Text': (base) => [
78
+ { name: 'Sample', node: nodeFromPropsRaw('base.Text', { value: 'The quick brown fox.' }, base) },
79
79
  ],
80
- 'base.primitives.Heading': (base) => [
81
- { name: 'Sample', node: nodeFromPropsRaw('base.primitives.Heading', { value: 'A section heading' }, base) },
80
+ 'base.Heading': (base) => [
81
+ { name: 'Sample', node: nodeFromPropsRaw('base.Heading', { value: 'A section heading' }, base) },
82
82
  ],
83
- 'base.primitives.Image': (base) => [
83
+ 'base.Image': (base) => [
84
84
  {
85
85
  name: 'Placeholder',
86
- node: nodeFromPropsRaw('base.primitives.Image', { src: 'https://placehold.co/320x180', alt: 'Placeholder image' }, base),
86
+ node: nodeFromPropsRaw('base.Image', { src: 'https://placehold.co/320x180', alt: 'Placeholder image' }, base),
87
87
  },
88
88
  ],
89
- 'base.primitives.Button': (base) => [
90
- { name: 'Sample', node: nodeFromPropsRaw('base.primitives.Button', { label: 'Click me' }, base) },
89
+ 'base.Button': (base) => [
90
+ { name: 'Sample', node: nodeFromPropsRaw('base.Button', { label: 'Click me' }, base) },
91
91
  ],
92
92
  };
93
93
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"preview.js","sourceRoot":"","sources":["../../../src/lib/registry/preview.ts"],"names":[],"mappings":"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","sourcesContent":["import { staticProp } from '@elytracms/core/project-graph'\nimport type { ComponentNode, JsonValue } from '@elytracms/core/project-graph'\nimport type { ComponentManifest } from '@elytracms/core/component-registry'\n\n/** A named, renderable preview state for a component. */\nexport interface PreviewSpec {\n name: string\n node: ComponentNode\n}\n\n/**\n * Node ids are derived deterministically from a stable base (manifest id +\n * fixture index) so previews are byte-identical across SSR and client hydration\n * and reproducible across runs — no module-level counter, no time/random.\n */\nfunction text(value: string, id: string): ComponentNode {\n return { id, componentId: 'base.primitives.Text', props: { value: staticProp(value) } }\n}\n\n/** Build a node from a manifest's defaults, overlaying any provided prop values. */\nexport function nodeFromProps(\n manifest: ComponentManifest,\n overrides: Record<string, JsonValue>,\n id: string,\n): ComponentNode {\n const props: ComponentNode['props'] = {}\n for (const [name, spec] of Object.entries(manifest.props)) {\n const value = name in overrides ? overrides[name] : (spec.default as JsonValue | undefined)\n if (value !== undefined) props[name] = staticProp(value)\n }\n return { id, componentId: manifest.id, props }\n}\n\nfunction nodeFromPropsRaw(componentId: string, props: Record<string, JsonValue>, id: string): ComponentNode {\n const out: ComponentNode['props'] = {}\n for (const [k, v] of Object.entries(props)) out[k] = staticProp(v)\n return { id, componentId, props: out }\n}\n\n/**\n * Authored preview nodes for the base primitives (which ship no fixtures of their\n * own). Slot-bearing primitives get sample children so previews aren't blank.\n * Each builder receives a deterministic `base` id prefix.\n */\nconst PRIMITIVE_PREVIEWS: Record<string, (base: string) => PreviewSpec[]> = {\n 'base.primitives.Container': (base) => [\n {\n name: 'With content',\n node: {\n id: base,\n componentId: 'base.primitives.Container',\n slots: { children: [text('Container content', `${base}-c0`)] },\n },\n },\n ],\n 'base.primitives.Stack': (base) => [\n {\n name: 'Gap 4',\n node: {\n id: base,\n componentId: 'base.primitives.Stack',\n props: { gap: staticProp(4) },\n slots: {\n children: [\n text('Item one', `${base}-c0`),\n text('Item two', `${base}-c1`),\n text('Item three', `${base}-c2`),\n ],\n },\n },\n },\n ],\n 'base.primitives.Grid': (base) => [\n {\n name: '2 columns',\n node: {\n id: base,\n componentId: 'base.primitives.Grid',\n props: { columns: staticProp(2), gap: staticProp(3) },\n slots: {\n children: [\n text('Cell A', `${base}-c0`),\n text('Cell B', `${base}-c1`),\n text('Cell C', `${base}-c2`),\n text('Cell D', `${base}-c3`),\n ],\n },\n },\n },\n ],\n 'base.primitives.Text': (base) => [\n { name: 'Sample', node: nodeFromPropsRaw('base.primitives.Text', { value: 'The quick brown fox.' }, base) },\n ],\n 'base.primitives.Heading': (base) => [\n { name: 'Sample', node: nodeFromPropsRaw('base.primitives.Heading', { value: 'A section heading' }, base) },\n ],\n 'base.primitives.Image': (base) => [\n {\n name: 'Placeholder',\n node: nodeFromPropsRaw(\n 'base.primitives.Image',\n { src: 'https://placehold.co/320x180', alt: 'Placeholder image' },\n base,\n ),\n },\n ],\n 'base.primitives.Button': (base) => [\n { name: 'Sample', node: nodeFromPropsRaw('base.primitives.Button', { label: 'Click me' }, base) },\n ],\n}\n\n/**\n * Resolve the preview states for a component (brief §7.2, §8.3, EC-054):\n * declared manifest fixtures first; otherwise authored primitive previews;\n * otherwise a single default-props preview. Deterministic ids throughout.\n */\nexport function previewSpecs(manifest: ComponentManifest): PreviewSpec[] {\n if (manifest.fixtures && manifest.fixtures.length > 0) {\n return manifest.fixtures.map((fixture, i) => ({\n name: fixture.name,\n node: nodeFromProps(manifest, fixture.props ?? {}, `preview-${manifest.id}-${i}`),\n }))\n }\n const authored = PRIMITIVE_PREVIEWS[manifest.id]\n if (authored) return authored(`preview-${manifest.id}-0`)\n return [{ name: 'Default', node: nodeFromProps(manifest, {}, `preview-${manifest.id}-0`) }]\n}\n"]}
1
+ {"version":3,"file":"preview.js","sourceRoot":"","sources":["../../../src/lib/registry/preview.ts"],"names":[],"mappings":"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","sourcesContent":["import { staticProp } from '@elytracms/core/project-graph'\nimport type { ComponentNode, JsonValue } from '@elytracms/core/project-graph'\nimport type { ComponentManifest } from '@elytracms/core/component-registry'\n\n/** A named, renderable preview state for a component. */\nexport interface PreviewSpec {\n name: string\n node: ComponentNode\n}\n\n/**\n * Node ids are derived deterministically from a stable base (manifest id +\n * fixture index) so previews are byte-identical across SSR and client hydration\n * and reproducible across runs — no module-level counter, no time/random.\n */\nfunction text(value: string, id: string): ComponentNode {\n return { id, componentId: 'base.Text', props: { value: staticProp(value) } }\n}\n\n/** Build a node from a manifest's defaults, overlaying any provided prop values. */\nexport function nodeFromProps(\n manifest: ComponentManifest,\n overrides: Record<string, JsonValue>,\n id: string,\n): ComponentNode {\n const props: ComponentNode['props'] = {}\n for (const [name, spec] of Object.entries(manifest.props)) {\n const value = name in overrides ? overrides[name] : (spec.default as JsonValue | undefined)\n if (value !== undefined) props[name] = staticProp(value)\n }\n return { id, componentId: manifest.id, props }\n}\n\nfunction nodeFromPropsRaw(componentId: string, props: Record<string, JsonValue>, id: string): ComponentNode {\n const out: ComponentNode['props'] = {}\n for (const [k, v] of Object.entries(props)) out[k] = staticProp(v)\n return { id, componentId, props: out }\n}\n\n/**\n * Authored preview nodes for the base primitives (which ship no fixtures of their\n * own). Slot-bearing primitives get sample children so previews aren't blank.\n * Each builder receives a deterministic `base` id prefix.\n */\nconst PRIMITIVE_PREVIEWS: Record<string, (base: string) => PreviewSpec[]> = {\n 'base.Container': (base) => [\n {\n name: 'With content',\n node: {\n id: base,\n componentId: 'base.Container',\n slots: { children: [text('Container content', `${base}-c0`)] },\n },\n },\n ],\n 'base.Stack': (base) => [\n {\n name: 'Gap 4',\n node: {\n id: base,\n componentId: 'base.Stack',\n props: { gap: staticProp(4) },\n slots: {\n children: [\n text('Item one', `${base}-c0`),\n text('Item two', `${base}-c1`),\n text('Item three', `${base}-c2`),\n ],\n },\n },\n },\n ],\n 'base.Grid': (base) => [\n {\n name: '2 columns',\n node: {\n id: base,\n componentId: 'base.Grid',\n props: { columns: staticProp(2), gap: staticProp(3) },\n slots: {\n children: [\n text('Cell A', `${base}-c0`),\n text('Cell B', `${base}-c1`),\n text('Cell C', `${base}-c2`),\n text('Cell D', `${base}-c3`),\n ],\n },\n },\n },\n ],\n 'base.Text': (base) => [\n { name: 'Sample', node: nodeFromPropsRaw('base.Text', { value: 'The quick brown fox.' }, base) },\n ],\n 'base.Heading': (base) => [\n { name: 'Sample', node: nodeFromPropsRaw('base.Heading', { value: 'A section heading' }, base) },\n ],\n 'base.Image': (base) => [\n {\n name: 'Placeholder',\n node: nodeFromPropsRaw(\n 'base.Image',\n { src: 'https://placehold.co/320x180', alt: 'Placeholder image' },\n base,\n ),\n },\n ],\n 'base.Button': (base) => [\n { name: 'Sample', node: nodeFromPropsRaw('base.Button', { label: 'Click me' }, base) },\n ],\n}\n\n/**\n * Resolve the preview states for a component (brief §7.2, §8.3, EC-054):\n * declared manifest fixtures first; otherwise authored primitive previews;\n * otherwise a single default-props preview. Deterministic ids throughout.\n */\nexport function previewSpecs(manifest: ComponentManifest): PreviewSpec[] {\n if (manifest.fixtures && manifest.fixtures.length > 0) {\n return manifest.fixtures.map((fixture, i) => ({\n name: fixture.name,\n node: nodeFromProps(manifest, fixture.props ?? {}, `preview-${manifest.id}-${i}`),\n }))\n }\n const authored = PRIMITIVE_PREVIEWS[manifest.id]\n if (authored) return authored(`preview-${manifest.id}-0`)\n return [{ name: 'Default', node: nodeFromProps(manifest, {}, `preview-${manifest.id}-0`) }]\n}\n"]}
@@ -15,24 +15,21 @@ const MarketingHeroManifest = defineComponent({
15
15
  description: 'A headline hero section with eyebrow, subheading, and a call to action.',
16
16
  category: 'marketing',
17
17
  props: {
18
- eyebrow: { type: 'text', context: 'prop', default: 'New', form: { label: 'Eyebrow' } },
18
+ eyebrow: { type: 'text', default: 'New', form: { label: 'Eyebrow' } },
19
19
  heading: {
20
20
  type: 'text',
21
- context: 'prop',
22
21
  default: 'Build code-first websites',
23
22
  validation: { min: 1 },
24
23
  form: { label: 'Heading' },
25
24
  },
26
25
  subheading: {
27
26
  type: 'text',
28
- context: 'prop',
29
27
  default: 'One canonical graph powers editing, content, and export.',
30
28
  form: { label: 'Subheading', control: 'textarea' },
31
29
  },
32
- ctaLabel: { type: 'text', context: 'prop', default: 'Get started', form: { label: 'CTA label' } },
30
+ ctaLabel: { type: 'text', default: 'Get started', form: { label: 'CTA label' } },
33
31
  align: {
34
32
  type: 'select',
35
- context: 'prop',
36
33
  default: 'left',
37
34
  options: [
38
35
  { value: 'left', label: 'Left' },
@@ -66,20 +63,17 @@ const FeatureCardManifest = defineComponent({
66
63
  props: {
67
64
  title: {
68
65
  type: 'text',
69
- context: 'prop',
70
66
  default: 'Validation first',
71
67
  validation: { min: 1 },
72
68
  form: { label: 'Title' },
73
69
  },
74
70
  body: {
75
71
  type: 'text',
76
- context: 'prop',
77
72
  default: 'Broken bindings and missing components are explicit, never silent.',
78
73
  form: { label: 'Body', control: 'textarea' },
79
74
  },
80
75
  tone: {
81
76
  type: 'select',
82
- context: 'prop',
83
77
  default: 'default',
84
78
  options: [
85
79
  { value: 'default', label: 'Default' },
@@ -106,14 +100,13 @@ const PricingCalloutManifest = defineComponent({
106
100
  props: {
107
101
  plan: {
108
102
  type: 'text',
109
- context: 'prop',
110
103
  default: 'Pro',
111
104
  validation: { min: 1 },
112
105
  form: { label: 'Plan' },
113
106
  },
114
- price: { type: 'text', context: 'prop', default: '$29', form: { label: 'Price' } },
115
- period: { type: 'text', context: 'prop', default: 'mo', form: { label: 'Period' } },
116
- featured: { type: 'boolean', context: 'prop', default: false, form: { label: 'Featured' } },
107
+ price: { type: 'text', default: '$29', form: { label: 'Price' } },
108
+ period: { type: 'text', default: 'mo', form: { label: 'Period' } },
109
+ featured: { type: 'boolean', default: false, form: { label: 'Featured' } },
117
110
  },
118
111
  slots: [],
119
112
  designTokens: ['color.primary', 'space.card'],
@@ -131,7 +124,7 @@ const LegacyBannerManifest = defineComponent({
131
124
  description: 'A deprecated banner whose implementation has not been wired up.',
132
125
  category: 'marketing',
133
126
  props: {
134
- message: { type: 'text', context: 'prop', default: 'Deprecated', form: { label: 'Message' } },
127
+ message: { type: 'text', default: 'Deprecated', form: { label: 'Message' } },
135
128
  },
136
129
  slots: [],
137
130
  fixtures: [{ name: 'Default' }],