@agent-native/core 0.41.1 → 0.43.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 (181) hide show
  1. package/README.md +17 -56
  2. package/dist/action.d.ts +13 -1
  3. package/dist/action.d.ts.map +1 -1
  4. package/dist/action.js.map +1 -1
  5. package/dist/agent/production-agent.d.ts +8 -0
  6. package/dist/agent/production-agent.d.ts.map +1 -1
  7. package/dist/agent/production-agent.js +93 -0
  8. package/dist/agent/production-agent.js.map +1 -1
  9. package/dist/cli/app-skill.d.ts +16 -0
  10. package/dist/cli/app-skill.d.ts.map +1 -1
  11. package/dist/cli/app-skill.js +33 -3
  12. package/dist/cli/app-skill.js.map +1 -1
  13. package/dist/cli/pr-visual-recap-workflow.d.ts +1 -1
  14. package/dist/cli/pr-visual-recap-workflow.d.ts.map +1 -1
  15. package/dist/cli/pr-visual-recap-workflow.js +1 -1
  16. package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
  17. package/dist/cli/recap.d.ts.map +1 -1
  18. package/dist/cli/recap.js +38 -16
  19. package/dist/cli/recap.js.map +1 -1
  20. package/dist/cli/skills.d.ts +30 -3
  21. package/dist/cli/skills.d.ts.map +1 -1
  22. package/dist/cli/skills.js +180 -114
  23. package/dist/cli/skills.js.map +1 -1
  24. package/dist/client/AssistantChat.d.ts.map +1 -1
  25. package/dist/client/AssistantChat.js +2 -2
  26. package/dist/client/AssistantChat.js.map +1 -1
  27. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  28. package/dist/client/agent-chat-adapter.js +172 -5
  29. package/dist/client/agent-chat-adapter.js.map +1 -1
  30. package/dist/client/blocks/index.d.ts +11 -0
  31. package/dist/client/blocks/index.d.ts.map +1 -1
  32. package/dist/client/blocks/index.js +11 -0
  33. package/dist/client/blocks/index.js.map +1 -1
  34. package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts +19 -0
  35. package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts.map +1 -1
  36. package/dist/client/blocks/library/AnnotatedCodeBlock.js +6 -58
  37. package/dist/client/blocks/library/AnnotatedCodeBlock.js.map +1 -1
  38. package/dist/client/blocks/library/ApiEndpointBlock.d.ts.map +1 -1
  39. package/dist/client/blocks/library/ApiEndpointBlock.js +116 -7
  40. package/dist/client/blocks/library/ApiEndpointBlock.js.map +1 -1
  41. package/dist/client/blocks/library/DataModelBlock.d.ts.map +1 -1
  42. package/dist/client/blocks/library/DataModelBlock.js +75 -9
  43. package/dist/client/blocks/library/DataModelBlock.js.map +1 -1
  44. package/dist/client/blocks/library/DiffBlock.d.ts +1 -1
  45. package/dist/client/blocks/library/DiffBlock.d.ts.map +1 -1
  46. package/dist/client/blocks/library/DiffBlock.js +265 -39
  47. package/dist/client/blocks/library/DiffBlock.js.map +1 -1
  48. package/dist/client/blocks/library/FileTreeBlock.d.ts.map +1 -1
  49. package/dist/client/blocks/library/FileTreeBlock.js +27 -4
  50. package/dist/client/blocks/library/FileTreeBlock.js.map +1 -1
  51. package/dist/client/blocks/library/HighlightedCode.d.ts +1 -1
  52. package/dist/client/blocks/library/HighlightedCode.js +1 -1
  53. package/dist/client/blocks/library/HighlightedCode.js.map +1 -1
  54. package/dist/client/blocks/library/JsonExplorerBlock.js +1 -1
  55. package/dist/client/blocks/library/JsonExplorerBlock.js.map +1 -1
  56. package/dist/client/blocks/library/MermaidBlock.js +1 -1
  57. package/dist/client/blocks/library/MermaidBlock.js.map +1 -1
  58. package/dist/client/blocks/library/annotation-rail.d.ts +115 -0
  59. package/dist/client/blocks/library/annotation-rail.d.ts.map +1 -0
  60. package/dist/client/blocks/library/annotation-rail.js +139 -0
  61. package/dist/client/blocks/library/annotation-rail.js.map +1 -0
  62. package/dist/client/blocks/library/api-endpoint.config.d.ts +31 -6
  63. package/dist/client/blocks/library/api-endpoint.config.d.ts.map +1 -1
  64. package/dist/client/blocks/library/api-endpoint.config.js +30 -6
  65. package/dist/client/blocks/library/api-endpoint.config.js.map +1 -1
  66. package/dist/client/blocks/library/callout.config.d.ts +29 -0
  67. package/dist/client/blocks/library/callout.config.d.ts.map +1 -0
  68. package/dist/client/blocks/library/callout.config.js +33 -0
  69. package/dist/client/blocks/library/callout.config.js.map +1 -0
  70. package/dist/client/blocks/library/callout.d.ts +20 -0
  71. package/dist/client/blocks/library/callout.d.ts.map +1 -0
  72. package/dist/client/blocks/library/callout.js +61 -0
  73. package/dist/client/blocks/library/callout.js.map +1 -0
  74. package/dist/client/blocks/library/checklist.d.ts.map +1 -1
  75. package/dist/client/blocks/library/checklist.js +3 -3
  76. package/dist/client/blocks/library/checklist.js.map +1 -1
  77. package/dist/client/blocks/library/code.d.ts.map +1 -1
  78. package/dist/client/blocks/library/code.js +32 -15
  79. package/dist/client/blocks/library/code.js.map +1 -1
  80. package/dist/client/blocks/library/columns.d.ts.map +1 -1
  81. package/dist/client/blocks/library/columns.js +56 -35
  82. package/dist/client/blocks/library/columns.js.map +1 -1
  83. package/dist/client/blocks/library/data-model.config.d.ts +17 -0
  84. package/dist/client/blocks/library/data-model.config.d.ts.map +1 -1
  85. package/dist/client/blocks/library/data-model.config.js +15 -0
  86. package/dist/client/blocks/library/data-model.config.js.map +1 -1
  87. package/dist/client/blocks/library/decision.config.d.ts +37 -0
  88. package/dist/client/blocks/library/decision.config.d.ts.map +1 -0
  89. package/dist/client/blocks/library/decision.config.js +32 -0
  90. package/dist/client/blocks/library/decision.config.js.map +1 -0
  91. package/dist/client/blocks/library/decision.d.ts +19 -0
  92. package/dist/client/blocks/library/decision.d.ts.map +1 -0
  93. package/dist/client/blocks/library/decision.js +119 -0
  94. package/dist/client/blocks/library/decision.js.map +1 -0
  95. package/dist/client/blocks/library/diagram.config.d.ts +64 -0
  96. package/dist/client/blocks/library/diagram.config.d.ts.map +1 -0
  97. package/dist/client/blocks/library/diagram.config.js +111 -0
  98. package/dist/client/blocks/library/diagram.config.js.map +1 -0
  99. package/dist/client/blocks/library/diagram.d.ts +16 -0
  100. package/dist/client/blocks/library/diagram.d.ts.map +1 -0
  101. package/dist/client/blocks/library/diagram.js +261 -0
  102. package/dist/client/blocks/library/diagram.js.map +1 -0
  103. package/dist/client/blocks/library/diff.config.d.ts +28 -6
  104. package/dist/client/blocks/library/diff.config.d.ts.map +1 -1
  105. package/dist/client/blocks/library/diff.config.js +30 -6
  106. package/dist/client/blocks/library/diff.config.js.map +1 -1
  107. package/dist/client/blocks/library/question-form.config.d.ts +69 -0
  108. package/dist/client/blocks/library/question-form.config.d.ts.map +1 -0
  109. package/dist/client/blocks/library/question-form.config.js +58 -0
  110. package/dist/client/blocks/library/question-form.config.js.map +1 -0
  111. package/dist/client/blocks/library/question-form.d.ts +20 -0
  112. package/dist/client/blocks/library/question-form.d.ts.map +1 -0
  113. package/dist/client/blocks/library/question-form.js +286 -0
  114. package/dist/client/blocks/library/question-form.js.map +1 -0
  115. package/dist/client/blocks/library/sanitize-html.d.ts +5 -0
  116. package/dist/client/blocks/library/sanitize-html.d.ts.map +1 -0
  117. package/dist/client/blocks/library/sanitize-html.js +240 -0
  118. package/dist/client/blocks/library/sanitize-html.js.map +1 -0
  119. package/dist/client/blocks/library/server-specs.d.ts.map +1 -1
  120. package/dist/client/blocks/library/server-specs.js +59 -0
  121. package/dist/client/blocks/library/server-specs.js.map +1 -1
  122. package/dist/client/blocks/library/specs.d.ts.map +1 -1
  123. package/dist/client/blocks/library/specs.js +11 -0
  124. package/dist/client/blocks/library/specs.js.map +1 -1
  125. package/dist/client/blocks/library/tabs.d.ts.map +1 -1
  126. package/dist/client/blocks/library/tabs.js +12 -12
  127. package/dist/client/blocks/library/tabs.js.map +1 -1
  128. package/dist/client/blocks/library/wireframe-kit.d.ts +260 -0
  129. package/dist/client/blocks/library/wireframe-kit.d.ts.map +1 -0
  130. package/dist/client/blocks/library/wireframe-kit.js +920 -0
  131. package/dist/client/blocks/library/wireframe-kit.js.map +1 -0
  132. package/dist/client/blocks/library/wireframe.config.d.ts +123 -0
  133. package/dist/client/blocks/library/wireframe.config.d.ts.map +1 -0
  134. package/dist/client/blocks/library/wireframe.config.js +294 -0
  135. package/dist/client/blocks/library/wireframe.config.js.map +1 -0
  136. package/dist/client/blocks/library/wireframe.d.ts +15 -0
  137. package/dist/client/blocks/library/wireframe.d.ts.map +1 -0
  138. package/dist/client/blocks/library/wireframe.js +206 -0
  139. package/dist/client/blocks/library/wireframe.js.map +1 -0
  140. package/dist/client/blocks/registry.d.ts +9 -0
  141. package/dist/client/blocks/registry.d.ts.map +1 -1
  142. package/dist/client/blocks/registry.js +12 -5
  143. package/dist/client/blocks/registry.js.map +1 -1
  144. package/dist/client/blocks/server.d.ts +1 -0
  145. package/dist/client/blocks/server.d.ts.map +1 -1
  146. package/dist/client/blocks/server.js +1 -0
  147. package/dist/client/blocks/server.js.map +1 -1
  148. package/dist/client/blocks/types.d.ts +10 -2
  149. package/dist/client/blocks/types.d.ts.map +1 -1
  150. package/dist/client/blocks/types.js.map +1 -1
  151. package/dist/client/rich-markdown-editor/DragHandle.d.ts.map +1 -1
  152. package/dist/client/rich-markdown-editor/DragHandle.js +152 -21
  153. package/dist/client/rich-markdown-editor/DragHandle.js.map +1 -1
  154. package/dist/client/rich-markdown-editor/RegistryBlockNode.d.ts +25 -1
  155. package/dist/client/rich-markdown-editor/RegistryBlockNode.d.ts.map +1 -1
  156. package/dist/client/rich-markdown-editor/RegistryBlockNode.js +29 -6
  157. package/dist/client/rich-markdown-editor/RegistryBlockNode.js.map +1 -1
  158. package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts +8 -1
  159. package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts.map +1 -1
  160. package/dist/client/rich-markdown-editor/SharedRichEditor.js +5 -1
  161. package/dist/client/rich-markdown-editor/SharedRichEditor.js.map +1 -1
  162. package/dist/extensions/actions.d.ts.map +1 -1
  163. package/dist/extensions/actions.js +159 -12
  164. package/dist/extensions/actions.js.map +1 -1
  165. package/dist/extensions/store.d.ts +21 -0
  166. package/dist/extensions/store.d.ts.map +1 -1
  167. package/dist/extensions/store.js +33 -1
  168. package/dist/extensions/store.js.map +1 -1
  169. package/dist/server/recap-image-route.d.ts.map +1 -1
  170. package/dist/server/recap-image-route.js +12 -3
  171. package/dist/server/recap-image-route.js.map +1 -1
  172. package/dist/styles/agent-native.css +1 -0
  173. package/dist/styles/blocks.css +1380 -0
  174. package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +30 -5
  175. package/docs/content/plan-plugin.md +107 -0
  176. package/docs/content/pr-visual-recap.md +2 -2
  177. package/docs/content/skills-guide.md +8 -0
  178. package/docs/content/template-plan.md +94 -17
  179. package/package.json +2 -1
  180. package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +30 -5
  181. package/docs/content/visual-plans.md +0 -80
