@agent-native/core 0.38.0 → 0.39.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 (156) hide show
  1. package/dist/cli/create.d.ts.map +1 -1
  2. package/dist/cli/create.js +8 -1
  3. package/dist/cli/create.js.map +1 -1
  4. package/dist/cli/skills.d.ts +5 -4
  5. package/dist/cli/skills.d.ts.map +1 -1
  6. package/dist/cli/skills.js +450 -125
  7. package/dist/cli/skills.js.map +1 -1
  8. package/dist/client/blocks/BlockView.d.ts +13 -4
  9. package/dist/client/blocks/BlockView.d.ts.map +1 -1
  10. package/dist/client/blocks/BlockView.js +34 -13
  11. package/dist/client/blocks/BlockView.js.map +1 -1
  12. package/dist/client/blocks/SchemaBlockEditor.d.ts.map +1 -1
  13. package/dist/client/blocks/SchemaBlockEditor.js +96 -3
  14. package/dist/client/blocks/SchemaBlockEditor.js.map +1 -1
  15. package/dist/client/blocks/index.d.ts +18 -1
  16. package/dist/client/blocks/index.d.ts.map +1 -1
  17. package/dist/client/blocks/index.js +26 -1
  18. package/dist/client/blocks/index.js.map +1 -1
  19. package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts +6 -0
  20. package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts.map +1 -0
  21. package/dist/client/blocks/library/AnnotatedCodeBlock.js +135 -0
  22. package/dist/client/blocks/library/AnnotatedCodeBlock.js.map +1 -0
  23. package/dist/client/blocks/library/ApiEndpointBlock.d.ts +20 -0
  24. package/dist/client/blocks/library/ApiEndpointBlock.d.ts.map +1 -0
  25. package/dist/client/blocks/library/ApiEndpointBlock.js +131 -0
  26. package/dist/client/blocks/library/ApiEndpointBlock.js.map +1 -0
  27. package/dist/client/blocks/library/DataModelBlock.d.ts +28 -0
  28. package/dist/client/blocks/library/DataModelBlock.d.ts.map +1 -0
  29. package/dist/client/blocks/library/DataModelBlock.js +222 -0
  30. package/dist/client/blocks/library/DataModelBlock.js.map +1 -0
  31. package/dist/client/blocks/library/DiffBlock.d.ts +6 -0
  32. package/dist/client/blocks/library/DiffBlock.d.ts.map +1 -0
  33. package/dist/client/blocks/library/DiffBlock.js +293 -0
  34. package/dist/client/blocks/library/DiffBlock.js.map +1 -0
  35. package/dist/client/blocks/library/FileTreeBlock.d.ts +23 -0
  36. package/dist/client/blocks/library/FileTreeBlock.d.ts.map +1 -0
  37. package/dist/client/blocks/library/FileTreeBlock.js +225 -0
  38. package/dist/client/blocks/library/FileTreeBlock.js.map +1 -0
  39. package/dist/client/blocks/library/JsonExplorerBlock.d.ts +19 -0
  40. package/dist/client/blocks/library/JsonExplorerBlock.d.ts.map +1 -0
  41. package/dist/client/blocks/library/JsonExplorerBlock.js +171 -0
  42. package/dist/client/blocks/library/JsonExplorerBlock.js.map +1 -0
  43. package/dist/client/blocks/library/MermaidBlock.d.ts +17 -0
  44. package/dist/client/blocks/library/MermaidBlock.d.ts.map +1 -0
  45. package/dist/client/blocks/library/MermaidBlock.js +131 -0
  46. package/dist/client/blocks/library/MermaidBlock.js.map +1 -0
  47. package/dist/client/blocks/library/OpenApiSpecBlock.d.ts +19 -0
  48. package/dist/client/blocks/library/OpenApiSpecBlock.d.ts.map +1 -0
  49. package/dist/client/blocks/library/OpenApiSpecBlock.js +494 -0
  50. package/dist/client/blocks/library/OpenApiSpecBlock.js.map +1 -0
  51. package/dist/client/blocks/library/annotated-code.config.d.ts +58 -0
  52. package/dist/client/blocks/library/annotated-code.config.d.ts.map +1 -0
  53. package/dist/client/blocks/library/annotated-code.config.js +53 -0
  54. package/dist/client/blocks/library/annotated-code.config.js.map +1 -0
  55. package/dist/client/blocks/library/api-endpoint.config.d.ts +71 -0
  56. package/dist/client/blocks/library/api-endpoint.config.d.ts.map +1 -0
  57. package/dist/client/blocks/library/api-endpoint.config.js +91 -0
  58. package/dist/client/blocks/library/api-endpoint.config.js.map +1 -0
  59. package/dist/client/blocks/library/checklist.d.ts.map +1 -1
  60. package/dist/client/blocks/library/checklist.js +3 -1
  61. package/dist/client/blocks/library/checklist.js.map +1 -1
  62. package/dist/client/blocks/library/code-tabs.js +1 -1
  63. package/dist/client/blocks/library/code-tabs.js.map +1 -1
  64. package/dist/client/blocks/library/data-model.config.d.ts +72 -0
  65. package/dist/client/blocks/library/data-model.config.d.ts.map +1 -0
  66. package/dist/client/blocks/library/data-model.config.js +59 -0
  67. package/dist/client/blocks/library/data-model.config.js.map +1 -0
  68. package/dist/client/blocks/library/dev-doc-ui.d.ts +49 -0
  69. package/dist/client/blocks/library/dev-doc-ui.d.ts.map +1 -0
  70. package/dist/client/blocks/library/dev-doc-ui.js +50 -0
  71. package/dist/client/blocks/library/dev-doc-ui.js.map +1 -0
  72. package/dist/client/blocks/library/diff.config.d.ts +41 -0
  73. package/dist/client/blocks/library/diff.config.d.ts.map +1 -0
  74. package/dist/client/blocks/library/diff.config.js +34 -0
  75. package/dist/client/blocks/library/diff.config.js.map +1 -0
  76. package/dist/client/blocks/library/file-tree.config.d.ts +59 -0
  77. package/dist/client/blocks/library/file-tree.config.d.ts.map +1 -0
  78. package/dist/client/blocks/library/file-tree.config.js +45 -0
  79. package/dist/client/blocks/library/file-tree.config.js.map +1 -0
  80. package/dist/client/blocks/library/html.d.ts.map +1 -1
  81. package/dist/client/blocks/library/html.js +4 -1
  82. package/dist/client/blocks/library/html.js.map +1 -1
  83. package/dist/client/blocks/library/json-explorer.config.d.ts +46 -0
  84. package/dist/client/blocks/library/json-explorer.config.d.ts.map +1 -0
  85. package/dist/client/blocks/library/json-explorer.config.js +28 -0
  86. package/dist/client/blocks/library/json-explorer.config.js.map +1 -0
  87. package/dist/client/blocks/library/mermaid.config.d.ts +32 -0
  88. package/dist/client/blocks/library/mermaid.config.d.ts.map +1 -0
  89. package/dist/client/blocks/library/mermaid.config.js +24 -0
  90. package/dist/client/blocks/library/mermaid.config.js.map +1 -0
  91. package/dist/client/blocks/library/openapi-spec.config.d.ts +49 -0
  92. package/dist/client/blocks/library/openapi-spec.config.d.ts.map +1 -0
  93. package/dist/client/blocks/library/openapi-spec.config.js +24 -0
  94. package/dist/client/blocks/library/openapi-spec.config.js.map +1 -0
  95. package/dist/client/blocks/library/server-specs.d.ts +35 -0
  96. package/dist/client/blocks/library/server-specs.d.ts.map +1 -0
  97. package/dist/client/blocks/library/server-specs.js +171 -0
  98. package/dist/client/blocks/library/server-specs.js.map +1 -0
  99. package/dist/client/blocks/library/specs.d.ts +29 -0
  100. package/dist/client/blocks/library/specs.d.ts.map +1 -0
  101. package/dist/client/blocks/library/specs.js +229 -0
  102. package/dist/client/blocks/library/specs.js.map +1 -0
  103. package/dist/client/blocks/library/table.d.ts.map +1 -1
  104. package/dist/client/blocks/library/table.js +3 -1
  105. package/dist/client/blocks/library/table.js.map +1 -1
  106. package/dist/client/blocks/library/tabs.js +1 -1
  107. package/dist/client/blocks/library/tabs.js.map +1 -1
  108. package/dist/client/blocks/registry.d.ts +8 -0
  109. package/dist/client/blocks/registry.d.ts.map +1 -1
  110. package/dist/client/blocks/registry.js +15 -0
  111. package/dist/client/blocks/registry.js.map +1 -1
  112. package/dist/client/blocks/server.d.ts +9 -0
  113. package/dist/client/blocks/server.d.ts.map +1 -1
  114. package/dist/client/blocks/server.js +16 -0
  115. package/dist/client/blocks/server.js.map +1 -1
  116. package/dist/client/blocks/types.d.ts +40 -0
  117. package/dist/client/blocks/types.d.ts.map +1 -1
  118. package/dist/client/blocks/types.js.map +1 -1
  119. package/dist/client/index.d.ts +2 -1
  120. package/dist/client/index.d.ts.map +1 -1
  121. package/dist/client/index.js +10 -1
  122. package/dist/client/index.js.map +1 -1
  123. package/dist/client/rich-markdown-editor/DragHandle.d.ts +52 -0
  124. package/dist/client/rich-markdown-editor/DragHandle.d.ts.map +1 -0
  125. package/dist/client/rich-markdown-editor/DragHandle.js +403 -0
  126. package/dist/client/rich-markdown-editor/DragHandle.js.map +1 -0
  127. package/dist/client/rich-markdown-editor/RegistryBlockNode.d.ts +97 -0
  128. package/dist/client/rich-markdown-editor/RegistryBlockNode.d.ts.map +1 -0
  129. package/dist/client/rich-markdown-editor/RegistryBlockNode.js +214 -0
  130. package/dist/client/rich-markdown-editor/RegistryBlockNode.js.map +1 -0
  131. package/dist/client/rich-markdown-editor/RunId.d.ts +28 -0
  132. package/dist/client/rich-markdown-editor/RunId.d.ts.map +1 -0
  133. package/dist/client/rich-markdown-editor/RunId.js +60 -0
  134. package/dist/client/rich-markdown-editor/RunId.js.map +1 -0
  135. package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts +25 -1
  136. package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts.map +1 -1
  137. package/dist/client/rich-markdown-editor/SharedRichEditor.js +14 -5
  138. package/dist/client/rich-markdown-editor/SharedRichEditor.js.map +1 -1
  139. package/dist/client/rich-markdown-editor/gfmDoc.d.ts +24 -0
  140. package/dist/client/rich-markdown-editor/gfmDoc.d.ts.map +1 -0
  141. package/dist/client/rich-markdown-editor/gfmDoc.js +83 -0
  142. package/dist/client/rich-markdown-editor/gfmDoc.js.map +1 -0
  143. package/dist/client/rich-markdown-editor/index.d.ts +5 -0
  144. package/dist/client/rich-markdown-editor/index.d.ts.map +1 -1
  145. package/dist/client/rich-markdown-editor/index.js +5 -0
  146. package/dist/client/rich-markdown-editor/index.js.map +1 -1
  147. package/dist/client/rich-markdown-editor/registrySlashCommands.d.ts +46 -0
  148. package/dist/client/rich-markdown-editor/registrySlashCommands.d.ts.map +1 -0
  149. package/dist/client/rich-markdown-editor/registrySlashCommands.js +13 -0
  150. package/dist/client/rich-markdown-editor/registrySlashCommands.js.map +1 -0
  151. package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
  152. package/dist/client/rich-markdown-editor/useCollabReconcile.js +33 -0
  153. package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
  154. package/docs/content/template-plan.md +19 -4
  155. package/docs/content/visual-plans.md +3 -1
  156. package/package.json +1 -1
