@agent-native/core 0.39.1 → 0.40.0

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 (208) hide show
  1. package/README.md +1 -1
  2. package/dist/action.js +12 -0
  3. package/dist/action.js.map +1 -1
  4. package/dist/cli/create.d.ts.map +1 -1
  5. package/dist/cli/create.js +5 -1
  6. package/dist/cli/create.js.map +1 -1
  7. package/dist/cli/index.js +1 -1
  8. package/dist/cli/index.js.map +1 -1
  9. package/dist/cli/skills.d.ts +6 -6
  10. package/dist/cli/skills.d.ts.map +1 -1
  11. package/dist/cli/skills.js +936 -1167
  12. package/dist/cli/skills.js.map +1 -1
  13. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  14. package/dist/client/MultiTabAssistantChat.js +2 -5
  15. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  16. package/dist/client/NewWorkspaceAppFlow.js +1 -1
  17. package/dist/client/NewWorkspaceAppFlow.js.map +1 -1
  18. package/dist/client/blocks/AiEditableField.d.ts +8 -0
  19. package/dist/client/blocks/AiEditableField.d.ts.map +1 -0
  20. package/dist/client/blocks/AiEditableField.js +10 -0
  21. package/dist/client/blocks/AiEditableField.js.map +1 -0
  22. package/dist/client/blocks/BlockView.d.ts +3 -3
  23. package/dist/client/blocks/BlockView.d.ts.map +1 -1
  24. package/dist/client/blocks/BlockView.js +15 -3
  25. package/dist/client/blocks/BlockView.js.map +1 -1
  26. package/dist/client/blocks/SchemaBlockEditor.js +2 -2
  27. package/dist/client/blocks/SchemaBlockEditor.js.map +1 -1
  28. package/dist/client/blocks/index.d.ts +5 -2
  29. package/dist/client/blocks/index.d.ts.map +1 -1
  30. package/dist/client/blocks/index.js +6 -3
  31. package/dist/client/blocks/index.js.map +1 -1
  32. package/dist/client/blocks/library/ApiEndpointBlock.d.ts.map +1 -1
  33. package/dist/client/blocks/library/ApiEndpointBlock.js +20 -6
  34. package/dist/client/blocks/library/ApiEndpointBlock.js.map +1 -1
  35. package/dist/client/blocks/library/DiffBlock.d.ts +29 -0
  36. package/dist/client/blocks/library/DiffBlock.d.ts.map +1 -1
  37. package/dist/client/blocks/library/DiffBlock.js +190 -30
  38. package/dist/client/blocks/library/DiffBlock.js.map +1 -1
  39. package/dist/client/blocks/library/FileTreeBlock.d.ts.map +1 -1
  40. package/dist/client/blocks/library/FileTreeBlock.js +46 -7
  41. package/dist/client/blocks/library/FileTreeBlock.js.map +1 -1
  42. package/dist/client/blocks/library/HighlightedCode.d.ts +10 -0
  43. package/dist/client/blocks/library/HighlightedCode.d.ts.map +1 -0
  44. package/dist/client/blocks/library/HighlightedCode.js +92 -0
  45. package/dist/client/blocks/library/HighlightedCode.js.map +1 -0
  46. package/dist/client/blocks/library/JsonExplorerBlock.d.ts +9 -4
  47. package/dist/client/blocks/library/JsonExplorerBlock.d.ts.map +1 -1
  48. package/dist/client/blocks/library/JsonExplorerBlock.js +66 -30
  49. package/dist/client/blocks/library/JsonExplorerBlock.js.map +1 -1
  50. package/dist/client/blocks/library/MermaidBlock.d.ts.map +1 -1
  51. package/dist/client/blocks/library/MermaidBlock.js +73 -44
  52. package/dist/client/blocks/library/MermaidBlock.js.map +1 -1
  53. package/dist/client/blocks/library/OpenApiSpecBlock.d.ts.map +1 -1
  54. package/dist/client/blocks/library/OpenApiSpecBlock.js +3 -2
  55. package/dist/client/blocks/library/OpenApiSpecBlock.js.map +1 -1
  56. package/dist/client/blocks/library/checklist.d.ts.map +1 -1
  57. package/dist/client/blocks/library/checklist.js +1 -0
  58. package/dist/client/blocks/library/checklist.js.map +1 -1
  59. package/dist/client/blocks/library/code-tabs.d.ts.map +1 -1
  60. package/dist/client/blocks/library/code-tabs.js +183 -102
  61. package/dist/client/blocks/library/code-tabs.js.map +1 -1
  62. package/dist/client/blocks/library/columns.config.d.ts +60 -0
  63. package/dist/client/blocks/library/columns.config.d.ts.map +1 -0
  64. package/dist/client/blocks/library/columns.config.js +37 -0
  65. package/dist/client/blocks/library/columns.config.js.map +1 -0
  66. package/dist/client/blocks/library/columns.d.ts +25 -0
  67. package/dist/client/blocks/library/columns.d.ts.map +1 -0
  68. package/dist/client/blocks/library/columns.js +199 -0
  69. package/dist/client/blocks/library/columns.js.map +1 -0
  70. package/dist/client/blocks/library/dev-doc-ui.d.ts +2 -1
  71. package/dist/client/blocks/library/dev-doc-ui.d.ts.map +1 -1
  72. package/dist/client/blocks/library/dev-doc-ui.js +2 -1
  73. package/dist/client/blocks/library/dev-doc-ui.js.map +1 -1
  74. package/dist/client/blocks/library/html.d.ts +1 -1
  75. package/dist/client/blocks/library/html.d.ts.map +1 -1
  76. package/dist/client/blocks/library/html.js +34 -4
  77. package/dist/client/blocks/library/html.js.map +1 -1
  78. package/dist/client/blocks/library/json-explorer.config.d.ts +3 -1
  79. package/dist/client/blocks/library/json-explorer.config.d.ts.map +1 -1
  80. package/dist/client/blocks/library/json-explorer.config.js +30 -1
  81. package/dist/client/blocks/library/json-explorer.config.js.map +1 -1
  82. package/dist/client/blocks/library/server-specs.d.ts.map +1 -1
  83. package/dist/client/blocks/library/server-specs.js +13 -3
  84. package/dist/client/blocks/library/server-specs.js.map +1 -1
  85. package/dist/client/blocks/library/specs.d.ts +4 -4
  86. package/dist/client/blocks/library/specs.d.ts.map +1 -1
  87. package/dist/client/blocks/library/specs.js +21 -16
  88. package/dist/client/blocks/library/specs.js.map +1 -1
  89. package/dist/client/blocks/library/table.config.d.ts +3 -0
  90. package/dist/client/blocks/library/table.config.d.ts.map +1 -1
  91. package/dist/client/blocks/library/table.config.js +13 -1
  92. package/dist/client/blocks/library/table.config.js.map +1 -1
  93. package/dist/client/blocks/library/table.d.ts.map +1 -1
  94. package/dist/client/blocks/library/table.js +90 -9
  95. package/dist/client/blocks/library/table.js.map +1 -1
  96. package/dist/client/blocks/library/tabs.config.d.ts +16 -8
  97. package/dist/client/blocks/library/tabs.config.d.ts.map +1 -1
  98. package/dist/client/blocks/library/tabs.config.js +10 -4
  99. package/dist/client/blocks/library/tabs.config.js.map +1 -1
  100. package/dist/client/blocks/library/tabs.d.ts.map +1 -1
  101. package/dist/client/blocks/library/tabs.js +146 -21
  102. package/dist/client/blocks/library/tabs.js.map +1 -1
  103. package/dist/client/blocks/server.d.ts +2 -1
  104. package/dist/client/blocks/server.d.ts.map +1 -1
  105. package/dist/client/blocks/server.js +1 -0
  106. package/dist/client/blocks/server.js.map +1 -1
  107. package/dist/client/blocks/types.d.ts +99 -9
  108. package/dist/client/blocks/types.d.ts.map +1 -1
  109. package/dist/client/blocks/types.js.map +1 -1
  110. package/dist/client/index.d.ts +1 -1
  111. package/dist/client/index.d.ts.map +1 -1
  112. package/dist/client/index.js +2 -2
  113. package/dist/client/index.js.map +1 -1
  114. package/dist/client/rich-markdown-editor/BubbleToolbar.d.ts.map +1 -1
  115. package/dist/client/rich-markdown-editor/BubbleToolbar.js +13 -3
  116. package/dist/client/rich-markdown-editor/BubbleToolbar.js.map +1 -1
  117. package/dist/client/rich-markdown-editor/DragHandle.d.ts +49 -4
  118. package/dist/client/rich-markdown-editor/DragHandle.d.ts.map +1 -1
  119. package/dist/client/rich-markdown-editor/DragHandle.js +656 -88
  120. package/dist/client/rich-markdown-editor/DragHandle.js.map +1 -1
  121. package/dist/client/rich-markdown-editor/RegistryBlockNode.d.ts +10 -1
  122. package/dist/client/rich-markdown-editor/RegistryBlockNode.d.ts.map +1 -1
  123. package/dist/client/rich-markdown-editor/RegistryBlockNode.js +180 -15
  124. package/dist/client/rich-markdown-editor/RegistryBlockNode.js.map +1 -1
  125. package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts +2 -1
  126. package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts.map +1 -1
  127. package/dist/client/rich-markdown-editor/SharedRichEditor.js +3 -1
  128. package/dist/client/rich-markdown-editor/SharedRichEditor.js.map +1 -1
  129. package/dist/client/rich-markdown-editor/SlashCommandMenu.d.ts +5 -0
  130. package/dist/client/rich-markdown-editor/SlashCommandMenu.d.ts.map +1 -1
  131. package/dist/client/rich-markdown-editor/SlashCommandMenu.js +33 -5
  132. package/dist/client/rich-markdown-editor/SlashCommandMenu.js.map +1 -1
  133. package/dist/client/rich-markdown-editor/index.d.ts +3 -3
  134. package/dist/client/rich-markdown-editor/index.d.ts.map +1 -1
  135. package/dist/client/rich-markdown-editor/index.js +2 -2
  136. package/dist/client/rich-markdown-editor/index.js.map +1 -1
  137. package/dist/client/rich-markdown-editor/registrySlashCommands.d.ts +14 -0
  138. package/dist/client/rich-markdown-editor/registrySlashCommands.d.ts.map +1 -1
  139. package/dist/client/rich-markdown-editor/registrySlashCommands.js +38 -0
  140. package/dist/client/rich-markdown-editor/registrySlashCommands.js.map +1 -1
  141. package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts +1 -0
  142. package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
  143. package/dist/client/rich-markdown-editor/useCollabReconcile.js +4 -0
  144. package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
  145. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  146. package/dist/client/settings/SettingsPanel.js +11 -19
  147. package/dist/client/settings/SettingsPanel.js.map +1 -1
  148. package/dist/client/use-chat-models.d.ts.map +1 -1
  149. package/dist/client/use-chat-models.js +2 -5
  150. package/dist/client/use-chat-models.js.map +1 -1
  151. package/dist/db/client.d.ts.map +1 -1
  152. package/dist/db/client.js +17 -1
  153. package/dist/db/client.js.map +1 -1
  154. package/dist/deploy/build.d.ts.map +1 -1
  155. package/dist/deploy/build.js +2 -1
  156. package/dist/deploy/build.js.map +1 -1
  157. package/dist/deploy/route-discovery.d.ts +29 -0
  158. package/dist/deploy/route-discovery.d.ts.map +1 -1
  159. package/dist/deploy/route-discovery.js +158 -11
  160. package/dist/deploy/route-discovery.js.map +1 -1
  161. package/dist/server/auth.d.ts +2 -0
  162. package/dist/server/auth.d.ts.map +1 -1
  163. package/dist/server/auth.js +9 -0
  164. package/dist/server/auth.js.map +1 -1
  165. package/dist/sharing/access.d.ts +4 -2
  166. package/dist/sharing/access.d.ts.map +1 -1
  167. package/dist/sharing/access.js +8 -3
  168. package/dist/sharing/access.js.map +1 -1
  169. package/dist/sharing/actions/set-resource-visibility.d.ts.map +1 -1
  170. package/dist/sharing/actions/set-resource-visibility.js +2 -3
  171. package/dist/sharing/actions/set-resource-visibility.js.map +1 -1
  172. package/dist/sharing/registry.d.ts +13 -0
  173. package/dist/sharing/registry.d.ts.map +1 -1
  174. package/dist/sharing/registry.js.map +1 -1
  175. package/dist/styles/rich-markdown-editor.css +15 -0
  176. package/dist/templates/default/.agents/skills/actions/SKILL.md +96 -11
  177. package/dist/templates/default/.agents/skills/adding-a-feature/SKILL.md +126 -26
  178. package/dist/templates/default/.agents/skills/capture-learnings/SKILL.md +56 -30
  179. package/dist/templates/default/.agents/skills/create-skill/SKILL.md +28 -0
  180. package/dist/templates/default/.agents/skills/delegate-to-agent/SKILL.md +75 -5
  181. package/dist/templates/default/.agents/skills/frontend-design/SKILL.md +17 -0
  182. package/dist/templates/default/.agents/skills/real-time-collab/SKILL.md +99 -124
  183. package/dist/templates/default/.agents/skills/real-time-sync/SKILL.md +43 -10
  184. package/dist/templates/default/.agents/skills/security/SKILL.md +162 -144
  185. package/dist/templates/default/.agents/skills/self-modifying-code/SKILL.md +5 -3
  186. package/dist/templates/default/.agents/skills/shadcn-ui/SKILL.md +15 -0
  187. package/dist/templates/default/.agents/skills/storing-data/SKILL.md +116 -83
  188. package/dist/templates/default/DEVELOPING.md +10 -13
  189. package/dist/templates/workspace-core/.agents/skills/client-methods/references/legacy-client-fetch-audit-2026-06-03.md +9 -0
  190. package/dist/templates/workspace-core/.agents/skills/writing-agent-instructions/SKILL.md +27 -0
  191. package/docs/content/template-plan.md +5 -3
  192. package/docs/content/visual-plans.md +5 -2
  193. package/package.json +16 -1
  194. package/src/templates/default/.agents/skills/actions/SKILL.md +96 -11
  195. package/src/templates/default/.agents/skills/adding-a-feature/SKILL.md +126 -26
  196. package/src/templates/default/.agents/skills/capture-learnings/SKILL.md +56 -30
  197. package/src/templates/default/.agents/skills/create-skill/SKILL.md +28 -0
  198. package/src/templates/default/.agents/skills/delegate-to-agent/SKILL.md +75 -5
  199. package/src/templates/default/.agents/skills/frontend-design/SKILL.md +17 -0
  200. package/src/templates/default/.agents/skills/real-time-collab/SKILL.md +99 -124
  201. package/src/templates/default/.agents/skills/real-time-sync/SKILL.md +43 -10
  202. package/src/templates/default/.agents/skills/security/SKILL.md +162 -144
  203. package/src/templates/default/.agents/skills/self-modifying-code/SKILL.md +5 -3
  204. package/src/templates/default/.agents/skills/shadcn-ui/SKILL.md +15 -0
  205. package/src/templates/default/.agents/skills/storing-data/SKILL.md +116 -83
  206. package/src/templates/default/DEVELOPING.md +10 -13
  207. package/src/templates/workspace-core/.agents/skills/client-methods/references/legacy-client-fetch-audit-2026-06-03.md +9 -0
  208. package/src/templates/workspace-core/.agents/skills/writing-agent-instructions/SKILL.md +27 -0