@@ -1,18 +1,40 @@
1
1
  import { z } from "zod";
2
+ /**
3
+ * A 1-based line reference: `"3"` or `"3-5"` (inclusive). Whitespace tolerant.
4
+ * Matches the `annotated-code` line-ref schema so both blocks validate refs
5
+ * identically.
6
+ */
7
+ const lineRefSchema = z
8
+ .string()
9
+ .trim()
10
+ .regex(/^\d+(\s*-\s*\d+)?$/, {
11
+ message: 'lines must be a 1-based line ref like "3" or "3-5"',
12
+ })
13
+ .max(40);
14
+ const diffAnnotationSchema = z.object({
15
+ side: z.enum(["before", "after"]).optional(),
16
+ lines: lineRefSchema,
17
+ label: z.string().trim().max(160).optional(),
18
+ note: z.string().trim().min(1).max(4_000),
19
+ });
2
20
  export const diffSchema = z.object({
3
21
  filename: z.string().trim().max(400).optional(),
4
22
  language: z.string().trim().max(40).optional(),
5
23
  before: z.string().max(100_000),
6
24
  after: z.string().max(100_000),
7
25
  mode: z.enum(["unified", "split"]).optional(),
26
+ annotations: z.array(diffAnnotationSchema).max(80).optional(),
8
27
  });
9
28
  /**
10
- * MDX config: `filename`, `language`, `mode`, `before`, and `after` are flat
11
- * attributes — the `<Diff id … filename language mode before after />`
12
- * self-closing form. Insertion order of `toAttrs` is the on-disk attribute order.
13
- * `fromAttrs` mirrors a forgiving parse (`before ?? ""`, `after ?? ""`, optional
14
- * `filename`/`language`/`mode` undefined when absent) so a plan missing an
15
- * attribute still parses.
29
+ * MDX config: `filename`, `language`, `mode`, `before`, `after`, and
30
+ * `annotations` are flat attributes — the
31
+ * `<Diff id filename language mode before after annotations />` self-closing
32
+ * form. Insertion order of `toAttrs` is the on-disk attribute order. `before`/
33
+ * `after` are multiline string attributes (round-trip through the shared `prop()`
34
+ * encoder); `annotations` is a JSON array attribute, encoded the same way as the
35
+ * `annotated-code` block. `fromAttrs` mirrors a forgiving parse (`before ?? ""`,
36
+ * `after ?? ""`, `annotations ?? []`, optional `filename`/`language`/`mode`
37
+ * undefined when absent) so a plan missing an attribute still parses.
16
38
  */
17
39
  export const diffMdx = {
18
40
  tag: "Diff",
@@ -22,6 +44,7 @@ export const diffMdx = {
22
44
  mode: data.mode,
23
45
  before: data.before,
24
46
  after: data.after,
47
+ annotations: data.annotations,
25
48
  }),
26
49
  fromAttrs: (attrs) => ({
27
50
  filename: attrs.string("filename"),
@@ -29,6 +52,7 @@ export const diffMdx = {
29
52
  mode: attrs.string("mode"),
30
53
  before: attrs.string("before") ?? "",
31
54
  after: attrs.string("after") ?? "",
55
+ annotations: attrs.array("annotations") ?? [],
32
56
  }),
33
57
  };