@@ -0,0 +1,171 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useId, useMemo, useState } from "react";
3
+ import { IconChevronRight } from "@tabler/icons-react";
4
+ import { cn } from "../../utils.js";
5
+ import { DevInput, DevLabel, DevTextarea } from "./dev-doc-ui.js";
6
+ /**
7
+ * Read + Edit renderers for a `json-explorer` block — a browser-devtools /
8
+ * Postman-style collapsible JSON tree. The raw JSON TEXT (`data.json`) is the
9
+ * source of truth; the Read renderer parses it defensively and, on any parse
10
+ * error, falls back to the raw text plus the error message (it never throws).
11
+ * Lives in core so any app can register the dev-doc block (no shadcn import).
12
+ *
13
+ * Progressive disclosure is the whole point: object/array nodes show a chevron
14
+ * and a one-line summary ("{…} 3 keys" / "[…] 5 items"); each node tracks its
15
+ * own open/closed state (`useState`) seeded by `collapsedDepth` so deep payloads
16
+ * stay scannable. Leaf values are type-colored (string = green, number = blue,
17
+ * boolean = violet, null = muted); keys use a stable accent color; subtle indent
18
+ * guide lines mark nesting.
19
+ *
20
+ * DARK/LIGHT: the plan editor toggles a `.dark` class on <html>. Every color
21
+ * token — value types, keys, guide lines, chrome — uses Tailwind `dark:` variants
22
+ * or the theme-aware plan CSS-var utilities, so the tree reads correctly in BOTH
23
+ * modes (no hardcoded dark-only palette).
24
+ */
25
+ /* ── Theme-aware value-type color tokens ───────────────────────────────────── */
26
+ /** String leaves: green in both modes. */
27
+ const STRING_CLASS = "text-emerald-700 dark:text-emerald-300";
28
+ /** Number leaves: blue in both modes. */
29
+ const NUMBER_CLASS = "text-blue-700 dark:text-blue-300";
30
+ /** Boolean leaves: violet in both modes. */
31
+ const BOOLEAN_CLASS = "text-violet-700 dark:text-violet-300";
32
+ /** `null`/`undefined` leaves: muted (theme-aware plan var). */
33
+ const NULL_CLASS = "text-plan-muted italic";
34
+ /** Object keys: a stable, saturated accent that reads in both modes. */
35
+ const KEY_CLASS = "text-rose-700 dark:text-rose-300";
36
+ /** Structural punctuation (braces, brackets, commas, colons). */
37
+ const PUNCT_CLASS = "text-plan-muted";
38
+ function parseJson(raw) {
39
+ const trimmed = raw.trim();
40
+ if (!trimmed) {
41
+ return { ok: false, error: "Empty payload — add some JSON to explore." };
42
+ }
43
+ try {
44
+ return { ok: true, value: JSON.parse(trimmed) };
45
+ }
46
+ catch (error) {
47
+ return {
48
+ ok: false,
49
+ error: error instanceof Error ? error.message : "Invalid JSON",
50
+ };
51
+ }
52
+ }
53
+ function isContainer(value) {
54
+ return value !== null && typeof value === "object";
55
+ }
56
+ /** One-line summary for a collapsed container, devtools style. */
57
+ function containerSummary(value) {
58
+ if (Array.isArray(value)) {
59
+ const count = value.length;
60
+ return `[…] ${count} ${count === 1 ? "item" : "items"}`;
61
+ }
62
+ const count = Object.keys(value).length;
63
+ return `{…} ${count} ${count === 1 ? "key" : "keys"}`;
64
+ }
65
+ /** Render a leaf (primitive) value with its type color. */
66
+ function LeafValue({ value }) {
67
+ if (value === null) {
68
+ return _jsx("span", { className: NULL_CLASS, children: "null" });
69
+ }
70
+ if (typeof value === "string") {
71
+ return _jsx("span", { className: STRING_CLASS, children: JSON.stringify(value) });
72
+ }
73
+ if (typeof value === "number") {
74
+ return _jsx("span", { className: NUMBER_CLASS, children: String(value) });
75
+ }
76
+ // boolean
77
+ return _jsx("span", { className: BOOLEAN_CLASS, children: String(value) });
78
+ }
79
+ /**
80
+ * A single tree node. Containers (object/array) get their own collapse state,
81
+ * seeded from `collapsedDepth` and re-seeded whenever the global expand/collapse
82
+ * "pulse" (`forceOpen`) flips. Leaves render inline with their type color.
83
+ */
84
+ function JsonNode({ label, value, depth, collapsedDepth, forceOpen, trailingComma, }) {
85
+ const seededOpen = depth < collapsedDepth;
86
+ // `forceOpen` is the global pulse: when the user hits expand/collapse all we
87
+ // flip every node, but per-node toggles still win afterward (the pulse is part
88
+ // of the state key via `useMemo` reseed below).
89
+ const [open, setOpen] = useState(seededOpen);
90
+ // Re-seed when the global pulse changes. Keyed by `forceOpen` identity.
91
+ useMemo(() => {
92
+ if (forceOpen !== null)
93
+ setOpen(forceOpen);
94
+ // eslint-disable-next-line react-hooks/exhaustive-deps
95
+ }, [forceOpen]);
96
+ const keyEl = label !== undefined ? (_jsxs(_Fragment, { children: [_jsx("span", { className: KEY_CLASS, children: typeof label === "number" ? label : JSON.stringify(label) }), _jsx("span", { className: PUNCT_CLASS, children: ": " })] })) : null;
97
+ if (!isContainer(value)) {
98
+ return (_jsxs("div", { className: "flex items-start py-0.5 leading-relaxed", children: [_jsx("span", { className: "select-none whitespace-pre", children: keyEl }), _jsx(LeafValue, { value: value }), trailingComma && _jsx("span", { className: PUNCT_CLASS, children: "," })] }));
99
+ }
100
+ const isArray = Array.isArray(value);
101
+ const entries = isArray
102
+ ? value.map((item, index) => [index, item])
103
+ : Object.entries(value);
104
+ const openBrace = isArray ? "[" : "{";
105
+ const closeBrace = isArray ? "]" : "}";
106
+ const empty = entries.length === 0;
107
+ return (_jsxs("div", { className: "leading-relaxed", children: [_jsxs("button", { type: "button", "data-plan-interactive": true, "aria-expanded": open, disabled: empty, onClick: () => setOpen((value) => !value), className: cn("group flex w-full items-start gap-1 rounded py-0.5 text-left transition-colors", !empty && "hover:bg-accent/40", empty && "cursor-default"), children: [_jsx(IconChevronRight, { className: cn("mt-1 size-3.5 shrink-0 text-plan-muted transition-transform", open && "rotate-90", empty && "opacity-0") }), _jsxs("span", { className: "min-w-0 whitespace-pre-wrap break-words", children: [keyEl, _jsx("span", { className: PUNCT_CLASS, children: openBrace }), !open && !empty && (_jsx("span", { className: "ml-1 text-plan-muted", children: containerSummary(value) })), (!open || empty) && (_jsxs("span", { className: PUNCT_CLASS, children: [empty ? "" : "…", closeBrace, trailingComma ? "," : ""] }))] })] }), open && !empty && (_jsxs(_Fragment, { children: [_jsx("div", { className: "ml-[7px] border-l border-plan-line pl-3.5", children: entries.map(([entryKey, entryValue], index) => (_jsx(JsonNode, { label: entryKey, value: entryValue, depth: depth + 1, collapsedDepth: collapsedDepth, forceOpen: forceOpen, trailingComma: index < entries.length - 1 }, String(entryKey)))) }), _jsx("div", { className: "flex items-start", children: _jsxs("span", { className: "ml-[18px] whitespace-pre", children: [_jsx("span", { className: PUNCT_CLASS, children: closeBrace }), trailingComma && _jsx("span", { className: PUNCT_CLASS, children: "," })] }) })] }))] }));
108
+ }
109
+ /* ── Read (collapsible devtools tree) ──────────────────────────────────────── */
110
+ /**
111
+ * Read-only renderer for a `json-explorer` block. Parses `data.json` defensively
112
+ * and renders the collapsible tree; on a parse error it shows the raw payload in
113
+ * a monospace block plus the error (never throws). An "Expand all / Collapse
114
+ * all" control toggles every node at once via a global pulse counter.
115
+ */
116
+ export function JsonExplorerRead({ data, blockId, title, summary, }) {
117
+ const parsed = useMemo(() => parseJson(data.json), [data.json]);
118
+ const collapsedDepth = data.collapsedDepth ?? 1;
119
+ // `pulse` carries a boolean (expand/collapse) plus a nonce so repeated clicks
120
+ // of the same action still re-fire the reseed in each node.
121
+ const [pulse, setPulse] = useState(null);
122
+ const heading = data.title ?? title;
123
+ return (_jsxs("section", { className: "plan-block", "data-block-id": blockId, children: [heading && _jsx("div", { className: "plan-block-label", children: heading }), _jsxs("div", { className: "overflow-hidden rounded-xl border border-plan-line bg-plan-code", children: [_jsxs("div", { className: "flex items-center justify-between gap-2 border-b border-plan-line px-3 py-1.5", children: [_jsx("span", { className: "font-mono text-xs uppercase tracking-wide text-plan-muted", children: "JSON" }), parsed.ok && isContainer(parsed.value) && (_jsxs("div", { className: "flex items-center gap-1", children: [_jsx("button", { type: "button", "data-plan-interactive": true, onClick: () => setPulse((prev) => ({
124
+ open: true,
125
+ nonce: (prev?.nonce ?? 0) + 1,
126
+ })), className: "rounded px-1.5 py-0.5 text-xs text-plan-muted transition-colors hover:bg-accent/60 hover:text-plan-text", children: "Expand all" }), _jsx("span", { className: "text-plan-muted", children: "\u00B7" }), _jsx("button", { type: "button", "data-plan-interactive": true, onClick: () => setPulse((prev) => ({
127
+ open: false,
128
+ nonce: (prev?.nonce ?? 0) + 1,
129
+ })), className: "rounded px-1.5 py-0.5 text-xs text-plan-muted transition-colors hover:bg-accent/60 hover:text-plan-text", children: "Collapse all" })] }))] }), _jsx("div", { className: "overflow-auto px-3 py-2.5 font-mono text-sm text-plan-code-text", children: parsed.ok ? (_jsx(JsonNode
130
+ // Remount the whole tree when the global pulse fires so every node
131
+ // re-seeds from the new open/closed state cleanly.
132
+ , { value: parsed.value, depth: 0, collapsedDepth: collapsedDepth, forceOpen: pulse?.open ?? null }, pulse?.nonce ?? 0)) : (_jsxs("div", { className: "space-y-2", children: [_jsx("pre", { className: "overflow-auto whitespace-pre-wrap break-words text-plan-code-text", children: data.json || "—" }), _jsxs("p", { className: "text-xs text-red-600 dark:text-red-300", children: ["Could not parse JSON: ", parsed.error] })] })) })] }), summary && _jsx("p", { className: "mt-5 text-plan-muted", children: summary })] }));
133
+ }
134
+ /* ── Edit (panel form) ─────────────────────────────────────────────────────── */
135
+ /**
136
+ * Panel editor for a `json-explorer` block: a monospace textarea bound to the
137
+ * raw `json`, a "Format" button that pretty-prints via `JSON.parse` →
138
+ * `JSON.stringify(_, null, 2)` (guarded — shows an INLINE error, never
139
+ * `window.alert`), a `collapsedDepth` number input, and a `title` input. Renders
140
+ * BARE content (no `<section>`); the registry's panel surface supplies the
141
+ * popover chrome.
142
+ */
143
+ export function JsonExplorerEdit({ data, onChange, editable, }) {
144
+ const jsonId = useId();
145
+ const titleId = useId();
146
+ const depthId = useId();
147
+ const [formatError, setFormatError] = useState(null);
148
+ const handleFormat = () => {
149
+ try {
150
+ const formatted = JSON.stringify(JSON.parse(data.json), null, 2);
151
+ setFormatError(null);
152
+ onChange({ ...data, json: formatted });
153
+ }
154
+ catch (error) {
155
+ setFormatError(error instanceof Error ? error.message : "Invalid JSON — cannot format");
156
+ }
157
+ };
158
+ return (_jsxs("div", { className: "grid gap-3", "data-plan-interactive": true, children: [_jsxs("div", { className: "grid gap-1.5", children: [_jsx(DevLabel, { htmlFor: titleId, children: "Title" }), _jsx(DevInput, { id: titleId, value: data.title ?? "", readOnly: !editable, onChange: (event) => onChange({ ...data, title: event.target.value || undefined }), placeholder: "Optional heading" })] }), _jsxs("div", { className: "grid gap-1.5", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx(DevLabel, { htmlFor: jsonId, children: "JSON payload" }), editable && (_jsx("button", { type: "button", "data-plan-interactive": true, onClick: handleFormat, className: "inline-flex h-7 cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-md border border-input bg-background px-2 text-xs font-medium ring-offset-background transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", children: "Format" }))] }), _jsx(DevTextarea, { id: jsonId, value: data.json, readOnly: !editable, spellCheck: false, onChange: (event) => {
159
+ setFormatError(null);
160
+ onChange({ ...data, json: event.target.value });
161
+ }, className: "min-h-56 font-mono text-xs", placeholder: '{\n "id": "abc123",\n "active": true\n}' }), formatError && (_jsx("p", { className: "text-xs text-red-600 dark:text-red-300", children: formatError })), _jsx("p", { className: "text-xs text-muted-foreground", children: "Raw JSON text is the source of truth. Use Format to pretty-print it." })] }), _jsxs("div", { className: "grid gap-1.5", children: [_jsx(DevLabel, { htmlFor: depthId, children: "Collapsed depth" }), _jsx(DevInput, { id: depthId, type: "number", min: 0, max: 20, value: data.collapsedDepth ?? 1, readOnly: !editable, onChange: (event) => {
162
+ const next = Number.parseInt(event.target.value, 10);
163
+ onChange({
164
+ ...data,
165
+ collapsedDepth: Number.isFinite(next)
166
+ ? Math.max(0, Math.min(20, next))
167
+ : undefined,
168
+ });
169
+ }, className: "w-24" }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Nodes deeper than this start collapsed (default 1)." })] })] }));
170
+ }
171
+ //# sourceMappingURL=JsonExplorerBlock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JsonExplorerBlock.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/JsonExplorerBlock.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useId, useMemo, useState } from \"react\";\nimport { IconChevronRight } from \"@tabler/icons-react\";\nimport { cn } from \"../../utils.js\";\nimport type { BlockEditProps, BlockReadProps } from \"../types.js\";\nimport type { JsonExplorerData } from \"./json-explorer.config.js\";\nimport { DevInput, DevLabel, DevTextarea } from \"./dev-doc-ui.js\";\n\n/**\n * Read + Edit renderers for a `json-explorer` block — a browser-devtools /\n * Postman-style collapsible JSON tree. The raw JSON TEXT (`data.json`) is the\n * source of truth; the Read renderer parses it defensively and, on any parse\n * error, falls back to the raw text plus the error message (it never throws).\n * Lives in core so any app can register the dev-doc block (no shadcn import).\n *\n * Progressive disclosure is the whole point: object/array nodes show a chevron\n * and a one-line summary (\"{…} 3 keys\" / \"[…] 5 items\"); each node tracks its\n * own open/closed state (`useState`) seeded by `collapsedDepth` so deep payloads\n * stay scannable. Leaf values are type-colored (string = green, number = blue,\n * boolean = violet, null = muted); keys use a stable accent color; subtle indent\n * guide lines mark nesting.\n *\n * DARK/LIGHT: the plan editor toggles a `.dark` class on <html>. Every color\n * token — value types, keys, guide lines, chrome — uses Tailwind `dark:` variants\n * or the theme-aware plan CSS-var utilities, so the tree reads correctly in BOTH\n * modes (no hardcoded dark-only palette).\n */\n\n/* ── Theme-aware value-type color tokens ───────────────────────────────────── */\n\n/** String leaves: green in both modes. */\nconst STRING_CLASS = \"text-emerald-700 dark:text-emerald-300\";\n/** Number leaves: blue in both modes. */\nconst NUMBER_CLASS = \"text-blue-700 dark:text-blue-300\";\n/** Boolean leaves: violet in both modes. */\nconst BOOLEAN_CLASS = \"text-violet-700 dark:text-violet-300\";\n/** `null`/`undefined` leaves: muted (theme-aware plan var). */\nconst NULL_CLASS = \"text-plan-muted italic\";\n/** Object keys: a stable, saturated accent that reads in both modes. */\nconst KEY_CLASS = \"text-rose-700 dark:text-rose-300\";\n/** Structural punctuation (braces, brackets, commas, colons). */\nconst PUNCT_CLASS = \"text-plan-muted\";\n\ntype JsonValue =\n | string\n | number\n | boolean\n | null\n | JsonValue[]\n | { [key: string]: JsonValue };\ntype JsonObject = { [key: string]: JsonValue };\n\ninterface ParseResult {\n ok: boolean;\n value?: JsonValue;\n error?: string;\n}\n\nfunction parseJson(raw: string): ParseResult {\n const trimmed = raw.trim();\n if (!trimmed) {\n return { ok: false, error: \"Empty payload — add some JSON to explore.\" };\n }\n try {\n return { ok: true, value: JSON.parse(trimmed) as JsonValue };\n } catch (error) {\n return {\n ok: false,\n error: error instanceof Error ? error.message : \"Invalid JSON\",\n };\n }\n}\n\nfunction isContainer(value: JsonValue): value is JsonValue[] | JsonObject {\n return value !== null && typeof value === \"object\";\n}\n\n/** One-line summary for a collapsed container, devtools style. */\nfunction containerSummary(value: JsonValue[] | JsonObject): string {\n if (Array.isArray(value)) {\n const count = value.length;\n return `[…] ${count} ${count === 1 ? \"item\" : \"items\"}`;\n }\n const count = Object.keys(value).length;\n return `{…} ${count} ${count === 1 ? \"key\" : \"keys\"}`;\n}\n\n/** Render a leaf (primitive) value with its type color. */\nfunction LeafValue({ value }: { value: string | number | boolean | null }) {\n if (value === null) {\n return <span className={NULL_CLASS}>null</span>;\n }\n if (typeof value === \"string\") {\n return <span className={STRING_CLASS}>{JSON.stringify(value)}</span>;\n }\n if (typeof value === \"number\") {\n return <span className={NUMBER_CLASS}>{String(value)}</span>;\n }\n // boolean\n return <span className={BOOLEAN_CLASS}>{String(value)}</span>;\n}\n\ninterface JsonNodeProps {\n /** The object key or array index label for this node (root has none). */\n label?: string | number;\n value: JsonValue;\n depth: number;\n /** Depth beyond which nodes start collapsed. */\n collapsedDepth: number;\n /** Global expand/collapse pulse — overrides per-node seed when changed. */\n forceOpen: boolean | null;\n /** True when this node is followed by a sibling (renders a trailing comma). */\n trailingComma?: boolean;\n}\n\n/**\n * A single tree node. Containers (object/array) get their own collapse state,\n * seeded from `collapsedDepth` and re-seeded whenever the global expand/collapse\n * \"pulse\" (`forceOpen`) flips. Leaves render inline with their type color.\n */\nfunction JsonNode({\n label,\n value,\n depth,\n collapsedDepth,\n forceOpen,\n trailingComma,\n}: JsonNodeProps) {\n const seededOpen = depth < collapsedDepth;\n // `forceOpen` is the global pulse: when the user hits expand/collapse all we\n // flip every node, but per-node toggles still win afterward (the pulse is part\n // of the state key via `useMemo` reseed below).\n const [open, setOpen] = useState(seededOpen);\n // Re-seed when the global pulse changes. Keyed by `forceOpen` identity.\n useMemo(() => {\n if (forceOpen !== null) setOpen(forceOpen);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [forceOpen]);\n\n const keyEl =\n label !== undefined ? (\n <>\n <span className={KEY_CLASS}>\n {typeof label === \"number\" ? label : JSON.stringify(label)}\n </span>\n <span className={PUNCT_CLASS}>: </span>\n </>\n ) : null;\n\n if (!isContainer(value)) {\n return (\n <div className=\"flex items-start py-0.5 leading-relaxed\">\n <span className=\"select-none whitespace-pre\">{keyEl}</span>\n <LeafValue value={value} />\n {trailingComma && <span className={PUNCT_CLASS}>,</span>}\n </div>\n );\n }\n\n const isArray = Array.isArray(value);\n const entries: Array<[string | number, JsonValue]> = isArray\n ? (value as JsonValue[]).map((item, index) => [index, item])\n : Object.entries(value as JsonObject);\n const openBrace = isArray ? \"[\" : \"{\";\n const closeBrace = isArray ? \"]\" : \"}\";\n const empty = entries.length === 0;\n\n return (\n <div className=\"leading-relaxed\">\n <button\n type=\"button\"\n data-plan-interactive\n aria-expanded={open}\n disabled={empty}\n onClick={() => setOpen((value) => !value)}\n className={cn(\n \"group flex w-full items-start gap-1 rounded py-0.5 text-left transition-colors\",\n !empty && \"hover:bg-accent/40\",\n empty && \"cursor-default\",\n )}\n >\n <IconChevronRight\n className={cn(\n \"mt-1 size-3.5 shrink-0 text-plan-muted transition-transform\",\n open && \"rotate-90\",\n empty && \"opacity-0\",\n )}\n />\n <span className=\"min-w-0 whitespace-pre-wrap break-words\">\n {keyEl}\n <span className={PUNCT_CLASS}>{openBrace}</span>\n {!open && !empty && (\n <span className=\"ml-1 text-plan-muted\">\n {containerSummary(value)}\n </span>\n )}\n {(!open || empty) && (\n <span className={PUNCT_CLASS}>\n {empty ? \"\" : \"…\"}\n {closeBrace}\n {trailingComma ? \",\" : \"\"}\n </span>\n )}\n </span>\n </button>\n\n {open && !empty && (\n <>\n {/* Indent guide: a subtle vertical rule marks the nesting level. */}\n <div className=\"ml-[7px] border-l border-plan-line pl-3.5\">\n {entries.map(([entryKey, entryValue], index) => (\n <JsonNode\n key={String(entryKey)}\n label={entryKey}\n value={entryValue}\n depth={depth + 1}\n collapsedDepth={collapsedDepth}\n forceOpen={forceOpen}\n trailingComma={index < entries.length - 1}\n />\n ))}\n </div>\n <div className=\"flex items-start\">\n {/* Align the closing brace under the chevron column. */}\n <span className=\"ml-[18px] whitespace-pre\">\n <span className={PUNCT_CLASS}>{closeBrace}</span>\n {trailingComma && <span className={PUNCT_CLASS}>,</span>}\n </span>\n </div>\n </>\n )}\n </div>\n );\n}\n\n/* ── Read (collapsible devtools tree) ──────────────────────────────────────── */\n\n/**\n * Read-only renderer for a `json-explorer` block. Parses `data.json` defensively\n * and renders the collapsible tree; on a parse error it shows the raw payload in\n * a monospace block plus the error (never throws). An \"Expand all / Collapse\n * all\" control toggles every node at once via a global pulse counter.\n */\nexport function JsonExplorerRead({\n data,\n blockId,\n title,\n summary,\n}: BlockReadProps<JsonExplorerData>) {\n const parsed = useMemo(() => parseJson(data.json), [data.json]);\n const collapsedDepth = data.collapsedDepth ?? 1;\n // `pulse` carries a boolean (expand/collapse) plus a nonce so repeated clicks\n // of the same action still re-fire the reseed in each node.\n const [pulse, setPulse] = useState<{ open: boolean; nonce: number } | null>(\n null,\n );\n const heading = data.title ?? title;\n\n return (\n <section className=\"plan-block\" data-block-id={blockId}>\n {heading && <div className=\"plan-block-label\">{heading}</div>}\n <div className=\"overflow-hidden rounded-xl border border-plan-line bg-plan-code\">\n <div className=\"flex items-center justify-between gap-2 border-b border-plan-line px-3 py-1.5\">\n <span className=\"font-mono text-xs uppercase tracking-wide text-plan-muted\">\n JSON\n </span>\n {parsed.ok && isContainer(parsed.value as JsonValue) && (\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n data-plan-interactive\n onClick={() =>\n setPulse((prev) => ({\n open: true,\n nonce: (prev?.nonce ?? 0) + 1,\n }))\n }\n className=\"rounded px-1.5 py-0.5 text-xs text-plan-muted transition-colors hover:bg-accent/60 hover:text-plan-text\"\n >\n Expand all\n </button>\n <span className=\"text-plan-muted\">·</span>\n <button\n type=\"button\"\n data-plan-interactive\n onClick={() =>\n setPulse((prev) => ({\n open: false,\n nonce: (prev?.nonce ?? 0) + 1,\n }))\n }\n className=\"rounded px-1.5 py-0.5 text-xs text-plan-muted transition-colors hover:bg-accent/60 hover:text-plan-text\"\n >\n Collapse all\n </button>\n </div>\n )}\n </div>\n <div className=\"overflow-auto px-3 py-2.5 font-mono text-sm text-plan-code-text\">\n {parsed.ok ? (\n <JsonNode\n // Remount the whole tree when the global pulse fires so every node\n // re-seeds from the new open/closed state cleanly.\n key={pulse?.nonce ?? 0}\n value={parsed.value as JsonValue}\n depth={0}\n collapsedDepth={collapsedDepth}\n forceOpen={pulse?.open ?? null}\n />\n ) : (\n <div className=\"space-y-2\">\n <pre className=\"overflow-auto whitespace-pre-wrap break-words text-plan-code-text\">\n {data.json || \"—\"}\n </pre>\n <p className=\"text-xs text-red-600 dark:text-red-300\">\n Could not parse JSON: {parsed.error}\n </p>\n </div>\n )}\n </div>\n </div>\n {summary && <p className=\"mt-5 text-plan-muted\">{summary}</p>}\n </section>\n );\n}\n\n/* ── Edit (panel form) ─────────────────────────────────────────────────────── */\n\n/**\n * Panel editor for a `json-explorer` block: a monospace textarea bound to the\n * raw `json`, a \"Format\" button that pretty-prints via `JSON.parse` →\n * `JSON.stringify(_, null, 2)` (guarded — shows an INLINE error, never\n * `window.alert`), a `collapsedDepth` number input, and a `title` input. Renders\n * BARE content (no `<section>`); the registry's panel surface supplies the\n * popover chrome.\n */\nexport function JsonExplorerEdit({\n data,\n onChange,\n editable,\n}: BlockEditProps<JsonExplorerData>) {\n const jsonId = useId();\n const titleId = useId();\n const depthId = useId();\n const [formatError, setFormatError] = useState<string | null>(null);\n\n const handleFormat = () => {\n try {\n const formatted = JSON.stringify(JSON.parse(data.json), null, 2);\n setFormatError(null);\n onChange({ ...data, json: formatted });\n } catch (error) {\n setFormatError(\n error instanceof Error ? error.message : \"Invalid JSON — cannot format\",\n );\n }\n };\n\n return (\n <div className=\"grid gap-3\" data-plan-interactive>\n <div className=\"grid gap-1.5\">\n <DevLabel htmlFor={titleId}>Title</DevLabel>\n <DevInput\n id={titleId}\n value={data.title ?? \"\"}\n readOnly={!editable}\n onChange={(event) =>\n onChange({ ...data, title: event.target.value || undefined })\n }\n placeholder=\"Optional heading\"\n />\n </div>\n\n <div className=\"grid gap-1.5\">\n <div className=\"flex items-center justify-between\">\n <DevLabel htmlFor={jsonId}>JSON payload</DevLabel>\n {editable && (\n <button\n type=\"button\"\n data-plan-interactive\n onClick={handleFormat}\n className=\"inline-flex h-7 cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-md border border-input bg-background px-2 text-xs font-medium ring-offset-background transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50\"\n >\n Format\n </button>\n )}\n </div>\n <DevTextarea\n id={jsonId}\n value={data.json}\n readOnly={!editable}\n spellCheck={false}\n onChange={(event) => {\n setFormatError(null);\n onChange({ ...data, json: event.target.value });\n }}\n className=\"min-h-56 font-mono text-xs\"\n placeholder={'{\\n \"id\": \"abc123\",\\n \"active\": true\\n}'}\n />\n {formatError && (\n <p className=\"text-xs text-red-600 dark:text-red-300\">\n {formatError}\n </p>\n )}\n <p className=\"text-xs text-muted-foreground\">\n Raw JSON text is the source of truth. Use Format to pretty-print it.\n </p>\n </div>\n\n <div className=\"grid gap-1.5\">\n <DevLabel htmlFor={depthId}>Collapsed depth</DevLabel>\n <DevInput\n id={depthId}\n type=\"number\"\n min={0}\n max={20}\n value={data.collapsedDepth ?? 1}\n readOnly={!editable}\n onChange={(event) => {\n const next = Number.parseInt(event.target.value, 10);\n onChange({\n ...data,\n collapsedDepth: Number.isFinite(next)\n ? Math.max(0, Math.min(20, next))\n : undefined,\n });\n }}\n className=\"w-24\"\n />\n <p className=\"text-xs text-muted-foreground\">\n Nodes deeper than this start collapsed (default 1).\n </p>\n </div>\n </div>\n );\n}\n"]}
@@ -0,0 +1,17 @@
1
+ import type { BlockEditProps, BlockReadProps } from "../types.js";
2
+ import type { MermaidData } from "./mermaid.config.js";
3
+ /**
4
+ * Read-only renderer for a `mermaid` block. Wraps the diagram in the standard
5
+ * titled `plan-block` section + an optional muted caption, matching the plan
6
+ * house style.
7
+ */
8
+ export declare function MermaidRead({ data, blockId, title, summary, }: BlockReadProps<MermaidData>): import("react/jsx-runtime").JSX.Element;
9
+ /**
10
+ * Edit renderer (panel surface) for a `mermaid` block: a monospace textarea for
11
+ * the diagram source plus an optional caption input. Both commit immediately via
12
+ * `onChange`. `editSurface: "panel"` means the registry renders the `Read` view
13
+ * with a corner edit button that opens this form in the plan's shared popover, so
14
+ * this renders only the form (the popover supplies the chrome and title).
15
+ */
16
+ export declare function MermaidEdit({ data, onChange, editable, }: BlockEditProps<MermaidData>): import("react/jsx-runtime").JSX.Element;
17
+ //# sourceMappingURL=MermaidBlock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MermaidBlock.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/MermaidBlock.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAyJvD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,OAAO,EACP,KAAK,EACL,OAAO,GACR,EAAE,cAAc,CAAC,WAAW,CAAC,2CAW7B;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,QAAQ,EACR,QAAQ,GACT,EAAE,cAAc,CAAC,WAAW,CAAC,2CAwC7B"}
@@ -0,0 +1,131 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useId, useMemo, useState } from "react";
3
+ import { DevInput, DevLabel } from "./dev-doc-ui.js";
4
+ /**
5
+ * Read + Edit renderers for a `mermaid` block — a Mermaid diagram definition
6
+ * (flowchart, sequence, etc.) edited as raw text and rendered with Mermaid's
7
+ * `handDrawn` look so it matches the plan's hand-drawn / sketchy house style.
8
+ * Lives in core so any app can register the dev-doc block; it stays app-agnostic
9
+ * (no shadcn / next-themes import).
10
+ *
11
+ * `mermaid` is a browser-only runtime (it touches `document`/DOM measurement),
12
+ * so the Read renderer SSR-guards: it renders a lightweight placeholder until a
13
+ * `useEffect` confirms it is mounted, then dynamically imports `mermaid` and
14
+ * injects the rendered SVG. Parse errors never throw — they fall back to the raw
15
+ * source in a styled monospace block plus the error message. (The dynamic import
16
+ * uses a runtime specifier so this module never forces `mermaid` into the core
17
+ * package's own dependency graph — the host app provides it.)
18
+ *
19
+ * Dark mode: the plan editor toggles a `.dark` class on <html>. The Read renderer
20
+ * reads `document.documentElement.classList.contains("dark")` (re-checking on a
21
+ * `MutationObserver` of the html class) and re-renders the diagram with Mermaid's
22
+ * `dark` theme (vs `neutral` in light) — the resolved theme is in the render
23
+ * effect's deps so toggling dark/light updates the SVG live.
24
+ */
25
+ /** Module specifier kept in a variable so the bundler/tsc treats it as a runtime
26
+ * import (core does not depend on `mermaid`; the host app provides it). */
27
+ const MERMAID_MODULE = "mermaid";
28
+ /** Read the live dark-mode flag from the document root (next-themes-free). */
29
+ function useIsDark() {
30
+ const [isDark, setIsDark] = useState(false);
31
+ useEffect(() => {
32
+ if (typeof document === "undefined")
33
+ return;
34
+ const root = document.documentElement;
35
+ const read = () => setIsDark(root.classList.contains("dark"));
36
+ read();
37
+ const observer = new MutationObserver(read);
38
+ observer.observe(root, { attributes: true, attributeFilter: ["class"] });
39
+ return () => observer.disconnect();
40
+ }, []);
41
+ return isDark;
42
+ }
43
+ function MermaidDiagram({ source, idSeed, }) {
44
+ const isDark = useIsDark();
45
+ // Only render the diagram after mount: `mermaid` is client-only and SSR has no
46
+ // DOM for it to measure against.
47
+ const [mounted, setMounted] = useState(false);
48
+ const [state, setState] = useState({});
49
+ // A DOM-id-safe, stable-per-block render id. Mermaid requires a valid CSS id.
50
+ const renderId = useMemo(() => `mermaid-${idSeed.replace(/[^a-zA-Z0-9_-]/g, "-")}`, [idSeed]);
51
+ useEffect(() => {
52
+ setMounted(true);
53
+ }, []);
54
+ useEffect(() => {
55
+ if (!mounted)
56
+ return;
57
+ let cancelled = false;
58
+ const trimmed = source.trim();
59
+ if (!trimmed) {
60
+ setState({});
61
+ return;
62
+ }
63
+ (async () => {
64
+ try {
65
+ const mermaid = (await import(MERMAID_MODULE))
66
+ .default;
67
+ mermaid.initialize({
68
+ startOnLoad: false,
69
+ securityLevel: "strict",
70
+ look: "handDrawn",
71
+ theme: isDark ? "dark" : "neutral",
72
+ });
73
+ // Unique id per render pass so re-renders (theme/source change) never
74
+ // collide with a stale, still-mounted SVG node id.
75
+ const { svg } = await mermaid.render(`${renderId}-${isDark ? "d" : "l"}`, trimmed);
76
+ if (!cancelled)
77
+ setState({ svg });
78
+ }
79
+ catch (error) {
80
+ if (!cancelled) {
81
+ setState({
82
+ error: error instanceof Error
83
+ ? error.message
84
+ : "Failed to render diagram",
85
+ });
86
+ }
87
+ }
88
+ })();
89
+ return () => {
90
+ cancelled = true;
91
+ };
92
+ // Re-render when the source OR the resolved theme changes so toggling
93
+ // dark/light updates the diagram live.
94
+ }, [mounted, source, isDark, renderId]);
95
+ if (!mounted) {
96
+ return (_jsx("div", { className: "mt-2 flex min-h-24 items-center justify-center rounded-lg border border-plan-line bg-plan-code text-sm text-plan-muted", children: "Loading diagram\u2026" }));
97
+ }
98
+ if (state.error) {
99
+ return (_jsxs("div", { className: "mt-2 space-y-2", children: [_jsx("pre", { className: "overflow-auto rounded-lg border border-plan-line bg-plan-code px-3 py-2 font-mono text-sm text-plan-code-text", children: source }), _jsxs("p", { className: "text-sm text-plan-muted", children: ["Could not render diagram: ", state.error] })] }));
100
+ }
101
+ if (!state.svg) {
102
+ return (_jsx("div", { className: "mt-2 flex min-h-24 items-center justify-center rounded-lg border border-plan-line bg-plan-code text-sm text-plan-muted", children: "Add a diagram definition to render it." }));
103
+ }
104
+ return (_jsx("div", { className: "mt-2 flex justify-center overflow-auto [&_svg]:max-w-full [&_svg]:h-auto",
105
+ // Mermaid output is already sanitized under `securityLevel: "strict"`.
106
+ dangerouslySetInnerHTML: { __html: state.svg } }));
107
+ }
108
+ /**
109
+ * Read-only renderer for a `mermaid` block. Wraps the diagram in the standard
110
+ * titled `plan-block` section + an optional muted caption, matching the plan
111
+ * house style.
112
+ */
113
+ export function MermaidRead({ data, blockId, title, summary, }) {
114
+ return (_jsxs("section", { className: "plan-block", "data-block-id": blockId, children: [title && _jsx("div", { className: "plan-block-label", children: title }), _jsx(MermaidDiagram, { source: data.source, idSeed: blockId }), data.caption && (_jsx("p", { className: "mt-3 text-sm text-plan-muted", children: data.caption })), summary && _jsx("p", { className: "mt-5 text-plan-muted", children: summary })] }));
115
+ }
116
+ /**
117
+ * Edit renderer (panel surface) for a `mermaid` block: a monospace textarea for
118
+ * the diagram source plus an optional caption input. Both commit immediately via
119
+ * `onChange`. `editSurface: "panel"` means the registry renders the `Read` view
120
+ * with a corner edit button that opens this form in the plan's shared popover, so
121
+ * this renders only the form (the popover supplies the chrome and title).
122
+ */
123
+ export function MermaidEdit({ data, onChange, editable, }) {
124
+ const sourceId = useId();
125
+ const captionId = useId();
126
+ return (_jsxs("div", { className: "grid gap-3", "data-plan-interactive": true, children: [_jsxs("div", { className: "grid gap-1.5", children: [_jsx(DevLabel, { htmlFor: sourceId, children: "Diagram source" }), _jsx("textarea", { id: sourceId, value: data.source, readOnly: !editable, spellCheck: false, onChange: (event) => onChange({ ...data, source: event.target.value }), className: "flex min-h-56 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 disabled:cursor-not-allowed disabled:opacity-50", placeholder: "flowchart TD\n A[Start] --> B{Decision}" }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Mermaid syntax \u2014 flowcharts, sequence diagrams, and more." })] }), _jsxs("div", { className: "grid gap-1.5", children: [_jsx(DevLabel, { htmlFor: captionId, children: "Caption" }), _jsx(DevInput, { id: captionId, value: data.caption ?? "", readOnly: !editable, onChange: (event) => onChange({
127
+ ...data,
128
+ caption: event.target.value || undefined,
129
+ }), placeholder: "Optional caption" })] })] }));
130
+ }
131
+ //# sourceMappingURL=MermaidBlock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MermaidBlock.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/MermaidBlock.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useId, useMemo, useState } from \"react\";\nimport type { BlockEditProps, BlockReadProps } from \"../types.js\";\nimport type { MermaidData } from \"./mermaid.config.js\";\nimport { DevInput, DevLabel } from \"./dev-doc-ui.js\";\n\n/**\n * Read + Edit renderers for a `mermaid` block — a Mermaid diagram definition\n * (flowchart, sequence, etc.) edited as raw text and rendered with Mermaid's\n * `handDrawn` look so it matches the plan's hand-drawn / sketchy house style.\n * Lives in core so any app can register the dev-doc block; it stays app-agnostic\n * (no shadcn / next-themes import).\n *\n * `mermaid` is a browser-only runtime (it touches `document`/DOM measurement),\n * so the Read renderer SSR-guards: it renders a lightweight placeholder until a\n * `useEffect` confirms it is mounted, then dynamically imports `mermaid` and\n * injects the rendered SVG. Parse errors never throw — they fall back to the raw\n * source in a styled monospace block plus the error message. (The dynamic import\n * uses a runtime specifier so this module never forces `mermaid` into the core\n * package's own dependency graph — the host app provides it.)\n *\n * Dark mode: the plan editor toggles a `.dark` class on <html>. The Read renderer\n * reads `document.documentElement.classList.contains(\"dark\")` (re-checking on a\n * `MutationObserver` of the html class) and re-renders the diagram with Mermaid's\n * `dark` theme (vs `neutral` in light) — the resolved theme is in the render\n * effect's deps so toggling dark/light updates the SVG live.\n */\n\n/** Module specifier kept in a variable so the bundler/tsc treats it as a runtime\n * import (core does not depend on `mermaid`; the host app provides it). */\nconst MERMAID_MODULE = \"mermaid\";\n\ninterface MermaidRenderState {\n svg?: string;\n error?: string;\n}\n\n/** Read the live dark-mode flag from the document root (next-themes-free). */\nfunction useIsDark(): boolean {\n const [isDark, setIsDark] = useState(false);\n useEffect(() => {\n if (typeof document === \"undefined\") return;\n const root = document.documentElement;\n const read = () => setIsDark(root.classList.contains(\"dark\"));\n read();\n const observer = new MutationObserver(read);\n observer.observe(root, { attributes: true, attributeFilter: [\"class\"] });\n return () => observer.disconnect();\n }, []);\n return isDark;\n}\n\nfunction MermaidDiagram({\n source,\n idSeed,\n}: {\n source: string;\n idSeed: string;\n}) {\n const isDark = useIsDark();\n // Only render the diagram after mount: `mermaid` is client-only and SSR has no\n // DOM for it to measure against.\n const [mounted, setMounted] = useState(false);\n const [state, setState] = useState<MermaidRenderState>({});\n\n // A DOM-id-safe, stable-per-block render id. Mermaid requires a valid CSS id.\n const renderId = useMemo(\n () => `mermaid-${idSeed.replace(/[^a-zA-Z0-9_-]/g, \"-\")}`,\n [idSeed],\n );\n\n useEffect(() => {\n setMounted(true);\n }, []);\n\n useEffect(() => {\n if (!mounted) return;\n let cancelled = false;\n const trimmed = source.trim();\n if (!trimmed) {\n setState({});\n return;\n }\n (async () => {\n try {\n const mermaid = ((await import(MERMAID_MODULE)) as { default: any })\n .default;\n mermaid.initialize({\n startOnLoad: false,\n securityLevel: \"strict\",\n look: \"handDrawn\",\n theme: isDark ? \"dark\" : \"neutral\",\n });\n // Unique id per render pass so re-renders (theme/source change) never\n // collide with a stale, still-mounted SVG node id.\n const { svg } = await mermaid.render(\n `${renderId}-${isDark ? \"d\" : \"l\"}`,\n trimmed,\n );\n if (!cancelled) setState({ svg });\n } catch (error) {\n if (!cancelled) {\n setState({\n error:\n error instanceof Error\n ? error.message\n : \"Failed to render diagram\",\n });\n }\n }\n })();\n return () => {\n cancelled = true;\n };\n // Re-render when the source OR the resolved theme changes so toggling\n // dark/light updates the diagram live.\n }, [mounted, source, isDark, renderId]);\n\n if (!mounted) {\n return (\n <div className=\"mt-2 flex min-h-24 items-center justify-center rounded-lg border border-plan-line bg-plan-code text-sm text-plan-muted\">\n Loading diagram…\n </div>\n );\n }\n\n if (state.error) {\n return (\n <div className=\"mt-2 space-y-2\">\n <pre className=\"overflow-auto rounded-lg border border-plan-line bg-plan-code px-3 py-2 font-mono text-sm text-plan-code-text\">\n {source}\n </pre>\n <p className=\"text-sm text-plan-muted\">\n Could not render diagram: {state.error}\n </p>\n </div>\n );\n }\n\n if (!state.svg) {\n return (\n <div className=\"mt-2 flex min-h-24 items-center justify-center rounded-lg border border-plan-line bg-plan-code text-sm text-plan-muted\">\n Add a diagram definition to render it.\n </div>\n );\n }\n\n return (\n <div\n className=\"mt-2 flex justify-center overflow-auto [&_svg]:max-w-full [&_svg]:h-auto\"\n // Mermaid output is already sanitized under `securityLevel: \"strict\"`.\n dangerouslySetInnerHTML={{ __html: state.svg }}\n />\n );\n}\n\n/**\n * Read-only renderer for a `mermaid` block. Wraps the diagram in the standard\n * titled `plan-block` section + an optional muted caption, matching the plan\n * house style.\n */\nexport function MermaidRead({\n data,\n blockId,\n title,\n summary,\n}: BlockReadProps<MermaidData>) {\n return (\n <section className=\"plan-block\" data-block-id={blockId}>\n {title && <div className=\"plan-block-label\">{title}</div>}\n <MermaidDiagram source={data.source} idSeed={blockId} />\n {data.caption && (\n <p className=\"mt-3 text-sm text-plan-muted\">{data.caption}</p>\n )}\n {summary && <p className=\"mt-5 text-plan-muted\">{summary}</p>}\n </section>\n );\n}\n\n/**\n * Edit renderer (panel surface) for a `mermaid` block: a monospace textarea for\n * the diagram source plus an optional caption input. Both commit immediately via\n * `onChange`. `editSurface: \"panel\"` means the registry renders the `Read` view\n * with a corner edit button that opens this form in the plan's shared popover, so\n * this renders only the form (the popover supplies the chrome and title).\n */\nexport function MermaidEdit({\n data,\n onChange,\n editable,\n}: BlockEditProps<MermaidData>) {\n const sourceId = useId();\n const captionId = useId();\n\n return (\n <div className=\"grid gap-3\" data-plan-interactive>\n <div className=\"grid gap-1.5\">\n <DevLabel htmlFor={sourceId}>Diagram source</DevLabel>\n <textarea\n id={sourceId}\n value={data.source}\n readOnly={!editable}\n spellCheck={false}\n onChange={(event) =>\n onChange({ ...data, source: event.target.value })\n }\n className=\"flex min-h-56 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 disabled:cursor-not-allowed disabled:opacity-50\"\n placeholder={\"flowchart TD\\n A[Start] --> B{Decision}\"}\n />\n <p className=\"text-xs text-muted-foreground\">\n Mermaid syntax — flowcharts, sequence diagrams, and more.\n </p>\n </div>\n <div className=\"grid gap-1.5\">\n <DevLabel htmlFor={captionId}>Caption</DevLabel>\n <DevInput\n id={captionId}\n value={data.caption ?? \"\"}\n readOnly={!editable}\n onChange={(event) =>\n onChange({\n ...data,\n caption: event.target.value || undefined,\n })\n }\n placeholder=\"Optional caption\"\n />\n </div>\n </div>\n );\n}\n"]}
@@ -0,0 +1,19 @@
1
+ import type { BlockEditProps, BlockReadProps } from "../types.js";
2
+ import type { OpenApiSpecData } from "./openapi-spec.config.js";
3
+ /**
4
+ * Read-only renderer for an `openapi-spec` block. Parses `data.spec` defensively
5
+ * and renders a Redoc / Swagger-UI-style reference: a header (title + version +
6
+ * format badge), then operations grouped by tag, each a collapsed-by-default row
7
+ * that expands to the full per-operation reference. On a parse error it shows the
8
+ * error plus the raw payload (never throws).
9
+ */
10
+ export declare function OpenApiSpecRead({ data, blockId, title, summary, ctx, }: BlockReadProps<OpenApiSpecData>): import("react/jsx-runtime").JSX.Element;
11
+ /**
12
+ * Panel editor for an `openapi-spec` block: a `title` input plus a monospace
13
+ * textarea bound to the raw `spec`, with a "Format" button that pretty-prints via
14
+ * `JSON.parse` → `JSON.stringify(_, null, 2)` (guarded — shows an INLINE error,
15
+ * never `window.alert`). Renders BARE content (no `<section>`); the registry's
16
+ * panel surface supplies the popover chrome.
17
+ */
18
+ export declare function OpenApiSpecEdit({ data, onChange, editable, }: BlockEditProps<OpenApiSpecData>): import("react/jsx-runtime").JSX.Element;
19
+ //# sourceMappingURL=OpenApiSpecBlock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OpenApiSpecBlock.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/OpenApiSpecBlock.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AA8xBhE;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,EAC9B,IAAI,EACJ,OAAO,EACP,KAAK,EACL,OAAO,EACP,GAAG,GACJ,EAAE,cAAc,CAAC,eAAe,CAAC,2CAgFjC;AAID;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,EAC9B,IAAI,EACJ,QAAQ,EACR,QAAQ,GACT,EAAE,cAAc,CAAC,eAAe,CAAC,2CAwEjC"}