@@ -0,0 +1,25 @@
1
+ import type { BlockEditProps, BlockReadProps } from "../types.js";
2
+ import { type ColumnsData } from "./columns.config.js";
3
+ /** Read renderer: a responsive grid of columns, each child rendered read-only. */
4
+ export declare function ColumnsBlockReader({ data, blockId, title, ctx, }: BlockReadProps<ColumnsData>): import("react/jsx-runtime").JSX.Element;
5
+ /**
6
+ * Editor: the same responsive grid, with child blocks rendered editable in
7
+ * place through the app dispatcher. A child change updates that child within
8
+ * its column and commits the whole
9
+ * columns block — mirroring the legacy `tabs` onChange bubbling so the plan's
10
+ * recursive `updateBlocks`/`findBlock` (`PlanContentRenderer`) keeps working.
11
+ *
12
+ * Renders BARE (no `plan-block` section / title): in edit mode the app's block
13
+ * dispatcher already wraps registered editors in a titled `plan-block` section,
14
+ * so wrapping again here would double-nest. The read renderer owns its own
15
+ * section because read mode renders the spec directly.
16
+ */
17
+ export declare function ColumnsBlockEditor({ data, onChange, editable, blockId, ctx, }: BlockEditProps<ColumnsData>): import("react/jsx-runtime").JSX.Element;
18
+ /**
19
+ * The standard columns block spec (with React `Read`/`Edit`). Apps register this
20
+ * in their browser registry. The schema + MDX config come from
21
+ * `./columns.config.ts`, the exact same object server / agent code registers, so
22
+ * rendering and source round-trip never drift.
23
+ */
24
+ export declare const columnsBlock: import("../types.js").BlockSpec<ColumnsData>;
25
+ //# sourceMappingURL=columns.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"columns.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/columns.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAEV,cAAc,EACd,cAAc,EAEf,MAAM,aAAa,CAAC;AACrB,OAAO,EAGL,KAAK,WAAW,EAEjB,MAAM,qBAAqB,CAAC;AAiG7B,kFAAkF;AAClF,wBAAgB,kBAAkB,CAAC,EACjC,IAAI,EACJ,OAAO,EACP,KAAK,EACL,GAAG,GACJ,EAAE,cAAc,CAAC,WAAW,CAAC,2CAmB7B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,IAAI,EACJ,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,GAAG,GACJ,EAAE,cAAc,CAAC,WAAW,CAAC,2CAmE7B;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,8CA0DvB,CAAC"}
@@ -0,0 +1,199 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { IconColumns } from "@tabler/icons-react";
3
+ import { cn } from "../../utils.js";
4
+ import { defineBlock } from "../types.js";
5
+ import { columnsSchema, columnsMdx, } from "./columns.config.js";
6
+ /**
7
+ * Standard `columns` block: a multi-column side-by-side container whose columns
8
+ * each hold a list of child blocks. Labels may still exist in stored data for
9
+ * source round-tripping, but the document UI intentionally renders bare regions.
10
+ *
11
+ * Like `tabs`, child rendering flows through `ctx.renderBlock` — the app's own
12
+ * block dispatcher — so registered children render via their spec and
13
+ * unconverted children fall through the app's legacy switch. This is the
14
+ * coexistence seam: the core columns block never has to know app-specific child
15
+ * block types. The plan CSS classes (`plan-block`, `text-plan-*`) resolve
16
+ * against the plan app's stylesheet at render time.
17
+ */
18
+ /** Mint a reasonably-unique column id without pulling a dep into core. */
19
+ function newColId() {
20
+ return `col-${Math.random().toString(36).slice(2, 10)}`;
21
+ }
22
+ /**
23
+ * Tailwind grid-column classes keyed by the column count. The grid collapses to
24
+ * a single column on small screens and fans out to the column count at `md`+, so
25
+ * narrow viewports stack the panels instead of crushing them.
26
+ */
27
+ const COLS_CLASS = {
28
+ 1: "grid-cols-1",
29
+ 2: "grid-cols-1 md:grid-cols-2",
30
+ 3: "grid-cols-1 md:grid-cols-3",
31
+ 4: "grid-cols-1 md:grid-cols-4",
32
+ };
33
+ /** Resolve the responsive grid class for a column count (clamped to 1–4). */
34
+ function gridColsClass(count) {
35
+ return COLS_CLASS[Math.min(4, Math.max(1, count))] ?? COLS_CLASS[1];
36
+ }
37
+ const API_REFERENCE_BLOCK_TYPES = new Set(["api-endpoint", "openapi-spec"]);
38
+ const BEFORE_LABELS = new Set(["before", "old", "previous", "current"]);
39
+ const AFTER_LABELS = new Set(["after", "new", "next", "target"]);
40
+ function normalizedLabel(column) {
41
+ return column.label?.trim().toLowerCase() ?? "";
42
+ }
43
+ function isComparisonGroup(columns) {
44
+ const labels = columns.map(normalizedLabel);
45
+ return (labels.some((label) => BEFORE_LABELS.has(label)) &&
46
+ labels.some((label) => AFTER_LABELS.has(label)));
47
+ }
48
+ function isApiReferenceGroup(columns) {
49
+ return (columns.length > 1 &&
50
+ !isComparisonGroup(columns) &&
51
+ columns.every((column) => column.blocks.length > 0 &&
52
+ column.blocks.every((block) => API_REFERENCE_BLOCK_TYPES.has(block.type))));
53
+ }
54
+ function columnsLayoutClass(columns) {
55
+ return isApiReferenceGroup(columns)
56
+ ? COLS_CLASS[1]
57
+ : gridColsClass(columns.length);
58
+ }
59
+ function isBlankRichTextBlock(block) {
60
+ if (block.type !== "rich-text")
61
+ return false;
62
+ const data = block.data;
63
+ if (!data || typeof data !== "object" || Array.isArray(data))
64
+ return false;
65
+ const markdown = data.markdown;
66
+ return typeof markdown === "string" && markdown.trim().length === 0;
67
+ }
68
+ function isEffectivelyEmptyRegion(blocks) {
69
+ return (blocks.length === 0 ||
70
+ (blocks.length === 1 && isBlankRichTextBlock(blocks[0])));
71
+ }
72
+ function areSameBlocks(a, b) {
73
+ if (a === b)
74
+ return true;
75
+ try {
76
+ return JSON.stringify(a) === JSON.stringify(b);
77
+ }
78
+ catch {
79
+ return false;
80
+ }
81
+ }
82
+ /** Read renderer: a responsive grid of columns, each child rendered read-only. */
83
+ export function ColumnsBlockReader({ data, blockId, title, ctx, }) {
84
+ return (_jsxs("section", { className: "plan-block", "data-block-id": blockId, children: [title && _jsx("div", { className: "plan-block-label", children: title }), _jsx("div", { className: cn("grid gap-6", columnsLayoutClass(data.columns)), children: data.columns.map((column) => (_jsx("div", { className: "min-w-0", children: _jsx("div", { children: column.blocks.map((child) => (_jsx("div", { children: ctx.renderBlock?.({ block: child, editing: false }) }, child.id))) }) }, column.id))) })] }));
85
+ }
86
+ /**
87
+ * Editor: the same responsive grid, with child blocks rendered editable in
88
+ * place through the app dispatcher. A child change updates that child within
89
+ * its column and commits the whole
90
+ * columns block — mirroring the legacy `tabs` onChange bubbling so the plan's
91
+ * recursive `updateBlocks`/`findBlock` (`PlanContentRenderer`) keeps working.
92
+ *
93
+ * Renders BARE (no `plan-block` section / title): in edit mode the app's block
94
+ * dispatcher already wraps registered editors in a titled `plan-block` section,
95
+ * so wrapping again here would double-nest. The read renderer owns its own
96
+ * section because read mode renders the spec directly.
97
+ */
98
+ export function ColumnsBlockEditor({ data, onChange, editable, blockId, ctx, }) {
99
+ const commit = (columns) => onChange({ columns });
100
+ const updateChild = (columnId, child) => commit(data.columns.map((column) => column.id === columnId
101
+ ? {
102
+ ...column,
103
+ blocks: column.blocks.map((existing) => existing.id === child.id ? child : existing),
104
+ }
105
+ : column));
106
+ return (_jsx("div", { "data-columns-edit-block": blockId, className: "grid gap-3", children: _jsx("div", { className: cn("grid gap-6", columnsLayoutClass(data.columns)), children: data.columns.map((column) => (_jsx("div", { className: "min-w-0", children: _jsx("div", { children: ctx.renderBlocksEditor
107
+ ? ctx.renderBlocksEditor({
108
+ blocks: column.blocks,
109
+ onChange: (nextBlocks) => {
110
+ if (areSameBlocks(nextBlocks, column.blocks))
111
+ return;
112
+ onChange({
113
+ columns: data.columns.map((existing) => existing.id === column.id
114
+ ? {
115
+ ...existing,
116
+ blocks: nextBlocks,
117
+ }
118
+ : existing),
119
+ }, {
120
+ containerRegion: {
121
+ regionId: column.id,
122
+ blocks: nextBlocks,
123
+ },
124
+ });
125
+ },
126
+ editable,
127
+ containerBlockId: blockId,
128
+ regionId: column.id,
129
+ regionLabel: column.label,
130
+ })
131
+ : column.blocks.map((child) => (_jsx("div", { children: ctx.renderBlock?.({
132
+ block: child,
133
+ editing: true,
134
+ onChange: (next) => updateChild(column.id, next),
135
+ }) }, child.id))) }) }, column.id))) }) }));
136
+ }
137
+ /**
138
+ * The standard columns block spec (with React `Read`/`Edit`). Apps register this
139
+ * in their browser registry. The schema + MDX config come from
140
+ * `./columns.config.ts`, the exact same object server / agent code registers, so
141
+ * rendering and source round-trip never drift.
142
+ */
143
+ export const columnsBlock = defineBlock({
144
+ type: "columns",
145
+ schema: columnsSchema,
146
+ mdx: columnsMdx,
147
+ Read: ColumnsBlockReader,
148
+ Edit: ColumnsBlockEditor,
149
+ placement: ["block"],
150
+ editSurface: "container",
151
+ container: {
152
+ regions: (data) => data.columns,
153
+ updateRegion: (data, regionId, blocks) => {
154
+ const shouldRemoveRegion = data.columns.length > 1 && isEffectivelyEmptyRegion(blocks);
155
+ return {
156
+ columns: data.columns
157
+ .map((column) => column.id === regionId ? { ...column, blocks } : column)
158
+ .filter((column) => column.id !== regionId || !shouldRemoveRegion),
159
+ };
160
+ },
161
+ addRegion: (data, afterRegionId) => {
162
+ if (data.columns.length >= 4)
163
+ return data;
164
+ const nextColumn = {
165
+ id: newColId(),
166
+ blocks: [],
167
+ };
168
+ if (!afterRegionId)
169
+ return { columns: [...data.columns, nextColumn] };
170
+ const afterIndex = data.columns.findIndex((column) => column.id === afterRegionId);
171
+ if (afterIndex < 0)
172
+ return { columns: [...data.columns, nextColumn] };
173
+ return {
174
+ columns: [
175
+ ...data.columns.slice(0, afterIndex + 1),
176
+ nextColumn,
177
+ ...data.columns.slice(afterIndex + 1),
178
+ ],
179
+ };
180
+ },
181
+ removeRegion: (data, regionId) => {
182
+ if (data.columns.length <= 1)
183
+ return data;
184
+ return {
185
+ columns: data.columns.filter((column) => column.id !== regionId),
186
+ };
187
+ },
188
+ },
189
+ label: "Columns",
190
+ icon: IconColumns,
191
+ description: "A multi-column side-by-side layout container; each column holds its own list of blocks. Ideal for before/after or current/target comparisons.",
192
+ empty: () => ({
193
+ columns: [
194
+ { id: newColId(), blocks: [] },
195
+ { id: newColId(), blocks: [] },
196
+ ],
197
+ }),
198
+ });
199
+ //# sourceMappingURL=columns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"columns.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/columns.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAO1C,OAAO,EACL,aAAa,EACb,UAAU,GAGX,MAAM,qBAAqB,CAAC;AAE7B;;;;;;;;;;;GAWG;AAEH,0EAA0E;AAC1E,SAAS,QAAQ;IACf,OAAO,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;AAC1D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,GAA2B;IACzC,CAAC,EAAE,aAAa;IAChB,CAAC,EAAE,4BAA4B;IAC/B,CAAC,EAAE,4BAA4B;IAC/B,CAAC,EAAE,4BAA4B;CAChC,CAAC;AAEF,6EAA6E;AAC7E,SAAS,aAAa,CAAC,KAAa;IAClC,OAAO,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;AACtE,CAAC;AAED,MAAM,yBAAyB,GAAG,IAAI,GAAG,CAAC,CAAC,cAAc,EAAE,cAAc,CAAC,CAAC,CAAC;AAC5E,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC;AACxE,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;AAEjE,SAAS,eAAe,CAAC,MAAqB;IAC5C,OAAO,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC;AAClD,CAAC;AAED,SAAS,iBAAiB,CAAC,OAAwB;IACjD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAC5C,OAAO,CACL,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAChD,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAChD,CAAC;AACJ,CAAC;AAED,SAAS,mBAAmB,CAAC,OAAwB;IACnD,OAAO,CACL,OAAO,CAAC,MAAM,GAAG,CAAC;QAClB,CAAC,iBAAiB,CAAC,OAAO,CAAC;QAC3B,OAAO,CAAC,KAAK,CACX,CAAC,MAAM,EAAE,EAAE,CACT,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;YACxB,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAC5B,yBAAyB,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAC1C,CACJ,CACF,CAAC;AACJ,CAAC;AAED,SAAS,kBAAkB,CAAC,OAAwB;IAClD,OAAO,mBAAmB,CAAC,OAAO,CAAC;QACjC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;QACf,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;AACpC,CAAC;AAED,SAAS,oBAAoB,CAAC,KAAkB;IAC9C,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW;QAAE,OAAO,KAAK,CAAC;IAC7C,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;IACxB,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC3E,MAAM,QAAQ,GAAI,IAA+B,CAAC,QAAQ,CAAC;IAC3D,OAAO,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,CAAC;AACtE,CAAC;AAED,SAAS,wBAAwB,CAAC,MAAqB;IACrD,OAAO,CACL,MAAM,CAAC,MAAM,KAAK,CAAC;QACnB,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,oBAAoB,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,CAAC,CAC1D,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,CAAgB,EAAE,CAAgB;IACvD,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACzB,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IACjD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,kBAAkB,CAAC,EACjC,IAAI,EACJ,OAAO,EACP,KAAK,EACL,GAAG,GACyB;IAC5B,OAAO,CACL,mBAAS,SAAS,EAAC,YAAY,mBAAgB,OAAO,aACnD,KAAK,IAAI,cAAK,SAAS,EAAC,kBAAkB,YAAE,KAAK,GAAO,EACzD,cAAK,SAAS,EAAE,EAAE,CAAC,YAAY,EAAE,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,YAC/D,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAC5B,cAAqB,SAAS,EAAC,SAAS,YACtC,wBACG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAC5B,wBACG,GAAG,CAAC,WAAW,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,IAD5C,KAAK,CAAC,EAAE,CAEZ,CACP,CAAC,GACE,IAPE,MAAM,CAAC,EAAE,CAQb,CACP,CAAC,GACE,IACE,CACX,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,kBAAkB,CAAC,EACjC,IAAI,EACJ,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,GAAG,GACyB;IAC5B,MAAM,MAAM,GAAG,CAAC,OAAwB,EAAE,EAAE,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IAEnE,MAAM,WAAW,GAAG,CAAC,QAAgB,EAAE,KAAkB,EAAE,EAAE,CAC3D,MAAM,CACJ,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAC1B,MAAM,CAAC,EAAE,KAAK,QAAQ;QACpB,CAAC,CAAC;YACE,GAAG,MAAM;YACT,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CACrC,QAAQ,CAAC,EAAE,KAAK,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAC5C;SACF;QACH,CAAC,CAAC,MAAM,CACX,CACF,CAAC;IAEJ,OAAO,CACL,yCAA8B,OAAO,EAAE,SAAS,EAAC,YAAY,YAC3D,cAAK,SAAS,EAAE,EAAE,CAAC,YAAY,EAAE,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,YAC/D,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAC5B,cAAqB,SAAS,EAAC,SAAS,YACtC,wBACG,GAAG,CAAC,kBAAkB;wBACrB,CAAC,CAAC,GAAG,CAAC,kBAAkB,CAAC;4BACrB,MAAM,EAAE,MAAM,CAAC,MAAM;4BACrB,QAAQ,EAAE,CAAC,UAAU,EAAE,EAAE;gCACvB,IAAI,aAAa,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC;oCAAE,OAAO;gCACrD,QAAQ,CACN;oCACE,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CACrC,QAAQ,CAAC,EAAE,KAAK,MAAM,CAAC,EAAE;wCACvB,CAAC,CAAC;4CACE,GAAG,QAAQ;4CACX,MAAM,EAAE,UAAU;yCACnB;wCACH,CAAC,CAAC,QAAQ,CACb;iCACF,EACD;oCACE,eAAe,EAAE;wCACf,QAAQ,EAAE,MAAM,CAAC,EAAE;wCACnB,MAAM,EAAE,UAAU;qCACnB;iCACF,CACF,CAAC;4BACJ,CAAC;4BACD,QAAQ;4BACR,gBAAgB,EAAE,OAAO;4BACzB,QAAQ,EAAE,MAAM,CAAC,EAAE;4BACnB,WAAW,EAAE,MAAM,CAAC,KAAK;yBAC1B,CAAC;wBACJ,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAC3B,wBACG,GAAG,CAAC,WAAW,EAAE,CAAC;gCACjB,KAAK,EAAE,KAAK;gCACZ,OAAO,EAAE,IAAI;gCACb,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC;6BACjD,CAAC,IALM,KAAK,CAAC,EAAE,CAMZ,CACP,CAAC,GACF,IAxCE,MAAM,CAAC,EAAE,CAyCb,CACP,CAAC,GACE,GACF,CACP,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,WAAW,CAAc;IACnD,IAAI,EAAE,SAAS;IACf,MAAM,EAAE,aAAa;IACrB,GAAG,EAAE,UAAU;IACf,IAAI,EAAE,kBAAkB;IACxB,IAAI,EAAE,kBAAkB;IACxB,SAAS,EAAE,CAAC,OAAO,CAAC;IACpB,WAAW,EAAE,WAAW;IACxB,SAAS,EAAE;QACT,OAAO,EAAE,CAAC,IAAI,EAA0B,EAAE,CAAC,IAAI,CAAC,OAAO;QACvD,YAAY,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE;YACvC,MAAM,kBAAkB,GACtB,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,wBAAwB,CAAC,MAAM,CAAC,CAAC;YAE9D,OAAO;gBACL,OAAO,EAAE,IAAI,CAAC,OAAO;qBAClB,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CACd,MAAM,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,MAAM,CACxD;qBACA,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,QAAQ,IAAI,CAAC,kBAAkB,CAAC;aACrE,CAAC;QACJ,CAAC;QACD,SAAS,EAAE,CAAC,IAAI,EAAE,aAAa,EAAE,EAAE;YACjC,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC;YAC1C,MAAM,UAAU,GAAkB;gBAChC,EAAE,EAAE,QAAQ,EAAE;gBACd,MAAM,EAAE,EAAE;aACX,CAAC;YACF,IAAI,CAAC,aAAa;gBAAE,OAAO,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,CAAC;YACtE,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CACvC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,aAAa,CACxC,CAAC;YACF,IAAI,UAAU,GAAG,CAAC;gBAAE,OAAO,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,CAAC;YACtE,OAAO;gBACL,OAAO,EAAE;oBACP,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,GAAG,CAAC,CAAC;oBACxC,UAAU;oBACV,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC;iBACtC;aACF,CAAC;QACJ,CAAC;QACD,YAAY,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE;YAC/B,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC;YAC1C,OAAO;gBACL,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,QAAQ,CAAC;aACjE,CAAC;QACJ,CAAC;KACF;IACD,KAAK,EAAE,SAAS;IAChB,IAAI,EAAE,WAAW;IACjB,WAAW,EACT,+IAA+I;IACjJ,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QACZ,OAAO,EAAE;YACP,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;YAC9B,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;SAC/B;KACF,CAAC;CACH,CAAC,CAAC","sourcesContent":["import { IconColumns } from \"@tabler/icons-react\";\nimport { cn } from \"../../utils.js\";\nimport { defineBlock } from \"../types.js\";\nimport type {\n BlockContainerRegion,\n BlockEditProps,\n BlockReadProps,\n NestedBlock,\n} from \"../types.js\";\nimport {\n columnsSchema,\n columnsMdx,\n type ColumnsData,\n type ColumnsColumn,\n} from \"./columns.config.js\";\n\n/**\n * Standard `columns` block: a multi-column side-by-side container whose columns\n * each hold a list of child blocks. Labels may still exist in stored data for\n * source round-tripping, but the document UI intentionally renders bare regions.\n *\n * Like `tabs`, child rendering flows through `ctx.renderBlock` — the app's own\n * block dispatcher — so registered children render via their spec and\n * unconverted children fall through the app's legacy switch. This is the\n * coexistence seam: the core columns block never has to know app-specific child\n * block types. The plan CSS classes (`plan-block`, `text-plan-*`) resolve\n * against the plan app's stylesheet at render time.\n */\n\n/** Mint a reasonably-unique column id without pulling a dep into core. */\nfunction newColId(): string {\n return `col-${Math.random().toString(36).slice(2, 10)}`;\n}\n\n/**\n * Tailwind grid-column classes keyed by the column count. The grid collapses to\n * a single column on small screens and fans out to the column count at `md`+, so\n * narrow viewports stack the panels instead of crushing them.\n */\nconst COLS_CLASS: Record<number, string> = {\n 1: \"grid-cols-1\",\n 2: \"grid-cols-1 md:grid-cols-2\",\n 3: \"grid-cols-1 md:grid-cols-3\",\n 4: \"grid-cols-1 md:grid-cols-4\",\n};\n\n/** Resolve the responsive grid class for a column count (clamped to 1–4). */\nfunction gridColsClass(count: number): string {\n return COLS_CLASS[Math.min(4, Math.max(1, count))] ?? COLS_CLASS[1];\n}\n\nconst API_REFERENCE_BLOCK_TYPES = new Set([\"api-endpoint\", \"openapi-spec\"]);\nconst BEFORE_LABELS = new Set([\"before\", \"old\", \"previous\", \"current\"]);\nconst AFTER_LABELS = new Set([\"after\", \"new\", \"next\", \"target\"]);\n\nfunction normalizedLabel(column: ColumnsColumn): string {\n return column.label?.trim().toLowerCase() ?? \"\";\n}\n\nfunction isComparisonGroup(columns: ColumnsColumn[]): boolean {\n const labels = columns.map(normalizedLabel);\n return (\n labels.some((label) => BEFORE_LABELS.has(label)) &&\n labels.some((label) => AFTER_LABELS.has(label))\n );\n}\n\nfunction isApiReferenceGroup(columns: ColumnsColumn[]): boolean {\n return (\n columns.length > 1 &&\n !isComparisonGroup(columns) &&\n columns.every(\n (column) =>\n column.blocks.length > 0 &&\n column.blocks.every((block) =>\n API_REFERENCE_BLOCK_TYPES.has(block.type),\n ),\n )\n );\n}\n\nfunction columnsLayoutClass(columns: ColumnsColumn[]): string {\n return isApiReferenceGroup(columns)\n ? COLS_CLASS[1]\n : gridColsClass(columns.length);\n}\n\nfunction isBlankRichTextBlock(block: NestedBlock): boolean {\n if (block.type !== \"rich-text\") return false;\n const data = block.data;\n if (!data || typeof data !== \"object\" || Array.isArray(data)) return false;\n const markdown = (data as { markdown?: unknown }).markdown;\n return typeof markdown === \"string\" && markdown.trim().length === 0;\n}\n\nfunction isEffectivelyEmptyRegion(blocks: NestedBlock[]): boolean {\n return (\n blocks.length === 0 ||\n (blocks.length === 1 && isBlankRichTextBlock(blocks[0]!))\n );\n}\n\nfunction areSameBlocks(a: NestedBlock[], b: NestedBlock[]): boolean {\n if (a === b) return true;\n try {\n return JSON.stringify(a) === JSON.stringify(b);\n } catch {\n return false;\n }\n}\n\n/** Read renderer: a responsive grid of columns, each child rendered read-only. */\nexport function ColumnsBlockReader({\n data,\n blockId,\n title,\n ctx,\n}: BlockReadProps<ColumnsData>) {\n return (\n <section className=\"plan-block\" data-block-id={blockId}>\n {title && <div className=\"plan-block-label\">{title}</div>}\n <div className={cn(\"grid gap-6\", columnsLayoutClass(data.columns))}>\n {data.columns.map((column) => (\n <div key={column.id} className=\"min-w-0\">\n <div>\n {column.blocks.map((child) => (\n <div key={child.id}>\n {ctx.renderBlock?.({ block: child, editing: false })}\n </div>\n ))}\n </div>\n </div>\n ))}\n </div>\n </section>\n );\n}\n\n/**\n * Editor: the same responsive grid, with child blocks rendered editable in\n * place through the app dispatcher. A child change updates that child within\n * its column and commits the whole\n * columns block — mirroring the legacy `tabs` onChange bubbling so the plan's\n * recursive `updateBlocks`/`findBlock` (`PlanContentRenderer`) keeps working.\n *\n * Renders BARE (no `plan-block` section / title): in edit mode the app's block\n * dispatcher already wraps registered editors in a titled `plan-block` section,\n * so wrapping again here would double-nest. The read renderer owns its own\n * section because read mode renders the spec directly.\n */\nexport function ColumnsBlockEditor({\n data,\n onChange,\n editable,\n blockId,\n ctx,\n}: BlockEditProps<ColumnsData>) {\n const commit = (columns: ColumnsColumn[]) => onChange({ columns });\n\n const updateChild = (columnId: string, child: NestedBlock) =>\n commit(\n data.columns.map((column) =>\n column.id === columnId\n ? {\n ...column,\n blocks: column.blocks.map((existing) =>\n existing.id === child.id ? child : existing,\n ),\n }\n : column,\n ),\n );\n\n return (\n <div data-columns-edit-block={blockId} className=\"grid gap-3\">\n <div className={cn(\"grid gap-6\", columnsLayoutClass(data.columns))}>\n {data.columns.map((column) => (\n <div key={column.id} className=\"min-w-0\">\n <div>\n {ctx.renderBlocksEditor\n ? ctx.renderBlocksEditor({\n blocks: column.blocks,\n onChange: (nextBlocks) => {\n if (areSameBlocks(nextBlocks, column.blocks)) return;\n onChange(\n {\n columns: data.columns.map((existing) =>\n existing.id === column.id\n ? {\n ...existing,\n blocks: nextBlocks,\n }\n : existing,\n ),\n },\n {\n containerRegion: {\n regionId: column.id,\n blocks: nextBlocks,\n },\n },\n );\n },\n editable,\n containerBlockId: blockId,\n regionId: column.id,\n regionLabel: column.label,\n })\n : column.blocks.map((child) => (\n <div key={child.id}>\n {ctx.renderBlock?.({\n block: child,\n editing: true,\n onChange: (next) => updateChild(column.id, next),\n })}\n </div>\n ))}\n </div>\n </div>\n ))}\n </div>\n </div>\n );\n}\n\n/**\n * The standard columns block spec (with React `Read`/`Edit`). Apps register this\n * in their browser registry. The schema + MDX config come from\n * `./columns.config.ts`, the exact same object server / agent code registers, so\n * rendering and source round-trip never drift.\n */\nexport const columnsBlock = defineBlock<ColumnsData>({\n type: \"columns\",\n schema: columnsSchema,\n mdx: columnsMdx,\n Read: ColumnsBlockReader,\n Edit: ColumnsBlockEditor,\n placement: [\"block\"],\n editSurface: \"container\",\n container: {\n regions: (data): BlockContainerRegion[] => data.columns,\n updateRegion: (data, regionId, blocks) => {\n const shouldRemoveRegion =\n data.columns.length > 1 && isEffectivelyEmptyRegion(blocks);\n\n return {\n columns: data.columns\n .map((column) =>\n column.id === regionId ? { ...column, blocks } : column,\n )\n .filter((column) => column.id !== regionId || !shouldRemoveRegion),\n };\n },\n addRegion: (data, afterRegionId) => {\n if (data.columns.length >= 4) return data;\n const nextColumn: ColumnsColumn = {\n id: newColId(),\n blocks: [],\n };\n if (!afterRegionId) return { columns: [...data.columns, nextColumn] };\n const afterIndex = data.columns.findIndex(\n (column) => column.id === afterRegionId,\n );\n if (afterIndex < 0) return { columns: [...data.columns, nextColumn] };\n return {\n columns: [\n ...data.columns.slice(0, afterIndex + 1),\n nextColumn,\n ...data.columns.slice(afterIndex + 1),\n ],\n };\n },\n removeRegion: (data, regionId) => {\n if (data.columns.length <= 1) return data;\n return {\n columns: data.columns.filter((column) => column.id !== regionId),\n };\n },\n },\n label: \"Columns\",\n icon: IconColumns,\n description:\n \"A multi-column side-by-side layout container; each column holds its own list of blocks. Ideal for before/after or current/target comparisons.\",\n empty: () => ({\n columns: [\n { id: newColId(), blocks: [] },\n { id: newColId(), blocks: [] },\n ],\n }),\n});\n"]}
@@ -2,7 +2,8 @@ import type { ComponentProps, InputHTMLAttributes, LabelHTMLAttributes, ReactNod
2
2
  /**
3
3
  * Minimal, app-agnostic form primitives for the core "dev-doc" block library
4
4
  * (mermaid / api-endpoint / data-model / diff / file-tree / json-explorer /
5
- * annotated-code). These blocks previously imported the plan app's shadcn/ui
5
+ * annotated-code).
6
+ * These blocks previously imported the plan app's shadcn/ui
6
7
  * components (`@/components/ui/*`); core blocks must stay portable, so these are
7
8
  * plain styled elements that reproduce the SAME shadcn Tailwind classes byte-for
8
9
  * -byte. They resolve against whatever shadcn token theme (`border-input`,
@@ -1 +1 @@
1
- {"version":3,"file":"dev-doc-ui.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/dev-doc-ui.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,cAAc,EACd,mBAAmB,EACnB,mBAAmB,EACnB,SAAS,EACT,sBAAsB,EACvB,MAAM,OAAO,CAAC;AAIf;;;;;;;;;;;;;;GAcG;AAIH,eAAO,MAAM,QAAQ,oIAanB,CAAC;AAKH,eAAO,MAAM,QAAQ,oIAYnB,CAAC;AAKH,eAAO,MAAM,WAAW,6IAYtB,CAAC;AAKH,gEAAgE;AAChE,wBAAgB,QAAQ,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,cAAc,CAAC,MAAM,CAAC,2CAUvE;AAID;;;GAGG;AACH,wBAAgB,SAAS,CAAC,EACxB,OAAO,EACP,eAAe,EACf,QAAQ,EACR,SAAS,EACT,GAAG,KAAK,EACT,EAAE;IACD,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,IAAI,CACN,mBAAmB,CAAC,iBAAiB,CAAC,EACtC,UAAU,GAAG,SAAS,GAAG,MAAM,CAChC,2CAwBA;AAID,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,aAAa,EACb,OAAO,EACP,QAAQ,EACR,SAAS,EACT,YAAY,EAAE,SAAS,GACxB,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,2CAsBA"}
1
+ {"version":3,"file":"dev-doc-ui.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/dev-doc-ui.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,cAAc,EACd,mBAAmB,EACnB,mBAAmB,EACnB,SAAS,EACT,sBAAsB,EACvB,MAAM,OAAO,CAAC;AAIf;;;;;;;;;;;;;;;GAeG;AAIH,eAAO,MAAM,QAAQ,oIAanB,CAAC;AAKH,eAAO,MAAM,QAAQ,oIAYnB,CAAC;AAKH,eAAO,MAAM,WAAW,6IAYtB,CAAC;AAKH,gEAAgE;AAChE,wBAAgB,QAAQ,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,cAAc,CAAC,MAAM,CAAC,2CAUvE;AAID;;;GAGG;AACH,wBAAgB,SAAS,CAAC,EACxB,OAAO,EACP,eAAe,EACf,QAAQ,EACR,SAAS,EACT,GAAG,KAAK,EACT,EAAE;IACD,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,IAAI,CACN,mBAAmB,CAAC,iBAAiB,CAAC,EACtC,UAAU,GAAG,SAAS,GAAG,MAAM,CAChC,2CAwBA;AAID,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,aAAa,EACb,OAAO,EACP,QAAQ,EACR,SAAS,EACT,YAAY,EAAE,SAAS,GACxB,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,2CAsBA"}
@@ -5,7 +5,8 @@ import { cn } from "../../utils.js";
5
5
  /**
6
6
  * Minimal, app-agnostic form primitives for the core "dev-doc" block library
7
7
  * (mermaid / api-endpoint / data-model / diff / file-tree / json-explorer /
8
- * annotated-code). These blocks previously imported the plan app's shadcn/ui
8
+ * annotated-code).
9
+ * These blocks previously imported the plan app's shadcn/ui
9
10
  * components (`@/components/ui/*`); core blocks must stay portable, so these are
10
11
  * plain styled elements that reproduce the SAME shadcn Tailwind classes byte-for
11
12
  * -byte. They resolve against whatever shadcn token theme (`border-input`,
@@ -1 +1 @@
1
- {"version":3,"file":"dev-doc-ui.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/dev-doc-ui.tsx"],"names":[],"mappings":"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","sourcesContent":["import { forwardRef } from \"react\";\nimport type {\n ComponentProps,\n InputHTMLAttributes,\n LabelHTMLAttributes,\n ReactNode,\n TextareaHTMLAttributes,\n} from \"react\";\nimport { IconChevronDown } from \"@tabler/icons-react\";\nimport { cn } from \"../../utils.js\";\n\n/**\n * Minimal, app-agnostic form primitives for the core \"dev-doc\" block library\n * (mermaid / api-endpoint / data-model / diff / file-tree / json-explorer /\n * annotated-code). These blocks previously imported the plan app's shadcn/ui\n * components (`@/components/ui/*`); core blocks must stay portable, so these are\n * plain styled elements that reproduce the SAME shadcn Tailwind classes byte-for\n * -byte. They resolve against whatever shadcn token theme (`border-input`,\n * `bg-background`, `ring-ring`, …) the host app ships, so the rendered look is\n * unchanged across apps.\n *\n * The Select is intentionally a NATIVE `<select>` styled to match the shadcn\n * trigger rather than a Radix popover: it keeps core dependency-free and behaves\n * identically for the simple enum pickers these editors use (method, change,\n * param location, diff mode).\n */\n\n/* ── Input ─────────────────────────────────────────────────────────────────── */\n\nexport const DevInput = forwardRef<\n HTMLInputElement,\n InputHTMLAttributes<HTMLInputElement>\n>(({ className, type, ...props }, ref) => (\n <input\n ref={ref}\n type={type}\n className={cn(\n \"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm\",\n className,\n )}\n {...props}\n />\n));\nDevInput.displayName = \"DevInput\";\n\n/* ── Label ─────────────────────────────────────────────────────────────────── */\n\nexport const DevLabel = forwardRef<\n HTMLLabelElement,\n LabelHTMLAttributes<HTMLLabelElement>\n>(({ className, ...props }, ref) => (\n <label\n ref={ref}\n className={cn(\n \"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70\",\n className,\n )}\n {...props}\n />\n));\nDevLabel.displayName = \"DevLabel\";\n\n/* ── Textarea ──────────────────────────────────────────────────────────────── */\n\nexport const DevTextarea = forwardRef<\n HTMLTextAreaElement,\n TextareaHTMLAttributes<HTMLTextAreaElement>\n>(({ className, ...props }, ref) => (\n <textarea\n ref={ref}\n className={cn(\n \"flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50\",\n className,\n )}\n {...props}\n />\n));\nDevTextarea.displayName = \"DevTextarea\";\n\n/* ── Badge ─────────────────────────────────────────────────────────────────── */\n\n/** Only the `outline` badge variant is used by these blocks. */\nexport function DevBadge({ className, ...props }: ComponentProps<\"span\">) {\n return (\n <span\n className={cn(\n \"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 text-foreground\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/* ── Switch ────────────────────────────────────────────────────────────────── */\n\n/**\n * A native-checkbox toggle styled to read like the shadcn Switch. `onCheckedChange`\n * mirrors the shadcn/Radix API so call sites stay identical.\n */\nexport function DevSwitch({\n checked,\n onCheckedChange,\n disabled,\n className,\n ...props\n}: {\n checked: boolean;\n onCheckedChange: (checked: boolean) => void;\n disabled?: boolean;\n className?: string;\n} & Omit<\n InputHTMLAttributes<HTMLButtonElement>,\n \"onChange\" | \"checked\" | \"type\"\n>) {\n return (\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={checked}\n disabled={disabled}\n data-plan-interactive\n onClick={() => onCheckedChange(!checked)}\n className={cn(\n \"peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50\",\n checked ? \"bg-primary\" : \"bg-input\",\n className,\n )}\n {...(props as Record<string, unknown>)}\n >\n <span\n className={cn(\n \"pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform\",\n checked ? \"translate-x-5\" : \"translate-x-0\",\n )}\n />\n </button>\n );\n}\n\n/* ── Select (native, shadcn-trigger styled) ────────────────────────────────── */\n\nexport interface DevSelectOption {\n value: string;\n label: ReactNode;\n}\n\n/**\n * A native `<select>` styled to match the shadcn SelectTrigger. Drop-in for the\n * simple enum pickers the dev-doc editors use. `onValueChange` mirrors the shadcn\n * API. The chevron is positioned over the native control.\n */\nexport function DevSelect({\n value,\n onValueChange,\n options,\n disabled,\n className,\n \"aria-label\": ariaLabel,\n}: {\n value: string;\n onValueChange: (value: string) => void;\n options: DevSelectOption[];\n disabled?: boolean;\n className?: string;\n \"aria-label\"?: string;\n}) {\n return (\n <div className={cn(\"relative\", className)}>\n <select\n value={value}\n disabled={disabled}\n aria-label={ariaLabel}\n data-plan-interactive\n onChange={(event) => onValueChange(event.target.value)}\n className={cn(\n \"flex h-10 w-full appearance-none items-center justify-between rounded-md border border-input bg-background px-3 py-2 pr-8 text-sm ring-offset-background focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50\",\n )}\n >\n {options.map((option) => (\n <option key={option.value} value={option.value}>\n {typeof option.label === \"string\" ? option.label : option.value}\n </option>\n ))}\n </select>\n <IconChevronDown className=\"pointer-events-none absolute right-2.5 top-1/2 size-4 -translate-y-1/2 opacity-50\" />\n </div>\n );\n}\n"]}
1
+ {"version":3,"file":"dev-doc-ui.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/dev-doc-ui.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAQnC,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AAEpC;;;;;;;;;;;;;;;GAeG;AAEH,kFAAkF;AAElF,MAAM,CAAC,MAAM,QAAQ,GAAG,UAAU,CAGhC,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,CACxC,gBACE,GAAG,EAAE,GAAG,EACR,IAAI,EAAE,IAAI,EACV,SAAS,EAAE,EAAE,CACX,gYAAgY,EAChY,SAAS,CACV,KACG,KAAK,GACT,CACH,CAAC,CAAC;AACH,QAAQ,CAAC,WAAW,GAAG,UAAU,CAAC;AAElC,kFAAkF;AAElF,MAAM,CAAC,MAAM,QAAQ,GAAG,UAAU,CAGhC,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,CAClC,gBACE,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,EAAE,CACX,4FAA4F,EAC5F,SAAS,CACV,KACG,KAAK,GACT,CACH,CAAC,CAAC;AACH,QAAQ,CAAC,WAAW,GAAG,UAAU,CAAC;AAElC,kFAAkF;AAElF,MAAM,CAAC,MAAM,WAAW,GAAG,UAAU,CAGnC,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,CAClC,mBACE,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,EAAE,CACX,sSAAsS,EACtS,SAAS,CACV,KACG,KAAK,GACT,CACH,CAAC,CAAC;AACH,WAAW,CAAC,WAAW,GAAG,aAAa,CAAC;AAExC,kFAAkF;AAElF,gEAAgE;AAChE,MAAM,UAAU,QAAQ,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAA0B;IACtE,OAAO,CACL,eACE,SAAS,EAAE,EAAE,CACX,wLAAwL,EACxL,SAAS,CACV,KACG,KAAK,GACT,CACH,CAAC;AACJ,CAAC;AAED,kFAAkF;AAElF;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,EACxB,OAAO,EACP,eAAe,EACf,QAAQ,EACR,SAAS,EACT,GAAG,KAAK,EAST;IACC,OAAO,CACL,iBACE,IAAI,EAAC,QAAQ,EACb,IAAI,EAAC,QAAQ,kBACC,OAAO,EACrB,QAAQ,EAAE,QAAQ,iCAElB,OAAO,EAAE,GAAG,EAAE,CAAC,eAAe,CAAC,CAAC,OAAO,CAAC,EACxC,SAAS,EAAE,EAAE,CACX,oTAAoT,EACpT,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,UAAU,EACnC,SAAS,CACV,KACI,KAAiC,YAEtC,eACE,SAAS,EAAE,EAAE,CACX,oGAAoG,EACpG,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,eAAe,CAC5C,GACD,GACK,CACV,CAAC;AACJ,CAAC;AASD;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,EACxB,KAAK,EACL,aAAa,EACb,OAAO,EACP,QAAQ,EACR,SAAS,EACT,YAAY,EAAE,SAAS,GAQxB;IACC,OAAO,CACL,eAAK,SAAS,EAAE,EAAE,CAAC,UAAU,EAAE,SAAS,CAAC,aACvC,iBACE,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,QAAQ,gBACN,SAAS,iCAErB,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,EACtD,SAAS,EAAE,EAAE,CACX,8QAA8Q,CAC/Q,YAEA,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CACvB,iBAA2B,KAAK,EAAE,MAAM,CAAC,KAAK,YAC3C,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,IADpD,MAAM,CAAC,KAAK,CAEhB,CACV,CAAC,GACK,EACT,KAAC,eAAe,IAAC,SAAS,EAAC,mFAAmF,GAAG,IAC7G,CACP,CAAC;AACJ,CAAC","sourcesContent":["import { forwardRef } from \"react\";\nimport type {\n ComponentProps,\n InputHTMLAttributes,\n LabelHTMLAttributes,\n ReactNode,\n TextareaHTMLAttributes,\n} from \"react\";\nimport { IconChevronDown } from \"@tabler/icons-react\";\nimport { cn } from \"../../utils.js\";\n\n/**\n * Minimal, app-agnostic form primitives for the core \"dev-doc\" block library\n * (mermaid / api-endpoint / data-model / diff / file-tree / json-explorer /\n * annotated-code).\n * These blocks previously imported the plan app's shadcn/ui\n * components (`@/components/ui/*`); core blocks must stay portable, so these are\n * plain styled elements that reproduce the SAME shadcn Tailwind classes byte-for\n * -byte. They resolve against whatever shadcn token theme (`border-input`,\n * `bg-background`, `ring-ring`, …) the host app ships, so the rendered look is\n * unchanged across apps.\n *\n * The Select is intentionally a NATIVE `<select>` styled to match the shadcn\n * trigger rather than a Radix popover: it keeps core dependency-free and behaves\n * identically for the simple enum pickers these editors use (method, change,\n * param location, diff mode).\n */\n\n/* ── Input ─────────────────────────────────────────────────────────────────── */\n\nexport const DevInput = forwardRef<\n HTMLInputElement,\n InputHTMLAttributes<HTMLInputElement>\n>(({ className, type, ...props }, ref) => (\n <input\n ref={ref}\n type={type}\n className={cn(\n \"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm\",\n className,\n )}\n {...props}\n />\n));\nDevInput.displayName = \"DevInput\";\n\n/* ── Label ─────────────────────────────────────────────────────────────────── */\n\nexport const DevLabel = forwardRef<\n HTMLLabelElement,\n LabelHTMLAttributes<HTMLLabelElement>\n>(({ className, ...props }, ref) => (\n <label\n ref={ref}\n className={cn(\n \"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70\",\n className,\n )}\n {...props}\n />\n));\nDevLabel.displayName = \"DevLabel\";\n\n/* ── Textarea ──────────────────────────────────────────────────────────────── */\n\nexport const DevTextarea = forwardRef<\n HTMLTextAreaElement,\n TextareaHTMLAttributes<HTMLTextAreaElement>\n>(({ className, ...props }, ref) => (\n <textarea\n ref={ref}\n className={cn(\n \"flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50\",\n className,\n )}\n {...props}\n />\n));\nDevTextarea.displayName = \"DevTextarea\";\n\n/* ── Badge ─────────────────────────────────────────────────────────────────── */\n\n/** Only the `outline` badge variant is used by these blocks. */\nexport function DevBadge({ className, ...props }: ComponentProps<\"span\">) {\n return (\n <span\n className={cn(\n \"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 text-foreground\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/* ── Switch ────────────────────────────────────────────────────────────────── */\n\n/**\n * A native-checkbox toggle styled to read like the shadcn Switch. `onCheckedChange`\n * mirrors the shadcn/Radix API so call sites stay identical.\n */\nexport function DevSwitch({\n checked,\n onCheckedChange,\n disabled,\n className,\n ...props\n}: {\n checked: boolean;\n onCheckedChange: (checked: boolean) => void;\n disabled?: boolean;\n className?: string;\n} & Omit<\n InputHTMLAttributes<HTMLButtonElement>,\n \"onChange\" | \"checked\" | \"type\"\n>) {\n return (\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={checked}\n disabled={disabled}\n data-plan-interactive\n onClick={() => onCheckedChange(!checked)}\n className={cn(\n \"peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50\",\n checked ? \"bg-primary\" : \"bg-input\",\n className,\n )}\n {...(props as Record<string, unknown>)}\n >\n <span\n className={cn(\n \"pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform\",\n checked ? \"translate-x-5\" : \"translate-x-0\",\n )}\n />\n </button>\n );\n}\n\n/* ── Select (native, shadcn-trigger styled) ────────────────────────────────── */\n\nexport interface DevSelectOption {\n value: string;\n label: ReactNode;\n}\n\n/**\n * A native `<select>` styled to match the shadcn SelectTrigger. Drop-in for the\n * simple enum pickers the dev-doc editors use. `onValueChange` mirrors the shadcn\n * API. The chevron is positioned over the native control.\n */\nexport function DevSelect({\n value,\n onValueChange,\n options,\n disabled,\n className,\n \"aria-label\": ariaLabel,\n}: {\n value: string;\n onValueChange: (value: string) => void;\n options: DevSelectOption[];\n disabled?: boolean;\n className?: string;\n \"aria-label\"?: string;\n}) {\n return (\n <div className={cn(\"relative\", className)}>\n <select\n value={value}\n disabled={disabled}\n aria-label={ariaLabel}\n data-plan-interactive\n onChange={(event) => onValueChange(event.target.value)}\n className={cn(\n \"flex h-10 w-full appearance-none items-center justify-between rounded-md border border-input bg-background px-3 py-2 pr-8 text-sm ring-offset-background focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50\",\n )}\n >\n {options.map((option) => (\n <option key={option.value} value={option.value}>\n {typeof option.label === \"string\" ? option.label : option.value}\n </option>\n ))}\n </select>\n <IconChevronDown className=\"pointer-events-none absolute right-2.5 top-1/2 size-4 -translate-y-1/2 opacity-50\" />\n </div>\n );\n}\n"]}
@@ -10,7 +10,7 @@ export declare function HtmlReadBlock({ data, blockId, title, ctx, }: BlockReadP
10
10
  * which the app routes through its generic `update-block` patch (re-validated by
11
11
  * the app schema).
12
12
  */
13
- export declare function HtmlEditBlock({ data, onChange, editable, title, ctx, }: BlockEditProps<HtmlBlockData>): import("react/jsx-runtime").JSX.Element;
13
+ export declare function HtmlEditBlock({ data, onChange, editable, blockId, title, summary, ctx, }: BlockEditProps<HtmlBlockData>): import("react/jsx-runtime").JSX.Element;
14
14
  /**
15
15
  * The standard HTML / Tailwind block spec. Both apps register this; the plan app
16
16
  * registers the matching React-free `{ schema, mdx }` server-side via
@@ -1 +1 @@
1
- {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/html.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAuB,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAqD3E,iFAAiF;AACjF,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,OAAO,EACP,KAAK,EACL,GAAG,GACJ,EAAE,cAAc,CAAC,aAAa,CAAC,2CAO/B;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,QAAQ,EACR,QAAQ,EACR,KAAK,EACL,GAAG,GACJ,EAAE,cAAc,CAAC,aAAa,CAAC,2CAiE/B;AAED;;;;;GAKG;AACH,eAAO,MAAM,SAAS,gDAepB,CAAC"}
1
+ {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/html.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElE,OAAO,EAAuB,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAqD3E,iFAAiF;AACjF,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,OAAO,EACP,KAAK,EACL,GAAG,GACJ,EAAE,cAAc,CAAC,aAAa,CAAC,2CAO/B;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,KAAK,EACL,OAAO,EACP,GAAG,GACJ,EAAE,cAAc,CAAC,aAAa,CAAC,2CAyI/B;AAED;;;;;GAKG;AACH,eAAO,MAAM,SAAS,gDAepB,CAAC"}
@@ -1,7 +1,8 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState } from "react";
2
+ import { useEffect, useId, useState } from "react";
3
3
  import { IconCode, IconEdit, IconX } from "@tabler/icons-react";
4
4
  import { defineBlock } from "../types.js";
5
+ import { AiEditableFieldLabel } from "../AiEditableField.js";
5
6
  import { htmlSchema, htmlMdx } from "./html.config.js";
6
7
  /**
7
8
  * Standard library HTML / Tailwind block. The registry form of the plan
@@ -39,12 +40,41 @@ export function HtmlReadBlock({ data, blockId, title, ctx, }) {
39
40
  * which the app routes through its generic `update-block` patch (re-validated by
40
41
  * the app schema).
41
42
  */
42
- export function HtmlEditBlock({ data, onChange, editable, title, ctx, }) {
43
+ export function HtmlEditBlock({ data, onChange, editable, blockId, title, summary, ctx, }) {
44
+ const htmlId = useId();
45
+ const cssId = useId();
46
+ const captionId = useId();
43
47
  const [editing, setEditing] = useState(false);
44
48
  const [html, setHtml] = useState(data.html);
45
49
  const [css, setCss] = useState(data.css ?? "");
46
- return (_jsxs("div", { className: "plan-html-block group", "data-an-block-edit": true, children: [_jsx("div", { className: "flex items-start justify-end gap-4", children: editable && (_jsxs("button", { type: "button", "data-plan-interactive": true, className: "inline-flex h-8 items-center gap-1.5 rounded-md px-2.5 text-sm font-medium text-muted-foreground transition-colors hover:bg-muted hover:text-foreground", onClick: () => setEditing((value) => !value), children: [editing ? (_jsx(IconX, { className: "size-4" })) : (_jsx(IconEdit, { className: "size-4" })), editing ? "Cancel" : "Edit source"] })) }), editing ? (_jsxs("div", { className: "mt-2 grid gap-3", "data-plan-interactive": true, children: [_jsx("textarea", { value: html, onChange: (event) => setHtml(event.target.value), className: "flex min-h-48 w-full rounded-md border border-input bg-transparent px-3 py-2 font-mono text-sm shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", placeholder: "HTML fragment" }), _jsx("textarea", { value: css, onChange: (event) => setCss(event.target.value), className: "flex min-h-32 w-full rounded-md border border-input bg-transparent px-3 py-2 font-mono text-sm shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", placeholder: "Optional CSS" }), _jsxs("div", { className: "flex justify-end gap-2", children: [_jsx("button", { type: "button", "data-plan-interactive": true, className: "inline-flex h-9 items-center rounded-md px-4 text-sm font-medium text-muted-foreground transition-colors hover:bg-muted hover:text-foreground", onClick: () => setEditing(false), children: "Cancel" }), _jsx("button", { type: "button", "data-plan-interactive": true, className: "inline-flex h-9 items-center rounded-md bg-primary px-4 text-sm font-medium text-primary-foreground shadow transition-colors hover:bg-primary/90", onClick: () => {
47
- onChange({ ...data, html, css: css || undefined });
50
+ const [caption, setCaption] = useState(data.caption ?? "");
51
+ useEffect(() => {
52
+ setHtml(data.html);
53
+ setCss(data.css ?? "");
54
+ setCaption(data.caption ?? "");
55
+ }, [data]);
56
+ const fieldAction = (field, value) => ({
57
+ blockId,
58
+ blockType: "custom-html",
59
+ blockTitle: title,
60
+ blockSummary: summary,
61
+ fieldValue: value,
62
+ draftScope: `block:custom-html:${blockId}:${field.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`,
63
+ disabled: !editable,
64
+ instructions: "Update the plan with update-visual-plan using a targeted update-block content patch for this custom-html block id. Preserve unrelated HTML/CSS/caption fields unless the requested edit requires changing them.",
65
+ companionFields: [
66
+ { label: "HTML fragment", value: html || "(empty)", language: "html" },
67
+ { label: "CSS", value: css || "(empty)", language: "css" },
68
+ { label: "Caption", value: caption || "(empty)", language: "text" },
69
+ ],
70
+ });
71
+ return (_jsxs("div", { className: "plan-html-block group", "data-an-block-edit": true, children: [_jsx("div", { className: "flex items-start justify-end gap-4", children: editable && (_jsx("button", { type: "button", "data-plan-interactive": true, "aria-label": editing ? "Cancel editing source" : "Edit source", className: "inline-flex size-8 items-center justify-center rounded-md text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", onClick: () => setEditing((value) => !value), children: editing ? (_jsx(IconX, { className: "size-4" })) : (_jsx(IconEdit, { className: "size-4" })) })) }), editing ? (_jsxs("div", { className: "mt-2 grid gap-3", "data-plan-interactive": true, children: [_jsxs("div", { className: "group/field grid gap-1.5", children: [_jsx(AiEditableFieldLabel, { htmlFor: htmlId, label: "HTML fragment", ctx: ctx, action: fieldAction("HTML fragment", html) }), _jsx("textarea", { id: htmlId, value: html, disabled: !editable, onChange: (event) => setHtml(event.target.value), className: "flex min-h-48 w-full rounded-md border border-input bg-transparent px-3 py-2 font-mono text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50", placeholder: "HTML fragment" })] }), _jsxs("div", { className: "group/field grid gap-1.5", children: [_jsx(AiEditableFieldLabel, { htmlFor: cssId, label: "CSS", ctx: ctx, action: fieldAction("CSS", css) }), _jsx("textarea", { id: cssId, value: css, disabled: !editable, onChange: (event) => setCss(event.target.value), className: "flex min-h-32 w-full rounded-md border border-input bg-transparent px-3 py-2 font-mono text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50", placeholder: "Optional CSS" })] }), _jsxs("div", { className: "group/field grid gap-1.5", children: [_jsx(AiEditableFieldLabel, { htmlFor: captionId, label: "Caption", ctx: ctx, action: fieldAction("Caption", caption) }), _jsx("input", { id: captionId, type: "text", value: caption, disabled: !editable, onChange: (event) => setCaption(event.target.value), className: "flex h-9 w-full rounded-md border border-input bg-transparent px-3 text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50", placeholder: "Optional caption" })] }), _jsxs("div", { className: "flex justify-end gap-2", children: [_jsx("button", { type: "button", "data-plan-interactive": true, className: "inline-flex h-9 items-center rounded-md px-4 text-sm font-medium text-muted-foreground transition-colors hover:bg-muted hover:text-foreground", onClick: () => setEditing(false), children: "Cancel" }), _jsx("button", { type: "button", "data-plan-interactive": true, className: "inline-flex h-9 items-center rounded-md bg-primary px-4 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90", onClick: () => {
72
+ onChange({
73
+ ...data,
74
+ html,
75
+ css: css || undefined,
76
+ caption: caption || undefined,
77
+ });
48
78
  setEditing(false);
49
79
  }, children: "Save" })] })] })) : (_jsx(HtmlPreview, { data: data, title: title, sanitize: ctx.sanitizeHtml }))] }));
50
80
  }
@@ -1 +1 @@
1
- {"version":3,"file":"html.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/html.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useState } from \"react\";\nimport { IconCode, IconEdit, IconX } from \"@tabler/icons-react\";\nimport { defineBlock } from \"../types.js\";\nimport type { BlockReadProps, BlockEditProps } from \"../types.js\";\nimport { htmlSchema, htmlMdx, type HtmlBlockData } from \"./html.config.js\";\n\n/**\n * Standard library HTML / Tailwind block. The registry form of the plan\n * `custom-html` block: an author-supplied HTML (+ optional CSS) fragment\n * rendered inside a sandboxed iframe, with an inline source editor.\n *\n * Security: the fragment is rendered in a `sandbox=\"allow-same-origin\"` iframe\n * with `referrerPolicy=\"no-referrer\"` — no scripts execute — and the schema's\n * `noFullHtmlDocument` refine rejects document/script/handler markup before it\n * is ever stored. When the app injects `ctx.sanitizeHtml`, the fragment + CSS\n * are additionally sanitized before being placed in the iframe `srcDoc`.\n *\n * Styling uses app-agnostic shadcn utility classes (`border`, `bg-muted`,\n * `text-muted-foreground`) so the block renders cleanly in any template, not\n * just the plan app.\n */\n\n/** Build the iframe document for a fragment, applying app sanitization if given. */\nfunction buildSrcDoc(\n data: HtmlBlockData,\n sanitize?: (html: string, css?: string) => string,\n): string {\n const css = data.css ?? \"\";\n const body = sanitize ? sanitize(data.html, data.css) : data.html;\n return `<!doctype html><html><head><style>body{margin:0;min-height:100%;font-family:Inter,system-ui,sans-serif;color:#1f1f1d;background:transparent;}*{box-sizing:border-box}${css}</style></head><body>${body}</body></html>`;\n}\n\nfunction HtmlPreview({\n data,\n title,\n sanitize,\n}: {\n data: HtmlBlockData;\n title?: string;\n sanitize?: (html: string, css?: string) => string;\n}) {\n return (\n <>\n <iframe\n title={title || \"Custom HTML block\"}\n srcDoc={buildSrcDoc(data, sanitize)}\n sandbox=\"allow-same-origin\"\n referrerPolicy=\"no-referrer\"\n className=\"mt-4 h-[360px] w-full rounded-xl border bg-muted\"\n />\n {data.caption && (\n <p className=\"mt-3 text-sm text-muted-foreground\">{data.caption}</p>\n )}\n </>\n );\n}\n\n/** Read-only renderer: the sandboxed iframe preview plus an optional caption. */\nexport function HtmlReadBlock({\n data,\n blockId,\n title,\n ctx,\n}: BlockReadProps<HtmlBlockData>) {\n return (\n <section className=\"plan-block group\" data-block-id={blockId}>\n {title && <div className=\"plan-block-label\">{title}</div>}\n <HtmlPreview data={data} title={title} sanitize={ctx.sanitizeHtml} />\n </section>\n );\n}\n\n/**\n * Custom editor: an \"Edit source\" toggle that flips between the live preview and\n * inline HTML + CSS textareas (ported from the plan `CustomHtmlBlock`). The\n * title is rendered by the registry's edit-mode section wrapper, so this only\n * renders the toggle + content. Edits commit the merged data via `onChange`,\n * which the app routes through its generic `update-block` patch (re-validated by\n * the app schema).\n */\nexport function HtmlEditBlock({\n data,\n onChange,\n editable,\n title,\n ctx,\n}: BlockEditProps<HtmlBlockData>) {\n const [editing, setEditing] = useState(false);\n const [html, setHtml] = useState(data.html);\n const [css, setCss] = useState(data.css ?? \"\");\n\n return (\n <div className=\"plan-html-block group\" data-an-block-edit>\n <div className=\"flex items-start justify-end gap-4\">\n {editable && (\n <button\n type=\"button\"\n data-plan-interactive\n className=\"inline-flex h-8 items-center gap-1.5 rounded-md px-2.5 text-sm font-medium text-muted-foreground transition-colors hover:bg-muted hover:text-foreground\"\n onClick={() => setEditing((value) => !value)}\n >\n {editing ? (\n <IconX className=\"size-4\" />\n ) : (\n <IconEdit className=\"size-4\" />\n )}\n {editing ? \"Cancel\" : \"Edit source\"}\n </button>\n )}\n </div>\n {editing ? (\n <div className=\"mt-2 grid gap-3\" data-plan-interactive>\n <textarea\n value={html}\n onChange={(event) => setHtml(event.target.value)}\n className=\"flex min-h-48 w-full rounded-md border border-input bg-transparent px-3 py-2 font-mono text-sm shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring\"\n placeholder=\"HTML fragment\"\n />\n <textarea\n value={css}\n onChange={(event) => setCss(event.target.value)}\n className=\"flex min-h-32 w-full rounded-md border border-input bg-transparent px-3 py-2 font-mono text-sm shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring\"\n placeholder=\"Optional CSS\"\n />\n <div className=\"flex justify-end gap-2\">\n <button\n type=\"button\"\n data-plan-interactive\n className=\"inline-flex h-9 items-center rounded-md px-4 text-sm font-medium text-muted-foreground transition-colors hover:bg-muted hover:text-foreground\"\n onClick={() => setEditing(false)}\n >\n Cancel\n </button>\n <button\n type=\"button\"\n data-plan-interactive\n className=\"inline-flex h-9 items-center rounded-md bg-primary px-4 text-sm font-medium text-primary-foreground shadow transition-colors hover:bg-primary/90\"\n onClick={() => {\n onChange({ ...data, html, css: css || undefined });\n setEditing(false);\n }}\n >\n Save\n </button>\n </div>\n </div>\n ) : (\n <HtmlPreview data={data} title={title} sanitize={ctx.sanitizeHtml} />\n )}\n </div>\n );\n}\n\n/**\n * The standard HTML / Tailwind block spec. Both apps register this; the plan app\n * registers the matching React-free `{ schema, mdx }` server-side via\n * `html.config.ts`. `empty()` seeds a friendly starter fragment for slash\n * insertion.\n */\nexport const htmlBlock = defineBlock<HtmlBlockData>({\n type: \"custom-html\",\n schema: htmlSchema,\n mdx: htmlMdx,\n Read: HtmlReadBlock,\n Edit: HtmlEditBlock,\n placement: [\"block\"],\n // Config-driven: the render (a sandboxed card) differs from its source, so edit\n // the html/css/caption from a corner button + panel rather than always-inline.\n editSurface: \"panel\",\n label: \"HTML / Tailwind\",\n icon: IconCode,\n description:\n \"An author-supplied HTML (with optional CSS) fragment rendered in a sandboxed iframe, with inline source editing.\",\n empty: () => ({ html: '<div class=\"p-6\">Edit this HTML fragment…</div>' }),\n});\n"]}
1
+ {"version":3,"file":"html.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/html.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useId, useState } from \"react\";\nimport { IconCode, IconEdit, IconX } from \"@tabler/icons-react\";\nimport { defineBlock } from \"../types.js\";\nimport type { BlockReadProps, BlockEditProps } from \"../types.js\";\nimport { AiEditableFieldLabel } from \"../AiEditableField.js\";\nimport { htmlSchema, htmlMdx, type HtmlBlockData } from \"./html.config.js\";\n\n/**\n * Standard library HTML / Tailwind block. The registry form of the plan\n * `custom-html` block: an author-supplied HTML (+ optional CSS) fragment\n * rendered inside a sandboxed iframe, with an inline source editor.\n *\n * Security: the fragment is rendered in a `sandbox=\"allow-same-origin\"` iframe\n * with `referrerPolicy=\"no-referrer\"` — no scripts execute — and the schema's\n * `noFullHtmlDocument` refine rejects document/script/handler markup before it\n * is ever stored. When the app injects `ctx.sanitizeHtml`, the fragment + CSS\n * are additionally sanitized before being placed in the iframe `srcDoc`.\n *\n * Styling uses app-agnostic shadcn utility classes (`border`, `bg-muted`,\n * `text-muted-foreground`) so the block renders cleanly in any template, not\n * just the plan app.\n */\n\n/** Build the iframe document for a fragment, applying app sanitization if given. */\nfunction buildSrcDoc(\n data: HtmlBlockData,\n sanitize?: (html: string, css?: string) => string,\n): string {\n const css = data.css ?? \"\";\n const body = sanitize ? sanitize(data.html, data.css) : data.html;\n return `<!doctype html><html><head><style>body{margin:0;min-height:100%;font-family:Inter,system-ui,sans-serif;color:#1f1f1d;background:transparent;}*{box-sizing:border-box}${css}</style></head><body>${body}</body></html>`;\n}\n\nfunction HtmlPreview({\n data,\n title,\n sanitize,\n}: {\n data: HtmlBlockData;\n title?: string;\n sanitize?: (html: string, css?: string) => string;\n}) {\n return (\n <>\n <iframe\n title={title || \"Custom HTML block\"}\n srcDoc={buildSrcDoc(data, sanitize)}\n sandbox=\"allow-same-origin\"\n referrerPolicy=\"no-referrer\"\n className=\"mt-4 h-[360px] w-full rounded-xl border bg-muted\"\n />\n {data.caption && (\n <p className=\"mt-3 text-sm text-muted-foreground\">{data.caption}</p>\n )}\n </>\n );\n}\n\n/** Read-only renderer: the sandboxed iframe preview plus an optional caption. */\nexport function HtmlReadBlock({\n data,\n blockId,\n title,\n ctx,\n}: BlockReadProps<HtmlBlockData>) {\n return (\n <section className=\"plan-block group\" data-block-id={blockId}>\n {title && <div className=\"plan-block-label\">{title}</div>}\n <HtmlPreview data={data} title={title} sanitize={ctx.sanitizeHtml} />\n </section>\n );\n}\n\n/**\n * Custom editor: an \"Edit source\" toggle that flips between the live preview and\n * inline HTML + CSS textareas (ported from the plan `CustomHtmlBlock`). The\n * title is rendered by the registry's edit-mode section wrapper, so this only\n * renders the toggle + content. Edits commit the merged data via `onChange`,\n * which the app routes through its generic `update-block` patch (re-validated by\n * the app schema).\n */\nexport function HtmlEditBlock({\n data,\n onChange,\n editable,\n blockId,\n title,\n summary,\n ctx,\n}: BlockEditProps<HtmlBlockData>) {\n const htmlId = useId();\n const cssId = useId();\n const captionId = useId();\n const [editing, setEditing] = useState(false);\n const [html, setHtml] = useState(data.html);\n const [css, setCss] = useState(data.css ?? \"\");\n const [caption, setCaption] = useState(data.caption ?? \"\");\n\n useEffect(() => {\n setHtml(data.html);\n setCss(data.css ?? \"\");\n setCaption(data.caption ?? \"\");\n }, [data]);\n\n const fieldAction = (\n field: \"HTML fragment\" | \"CSS\" | \"Caption\",\n value: string,\n ) => ({\n blockId,\n blockType: \"custom-html\",\n blockTitle: title,\n blockSummary: summary,\n fieldValue: value,\n draftScope: `block:custom-html:${blockId}:${field.toLowerCase().replace(/[^a-z0-9]+/g, \"-\")}`,\n disabled: !editable,\n instructions:\n \"Update the plan with update-visual-plan using a targeted update-block content patch for this custom-html block id. Preserve unrelated HTML/CSS/caption fields unless the requested edit requires changing them.\",\n companionFields: [\n { label: \"HTML fragment\", value: html || \"(empty)\", language: \"html\" },\n { label: \"CSS\", value: css || \"(empty)\", language: \"css\" },\n { label: \"Caption\", value: caption || \"(empty)\", language: \"text\" },\n ],\n });\n\n return (\n <div className=\"plan-html-block group\" data-an-block-edit>\n <div className=\"flex items-start justify-end gap-4\">\n {editable && (\n <button\n type=\"button\"\n data-plan-interactive\n aria-label={editing ? \"Cancel editing source\" : \"Edit source\"}\n className=\"inline-flex size-8 items-center justify-center rounded-md text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring\"\n onClick={() => setEditing((value) => !value)}\n >\n {editing ? (\n <IconX className=\"size-4\" />\n ) : (\n <IconEdit className=\"size-4\" />\n )}\n </button>\n )}\n </div>\n {editing ? (\n <div className=\"mt-2 grid gap-3\" data-plan-interactive>\n <div className=\"group/field grid gap-1.5\">\n <AiEditableFieldLabel\n htmlFor={htmlId}\n label=\"HTML fragment\"\n ctx={ctx}\n action={fieldAction(\"HTML fragment\", html)}\n />\n <textarea\n id={htmlId}\n value={html}\n disabled={!editable}\n onChange={(event) => setHtml(event.target.value)}\n className=\"flex min-h-48 w-full rounded-md border border-input bg-transparent px-3 py-2 font-mono text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50\"\n placeholder=\"HTML fragment\"\n />\n </div>\n <div className=\"group/field grid gap-1.5\">\n <AiEditableFieldLabel\n htmlFor={cssId}\n label=\"CSS\"\n ctx={ctx}\n action={fieldAction(\"CSS\", css)}\n />\n <textarea\n id={cssId}\n value={css}\n disabled={!editable}\n onChange={(event) => setCss(event.target.value)}\n className=\"flex min-h-32 w-full rounded-md border border-input bg-transparent px-3 py-2 font-mono text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50\"\n placeholder=\"Optional CSS\"\n />\n </div>\n <div className=\"group/field grid gap-1.5\">\n <AiEditableFieldLabel\n htmlFor={captionId}\n label=\"Caption\"\n ctx={ctx}\n action={fieldAction(\"Caption\", caption)}\n />\n <input\n id={captionId}\n type=\"text\"\n value={caption}\n disabled={!editable}\n onChange={(event) => setCaption(event.target.value)}\n className=\"flex h-9 w-full rounded-md border border-input bg-transparent px-3 text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50\"\n placeholder=\"Optional caption\"\n />\n </div>\n <div className=\"flex justify-end gap-2\">\n <button\n type=\"button\"\n data-plan-interactive\n className=\"inline-flex h-9 items-center rounded-md px-4 text-sm font-medium text-muted-foreground transition-colors hover:bg-muted hover:text-foreground\"\n onClick={() => setEditing(false)}\n >\n Cancel\n </button>\n <button\n type=\"button\"\n data-plan-interactive\n className=\"inline-flex h-9 items-center rounded-md bg-primary px-4 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90\"\n onClick={() => {\n onChange({\n ...data,\n html,\n css: css || undefined,\n caption: caption || undefined,\n });\n setEditing(false);\n }}\n >\n Save\n </button>\n </div>\n </div>\n ) : (\n <HtmlPreview data={data} title={title} sanitize={ctx.sanitizeHtml} />\n )}\n </div>\n );\n}\n\n/**\n * The standard HTML / Tailwind block spec. Both apps register this; the plan app\n * registers the matching React-free `{ schema, mdx }` server-side via\n * `html.config.ts`. `empty()` seeds a friendly starter fragment for slash\n * insertion.\n */\nexport const htmlBlock = defineBlock<HtmlBlockData>({\n type: \"custom-html\",\n schema: htmlSchema,\n mdx: htmlMdx,\n Read: HtmlReadBlock,\n Edit: HtmlEditBlock,\n placement: [\"block\"],\n // Config-driven: the render (a sandboxed card) differs from its source, so edit\n // the html/css/caption from a corner button + panel rather than always-inline.\n editSurface: \"panel\",\n label: \"HTML / Tailwind\",\n icon: IconCode,\n description:\n \"An author-supplied HTML (with optional CSS) fragment rendered in a sandboxed iframe, with inline source editing.\",\n empty: () => ({ html: '<div class=\"p-6\">Edit this HTML fragment…</div>' }),\n});\n"]}
@@ -22,13 +22,15 @@ import type { BlockMdxConfig } from "../types.js";
22
22
  * children) — the shared `prop()` encoder round-trips multiline strings cleanly,
23
23
  * and keeping it an attribute avoids the payload being reflowed as prose.
24
24
  */
25
+ export declare const JSON_EXPLORER_DEFAULT_COLLAPSED_DEPTH = 2;
26
+ export declare const JSON_EXPLORER_MAX_COLLAPSED_DEPTH = 20;
25
27
  export interface JsonExplorerData {
26
28
  /** Optional heading shown above the tree. */
27
29
  title?: string;
28
30
  /** Raw JSON text — the source of truth, parsed defensively at render time. */
29
31
  json: string;
30
32
  /**
31
- * Depth beyond which nodes start collapsed (default 1). Nodes at a depth `<`
33
+ * Depth beyond which nodes start collapsed (default 2). Nodes at a depth `<`
32
34
  * this value render expanded; deeper nodes render collapsed until clicked.
33
35
  */
34
36
  collapsedDepth?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"json-explorer.config.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/json-explorer.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,eAAO,MAAM,kBAAkB,EAId,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;AAE7C;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,EAAE,cAAc,CAAC,gBAAgB,CAY5D,CAAC"}
1
+ {"version":3,"file":"json-explorer.config.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/json-explorer.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,eAAO,MAAM,qCAAqC,IAAI,CAAC;AACvD,eAAO,MAAM,iCAAiC,KAAK,CAAC;AAEpD,MAAM,WAAW,gBAAgB;IAC/B,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,eAAO,MAAM,kBAAkB,EASd,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;AAE7C;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,EAAE,cAAc,CAAC,gBAAgB,CAY5D,CAAC"}
@@ -1,8 +1,37 @@
1
1
  import { z } from "zod";
2
+ /**
3
+ * Pure (React-free) part of the PLAN-SPECIFIC `json-explorer` block: its data
4
+ * schema and MDX round-trip config. Shared by the server MDX adapter
5
+ * (`plan-mdx.ts` via `plan-block-registry.ts`) and the client spec
6
+ * (`planBlocks.tsx`). Keeping this React-free means importing it into a server
7
+ * module never pulls React into the Nitro/SSR bundle.
8
+ *
9
+ * The block renders a browser-devtools / Postman-style collapsible JSON tree:
10
+ * object/array nodes show a chevron + a one-line summary ("{…} 3 keys" /
11
+ * "[…] 5 items") and expand/collapse; leaf values are type-colored (string =
12
+ * green, number = blue, boolean = violet, null = muted). The raw JSON TEXT is
13
+ * the source of truth (`json`), so authoring round-trips losslessly even when
14
+ * the JSON is invalid — the reader parses defensively and falls back to the raw
15
+ * text on a parse error rather than throwing.
16
+ *
17
+ * The schema MUST stay data-compatible with the inline `json-explorer` member of
18
+ * `planBlockSchema` (`plan-content.ts`), and the MDX `tag` (`Json`) +
19
+ * attribute shape MUST match the `<Json title json collapsedDepth />` encoding so
20
+ * stored `.mdx` round-trips. `json` lives in the `json` ATTRIBUTE (not MDX
21
+ * children) — the shared `prop()` encoder round-trips multiline strings cleanly,
22
+ * and keeping it an attribute avoids the payload being reflowed as prose.
23
+ */
24
+ export const JSON_EXPLORER_DEFAULT_COLLAPSED_DEPTH = 2;
25
+ export const JSON_EXPLORER_MAX_COLLAPSED_DEPTH = 20;
2
26
  export const jsonExplorerSchema = z.object({
3
27
  title: z.string().trim().max(200).optional(),
4
28
  json: z.string().max(200_000),
5
- collapsedDepth: z.number().int().min(0).max(20).optional(),
29
+ collapsedDepth: z
30
+ .number()
31
+ .int()
32
+ .min(0)
33
+ .max(JSON_EXPLORER_MAX_COLLAPSED_DEPTH)
34
+ .optional(),
6
35
  });
7
36
  /**
8
37
  * MDX config: `title`, `json`, and `collapsedDepth` are flat attributes — the
@@ -1 +1 @@
1
- {"version":3,"file":"json-explorer.config.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/json-explorer.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAsCxB,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;IAC5C,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC;IAC7B,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE;CAC3D,CAA2C,CAAC;AAE7C;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,eAAe,GAAqC;IAC/D,GAAG,EAAE,MAAM;IACX,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAClB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,cAAc,EAAE,IAAI,CAAC,cAAc;KACpC,CAAC;IACF,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACrB,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE;QAChC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC;QAC5B,cAAc,EAAE,KAAK,CAAC,MAAM,CAAC,gBAAgB,CAAC;KAC/C,CAAC;CACH,CAAC","sourcesContent":["import { z } from \"zod\";\nimport type { BlockMdxConfig } from \"../types.js\";\n\n/**\n * Pure (React-free) part of the PLAN-SPECIFIC `json-explorer` block: its data\n * schema and MDX round-trip config. Shared by the server MDX adapter\n * (`plan-mdx.ts` via `plan-block-registry.ts`) and the client spec\n * (`planBlocks.tsx`). Keeping this React-free means importing it into a server\n * module never pulls React into the Nitro/SSR bundle.\n *\n * The block renders a browser-devtools / Postman-style collapsible JSON tree:\n * object/array nodes show a chevron + a one-line summary (\"{…} 3 keys\" /\n * \"[…] 5 items\") and expand/collapse; leaf values are type-colored (string =\n * green, number = blue, boolean = violet, null = muted). The raw JSON TEXT is\n * the source of truth (`json`), so authoring round-trips losslessly even when\n * the JSON is invalid — the reader parses defensively and falls back to the raw\n * text on a parse error rather than throwing.\n *\n * The schema MUST stay data-compatible with the inline `json-explorer` member of\n * `planBlockSchema` (`plan-content.ts`), and the MDX `tag` (`Json`) +\n * attribute shape MUST match the `<Json title json collapsedDepth />` encoding so\n * stored `.mdx` round-trips. `json` lives in the `json` ATTRIBUTE (not MDX\n * children) — the shared `prop()` encoder round-trips multiline strings cleanly,\n * and keeping it an attribute avoids the payload being reflowed as prose.\n */\n\nexport interface JsonExplorerData {\n /** Optional heading shown above the tree. */\n title?: string;\n /** Raw JSON text — the source of truth, parsed defensively at render time. */\n json: string;\n /**\n * Depth beyond which nodes start collapsed (default 1). Nodes at a depth `<`\n * this value render expanded; deeper nodes render collapsed until clicked.\n */\n collapsedDepth?: number;\n}\n\nexport const jsonExplorerSchema = z.object({\n title: z.string().trim().max(200).optional(),\n json: z.string().max(200_000),\n collapsedDepth: z.number().int().min(0).max(20).optional(),\n}) as unknown as z.ZodType<JsonExplorerData>;\n\n/**\n * MDX config: `title`, `json`, and `collapsedDepth` are flat attributes — the\n * `<Json id … title json collapsedDepth />` self-closing form. Insertion order\n * of `toAttrs` is the on-disk attribute order (`title` → `json` →\n * `collapsedDepth`). `fromAttrs` mirrors a forgiving parse (`json ?? \"\"`,\n * `title`/`collapsedDepth` undefined when absent) so a plan missing an attribute\n * still parses.\n */\nexport const jsonExplorerMdx: BlockMdxConfig<JsonExplorerData> = {\n tag: \"Json\",\n toAttrs: (data) => ({\n title: data.title,\n json: data.json,\n collapsedDepth: data.collapsedDepth,\n }),\n fromAttrs: (attrs) => ({\n json: attrs.string(\"json\") ?? \"\",\n title: attrs.string(\"title\"),\n collapsedDepth: attrs.number(\"collapsedDepth\"),\n }),\n};\n"]}
1
+ {"version":3,"file":"json-explorer.config.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/json-explorer.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,MAAM,CAAC,MAAM,qCAAqC,GAAG,CAAC,CAAC;AACvD,MAAM,CAAC,MAAM,iCAAiC,GAAG,EAAE,CAAC;AAcpD,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;IAC5C,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC;IAC7B,cAAc,EAAE,CAAC;SACd,MAAM,EAAE;SACR,GAAG,EAAE;SACL,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,iCAAiC,CAAC;SACtC,QAAQ,EAAE;CACd,CAA2C,CAAC;AAE7C;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,eAAe,GAAqC;IAC/D,GAAG,EAAE,MAAM;IACX,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAClB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,cAAc,EAAE,IAAI,CAAC,cAAc;KACpC,CAAC;IACF,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACrB,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE;QAChC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC;QAC5B,cAAc,EAAE,KAAK,CAAC,MAAM,CAAC,gBAAgB,CAAC;KAC/C,CAAC;CACH,CAAC","sourcesContent":["import { z } from \"zod\";\nimport type { BlockMdxConfig } from \"../types.js\";\n\n/**\n * Pure (React-free) part of the PLAN-SPECIFIC `json-explorer` block: its data\n * schema and MDX round-trip config. Shared by the server MDX adapter\n * (`plan-mdx.ts` via `plan-block-registry.ts`) and the client spec\n * (`planBlocks.tsx`). Keeping this React-free means importing it into a server\n * module never pulls React into the Nitro/SSR bundle.\n *\n * The block renders a browser-devtools / Postman-style collapsible JSON tree:\n * object/array nodes show a chevron + a one-line summary (\"{…} 3 keys\" /\n * \"[…] 5 items\") and expand/collapse; leaf values are type-colored (string =\n * green, number = blue, boolean = violet, null = muted). The raw JSON TEXT is\n * the source of truth (`json`), so authoring round-trips losslessly even when\n * the JSON is invalid — the reader parses defensively and falls back to the raw\n * text on a parse error rather than throwing.\n *\n * The schema MUST stay data-compatible with the inline `json-explorer` member of\n * `planBlockSchema` (`plan-content.ts`), and the MDX `tag` (`Json`) +\n * attribute shape MUST match the `<Json title json collapsedDepth />` encoding so\n * stored `.mdx` round-trips. `json` lives in the `json` ATTRIBUTE (not MDX\n * children) — the shared `prop()` encoder round-trips multiline strings cleanly,\n * and keeping it an attribute avoids the payload being reflowed as prose.\n */\n\nexport const JSON_EXPLORER_DEFAULT_COLLAPSED_DEPTH = 2;\nexport const JSON_EXPLORER_MAX_COLLAPSED_DEPTH = 20;\n\nexport interface JsonExplorerData {\n /** Optional heading shown above the tree. */\n title?: string;\n /** Raw JSON text — the source of truth, parsed defensively at render time. */\n json: string;\n /**\n * Depth beyond which nodes start collapsed (default 2). Nodes at a depth `<`\n * this value render expanded; deeper nodes render collapsed until clicked.\n */\n collapsedDepth?: number;\n}\n\nexport const jsonExplorerSchema = z.object({\n title: z.string().trim().max(200).optional(),\n json: z.string().max(200_000),\n collapsedDepth: z\n .number()\n .int()\n .min(0)\n .max(JSON_EXPLORER_MAX_COLLAPSED_DEPTH)\n .optional(),\n}) as unknown as z.ZodType<JsonExplorerData>;\n\n/**\n * MDX config: `title`, `json`, and `collapsedDepth` are flat attributes — the\n * `<Json id … title json collapsedDepth />` self-closing form. Insertion order\n * of `toAttrs` is the on-disk attribute order (`title` → `json` →\n * `collapsedDepth`). `fromAttrs` mirrors a forgiving parse (`json ?? \"\"`,\n * `title`/`collapsedDepth` undefined when absent) so a plan missing an attribute\n * still parses.\n */\nexport const jsonExplorerMdx: BlockMdxConfig<JsonExplorerData> = {\n tag: \"Json\",\n toAttrs: (data) => ({\n title: data.title,\n json: data.json,\n collapsedDepth: data.collapsedDepth,\n }),\n fromAttrs: (attrs) => ({\n json: attrs.string(\"json\") ?? \"\",\n title: attrs.string(\"title\"),\n collapsedDepth: attrs.number(\"collapsedDepth\"),\n }),\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"server-specs.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/server-specs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAkB,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AA0DpE;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,CAAC,GAAG,CAAC,EAqI/C,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,2BAA2B,GAAG,MAAM,CAC9C,MAAM,EACN,OAAO,CACL,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,aAAa,GAAG,kBAAkB,CAAC,CAC5E,CACF,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,aAAa,EACvB,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,2BAA2B,CAAA;CAAO,GACxD,IAAI,CAON"}
1
+ {"version":3,"file":"server-specs.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/server-specs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAkB,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AA+DpE;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,CAAC,GAAG,CAAC,EA+I/C,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,2BAA2B,GAAG,MAAM,CAC9C,MAAM,EACN,OAAO,CACL,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,aAAa,GAAG,kBAAkB,CAAC,CAC5E,CACF,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,aAAa,EACvB,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,2BAA2B,CAAA;CAAO,GACxD,IAAI,CAON"}