@elytracms/studio 0.0.14 → 0.0.16

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 (143) hide show
  1. package/convex/cms.ts +8 -31
  2. package/convex/content.ts +3 -44
  3. package/convex/delivery.ts +8 -42
  4. package/convex/functions.ts +17 -2
  5. package/convex/members.ts +2 -2
  6. package/convex/referenceMaintenance.ts +4 -73
  7. package/convex/references.ts +4 -2
  8. package/convex/schema.ts +17 -37
  9. package/convex/sync.ts +10 -42
  10. package/convex/triggers.ts +57 -0
  11. package/convex/webhooks.ts +3 -2
  12. package/dist/components/app-views/default-modules.d.ts +41 -0
  13. package/dist/components/app-views/default-modules.js +201 -0
  14. package/dist/components/app-views/default-modules.js.map +1 -0
  15. package/dist/components/app-views/module.d.ts +93 -0
  16. package/dist/components/app-views/module.js +75 -0
  17. package/dist/components/app-views/module.js.map +1 -0
  18. package/dist/components/app-views/sidebar.d.ts +95 -0
  19. package/dist/components/app-views/sidebar.js +153 -0
  20. package/dist/components/app-views/sidebar.js.map +1 -0
  21. package/dist/components/builder/builder-store.d.ts +92 -40
  22. package/dist/components/builder/builder-store.js +30 -126
  23. package/dist/components/builder/builder-store.js.map +1 -1
  24. package/dist/components/builder/canvas-breakpoints.d.ts +53 -0
  25. package/dist/components/builder/canvas-breakpoints.js +55 -0
  26. package/dist/components/builder/canvas-breakpoints.js.map +1 -0
  27. package/dist/components/builder/canvas.js +35 -61
  28. package/dist/components/builder/canvas.js.map +1 -1
  29. package/dist/components/builder/clipboard-actions.js +3 -3
  30. package/dist/components/builder/clipboard-actions.js.map +1 -1
  31. package/dist/components/builder/composition-field-editor.js +31 -40
  32. package/dist/components/builder/composition-field-editor.js.map +1 -1
  33. package/dist/components/builder/insert-context.js +1 -1
  34. package/dist/components/builder/insert-context.js.map +1 -1
  35. package/dist/components/builder/page-tree.js +3 -4
  36. package/dist/components/builder/page-tree.js.map +1 -1
  37. package/dist/components/builder/property-inspector.js +4 -64
  38. package/dist/components/builder/property-inspector.js.map +1 -1
  39. package/dist/components/cms/bulk-actions.d.ts +23 -5
  40. package/dist/components/cms/bulk-actions.js +7 -3
  41. package/dist/components/cms/bulk-actions.js.map +1 -1
  42. package/dist/components/cms/bulk-bar.d.ts +22 -6
  43. package/dist/components/cms/bulk-bar.js +46 -9
  44. package/dist/components/cms/bulk-bar.js.map +1 -1
  45. package/dist/components/cms/cell.d.ts +0 -4
  46. package/dist/components/cms/cell.js +1 -5
  47. package/dist/components/cms/cell.js.map +1 -1
  48. package/dist/components/cms/cms-index.d.ts +4 -2
  49. package/dist/components/cms/cms-index.js +6 -4
  50. package/dist/components/cms/cms-index.js.map +1 -1
  51. package/dist/components/cms/cms-store.d.ts +7 -6
  52. package/dist/components/cms/cms-store.js +55 -18
  53. package/dist/components/cms/cms-store.js.map +1 -1
  54. package/dist/components/cms/collection-table.js +8 -4
  55. package/dist/components/cms/collection-table.js.map +1 -1
  56. package/dist/components/cms/collections-sidebar.d.ts +10 -1
  57. package/dist/components/cms/collections-sidebar.js +13 -2
  58. package/dist/components/cms/collections-sidebar.js.map +1 -1
  59. package/dist/components/cms/detail-editor.js +2 -2
  60. package/dist/components/cms/detail-editor.js.map +1 -1
  61. package/dist/components/cms/doc-state.d.ts +0 -5
  62. package/dist/components/cms/doc-state.js +1 -7
  63. package/dist/components/cms/doc-state.js.map +1 -1
  64. package/dist/components/cms/editable-cell.js +1 -1
  65. package/dist/components/cms/editable-cell.js.map +1 -1
  66. package/dist/components/cms/field-control.d.ts +11 -2
  67. package/dist/components/cms/field-control.js +22 -1
  68. package/dist/components/cms/field-control.js.map +1 -1
  69. package/dist/components/cms/field-renderer.d.ts +71 -0
  70. package/dist/components/cms/field-renderer.js +67 -0
  71. package/dist/components/cms/field-renderer.js.map +1 -0
  72. package/dist/components/cms/filter-builder.js +6 -28
  73. package/dist/components/cms/filter-builder.js.map +1 -1
  74. package/dist/components/cms/scalar-value-editor.d.ts +35 -0
  75. package/dist/components/cms/scalar-value-editor.js +43 -0
  76. package/dist/components/cms/scalar-value-editor.js.map +1 -0
  77. package/dist/components/collab/collab-context.d.ts +6 -14
  78. package/dist/components/collab/collab-context.js +3 -12
  79. package/dist/components/collab/collab-context.js.map +1 -1
  80. package/dist/components/collab/collab-seed.d.ts +7 -19
  81. package/dist/components/collab/collab-seed.js +9 -31
  82. package/dist/components/collab/collab-seed.js.map +1 -1
  83. package/dist/components/components/registry-browser.js +6 -12
  84. package/dist/components/components/registry-browser.js.map +1 -1
  85. package/dist/components/guards.d.ts +0 -9
  86. package/dist/components/guards.js +0 -12
  87. package/dist/components/guards.js.map +1 -1
  88. package/dist/components/project-session.js +8 -3
  89. package/dist/components/project-session.js.map +1 -1
  90. package/dist/components/section-nav.d.ts +10 -10
  91. package/dist/components/section-nav.js +48 -25
  92. package/dist/components/section-nav.js.map +1 -1
  93. package/dist/components/section-state.d.ts +0 -2
  94. package/dist/components/section-state.js +0 -9
  95. package/dist/components/section-state.js.map +1 -1
  96. package/dist/components/section-surface.d.ts +3 -7
  97. package/dist/components/section-surface.js +6 -14
  98. package/dist/components/section-surface.js.map +1 -1
  99. package/dist/components/sections.d.ts +0 -1
  100. package/dist/components/sections.js +0 -10
  101. package/dist/components/sections.js.map +1 -1
  102. package/dist/components/studio-project-screen.d.ts +3 -4
  103. package/dist/components/studio-project-screen.js +41 -160
  104. package/dist/components/studio-project-screen.js.map +1 -1
  105. package/dist/components/studio-shell.d.ts +0 -4
  106. package/dist/components/studio-shell.js +14 -14
  107. package/dist/components/studio-shell.js.map +1 -1
  108. package/dist/lib/data/collection-content.d.ts +19 -1
  109. package/dist/lib/data/collection-content.js +16 -0
  110. package/dist/lib/data/collection-content.js.map +1 -1
  111. package/dist/lib/operations/graph-commands.d.ts +2 -6
  112. package/dist/lib/operations/graph-commands.js +1 -2
  113. package/dist/lib/operations/graph-commands.js.map +1 -1
  114. package/dist/lib/operations/index.d.ts +0 -2
  115. package/dist/lib/operations/index.js +0 -2
  116. package/dist/lib/operations/index.js.map +1 -1
  117. package/dist/lib/operations/session-hydration.d.ts +14 -24
  118. package/dist/lib/operations/session-hydration.js +15 -34
  119. package/dist/lib/operations/session-hydration.js.map +1 -1
  120. package/dist/lib/persistence/convex-adapter.d.ts +1 -3
  121. package/dist/lib/persistence/convex-adapter.js +1 -154
  122. package/dist/lib/persistence/convex-adapter.js.map +1 -1
  123. package/dist/lib/persona/labels.js +0 -2
  124. package/dist/lib/persona/labels.js.map +1 -1
  125. package/dist/lib/references/reference-model.d.ts +0 -16
  126. package/dist/lib/references/reference-model.js +2 -54
  127. package/dist/lib/references/reference-model.js.map +1 -1
  128. package/dist/lib/references/references-store.js +3 -4
  129. package/dist/lib/references/references-store.js.map +1 -1
  130. package/dist/lib/registry/index.d.ts +0 -16
  131. package/dist/lib/registry/index.js +0 -15
  132. package/dist/lib/registry/index.js.map +1 -1
  133. package/dist/lib/webhooks/tags.js +2 -16
  134. package/dist/lib/webhooks/tags.js.map +1 -1
  135. package/dist/lib/workspace/index.d.ts +27 -1
  136. package/dist/lib/workspace/index.js +34 -0
  137. package/dist/lib/workspace/index.js.map +1 -1
  138. package/dist/router/match.d.ts +22 -34
  139. package/dist/router/match.js +22 -40
  140. package/dist/router/match.js.map +1 -1
  141. package/package.json +3 -2
  142. package/convex/graphs.ts +0 -55
  143. package/convex/publishing.ts +0 -76
@@ -1,15 +1,12 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { EyeOff } from 'lucide-react';
3
2
  import { useEffect, useState } from 'react';
4
- import { conditionOperatorSchema, staticProp } from '@elytracms/core/project-graph';
5
- import { pathFromToken } from '@elytracms/core/data-binding';
3
+ import { staticProp } from '@elytracms/core/project-graph';
6
4
  import { manifestSlots } from '@elytracms/core/component-registry';
7
5
  import { fieldValueValidator } from '@elytracms/core/cms-core';
8
6
  import { getStudioRegistry } from '@elytracms/studio/lib/registry';
9
- import { useData } from '@elytracms/studio/lib/data';
10
- import { Badge, Button, Code, Field, Input, InspectorSection, PanelHeader, Row, Select, Spacer, Stack, Text, } from '@elytracms/studio/ui';
7
+ import { Badge, Code, Field, Input, InspectorSection, PanelHeader, Row, Select, Spacer, Stack, Text, } from '@elytracms/studio/ui';
11
8
  import { humanizeName } from '@elytracms/studio/lib/utils';
12
- import { componentDisplayName, conditionOperatorLabel } from '@elytracms/studio/lib/persona/labels';
9
+ import { componentDisplayName } from '@elytracms/studio/lib/persona/labels';
13
10
  import { FieldControl } from '../cms/field-control';
14
11
  import { useBuilder } from './builder-store';
15
12
  import { LayoutSection, layoutManagedPropNames } from './layout-inspector';
