@agent-native/core 0.42.0 → 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 (110) hide show
  1. package/README.md +17 -56
  2. package/dist/cli/recap.d.ts.map +1 -1
  3. package/dist/cli/recap.js +24 -13
  4. package/dist/cli/recap.js.map +1 -1
  5. package/dist/cli/skills.d.ts +2 -6
  6. package/dist/cli/skills.d.ts.map +1 -1
  7. package/dist/cli/skills.js +8 -66
  8. package/dist/cli/skills.js.map +1 -1
  9. package/dist/client/blocks/index.d.ts +11 -0
  10. package/dist/client/blocks/index.d.ts.map +1 -1
  11. package/dist/client/blocks/index.js +11 -0
  12. package/dist/client/blocks/index.js.map +1 -1
  13. package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts.map +1 -1
  14. package/dist/client/blocks/library/AnnotatedCodeBlock.js +2 -2
  15. package/dist/client/blocks/library/AnnotatedCodeBlock.js.map +1 -1
  16. package/dist/client/blocks/library/DiffBlock.d.ts.map +1 -1
  17. package/dist/client/blocks/library/DiffBlock.js +86 -21
  18. package/dist/client/blocks/library/DiffBlock.js.map +1 -1
  19. package/dist/client/blocks/library/FileTreeBlock.d.ts.map +1 -1
  20. package/dist/client/blocks/library/FileTreeBlock.js +27 -4
  21. package/dist/client/blocks/library/FileTreeBlock.js.map +1 -1
  22. package/dist/client/blocks/library/JsonExplorerBlock.js +1 -1
  23. package/dist/client/blocks/library/JsonExplorerBlock.js.map +1 -1
  24. package/dist/client/blocks/library/MermaidBlock.js +1 -1
  25. package/dist/client/blocks/library/MermaidBlock.js.map +1 -1
  26. package/dist/client/blocks/library/annotation-rail.d.ts +19 -0
  27. package/dist/client/blocks/library/annotation-rail.d.ts.map +1 -1
  28. package/dist/client/blocks/library/annotation-rail.js +19 -0
  29. package/dist/client/blocks/library/annotation-rail.js.map +1 -1
  30. package/dist/client/blocks/library/callout.config.d.ts +29 -0
  31. package/dist/client/blocks/library/callout.config.d.ts.map +1 -0
  32. package/dist/client/blocks/library/callout.config.js +33 -0
  33. package/dist/client/blocks/library/callout.config.js.map +1 -0
  34. package/dist/client/blocks/library/callout.d.ts +20 -0
  35. package/dist/client/blocks/library/callout.d.ts.map +1 -0
  36. package/dist/client/blocks/library/callout.js +61 -0
  37. package/dist/client/blocks/library/callout.js.map +1 -0
  38. package/dist/client/blocks/library/checklist.d.ts.map +1 -1
  39. package/dist/client/blocks/library/checklist.js +3 -3
  40. package/dist/client/blocks/library/checklist.js.map +1 -1
  41. package/dist/client/blocks/library/decision.config.d.ts +37 -0
  42. package/dist/client/blocks/library/decision.config.d.ts.map +1 -0
  43. package/dist/client/blocks/library/decision.config.js +32 -0
  44. package/dist/client/blocks/library/decision.config.js.map +1 -0
  45. package/dist/client/blocks/library/decision.d.ts +19 -0
  46. package/dist/client/blocks/library/decision.d.ts.map +1 -0
  47. package/dist/client/blocks/library/decision.js +119 -0
  48. package/dist/client/blocks/library/decision.js.map +1 -0
  49. package/dist/client/blocks/library/diagram.config.d.ts +64 -0
  50. package/dist/client/blocks/library/diagram.config.d.ts.map +1 -0
  51. package/dist/client/blocks/library/diagram.config.js +111 -0
  52. package/dist/client/blocks/library/diagram.config.js.map +1 -0
  53. package/dist/client/blocks/library/diagram.d.ts +16 -0
  54. package/dist/client/blocks/library/diagram.d.ts.map +1 -0
  55. package/dist/client/blocks/library/diagram.js +261 -0
  56. package/dist/client/blocks/library/diagram.js.map +1 -0
  57. package/dist/client/blocks/library/question-form.config.d.ts +69 -0
  58. package/dist/client/blocks/library/question-form.config.d.ts.map +1 -0
  59. package/dist/client/blocks/library/question-form.config.js +58 -0
  60. package/dist/client/blocks/library/question-form.config.js.map +1 -0
  61. package/dist/client/blocks/library/question-form.d.ts +20 -0
  62. package/dist/client/blocks/library/question-form.d.ts.map +1 -0
  63. package/dist/client/blocks/library/question-form.js +286 -0
  64. package/dist/client/blocks/library/question-form.js.map +1 -0
  65. package/dist/client/blocks/library/sanitize-html.d.ts +5 -0
  66. package/dist/client/blocks/library/sanitize-html.d.ts.map +1 -0
  67. package/dist/client/blocks/library/sanitize-html.js +240 -0
  68. package/dist/client/blocks/library/sanitize-html.js.map +1 -0
  69. package/dist/client/blocks/library/server-specs.d.ts.map +1 -1
  70. package/dist/client/blocks/library/server-specs.js +59 -0
  71. package/dist/client/blocks/library/server-specs.js.map +1 -1
  72. package/dist/client/blocks/library/specs.d.ts.map +1 -1
  73. package/dist/client/blocks/library/specs.js +11 -0
  74. package/dist/client/blocks/library/specs.js.map +1 -1
  75. package/dist/client/blocks/library/tabs.d.ts.map +1 -1
  76. package/dist/client/blocks/library/tabs.js +12 -12
  77. package/dist/client/blocks/library/tabs.js.map +1 -1
  78. package/dist/client/blocks/library/wireframe-kit.d.ts +260 -0
  79. package/dist/client/blocks/library/wireframe-kit.d.ts.map +1 -0
  80. package/dist/client/blocks/library/wireframe-kit.js +920 -0
  81. package/dist/client/blocks/library/wireframe-kit.js.map +1 -0
  82. package/dist/client/blocks/library/wireframe.config.d.ts +123 -0
  83. package/dist/client/blocks/library/wireframe.config.d.ts.map +1 -0
  84. package/dist/client/blocks/library/wireframe.config.js +294 -0
  85. package/dist/client/blocks/library/wireframe.config.js.map +1 -0
  86. package/dist/client/blocks/library/wireframe.d.ts +15 -0
  87. package/dist/client/blocks/library/wireframe.d.ts.map +1 -0
  88. package/dist/client/blocks/library/wireframe.js +206 -0
  89. package/dist/client/blocks/library/wireframe.js.map +1 -0
  90. package/dist/client/blocks/registry.d.ts +9 -0
  91. package/dist/client/blocks/registry.d.ts.map +1 -1
  92. package/dist/client/blocks/registry.js +12 -5
  93. package/dist/client/blocks/registry.js.map +1 -1
  94. package/dist/client/blocks/server.d.ts +1 -0
  95. package/dist/client/blocks/server.d.ts.map +1 -1
  96. package/dist/client/blocks/server.js +1 -0
  97. package/dist/client/blocks/server.js.map +1 -1
  98. package/dist/client/blocks/types.d.ts +8 -0
  99. package/dist/client/blocks/types.d.ts.map +1 -1
  100. package/dist/client/blocks/types.js.map +1 -1
  101. package/dist/client/rich-markdown-editor/DragHandle.d.ts.map +1 -1
  102. package/dist/client/rich-markdown-editor/DragHandle.js +77 -12
  103. package/dist/client/rich-markdown-editor/DragHandle.js.map +1 -1
  104. package/dist/styles/agent-native.css +1 -0
  105. package/dist/styles/blocks.css +1380 -0
  106. package/docs/content/plan-plugin.md +8 -8
  107. package/docs/content/pr-visual-recap.md +2 -2
  108. package/docs/content/template-plan.md +94 -17
  109. package/package.json +2 -1
  110. package/docs/content/visual-plans.md +0 -82
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagram.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/diagram.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useId, useMemo, useRef, useState } from \"react\";\nimport { cn } from \"../../utils.js\";\nimport { defineBlock } from \"../types.js\";\nimport type {\n BlockReadProps,\n BlockEditProps,\n BlockRenderContext,\n} from \"../types.js\";\nimport { AiEditableFieldLabel } from \"../AiEditableField.js\";\nimport { RoughOverlay, useIsDark, useWireframeStyle } from \"./wireframe-kit.js\";\nimport {\n sanitizeDiagramHtml,\n sanitizeWireframeCss,\n scopeDesignCss,\n} from \"./sanitize-html.js\";\nimport {\n diagramMdx,\n diagramSchema,\n type DiagramData,\n type DiagramEdge,\n type DiagramNode,\n} from \"./diagram.config.js\";\n\n/**\n * Read + Edit renderers for the shared `diagram` block — a flexible inline\n * architecture/code diagram. The preferred authoring path is a scoped, inert\n * HTML/SVG fragment that leans on `.diagram-*` primitives and `--wf-*` tokens;\n * a legacy positional / sequence node-graph path is kept for older/simple plans.\n * Lives in core so any app can register it (it originated in the plan template).\n *\n * DECOUPLING from the plan original (mirrors the sibling `wireframe.tsx` port):\n * - Theme: `useIsDark()` reads `document.documentElement.classList` instead of\n * `next-themes`; `useWireframeStyle()` reads the viewer's sketchy/clean\n * preference from the shared `plan-wireframe-style` localStorage key — both\n * from `./wireframe-kit.js`, so core stays plan-free and shadcn-free.\n * - HTML sanitize: the HTML/SVG fragment + CSS run through the app-injected\n * `ctx.sanitizeHtml` at the render point (defense-in-depth against stored\n * XSS). Without a sanitizer wired, the HTML path emits nothing — core never\n * injects unsanitized author HTML. The React-free `diagramSchema` already\n * rejects active markup before storage.\n * - The rough.js sketch overlay reuses the kit's shared `RoughOverlay`, scoped\n * with the diagram selector and `drawFrame={false}` (the same call the plan\n * `HtmlDiagram` made). The `.plan-diagram-frame` / `.diagram-*` / `data-rough`\n * class contract is preserved exactly so the theme-token CSS in core's\n * `blocks.css` styles it in any app.\n *\n * The section carries the app-neutral `an-block` class plus the legacy\n * `plan-block` class so plan renders byte-identically while any other app gets\n * the theme-token treatment.\n */\n\n/* -------------------------------------------------------------------------- */\n/* HTML/SVG diagram path */\n/* -------------------------------------------------------------------------- */\n\n/** The rough-overlay selector for diagram bordered boxes (mirrors the plan). */\nconst DIAGRAM_ROUGH_SELECTOR =\n \"[data-rough],.diagram-panel,.diagram-node,.diagram-box,.diagram-pill,.diagram-card,[class*='card'],[class*='box'],[class*='panel'],[class*='pill'],[class*='chip'],[class*='badge'],hr\";\n\nfunction HtmlDiagram({\n data,\n ctx,\n compact,\n}: {\n data: DiagramData;\n ctx: BlockRenderContext;\n compact?: boolean;\n}) {\n const ref = useRef<HTMLDivElement>(null);\n const isDark = useIsDark();\n const style = useWireframeStyle();\n const scopeId = useId().replace(/[^a-zA-Z0-9_-]/g, \"\");\n // Sanitize author HTML/CSS at the render point (defense-in-depth against\n // stored XSS). Self-contained in core via the shared block sanitizer (DOM-based\n // in the browser, regex fallback on the server) so diagrams render in any app\n // without the host wiring a sanitizer hook.\n const safeHtml = useMemo(() => sanitizeDiagramHtml(data.html), [data.html]);\n const scopedCss = useMemo(() => {\n const safeCss = sanitizeWireframeCss(data.css);\n // Scope every author selector under this diagram instance so global\n // selectors (body, *, .app-shell, :root) can't escape and restyle the page.\n return safeCss\n ? scopeDesignCss(safeCss, `[data-plan-diagram-scope=\"${scopeId}\"]`)\n : \"\";\n }, [data.css, scopeId]);\n\n return (\n <div\n ref={ref}\n className={cn(\"plan-diagram-shell\", compact && \"plan-diagram-compact\")}\n >\n <div\n className=\"plan-diagram-frame\"\n data-theme={isDark ? \"dark\" : \"light\"}\n data-style={style}\n data-plan-diagram-scope={scopeId}\n >\n {scopedCss && <style>{scopedCss}</style>}\n <div\n className=\"plan-diagram-frame-content\"\n dangerouslySetInnerHTML={{ __html: safeHtml }}\n />\n </div>\n <RoughOverlay\n scopeRef={ref}\n enabled={style === \"sketchy\"}\n drawFrame={false}\n selector={DIAGRAM_ROUGH_SELECTOR}\n />\n {data.caption && !compact && (\n <p className=\"mt-3 text-sm leading-6 text-muted-foreground\">\n {data.caption}\n </p>\n )}\n </div>\n );\n}\n\n/* -------------------------------------------------------------------------- */\n/* Legacy node-graph paths */\n/* -------------------------------------------------------------------------- */\n\nfunction clampDiagramPercent(value: number) {\n if (!Number.isFinite(value)) return 50;\n return Math.max(4, Math.min(96, value));\n}\n\nfunction hasPositionedDiagramNodes(data: DiagramData): boolean {\n return (data.nodes ?? []).some(\n (node) => typeof node.x === \"number\" && typeof node.y === \"number\",\n );\n}\n\nfunction orderDiagramNodes(\n nodes: DiagramNode[],\n edges: DiagramEdge[],\n): DiagramNode[] {\n if (nodes.length === 0) return [];\n const byId = new Map(nodes.map((node) => [node.id, node]));\n const indegree = new Map(nodes.map((node) => [node.id, 0]));\n for (const edge of edges) {\n if (byId.has(edge.from) && byId.has(edge.to)) {\n indegree.set(edge.to, (indegree.get(edge.to) ?? 0) + 1);\n }\n }\n const start = nodes.find((node) => (indegree.get(node.id) ?? 0) === 0);\n if (!start) return nodes;\n const ordered: DiagramNode[] = [];\n const seen = new Set<string>();\n let current: DiagramNode | undefined = start;\n while (current && !seen.has(current.id)) {\n const node: DiagramNode = current;\n ordered.push(node);\n seen.add(node.id);\n const next = edges.find((edge) => edge.from === node.id);\n current = next ? byId.get(next.to) : undefined;\n }\n for (const node of nodes) if (!seen.has(node.id)) ordered.push(node);\n return ordered;\n}\n\nfunction PositionedDiagram({\n data,\n compact,\n markerId,\n}: {\n data: DiagramData;\n compact?: boolean;\n markerId: string;\n}) {\n const nodes = (data.nodes ?? []).map((node) => ({\n ...node,\n x: clampDiagramPercent(node.x ?? 50),\n y: clampDiagramPercent(node.y ?? 50),\n }));\n const edges = data.edges ?? [];\n const nodeById = new Map(nodes.map((node) => [node.id, node]));\n const arrowId = `${markerId}-diagram-arrow`;\n const nodeWidth = compact ? 150 : 190;\n const canvasHeight = compact ? 280 : 430;\n\n return (\n <div className=\"plan-sketch rounded-[16px] border border-border bg-muted p-5\">\n <div\n className=\"relative overflow-hidden rounded-xl border border-border bg-background\"\n style={{ minHeight: canvasHeight }}\n >\n <svg\n className=\"pointer-events-none absolute inset-0 z-0 h-full w-full\"\n viewBox=\"0 0 100 100\"\n preserveAspectRatio=\"none\"\n aria-hidden=\"true\"\n >\n <defs>\n <marker\n id={arrowId}\n viewBox=\"0 0 10 10\"\n refX=\"8\"\n refY=\"5\"\n markerWidth=\"5\"\n markerHeight=\"5\"\n orient=\"auto-start-reverse\"\n >\n <path\n d=\"M 0 0 L 10 5 L 0 10 z\"\n className=\"fill-muted-foreground\"\n />\n </marker>\n </defs>\n {edges.map((edge, index) => {\n const from = nodeById.get(edge.from);\n const to = nodeById.get(edge.to);\n if (!from || !to) return null;\n return (\n <line\n key={`${edge.from}-${edge.to}-${index}`}\n x1={from.x}\n y1={from.y}\n x2={to.x}\n y2={to.y}\n markerEnd={`url(#${arrowId})`}\n vectorEffect=\"non-scaling-stroke\"\n className=\"stroke-border\"\n strokeWidth={2}\n strokeDasharray={edge.label ? \"0\" : \"6 5\"}\n />\n );\n })}\n </svg>\n\n {!compact &&\n edges.map((edge, index) => {\n const from = nodeById.get(edge.from);\n const to = nodeById.get(edge.to);\n if (!edge.label || !from || !to) return null;\n return (\n <span\n key={`${edge.from}-${edge.to}-${index}-label`}\n className=\"absolute z-10 max-w-[130px] -translate-x-1/2 -translate-y-1/2 rounded-full border border-border bg-background px-2 py-0.5 text-center text-[11px] font-semibold text-muted-foreground shadow-sm\"\n style={{\n left: `${(from.x + to.x) / 2}%`,\n top: `${(from.y + to.y) / 2}%`,\n }}\n >\n {edge.label}\n </span>\n );\n })}\n\n {nodes.map((node, index) => (\n <article\n key={node.id}\n className=\"absolute z-20 -translate-x-1/2 -translate-y-1/2 rounded-xl border-2 border-border bg-background p-3 text-foreground shadow-sm\"\n style={{\n left: `${node.x}%`,\n top: `${node.y}%`,\n width: nodeWidth,\n }}\n >\n <p className=\"text-[11px] font-semibold uppercase tracking-[0.12em] text-muted-foreground\">\n {index + 1}\n </p>\n <h3 className=\"mt-2 text-base font-semibold leading-tight\">\n {node.label}\n </h3>\n {node.detail && !compact && (\n <p className=\"mt-2 text-xs leading-5 text-muted-foreground\">\n {node.detail}\n </p>\n )}\n </article>\n ))}\n </div>\n {data.notes && data.notes.length > 0 && !compact && (\n <div className=\"mt-4 grid gap-2 border-t border-border pt-4 text-sm text-muted-foreground md:grid-cols-2\">\n {data.notes.map((note) => (\n <p key={note.id}>{note.text}</p>\n ))}\n </div>\n )}\n </div>\n );\n}\n\nfunction SequenceDiagram({\n data,\n compact,\n}: {\n data: DiagramData;\n compact?: boolean;\n}) {\n const edges = data.edges ?? [];\n const nodes = orderDiagramNodes(data.nodes ?? [], edges);\n if (nodes.length === 0) {\n return (\n <div className=\"rounded-[12px] border border-border bg-muted p-4 text-sm text-muted-foreground\">\n Diagram content is empty.\n </div>\n );\n }\n return (\n <div className=\"plan-sketch rounded-[16px] border border-border bg-muted p-5\">\n <div\n className={cn(\n \"flex gap-3 overflow-x-auto pb-2\",\n compact ? \"items-center\" : \"items-stretch\",\n )}\n >\n {nodes.map((node, index) => {\n const next = nodes[index + 1];\n const edge = next\n ? edges.find(\n (candidate) =>\n candidate.from === node.id && candidate.to === next.id,\n )\n : undefined;\n return (\n <div key={node.id} className=\"flex min-w-max items-center gap-3\">\n <article\n className={cn(\n \"w-[180px] rounded-xl border-2 border-border bg-background p-3 text-foreground\",\n compact && \"w-[150px]\",\n )}\n >\n <p className=\"text-[11px] font-semibold uppercase tracking-[0.12em] text-muted-foreground\">\n {index + 1}\n </p>\n <h3 className=\"mt-2 text-base font-semibold leading-tight\">\n {node.label}\n </h3>\n {node.detail && !compact && (\n <p className=\"mt-2 text-xs leading-5 text-muted-foreground\">\n {node.detail}\n </p>\n )}\n </article>\n {next && (\n <div className=\"grid min-w-[72px] justify-items-center gap-1 text-muted-foreground\">\n {edge?.label && (\n <span className=\"max-w-[96px] truncate rounded-full border border-border px-2 py-0.5 text-[11px] font-semibold\">\n {edge.label}\n </span>\n )}\n <span className=\"h-0.5 w-full rounded-full border-t-2 border-dashed border-border\" />\n </div>\n )}\n </div>\n );\n })}\n </div>\n {data.notes && data.notes.length > 0 && !compact && (\n <div className=\"mt-4 grid gap-2 border-t border-border pt-4 text-sm text-muted-foreground md:grid-cols-2\">\n {data.notes.map((note) => (\n <p key={note.id}>{note.text}</p>\n ))}\n </div>\n )}\n </div>\n );\n}\n\n/**\n * The diagram body. Routes to the preferred HTML/SVG path (when `data.html` is\n * set) and otherwise to a legacy node-graph path (positioned canvas when nodes\n * carry x/y, else an ordered sequence).\n */\nfunction DiagramBody({\n data,\n ctx,\n compact,\n}: {\n data: DiagramData;\n ctx: BlockRenderContext;\n compact?: boolean;\n}) {\n const markerId = useId().replace(/:/g, \"\");\n if (data.html?.trim()) {\n return <HtmlDiagram data={data} ctx={ctx} compact={compact} />;\n }\n if (hasPositionedDiagramNodes(data)) {\n return (\n <PositionedDiagram data={data} compact={compact} markerId={markerId} />\n );\n }\n return <SequenceDiagram data={data} compact={compact} />;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Read + Edit */\n/* -------------------------------------------------------------------------- */\n\n/** Read-only renderer: the diagram body wrapped in the standard titled block. */\nexport function DiagramRead({\n data,\n blockId,\n title,\n summary,\n ctx,\n}: BlockReadProps<DiagramData>) {\n return (\n <section className=\"an-block plan-block\" data-block-id={blockId}>\n {title && <div className=\"an-block-label plan-block-label\">{title}</div>}\n <DiagramBody data={data} ctx={ctx} />\n {summary && <p className=\"mt-5 text-muted-foreground\">{summary}</p>}\n </section>\n );\n}\n\n/**\n * Edit form (panel surface). The block can be an HTML/SVG fragment or a legacy\n * node/edge/note graph, so this exposes html/css/caption plus a collapsible\n * legacy node-graph JSON editor, each with an AI-edit affordance via\n * `ctx.renderAiFieldAction` (through `AiEditableFieldLabel`). `editSurface:\n * \"panel\"` means the registry renders the `Read` view with a corner edit button\n * that opens this form in the app-provided popover.\n */\nexport function DiagramEdit({\n data,\n onChange,\n editable,\n blockId,\n title,\n summary,\n ctx,\n}: BlockEditProps<DiagramData>) {\n const htmlId = useId();\n const cssId = useId();\n const captionId = useId();\n const legacyId = useId();\n const [html, setHtml] = useState(data.html ?? \"\");\n const [css, setCss] = useState(data.css ?? \"\");\n const [caption, setCaption] = useState(data.caption ?? \"\");\n const [legacyJson, setLegacyJson] = useState(() =>\n JSON.stringify(\n {\n nodes: data.nodes ?? [],\n edges: data.edges ?? [],\n notes: data.notes ?? [],\n },\n null,\n 2,\n ),\n );\n\n useEffect(() => {\n setHtml(data.html ?? \"\");\n setCss(data.css ?? \"\");\n setCaption(data.caption ?? \"\");\n setLegacyJson(\n JSON.stringify(\n {\n nodes: data.nodes ?? [],\n edges: data.edges ?? [],\n notes: data.notes ?? [],\n },\n null,\n 2,\n ),\n );\n }, [data]);\n\n const saveHtmlDiagram = () => {\n onChange({\n html: html.trim() || undefined,\n css: css.trim() || undefined,\n caption: caption.trim() || undefined,\n nodes: data.nodes,\n edges: data.edges,\n notes: data.notes,\n });\n };\n\n const saveLegacyDiagram = () => {\n const parsed = JSON.parse(legacyJson) as Pick<\n DiagramData,\n \"nodes\" | \"edges\" | \"notes\"\n >;\n onChange({\n ...data,\n nodes: parsed.nodes ?? [],\n edges: parsed.edges ?? [],\n notes: parsed.notes ?? [],\n });\n };\n\n const fieldAction = (\n field: \"HTML / SVG fragment\" | \"CSS\" | \"caption\" | \"legacy node graph JSON\",\n value: string,\n ) => ({\n blockId,\n blockType: \"diagram\",\n blockTitle: title,\n blockSummary: summary,\n fieldValue: value,\n draftScope: `block:diagram:${blockId}:${field.toLowerCase().replace(/[^a-z0-9]+/g, \"-\")}`,\n disabled: !editable,\n instructions:\n \"Update the plan with update-visual-plan using a targeted update-block content patch for this diagram block id. Preserve unrelated diagram fields unless the requested edit requires changing them. Keep diagram HTML/CSS on renderer-owned .diagram-* primitives and --wf-* tokens; do not introduce custom font-family or hard-coded hex/rgb/hsl colors.\",\n companionFields: [\n {\n label: \"HTML / SVG fragment\",\n value: html.trim() || \"(empty)\",\n language: \"html\",\n },\n { label: \"CSS\", value: css.trim() || \"(empty)\", language: \"css\" },\n {\n label: \"caption\",\n value: caption.trim() || \"(empty)\",\n language: \"text\",\n },\n ],\n });\n\n return (\n <div className=\"grid gap-4\" data-plan-interactive>\n <button\n type=\"button\"\n className=\"inline-flex h-8 w-fit items-center justify-center rounded-md bg-primary px-3 text-xs font-medium text-primary-foreground disabled:opacity-50\"\n disabled={!editable}\n onClick={saveHtmlDiagram}\n >\n Save diagram\n </button>\n <div className=\"group/field grid gap-1.5\">\n <AiEditableFieldLabel\n htmlFor={htmlId}\n label=\"HTML / SVG fragment\"\n ctx={ctx}\n action={fieldAction(\"HTML / SVG fragment\", html)}\n />\n <textarea\n id={htmlId}\n className=\"min-h-48 w-full rounded-md border border-input bg-background px-3 py-2 font-mono text-xs leading-5 text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring\"\n value={html}\n disabled={!editable}\n onChange={(event) => setHtml(event.target.value)}\n placeholder=\"<div class='diagram'>...</div>\"\n />\n </div>\n <div className=\"group/field grid gap-1.5\">\n <AiEditableFieldLabel\n htmlFor={cssId}\n label=\"CSS\"\n ctx={ctx}\n action={fieldAction(\"CSS\", css)}\n />\n <textarea\n id={cssId}\n className=\"min-h-32 w-full rounded-md border border-input bg-background px-3 py-2 font-mono text-xs leading-5 text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring\"\n value={css}\n disabled={!editable}\n onChange={(event) => setCss(event.target.value)}\n placeholder=\".diagram { display: grid; }\"\n />\n </div>\n <div className=\"group/field grid gap-1.5\">\n <AiEditableFieldLabel\n htmlFor={captionId}\n label=\"Caption\"\n ctx={ctx}\n action={fieldAction(\"caption\", caption)}\n />\n <input\n id={captionId}\n className=\"h-9 w-full rounded-md border border-input bg-background px-3 text-sm text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring\"\n value={caption}\n disabled={!editable}\n onChange={(event) => setCaption(event.target.value)}\n />\n </div>\n {!data.html && (\n <details className=\"rounded-md border border-border p-3\">\n <summary className=\"cursor-pointer text-xs font-semibold text-muted-foreground\">\n Legacy node graph data\n </summary>\n <div className=\"group/field mt-3 grid gap-1.5\">\n <AiEditableFieldLabel\n htmlFor={legacyId}\n label=\"JSON\"\n ctx={ctx}\n action={fieldAction(\"legacy node graph JSON\", legacyJson)}\n />\n <textarea\n id={legacyId}\n className=\"min-h-44 w-full rounded-md border border-input bg-background px-3 py-2 font-mono text-xs leading-5 text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring\"\n value={legacyJson}\n disabled={!editable}\n onChange={(event) => setLegacyJson(event.target.value)}\n />\n </div>\n <button\n type=\"button\"\n className=\"mt-3 inline-flex h-8 items-center justify-center rounded-md border border-input px-3 text-xs font-medium text-foreground disabled:opacity-50\"\n disabled={!editable}\n onClick={saveLegacyDiagram}\n >\n Save graph data\n </button>\n </details>\n )}\n </div>\n );\n}\n\n/** Full client spec for the shared `diagram` block (schema + MDX + Read/Edit). */\nexport const diagramBlock = defineBlock<DiagramData>({\n type: \"diagram\",\n schema: diagramSchema,\n mdx: diagramMdx,\n Read: DiagramRead,\n Edit: DiagramEdit,\n placement: [\"block\"],\n // Config-driven: the rendered diagram differs from its raw html/css source, so\n // edit from a corner button + panel rather than inline.\n editSurface: \"panel\",\n label: \"Diagram\",\n description:\n \"A flexible inline architecture/code diagram. Prefer html/css with SVG or semantic HTML for polished two-dimensional layouts; use .diagram-* primitives and --wf-* tokens for theme/sketch compatibility. Legacy nodes/edges are only for simple previews.\",\n // Seed the legacy fallback shape so a fresh block validates while agents can\n // replace it with html/css when layout quality matters.\n empty: () => ({ nodes: [{ id: \"n1\", label: \"Module\" }], edges: [] }),\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":"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","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