34
58
  //# sourceMappingURL=diff.config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"diff.config.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/diff.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAkCxB,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC;IACjC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;IAC/C,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE;IAC9C,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC;IAC/B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC;IAC9B,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE;CAC9C,CAAmC,CAAC;AAErC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,OAAO,GAA6B;IAC/C,GAAG,EAAE,MAAM;IACX,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAClB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,KAAK,EAAE,IAAI,CAAC,KAAK;KAClB,CAAC;IACF,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACrB,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC;QAClC,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC;QAClC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,CAAyB;QAClD,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE;QACpC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE;KACnC,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 diff block: its data schema and MDX\n * round-trip config. Shared by the server MDX adapter (`plan-mdx.ts` via\n * `plan-block-registry.ts`) and the client spec (`planBlocks.tsx`). Keeping this\n * React-free means importing it into a server module never pulls React (or the\n * `diff` line-differ used only by the renderer) into the Nitro/SSR bundle.\n *\n * The schema MUST stay data-compatible with the `diff` branch of `planBlockSchema`\n * (`plan-content.ts`), and the MDX `tag` + flat attribute shape MUST match the\n * `<Diff filename language mode before after />` self-closing encoding so stored\n * `.mdx` round-trips. The `before`/`after` source lives in ATTRIBUTES (not MDX\n * children) — the shared `prop()` encoder round-trips multiline strings cleanly,\n * and keeping them attributes avoids the code being reflowed as prose.\n */\n\n/** Rendering layout for the diff body. */\nexport type DiffMode = \"unified\" | \"split\";\n\nexport interface DiffData {\n /** Optional file path shown in the header (e.g. `src/add.ts`). */\n filename?: string;\n /** Optional language label rendered as a chip (e.g. `ts`). Purely cosmetic. */\n language?: string;\n /** Original (\"before\") source. */\n before: string;\n /** New (\"after\") source. */\n after: string;\n /** Layout: unified (default, one column) or split (side-by-side). */\n mode?: DiffMode;\n}\n\nexport const diffSchema = z.object({\n filename: z.string().trim().max(400).optional(),\n language: z.string().trim().max(40).optional(),\n before: z.string().max(100_000),\n after: z.string().max(100_000),\n mode: z.enum([\"unified\", \"split\"]).optional(),\n}) as unknown as z.ZodType<DiffData>;\n\n/**\n * MDX config: `filename`, `language`, `mode`, `before`, and `after` are flat\n * attributes — the `<Diff id … filename language mode before after />`\n * self-closing form. Insertion order of `toAttrs` is the on-disk attribute order.\n * `fromAttrs` mirrors a forgiving parse (`before ?? \"\"`, `after ?? \"\"`, optional\n * `filename`/`language`/`mode` undefined when absent) so a plan missing an\n * attribute still parses.\n */\nexport const diffMdx: BlockMdxConfig<DiffData> = {\n tag: \"Diff\",\n toAttrs: (data) => ({\n filename: data.filename,\n language: data.language,\n mode: data.mode,\n before: data.before,\n after: data.after,\n }),\n fromAttrs: (attrs) => ({\n filename: attrs.string(\"filename\"),\n language: attrs.string(\"language\"),\n mode: attrs.string(\"mode\") as DiffMode | undefined,\n before: attrs.string(\"before\") ?? \"\",\n after: attrs.string(\"after\") ?? \"\",\n }),\n};\n"]}