@@ -38,7 +35,7 @@ function SettingsTab({ node, manifest, }) {
38
35
  return (_jsxs(_Fragment, { children: [_jsx(LayoutSection, { node: node }), !manifest ? (_jsxs("div", { className: "p-3", children: [_jsx(Badge, { variant: "error", children: "unknown component" }), _jsx(Text, { size: "xs", tone: "muted", className: "mt-2", children: "No manifest is registered for this component, so its props can't be edited." })] })) : (_jsx(InspectorSection, { title: "Props", children: props.length === 0 ? (_jsx(Text, { size: "xs", tone: "subtle", children: "No editable props." })) : (_jsx(Stack, { gap: "4", children: props.map(([name, spec]) => (
39
36
  // Key by node id so per-node local control state never leaks
40
37
  // across a selection change to a same-named prop.
41
- _jsx(PropField, { node: node, name: name, spec: spec }, `${node.id}:${name}`))) })) })), _jsx(InspectorSection, { title: "Condition", children: _jsx(ConditionEditor, { node: node }) }), slots.length > 0 ? (_jsxs(InspectorSection, { title: "Slots", children: [_jsx(Stack, { gap: "1", children: slots.map((slot) => {
38
+ _jsx(PropField, { node: node, name: name, spec: spec }, `${node.id}:${name}`))) })) })), slots.length > 0 ? (_jsxs(InspectorSection, { title: "Slots", children: [_jsx(Stack, { gap: "1", children: slots.map((slot) => {
42
39
  const count = node.slots?.[slot.name]?.length ?? 0;
43
40
  const missing = slot.required && count === 0;
44
41
  return (_jsxs(Row, { gap: "2", children: [_jsx(Text, { size: "xs", title: slot.name, children: humanizeName(slot.name) }), slot.required ? (_jsx(Badge, { variant: missing ? 'error' : 'outline', children: "required" })) : null, _jsx(Spacer, {}), _jsx(Text, { size: "xs", tone: "subtle", mono: true, children: count })] }, slot.name));
@@ -173,61 +170,4 @@ function JsonControl({ id, current, onSet, }) {
173
170
  }
174
171
  }, className: "flex w-full rounded-md border border-input bg-background px-2.5 py-1.5 font-mono text-xs text-foreground shadow-sm focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40 outline-none" }), error ? (_jsx(Text, { size: "xs", className: "text-destructive", children: error })) : null] }));
175
172
  }
176
- /** Plain-language label for a stored data path ("item 1 → title") — no raw syntax. */
177
- function friendlyPathLabel(token) {
178
- const path = pathFromToken(token);
179
- if (path === undefined)
180
- return 'this data';
181
- if (path.length === 0)
182
- return 'Everything';
183
- return path
184
- .map((seg) => (/^\d+$/.test(seg) ? `entry ${Number(seg) + 1}` : humanizeName(seg)))
185
- .join(' → ');
186
- }
187
- /** Edit a node's render condition (EC-088): source field + operator + comparison value. */
188
- function ConditionEditor({ node }) {
189
- const builder = useBuilder();
190
- const data = useData();
191
- const condition = node.condition;
192
- const firstSource = data.sources[0];
193
- if (!condition) {
194
- return (_jsxs(Stack, { gap: "2", children: [_jsx(Text, { size: "xs", tone: "subtle", children: "This node always renders. Add a condition to gate it on a data value." }), _jsxs(Button, { size: "sm", variant: "outline", className: "w-fit", disabled: !firstSource, onClick: () => firstSource &&
195
- builder.dispatch({
196
- type: 'setCondition',
197
- id: node.id,
198
- condition: {
199
- source: { sourceId: firstSource.id, token: '/', mode: 'value' },
200
- operator: 'truthy',
201
- },
202
- }), children: [_jsx(EyeOff, {}), " Add condition"] })] }));
203
- }
204
- const updateCondition = (patch) => builder.dispatch({ type: 'setCondition', id: node.id, condition: { ...condition, ...patch } });
205
- const updateSource = (patch) => updateCondition({ source: { ...condition.source, ...patch } });
206
- const tokens = data.tokensOf(condition.source.sourceId);
207
- const source = data.getSource(condition.source.sourceId);
208
- const needsValue = !['truthy', 'exists'].includes(condition.operator);
209
- const result = data.resolveCondition(condition);
210
- const pass = result.kind === 'condition' ? result.pass : false;
211
- const issue = result.kind === 'issue' ? result.issue : null;
212
- return (_jsxs(Stack, { gap: "2", children: [_jsx(Field, { label: "Source", htmlFor: `cond-src-${node.id}`, children: _jsxs(Select, { id: `cond-src-${node.id}`, value: condition.source.sourceId, onChange: (e) => updateSource({ sourceId: e.target.value, token: '/' }), children: [data.sources.map((s) => (_jsx("option", { value: s.id, children: s.label }, s.id))), !source ? (_jsx("option", { value: condition.source.sourceId, title: condition.source.sourceId, children: "Missing data source" })) : null] }) }), _jsxs(Row, { gap: "2", children: [_jsx(Field, { label: "Field", htmlFor: `cond-token-${node.id}`, className: "min-w-0 flex-1", children: _jsxs(Select, { id: `cond-token-${node.id}`, value: condition.source.token, onChange: (e) => updateSource({ token: e.target.value }), children: [tokens.map((t) => (_jsx("option", { value: t, children: friendlyPathLabel(t) }, t))), !tokens.includes(condition.source.token) ? (_jsx("option", { value: condition.source.token, children: friendlyPathLabel(condition.source.token) })) : null] }) }), _jsx(Field, { label: "Operator", htmlFor: `cond-op-${node.id}`, className: "w-28", children: _jsx(Select, { id: `cond-op-${node.id}`, value: condition.operator, onChange: (e) => updateCondition({ operator: e.target.value }), children: conditionOperatorSchema.options.map((op) => (_jsx("option", { value: op, title: op, children: conditionOperatorLabel(op) }, op))) }) })] }), needsValue ? (_jsx(Field, { label: "Compare to", htmlFor: `cond-val-${node.id}`, children: _jsx(Input, { id: `cond-val-${node.id}`, value: condition.value === undefined ? '' : String(condition.value), onChange: (e) => updateCondition({ value: coerceScalar(e.target.value) }), placeholder: "value", className: "font-mono text-xs" }) })) : null, _jsxs(Row, { gap: "2", children: [issue ? (_jsx(Badge, { variant: "error", children: "broken" })) : pass ? (_jsx(Badge, { variant: "success", children: "renders" })) : (_jsxs(Badge, { variant: "warning", children: [_jsx(EyeOff, { className: "size-3" }), " hidden"] })), _jsx(Text, { size: "xs", tone: "muted", className: "min-w-0 flex-1 truncate", children: issue
213
- ? issue.message
214
- : pass
215
- ? 'Condition passes — node renders.'
216
- : 'Condition fails — node is hidden on the canvas.' }), _jsx(Spacer, {}), _jsx(Button, { size: "sm", variant: "ghost", className: "text-destructive hover:text-destructive", onClick: () => builder.dispatch({ type: 'setCondition', id: node.id, condition: undefined }), children: "Remove" })] })] }));
217
- }
218
- /** Parse a comparison-value string into a number/boolean/null when it looks like one. */
219
- function coerceScalar(raw) {
220
- if (raw === '')
221
- return '';
222
- if (raw === 'true')
223
- return true;
224
- if (raw === 'false')
225
- return false;
226
- if (raw === 'null')
227
- return null;
228
- const n = Number(raw);
229
- if (raw.trim() !== '' && Number.isFinite(n))
230
- return n;
231
- return raw;
232
- }
233
173
  //# sourceMappingURL=property-inspector.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"property-inspector.js","sourceRoot":"","sources":["../../../src/components/builder/property-inspector.tsx"],"names":[],"mappings":"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","sourcesContent":["import { EyeOff } from 'lucide-react'\nimport { useEffect, useState } from 'react'\nimport { conditionOperatorSchema, staticProp } from '@elytracms/core/project-graph'\nimport type {\n BindingReference,\n ComponentNode,\n Condition,\n ConditionOperator,\n JsonValue,\n PropValue,\n} from '@elytracms/core/project-graph'\nimport { pathFromToken } from '@elytracms/core/data-binding'\nimport type { ComponentManifest, PropField } from '@elytracms/core/component-registry'\nimport { manifestSlots } from '@elytracms/core/component-registry'\nimport { fieldValueValidator } from '@elytracms/core/cms-core'\nimport type { FieldDef, SelectOption } from '@elytracms/core/cms-core'\nimport { getStudioRegistry } from '@elytracms/studio/lib/registry'\nimport { useData } from '@elytracms/studio/lib/data'\nimport {\n Badge,\n Button,\n Code,\n Field,\n Input,\n InspectorSection,\n PanelHeader,\n Row,\n Select,\n Spacer,\n Stack,\n Text,\n} from '@elytracms/studio/ui'\nimport { humanizeName } from '@elytracms/studio/lib/utils'\nimport { componentDisplayName, conditionOperatorLabel } from '@elytracms/studio/lib/persona/labels'\nimport { FieldControl } from '../cms/field-control'\nimport { useBuilder } from './builder-store'\nimport { LayoutSection, layoutManagedPropNames } from './layout-inspector'\n\nexport function PropertyInspector() {\n const builder = useBuilder()\n const studio = getStudioRegistry()\n const node = builder.selectedNode\n\n if (!node) {\n return (\n <div className=\"grid h-full place-items-center p-6 text-center\">\n <Text size=\"sm\" tone=\"muted\">\n Select a node in the tree or canvas to edit its properties.\n </Text>\n </div>\n )\n }\n\n const manifest = studio.registry.getManifest(node.componentId)\n\n return (\n <div className=\"flex h-full min-h-0 flex-col\">\n <PanelHeader>\n <Text size=\"sm\" weight=\"semibold\">\n Inspector\n </Text>\n <Spacer />\n <Code tone=\"muted\" title={node.componentId}>\n {componentDisplayName(node.componentId, manifest)}\n </Code>\n </PanelHeader>\n\n <div className=\"min-h-0 flex-1 overflow-auto\">\n <SettingsTab node={node} manifest={manifest} />\n </div>\n </div>\n )\n}\n\n/** The full property surface: node identity, static props, condition, slots. */\nfunction SettingsTab({\n node,\n manifest,\n}: {\n node: ComponentNode\n manifest: ComponentManifest | undefined\n}) {\n // Container layout props render in the dedicated Layout section (EC-162),\n // not as a second set of generic controls.\n const layoutManaged = new Set(layoutManagedPropNames(node))\n // EC-190: `blocks` props are slots (named child regions), edited via the page\n // tree / composition surface, not as a scalar control — so they show in the\n // Slots section, not Props.\n const props = manifest\n ? Object.entries(manifest.props).filter(\n ([name, spec]) => !layoutManaged.has(name) && spec.type !== 'blocks',\n )\n : []\n const slots = manifest ? manifestSlots(manifest) : []\n\n return (\n <>\n {/* EC-192: the editor-facing inspector no longer renders the raw node id or\n namespaced component id (the persona promise). The component's human name\n is in the panel header (with the manifest id in its `title`); the canvas\n and tree provide selection. */}\n {/* Contextual layout inspector for Stack/Grid containers (EC-162). */}\n <LayoutSection node={node} />\n\n {!manifest ? (\n <div className=\"p-3\">\n <Badge variant=\"error\">unknown component</Badge>\n <Text size=\"xs\" tone=\"muted\" className=\"mt-2\">\n No manifest is registered for this component, so its props can't be edited.\n </Text>\n </div>\n ) : (\n <InspectorSection title=\"Props\">\n {props.length === 0 ? (\n <Text size=\"xs\" tone=\"subtle\">\n No editable props.\n </Text>\n ) : (\n <Stack gap=\"4\">\n {props.map(([name, spec]) => (\n // Key by node id so per-node local control state never leaks\n // across a selection change to a same-named prop.\n <PropField key={`${node.id}:${name}`} node={node} name={name} spec={spec} />\n ))}\n </Stack>\n )}\n </InspectorSection>\n )}\n\n {/* Conditional rendering (EC-088). */}\n <InspectorSection title=\"Condition\">\n <ConditionEditor node={node} />\n </InspectorSection>\n\n {slots.length > 0 ? (\n <InspectorSection title=\"Slots\">\n <Stack gap=\"1\">\n {slots.map((slot) => {\n const count = node.slots?.[slot.name]?.length ?? 0\n const missing = slot.required && count === 0\n return (\n <Row key={slot.name} gap=\"2\">\n <Text size=\"xs\" title={slot.name}>\n {humanizeName(slot.name)}\n </Text>\n {slot.required ? (\n <Badge variant={missing ? 'error' : 'outline'}>required</Badge>\n ) : null}\n <Spacer />\n <Text size=\"xs\" tone=\"subtle\" mono>\n {count}\n </Text>\n </Row>\n )\n })}\n </Stack>\n <Text size=\"xs\" tone=\"subtle\" className=\"mt-2\">\n Add or reorder slot children from the page tree.\n </Text>\n </InspectorSection>\n ) : null}\n </>\n )\n}\n\nfunction PropField({ node, name, spec }: { node: ComponentNode; name: string; spec: PropField }) {\n const value = node.props?.[name]\n\n return (\n <Field label={spec.form?.label ?? name} htmlFor={`prop-${node.id}-${name}`}>\n <StaticControl node={node} name={name} spec={spec} value={value} />\n </Field>\n )\n}\n\n/**\n * EC-190: the inspector control to render for a prop field-def. The old\n * `control.kind` vocabulary derives from the field `type` plus the optional\n * `form.control` presentation hint on `text` fields. Slice 3 unifies this with\n * the CMS `FieldControl`; for now it keeps the existing per-kind control set.\n */\ntype ControlKind =\n | 'text'\n | 'textarea'\n | 'number'\n | 'boolean'\n | 'select'\n | 'json'\n | 'color'\n | 'richtext'\n | 'asset'\n | 'object'\n | 'relation'\nfunction controlKind(spec: PropField): ControlKind {\n switch (spec.type) {\n case 'boolean':\n return 'boolean'\n case 'number':\n return 'number'\n case 'select':\n return 'select'\n case 'richText':\n return 'richtext'\n // EC-195: an `asset`-typed prop (e.g. the Image `src`) picks from the asset\n // library — never a raw asset id typed into a text box (the EC-192 persona).\n case 'asset':\n return 'asset'\n // EC-253: an `object`/repeater prop reuses the CMS repeater control below.\n case 'object':\n return 'object'\n // EC-262: a `relation`-typed prop (e.g. a ProductGrid's `category`) reuses the\n // CMS relation picker — never a text box that stringifies the ref to \"[object Object]\".\n case 'relation':\n return 'relation'\n case 'text': {\n const c = spec.form?.control\n if (c === 'textarea') return 'textarea'\n if (c === 'color') return 'color'\n if (c === 'json') return 'json'\n return 'text'\n }\n default:\n return 'text'\n }\n}\n\nfunction StaticControl({\n node,\n name,\n spec,\n value,\n}: {\n node: ComponentNode\n name: string\n spec: PropField\n value: PropValue | undefined\n}) {\n const builder = useBuilder()\n const current = value?.kind === 'static' ? value.value : (spec.default as JsonValue | undefined)\n const id = `prop-${node.id}-${name}`\n const coalesceKey = `${node.id}:${name}`\n\n function set(v: JsonValue) {\n builder.dispatch({ type: 'setProp', id: node.id, name, value: staticProp(v), coalesceKey })\n }\n function clear() {\n builder.dispatch({ type: 'removeProp', id: node.id, name })\n }\n\n // EC-190: validate with the prop's field-def value validator (was `spec.schema.safeParse`).\n const valid = current === undefined || fieldValueValidator({ ...spec, name } as FieldDef)(current)\n // EC-190: derive the editor control from the field `type` + optional `form.control`.\n const kind = controlKind(spec)\n // Select options live on the `select` field-def (was `spec.control.options`).\n const options: readonly SelectOption[] = spec.type === 'select' ? spec.options : []\n\n let control\n if (kind === 'boolean') {\n control = (\n <label className=\"flex h-8 items-center gap-2 text-sm\">\n <input\n id={id}\n type=\"checkbox\"\n checked={current === true}\n onChange={(e) => set(e.target.checked)}\n className=\"size-4 accent-primary\"\n />\n <Text size=\"sm\" tone=\"muted\">\n {current === true ? 'true' : 'false'}\n </Text>\n </label>\n )\n } else if (kind === 'select') {\n control = (\n <Select\n id={id}\n value={String(current ?? '')}\n onChange={(e) => {\n // EC-190: select option values are strings (field-def `options`).\n const opt = options.find((o) => o.value === e.target.value)\n set(opt ? opt.value : e.target.value)\n }}\n >\n {options.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label ?? opt.value}\n </option>\n ))}\n </Select>\n )\n } else if (kind === 'number') {\n control = <NumberControl id={id} current={current} onSet={set} onClear={clear} />\n } else if (kind === 'json') {\n control = <JsonControl id={id} current={current} onSet={set} />\n } else if (kind === 'color') {\n control = (\n <Row gap=\"2\">\n <input\n id={id}\n type=\"color\"\n value={\n typeof current === 'string' && /^#[0-9a-f]{6}$/i.test(current) ? current : '#000000'\n }\n onChange={(e) => set(e.target.value)}\n className=\"h-8 w-10 shrink-0 rounded-md border border-input bg-background\"\n />\n <Input\n value={typeof current === 'string' ? current : ''}\n onChange={(e) => set(e.target.value)}\n />\n </Row>\n )\n } else if (kind === 'textarea' || kind === 'richtext') {\n control = (\n <textarea\n id={id}\n rows={2}\n value={typeof current === 'string' ? current : ''}\n onChange={(e) => set(e.target.value)}\n className=\"flex w-full rounded-md border border-input bg-background px-2.5 py-1.5 text-sm text-foreground shadow-sm focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40 outline-none\"\n />\n )\n } else if (kind === 'asset' || kind === 'object' || kind === 'relation') {\n // EC-195/EC-253/EC-262: reuse the CMS field control. `asset` = library picker +\n // preview; `object` = the nested/repeater form; `relation` = the document picker\n // (resolves the ref to its title, instead of stringifying it to \"[object Object]\").\n // All edit a structured static prop value through the same shared control documents use.\n control = (\n <FieldControl\n id={id}\n field={{ ...spec, name } as FieldDef}\n value={current}\n onChange={(v) => (v === undefined || v === null ? clear() : set(v as JsonValue))}\n readOnly={false}\n />\n )\n } else {\n control = (\n <Input\n id={id}\n value={typeof current === 'string' ? current : String(current ?? '')}\n onChange={(e) => set(e.target.value)}\n />\n )\n }\n\n return (\n <Stack gap=\"1\">\n {control}\n {!valid ? (\n <Text size=\"xs\" className=\"text-destructive\">\n Value doesn't match the prop schema — the canvas falls back to the default.\n </Text>\n ) : null}\n </Stack>\n )\n}\n\n/** Number input with a local buffer: commits only finite values, never NaN; empty clears the prop. */\nfunction NumberControl({\n id,\n current,\n onSet,\n onClear,\n}: {\n id: string\n current: JsonValue | undefined\n onSet: (v: number) => void\n onClear: () => void\n}) {\n const [raw, setRaw] = useState(typeof current === 'number' ? String(current) : '')\n useEffect(() => {\n setRaw(typeof current === 'number' ? String(current) : '')\n }, [current])\n return (\n <Input\n id={id}\n type=\"number\"\n value={raw}\n onChange={(e) => {\n const v = e.target.value\n setRaw(v)\n if (v.trim() === '') {\n onClear()\n return\n }\n const n = Number(v)\n if (Number.isFinite(n)) onSet(n)\n }}\n />\n )\n}\n\n/** JSON textarea with a local buffer: commits only when the text parses; flags parse errors. */\nfunction JsonControl({\n id,\n current,\n onSet,\n}: {\n id: string\n current: JsonValue | undefined\n onSet: (v: JsonValue) => void\n}) {\n const [raw, setRaw] = useState(() => JSON.stringify(current ?? null))\n const [error, setError] = useState<string | null>(null)\n useEffect(() => {\n setRaw(JSON.stringify(current ?? null))\n setError(null)\n }, [current])\n return (\n <Stack gap=\"1\">\n <textarea\n id={id}\n rows={3}\n value={raw}\n onChange={(e) => {\n const v = e.target.value\n setRaw(v)\n try {\n onSet(JSON.parse(v) as JsonValue)\n setError(null)\n } catch {\n setError('Invalid JSON')\n }\n }}\n className=\"flex w-full rounded-md border border-input bg-background px-2.5 py-1.5 font-mono text-xs text-foreground shadow-sm focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40 outline-none\"\n />\n {error ? (\n <Text size=\"xs\" className=\"text-destructive\">\n {error}\n </Text>\n ) : null}\n </Stack>\n )\n}\n\n/** Plain-language label for a stored data path (\"item 1 → title\") — no raw syntax. */\nfunction friendlyPathLabel(token: string): string {\n const path = pathFromToken(token)\n if (path === undefined) return 'this data'\n if (path.length === 0) return 'Everything'\n return path\n .map((seg) => (/^\\d+$/.test(seg) ? `entry ${Number(seg) + 1}` : humanizeName(seg)))\n .join(' → ')\n}\n\n/** Edit a node's render condition (EC-088): source field + operator + comparison value. */\nfunction ConditionEditor({ node }: { node: ComponentNode }) {\n const builder = useBuilder()\n const data = useData()\n const condition = node.condition\n const firstSource = data.sources[0]\n\n if (!condition) {\n return (\n <Stack gap=\"2\">\n <Text size=\"xs\" tone=\"subtle\">\n This node always renders. Add a condition to gate it on a data value.\n </Text>\n <Button\n size=\"sm\"\n variant=\"outline\"\n className=\"w-fit\"\n disabled={!firstSource}\n onClick={() =>\n firstSource &&\n builder.dispatch({\n type: 'setCondition',\n id: node.id,\n condition: {\n source: { sourceId: firstSource.id, token: '/', mode: 'value' },\n operator: 'truthy',\n },\n })\n }\n >\n <EyeOff /> Add condition\n </Button>\n </Stack>\n )\n }\n\n const updateCondition = (patch: Partial<Condition>) =>\n builder.dispatch({ type: 'setCondition', id: node.id, condition: { ...condition, ...patch } })\n const updateSource = (patch: Partial<BindingReference>) =>\n updateCondition({ source: { ...condition.source, ...patch } })\n\n const tokens = data.tokensOf(condition.source.sourceId)\n const source = data.getSource(condition.source.sourceId)\n const needsValue = !['truthy', 'exists'].includes(condition.operator)\n const result = data.resolveCondition(condition)\n const pass = result.kind === 'condition' ? result.pass : false\n const issue = result.kind === 'issue' ? result.issue : null\n\n return (\n <Stack gap=\"2\">\n <Field label=\"Source\" htmlFor={`cond-src-${node.id}`}>\n <Select\n id={`cond-src-${node.id}`}\n value={condition.source.sourceId}\n onChange={(e) => updateSource({ sourceId: e.target.value, token: '/' })}\n >\n {data.sources.map((s) => (\n <option key={s.id} value={s.id}>\n {s.label}\n </option>\n ))}\n {!source ? (\n <option value={condition.source.sourceId} title={condition.source.sourceId}>\n Missing data source\n </option>\n ) : null}\n </Select>\n </Field>\n\n <Row gap=\"2\">\n <Field label=\"Field\" htmlFor={`cond-token-${node.id}`} className=\"min-w-0 flex-1\">\n <Select\n id={`cond-token-${node.id}`}\n value={condition.source.token}\n onChange={(e) => updateSource({ token: e.target.value })}\n >\n {tokens.map((t) => (\n <option key={t} value={t}>\n {friendlyPathLabel(t)}\n </option>\n ))}\n {!tokens.includes(condition.source.token) ? (\n <option value={condition.source.token}>\n {friendlyPathLabel(condition.source.token)}\n </option>\n ) : null}\n </Select>\n </Field>\n <Field label=\"Operator\" htmlFor={`cond-op-${node.id}`} className=\"w-28\">\n <Select\n id={`cond-op-${node.id}`}\n value={condition.operator}\n onChange={(e) => updateCondition({ operator: e.target.value as ConditionOperator })}\n >\n {conditionOperatorSchema.options.map((op) => (\n <option key={op} value={op} title={op}>\n {conditionOperatorLabel(op)}\n </option>\n ))}\n </Select>\n </Field>\n </Row>\n\n {needsValue ? (\n <Field label=\"Compare to\" htmlFor={`cond-val-${node.id}`}>\n <Input\n id={`cond-val-${node.id}`}\n value={condition.value === undefined ? '' : String(condition.value)}\n onChange={(e) => updateCondition({ value: coerceScalar(e.target.value) })}\n placeholder=\"value\"\n className=\"font-mono text-xs\"\n />\n </Field>\n ) : null}\n\n <Row gap=\"2\">\n {issue ? (\n <Badge variant=\"error\">broken</Badge>\n ) : pass ? (\n <Badge variant=\"success\">renders</Badge>\n ) : (\n <Badge variant=\"warning\">\n <EyeOff className=\"size-3\" /> hidden\n </Badge>\n )}\n <Text size=\"xs\" tone=\"muted\" className=\"min-w-0 flex-1 truncate\">\n {issue\n ? issue.message\n : pass\n ? 'Condition passes — node renders.'\n : 'Condition fails — node is hidden on the canvas.'}\n </Text>\n <Spacer />\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"text-destructive hover:text-destructive\"\n onClick={() =>\n builder.dispatch({ type: 'setCondition', id: node.id, condition: undefined })\n }\n >\n Remove\n </Button>\n </Row>\n </Stack>\n )\n}\n\n/** Parse a comparison-value string into a number/boolean/null when it looks like one. */\nfunction coerceScalar(raw: string): JsonValue {\n if (raw === '') return ''\n if (raw === 'true') return true\n if (raw === 'false') return false\n if (raw === 'null') return null\n const n = Number(raw)\n if (raw.trim() !== '' && Number.isFinite(n)) return n\n return raw\n}\n"]}
1
+ {"version":3,"file":"property-inspector.js","sourceRoot":"","sources":["../../../src/components/builder/property-inspector.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAA;AAG1D,OAAO,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAA;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAE9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAClE,OAAO,EACL,KAAK,EACL,IAAI,EACJ,KAAK,EACL,KAAK,EACL,gBAAgB,EAChB,WAAW,EACX,GAAG,EACH,MAAM,EACN,MAAM,EACN,KAAK,EACL,IAAI,GACL,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAA;AAC3E,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AAE1E,MAAM,UAAU,iBAAiB;IAC/B,MAAM,OAAO,GAAG,UAAU,EAAE,CAAA;IAC5B,MAAM,MAAM,GAAG,iBAAiB,EAAE,CAAA;IAClC,MAAM,IAAI,GAAG,OAAO,CAAC,YAAY,CAAA;IAEjC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,CACL,cAAK,SAAS,EAAC,gDAAgD,YAC7D,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,4EAErB,GACH,CACP,CAAA;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;IAE9D,OAAO,CACL,eAAK,SAAS,EAAC,8BAA8B,aAC3C,MAAC,WAAW,eACV,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,MAAM,EAAC,UAAU,0BAE1B,EACP,KAAC,MAAM,KAAG,EACV,KAAC,IAAI,IAAC,IAAI,EAAC,OAAO,EAAC,KAAK,EAAE,IAAI,CAAC,WAAW,YACvC,oBAAoB,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,GAC5C,IACK,EAEd,cAAK,SAAS,EAAC,8BAA8B,YAC3C,KAAC,WAAW,IAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,GAAI,GAC3C,IACF,CACP,CAAA;AACH,CAAC;AAED,gFAAgF;AAChF,SAAS,WAAW,CAAC,EACnB,IAAI,EACJ,QAAQ,GAIT;IACC,0EAA0E;IAC1E,2CAA2C;IAC3C,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,sBAAsB,CAAC,IAAI,CAAC,CAAC,CAAA;IAC3D,8EAA8E;IAC9E,4EAA4E;IAC5E,4BAA4B;IAC5B,MAAM,KAAK,GAAG,QAAQ;QACpB,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,MAAM,CACnC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,CACrE;QACH,CAAC,CAAC,EAAE,CAAA;IACN,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IAErD,OAAO,CACL,8BAME,KAAC,aAAa,IAAC,IAAI,EAAE,IAAI,GAAI,EAE5B,CAAC,QAAQ,CAAC,CAAC,CAAC,CACX,eAAK,SAAS,EAAC,KAAK,aAClB,KAAC,KAAK,IAAC,OAAO,EAAC,OAAO,kCAA0B,EAChD,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,EAAC,SAAS,EAAC,MAAM,4FAEtC,IACH,CACP,CAAC,CAAC,CAAC,CACF,KAAC,gBAAgB,IAAC,KAAK,EAAC,OAAO,YAC5B,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CACpB,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,QAAQ,mCAEtB,CACR,CAAC,CAAC,CAAC,CACF,KAAC,KAAK,IAAC,GAAG,EAAC,GAAG,YACX,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC;oBAC3B,6DAA6D;oBAC7D,kDAAkD;oBAClD,KAAC,SAAS,IAA4B,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,IAAxD,GAAG,IAAI,CAAC,EAAE,IAAI,IAAI,EAAE,CAAwC,CAC7E,CAAC,GACI,CACT,GACgB,CACpB,EAEA,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAClB,MAAC,gBAAgB,IAAC,KAAK,EAAC,OAAO,aAC7B,KAAC,KAAK,IAAC,GAAG,EAAC,GAAG,YACX,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;4BAClB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,IAAI,CAAC,CAAA;4BAClD,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,IAAI,KAAK,KAAK,CAAC,CAAA;4BAC5C,OAAO,CACL,MAAC,GAAG,IAAiB,GAAG,EAAC,GAAG,aAC1B,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,KAAK,EAAE,IAAI,CAAC,IAAI,YAC7B,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,GACnB,EACN,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CACf,KAAC,KAAK,IAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,yBAAkB,CAChE,CAAC,CAAC,CAAC,IAAI,EACR,KAAC,MAAM,KAAG,EACV,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,QAAQ,EAAC,IAAI,kBAC/B,KAAK,GACD,KAVC,IAAI,CAAC,IAAI,CAWb,CACP,CAAA;wBACH,CAAC,CAAC,GACI,EACR,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,QAAQ,EAAC,SAAS,EAAC,MAAM,iEAEvC,IACU,CACpB,CAAC,CAAC,CAAC,IAAI,IACP,CACJ,CAAA;AACH,CAAC;AAED,SAAS,SAAS,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAA0D;IAC7F,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,CAAA;IAEhC,OAAO,CACL,KAAC,KAAK,IAAC,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,IAAI,IAAI,EAAE,OAAO,EAAE,QAAQ,IAAI,CAAC,EAAE,IAAI,IAAI,EAAE,YACxE,KAAC,aAAa,IAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,GAAI,GAC7D,CACT,CAAA;AACH,CAAC;AAoBD,SAAS,WAAW,CAAC,IAAe;IAClC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,SAAS;YACZ,OAAO,SAAS,CAAA;QAClB,KAAK,QAAQ;YACX,OAAO,QAAQ,CAAA;QACjB,KAAK,QAAQ;YACX,OAAO,QAAQ,CAAA;QACjB,KAAK,UAAU;YACb,OAAO,UAAU,CAAA;QACnB,4EAA4E;QAC5E,6EAA6E;QAC7E,KAAK,OAAO;YACV,OAAO,OAAO,CAAA;QAChB,2EAA2E;QAC3E,KAAK,QAAQ;YACX,OAAO,QAAQ,CAAA;QACjB,+EAA+E;QAC/E,wFAAwF;QACxF,KAAK,UAAU;YACb,OAAO,UAAU,CAAA;QACnB,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,OAAO,CAAA;YAC5B,IAAI,CAAC,KAAK,UAAU;gBAAE,OAAO,UAAU,CAAA;YACvC,IAAI,CAAC,KAAK,OAAO;gBAAE,OAAO,OAAO,CAAA;YACjC,IAAI,CAAC,KAAK,MAAM;gBAAE,OAAO,MAAM,CAAA;YAC/B,OAAO,MAAM,CAAA;QACf,CAAC;QACD;YACE,OAAO,MAAM,CAAA;IACjB,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,EACrB,IAAI,EACJ,IAAI,EACJ,IAAI,EACJ,KAAK,GAMN;IACC,MAAM,OAAO,GAAG,UAAU,EAAE,CAAA;IAC5B,MAAM,OAAO,GAAG,KAAK,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAE,IAAI,CAAC,OAAiC,CAAA;IAChG,MAAM,EAAE,GAAG,QAAQ,IAAI,CAAC,EAAE,IAAI,IAAI,EAAE,CAAA;IACpC,MAAM,WAAW,GAAG,GAAG,IAAI,CAAC,EAAE,IAAI,IAAI,EAAE,CAAA;IAExC,SAAS,GAAG,CAAC,CAAY;QACvB,OAAO,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAA;IAC7F,CAAC;IACD,SAAS,KAAK;QACZ,OAAO,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAA;IAC7D,CAAC;IAED,4FAA4F;IAC5F,MAAM,KAAK,GAAG,OAAO,KAAK,SAAS,IAAI,mBAAmB,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,EAAc,CAAC,CAAC,OAAO,CAAC,CAAA;IAClG,qFAAqF;IACrF,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,CAAA;IAC9B,8EAA8E;IAC9E,MAAM,OAAO,GAA4B,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAA;IAEnF,IAAI,OAAO,CAAA;IACX,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO,GAAG,CACR,iBAAO,SAAS,EAAC,qCAAqC,aACpD,gBACE,EAAE,EAAE,EAAE,EACN,IAAI,EAAC,UAAU,EACf,OAAO,EAAE,OAAO,KAAK,IAAI,EACzB,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EACtC,SAAS,EAAC,uBAAuB,GACjC,EACF,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,YACzB,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,GAC/B,IACD,CACT,CAAA;IACH,CAAC;SAAM,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,OAAO,GAAG,CACR,KAAC,MAAM,IACL,EAAE,EAAE,EAAE,EACN,KAAK,EAAE,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,EAC5B,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE;gBACd,kEAAkE;gBAClE,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;gBAC3D,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YACvC,CAAC,YAEA,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CACpB,iBAAwB,KAAK,EAAE,GAAG,CAAC,KAAK,YACrC,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,KAAK,IADZ,GAAG,CAAC,KAAK,CAEb,CACV,CAAC,GACK,CACV,CAAA;IACH,CAAC;SAAM,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,OAAO,GAAG,KAAC,aAAa,IAAC,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,GAAI,CAAA;IACnF,CAAC;SAAM,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QAC3B,OAAO,GAAG,KAAC,WAAW,IAAC,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,GAAI,CAAA;IACjE,CAAC;SAAM,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QAC5B,OAAO,GAAG,CACR,MAAC,GAAG,IAAC,GAAG,EAAC,GAAG,aACV,gBACE,EAAE,EAAE,EAAE,EACN,IAAI,EAAC,OAAO,EACZ,KAAK,EACH,OAAO,OAAO,KAAK,QAAQ,IAAI,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,EAEtF,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EACpC,SAAS,EAAC,gEAAgE,GAC1E,EACF,KAAC,KAAK,IACJ,KAAK,EAAE,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EACjD,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,GACpC,IACE,CACP,CAAA;IACH,CAAC;SAAM,IAAI,IAAI,KAAK,UAAU,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;QACtD,OAAO,GAAG,CACR,mBACE,EAAE,EAAE,EAAE,EACN,IAAI,EAAE,CAAC,EACP,KAAK,EAAE,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EACjD,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EACpC,SAAS,EAAC,iMAAiM,GAC3M,CACH,CAAA;IACH,CAAC;SAAM,IAAI,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;QACxE,gFAAgF;QAChF,iFAAiF;QACjF,oFAAoF;QACpF,yFAAyF;QACzF,OAAO,GAAG,CACR,KAAC,YAAY,IACX,EAAE,EAAE,EAAE,EACN,KAAK,EAAE,EAAE,GAAG,IAAI,EAAE,IAAI,EAAc,EACpC,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAc,CAAC,CAAC,EAChF,QAAQ,EAAE,KAAK,GACf,CACH,CAAA;IACH,CAAC;SAAM,CAAC;QACN,OAAO,GAAG,CACR,KAAC,KAAK,IACJ,EAAE,EAAE,EAAE,EACN,KAAK,EAAE,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,EACpE,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,GACpC,CACH,CAAA;IACH,CAAC;IAED,OAAO,CACL,MAAC,KAAK,IAAC,GAAG,EAAC,GAAG,aACX,OAAO,EACP,CAAC,KAAK,CAAC,CAAC,CAAC,CACR,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,SAAS,EAAC,kBAAkB,iGAErC,CACR,CAAC,CAAC,CAAC,IAAI,IACF,CACT,CAAA;AACH,CAAC;AAED,sGAAsG;AACtG,SAAS,aAAa,CAAC,EACrB,EAAE,EACF,OAAO,EACP,KAAK,EACL,OAAO,GAMR;IACC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAC,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IAClF,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,CAAC,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IAC5D,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAA;IACb,OAAO,CACL,KAAC,KAAK,IACJ,EAAE,EAAE,EAAE,EACN,IAAI,EAAC,QAAQ,EACb,KAAK,EAAE,GAAG,EACV,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE;YACd,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAA;YACxB,MAAM,CAAC,CAAC,CAAC,CAAA;YACT,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;gBACpB,OAAO,EAAE,CAAA;gBACT,OAAM;YACR,CAAC;YACD,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAA;YACnB,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;gBAAE,KAAK,CAAC,CAAC,CAAC,CAAA;QAClC,CAAC,GACD,CACH,CAAA;AACH,CAAC;AAED,gGAAgG;AAChG,SAAS,WAAW,CAAC,EACnB,EAAE,EACF,OAAO,EACP,KAAK,GAKN;IACC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC,CAAA;IACrE,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAA;IACvD,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC,CAAA;QACvC,QAAQ,CAAC,IAAI,CAAC,CAAA;IAChB,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAA;IACb,OAAO,CACL,MAAC,KAAK,IAAC,GAAG,EAAC,GAAG,aACZ,mBACE,EAAE,EAAE,EAAE,EACN,IAAI,EAAE,CAAC,EACP,KAAK,EAAE,GAAG,EACV,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE;oBACd,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAA;oBACxB,MAAM,CAAC,CAAC,CAAC,CAAA;oBACT,IAAI,CAAC;wBACH,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAc,CAAC,CAAA;wBACjC,QAAQ,CAAC,IAAI,CAAC,CAAA;oBAChB,CAAC;oBAAC,MAAM,CAAC;wBACP,QAAQ,CAAC,cAAc,CAAC,CAAA;oBAC1B,CAAC;gBACH,CAAC,EACD,SAAS,EAAC,2MAA2M,GACrN,EACD,KAAK,CAAC,CAAC,CAAC,CACP,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,SAAS,EAAC,kBAAkB,YACzC,KAAK,GACD,CACR,CAAC,CAAC,CAAC,IAAI,IACF,CACT,CAAA;AACH,CAAC","sourcesContent":["import { useEffect, useState } from 'react'\nimport { staticProp } from '@elytracms/core/project-graph'\nimport type { ComponentNode, JsonValue, PropValue } from '@elytracms/core/project-graph'\nimport type { ComponentManifest, PropField } from '@elytracms/core/component-registry'\nimport { manifestSlots } from '@elytracms/core/component-registry'\nimport { fieldValueValidator } from '@elytracms/core/cms-core'\nimport type { FieldDef, SelectOption } from '@elytracms/core/cms-core'\nimport { getStudioRegistry } from '@elytracms/studio/lib/registry'\nimport {\n Badge,\n Code,\n Field,\n Input,\n InspectorSection,\n PanelHeader,\n Row,\n Select,\n Spacer,\n Stack,\n Text,\n} from '@elytracms/studio/ui'\nimport { humanizeName } from '@elytracms/studio/lib/utils'\nimport { componentDisplayName } from '@elytracms/studio/lib/persona/labels'\nimport { FieldControl } from '../cms/field-control'\nimport { useBuilder } from './builder-store'\nimport { LayoutSection, layoutManagedPropNames } from './layout-inspector'\n\nexport function PropertyInspector() {\n const builder = useBuilder()\n const studio = getStudioRegistry()\n const node = builder.selectedNode\n\n if (!node) {\n return (\n <div className=\"grid h-full place-items-center p-6 text-center\">\n <Text size=\"sm\" tone=\"muted\">\n Select a node in the tree or canvas to edit its properties.\n </Text>\n </div>\n )\n }\n\n const manifest = studio.registry.getManifest(node.componentId)\n\n return (\n <div className=\"flex h-full min-h-0 flex-col\">\n <PanelHeader>\n <Text size=\"sm\" weight=\"semibold\">\n Inspector\n </Text>\n <Spacer />\n <Code tone=\"muted\" title={node.componentId}>\n {componentDisplayName(node.componentId, manifest)}\n </Code>\n </PanelHeader>\n\n <div className=\"min-h-0 flex-1 overflow-auto\">\n <SettingsTab node={node} manifest={manifest} />\n </div>\n </div>\n )\n}\n\n/** The full property surface: node identity, static props, condition, slots. */\nfunction SettingsTab({\n node,\n manifest,\n}: {\n node: ComponentNode\n manifest: ComponentManifest | undefined\n}) {\n // Container layout props render in the dedicated Layout section (EC-162),\n // not as a second set of generic controls.\n const layoutManaged = new Set(layoutManagedPropNames(node))\n // EC-190: `blocks` props are slots (named child regions), edited via the page\n // tree / composition surface, not as a scalar control — so they show in the\n // Slots section, not Props.\n const props = manifest\n ? Object.entries(manifest.props).filter(\n ([name, spec]) => !layoutManaged.has(name) && spec.type !== 'blocks',\n )\n : []\n const slots = manifest ? manifestSlots(manifest) : []\n\n return (\n <>\n {/* EC-192: the editor-facing inspector no longer renders the raw node id or\n namespaced component id (the persona promise). The component's human name\n is in the panel header (with the manifest id in its `title`); the canvas\n and tree provide selection. */}\n {/* Contextual layout inspector for Stack/Grid containers (EC-162). */}\n <LayoutSection node={node} />\n\n {!manifest ? (\n <div className=\"p-3\">\n <Badge variant=\"error\">unknown component</Badge>\n <Text size=\"xs\" tone=\"muted\" className=\"mt-2\">\n No manifest is registered for this component, so its props can't be edited.\n </Text>\n </div>\n ) : (\n <InspectorSection title=\"Props\">\n {props.length === 0 ? (\n <Text size=\"xs\" tone=\"subtle\">\n No editable props.\n </Text>\n ) : (\n <Stack gap=\"4\">\n {props.map(([name, spec]) => (\n // Key by node id so per-node local control state never leaks\n // across a selection change to a same-named prop.\n <PropField key={`${node.id}:${name}`} node={node} name={name} spec={spec} />\n ))}\n </Stack>\n )}\n </InspectorSection>\n )}\n\n {slots.length > 0 ? (\n <InspectorSection title=\"Slots\">\n <Stack gap=\"1\">\n {slots.map((slot) => {\n const count = node.slots?.[slot.name]?.length ?? 0\n const missing = slot.required && count === 0\n return (\n <Row key={slot.name} gap=\"2\">\n <Text size=\"xs\" title={slot.name}>\n {humanizeName(slot.name)}\n </Text>\n {slot.required ? (\n <Badge variant={missing ? 'error' : 'outline'}>required</Badge>\n ) : null}\n <Spacer />\n <Text size=\"xs\" tone=\"subtle\" mono>\n {count}\n </Text>\n </Row>\n )\n })}\n </Stack>\n <Text size=\"xs\" tone=\"subtle\" className=\"mt-2\">\n Add or reorder slot children from the page tree.\n </Text>\n </InspectorSection>\n ) : null}\n </>\n )\n}\n\nfunction PropField({ node, name, spec }: { node: ComponentNode; name: string; spec: PropField }) {\n const value = node.props?.[name]\n\n return (\n <Field label={spec.form?.label ?? name} htmlFor={`prop-${node.id}-${name}`}>\n <StaticControl node={node} name={name} spec={spec} value={value} />\n </Field>\n )\n}\n\n/**\n * EC-190: the inspector control to render for a prop field-def. The old\n * `control.kind` vocabulary derives from the field `type` plus the optional\n * `form.control` presentation hint on `text` fields. Slice 3 unifies this with\n * the CMS `FieldControl`; for now it keeps the existing per-kind control set.\n */\ntype ControlKind =\n | 'text'\n | 'textarea'\n | 'number'\n | 'boolean'\n | 'select'\n | 'json'\n | 'color'\n | 'richtext'\n | 'asset'\n | 'object'\n | 'relation'\nfunction controlKind(spec: PropField): ControlKind {\n switch (spec.type) {\n case 'boolean':\n return 'boolean'\n case 'number':\n return 'number'\n case 'select':\n return 'select'\n case 'richText':\n return 'richtext'\n // EC-195: an `asset`-typed prop (e.g. the Image `src`) picks from the asset\n // library — never a raw asset id typed into a text box (the EC-192 persona).\n case 'asset':\n return 'asset'\n // EC-253: an `object`/repeater prop reuses the CMS repeater control below.\n case 'object':\n return 'object'\n // EC-262: a `relation`-typed prop (e.g. a ProductGrid's `category`) reuses the\n // CMS relation picker — never a text box that stringifies the ref to \"[object Object]\".\n case 'relation':\n return 'relation'\n case 'text': {\n const c = spec.form?.control\n if (c === 'textarea') return 'textarea'\n if (c === 'color') return 'color'\n if (c === 'json') return 'json'\n return 'text'\n }\n default:\n return 'text'\n }\n}\n\nfunction StaticControl({\n node,\n name,\n spec,\n value,\n}: {\n node: ComponentNode\n name: string\n spec: PropField\n value: PropValue | undefined\n}) {\n const builder = useBuilder()\n const current = value?.kind === 'static' ? value.value : (spec.default as JsonValue | undefined)\n const id = `prop-${node.id}-${name}`\n const coalesceKey = `${node.id}:${name}`\n\n function set(v: JsonValue) {\n builder.dispatch({ type: 'setProp', id: node.id, name, value: staticProp(v), coalesceKey })\n }\n function clear() {\n builder.dispatch({ type: 'removeProp', id: node.id, name })\n }\n\n // EC-190: validate with the prop's field-def value validator (was `spec.schema.safeParse`).\n const valid = current === undefined || fieldValueValidator({ ...spec, name } as FieldDef)(current)\n // EC-190: derive the editor control from the field `type` + optional `form.control`.\n const kind = controlKind(spec)\n // Select options live on the `select` field-def (was `spec.control.options`).\n const options: readonly SelectOption[] = spec.type === 'select' ? spec.options : []\n\n let control\n if (kind === 'boolean') {\n control = (\n <label className=\"flex h-8 items-center gap-2 text-sm\">\n <input\n id={id}\n type=\"checkbox\"\n checked={current === true}\n onChange={(e) => set(e.target.checked)}\n className=\"size-4 accent-primary\"\n />\n <Text size=\"sm\" tone=\"muted\">\n {current === true ? 'true' : 'false'}\n </Text>\n </label>\n )\n } else if (kind === 'select') {\n control = (\n <Select\n id={id}\n value={String(current ?? '')}\n onChange={(e) => {\n // EC-190: select option values are strings (field-def `options`).\n const opt = options.find((o) => o.value === e.target.value)\n set(opt ? opt.value : e.target.value)\n }}\n >\n {options.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label ?? opt.value}\n </option>\n ))}\n </Select>\n )\n } else if (kind === 'number') {\n control = <NumberControl id={id} current={current} onSet={set} onClear={clear} />\n } else if (kind === 'json') {\n control = <JsonControl id={id} current={current} onSet={set} />\n } else if (kind === 'color') {\n control = (\n <Row gap=\"2\">\n <input\n id={id}\n type=\"color\"\n value={\n typeof current === 'string' && /^#[0-9a-f]{6}$/i.test(current) ? current : '#000000'\n }\n onChange={(e) => set(e.target.value)}\n className=\"h-8 w-10 shrink-0 rounded-md border border-input bg-background\"\n />\n <Input\n value={typeof current === 'string' ? current : ''}\n onChange={(e) => set(e.target.value)}\n />\n </Row>\n )\n } else if (kind === 'textarea' || kind === 'richtext') {\n control = (\n <textarea\n id={id}\n rows={2}\n value={typeof current === 'string' ? current : ''}\n onChange={(e) => set(e.target.value)}\n className=\"flex w-full rounded-md border border-input bg-background px-2.5 py-1.5 text-sm text-foreground shadow-sm focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40 outline-none\"\n />\n )\n } else if (kind === 'asset' || kind === 'object' || kind === 'relation') {\n // EC-195/EC-253/EC-262: reuse the CMS field control. `asset` = library picker +\n // preview; `object` = the nested/repeater form; `relation` = the document picker\n // (resolves the ref to its title, instead of stringifying it to \"[object Object]\").\n // All edit a structured static prop value through the same shared control documents use.\n control = (\n <FieldControl\n id={id}\n field={{ ...spec, name } as FieldDef}\n value={current}\n onChange={(v) => (v === undefined || v === null ? clear() : set(v as JsonValue))}\n readOnly={false}\n />\n )\n } else {\n control = (\n <Input\n id={id}\n value={typeof current === 'string' ? current : String(current ?? '')}\n onChange={(e) => set(e.target.value)}\n />\n )\n }\n\n return (\n <Stack gap=\"1\">\n {control}\n {!valid ? (\n <Text size=\"xs\" className=\"text-destructive\">\n Value doesn't match the prop schema — the canvas falls back to the default.\n </Text>\n ) : null}\n </Stack>\n )\n}\n\n/** Number input with a local buffer: commits only finite values, never NaN; empty clears the prop. */\nfunction NumberControl({\n id,\n current,\n onSet,\n onClear,\n}: {\n id: string\n current: JsonValue | undefined\n onSet: (v: number) => void\n onClear: () => void\n}) {\n const [raw, setRaw] = useState(typeof current === 'number' ? String(current) : '')\n useEffect(() => {\n setRaw(typeof current === 'number' ? String(current) : '')\n }, [current])\n return (\n <Input\n id={id}\n type=\"number\"\n value={raw}\n onChange={(e) => {\n const v = e.target.value\n setRaw(v)\n if (v.trim() === '') {\n onClear()\n return\n }\n const n = Number(v)\n if (Number.isFinite(n)) onSet(n)\n }}\n />\n )\n}\n\n/** JSON textarea with a local buffer: commits only when the text parses; flags parse errors. */\nfunction JsonControl({\n id,\n current,\n onSet,\n}: {\n id: string\n current: JsonValue | undefined\n onSet: (v: JsonValue) => void\n}) {\n const [raw, setRaw] = useState(() => JSON.stringify(current ?? null))\n const [error, setError] = useState<string | null>(null)\n useEffect(() => {\n setRaw(JSON.stringify(current ?? null))\n setError(null)\n }, [current])\n return (\n <Stack gap=\"1\">\n <textarea\n id={id}\n rows={3}\n value={raw}\n onChange={(e) => {\n const v = e.target.value\n setRaw(v)\n try {\n onSet(JSON.parse(v) as JsonValue)\n setError(null)\n } catch {\n setError('Invalid JSON')\n }\n }}\n className=\"flex w-full rounded-md border border-input bg-background px-2.5 py-1.5 font-mono text-xs text-foreground shadow-sm focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40 outline-none\"\n />\n {error ? (\n <Text size=\"xs\" className=\"text-destructive\">\n {error}\n </Text>\n ) : null}\n </Stack>\n )\n}\n\n"]}
@@ -1,17 +1,28 @@
1
1
  import type { CmsDocument, CmsValidationIssue, DocumentRef, PublicationState } from '@elytracms/core/cms-core';
2
+ import type { FilterScalar } from '@elytracms/core/content';
2
3
  import type { OperationError, OperationResult } from '@elytracms/studio/lib/operations';
3
4
  /**
4
5
  * Bulk actions over a table selection (EC-169) — the pure half.
5
6
  *
6
7
  * Each selected document gets its OWN operations command
7
- * (`documents.publish` / `documents.unpublish` / `documents.delete`, EC-224),
8
- * awaited in selection order, and
8
+ * (`documents.publish` / `documents.unpublish` / `documents.delete`, EC-224;
9
+ * `documents.upsert` for a set-field, EC-247), awaited in selection order, and
9
10
  * its own per-item outcome. The report enumerates every failure with its
10
11
  * reason (validation gate, authorization denial, not-found, …) — a partial
11
12
  * failure is never silent and never collapsed into one toast. Session state is
12
13
  * only updated for items whose command succeeded.
13
14
  */
14
- export type BulkAction = 'publish' | 'unpublish' | 'delete';
15
+ export type BulkAction = 'publish' | 'unpublish' | 'delete' | 'set-field';
16
+ /**
17
+ * The payload of a `set-field` bulk action (EC-247): assign one filterable
18
+ * scalar field the same value across every selected document. The value is
19
+ * folded into each document the locale-aware way (`applyCellValue`) by the
20
+ * caller's `applySetField`/`dispatch`; this module only carries it.
21
+ */
22
+ export interface BulkSetField {
23
+ field: string;
24
+ value: FilterScalar;
25
+ }
15
26
  /** The outcome of one document's command within a bulk run. */
16
27
  export interface BulkItemOutcome {
17
28
  ref: DocumentRef;
@@ -25,6 +36,8 @@ export interface BulkItemOutcome {
25
36
  }
26
37
  export interface BulkReport {
27
38
  action: BulkAction;
39
+ /** Present for `set-field`: the field + value that was applied (report label). */
40
+ setField?: BulkSetField;
28
41
  results: BulkItemOutcome[];
29
42
  }
30
43
  export declare function bulkSummary(report: BulkReport): {
@@ -43,6 +56,11 @@ export interface BulkContext {
43
56
  applyState(ref: DocumentRef, state: PublicationState): void;
44
57
  /** Apply one succeeded delete to the session. */
45
58
  applyDelete(ref: DocumentRef): void;
59
+ /**
60
+ * Apply one succeeded set-field to the session (locale-aware fold). Required
61
+ * when the action is `set-field`; unused otherwise.
62
+ */
63
+ applySetField?(ref: DocumentRef, set: BulkSetField): void;
46
64
  /**
47
65
  * Dispatch the ONE operations command for an item. Absent in fixture-only
48
66
  * sessions (no project scope) — items then mutate the session directly.
@@ -52,9 +70,9 @@ export interface BulkContext {
52
70
  /**
53
71
  * Run one bulk action over a selection, item by item:
54
72
  * 1. publish gates on validation — an invalid document fails with its issues
55
- * and dispatches NOTHING,
73
+ * and dispatches NOTHING (a set-field is a draft edit, EC-225 — never gated),
56
74
  * 2. each remaining item dispatches exactly one operations command, awaited,
57
75
  * 3. only succeeded items mutate the session,
58
76
  * 4. the report carries one honest outcome per item, in selection order.
59
77
  */
60
- export declare function runBulkAction(context: BulkContext, collectionId: string, ids: readonly string[], action: BulkAction): Promise<BulkReport>;
78
+ export declare function runBulkAction(context: BulkContext, collectionId: string, ids: readonly string[], action: BulkAction, setField?: BulkSetField): Promise<BulkReport>;
@@ -19,12 +19,12 @@ const ACTION_STATE = {
19
19
  /**
20
20
  * Run one bulk action over a selection, item by item:
21
21
  * 1. publish gates on validation — an invalid document fails with its issues
22
- * and dispatches NOTHING,
22
+ * and dispatches NOTHING (a set-field is a draft edit, EC-225 — never gated),
23
23
  * 2. each remaining item dispatches exactly one operations command, awaited,
24
24
  * 3. only succeeded items mutate the session,
25
25
  * 4. the report carries one honest outcome per item, in selection order.
26
26
  */
27
- export async function runBulkAction(context, collectionId, ids, action) {
27
+ export async function runBulkAction(context, collectionId, ids, action, setField) {
28
28
  const results = [];
29
29
  for (const id of ids) {
30
30
  const ref = { collection: collectionId, id };
@@ -86,10 +86,14 @@ export async function runBulkAction(context, collectionId, ids, action) {
86
86
  }
87
87
  if (action === 'delete')
88
88
  context.applyDelete(ref);
89
+ else if (action === 'set-field') {
90
+ if (setField)
91
+ context.applySetField?.(ref, setField);
92
+ }
89
93
  else
90
94
  context.applyState(ref, ACTION_STATE[action]);
91
95
  results.push({ ref, title, ok: true });
92
96
  }
93
- return { action, results };
97
+ return { action, ...(setField ? { setField } : {}), results };
94
98
  }
95
99
  //# sourceMappingURL=bulk-actions.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"bulk-actions.js","sourceRoot":"","sources":["../../../src/components/cms/bulk-actions.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n CmsDocument,\n CmsValidationIssue,\n DocumentRef,\n PublicationState,\n} from '@elytracms/core/cms-core'\nimport type { OperationError, OperationResult } from '@elytracms/studio/lib/operations'\n\n/**\n * Bulk actions over a table selection (EC-169) — the pure half.\n *\n * Each selected document gets its OWN operations command\n * (`documents.publish` / `documents.unpublish` / `documents.delete`, EC-224),\n * awaited in selection order, and\n * its own per-item outcome. The report enumerates every failure with its\n * reason (validation gate, authorization denial, not-found, …) — a partial\n * failure is never silent and never collapsed into one toast. Session state is\n * only updated for items whose command succeeded.\n */\n\nexport type BulkAction = 'publish' | 'unpublish' | 'delete'\n\n/** The outcome of one document's command within a bulk run. */\nexport interface BulkItemOutcome {\n ref: DocumentRef\n /** Display title at dispatch time (deleted docs still get named in the report). */\n title: string\n ok: boolean\n /** Human-readable failure reason — present exactly when `ok` is false. */\n reason?: string\n /** The structured error behind the reason (operation result / validation gate). */\n error?: OperationError\n}\n\nexport interface BulkReport {\n action: BulkAction\n results: BulkItemOutcome[]\n}\n\nexport function bulkSummary(report: BulkReport): { succeeded: number; failed: number } {\n let succeeded = 0\n for (const result of report.results) if (result.ok) succeeded += 1\n return { succeeded, failed: report.results.length - succeeded }\n}\n\n/** The failure reason a report row shows: validation issues beat the envelope. */\nexport function bulkErrorReason(error: OperationError): string {\n if (error.code === 'validation' && error.issues.length > 0) {\n return error.issues.map((issue) => issue.message).join(' · ')\n }\n return error.message\n}\n\nexport interface BulkContext {\n /** Current session documents (lookup + validation input). */\n documents: readonly CmsDocument[]\n titleOf(ref: DocumentRef): string\n /** Full document validation — gates publish: invalid documents never dispatch. */\n validate(doc: CmsDocument): CmsValidationIssue[]\n /** Apply one succeeded state transition to the session. */\n applyState(ref: DocumentRef, state: PublicationState): void\n /** Apply one succeeded delete to the session. */\n applyDelete(ref: DocumentRef): void\n /**\n * Dispatch the ONE operations command for an item. Absent in fixture-only\n * sessions (no project scope) — items then mutate the session directly.\n */\n dispatch?: (action: BulkAction, ref: DocumentRef) => Promise<OperationResult<unknown>>\n}\n\nconst ACTION_STATE: Record<Exclude<BulkAction, 'delete'>, PublicationState> = {\n publish: 'published',\n unpublish: 'draft',\n}\n\n/**\n * Run one bulk action over a selection, item by item:\n * 1. publish gates on validation — an invalid document fails with its issues\n * and dispatches NOTHING,\n * 2. each remaining item dispatches exactly one operations command, awaited,\n * 3. only succeeded items mutate the session,\n * 4. the report carries one honest outcome per item, in selection order.\n */\nexport async function runBulkAction(\n context: BulkContext,\n collectionId: string,\n ids: readonly string[],\n action: BulkAction,\n): Promise<BulkReport> {\n const results: BulkItemOutcome[] = []\n\n for (const id of ids) {\n const ref: DocumentRef = { collection: collectionId, id }\n const title = context.titleOf(ref)\n const fail = (error: OperationError) =>\n results.push({ ref, title, ok: false, reason: bulkErrorReason(error), error })\n\n const doc = context.documents.find((d) => d.collection === collectionId && d.id === id)\n if (!doc) {\n fail({\n code: 'not-found',\n message: 'Document is no longer in the session.',\n entity: 'cms-document',\n key: `${collectionId}:${id}`,\n })\n continue\n }\n\n if (action === 'publish') {\n const errors = context.validate(doc).filter((issue) => issue.severity === 'error')\n if (errors.length > 0) {\n fail({\n code: 'validation',\n message: 'Document is not valid to publish.',\n issues: errors.map((issue) => ({\n code: issue.code,\n message: issue.message,\n path: [...issue.path],\n domain: 'cms',\n })),\n })\n continue\n }\n }\n\n if (context.dispatch) {\n let result: OperationResult<unknown>\n try {\n result = await context.dispatch(action, ref)\n } catch (cause) {\n // Operations never throw by contract; a broken transport still becomes\n // a visible per-item failure instead of an aborted bulk run.\n result = {\n ok: false,\n error: {\n code: 'unknown',\n message: cause instanceof Error ? cause.message : String(cause),\n },\n }\n }\n if (!result.ok) {\n // Idempotent delete: the document was never persisted (e.g. a draft\n // whose baseline upsert was rejected) or another editor already\n // deleted it. The desired end state — gone — holds, so removing it\n // from the session is the honest outcome, not a failure.\n const idempotentDelete = action === 'delete' && result.error.code === 'not-found'\n if (!idempotentDelete) {\n fail(result.error)\n continue\n }\n }\n }\n\n if (action === 'delete') context.applyDelete(ref)\n else context.applyState(ref, ACTION_STATE[action])\n results.push({ ref, title, ok: true })\n }\n\n return { action, results }\n}\n"]}
1
+ {"version":3,"file":"bulk-actions.js","sourceRoot":"","sources":["../../../src/components/cms/bulk-actions.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n CmsDocument,\n CmsValidationIssue,\n DocumentRef,\n PublicationState,\n} from '@elytracms/core/cms-core'\nimport type { FilterScalar } from '@elytracms/core/content'\nimport type { OperationError, OperationResult } from '@elytracms/studio/lib/operations'\n\n/**\n * Bulk actions over a table selection (EC-169) — the pure half.\n *\n * Each selected document gets its OWN operations command\n * (`documents.publish` / `documents.unpublish` / `documents.delete`, EC-224;\n * `documents.upsert` for a set-field, EC-247), awaited in selection order, and\n * its own per-item outcome. The report enumerates every failure with its\n * reason (validation gate, authorization denial, not-found, …) — a partial\n * failure is never silent and never collapsed into one toast. Session state is\n * only updated for items whose command succeeded.\n */\n\nexport type BulkAction = 'publish' | 'unpublish' | 'delete' | 'set-field'\n\n/**\n * The payload of a `set-field` bulk action (EC-247): assign one filterable\n * scalar field the same value across every selected document. The value is\n * folded into each document the locale-aware way (`applyCellValue`) by the\n * caller's `applySetField`/`dispatch`; this module only carries it.\n */\nexport interface BulkSetField {\n field: string\n value: FilterScalar\n}\n\n/** The outcome of one document's command within a bulk run. */\nexport interface BulkItemOutcome {\n ref: DocumentRef\n /** Display title at dispatch time (deleted docs still get named in the report). */\n title: string\n ok: boolean\n /** Human-readable failure reason — present exactly when `ok` is false. */\n reason?: string\n /** The structured error behind the reason (operation result / validation gate). */\n error?: OperationError\n}\n\nexport interface BulkReport {\n action: BulkAction\n /** Present for `set-field`: the field + value that was applied (report label). */\n setField?: BulkSetField\n results: BulkItemOutcome[]\n}\n\nexport function bulkSummary(report: BulkReport): { succeeded: number; failed: number } {\n let succeeded = 0\n for (const result of report.results) if (result.ok) succeeded += 1\n return { succeeded, failed: report.results.length - succeeded }\n}\n\n/** The failure reason a report row shows: validation issues beat the envelope. */\nexport function bulkErrorReason(error: OperationError): string {\n if (error.code === 'validation' && error.issues.length > 0) {\n return error.issues.map((issue) => issue.message).join(' · ')\n }\n return error.message\n}\n\nexport interface BulkContext {\n /** Current session documents (lookup + validation input). */\n documents: readonly CmsDocument[]\n titleOf(ref: DocumentRef): string\n /** Full document validation — gates publish: invalid documents never dispatch. */\n validate(doc: CmsDocument): CmsValidationIssue[]\n /** Apply one succeeded state transition to the session. */\n applyState(ref: DocumentRef, state: PublicationState): void\n /** Apply one succeeded delete to the session. */\n applyDelete(ref: DocumentRef): void\n /**\n * Apply one succeeded set-field to the session (locale-aware fold). Required\n * when the action is `set-field`; unused otherwise.\n */\n applySetField?(ref: DocumentRef, set: BulkSetField): void\n /**\n * Dispatch the ONE operations command for an item. Absent in fixture-only\n * sessions (no project scope) — items then mutate the session directly.\n */\n dispatch?: (action: BulkAction, ref: DocumentRef) => Promise<OperationResult<unknown>>\n}\n\nconst ACTION_STATE: Record<'publish' | 'unpublish', PublicationState> = {\n publish: 'published',\n unpublish: 'draft',\n}\n\n/**\n * Run one bulk action over a selection, item by item:\n * 1. publish gates on validation — an invalid document fails with its issues\n * and dispatches NOTHING (a set-field is a draft edit, EC-225 — never gated),\n * 2. each remaining item dispatches exactly one operations command, awaited,\n * 3. only succeeded items mutate the session,\n * 4. the report carries one honest outcome per item, in selection order.\n */\nexport async function runBulkAction(\n context: BulkContext,\n collectionId: string,\n ids: readonly string[],\n action: BulkAction,\n setField?: BulkSetField,\n): Promise<BulkReport> {\n const results: BulkItemOutcome[] = []\n\n for (const id of ids) {\n const ref: DocumentRef = { collection: collectionId, id }\n const title = context.titleOf(ref)\n const fail = (error: OperationError) =>\n results.push({ ref, title, ok: false, reason: bulkErrorReason(error), error })\n\n const doc = context.documents.find((d) => d.collection === collectionId && d.id === id)\n if (!doc) {\n fail({\n code: 'not-found',\n message: 'Document is no longer in the session.',\n entity: 'cms-document',\n key: `${collectionId}:${id}`,\n })\n continue\n }\n\n if (action === 'publish') {\n const errors = context.validate(doc).filter((issue) => issue.severity === 'error')\n if (errors.length > 0) {\n fail({\n code: 'validation',\n message: 'Document is not valid to publish.',\n issues: errors.map((issue) => ({\n code: issue.code,\n message: issue.message,\n path: [...issue.path],\n domain: 'cms',\n })),\n })\n continue\n }\n }\n\n if (context.dispatch) {\n let result: OperationResult<unknown>\n try {\n result = await context.dispatch(action, ref)\n } catch (cause) {\n // Operations never throw by contract; a broken transport still becomes\n // a visible per-item failure instead of an aborted bulk run.\n result = {\n ok: false,\n error: {\n code: 'unknown',\n message: cause instanceof Error ? cause.message : String(cause),\n },\n }\n }\n if (!result.ok) {\n // Idempotent delete: the document was never persisted (e.g. a draft\n // whose baseline upsert was rejected) or another editor already\n // deleted it. The desired end state — gone — holds, so removing it\n // from the session is the honest outcome, not a failure.\n const idempotentDelete = action === 'delete' && result.error.code === 'not-found'\n if (!idempotentDelete) {\n fail(result.error)\n continue\n }\n }\n }\n\n if (action === 'delete') context.applyDelete(ref)\n else if (action === 'set-field') {\n if (setField) context.applySetField?.(ref, setField)\n } else context.applyState(ref, ACTION_STATE[action])\n results.push({ ref, title, ok: true })\n }\n\n return { action, ...(setField ? { setField } : {}), results }\n}\n"]}
@@ -1,16 +1,32 @@
1
- import type { BulkAction, BulkReport } from './bulk-actions';
1
+ import type { CollectionDef } from '@elytracms/core/cms-core';
2
+ import type { FilterScalar } from '@elytracms/core/content';
3
+ import type { FilterableFieldOption } from '@elytracms/studio/lib/cms-views';
4
+ import type { BulkAction, BulkReport, BulkSetField } from './bulk-actions';
5
+ /**
6
+ * The scalar filterable fields a bulk set-field may target (EC-247): the
7
+ * schema's `filterable` fields whose type edits inline (text/number/boolean/
8
+ * select/date — `cellEditKind` 'inline'). Relation/asset (picker) and the
9
+ * publication-state pseudo field are excluded — publish/unpublish own state,
10
+ * and a raw relation id is not a safe bulk control.
11
+ */
12
+ export declare function setFieldOptions(collection: CollectionDef): FilterableFieldOption[];
13
+ /** A sensible starting value for a freshly chosen set-field target. */
14
+ export declare function initialSetValue(option: FilterableFieldOption | undefined): FilterScalar;
2
15
  /**
3
16
  * The bulk-action bar of the collection table (EC-169): appears with a row
4
- * selection, offers publish/unpublish/delete over it. Unpublish and delete are
5
- * confirmed by the owner through the reference-aware EC-173 dialog
6
- * (`ReferenceConfirmDialog`), which enumerates every inbound reference that
7
- * will break before anything dispatches — this bar only raises the action.
17
+ * selection, offers publish/unpublish/delete and a set-field assignment
18
+ * (EC-247) over it. Unpublish and delete are confirmed by the owner through the
19
+ * reference-aware EC-173 dialog (`ReferenceConfirmDialog`), which enumerates
20
+ * every inbound reference that will break before anything dispatches — this bar
21
+ * only raises the action. Set-field is a draft edit, so it applies directly.
8
22
  */
9
- export declare function BulkBar({ count, busy, onAction, onClear, }: {
23
+ export declare function BulkBar({ collection, count, busy, onAction, onSetField, onClear, }: {
24
+ collection: CollectionDef;
10
25
  /** Selected rows in the current view. */
11
26
  count: number;
12
27
  busy: boolean;
13
28
  onAction: (action: BulkAction) => void;
29
+ onSetField: (set: BulkSetField) => void;
14
30
  onClear: () => void;
15
31
  }): import("react").JSX.Element | null;
16
32
  /**
@@ -1,18 +1,54 @@
1
1
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
- import { CheckCircle2, CircleSlash, Trash2, X } from 'lucide-react';
3
- import { Badge, Button, Code, Row, Spacer, Stack, Text } from '@elytracms/studio/ui';
2
+ import { useState } from 'react';
3
+ import { Check, CheckCircle2, CircleSlash, PencilLine, Trash2, X } from 'lucide-react';
4
+ import { Badge, Button, Code, Row, Select, Spacer, Stack, Text } from '@elytracms/studio/ui';
5
+ import { filterableFieldOptions } from '@elytracms/studio/lib/cms-views';
4
6
  import { bulkSummary } from './bulk-actions';
7
+ import { cellEditKind } from './cell-edit';
8
+ import { ScalarFieldValueEditor } from './scalar-value-editor';
9
+ /**
10
+ * The scalar filterable fields a bulk set-field may target (EC-247): the
11
+ * schema's `filterable` fields whose type edits inline (text/number/boolean/
12
+ * select/date — `cellEditKind` 'inline'). Relation/asset (picker) and the
13
+ * publication-state pseudo field are excluded — publish/unpublish own state,
14
+ * and a raw relation id is not a safe bulk control.
15
+ */
16
+ export function setFieldOptions(collection) {
17
+ return filterableFieldOptions(collection).filter((option) => option.def !== null && cellEditKind(option.def, false) === 'inline');
18
+ }
19
+ /** A sensible starting value for a freshly chosen set-field target. */
20
+ export function initialSetValue(option) {
21
+ return option?.def?.type === 'boolean' ? false : '';
22
+ }
5
23
  /**
6
24
  * The bulk-action bar of the collection table (EC-169): appears with a row
7
- * selection, offers publish/unpublish/delete over it. Unpublish and delete are
8
- * confirmed by the owner through the reference-aware EC-173 dialog
9
- * (`ReferenceConfirmDialog`), which enumerates every inbound reference that
10
- * will break before anything dispatches — this bar only raises the action.
25
+ * selection, offers publish/unpublish/delete and a set-field assignment
26
+ * (EC-247) over it. Unpublish and delete are confirmed by the owner through the
27
+ * reference-aware EC-173 dialog (`ReferenceConfirmDialog`), which enumerates
28
+ * every inbound reference that will break before anything dispatches — this bar
29
+ * only raises the action. Set-field is a draft edit, so it applies directly.
11
30
  */
12
- export function BulkBar({ count, busy, onAction, onClear, }) {
31
+ export function BulkBar({ collection, count, busy, onAction, onSetField, onClear, }) {
32
+ const [setMode, setSetMode] = useState(false);
13
33
  if (count === 0)
14
34
  return null;
15
- return (_jsxs(Row, { gap: "2", className: "shrink-0 border-b border-border bg-muted/40 px-4 py-2 sm:px-6", "data-testid": "bulk-bar", children: [_jsxs(Text, { size: "sm", weight: "medium", "data-testid": "bulk-count", children: [count, " selected"] }), _jsxs(Button, { variant: "outline", size: "sm", disabled: busy, "data-testid": "bulk-publish", onClick: () => onAction('publish'), children: [_jsx(CheckCircle2, {}), " Publish"] }), _jsxs(Button, { variant: "outline", size: "sm", disabled: busy, "data-testid": "bulk-unpublish", onClick: () => onAction('unpublish'), children: [_jsx(CircleSlash, {}), " Unpublish"] }), _jsxs(Button, { variant: "destructive", size: "sm", disabled: busy, "data-testid": "bulk-delete", onClick: () => onAction('delete'), children: [_jsx(Trash2, {}), " Delete"] }), _jsx(Spacer, {}), _jsxs(Button, { variant: "ghost", size: "sm", disabled: busy, "data-testid": "bulk-clear", onClick: onClear, children: [_jsx(X, {}), " Clear selection"] })] }));
35
+ const options = setFieldOptions(collection);
36
+ const settable = options.length > 0;
37
+ return (_jsxs(Stack, { gap: "none", className: "shrink-0", "data-testid": "bulk-bar", children: [_jsxs(Row, { gap: "2", className: "border-b border-border bg-muted/40 px-4 py-2 sm:px-6", children: [_jsxs(Text, { size: "sm", weight: "medium", "data-testid": "bulk-count", children: [count, " selected"] }), _jsxs(Button, { variant: "outline", size: "sm", disabled: busy, "data-testid": "bulk-publish", onClick: () => onAction('publish'), children: [_jsx(CheckCircle2, {}), " Publish"] }), _jsxs(Button, { variant: "outline", size: "sm", disabled: busy, "data-testid": "bulk-unpublish", onClick: () => onAction('unpublish'), children: [_jsx(CircleSlash, {}), " Unpublish"] }), _jsxs(Button, { variant: "outline", size: "sm", disabled: busy || !settable, "aria-pressed": setMode, title: settable ? undefined : 'No filterable scalar fields to set.', "data-testid": "bulk-set-field", onClick: () => setSetMode((prev) => !prev), children: [_jsx(PencilLine, {}), " Set field"] }), _jsxs(Button, { variant: "destructive", size: "sm", disabled: busy, "data-testid": "bulk-delete", onClick: () => onAction('delete'), children: [_jsx(Trash2, {}), " Delete"] }), _jsx(Spacer, {}), _jsxs(Button, { variant: "ghost", size: "sm", disabled: busy, "data-testid": "bulk-clear", onClick: onClear, children: [_jsx(X, {}), " Clear selection"] })] }), setMode && settable ? (_jsx(SetFieldPanel, { options: options, busy: busy, onApply: (set) => {
38
+ onSetField(set);
39
+ setSetMode(false);
40
+ }, onCancel: () => setSetMode(false) })) : null] }));
41
+ }
42
+ /** The inline "set <field> to <value>" composer over the current selection. */
43
+ function SetFieldPanel({ options, busy, onApply, onCancel, }) {
44
+ const [field, setField] = useState(options[0].field);
45
+ const option = options.find((candidate) => candidate.field === field) ?? options[0];
46
+ const [value, setValue] = useState(() => initialSetValue(option));
47
+ function changeField(next) {
48
+ setField(next);
49
+ setValue(initialSetValue(options.find((candidate) => candidate.field === next)));
50
+ }
51
+ return (_jsxs(Row, { gap: "2", align: "center", className: "border-b border-border bg-muted/20 px-4 py-2 sm:px-6", "data-testid": "bulk-set-panel", children: [_jsx(Text, { size: "sm", tone: "muted", children: "Set" }), _jsx(Select, { "aria-label": "Set field", value: field, onChange: (event) => changeField(event.target.value), className: "h-7 w-auto py-0 text-xs", "data-testid": "bulk-set-field-select", children: options.map((candidate) => (_jsx("option", { value: candidate.field, children: candidate.label }, candidate.field))) }), _jsx(Text, { size: "sm", tone: "muted", children: "to" }), _jsx(ScalarFieldValueEditor, { option: option, value: value, ariaLabel: "Set value", testId: "bulk-set-value", onChange: setValue }, field), _jsx(Spacer, {}), _jsxs(Button, { variant: "outline", size: "sm", disabled: busy, "data-testid": "bulk-set-apply", onClick: () => onApply({ field, value }), children: [_jsx(Check, {}), " Apply to selection"] }), _jsx(Button, { variant: "ghost", size: "sm", disabled: busy, "data-testid": "bulk-set-cancel", onClick: onCancel, children: "Cancel" })] }));
16
52
  }
17
53
  /**
18
54
  * Per-item bulk results (EC-169): the completion summary plus every failure,
@@ -21,6 +57,7 @@ export function BulkBar({ count, busy, onAction, onClear, }) {
21
57
  export function BulkReportPanel({ report, onDismiss, }) {
22
58
  const { succeeded, failed } = bulkSummary(report);
23
59
  const failures = report.results.filter((result) => !result.ok);
24
- return (_jsxs(Stack, { gap: "1", className: "shrink-0 border-b border-border px-4 py-2 sm:px-6", "data-testid": "bulk-report", children: [_jsxs(Row, { gap: "2", children: [_jsxs(Text, { size: "sm", weight: "medium", "data-testid": "bulk-report-summary", children: ["Bulk ", report.action, ": ", succeeded, " succeeded", failed > 0 ? `, ${failed} failed` : ''] }), failed > 0 ? _jsxs(Badge, { variant: "error", children: [failed, " failed"] }) : _jsx(Badge, { variant: "success", children: "done" }), _jsx(Spacer, {}), _jsx(Button, { variant: "ghost", size: "sm", "data-testid": "bulk-report-dismiss", onClick: onDismiss, children: "Dismiss" })] }), failures.map((failure) => (_jsxs(Row, { gap: "2", "data-testid": `bulk-failure-${failure.ref.id}`, children: [_jsx(Text, { size: "xs", className: "text-destructive", children: failure.title }), _jsx(Code, { tone: "muted", children: failure.ref.id }), _jsx(Text, { size: "xs", tone: "muted", children: failure.reason })] }, failure.ref.id)))] }));
60
+ const actionLabel = report.action === 'set-field' && report.setField ? `set ${report.setField.field}` : report.action;
61
+ return (_jsxs(Stack, { gap: "1", className: "shrink-0 border-b border-border px-4 py-2 sm:px-6", "data-testid": "bulk-report", children: [_jsxs(Row, { gap: "2", children: [_jsxs(Text, { size: "sm", weight: "medium", "data-testid": "bulk-report-summary", children: ["Bulk ", actionLabel, ": ", succeeded, " succeeded", failed > 0 ? `, ${failed} failed` : ''] }), failed > 0 ? _jsxs(Badge, { variant: "error", children: [failed, " failed"] }) : _jsx(Badge, { variant: "success", children: "done" }), _jsx(Spacer, {}), _jsx(Button, { variant: "ghost", size: "sm", "data-testid": "bulk-report-dismiss", onClick: onDismiss, children: "Dismiss" })] }), failures.map((failure) => (_jsxs(Row, { gap: "2", "data-testid": `bulk-failure-${failure.ref.id}`, children: [_jsx(Text, { size: "xs", className: "text-destructive", children: failure.title }), _jsx(Code, { tone: "muted", children: failure.ref.id }), _jsx(Text, { size: "xs", tone: "muted", children: failure.reason })] }, failure.ref.id)))] }));
25
62
  }
26
63
  //# sourceMappingURL=bulk-bar.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"bulk-bar.js","sourceRoot":"","sources":["../../../src/components/cms/bulk-bar.tsx"],"names":[],"mappings":"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","sourcesContent":["import { CheckCircle2, CircleSlash, Trash2, X } from 'lucide-react'\nimport { Badge, Button, Code, Row, Spacer, Stack, Text } from '@elytracms/studio/ui'\nimport { bulkSummary } from './bulk-actions'\nimport type { BulkAction, BulkReport } from './bulk-actions'\n\n/**\n * The bulk-action bar of the collection table (EC-169): appears with a row\n * selection, offers publish/unpublish/delete over it. Unpublish and delete are\n * confirmed by the owner through the reference-aware EC-173 dialog\n * (`ReferenceConfirmDialog`), which enumerates every inbound reference that\n * will break before anything dispatches — this bar only raises the action.\n */\nexport function BulkBar({\n count,\n busy,\n onAction,\n onClear,\n}: {\n /** Selected rows in the current view. */\n count: number\n busy: boolean\n onAction: (action: BulkAction) => void\n onClear: () => void\n}) {\n if (count === 0) return null\n\n return (\n <Row gap=\"2\" className=\"shrink-0 border-b border-border bg-muted/40 px-4 py-2 sm:px-6\" data-testid=\"bulk-bar\">\n <Text size=\"sm\" weight=\"medium\" data-testid=\"bulk-count\">\n {count} selected\n </Text>\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled={busy}\n data-testid=\"bulk-publish\"\n onClick={() => onAction('publish')}\n >\n <CheckCircle2 /> Publish\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled={busy}\n data-testid=\"bulk-unpublish\"\n onClick={() => onAction('unpublish')}\n >\n <CircleSlash /> Unpublish\n </Button>\n <Button\n variant=\"destructive\"\n size=\"sm\"\n disabled={busy}\n data-testid=\"bulk-delete\"\n onClick={() => onAction('delete')}\n >\n <Trash2 /> Delete\n </Button>\n <Spacer />\n <Button variant=\"ghost\" size=\"sm\" disabled={busy} data-testid=\"bulk-clear\" onClick={onClear}>\n <X /> Clear selection\n </Button>\n </Row>\n )\n}\n\n/**\n * Per-item bulk results (EC-169): the completion summary plus every failure,\n * named and with its reason — a partial failure is enumerated, never silent.\n */\nexport function BulkReportPanel({\n report,\n onDismiss,\n}: {\n report: BulkReport\n onDismiss: () => void\n}) {\n const { succeeded, failed } = bulkSummary(report)\n const failures = report.results.filter((result) => !result.ok)\n\n return (\n <Stack gap=\"1\" className=\"shrink-0 border-b border-border px-4 py-2 sm:px-6\" data-testid=\"bulk-report\">\n <Row gap=\"2\">\n <Text size=\"sm\" weight=\"medium\" data-testid=\"bulk-report-summary\">\n Bulk {report.action}: {succeeded} succeeded\n {failed > 0 ? `, ${failed} failed` : ''}\n </Text>\n {failed > 0 ? <Badge variant=\"error\">{failed} failed</Badge> : <Badge variant=\"success\">done</Badge>}\n <Spacer />\n <Button variant=\"ghost\" size=\"sm\" data-testid=\"bulk-report-dismiss\" onClick={onDismiss}>\n Dismiss\n </Button>\n </Row>\n {failures.map((failure) => (\n <Row gap=\"2\" key={failure.ref.id} data-testid={`bulk-failure-${failure.ref.id}`}>\n <Text size=\"xs\" className=\"text-destructive\">\n {failure.title}\n </Text>\n <Code tone=\"muted\">{failure.ref.id}</Code>\n <Text size=\"xs\" tone=\"muted\">\n {failure.reason}\n </Text>\n </Row>\n ))}\n </Stack>\n )\n}\n"]}
1
+ {"version":3,"file":"bulk-bar.js","sourceRoot":"","sources":["../../../src/components/cms/bulk-bar.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAChC,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;AAGtF,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AAC5F,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAA;AAExE,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAE5C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAE9D;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,UAAyB;IACvD,OAAO,sBAAsB,CAAC,UAAU,CAAC,CAAC,MAAM,CAC9C,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,KAAK,IAAI,IAAI,YAAY,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,KAAK,QAAQ,CAChF,CAAA;AACH,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,eAAe,CAAC,MAAyC;IACvE,OAAO,MAAM,EAAE,GAAG,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAA;AACrD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,OAAO,CAAC,EACtB,UAAU,EACV,KAAK,EACL,IAAI,EACJ,QAAQ,EACR,UAAU,EACV,OAAO,GASR;IACC,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC7C,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IAE5B,MAAM,OAAO,GAAG,eAAe,CAAC,UAAU,CAAC,CAAA;IAC3C,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,CAAA;IAEnC,OAAO,CACL,MAAC,KAAK,IAAC,GAAG,EAAC,MAAM,EAAC,SAAS,EAAC,UAAU,iBAAa,UAAU,aAC3D,MAAC,GAAG,IAAC,GAAG,EAAC,GAAG,EAAC,SAAS,EAAC,sDAAsD,aAC3E,MAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,MAAM,EAAC,QAAQ,iBAAa,YAAY,aACrD,KAAK,iBACD,EACP,MAAC,MAAM,IACL,OAAO,EAAC,SAAS,EACjB,IAAI,EAAC,IAAI,EACT,QAAQ,EAAE,IAAI,iBACF,cAAc,EAC1B,OAAO,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,aAElC,KAAC,YAAY,KAAG,gBACT,EACT,MAAC,MAAM,IACL,OAAO,EAAC,SAAS,EACjB,IAAI,EAAC,IAAI,EACT,QAAQ,EAAE,IAAI,iBACF,gBAAgB,EAC5B,OAAO,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC,aAEpC,KAAC,WAAW,KAAG,kBACR,EACT,MAAC,MAAM,IACL,OAAO,EAAC,SAAS,EACjB,IAAI,EAAC,IAAI,EACT,QAAQ,EAAE,IAAI,IAAI,CAAC,QAAQ,kBACb,OAAO,EACrB,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,qCAAqC,iBACvD,gBAAgB,EAC5B,OAAO,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,aAE1C,KAAC,UAAU,KAAG,kBACP,EACT,MAAC,MAAM,IACL,OAAO,EAAC,aAAa,EACrB,IAAI,EAAC,IAAI,EACT,QAAQ,EAAE,IAAI,iBACF,aAAa,EACzB,OAAO,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,aAEjC,KAAC,MAAM,KAAG,eACH,EACT,KAAC,MAAM,KAAG,EACV,MAAC,MAAM,IAAC,OAAO,EAAC,OAAO,EAAC,IAAI,EAAC,IAAI,EAAC,QAAQ,EAAE,IAAI,iBAAc,YAAY,EAAC,OAAO,EAAE,OAAO,aACzF,KAAC,CAAC,KAAG,wBACE,IACL,EACL,OAAO,IAAI,QAAQ,CAAC,CAAC,CAAC,CACrB,KAAC,aAAa,IACZ,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;oBACf,UAAU,CAAC,GAAG,CAAC,CAAA;oBACf,UAAU,CAAC,KAAK,CAAC,CAAA;gBACnB,CAAC,EACD,QAAQ,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,GACjC,CACH,CAAC,CAAC,CAAC,IAAI,IACF,CACT,CAAA;AACH,CAAC;AAED,+EAA+E;AAC/E,SAAS,aAAa,CAAC,EACrB,OAAO,EACP,IAAI,EACJ,OAAO,EACP,QAAQ,GAMT;IACC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC,CAAA;IACrD,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,KAAK,KAAK,CAAC,IAAI,OAAO,CAAC,CAAC,CAAE,CAAA;IACpF,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAe,GAAG,EAAE,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAA;IAE/E,SAAS,WAAW,CAAC,IAAY;QAC/B,QAAQ,CAAC,IAAI,CAAC,CAAA;QACd,QAAQ,CAAC,eAAe,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,CAAA;IAClF,CAAC;IAED,OAAO,CACL,MAAC,GAAG,IACF,GAAG,EAAC,GAAG,EACP,KAAK,EAAC,QAAQ,EACd,SAAS,EAAC,sDAAsD,iBACpD,gBAAgB,aAE5B,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,oBAErB,EACP,KAAC,MAAM,kBACM,WAAW,EACtB,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,EACpD,SAAS,EAAC,yBAAyB,iBACvB,uBAAuB,YAElC,OAAO,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAC1B,iBAA8B,KAAK,EAAE,SAAS,CAAC,KAAK,YACjD,SAAS,CAAC,KAAK,IADL,SAAS,CAAC,KAAK,CAEnB,CACV,CAAC,GACK,EACT,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,mBAErB,EACP,KAAC,sBAAsB,IAErB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,KAAK,EACZ,SAAS,EAAC,WAAW,EACrB,MAAM,EAAC,gBAAgB,EACvB,QAAQ,EAAE,QAAQ,IALb,KAAK,CAMV,EACF,KAAC,MAAM,KAAG,EACV,MAAC,MAAM,IACL,OAAO,EAAC,SAAS,EACjB,IAAI,EAAC,IAAI,EACT,QAAQ,EAAE,IAAI,iBACF,gBAAgB,EAC5B,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,aAExC,KAAC,KAAK,KAAG,2BACF,EACT,KAAC,MAAM,IAAC,OAAO,EAAC,OAAO,EAAC,IAAI,EAAC,IAAI,EAAC,QAAQ,EAAE,IAAI,iBAAc,iBAAiB,EAAC,OAAO,EAAE,QAAQ,uBAExF,IACL,CACP,CAAA;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,EAC9B,MAAM,EACN,SAAS,GAIV;IACC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,CAAA;IACjD,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;IAC9D,MAAM,WAAW,GACf,MAAM,CAAC,MAAM,KAAK,WAAW,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAA;IAEnG,OAAO,CACL,MAAC,KAAK,IAAC,GAAG,EAAC,GAAG,EAAC,SAAS,EAAC,mDAAmD,iBAAa,aAAa,aACpG,MAAC,GAAG,IAAC,GAAG,EAAC,GAAG,aACV,MAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,MAAM,EAAC,QAAQ,iBAAa,qBAAqB,sBACzD,WAAW,QAAI,SAAS,gBAC7B,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,MAAM,SAAS,CAAC,CAAC,CAAC,EAAE,IAClC,EACN,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAC,KAAK,IAAC,OAAO,EAAC,OAAO,aAAE,MAAM,eAAgB,CAAC,CAAC,CAAC,KAAC,KAAK,IAAC,OAAO,EAAC,SAAS,qBAAa,EACpG,KAAC,MAAM,KAAG,EACV,KAAC,MAAM,IAAC,OAAO,EAAC,OAAO,EAAC,IAAI,EAAC,IAAI,iBAAa,qBAAqB,EAAC,OAAO,EAAE,SAAS,wBAE7E,IACL,EACL,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CACzB,MAAC,GAAG,IAAC,GAAG,EAAC,GAAG,iBAAmC,gBAAgB,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,aAC7E,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,SAAS,EAAC,kBAAkB,YACzC,OAAO,CAAC,KAAK,GACT,EACP,KAAC,IAAI,IAAC,IAAI,EAAC,OAAO,YAAE,OAAO,CAAC,GAAG,CAAC,EAAE,GAAQ,EAC1C,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,YACzB,OAAO,CAAC,MAAM,GACV,KAPS,OAAO,CAAC,GAAG,CAAC,EAAE,CAQ1B,CACP,CAAC,IACI,CACT,CAAA;AACH,CAAC","sourcesContent":["import { useState } from 'react'\nimport { Check, CheckCircle2, CircleSlash, PencilLine, Trash2, X } from 'lucide-react'\nimport type { CollectionDef } from '@elytracms/core/cms-core'\nimport type { FilterScalar } from '@elytracms/core/content'\nimport { Badge, Button, Code, Row, Select, Spacer, Stack, Text } from '@elytracms/studio/ui'\nimport { filterableFieldOptions } from '@elytracms/studio/lib/cms-views'\nimport type { FilterableFieldOption } from '@elytracms/studio/lib/cms-views'\nimport { bulkSummary } from './bulk-actions'\nimport type { BulkAction, BulkReport, BulkSetField } from './bulk-actions'\nimport { cellEditKind } from './cell-edit'\nimport { ScalarFieldValueEditor } from './scalar-value-editor'\n\n/**\n * The scalar filterable fields a bulk set-field may target (EC-247): the\n * schema's `filterable` fields whose type edits inline (text/number/boolean/\n * select/date — `cellEditKind` 'inline'). Relation/asset (picker) and the\n * publication-state pseudo field are excluded — publish/unpublish own state,\n * and a raw relation id is not a safe bulk control.\n */\nexport function setFieldOptions(collection: CollectionDef): FilterableFieldOption[] {\n return filterableFieldOptions(collection).filter(\n (option) => option.def !== null && cellEditKind(option.def, false) === 'inline',\n )\n}\n\n/** A sensible starting value for a freshly chosen set-field target. */\nexport function initialSetValue(option: FilterableFieldOption | undefined): FilterScalar {\n return option?.def?.type === 'boolean' ? false : ''\n}\n\n/**\n * The bulk-action bar of the collection table (EC-169): appears with a row\n * selection, offers publish/unpublish/delete and a set-field assignment\n * (EC-247) over it. Unpublish and delete are confirmed by the owner through the\n * reference-aware EC-173 dialog (`ReferenceConfirmDialog`), which enumerates\n * every inbound reference that will break before anything dispatches — this bar\n * only raises the action. Set-field is a draft edit, so it applies directly.\n */\nexport function BulkBar({\n collection,\n count,\n busy,\n onAction,\n onSetField,\n onClear,\n}: {\n collection: CollectionDef\n /** Selected rows in the current view. */\n count: number\n busy: boolean\n onAction: (action: BulkAction) => void\n onSetField: (set: BulkSetField) => void\n onClear: () => void\n}) {\n const [setMode, setSetMode] = useState(false)\n if (count === 0) return null\n\n const options = setFieldOptions(collection)\n const settable = options.length > 0\n\n return (\n <Stack gap=\"none\" className=\"shrink-0\" data-testid=\"bulk-bar\">\n <Row gap=\"2\" className=\"border-b border-border bg-muted/40 px-4 py-2 sm:px-6\">\n <Text size=\"sm\" weight=\"medium\" data-testid=\"bulk-count\">\n {count} selected\n </Text>\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled={busy}\n data-testid=\"bulk-publish\"\n onClick={() => onAction('publish')}\n >\n <CheckCircle2 /> Publish\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled={busy}\n data-testid=\"bulk-unpublish\"\n onClick={() => onAction('unpublish')}\n >\n <CircleSlash /> Unpublish\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled={busy || !settable}\n aria-pressed={setMode}\n title={settable ? undefined : 'No filterable scalar fields to set.'}\n data-testid=\"bulk-set-field\"\n onClick={() => setSetMode((prev) => !prev)}\n >\n <PencilLine /> Set field\n </Button>\n <Button\n variant=\"destructive\"\n size=\"sm\"\n disabled={busy}\n data-testid=\"bulk-delete\"\n onClick={() => onAction('delete')}\n >\n <Trash2 /> Delete\n </Button>\n <Spacer />\n <Button variant=\"ghost\" size=\"sm\" disabled={busy} data-testid=\"bulk-clear\" onClick={onClear}>\n <X /> Clear selection\n </Button>\n </Row>\n {setMode && settable ? (\n <SetFieldPanel\n options={options}\n busy={busy}\n onApply={(set) => {\n onSetField(set)\n setSetMode(false)\n }}\n onCancel={() => setSetMode(false)}\n />\n ) : null}\n </Stack>\n )\n}\n\n/** The inline \"set <field> to <value>\" composer over the current selection. */\nfunction SetFieldPanel({\n options,\n busy,\n onApply,\n onCancel,\n}: {\n options: FilterableFieldOption[]\n busy: boolean\n onApply: (set: BulkSetField) => void\n onCancel: () => void\n}) {\n const [field, setField] = useState(options[0]!.field)\n const option = options.find((candidate) => candidate.field === field) ?? options[0]!\n const [value, setValue] = useState<FilterScalar>(() => initialSetValue(option))\n\n function changeField(next: string) {\n setField(next)\n setValue(initialSetValue(options.find((candidate) => candidate.field === next)))\n }\n\n return (\n <Row\n gap=\"2\"\n align=\"center\"\n className=\"border-b border-border bg-muted/20 px-4 py-2 sm:px-6\"\n data-testid=\"bulk-set-panel\"\n >\n <Text size=\"sm\" tone=\"muted\">\n Set\n </Text>\n <Select\n aria-label=\"Set field\"\n value={field}\n onChange={(event) => changeField(event.target.value)}\n className=\"h-7 w-auto py-0 text-xs\"\n data-testid=\"bulk-set-field-select\"\n >\n {options.map((candidate) => (\n <option key={candidate.field} value={candidate.field}>\n {candidate.label}\n </option>\n ))}\n </Select>\n <Text size=\"sm\" tone=\"muted\">\n to\n </Text>\n <ScalarFieldValueEditor\n key={field}\n option={option}\n value={value}\n ariaLabel=\"Set value\"\n testId=\"bulk-set-value\"\n onChange={setValue}\n />\n <Spacer />\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled={busy}\n data-testid=\"bulk-set-apply\"\n onClick={() => onApply({ field, value })}\n >\n <Check /> Apply to selection\n </Button>\n <Button variant=\"ghost\" size=\"sm\" disabled={busy} data-testid=\"bulk-set-cancel\" onClick={onCancel}>\n Cancel\n </Button>\n </Row>\n )\n}\n\n/**\n * Per-item bulk results (EC-169): the completion summary plus every failure,\n * named and with its reason — a partial failure is enumerated, never silent.\n */\nexport function BulkReportPanel({\n report,\n onDismiss,\n}: {\n report: BulkReport\n onDismiss: () => void\n}) {\n const { succeeded, failed } = bulkSummary(report)\n const failures = report.results.filter((result) => !result.ok)\n const actionLabel =\n report.action === 'set-field' && report.setField ? `set ${report.setField.field}` : report.action\n\n return (\n <Stack gap=\"1\" className=\"shrink-0 border-b border-border px-4 py-2 sm:px-6\" data-testid=\"bulk-report\">\n <Row gap=\"2\">\n <Text size=\"sm\" weight=\"medium\" data-testid=\"bulk-report-summary\">\n Bulk {actionLabel}: {succeeded} succeeded\n {failed > 0 ? `, ${failed} failed` : ''}\n </Text>\n {failed > 0 ? <Badge variant=\"error\">{failed} failed</Badge> : <Badge variant=\"success\">done</Badge>}\n <Spacer />\n <Button variant=\"ghost\" size=\"sm\" data-testid=\"bulk-report-dismiss\" onClick={onDismiss}>\n Dismiss\n </Button>\n </Row>\n {failures.map((failure) => (\n <Row gap=\"2\" key={failure.ref.id} data-testid={`bulk-failure-${failure.ref.id}`}>\n <Text size=\"xs\" className=\"text-destructive\">\n {failure.title}\n </Text>\n <Code tone=\"muted\">{failure.ref.id}</Code>\n <Text size=\"xs\" tone=\"muted\">\n {failure.reason}\n </Text>\n </Row>\n ))}\n </Stack>\n )\n}\n"]}
@@ -27,7 +27,3 @@ export declare function fallbackTitle(fallback: DisplayedFallback): string;
27
27
  * and legacy bare `{ collection, id }` via the shared tolerant reader.
28
28
  */
29
29
  export declare function toRefArray(value: unknown): DocumentRef[];
30
- /** Render a small mono code for the raw stored value (debug/primary-cell hint). */
31
- export declare function RawValue({ value }: {
32
- value: unknown;
33
- }): import("react").JSX.Element;
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Boxes, Check, Image, Link2, Minus } from 'lucide-react';
3
3
  import { readAssetId, readDocumentRef } from '@elytracms/core/cms-core';
4
4
  import { blockRootsOf } from '@elytracms/core/project-graph';
5
- import { Badge, Code, Text } from '@elytracms/studio/ui';
5
+ import { Badge, Text } from '@elytracms/studio/ui';
6
6
  import { localeChipLabel, resolveDisplayedValue } from '@elytracms/studio/lib/locale';
7
7
  import { useOptionalAssets } from '../assets/assets-store';
8
8
  import { useCms } from './cms-store';
@@ -121,8 +121,4 @@ export function toRefArray(value) {
121
121
  function stripHtml(html) {
122
122
  return html.replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim();
123
123
  }
124
- /** Render a small mono code for the raw stored value (debug/primary-cell hint). */
125
- export function RawValue({ value }) {
126
- return _jsx(Code, { tone: "muted", children: typeof value === 'string' ? value : JSON.stringify(value) });
127
- }
128
124
  //# sourceMappingURL=cell.js.map