1
+ {"version":3,"file":"diff.config.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/diff.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAsDxB;;;;GAIG;AACH,MAAM,aAAa,GAAG,CAAC;KACpB,MAAM,EAAE;KACR,IAAI,EAAE;KACN,KAAK,CAAC,oBAAoB,EAAE;IAC3B,OAAO,EAAE,oDAAoD;CAC9D,CAAC;KACD,GAAG,CAAC,EAAE,CAAC,CAAC;AAEX,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC5C,KAAK,EAAE,aAAa;IACpB,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,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC;CAC1C,CAA8B,CAAC;AAEhC,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC;IACjC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;IAC/C,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE;IAC9C,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC;IAC/B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC;IAC9B,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC7C,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE;CAC9D,CAAmC,CAAC;AAErC;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,OAAO,GAA6B;IAC/C,GAAG,EAAE,MAAM;IACX,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAClB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,WAAW,EAAE,IAAI,CAAC,WAAW;KAC9B,CAAC;IACF,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACrB,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC;QAClC,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC;QAClC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,CAAyB;QAClD,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE;QACpC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE;QAClC,WAAW,EAAE,KAAK,CAAC,KAAK,CAAiB,aAAa,CAAC,IAAI,EAAE;KAC9D,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 diff block: its data schema and MDX\n * round-trip config. Shared by the server MDX adapter (`plan-mdx.ts` via\n * `plan-block-registry.ts`) and the client spec (`planBlocks.tsx`). Keeping this\n * React-free means importing it into a server module never pulls React (or the\n * `diff` line-differ used only by the renderer) into the Nitro/SSR bundle.\n *\n * The schema MUST stay data-compatible with the `diff` branch of `planBlockSchema`\n * (`plan-content.ts`), and the MDX `tag` + flat attribute shape MUST match the\n * `<Diff filename language mode before after />` self-closing encoding so stored\n * `.mdx` round-trips. The `before`/`after` source lives in ATTRIBUTES (not MDX\n * children) — the shared `prop()` encoder round-trips multiline strings cleanly,\n * and keeping them attributes avoids the code being reflowed as prose.\n */\n\n/** Rendering layout for the diff body. */\nexport type DiffMode = \"unified\" | \"split\";\n\n/**\n * One line-anchored note attached to a diff, mirroring the `annotated-code`\n * annotation shape but adding `side`. The `lines` ref is 1-based against the\n * chosen side's source: `side: \"after\"` (the default) targets the new file's\n * line numbers, `side: \"before\"` the old file's. Optional ⇒ a diff without\n * annotations renders exactly as before.\n */\nexport interface DiffAnnotation {\n /** Which side the line ref targets; defaults to \"after\". */\n side?: \"before\" | \"after\";\n /** 1-based line ref against that side's text: \"13\" or \"13-15\" (inclusive). */\n lines: string;\n /** Optional short label shown before the note (e.g. \"Validation\"). */\n label?: string;\n /** The note prose (markdown), rendered through `ctx.renderMarkdown`. */\n note: string;\n}\n\nexport interface DiffData {\n /** Optional file path shown in the header (e.g. `src/add.ts`). */\n filename?: string;\n /** Optional language label rendered as a chip (e.g. `ts`). Purely cosmetic. */\n language?: string;\n /** Original (\"before\") source. */\n before: string;\n /** New (\"after\") source. */\n after: string;\n /** Layout: unified (default, one column) or split (side-by-side). */\n mode?: DiffMode;\n /** Line-anchored notes over the before/after sides. */\n annotations?: DiffAnnotation[];\n}\n\n/**\n * A 1-based line reference: `\"3\"` or `\"3-5\"` (inclusive). Whitespace tolerant.\n * Matches the `annotated-code` line-ref schema so both blocks validate refs\n * identically.\n */\nconst lineRefSchema = z\n .string()\n .trim()\n .regex(/^\\d+(\\s*-\\s*\\d+)?$/, {\n message: 'lines must be a 1-based line ref like \"3\" or \"3-5\"',\n })\n .max(40);\n\nconst diffAnnotationSchema = z.object({\n side: z.enum([\"before\", \"after\"]).optional(),\n lines: lineRefSchema,\n label: z.string().trim().max(160).optional(),\n note: z.string().trim().min(1).max(4_000),\n}) as z.ZodType<DiffAnnotation>;\n\nexport const diffSchema = z.object({\n filename: z.string().trim().max(400).optional(),\n language: z.string().trim().max(40).optional(),\n before: z.string().max(100_000),\n after: z.string().max(100_000),\n mode: z.enum([\"unified\", \"split\"]).optional(),\n annotations: z.array(diffAnnotationSchema).max(80).optional(),\n}) as unknown as z.ZodType<DiffData>;\n\n/**\n * MDX config: `filename`, `language`, `mode`, `before`, `after`, and\n * `annotations` are flat attributes — the\n * `<Diff id … filename language mode before after annotations />` self-closing\n * form. Insertion order of `toAttrs` is the on-disk attribute order. `before`/\n * `after` are multiline string attributes (round-trip through the shared `prop()`\n * encoder); `annotations` is a JSON array attribute, encoded the same way as the\n * `annotated-code` block. `fromAttrs` mirrors a forgiving parse (`before ?? \"\"`,\n * `after ?? \"\"`, `annotations ?? []`, optional `filename`/`language`/`mode`\n * undefined when absent) so a plan missing an attribute still parses.\n */\nexport const diffMdx: BlockMdxConfig<DiffData> = {\n tag: \"Diff\",\n toAttrs: (data) => ({\n filename: data.filename,\n language: data.language,\n mode: data.mode,\n before: data.before,\n after: data.after,\n annotations: data.annotations,\n }),\n fromAttrs: (attrs) => ({\n filename: attrs.string(\"filename\"),\n language: attrs.string(\"language\"),\n mode: attrs.string(\"mode\") as DiffMode | undefined,\n before: attrs.string(\"before\") ?? \"\",\n after: attrs.string(\"after\") ?? \"\",\n annotations: attrs.array<DiffAnnotation>(\"annotations\") ?? [],\n }),\n};\n"]}
@@ -0,0 +1,69 @@
1
+ import { z } from "zod";
2
+ import type { BlockMdxConfig } from "../types.js";
3
+ /**
4
+ * Pure (React-free) part of the shared `question-form` and `visual-questions`
5
+ * blocks: their data schema and MDX round-trip config. Lives in core so any
6
+ * app's server/shared registry and the client spec (`question-form.tsx`)
7
+ * consume one definition. Keeping it React-free means importing it into a server
8
+ * module never pulls React into the Nitro/SSR bundle.
9
+ *
10
+ * `question-form` is the general-purpose respondent-facing form (Open Questions,
11
+ * single/multi/freeform answers, recommended options, optional inline
12
+ * wireframe/diagram previews). `visual-questions` is the same data shape and UI,
13
+ * branded for an explicit visual-intake flow before a plan. Both originated in
14
+ * the plan template (the data shape mirrors the legacy `visual-questions` block)
15
+ * before moving here, so their MDX `tag` + attribute encoding MUST match the
16
+ * historical `<QuestionForm questions submitLabel />` / `<VisualQuestions … />`
17
+ * forms so stored `.mdx` round-trips byte-compatibly.
18
+ *
19
+ * The per-option `wireframe`/`diagram` previews stay opaque (`unknown`) at the
20
+ * core layer: the core block renders them through `ctx.renderBlock` (the same
21
+ * nested-block seam tabs/columns use) so core never imports an app's wireframe
22
+ * or diagram renderer. Each app supplies the matching `wireframe`/`diagram`
23
+ * block spec and shape.
24
+ */
25
+ export type QuestionMode = "single" | "multi" | "freeform";
26
+ export interface QuestionFormOption {
27
+ id: string;
28
+ label: string;
29
+ detail?: string;
30
+ /** Authored recommendation only (not a runtime answer). */
31
+ recommended?: boolean;
32
+ /**
33
+ * Optional inline visual previews for this option. Kept opaque at the core
34
+ * layer; rendered via `ctx.renderBlock` as a nested `wireframe`/`diagram`
35
+ * block so core stays app-agnostic. Apps validate the concrete shape with
36
+ * their own wireframe/diagram schemas.
37
+ */
38
+ wireframe?: unknown;
39
+ diagram?: unknown;
40
+ }
41
+ export interface QuestionFormQuestion {
42
+ id: string;
43
+ title: string;
44
+ subtitle?: string;
45
+ mode: QuestionMode;
46
+ options?: QuestionFormOption[];
47
+ allowOther?: boolean;
48
+ placeholder?: string;
49
+ required?: boolean;
50
+ }
51
+ export interface QuestionFormData {
52
+ questions: QuestionFormQuestion[];
53
+ submitLabel?: string;
54
+ }
55
+ /** `visual-questions` shares the exact `question-form` data shape. */
56
+ export type VisualQuestionsData = QuestionFormData;
57
+ export declare const questionFormSchema: z.ZodType<QuestionFormData>;
58
+ /** `visual-questions` validates with the same schema as `question-form`. */
59
+ export declare const visualQuestionsSchema: z.ZodType<VisualQuestionsData>;
60
+ /**
61
+ * MDX config: `questions` and `submitLabel` are both attributes (the questions
62
+ * array is JSON-encoded by the shared `prop()` encoder). Mirrors the legacy
63
+ * `<QuestionForm questions={[…]} submitLabel="…" />` encoding so stored `.mdx`
64
+ * round-trips byte-compatibly.
65
+ */
66
+ export declare const questionFormMdx: BlockMdxConfig<QuestionFormData>;
67
+ /** `visual-questions` uses the historical `<VisualQuestions … />` tag. */
68
+ export declare const visualQuestionsMdx: BlockMdxConfig<VisualQuestionsData>;
69
+ //# sourceMappingURL=question-form.config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"question-form.config.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/question-form.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,MAAM,YAAY,GAAG,QAAQ,GAAG,OAAO,GAAG,UAAU,CAAC;AAE3D,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,YAAY,CAAC;IACnB,OAAO,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAC/B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,oBAAoB,EAAE,CAAC;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,sEAAsE;AACtE,MAAM,MAAM,mBAAmB,GAAG,gBAAgB,CAAC;AA0BnD,eAAO,MAAM,kBAAkB,EAGd,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;AAE7C,4EAA4E;AAC5E,eAAO,MAAM,qBAAqB,EACC,CAAC,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC;AAElE;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,cAAc,CAAC,gBAAgB,CAU5D,CAAC;AAEF,0EAA0E;AAC1E,eAAO,MAAM,kBAAkB,EAAE,cAAc,CAAC,mBAAmB,CAUlE,CAAC"}
@@ -0,0 +1,58 @@
1
+ import { z } from "zod";
2
+ const idSchema = z.string().trim().min(1).max(120);
3
+ const questionOptionSchema = z.object({
4
+ id: idSchema,
5
+ label: z.string().trim().min(1).max(220),
6
+ detail: z.string().trim().max(800).optional(),
7
+ recommended: z.boolean().optional(),
8
+ // Opaque at the core layer — the inline preview renders via `ctx.renderBlock`
9
+ // with the app's own wireframe/diagram block spec; the app validates the shape.
10
+ wireframe: z.unknown().optional(),
11
+ diagram: z.unknown().optional(),
12
+ });
13
+ const questionSchema = z.object({
14
+ id: idSchema,
15
+ title: z.string().trim().min(1).max(260),
16
+ subtitle: z.string().trim().max(700).optional(),
17
+ mode: z.enum(["single", "multi", "freeform"]),
18
+ options: z.array(questionOptionSchema).max(40).optional(),
19
+ allowOther: z.boolean().optional(),
20
+ placeholder: z.string().trim().max(240).optional(),
21
+ required: z.boolean().optional(),
22
+ });
23
+ export const questionFormSchema = z.object({
24
+ questions: z.array(questionSchema).min(1).max(40),
25
+ submitLabel: z.string().trim().max(80).optional(),
26
+ });
27
+ /** `visual-questions` validates with the same schema as `question-form`. */
28
+ export const visualQuestionsSchema = questionFormSchema;
29
+ /**
30
+ * MDX config: `questions` and `submitLabel` are both attributes (the questions
31
+ * array is JSON-encoded by the shared `prop()` encoder). Mirrors the legacy
32
+ * `<QuestionForm questions={[…]} submitLabel="…" />` encoding so stored `.mdx`
33
+ * round-trips byte-compatibly.
34
+ */
35
+ export const questionFormMdx = {
36
+ tag: "QuestionForm",
37
+ toAttrs: (data) => ({
38
+ questions: data.questions,
39
+ submitLabel: data.submitLabel,
40
+ }),
41
+ fromAttrs: (attrs) => ({
42
+ questions: attrs.array("questions") ?? [],
43
+ submitLabel: attrs.string("submitLabel"),
44
+ }),
45
+ };
46
+ /** `visual-questions` uses the historical `<VisualQuestions … />` tag. */
47
+ export const visualQuestionsMdx = {
48
+ tag: "VisualQuestions",
49
+ toAttrs: (data) => ({
50
+ questions: data.questions,
51
+ submitLabel: data.submitLabel,
52
+ }),
53
+ fromAttrs: (attrs) => ({
54
+ questions: attrs.array("questions") ?? [],
55
+ submitLabel: attrs.string("submitLabel"),
56
+ }),
57
+ };
58
+ //# sourceMappingURL=question-form.config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"question-form.config.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/question-form.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA+DxB,MAAM,QAAQ,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAEnD,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,EAAE,EAAE,QAAQ;IACZ,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;IACxC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;IAC7C,WAAW,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IACnC,8EAA8E;IAC9E,gFAAgF;IAChF,SAAS,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IACjC,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;CAChC,CAAkC,CAAC;AAEpC,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9B,EAAE,EAAE,QAAQ;IACZ,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;IACxC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;IAC/C,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;IAC7C,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE;IACzD,UAAU,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAClC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;IAClD,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;CACjC,CAAoC,CAAC;AAEtC,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;IACjD,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE;CAClD,CAA2C,CAAC;AAE7C,4EAA4E;AAC5E,MAAM,CAAC,MAAM,qBAAqB,GAChC,kBAA+D,CAAC;AAElE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAqC;IAC/D,GAAG,EAAE,cAAc;IACnB,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAClB,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,WAAW,EAAE,IAAI,CAAC,WAAW;KAC9B,CAAC;IACF,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACrB,SAAS,EAAE,KAAK,CAAC,KAAK,CAAuB,WAAW,CAAC,IAAI,EAAE;QAC/D,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC;KACzC,CAAC;CACH,CAAC;AAEF,0EAA0E;AAC1E,MAAM,CAAC,MAAM,kBAAkB,GAAwC;IACrE,GAAG,EAAE,iBAAiB;IACtB,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAClB,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,WAAW,EAAE,IAAI,CAAC,WAAW;KAC9B,CAAC;IACF,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACrB,SAAS,EAAE,KAAK,CAAC,KAAK,CAAuB,WAAW,CAAC,IAAI,EAAE;QAC/D,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC;KACzC,CAAC;CACH,CAAC","sourcesContent":["import { z } from \"zod\";\nimport type { BlockMdxConfig } from \"../types.js\";\n\n/**\n * Pure (React-free) part of the shared `question-form` and `visual-questions`\n * blocks: their data schema and MDX round-trip config. Lives in core so any\n * app's server/shared registry and the client spec (`question-form.tsx`)\n * consume one definition. Keeping it React-free means importing it into a server\n * module never pulls React into the Nitro/SSR bundle.\n *\n * `question-form` is the general-purpose respondent-facing form (Open Questions,\n * single/multi/freeform answers, recommended options, optional inline\n * wireframe/diagram previews). `visual-questions` is the same data shape and UI,\n * branded for an explicit visual-intake flow before a plan. Both originated in\n * the plan template (the data shape mirrors the legacy `visual-questions` block)\n * before moving here, so their MDX `tag` + attribute encoding MUST match the\n * historical `<QuestionForm questions submitLabel />` / `<VisualQuestions … />`\n * forms so stored `.mdx` round-trips byte-compatibly.\n *\n * The per-option `wireframe`/`diagram` previews stay opaque (`unknown`) at the\n * core layer: the core block renders them through `ctx.renderBlock` (the same\n * nested-block seam tabs/columns use) so core never imports an app's wireframe\n * or diagram renderer. Each app supplies the matching `wireframe`/`diagram`\n * block spec and shape.\n */\n\nexport type QuestionMode = \"single\" | \"multi\" | \"freeform\";\n\nexport interface QuestionFormOption {\n id: string;\n label: string;\n detail?: string;\n /** Authored recommendation only (not a runtime answer). */\n recommended?: boolean;\n /**\n * Optional inline visual previews for this option. Kept opaque at the core\n * layer; rendered via `ctx.renderBlock` as a nested `wireframe`/`diagram`\n * block so core stays app-agnostic. Apps validate the concrete shape with\n * their own wireframe/diagram schemas.\n */\n wireframe?: unknown;\n diagram?: unknown;\n}\n\nexport interface QuestionFormQuestion {\n id: string;\n title: string;\n subtitle?: string;\n mode: QuestionMode;\n options?: QuestionFormOption[];\n allowOther?: boolean;\n placeholder?: string;\n required?: boolean;\n}\n\nexport interface QuestionFormData {\n questions: QuestionFormQuestion[];\n submitLabel?: string;\n}\n\n/** `visual-questions` shares the exact `question-form` data shape. */\nexport type VisualQuestionsData = QuestionFormData;\n\nconst idSchema = z.string().trim().min(1).max(120);\n\nconst questionOptionSchema = z.object({\n id: idSchema,\n label: z.string().trim().min(1).max(220),\n detail: z.string().trim().max(800).optional(),\n recommended: z.boolean().optional(),\n // Opaque at the core layer — the inline preview renders via `ctx.renderBlock`\n // with the app's own wireframe/diagram block spec; the app validates the shape.\n wireframe: z.unknown().optional(),\n diagram: z.unknown().optional(),\n}) as z.ZodType<QuestionFormOption>;\n\nconst questionSchema = z.object({\n id: idSchema,\n title: z.string().trim().min(1).max(260),\n subtitle: z.string().trim().max(700).optional(),\n mode: z.enum([\"single\", \"multi\", \"freeform\"]),\n options: z.array(questionOptionSchema).max(40).optional(),\n allowOther: z.boolean().optional(),\n placeholder: z.string().trim().max(240).optional(),\n required: z.boolean().optional(),\n}) as z.ZodType<QuestionFormQuestion>;\n\nexport const questionFormSchema = z.object({\n questions: z.array(questionSchema).min(1).max(40),\n submitLabel: z.string().trim().max(80).optional(),\n}) as unknown as z.ZodType<QuestionFormData>;\n\n/** `visual-questions` validates with the same schema as `question-form`. */\nexport const visualQuestionsSchema =\n questionFormSchema as unknown as z.ZodType<VisualQuestionsData>;\n\n/**\n * MDX config: `questions` and `submitLabel` are both attributes (the questions\n * array is JSON-encoded by the shared `prop()` encoder). Mirrors the legacy\n * `<QuestionForm questions={[…]} submitLabel=\"…\" />` encoding so stored `.mdx`\n * round-trips byte-compatibly.\n */\nexport const questionFormMdx: BlockMdxConfig<QuestionFormData> = {\n tag: \"QuestionForm\",\n toAttrs: (data) => ({\n questions: data.questions,\n submitLabel: data.submitLabel,\n }),\n fromAttrs: (attrs) => ({\n questions: attrs.array<QuestionFormQuestion>(\"questions\") ?? [],\n submitLabel: attrs.string(\"submitLabel\"),\n }),\n};\n\n/** `visual-questions` uses the historical `<VisualQuestions … />` tag. */\nexport const visualQuestionsMdx: BlockMdxConfig<VisualQuestionsData> = {\n tag: \"VisualQuestions\",\n toAttrs: (data) => ({\n questions: data.questions,\n submitLabel: data.submitLabel,\n }),\n fromAttrs: (attrs) => ({\n questions: attrs.array<QuestionFormQuestion>(\"questions\") ?? [],\n submitLabel: attrs.string(\"submitLabel\"),\n }),\n};\n"]}
@@ -0,0 +1,20 @@
1
+ import type { BlockReadProps, BlockEditProps } from "../types.js";
2
+ import { type QuestionFormData, type VisualQuestionsData } from "./question-form.config.js";
3
+ export declare function QuestionFormRead(props: BlockReadProps<QuestionFormData>): import("react/jsx-runtime").JSX.Element;
4
+ export declare function VisualQuestionsRead(props: BlockReadProps<VisualQuestionsData>): import("react/jsx-runtime").JSX.Element;
5
+ /** Shared editor for both `question-form` and `visual-questions`. */
6
+ export declare function QuestionFormEdit({ data, onChange, editable, }: BlockEditProps<QuestionFormData>): import("react/jsx-runtime").JSX.Element;
7
+ /**
8
+ * Full client spec for the shared `question-form` block. A respondent-facing
9
+ * intake form edited from the block panel (the schema-ish question shape lives
10
+ * behind the edit surface, not inline).
11
+ */
12
+ export declare const questionFormBlock: import("../types.js").BlockSpec<QuestionFormData>;
13
+ /**
14
+ * Full client spec for the shared `visual-questions` block — the same form UI
15
+ * and data shape as `question-form`, branded for explicit visual intake before a
16
+ * plan. Shares the Read/Edit internals; only the type, MDX tag, label, and seed
17
+ * differ.
18
+ */
19
+ export declare const visualQuestionsBlock: import("../types.js").BlockSpec<QuestionFormData>;
20
+ //# sourceMappingURL=question-form.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"question-form.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/question-form.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,cAAc,EACd,cAAc,EAGf,MAAM,aAAa,CAAC;AACrB,OAAO,EAKL,KAAK,gBAAgB,EAIrB,KAAK,mBAAmB,EACzB,MAAM,2BAA2B,CAAC;AAoanC,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,cAAc,CAAC,gBAAgB,CAAC,2CAEvE;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,cAAc,CAAC,mBAAmB,CAAC,2CAG3C;AAaD,qEAAqE;AACrE,wBAAgB,gBAAgB,CAAC,EAC/B,IAAI,EACJ,QAAQ,EACR,QAAQ,GACT,EAAE,cAAc,CAAC,gBAAgB,CAAC,2CAsTlC;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,mDAsB5B,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,mDAmC/B,CAAC"}
@@ -0,0 +1,286 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import { IconCheck, IconChevronDown, IconClipboardText, IconPlus, IconSend, IconTrash, } from "@tabler/icons-react";
4
+ import { cn } from "../../utils.js";
5
+ import { defineBlock } from "../types.js";
6
+ import { questionFormSchema, questionFormMdx, visualQuestionsSchema, visualQuestionsMdx, } from "./question-form.config.js";
7
+ function isAnswered(question, answer) {
8
+ if (question.mode === "freeform")
9
+ return Boolean(answer?.text?.trim());
10
+ return Boolean(answer?.selected?.length || answer?.text?.trim());
11
+ }
12
+ /**
13
+ * Build a readable, agent-ready summary string from the questions + collected
14
+ * answers. Generic replacement for the plan-specific `summarizeQuestionForm`.
15
+ */
16
+ function summarizeAnswers(blockId, blockTitle, questions, answers) {
17
+ const lines = [
18
+ "Use these question answers to revise the plan:",
19
+ blockId ? `Question block: ${blockId}` : "",
20
+ blockTitle ? `Section: ${blockTitle}` : "",
21
+ "",
22
+ ].filter((line) => line !== "");
23
+ for (const question of questions) {
24
+ const answer = answers[question.id];
25
+ const selectedLabels = question.options
26
+ ?.filter((option) => answer?.selected?.includes(option.id))
27
+ .map((option) => option.label) ?? [];
28
+ const other = answer?.text?.trim();
29
+ const value = question.mode === "freeform"
30
+ ? other
31
+ : [...selectedLabels, ...(other ? [`Other: ${other}`] : [])].join(", ");
32
+ lines.push(`- ${question.title}: ${value || "No answer yet"}`);
33
+ }
34
+ return lines.join("\n");
35
+ }
36
+ /** Render an inline preview (wireframe or diagram) through the app's dispatcher. */
37
+ function OptionVisual({ type, data, blockId, ctx, }) {
38
+ if (!data || !ctx.renderBlock)
39
+ return null;
40
+ const block = {
41
+ id: `${blockId}-${type}`,
42
+ type,
43
+ data,
44
+ };
45
+ return (_jsx(_Fragment, { children: ctx.renderBlock({ block, editing: false, compactVisuals: true }) }));
46
+ }
47
+ function QuestionView({ question, index, answer, blockId, ctx, onAnswer, }) {
48
+ const selected = answer?.selected ?? [];
49
+ const hasVisualOptions = Boolean(question.options?.some((option) => option.wireframe || option.diagram));
50
+ return (_jsxs("article", { className: "grid gap-4 sm:grid-cols-[36px_minmax(0,1fr)]", children: [_jsx("div", { className: "flex size-7 items-center justify-center rounded-full border border-border bg-card text-xs font-semibold text-muted-foreground", children: index + 1 }), _jsxs("div", { children: [_jsx("h3", { className: "text-lg font-semibold leading-7 text-foreground", children: question.title }), question.subtitle && (_jsx("p", { className: "mt-1.5 max-w-3xl text-sm leading-6 text-muted-foreground", children: question.subtitle })), question.mode === "freeform" ? (_jsx("textarea", { value: answer?.text ?? "", onChange: (event) => onAnswer({ text: event.target.value }), className: "mt-4 min-h-28 w-full rounded-xl border border-border bg-card px-3 py-2 text-sm text-foreground outline-none transition-colors placeholder:text-muted-foreground focus-visible:ring-1 focus-visible:ring-ring", "data-plan-interactive": true, placeholder: question.placeholder || "Add details..." })) : (_jsxs("div", { className: cn("mt-4", hasVisualOptions
51
+ ? "grid gap-4 md:grid-cols-2"
52
+ : "grid max-w-4xl gap-3"), children: [question.options?.map((option) => {
53
+ const isSelected = selected.includes(option.id);
54
+ return (_jsxs("button", { type: "button", "data-plan-interactive": true, "aria-pressed": isSelected, className: cn(hasVisualOptions
55
+ ? "grid gap-4 rounded-xl border border-border bg-card p-4 text-left transition-colors hover:bg-accent/30"
56
+ : "grid w-full gap-2 rounded-xl border border-border bg-card px-4 py-3 text-left text-foreground transition-colors hover:border-primary/40 hover:bg-accent/30", isSelected && "border-primary/40 bg-primary/10"), onClick: () => {
57
+ if (question.mode === "single") {
58
+ onAnswer({ ...answer, selected: [option.id] });
59
+ return;
60
+ }
61
+ onAnswer({
62
+ ...answer,
63
+ selected: isSelected
64
+ ? selected.filter((id) => id !== option.id)
65
+ : [...selected, option.id],
66
+ });
67
+ }, children: [_jsxs("div", { className: "flex min-w-0 items-start gap-3", children: [_jsx("span", { className: cn("mt-0.5 flex size-5 shrink-0 items-center justify-center border", question.mode === "single" ? "rounded-full" : "rounded", isSelected
68
+ ? "border-primary bg-primary text-primary-foreground"
69
+ : "border-border"), children: isSelected && _jsx(IconCheck, { className: "size-3.5" }) }), _jsxs("span", { children: [_jsx("span", { className: "text-base font-semibold leading-6 text-foreground", children: option.label }), option.recommended && (_jsxs(_Fragment, { children: [" ", _jsx("span", { className: "ml-3 rounded-md border border-primary/30 px-2 py-0.5 align-middle text-[11px] font-medium uppercase tracking-[0.12em] text-primary", children: "Recommended" })] })), option.detail && (_jsx("span", { className: "mt-1 block max-w-2xl whitespace-pre-line text-sm font-normal leading-6 text-muted-foreground", children: option.detail }))] })] }), hasVisualOptions && (option.wireframe || option.diagram) && (_jsxs("div", { className: "ml-8 grid gap-4 lg:grid-cols-[minmax(0,1fr)_280px]", children: [option.wireframe != null && (_jsx(OptionVisual, { type: "wireframe", data: option.wireframe, blockId: `${blockId}-${option.id}`, ctx: ctx })), option.diagram != null && (_jsx(OptionVisual, { type: "diagram", data: option.diagram, blockId: `${blockId}-${option.id}`, ctx: ctx }))] }))] }, option.id));
70
+ }), question.allowOther !== false && (_jsx("input", { value: answer?.text ?? "", onChange: (event) => onAnswer({ ...answer, text: event.target.value }), className: cn("h-10 w-full rounded-lg border border-border bg-card px-4 text-sm text-foreground outline-none transition-colors placeholder:text-muted-foreground focus-visible:ring-1 focus-visible:ring-ring", hasVisualOptions ? "md:col-span-2" : "sm:w-80"), "data-plan-interactive": true, placeholder: question.placeholder || "Other — type your own answer…" }))] }))] })] }));
71
+ }
72
+ /** The "Send to agent" affordance: a popover (via the app surface) when wired. */
73
+ function SubmitMenu({ blockId, ctx, onSubmit, buildSummary, }) {
74
+ const [open, setOpen] = useState(false);
75
+ const trigger = (_jsxs("button", { type: "button", "data-plan-interactive": true, className: "inline-flex h-9 shrink-0 items-center gap-1.5 rounded-md bg-primary px-4 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90", children: ["Send to agent", _jsx(IconChevronDown, { className: "size-3.5 opacity-70" })] }));
76
+ const menu = (_jsxs("div", { className: "grid gap-1", children: [_jsx("div", { className: "px-1 py-1 text-xs font-semibold text-muted-foreground", children: "Send feedback" }), _jsxs("button", { type: "button", "data-plan-interactive": true, disabled: !onSubmit, onClick: () => {
77
+ onSubmit?.(buildSummary());
78
+ setOpen(false);
79
+ }, className: "grid grid-cols-[auto_1fr] items-start gap-2 rounded-md px-2 py-2 text-left text-sm text-foreground transition-colors hover:bg-accent disabled:cursor-not-allowed disabled:opacity-50", children: [_jsx(IconSend, { className: "mt-0.5 size-4" }), _jsxs("span", { className: "grid gap-0.5", children: [_jsx("span", { children: "Send to inline agent" }), _jsx("span", { className: "text-xs font-normal leading-4 text-muted-foreground", children: "Posts answered questions into the app side agent." })] })] }), _jsxs("button", { type: "button", "data-plan-interactive": true, onClick: () => {
80
+ void navigator.clipboard.writeText(buildSummary());
81
+ setOpen(false);
82
+ }, className: "grid grid-cols-[auto_1fr] items-start gap-2 rounded-md px-2 py-2 text-left text-sm text-foreground transition-colors hover:bg-accent", children: [_jsx(IconClipboardText, { className: "mt-0.5 size-4" }), _jsxs("span", { className: "grid gap-0.5", children: [_jsx("span", { children: "Copy for your agent" }), _jsx("span", { className: "text-xs font-normal leading-4 text-muted-foreground", children: "Copies a prompt you can paste into chat." })] })] })] }));
83
+ // Prefer the app-provided popover surface (shadcn Popover in plan/content);
84
+ // core stays shadcn-free. Without a surface, fall back to a single button that
85
+ // submits directly so the form still works.
86
+ const surface = ctx.renderEditSurface?.({
87
+ title: "Send to agent",
88
+ open,
89
+ onOpenChange: setOpen,
90
+ blockId,
91
+ blockType: "question-form",
92
+ trigger,
93
+ children: menu,
94
+ });
95
+ if (surface)
96
+ return _jsx(_Fragment, { children: surface });
97
+ return (_jsx("button", { type: "button", "data-plan-interactive": true, disabled: !onSubmit, onClick: () => onSubmit?.(buildSummary()), className: "inline-flex h-9 shrink-0 items-center gap-1.5 rounded-md bg-primary px-4 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90 disabled:cursor-not-allowed disabled:opacity-50", children: "Send to agent" }));
98
+ }
99
+ /** Shared read renderer for both `question-form` and `visual-questions`. */
100
+ function QuestionFormReadInner({ data, blockId, title, ctx, }) {
101
+ const questions = data.questions;
102
+ const [answers, setAnswers] = useState({});
103
+ const submitCtx = ctx;
104
+ useEffect(() => {
105
+ setAnswers({});
106
+ }, [blockId]);
107
+ const setAnswer = (questionId, next) => {
108
+ setAnswers((current) => ({ ...current, [questionId]: next }));
109
+ };
110
+ const answered = questions.filter((question) => isAnswered(question, answers[question.id])).length;
111
+ const buildSummary = () => summarizeAnswers(blockId, title, questions, answers);
112
+ return (_jsxs("section", { className: "an-questions-block plan-questions-block", "data-block-id": blockId, children: [title && (_jsx("h2", { className: "text-[1.45rem] font-semibold leading-tight text-foreground", children: title })), _jsx("div", { className: "mt-7 grid gap-8", children: questions.map((question, index) => (_jsx(QuestionView, { question: question, index: index, answer: answers[question.id], blockId: blockId, ctx: ctx, onAnswer: (next) => setAnswer(question.id, next) }, question.id))) }), _jsxs("div", { className: "sticky bottom-0 mt-10 flex items-center justify-between gap-4 border-t border-border bg-background py-4 backdrop-blur", children: [_jsxs("p", { className: "text-sm font-semibold text-muted-foreground", children: [answered, "/", questions.length, " answered"] }), _jsx("div", { "data-plan-interactive": true, children: _jsx(SubmitMenu, { blockId: blockId, ctx: ctx, onSubmit: submitCtx.onQuestionFormSubmit, buildSummary: buildSummary }) })] })] }));
113
+ }
114
+ export function QuestionFormRead(props) {
115
+ return _jsx(QuestionFormReadInner, { ...props });
116
+ }
117
+ export function VisualQuestionsRead(props) {
118
+ return _jsx(QuestionFormReadInner, { ...props });
119
+ }
120
+ const inlineInputClass = "w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground shadow-sm outline-none transition-colors placeholder:text-muted-foreground focus-visible:ring-1 focus-visible:ring-ring";
121
+ const inlineTextareaClass = "w-full resize-y rounded-md border border-border bg-background px-3 py-2 text-sm leading-6 text-foreground shadow-sm outline-none transition-colors placeholder:text-muted-foreground focus-visible:ring-1 focus-visible:ring-ring";
122
+ const inlineLabelClass = "text-[11px] font-semibold uppercase tracking-[0.08em] text-muted-foreground";
123
+ function newLocalId(prefix) {
124
+ return `${prefix}-${Math.random().toString(36).slice(2, 10)}`;
125
+ }
126
+ /** Shared editor for both `question-form` and `visual-questions`. */
127
+ export function QuestionFormEdit({ data, onChange, editable, }) {
128
+ const updateQuestion = (questionId, patch) => onChange({
129
+ ...data,
130
+ questions: data.questions.map((question) => question.id === questionId ? { ...question, ...patch } : question),
131
+ });
132
+ const addQuestion = () => {
133
+ if (data.questions.length >= 40)
134
+ return;
135
+ onChange({
136
+ ...data,
137
+ questions: [
138
+ ...data.questions,
139
+ {
140
+ id: newLocalId("question"),
141
+ title: "New question",
142
+ mode: "freeform",
143
+ placeholder: "Type an answer...",
144
+ },
145
+ ],
146
+ });
147
+ };
148
+ const removeQuestion = (questionId) => {
149
+ if (data.questions.length <= 1)
150
+ return;
151
+ onChange({
152
+ ...data,
153
+ questions: data.questions.filter((question) => question.id !== questionId),
154
+ });
155
+ };
156
+ const setQuestionMode = (question, mode) => updateQuestion(question.id, {
157
+ mode,
158
+ options: mode === "freeform"
159
+ ? question.options
160
+ : question.options && question.options.length > 0
161
+ ? question.options
162
+ : [
163
+ { id: newLocalId("option"), label: "Option A" },
164
+ { id: newLocalId("option"), label: "Option B" },
165
+ ],
166
+ });
167
+ const updateOption = (questionId, optionId, patch) => onChange({
168
+ ...data,
169
+ questions: data.questions.map((question) => question.id === questionId
170
+ ? {
171
+ ...question,
172
+ options: (question.options ?? []).map((option) => option.id === optionId ? { ...option, ...patch } : option),
173
+ }
174
+ : question),
175
+ });
176
+ const addOption = (questionId) => onChange({
177
+ ...data,
178
+ questions: data.questions.map((question) => question.id === questionId && (question.options?.length ?? 0) < 40
179
+ ? {
180
+ ...question,
181
+ options: [
182
+ ...(question.options ?? []),
183
+ { id: newLocalId("option"), label: "New option" },
184
+ ],
185
+ }
186
+ : question),
187
+ });
188
+ const removeOption = (questionId, optionId) => onChange({
189
+ ...data,
190
+ questions: data.questions.map((question) => {
191
+ if (question.id !== questionId)
192
+ return question;
193
+ const nextOptions = (question.options ?? []).filter((option) => option.id !== optionId);
194
+ return { ...question, options: nextOptions };
195
+ }),
196
+ });
197
+ return (_jsxs("div", { className: "grid gap-6", "data-plan-interactive": true, children: [_jsx("div", { className: "grid gap-4", children: data.questions.map((question, index) => {
198
+ const options = question.options ?? [];
199
+ return (_jsxs("article", { className: "rounded-lg border border-border bg-card p-4", children: [_jsxs("div", { className: "mb-4 flex items-center justify-between gap-3", children: [_jsxs("span", { className: inlineLabelClass, children: ["Question ", index + 1] }), data.questions.length > 1 && (_jsx("button", { type: "button", "aria-label": `Delete question ${index + 1}`, className: "inline-flex size-8 items-center justify-center rounded-md border border-border text-muted-foreground hover:bg-muted hover:text-foreground", disabled: !editable, onClick: () => removeQuestion(question.id), children: _jsx(IconTrash, { className: "size-4" }) }))] }), _jsxs("div", { className: "grid gap-3 md:grid-cols-[minmax(0,1fr)_12rem]", children: [_jsxs("label", { className: "grid gap-1.5", children: [_jsx("span", { className: inlineLabelClass, children: "Title" }), _jsx("input", { className: inlineInputClass, value: question.title, disabled: !editable, onChange: (event) => updateQuestion(question.id, {
200
+ title: event.target.value,
201
+ }) })] }), _jsxs("label", { className: "grid gap-1.5", children: [_jsx("span", { className: inlineLabelClass, children: "Mode" }), _jsxs("select", { className: inlineInputClass, value: question.mode, disabled: !editable, onChange: (event) => setQuestionMode(question, event.target.value), children: [_jsx("option", { value: "freeform", children: "Freeform" }), _jsx("option", { value: "single", children: "Single choice" }), _jsx("option", { value: "multi", children: "Multi choice" })] })] })] }), _jsxs("label", { className: "mt-3 grid gap-1.5", children: [_jsx("span", { className: inlineLabelClass, children: "Subtitle" }), _jsx("textarea", { className: inlineTextareaClass, rows: 2, value: question.subtitle ?? "", disabled: !editable, onChange: (event) => updateQuestion(question.id, {
202
+ subtitle: event.target.value || undefined,
203
+ }) })] }), _jsxs("div", { className: "mt-3 grid gap-3 md:grid-cols-[minmax(0,1fr)_auto_auto]", children: [_jsxs("label", { className: "grid gap-1.5", children: [_jsx("span", { className: inlineLabelClass, children: "Placeholder" }), _jsx("input", { className: inlineInputClass, value: question.placeholder ?? "", disabled: !editable, onChange: (event) => updateQuestion(question.id, {
204
+ placeholder: event.target.value || undefined,
205
+ }) })] }), _jsxs("label", { className: "flex items-end gap-2 text-sm font-semibold text-muted-foreground", children: [_jsx("input", { type: "checkbox", className: "mb-2 size-4", checked: Boolean(question.required), disabled: !editable, onChange: (event) => updateQuestion(question.id, {
206
+ required: event.target.checked || undefined,
207
+ }) }), "Required"] }), question.mode !== "freeform" && (_jsxs("label", { className: "flex items-end gap-2 text-sm font-semibold text-muted-foreground", children: [_jsx("input", { type: "checkbox", className: "mb-2 size-4", checked: question.allowOther !== false, disabled: !editable, onChange: (event) => updateQuestion(question.id, {
208
+ allowOther: event.target.checked ? undefined : false,
209
+ }) }), "Allow write-in"] }))] }), question.mode !== "freeform" && (_jsxs("div", { className: "mt-4 grid gap-3", children: [options.map((option) => (_jsxs("div", { className: "grid gap-3 rounded-md border border-border/80 bg-background p-3 md:grid-cols-[minmax(0,1fr)_minmax(0,1fr)_auto]", children: [_jsxs("label", { className: "grid gap-1.5", children: [_jsx("span", { className: inlineLabelClass, children: "Option" }), _jsx("input", { className: inlineInputClass, value: option.label, disabled: !editable, onChange: (event) => updateOption(question.id, option.id, {
210
+ label: event.target.value,
211
+ }) })] }), _jsxs("label", { className: "grid gap-1.5", children: [_jsx("span", { className: inlineLabelClass, children: "Detail" }), _jsx("input", { className: inlineInputClass, value: option.detail ?? "", disabled: !editable, onChange: (event) => updateOption(question.id, option.id, {
212
+ detail: event.target.value || undefined,
213
+ }) })] }), _jsxs("div", { className: "flex items-end gap-2", children: [_jsxs("button", { type: "button", className: cn("inline-flex h-9 items-center gap-1.5 rounded-md border border-border px-3 text-sm font-semibold text-muted-foreground hover:bg-muted hover:text-foreground", option.recommended && "border-ring text-foreground"), disabled: !editable, onClick: () => updateOption(question.id, option.id, {
214
+ recommended: !option.recommended,
215
+ }), children: [option.recommended && (_jsx(IconCheck, { className: "size-4" })), "Recommended"] }), options.length > 1 && (_jsx("button", { type: "button", "aria-label": `Delete ${option.label}`, className: "inline-flex size-9 items-center justify-center rounded-md border border-border text-muted-foreground hover:bg-muted hover:text-foreground", disabled: !editable, onClick: () => removeOption(question.id, option.id), children: _jsx(IconTrash, { className: "size-4" }) }))] })] }, option.id))), _jsxs("button", { type: "button", className: "inline-flex h-9 w-fit items-center gap-1.5 rounded-md border border-border px-3 text-sm font-semibold text-muted-foreground hover:bg-muted hover:text-foreground", disabled: !editable || options.length >= 40, onClick: () => addOption(question.id), children: [_jsx(IconPlus, { className: "size-4" }), "Add option"] })] }))] }, question.id));
216
+ }) }), _jsxs("button", { type: "button", className: "inline-flex h-9 w-fit items-center gap-1.5 rounded-md border border-border px-3 text-sm font-semibold text-muted-foreground hover:bg-muted hover:text-foreground", disabled: !editable || data.questions.length >= 40, onClick: addQuestion, children: [_jsx(IconPlus, { className: "size-4" }), "Add question"] })] }));
217
+ }
218
+ /**
219
+ * Full client spec for the shared `question-form` block. A respondent-facing
220
+ * intake form edited from the block panel (the schema-ish question shape lives
221
+ * behind the edit surface, not inline).
222
+ */
223
+ export const questionFormBlock = defineBlock({
224
+ type: "question-form",
225
+ schema: questionFormSchema,
226
+ mdx: questionFormMdx,
227
+ Read: QuestionFormRead,
228
+ Edit: QuestionFormEdit,
229
+ placement: ["block"],
230
+ editSurface: "panel",
231
+ label: "Question form",
232
+ description: "An interactive respondent-facing form block for open questions, single-choice or multi-choice option rows, freeform answers, recommended options, and optional wireframe/diagram previews. Edit the question schema from the block panel.",
233
+ empty: () => ({
234
+ submitLabel: "Send to agent",
235
+ questions: [
236
+ {
237
+ id: "open-question",
238
+ title: "What should the agent clarify before revising this plan?",
239
+ mode: "freeform",
240
+ placeholder: "Add constraints, preferences, or a decision...",
241
+ },
242
+ ],
243
+ }),
244
+ });
245
+ /**
246
+ * Full client spec for the shared `visual-questions` block — the same form UI
247
+ * and data shape as `question-form`, branded for explicit visual intake before a
248
+ * plan. Shares the Read/Edit internals; only the type, MDX tag, label, and seed
249
+ * differ.
250
+ */
251
+ export const visualQuestionsBlock = defineBlock({
252
+ type: "visual-questions",
253
+ schema: visualQuestionsSchema,
254
+ mdx: visualQuestionsMdx,
255
+ Read: VisualQuestionsRead,
256
+ Edit: QuestionFormEdit,
257
+ placement: ["block"],
258
+ editSurface: "panel",
259
+ label: "Visual questions",
260
+ description: "A visual-intake question block that renders the respondent-facing question UI (single/multi/freeform, recommended options, inline wireframe/diagram previews) and keeps schema editing in the block panel.",
261
+ empty: () => ({
262
+ submitLabel: "Send to agent",
263
+ questions: [
264
+ {
265
+ id: "visual-question",
266
+ title: "Which direction should this plan take?",
267
+ mode: "single",
268
+ options: [
269
+ {
270
+ id: "option-a",
271
+ label: "Direction A",
272
+ detail: "Keep the current shape and refine it.",
273
+ recommended: true,
274
+ },
275
+ {
276
+ id: "option-b",
277
+ label: "Direction B",
278
+ detail: "Try a larger structural revision.",
279
+ },
280
+ ],
281
+ allowOther: true,
282
+ },
283
+ ],
284
+ }),
285
+ });
286
+ //# sourceMappingURL=question-form.js.map