@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.
- package/README.md +17 -56
- package/dist/cli/recap.d.ts.map +1 -1
- package/dist/cli/recap.js +24 -13
- package/dist/cli/recap.js.map +1 -1
- package/dist/cli/skills.d.ts +2 -6
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +8 -66
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/blocks/index.d.ts +11 -0
- package/dist/client/blocks/index.d.ts.map +1 -1
- package/dist/client/blocks/index.js +11 -0
- package/dist/client/blocks/index.js.map +1 -1
- package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/AnnotatedCodeBlock.js +2 -2
- package/dist/client/blocks/library/AnnotatedCodeBlock.js.map +1 -1
- package/dist/client/blocks/library/DiffBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/DiffBlock.js +86 -21
- package/dist/client/blocks/library/DiffBlock.js.map +1 -1
- package/dist/client/blocks/library/FileTreeBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/FileTreeBlock.js +27 -4
- package/dist/client/blocks/library/FileTreeBlock.js.map +1 -1
- package/dist/client/blocks/library/JsonExplorerBlock.js +1 -1
- package/dist/client/blocks/library/JsonExplorerBlock.js.map +1 -1
- package/dist/client/blocks/library/MermaidBlock.js +1 -1
- package/dist/client/blocks/library/MermaidBlock.js.map +1 -1
- package/dist/client/blocks/library/annotation-rail.d.ts +19 -0
- package/dist/client/blocks/library/annotation-rail.d.ts.map +1 -1
- package/dist/client/blocks/library/annotation-rail.js +19 -0
- package/dist/client/blocks/library/annotation-rail.js.map +1 -1
- package/dist/client/blocks/library/callout.config.d.ts +29 -0
- package/dist/client/blocks/library/callout.config.d.ts.map +1 -0
- package/dist/client/blocks/library/callout.config.js +33 -0
- package/dist/client/blocks/library/callout.config.js.map +1 -0
- package/dist/client/blocks/library/callout.d.ts +20 -0
- package/dist/client/blocks/library/callout.d.ts.map +1 -0
- package/dist/client/blocks/library/callout.js +61 -0
- package/dist/client/blocks/library/callout.js.map +1 -0
- package/dist/client/blocks/library/checklist.d.ts.map +1 -1
- package/dist/client/blocks/library/checklist.js +3 -3
- package/dist/client/blocks/library/checklist.js.map +1 -1
- package/dist/client/blocks/library/decision.config.d.ts +37 -0
- package/dist/client/blocks/library/decision.config.d.ts.map +1 -0
- package/dist/client/blocks/library/decision.config.js +32 -0
- package/dist/client/blocks/library/decision.config.js.map +1 -0
- package/dist/client/blocks/library/decision.d.ts +19 -0
- package/dist/client/blocks/library/decision.d.ts.map +1 -0
- package/dist/client/blocks/library/decision.js +119 -0
- package/dist/client/blocks/library/decision.js.map +1 -0
- package/dist/client/blocks/library/diagram.config.d.ts +64 -0
- package/dist/client/blocks/library/diagram.config.d.ts.map +1 -0
- package/dist/client/blocks/library/diagram.config.js +111 -0
- package/dist/client/blocks/library/diagram.config.js.map +1 -0
- package/dist/client/blocks/library/diagram.d.ts +16 -0
- package/dist/client/blocks/library/diagram.d.ts.map +1 -0
- package/dist/client/blocks/library/diagram.js +261 -0
- package/dist/client/blocks/library/diagram.js.map +1 -0
- package/dist/client/blocks/library/question-form.config.d.ts +69 -0
- package/dist/client/blocks/library/question-form.config.d.ts.map +1 -0
- package/dist/client/blocks/library/question-form.config.js +58 -0
- package/dist/client/blocks/library/question-form.config.js.map +1 -0
- package/dist/client/blocks/library/question-form.d.ts +20 -0
- package/dist/client/blocks/library/question-form.d.ts.map +1 -0
- package/dist/client/blocks/library/question-form.js +286 -0
- package/dist/client/blocks/library/question-form.js.map +1 -0
- package/dist/client/blocks/library/sanitize-html.d.ts +5 -0
- package/dist/client/blocks/library/sanitize-html.d.ts.map +1 -0
- package/dist/client/blocks/library/sanitize-html.js +240 -0
- package/dist/client/blocks/library/sanitize-html.js.map +1 -0
- package/dist/client/blocks/library/server-specs.d.ts.map +1 -1
- package/dist/client/blocks/library/server-specs.js +59 -0
- package/dist/client/blocks/library/server-specs.js.map +1 -1
- package/dist/client/blocks/library/specs.d.ts.map +1 -1
- package/dist/client/blocks/library/specs.js +11 -0
- package/dist/client/blocks/library/specs.js.map +1 -1
- package/dist/client/blocks/library/tabs.d.ts.map +1 -1
- package/dist/client/blocks/library/tabs.js +12 -12
- package/dist/client/blocks/library/tabs.js.map +1 -1
- package/dist/client/blocks/library/wireframe-kit.d.ts +260 -0
- package/dist/client/blocks/library/wireframe-kit.d.ts.map +1 -0
- package/dist/client/blocks/library/wireframe-kit.js +920 -0
- package/dist/client/blocks/library/wireframe-kit.js.map +1 -0
- package/dist/client/blocks/library/wireframe.config.d.ts +123 -0
- package/dist/client/blocks/library/wireframe.config.d.ts.map +1 -0
- package/dist/client/blocks/library/wireframe.config.js +294 -0
- package/dist/client/blocks/library/wireframe.config.js.map +1 -0
- package/dist/client/blocks/library/wireframe.d.ts +15 -0
- package/dist/client/blocks/library/wireframe.d.ts.map +1 -0
- package/dist/client/blocks/library/wireframe.js +206 -0
- package/dist/client/blocks/library/wireframe.js.map +1 -0
- package/dist/client/blocks/registry.d.ts +9 -0
- package/dist/client/blocks/registry.d.ts.map +1 -1
- package/dist/client/blocks/registry.js +12 -5
- package/dist/client/blocks/registry.js.map +1 -1
- package/dist/client/blocks/server.d.ts +1 -0
- package/dist/client/blocks/server.d.ts.map +1 -1
- package/dist/client/blocks/server.js +1 -0
- package/dist/client/blocks/server.js.map +1 -1
- package/dist/client/blocks/types.d.ts +8 -0
- package/dist/client/blocks/types.d.ts.map +1 -1
- package/dist/client/blocks/types.js.map +1 -1
- package/dist/client/rich-markdown-editor/DragHandle.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/DragHandle.js +77 -12
- package/dist/client/rich-markdown-editor/DragHandle.js.map +1 -1
- package/dist/styles/agent-native.css +1 -0
- package/dist/styles/blocks.css +1380 -0
- package/docs/content/plan-plugin.md +8 -8
- package/docs/content/pr-visual-recap.md +2 -2
- package/docs/content/template-plan.md +94 -17
- package/package.json +2 -1
- package/docs/content/visual-plans.md +0 -82
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"annotation-rail.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/annotation-rail.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useMemo, type ReactNode } from \"react\";\nimport { cn } from \"../../utils.js\";\nimport type { BlockRenderContext } from \"../types.js\";\n\n/**\n * Shared line-anchored annotation UI for the `annotated-code` and `diff` blocks.\n *\n * Both blocks render a numbered code surface plus a side \"rail\" of notes, where\n * each note targets a 1-based `lines` ref (`\"3\"` or `\"3-5\"`) and hovering a code\n * line ↔ its note cross-highlights. This module owns the pure pieces that were\n * identical between them so neither block forks the behavior:\n *\n * - `parseLineRange` — the forgiving 1-based `lines` range parser.\n * - `resolveAnnotations` / `buildLineMarkerMap` — turn a raw annotation list\n * into stable, marker-numbered, range-resolved records and a line→markers map.\n * - `rangeLabel` — the human \"Line 8\" / \"Lines 3–6\" label.\n * - `AnnotationGutterMarker` — the numbered amber pip placed on an annotated row\n * (used by the diff grid; the annotated-code surface uses its own rail bar).\n * - `AnnotationNoteRail` — the responsive list of note cards with two-way hover.\n * `showMarker` opts the diff block into a leading numbered pip on each card so\n * a note can be matched to its `①`/`②` row marker; annotated-code omits it to\n * keep its original card chrome.\n *\n * `AnnotatedCodeBlock` annotates a single code surface; `DiffBlock` annotates a\n * before/after grid (each annotation also carries a `side`). The shared types\n * here are intentionally minimal — callers pass their own `side` handling and\n * decide which rows a marker lands on; this module only owns the parsing, the\n * resolved-record shape, and the rendered marker + rail chrome.\n */\n\n/* ── Line-ref parsing ──────────────────────────────────────────────────────── */\n\n/**\n * Parse a 1-based `lines` ref (`\"3\"` or `\"3-5\"`) into an inclusive `[start,end]`\n * pair, clamped to `[1, lineCount]`. Returns `null` for malformed or fully\n * out-of-range refs so callers can ignore them gracefully. A reversed range\n * (`\"5-3\"`) is normalized; a partially out-of-range range is clamped.\n */\nexport function parseLineRange(\n ref: string,\n lineCount: number,\n): { start: number; end: number } | null {\n const match = /^\\s*(\\d+)\\s*(?:-\\s*(\\d+)\\s*)?$/.exec(ref);\n if (!match) return null;\n let start = Number.parseInt(match[1], 10);\n let end = match[2] != null ? Number.parseInt(match[2], 10) : start;\n if (!Number.isFinite(start) || !Number.isFinite(end)) return null;\n if (start > end) [start, end] = [end, start];\n // Fully outside the file → ignore.\n if (end < 1 || start > lineCount) return null;\n return { start: Math.max(1, start), end: Math.min(lineCount, end) };\n}\n\n/** The minimal annotation shape the rail needs (a superset works too). */\nexport interface RailAnnotation {\n lines: string;\n label?: string;\n note: string;\n}\n\nexport interface ResolvedAnnotation<A extends RailAnnotation = RailAnnotation> {\n /** Index in the original `annotations` array (stable hover key). */\n index: number;\n /** 1-based marker number (authoring order). */\n marker: number;\n annotation: A;\n range: { start: number; end: number } | null;\n}\n\n/**\n * Resolve a raw annotation list into stable, marker-numbered records, parsing\n * each `lines` ref against `lineCount`. `lineCountFor` lets the diff block pick a\n * per-annotation line count (before-side vs after-side); annotated-code passes a\n * single constant. Markers are authoring-order, 1-based, and assigned to ALL\n * annotations (even unresolved ones) so numbering is stable regardless of which\n * refs happen to match.\n */\nexport function resolveAnnotations<A extends RailAnnotation>(\n annotations: A[] | undefined,\n lineCountFor: (annotation: A) => number,\n): ResolvedAnnotation<A>[] {\n return (annotations ?? []).map((annotation, index) => ({\n index,\n marker: index + 1,\n annotation,\n range: parseLineRange(annotation.lines, lineCountFor(annotation)),\n }));\n}\n\n/** Map a 1-based line number → the resolved annotations covering it. */\nexport function buildLineMarkerMap<A extends RailAnnotation>(\n resolved: ResolvedAnnotation<A>[],\n): Map<number, ResolvedAnnotation<A>[]> {\n const map = new Map<number, ResolvedAnnotation<A>[]>();\n for (const item of resolved) {\n if (!item.range) continue;\n for (let n = item.range.start; n <= item.range.end; n += 1) {\n const list = map.get(n) ?? [];\n list.push(item);\n map.set(n, list);\n }\n }\n return map;\n}\n\n/** Human label for a resolved annotation's line span (\"Line 8\" / \"Lines 3–6\"). */\nexport function rangeLabel(item: ResolvedAnnotation): string {\n if (!item.range) return `Lines ${item.annotation.lines}`;\n return item.range.start === item.range.end\n ? `Line ${item.range.start}`\n : `Lines ${item.range.start}–${item.range.end}`;\n}\n\n/* ── Marker ────────────────────────────────────────────────────────────────── */\n\n/**\n * The numbered amber pip rendered on an annotated code row's gutter. `active`\n * brightens it when its note (or a co-located row) is hovered.\n */\nexport function AnnotationGutterMarker({\n marker,\n active,\n className,\n}: {\n marker: number;\n active: boolean;\n className?: string;\n}) {\n return (\n <span\n aria-hidden\n className={cn(\n \"inline-flex size-[15px] shrink-0 items-center justify-center rounded-full text-[9px] font-semibold leading-none tabular-nums transition-colors\",\n active\n ? \"bg-amber-500 text-white dark:bg-amber-400 dark:text-amber-950\"\n : \"bg-amber-400/25 text-amber-700 dark:bg-amber-300/20 dark:text-amber-300\",\n className,\n )}\n >\n {marker}\n </span>\n );\n}\n\n/* ── Note rail ─────────────────────────────────────────────────────────────── */\n\n/**\n * The responsive list of line-anchored note cards. Each card shows its marker\n * pip, the resolved line span (\"Line 8\"), an optional label, and the markdown\n * `note` (via `ctx.renderMarkdown`). Hovering a card sets the active index;\n * `activeIndex` driven from outside lets a hovered code row light its card and\n * vice-versa. Only annotations whose `range` resolved are listed.\n */\nexport function AnnotationNoteRail<A extends RailAnnotation>({\n items,\n activeIndex,\n onActiveChange,\n ctx,\n className,\n showMarker = false,\n}: {\n items: ResolvedAnnotation<A>[];\n activeIndex: number | null;\n onActiveChange: (index: number | null) => void;\n ctx: BlockRenderContext;\n className?: string;\n /** Show a leading numbered pip on each card (diff block). */\n showMarker?: boolean;\n}) {\n const sideAnnotations = useMemo(\n () => items.filter((item) => item.range),\n [items],\n );\n return (\n <div className={cn(\"flex flex-col gap-2.5\", className)}>\n {sideAnnotations.map((item) => {\n const isActive = activeIndex === item.index;\n return (\n <div\n key={item.index}\n onMouseEnter={() => onActiveChange(item.index)}\n onMouseLeave={() => onActiveChange(null)}\n className={cn(\n \"rounded-lg border px-3.5 py-2.5 transition-colors\",\n isActive\n ? \"border-amber-400/70 bg-amber-50 dark:border-amber-300/40 dark:bg-amber-300/[0.08]\"\n : \"border-plan-line bg-plan-block/40 hover:border-amber-400/50\",\n )}\n >\n <div\n className={cn(\n \"flex flex-wrap gap-x-2 gap-y-0.5\",\n showMarker ? \"items-center\" : \"items-baseline\",\n )}\n >\n {showMarker && (\n <AnnotationGutterMarker\n marker={item.marker}\n active={isActive}\n />\n )}\n <span className=\"text-[11px] font-semibold uppercase tracking-wide text-plan-muted\">\n {rangeLabel(item)}\n </span>\n {item.annotation.label && (\n <span className=\"text-[13px] font-semibold text-plan-text\">\n {item.annotation.label}\n </span>\n )}\n </div>\n <div className=\"plan-annotation-note mt-1 text-[13px] leading-relaxed text-plan-text/85\">\n {ctx.renderMarkdown ? (\n ctx.renderMarkdown(item.annotation.note)\n ) : (\n <p>{item.annotation.note}</p>\n )}\n </div>\n </div>\n );\n })}\n </div>\n );\n}\n\n/** Whether a resolved list has at least one note worth rendering a rail for. */\nexport function hasRailAnnotations(items: ResolvedAnnotation[]): boolean {\n return items.some((item) => item.range);\n}\n\nexport type AnnotationRailChildren = ReactNode;\n"]}
|
|
1
|
+
{"version":3,"file":"annotation-rail.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/annotation-rail.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useMemo, type ReactNode } from \"react\";\nimport { cn } from \"../../utils.js\";\nimport type { BlockRenderContext } from \"../types.js\";\n\n/**\n * Shared line-anchored annotation UI for the `annotated-code` and `diff` blocks.\n *\n * Both blocks render a numbered code surface plus a side \"rail\" of notes, where\n * each note targets a 1-based `lines` ref (`\"3\"` or `\"3-5\"`) and hovering a code\n * line ↔ its note cross-highlights. This module owns the pure pieces that were\n * identical between them so neither block forks the behavior:\n *\n * - `parseLineRange` — the forgiving 1-based `lines` range parser.\n * - `resolveAnnotations` / `buildLineMarkerMap` — turn a raw annotation list\n * into stable, marker-numbered, range-resolved records and a line→markers map.\n * - `rangeLabel` — the human \"Line 8\" / \"Lines 3–6\" label.\n * - `AnnotationGutterMarker` — the numbered amber pip placed on an annotated row\n * (used by the diff grid; the annotated-code surface uses its own rail bar).\n * - `AnnotationNoteRail` — the responsive list of note cards with two-way hover.\n * `showMarker` opts the diff block into a leading numbered pip on each card so\n * a note can be matched to its `①`/`②` row marker; annotated-code omits it to\n * keep its original card chrome.\n *\n * `AnnotatedCodeBlock` annotates a single code surface; `DiffBlock` annotates a\n * before/after grid (each annotation also carries a `side`). The shared types\n * here are intentionally minimal — callers pass their own `side` handling and\n * decide which rows a marker lands on; this module only owns the parsing, the\n * resolved-record shape, and the rendered marker + rail chrome.\n */\n\n/* ── Line-ref parsing ──────────────────────────────────────────────────────── */\n\n/**\n * Parse a 1-based `lines` ref (`\"3\"` or `\"3-5\"`) into an inclusive `[start,end]`\n * pair, clamped to `[1, lineCount]`. Returns `null` for malformed or fully\n * out-of-range refs so callers can ignore them gracefully. A reversed range\n * (`\"5-3\"`) is normalized; a partially out-of-range range is clamped.\n */\nexport function parseLineRange(\n ref: string,\n lineCount: number,\n): { start: number; end: number } | null {\n const match = /^\\s*(\\d+)\\s*(?:-\\s*(\\d+)\\s*)?$/.exec(ref);\n if (!match) return null;\n let start = Number.parseInt(match[1], 10);\n let end = match[2] != null ? Number.parseInt(match[2], 10) : start;\n if (!Number.isFinite(start) || !Number.isFinite(end)) return null;\n if (start > end) [start, end] = [end, start];\n // Fully outside the file → ignore.\n if (end < 1 || start > lineCount) return null;\n return { start: Math.max(1, start), end: Math.min(lineCount, end) };\n}\n\n/** The minimal annotation shape the rail needs (a superset works too). */\nexport interface RailAnnotation {\n lines: string;\n label?: string;\n note: string;\n}\n\nexport interface ResolvedAnnotation<A extends RailAnnotation = RailAnnotation> {\n /** Index in the original `annotations` array (stable hover key). */\n index: number;\n /** 1-based marker number (authoring order). */\n marker: number;\n annotation: A;\n range: { start: number; end: number } | null;\n}\n\n/**\n * Resolve a raw annotation list into stable, marker-numbered records, parsing\n * each `lines` ref against `lineCount`. `lineCountFor` lets the diff block pick a\n * per-annotation line count (before-side vs after-side); annotated-code passes a\n * single constant. Markers are authoring-order, 1-based, and assigned to ALL\n * annotations (even unresolved ones) so numbering is stable regardless of which\n * refs happen to match.\n */\nexport function resolveAnnotations<A extends RailAnnotation>(\n annotations: A[] | undefined,\n lineCountFor: (annotation: A) => number,\n): ResolvedAnnotation<A>[] {\n return (annotations ?? []).map((annotation, index) => ({\n index,\n marker: index + 1,\n annotation,\n range: parseLineRange(annotation.lines, lineCountFor(annotation)),\n }));\n}\n\n/** Map a 1-based line number → the resolved annotations covering it. */\nexport function buildLineMarkerMap<A extends RailAnnotation>(\n resolved: ResolvedAnnotation<A>[],\n): Map<number, ResolvedAnnotation<A>[]> {\n const map = new Map<number, ResolvedAnnotation<A>[]>();\n for (const item of resolved) {\n if (!item.range) continue;\n for (let n = item.range.start; n <= item.range.end; n += 1) {\n const list = map.get(n) ?? [];\n list.push(item);\n map.set(n, list);\n }\n }\n return map;\n}\n\n/** Human label for a resolved annotation's line span (\"Line 8\" / \"Lines 3–6\"). */\nexport function rangeLabel(item: ResolvedAnnotation): string {\n if (!item.range) return `Lines ${item.annotation.lines}`;\n return item.range.start === item.range.end\n ? `Line ${item.range.start}`\n : `Lines ${item.range.start}–${item.range.end}`;\n}\n\n/* ── Marker ────────────────────────────────────────────────────────────────── */\n\n/**\n * The numbered amber pip rendered on an annotated code row's gutter. `active`\n * brightens it when its note (or a co-located row) is hovered.\n */\nexport function AnnotationGutterMarker({\n marker,\n active,\n className,\n}: {\n marker: number;\n active: boolean;\n className?: string;\n}) {\n return (\n <span\n aria-hidden\n className={cn(\n \"inline-flex size-[15px] shrink-0 items-center justify-center rounded-full text-[9px] font-semibold leading-none tabular-nums transition-colors\",\n active\n ? \"bg-amber-500 text-white dark:bg-amber-400 dark:text-amber-950\"\n : \"bg-amber-400/25 text-amber-700 dark:bg-amber-300/20 dark:text-amber-300\",\n className,\n )}\n >\n {marker}\n </span>\n );\n}\n\n/* ── Note rail ─────────────────────────────────────────────────────────────── */\n\n/**\n * The responsive list of line-anchored note cards. Each card shows its marker\n * pip, the resolved line span (\"Line 8\"), an optional label, and the markdown\n * `note` (via `ctx.renderMarkdown`). Hovering a card sets the active index;\n * `activeIndex` driven from outside lets a hovered code row light its card and\n * vice-versa. Only annotations whose `range` resolved are listed.\n */\nexport function AnnotationNoteRail<A extends RailAnnotation>({\n items,\n activeIndex,\n onActiveChange,\n ctx,\n className,\n showMarker = false,\n}: {\n items: ResolvedAnnotation<A>[];\n activeIndex: number | null;\n onActiveChange: (index: number | null) => void;\n ctx: BlockRenderContext;\n className?: string;\n /** Show a leading numbered pip on each card (diff block). */\n showMarker?: boolean;\n}) {\n const sideAnnotations = useMemo(\n () => items.filter((item) => item.range),\n [items],\n );\n return (\n <div className={cn(\"flex flex-col gap-2.5\", className)}>\n {sideAnnotations.map((item) => {\n const isActive = activeIndex === item.index;\n return (\n <div\n key={item.index}\n onMouseEnter={() => onActiveChange(item.index)}\n onMouseLeave={() => onActiveChange(null)}\n className={cn(\n \"rounded-lg border px-3.5 py-2.5 transition-colors\",\n isActive\n ? \"border-amber-400/70 bg-amber-50 dark:border-amber-300/40 dark:bg-amber-300/[0.08]\"\n : \"border-plan-line bg-plan-block/40 hover:border-amber-400/50\",\n )}\n >\n <div\n className={cn(\n \"flex flex-wrap gap-x-2 gap-y-0.5\",\n showMarker ? \"items-center\" : \"items-baseline\",\n )}\n >\n {showMarker && (\n <AnnotationGutterMarker\n marker={item.marker}\n active={isActive}\n />\n )}\n <span className=\"text-[11px] font-semibold uppercase tracking-wide text-plan-muted\">\n {rangeLabel(item)}\n </span>\n {item.annotation.label && (\n <span className=\"text-[13px] font-semibold text-plan-text\">\n {item.annotation.label}\n </span>\n )}\n </div>\n <div className=\"plan-annotation-note mt-1 text-[13px] leading-relaxed text-plan-text/85\">\n {ctx.renderMarkdown ? (\n ctx.renderMarkdown(item.annotation.note)\n ) : (\n <p>{item.annotation.note}</p>\n )}\n </div>\n </div>\n );\n })}\n </div>\n );\n}\n\n/* ── Inline note (GitHub-style) ─────────────────────────────────────────────── */\n\n/**\n * A single line-anchored note rendered INLINE in the diff flow — full-width,\n * directly under the line it annotates — instead of in a side rail (the\n * GitHub-review affordance). The diff block places one of these after the first\n * row of each annotation's range, so the numbered `①`/`②` row marker and the note\n * read together in one column. It carries the same marker pip, line range,\n * optional label, and markdown `note` as a rail card, and the same two-way hover\n * wiring (`active` / `onActiveChange`) so the row and its note cross-highlight.\n *\n * The outer band is `min-w-full` so the amber wash spans the diff's full scroll\n * width; the content is `sticky left-0` with a readable `max-w` so the note stays\n * pinned and legible while long code lines scroll horizontally beneath it.\n */\nexport function InlineAnnotationNote<A extends RailAnnotation>({\n item,\n active,\n onActiveChange,\n ctx,\n}: {\n item: ResolvedAnnotation<A>;\n active: boolean;\n onActiveChange: (index: number | null) => void;\n ctx: BlockRenderContext;\n}) {\n return (\n <div\n onMouseEnter={() => onActiveChange(item.index)}\n onMouseLeave={() => onActiveChange(null)}\n className={cn(\n \"min-w-full border-y transition-colors\",\n active\n ? \"border-amber-400/60 bg-amber-100/70 dark:border-amber-300/40 dark:bg-amber-300/[0.12]\"\n : \"border-amber-400/30 bg-amber-50/70 dark:border-amber-300/20 dark:bg-amber-300/[0.06]\",\n )}\n >\n <div className=\"sticky left-0 flex max-w-[44rem] gap-2.5 whitespace-normal px-3 py-2.5 font-sans\">\n <AnnotationGutterMarker marker={item.marker} active={active} />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex flex-wrap items-baseline gap-x-2 gap-y-0.5\">\n <span className=\"text-[11px] font-semibold uppercase tracking-wide text-plan-muted\">\n {rangeLabel(item)}\n </span>\n {item.annotation.label && (\n <span className=\"text-[13px] font-semibold text-plan-text\">\n {item.annotation.label}\n </span>\n )}\n </div>\n <div className=\"plan-annotation-note mt-1 text-[13px] leading-relaxed text-plan-text/85\">\n {ctx.renderMarkdown ? (\n ctx.renderMarkdown(item.annotation.note)\n ) : (\n <p>{item.annotation.note}</p>\n )}\n </div>\n </div>\n </div>\n </div>\n );\n}\n\n/** Whether a resolved list has at least one note worth rendering a rail for. */\nexport function hasRailAnnotations(items: ResolvedAnnotation[]): boolean {\n return items.some((item) => item.range);\n}\n\nexport type AnnotationRailChildren = ReactNode;\n"]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { BlockMdxConfig } from "../types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Pure (React-free) part of the shared `callout` block: its data schema and MDX
|
|
5
|
+
* round-trip config. Lives in core so BOTH apps' server/shared registries
|
|
6
|
+
* (`plan-block-registry.ts`, `nfm-registry.ts`) and the client spec
|
|
7
|
+
* (`callout.tsx`) consume one definition. Keeping it React-free means importing
|
|
8
|
+
* it into a server module never pulls React into the Nitro/SSR bundle.
|
|
9
|
+
*
|
|
10
|
+
* The MDX `tag` + attribute/children shape MUST match the legacy
|
|
11
|
+
* `<Callout tone>…body…</Callout>` encoding so stored `.mdx` round-trips
|
|
12
|
+
* byte-compatibly (the block originated in the plan template before moving here).
|
|
13
|
+
*/
|
|
14
|
+
export type CalloutTone = "info" | "decision" | "risk" | "warning" | "success";
|
|
15
|
+
export interface CalloutData {
|
|
16
|
+
tone?: CalloutTone;
|
|
17
|
+
/** Markdown body. Tagged `markdown()` so the auto-editor edits it inline. */
|
|
18
|
+
body: string;
|
|
19
|
+
}
|
|
20
|
+
export declare const CALLOUT_TONES: CalloutTone[];
|
|
21
|
+
export declare const calloutSchema: z.ZodType<CalloutData>;
|
|
22
|
+
/**
|
|
23
|
+
* MDX config: `tone` is an attribute, `body` is MDX children — exactly the
|
|
24
|
+
* legacy `<Callout id … tone>\n\n{body}\n\n</Callout>` form. `toAttrs` returns
|
|
25
|
+
* only `tone` (body is `childrenField`, excluded from attributes); `fromAttrs`
|
|
26
|
+
* reads `tone` and uses the stringified prose children as `body`.
|
|
27
|
+
*/
|
|
28
|
+
export declare const calloutMdx: BlockMdxConfig<CalloutData>;
|
|
29
|
+
//# sourceMappingURL=callout.config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"callout.config.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/callout.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD;;;;;;;;;;GAUG;AAEH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,CAAC;AAE/E,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,6EAA6E;IAC7E,IAAI,EAAE,MAAM,CAAC;CACd;AAED,eAAO,MAAM,aAAa,EAAE,WAAW,EAMtC,CAAC;AAEF,eAAO,MAAM,aAAa,EAOT,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;AAExC;;;;;GAKG;AACH,eAAO,MAAM,UAAU,EAAE,cAAc,CAAC,WAAW,CAQlD,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { markdown } from "../schema-form/introspect.js";
|
|
3
|
+
export const CALLOUT_TONES = [
|
|
4
|
+
"info",
|
|
5
|
+
"decision",
|
|
6
|
+
"risk",
|
|
7
|
+
"warning",
|
|
8
|
+
"success",
|
|
9
|
+
];
|
|
10
|
+
export const calloutSchema = z.object({
|
|
11
|
+
tone: z
|
|
12
|
+
.enum(["info", "decision", "risk", "warning", "success"])
|
|
13
|
+
.optional(),
|
|
14
|
+
// `markdown()` tags the field so `SchemaBlockEditor` renders it with the
|
|
15
|
+
// shared rich-markdown editor (inline, Notion-style) instead of a textarea.
|
|
16
|
+
body: markdown(z.string().trim().min(1).max(10_000)),
|
|
17
|
+
});
|
|
18
|
+
/**
|
|
19
|
+
* MDX config: `tone` is an attribute, `body` is MDX children — exactly the
|
|
20
|
+
* legacy `<Callout id … tone>\n\n{body}\n\n</Callout>` form. `toAttrs` returns
|
|
21
|
+
* only `tone` (body is `childrenField`, excluded from attributes); `fromAttrs`
|
|
22
|
+
* reads `tone` and uses the stringified prose children as `body`.
|
|
23
|
+
*/
|
|
24
|
+
export const calloutMdx = {
|
|
25
|
+
tag: "Callout",
|
|
26
|
+
childrenField: "body",
|
|
27
|
+
toAttrs: (data) => ({ tone: data.tone }),
|
|
28
|
+
fromAttrs: (attrs, children) => ({
|
|
29
|
+
tone: attrs.string("tone"),
|
|
30
|
+
body: children,
|
|
31
|
+
}),
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=callout.config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"callout.config.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/callout.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAuBxD,MAAM,CAAC,MAAM,aAAa,GAAkB;IAC1C,MAAM;IACN,UAAU;IACV,MAAM;IACN,SAAS;IACT,SAAS;CACV,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,IAAI,EAAE,CAAC;SACJ,IAAI,CAAC,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;SACxD,QAAQ,EAAwC;IACnD,yEAAyE;IACzE,4EAA4E;IAC5E,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAsB;CAC1E,CAAsC,CAAC;AAExC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,UAAU,GAAgC;IACrD,GAAG,EAAE,SAAS;IACd,aAAa,EAAE,MAAM;IACrB,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;IACxC,SAAS,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;QAC/B,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,CAA4B;QACrD,IAAI,EAAE,QAAQ;KACf,CAAC;CACH,CAAC","sourcesContent":["import { z } from \"zod\";\nimport { markdown } from \"../schema-form/introspect.js\";\nimport type { BlockMdxConfig } from \"../types.js\";\n\n/**\n * Pure (React-free) part of the shared `callout` block: its data schema and MDX\n * round-trip config. Lives in core so BOTH apps' server/shared registries\n * (`plan-block-registry.ts`, `nfm-registry.ts`) and the client spec\n * (`callout.tsx`) consume one definition. Keeping it React-free means importing\n * it into a server module never pulls React into the Nitro/SSR bundle.\n *\n * The MDX `tag` + attribute/children shape MUST match the legacy\n * `<Callout tone>…body…</Callout>` encoding so stored `.mdx` round-trips\n * byte-compatibly (the block originated in the plan template before moving here).\n */\n\nexport type CalloutTone = \"info\" | \"decision\" | \"risk\" | \"warning\" | \"success\";\n\nexport interface CalloutData {\n tone?: CalloutTone;\n /** Markdown body. Tagged `markdown()` so the auto-editor edits it inline. */\n body: string;\n}\n\nexport const CALLOUT_TONES: CalloutTone[] = [\n \"info\",\n \"decision\",\n \"risk\",\n \"warning\",\n \"success\",\n];\n\nexport const calloutSchema = z.object({\n tone: z\n .enum([\"info\", \"decision\", \"risk\", \"warning\", \"success\"])\n .optional() as z.ZodType<CalloutTone | undefined>,\n // `markdown()` tags the field so `SchemaBlockEditor` renders it with the\n // shared rich-markdown editor (inline, Notion-style) instead of a textarea.\n body: markdown(z.string().trim().min(1).max(10_000)) as z.ZodType<string>,\n}) as unknown as z.ZodType<CalloutData>;\n\n/**\n * MDX config: `tone` is an attribute, `body` is MDX children — exactly the\n * legacy `<Callout id … tone>\\n\\n{body}\\n\\n</Callout>` form. `toAttrs` returns\n * only `tone` (body is `childrenField`, excluded from attributes); `fromAttrs`\n * reads `tone` and uses the stringified prose children as `body`.\n */\nexport const calloutMdx: BlockMdxConfig<CalloutData> = {\n tag: \"Callout\",\n childrenField: \"body\",\n toAttrs: (data) => ({ tone: data.tone }),\n fromAttrs: (attrs, children) => ({\n tone: attrs.string(\"tone\") as CalloutTone | undefined,\n body: children,\n }),\n};\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { BlockReadProps, BlockEditProps } from "../types.js";
|
|
2
|
+
import { type CalloutData } from "./callout.config.js";
|
|
3
|
+
/**
|
|
4
|
+
* Standard `callout` block — an emphasized note with a tone (info / decision /
|
|
5
|
+
* risk / warning / success) and a markdown body. Lives in core so any app can
|
|
6
|
+
* register it (it originated in the plan template).
|
|
7
|
+
*
|
|
8
|
+
* The section carries BOTH the app-neutral `an-callout` classes (styled by
|
|
9
|
+
* core's `blocks.css` with shadcn theme tokens, so it looks right in any app)
|
|
10
|
+
* and the legacy `plan-callout` classes (styled by the plan template's own
|
|
11
|
+
* stylesheet). Plan therefore renders byte-identically to before; content (and
|
|
12
|
+
* any other app) gets the theme-token treatment. `data-tone` drives the accent
|
|
13
|
+
* in both. The body renders through `ctx.renderMarkdown` so each app supplies
|
|
14
|
+
* its own GFM renderer (plan's react-markdown reader, content's, etc.).
|
|
15
|
+
*/
|
|
16
|
+
export declare function CalloutBlock({ data, blockId, title, ctx, }: BlockReadProps<CalloutData>): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
export declare function CalloutBlockEdit({ data, onChange, editable, blockId, title, summary, ctx, }: BlockEditProps<CalloutData>): import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
/** Full client spec for the shared `callout` block (schema + MDX + Read/Edit). */
|
|
19
|
+
export declare const calloutBlock: import("../types.js").BlockSpec<CalloutData>;
|
|
20
|
+
//# sourceMappingURL=callout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"callout.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/callout.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAIL,KAAK,WAAW,EAEjB,MAAM,qBAAqB,CAAC;AAE7B;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,EAC3B,IAAI,EACJ,OAAO,EACP,KAAK,EACL,GAAG,GACJ,EAAE,cAAc,CAAC,WAAW,CAAC,2CAa7B;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,IAAI,EACJ,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,KAAK,EACL,OAAO,EACP,GAAG,GACJ,EAAE,cAAc,CAAC,WAAW,CAAC,2CA6E7B;AAED,kFAAkF;AAClF,eAAO,MAAM,YAAY,8CAcvB,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { IconPencil } from "@tabler/icons-react";
|
|
3
|
+
import { cn } from "../../utils.js";
|
|
4
|
+
import { defineBlock } from "../types.js";
|
|
5
|
+
import { CALLOUT_TONES, calloutMdx, calloutSchema, } from "./callout.config.js";
|
|
6
|
+
/**
|
|
7
|
+
* Standard `callout` block — an emphasized note with a tone (info / decision /
|
|
8
|
+
* risk / warning / success) and a markdown body. Lives in core so any app can
|
|
9
|
+
* register it (it originated in the plan template).
|
|
10
|
+
*
|
|
11
|
+
* The section carries BOTH the app-neutral `an-callout` classes (styled by
|
|
12
|
+
* core's `blocks.css` with shadcn theme tokens, so it looks right in any app)
|
|
13
|
+
* and the legacy `plan-callout` classes (styled by the plan template's own
|
|
14
|
+
* stylesheet). Plan therefore renders byte-identically to before; content (and
|
|
15
|
+
* any other app) gets the theme-token treatment. `data-tone` drives the accent
|
|
16
|
+
* in both. The body renders through `ctx.renderMarkdown` so each app supplies
|
|
17
|
+
* its own GFM renderer (plan's react-markdown reader, content's, etc.).
|
|
18
|
+
*/
|
|
19
|
+
export function CalloutBlock({ data, blockId, title, ctx, }) {
|
|
20
|
+
return (_jsxs("section", { className: "an-block an-callout plan-block plan-callout", "data-block-id": blockId, "data-tone": data.tone, children: [title && _jsx("div", { className: "an-block-label plan-block-label", children: title }), ctx.renderMarkdown?.(data.body) ?? (_jsx("div", { className: "an-callout-body whitespace-pre-wrap", children: data.body }))] }));
|
|
21
|
+
}
|
|
22
|
+
export function CalloutBlockEdit({ data, onChange, editable, blockId, title, summary, ctx, }) {
|
|
23
|
+
const activeTone = data.tone ?? "info";
|
|
24
|
+
const setTone = (tone) => onChange({ ...data, tone });
|
|
25
|
+
const toneSettings = editable
|
|
26
|
+
? ctx.renderEditSurface?.({
|
|
27
|
+
title: "Callout",
|
|
28
|
+
blockId,
|
|
29
|
+
blockType: "callout",
|
|
30
|
+
blockTitle: title,
|
|
31
|
+
blockSummary: summary,
|
|
32
|
+
blockData: data,
|
|
33
|
+
trigger: (_jsx("button", { type: "button", "data-plan-interactive": true, "aria-label": "Edit callout type", className: "an-block-edit-trigger flex size-7 items-center justify-center rounded-md text-muted-foreground opacity-0 transition-[color,opacity] hover:text-foreground focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring group-hover/callout:opacity-100", children: _jsx(IconPencil, { className: "size-4" }) })),
|
|
34
|
+
children: (_jsxs("div", { className: "grid gap-2", children: [_jsx("div", { className: "text-xs font-medium text-muted-foreground", children: "Type" }), _jsx("div", { className: "flex max-w-full flex-wrap items-center gap-1", children: CALLOUT_TONES.map((tone) => (_jsx("button", { type: "button", className: cn("rounded-md border border-transparent px-2 py-1 text-xs font-semibold capitalize transition-colors", activeTone === tone
|
|
35
|
+
? "border-border bg-muted text-foreground"
|
|
36
|
+
: "text-muted-foreground hover:bg-muted hover:text-foreground"), "aria-pressed": activeTone === tone, onClick: () => setTone(tone), children: tone }, tone))) })] })),
|
|
37
|
+
})
|
|
38
|
+
: null;
|
|
39
|
+
return (_jsxs("section", { className: "an-block an-callout plan-block plan-callout group/callout relative pr-10", "data-block-id": blockId, "data-tone": data.tone, children: [title && _jsx("div", { className: "an-block-label plan-block-label", children: title }), toneSettings && (_jsx("div", { className: "absolute right-2 top-2 z-10", children: toneSettings })), ctx.renderMarkdownEditor?.({
|
|
40
|
+
value: data.body,
|
|
41
|
+
onChange: (body) => onChange({ ...data, body }),
|
|
42
|
+
editable,
|
|
43
|
+
blockId,
|
|
44
|
+
}) ?? (_jsx("textarea", { "data-plan-interactive": true, className: "min-h-[120px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm leading-6 text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", value: data.body, disabled: !editable, onChange: (event) => onChange({ ...data, body: event.currentTarget.value }) }))] }));
|
|
45
|
+
}
|
|
46
|
+
/** Full client spec for the shared `callout` block (schema + MDX + Read/Edit). */
|
|
47
|
+
export const calloutBlock = defineBlock({
|
|
48
|
+
type: "callout",
|
|
49
|
+
schema: calloutSchema,
|
|
50
|
+
mdx: calloutMdx,
|
|
51
|
+
Read: CalloutBlock,
|
|
52
|
+
Edit: CalloutBlockEdit,
|
|
53
|
+
placement: ["block"],
|
|
54
|
+
editSurface: "inline",
|
|
55
|
+
label: "Callout",
|
|
56
|
+
description: "An emphasized note with a tone (info/decision/risk/warning/success) and a markdown body.",
|
|
57
|
+
// `body` is a `markdown(min(1))` field, so a fresh callout needs non-empty
|
|
58
|
+
// placeholder prose; `tone` defaults to the neutral "info" register.
|
|
59
|
+
empty: () => ({ tone: "info", body: "Callout text" }),
|
|
60
|
+
});
|
|
61
|
+
//# sourceMappingURL=callout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"callout.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/callout.tsx"],"names":[],"mappings":"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","sourcesContent":["import { IconPencil } from \"@tabler/icons-react\";\nimport { cn } from \"../../utils.js\";\nimport { defineBlock } from \"../types.js\";\nimport type { BlockReadProps, BlockEditProps } from \"../types.js\";\nimport {\n CALLOUT_TONES,\n calloutMdx,\n calloutSchema,\n type CalloutData,\n type CalloutTone,\n} from \"./callout.config.js\";\n\n/**\n * Standard `callout` block — an emphasized note with a tone (info / decision /\n * risk / warning / success) and a markdown body. Lives in core so any app can\n * register it (it originated in the plan template).\n *\n * The section carries BOTH the app-neutral `an-callout` classes (styled by\n * core's `blocks.css` with shadcn theme tokens, so it looks right in any app)\n * and the legacy `plan-callout` classes (styled by the plan template's own\n * stylesheet). Plan therefore renders byte-identically to before; content (and\n * any other app) gets the theme-token treatment. `data-tone` drives the accent\n * in both. The body renders through `ctx.renderMarkdown` so each app supplies\n * its own GFM renderer (plan's react-markdown reader, content's, etc.).\n */\nexport function CalloutBlock({\n data,\n blockId,\n title,\n ctx,\n}: BlockReadProps<CalloutData>) {\n return (\n <section\n className=\"an-block an-callout plan-block plan-callout\"\n data-block-id={blockId}\n data-tone={data.tone}\n >\n {title && <div className=\"an-block-label plan-block-label\">{title}</div>}\n {ctx.renderMarkdown?.(data.body) ?? (\n <div className=\"an-callout-body whitespace-pre-wrap\">{data.body}</div>\n )}\n </section>\n );\n}\n\nexport function CalloutBlockEdit({\n data,\n onChange,\n editable,\n blockId,\n title,\n summary,\n ctx,\n}: BlockEditProps<CalloutData>) {\n const activeTone = data.tone ?? \"info\";\n const setTone = (tone: CalloutTone) => onChange({ ...data, tone });\n const toneSettings = editable\n ? ctx.renderEditSurface?.({\n title: \"Callout\",\n blockId,\n blockType: \"callout\",\n blockTitle: title,\n blockSummary: summary,\n blockData: data,\n trigger: (\n <button\n type=\"button\"\n data-plan-interactive\n aria-label=\"Edit callout type\"\n className=\"an-block-edit-trigger flex size-7 items-center justify-center rounded-md text-muted-foreground opacity-0 transition-[color,opacity] hover:text-foreground focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring group-hover/callout:opacity-100\"\n >\n <IconPencil className=\"size-4\" />\n </button>\n ),\n children: (\n <div className=\"grid gap-2\">\n <div className=\"text-xs font-medium text-muted-foreground\">\n Type\n </div>\n <div className=\"flex max-w-full flex-wrap items-center gap-1\">\n {CALLOUT_TONES.map((tone) => (\n <button\n key={tone}\n type=\"button\"\n className={cn(\n \"rounded-md border border-transparent px-2 py-1 text-xs font-semibold capitalize transition-colors\",\n activeTone === tone\n ? \"border-border bg-muted text-foreground\"\n : \"text-muted-foreground hover:bg-muted hover:text-foreground\",\n )}\n aria-pressed={activeTone === tone}\n onClick={() => setTone(tone)}\n >\n {tone}\n </button>\n ))}\n </div>\n </div>\n ),\n })\n : null;\n\n return (\n <section\n className=\"an-block an-callout plan-block plan-callout group/callout relative pr-10\"\n data-block-id={blockId}\n data-tone={data.tone}\n >\n {title && <div className=\"an-block-label plan-block-label\">{title}</div>}\n {toneSettings && (\n <div className=\"absolute right-2 top-2 z-10\">{toneSettings}</div>\n )}\n {ctx.renderMarkdownEditor?.({\n value: data.body,\n onChange: (body) => onChange({ ...data, body }),\n editable,\n blockId,\n }) ?? (\n <textarea\n data-plan-interactive\n className=\"min-h-[120px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm leading-6 text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring\"\n value={data.body}\n disabled={!editable}\n onChange={(event) =>\n onChange({ ...data, body: event.currentTarget.value })\n }\n />\n )}\n </section>\n );\n}\n\n/** Full client spec for the shared `callout` block (schema + MDX + Read/Edit). */\nexport const calloutBlock = defineBlock<CalloutData>({\n type: \"callout\",\n schema: calloutSchema,\n mdx: calloutMdx,\n Read: CalloutBlock,\n Edit: CalloutBlockEdit,\n placement: [\"block\"],\n editSurface: \"inline\",\n label: \"Callout\",\n description:\n \"An emphasized note with a tone (info/decision/risk/warning/success) and a markdown body.\",\n // `body` is a `markdown(min(1))` field, so a fresh callout needs non-empty\n // placeholder prose; `tone` defaults to the neutral \"info\" register.\n empty: () => ({ tone: \"info\", body: \"Callout text\" }),\n});\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checklist.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/checklist.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAGL,KAAK,aAAa,EAEnB,MAAM,uBAAuB,CAAC;AAsB/B;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAC7B,IAAI,EACJ,OAAO,EACP,KAAK,EACL,QAAQ,GACT,EAAE,cAAc,CAAC,aAAa,CAAC,GAAG;IACjC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC,2CA8BA;
|
|
1
|
+
{"version":3,"file":"checklist.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/checklist.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAGL,KAAK,aAAa,EAEnB,MAAM,uBAAuB,CAAC;AAsB/B;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAC7B,IAAI,EACJ,OAAO,EACP,KAAK,EACL,QAAQ,GACT,EAAE,cAAc,CAAC,aAAa,CAAC,GAAG;IACjC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC,2CA8BA;AA4BD,6DAA6D;AAC7D,wBAAgB,eAAe,CAAC,EAC9B,IAAI,EACJ,QAAQ,EACR,QAAQ,GACT,EAAE,cAAc,CAAC,aAAa,CAAC,2CAuE/B;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,cAAc,gDAezB,CAAC"}
|
|
@@ -27,15 +27,15 @@ function newItemId() {
|
|
|
27
27
|
* the markers render statically.
|
|
28
28
|
*/
|
|
29
29
|
export function ChecklistBlock({ data, blockId, title, onToggle, }) {
|
|
30
|
-
return (_jsxs("section", { className: "plan-block", "data-block-id": blockId, children: [title && _jsx("div", { className: "plan-block-label", children: title }), _jsx("div", { className: "grid gap-
|
|
30
|
+
return (_jsxs("section", { className: "plan-block", "data-block-id": blockId, children: [title && _jsx("div", { className: "plan-block-label", children: title }), _jsx("div", { className: "grid gap-2", children: data.items.map((item) => onToggle ? (_jsxs("button", { type: "button", "data-plan-interactive": true, className: "flex w-full items-start gap-3 text-left text-plan-muted", onClick: () => onToggle(item.id), children: [_jsx(ChecklistMarker, { checked: item.checked }), _jsx(ChecklistItemBody, { item: item })] }, item.id)) : (_jsxs("div", { className: "flex w-full items-start gap-3 text-left text-plan-muted", children: [_jsx(ChecklistMarker, { checked: item.checked }), _jsx(ChecklistItemBody, { item: item })] }, item.id))) })] }));
|
|
31
31
|
}
|
|
32
32
|
function ChecklistMarker({ checked }) {
|
|
33
|
-
return (_jsx("span", { className: cn("mt-1 flex size-5 items-center justify-center rounded border", checked
|
|
33
|
+
return (_jsx("span", { className: cn("mt-1 flex size-5 shrink-0 items-center justify-center rounded border", checked
|
|
34
34
|
? "border-primary bg-primary text-primary-foreground"
|
|
35
35
|
: "border-plan-line"), children: checked && _jsx(IconCheck, { className: "size-3.5" }) }));
|
|
36
36
|
}
|
|
37
37
|
function ChecklistItemBody({ item }) {
|
|
38
|
-
return (_jsxs("span", { children: [_jsx("span", { className: "block text-plan-text", children: item.label }), item.note && _jsx("span", { className: "block text-sm", children: item.note })] }));
|
|
38
|
+
return (_jsxs("span", { className: "min-w-0 flex-1", children: [_jsx("span", { className: "block break-words text-plan-text", children: item.label }), item.note && (_jsx("span", { className: "block break-words text-sm", children: item.note }))] }));
|
|
39
39
|
}
|
|
40
40
|
/** Custom editor: toggle, relabel, add, and remove items. */
|
|
41
41
|
export function ChecklistEditor({ data, onChange, editable, }) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checklist.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/checklist.tsx"],"names":[],"mappings":"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","sourcesContent":["import { IconCheck, IconPlus, IconX } from \"@tabler/icons-react\";\nimport { cn } from \"../../utils.js\";\nimport { defineBlock } from \"../types.js\";\nimport type { BlockReadProps, BlockEditProps } from \"../types.js\";\nimport {\n checklistSchema,\n checklistMdx,\n type ChecklistData,\n type ChecklistItem,\n} from \"./checklist.config.js\";\n\n/**\n * Standard `checklist` block. A list of toggleable items, each with a label and\n * an optional note. Lives in core so any app can register it.\n *\n * `Read` mirrors the legacy plan `PlanBlockView` checklist branch byte-for-byte\n * (same `plan-block` section, toggle buttons, `IconCheck` marker, and the\n * existing toggle-via-`onChange` behavior) so converting the block to the\n * registry does not change rendered output. The plan CSS classes\n * (`plan-block`, `text-plan-*`, `border-plan-line`) resolve against the plan\n * app's stylesheet at render time, exactly as before.\n *\n * `Edit` is a custom editor (the schema auto-editor can't edit an array of\n * objects): it lets you add, remove, toggle, and relabel items inline.\n */\n\n/** Mint a reasonably-unique item id without pulling a dep into core. */\nfunction newItemId(): string {\n return `item-${Math.random().toString(36).slice(2, 10)}`;\n}\n\n/**\n * Read renderer. Note `onToggle` is supplied by the block dispatcher for the\n * historical click-to-toggle behavior; in pure read contexts it is omitted and\n * the markers render statically.\n */\nexport function ChecklistBlock({\n data,\n blockId,\n title,\n onToggle,\n}: BlockReadProps<ChecklistData> & {\n onToggle?: (itemId: string) => void;\n}) {\n return (\n <section className=\"plan-block\" data-block-id={blockId}>\n {title && <div className=\"plan-block-label\">{title}</div>}\n <div className=\"grid gap-3\">\n {data.items.map((item) =>\n onToggle ? (\n <button\n key={item.id}\n type=\"button\"\n data-plan-interactive\n className=\"flex items-start gap-3 text-left text-plan-muted\"\n onClick={() => onToggle(item.id)}\n >\n <ChecklistMarker checked={item.checked} />\n <ChecklistItemBody item={item} />\n </button>\n ) : (\n <div\n key={item.id}\n className=\"flex items-start gap-3 text-left text-plan-muted\"\n >\n <ChecklistMarker checked={item.checked} />\n <ChecklistItemBody item={item} />\n </div>\n ),\n )}\n </div>\n </section>\n );\n}\n\nfunction ChecklistMarker({ checked }: { checked?: boolean }) {\n return (\n <span\n className={cn(\n \"mt-1 flex size-5 items-center justify-center rounded border\",\n checked\n ? \"border-primary bg-primary text-primary-foreground\"\n : \"border-plan-line\",\n )}\n >\n {checked && <IconCheck className=\"size-3.5\" />}\n </span>\n );\n}\n\nfunction ChecklistItemBody({ item }: { item: ChecklistItem }) {\n return (\n <span>\n <span className=\"block text-plan-text\">{item.label}</span>\n {item.note && <span className=\"block text-sm\">{item.note}</span>}\n </span>\n );\n}\n\n/** Custom editor: toggle, relabel, add, and remove items. */\nexport function ChecklistEditor({\n data,\n onChange,\n editable,\n}: BlockEditProps<ChecklistData>) {\n const items = data.items;\n\n const update = (next: ChecklistItem[]) => onChange({ items: next });\n\n const toggle = (id: string) =>\n update(\n items.map((item) =>\n item.id === id ? { ...item, checked: !item.checked } : item,\n ),\n );\n\n const setLabel = (id: string, label: string) =>\n update(items.map((item) => (item.id === id ? { ...item, label } : item)));\n\n const remove = (id: string) => update(items.filter((item) => item.id !== id));\n\n const add = () =>\n update([...items, { id: newItemId(), label: \"\", checked: false }]);\n\n return (\n <div className=\"grid gap-2\">\n {items.map((item) => (\n <div key={item.id} className=\"group flex items-start gap-2\">\n <button\n type=\"button\"\n data-plan-interactive\n aria-label={item.checked ? \"Mark incomplete\" : \"Mark complete\"}\n className={cn(\n \"mt-1 flex size-5 shrink-0 items-center justify-center rounded border\",\n item.checked\n ? \"border-primary bg-primary text-primary-foreground\"\n : \"border-plan-line\",\n )}\n onClick={() => toggle(item.id)}\n >\n {item.checked && <IconCheck className=\"size-3.5\" />}\n </button>\n <input\n type=\"text\"\n data-plan-interactive\n className=\"flex h-9 w-full rounded-md bg-transparent px-3 py-1 text-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50\"\n placeholder=\"Checklist item\"\n value={item.label}\n disabled={!editable}\n onChange={(event) => setLabel(item.id, event.target.value)}\n />\n <button\n type=\"button\"\n data-plan-interactive\n aria-label=\"Remove item\"\n className=\"mt-1 flex size-7 shrink-0 items-center justify-center rounded text-muted-foreground opacity-0 transition-opacity hover:bg-muted hover:text-foreground focus-visible:opacity-100 group-hover:opacity-100 group-focus-within:opacity-100 disabled:opacity-50\"\n disabled={!editable}\n onClick={() => remove(item.id)}\n >\n <IconX className=\"size-4\" />\n </button>\n </div>\n ))}\n <button\n type=\"button\"\n data-plan-interactive\n className=\"flex items-center gap-1.5 self-start rounded-md px-2 py-1 text-sm text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-50\"\n disabled={!editable}\n onClick={add}\n >\n <IconPlus className=\"size-4\" />\n Add item\n </button>\n </div>\n );\n}\n\n/**\n * The standard checklist block spec (with React `Read`/`Edit`). Apps register\n * this in their browser registry. The schema + MDX config come from\n * `./checklist.config.ts`, the exact same object server / agent code registers,\n * so rendering and source round-trip never drift.\n *\n * `Read` is typed against `BlockReadProps<ChecklistData>`; the optional\n * `onToggle` the dispatcher injects for the legacy click-to-toggle behavior is\n * an extra prop the registry's `BlockView` passes through harmlessly when\n * present (it lives outside `BlockReadProps`, so it's wired by the app's block\n * dispatch rather than the generic `BlockView`).\n */\nexport const checklistBlock = defineBlock<ChecklistData>({\n type: \"checklist\",\n schema: checklistSchema,\n mdx: checklistMdx,\n Read: ChecklistBlock as never,\n Edit: ChecklistEditor,\n placement: [\"block\"],\n editSurface: \"inline\",\n // A checklist maps to NFM to-do items, so it round-trips to Notion.\n notionCompatible: true,\n label: \"Checklist\",\n icon: IconCheck,\n description:\n \"A list of toggleable items, each with a label and an optional note.\",\n empty: () => ({ items: [] }),\n});\n"]}
|
|
1
|
+
{"version":3,"file":"checklist.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/checklist.tsx"],"names":[],"mappings":"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","sourcesContent":["import { IconCheck, IconPlus, IconX } from \"@tabler/icons-react\";\nimport { cn } from \"../../utils.js\";\nimport { defineBlock } from \"../types.js\";\nimport type { BlockReadProps, BlockEditProps } from \"../types.js\";\nimport {\n checklistSchema,\n checklistMdx,\n type ChecklistData,\n type ChecklistItem,\n} from \"./checklist.config.js\";\n\n/**\n * Standard `checklist` block. A list of toggleable items, each with a label and\n * an optional note. Lives in core so any app can register it.\n *\n * `Read` mirrors the legacy plan `PlanBlockView` checklist branch byte-for-byte\n * (same `plan-block` section, toggle buttons, `IconCheck` marker, and the\n * existing toggle-via-`onChange` behavior) so converting the block to the\n * registry does not change rendered output. The plan CSS classes\n * (`plan-block`, `text-plan-*`, `border-plan-line`) resolve against the plan\n * app's stylesheet at render time, exactly as before.\n *\n * `Edit` is a custom editor (the schema auto-editor can't edit an array of\n * objects): it lets you add, remove, toggle, and relabel items inline.\n */\n\n/** Mint a reasonably-unique item id without pulling a dep into core. */\nfunction newItemId(): string {\n return `item-${Math.random().toString(36).slice(2, 10)}`;\n}\n\n/**\n * Read renderer. Note `onToggle` is supplied by the block dispatcher for the\n * historical click-to-toggle behavior; in pure read contexts it is omitted and\n * the markers render statically.\n */\nexport function ChecklistBlock({\n data,\n blockId,\n title,\n onToggle,\n}: BlockReadProps<ChecklistData> & {\n onToggle?: (itemId: string) => void;\n}) {\n return (\n <section className=\"plan-block\" data-block-id={blockId}>\n {title && <div className=\"plan-block-label\">{title}</div>}\n <div className=\"grid gap-2\">\n {data.items.map((item) =>\n onToggle ? (\n <button\n key={item.id}\n type=\"button\"\n data-plan-interactive\n className=\"flex w-full items-start gap-3 text-left text-plan-muted\"\n onClick={() => onToggle(item.id)}\n >\n <ChecklistMarker checked={item.checked} />\n <ChecklistItemBody item={item} />\n </button>\n ) : (\n <div\n key={item.id}\n className=\"flex w-full items-start gap-3 text-left text-plan-muted\"\n >\n <ChecklistMarker checked={item.checked} />\n <ChecklistItemBody item={item} />\n </div>\n ),\n )}\n </div>\n </section>\n );\n}\n\nfunction ChecklistMarker({ checked }: { checked?: boolean }) {\n return (\n <span\n className={cn(\n \"mt-1 flex size-5 shrink-0 items-center justify-center rounded border\",\n checked\n ? \"border-primary bg-primary text-primary-foreground\"\n : \"border-plan-line\",\n )}\n >\n {checked && <IconCheck className=\"size-3.5\" />}\n </span>\n );\n}\n\nfunction ChecklistItemBody({ item }: { item: ChecklistItem }) {\n return (\n <span className=\"min-w-0 flex-1\">\n <span className=\"block break-words text-plan-text\">{item.label}</span>\n {item.note && (\n <span className=\"block break-words text-sm\">{item.note}</span>\n )}\n </span>\n );\n}\n\n/** Custom editor: toggle, relabel, add, and remove items. */\nexport function ChecklistEditor({\n data,\n onChange,\n editable,\n}: BlockEditProps<ChecklistData>) {\n const items = data.items;\n\n const update = (next: ChecklistItem[]) => onChange({ items: next });\n\n const toggle = (id: string) =>\n update(\n items.map((item) =>\n item.id === id ? { ...item, checked: !item.checked } : item,\n ),\n );\n\n const setLabel = (id: string, label: string) =>\n update(items.map((item) => (item.id === id ? { ...item, label } : item)));\n\n const remove = (id: string) => update(items.filter((item) => item.id !== id));\n\n const add = () =>\n update([...items, { id: newItemId(), label: \"\", checked: false }]);\n\n return (\n <div className=\"grid gap-2\">\n {items.map((item) => (\n <div key={item.id} className=\"group flex items-start gap-2\">\n <button\n type=\"button\"\n data-plan-interactive\n aria-label={item.checked ? \"Mark incomplete\" : \"Mark complete\"}\n className={cn(\n \"mt-1 flex size-5 shrink-0 items-center justify-center rounded border\",\n item.checked\n ? \"border-primary bg-primary text-primary-foreground\"\n : \"border-plan-line\",\n )}\n onClick={() => toggle(item.id)}\n >\n {item.checked && <IconCheck className=\"size-3.5\" />}\n </button>\n <input\n type=\"text\"\n data-plan-interactive\n className=\"flex h-9 w-full rounded-md bg-transparent px-3 py-1 text-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50\"\n placeholder=\"Checklist item\"\n value={item.label}\n disabled={!editable}\n onChange={(event) => setLabel(item.id, event.target.value)}\n />\n <button\n type=\"button\"\n data-plan-interactive\n aria-label=\"Remove item\"\n className=\"mt-1 flex size-7 shrink-0 items-center justify-center rounded text-muted-foreground opacity-0 transition-opacity hover:bg-muted hover:text-foreground focus-visible:opacity-100 group-hover:opacity-100 group-focus-within:opacity-100 disabled:opacity-50\"\n disabled={!editable}\n onClick={() => remove(item.id)}\n >\n <IconX className=\"size-4\" />\n </button>\n </div>\n ))}\n <button\n type=\"button\"\n data-plan-interactive\n className=\"flex items-center gap-1.5 self-start rounded-md px-2 py-1 text-sm text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-50\"\n disabled={!editable}\n onClick={add}\n >\n <IconPlus className=\"size-4\" />\n Add item\n </button>\n </div>\n );\n}\n\n/**\n * The standard checklist block spec (with React `Read`/`Edit`). Apps register\n * this in their browser registry. The schema + MDX config come from\n * `./checklist.config.ts`, the exact same object server / agent code registers,\n * so rendering and source round-trip never drift.\n *\n * `Read` is typed against `BlockReadProps<ChecklistData>`; the optional\n * `onToggle` the dispatcher injects for the legacy click-to-toggle behavior is\n * an extra prop the registry's `BlockView` passes through harmlessly when\n * present (it lives outside `BlockReadProps`, so it's wired by the app's block\n * dispatch rather than the generic `BlockView`).\n */\nexport const checklistBlock = defineBlock<ChecklistData>({\n type: \"checklist\",\n schema: checklistSchema,\n mdx: checklistMdx,\n Read: ChecklistBlock as never,\n Edit: ChecklistEditor,\n placement: [\"block\"],\n editSurface: \"inline\",\n // A checklist maps to NFM to-do items, so it round-trips to Notion.\n notionCompatible: true,\n label: \"Checklist\",\n icon: IconCheck,\n description:\n \"A list of toggleable items, each with a label and an optional note.\",\n empty: () => ({ items: [] }),\n});\n"]}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { BlockMdxConfig } from "../types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Pure (React-free) part of the shared `decision` block: its data schema and MDX
|
|
5
|
+
* round-trip config. Lives in core so BOTH apps' server/shared registries and
|
|
6
|
+
* the client spec (`decision.tsx`) consume one definition. Keeping it React-free
|
|
7
|
+
* means importing it into a server module never pulls React into the Nitro/SSR
|
|
8
|
+
* bundle.
|
|
9
|
+
*
|
|
10
|
+
* The MDX `tag` + `question`/`options` attribute shape MUST match the legacy
|
|
11
|
+
* `<Decision question options />` encoding so stored `.mdx` round-trips
|
|
12
|
+
* byte-compatibly (the block originated in the plan template before moving here).
|
|
13
|
+
*/
|
|
14
|
+
export interface DecisionOption {
|
|
15
|
+
id: string;
|
|
16
|
+
label: string;
|
|
17
|
+
detail?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Authored recommendation only. A reviewer's actual selection does NOT live
|
|
20
|
+
* here — responses belong in comments / events, never in the canonical
|
|
21
|
+
* document body.
|
|
22
|
+
*/
|
|
23
|
+
recommended?: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface DecisionData {
|
|
26
|
+
question: string;
|
|
27
|
+
options: DecisionOption[];
|
|
28
|
+
}
|
|
29
|
+
export declare const decisionSchema: z.ZodType<DecisionData>;
|
|
30
|
+
/**
|
|
31
|
+
* MDX config: `question` and `options` are both attributes — exactly the legacy
|
|
32
|
+
* `<Decision question options />` form. `toAttrs` writes them in their historical
|
|
33
|
+
* order; `fromAttrs` tolerates missing attributes with the same `?? "Decision"` /
|
|
34
|
+
* `?? []` defaults the plan template used.
|
|
35
|
+
*/
|
|
36
|
+
export declare const decisionMdx: BlockMdxConfig<DecisionData>;
|
|
37
|
+
//# sourceMappingURL=decision.config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decision.config.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/decision.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,cAAc,EAAE,CAAC;CAC3B;AAID,eAAO,MAAM,cAAc,EAaV,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;AAEzC;;;;;GAKG;AACH,eAAO,MAAM,WAAW,EAAE,cAAc,CAAC,YAAY,CAUpD,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const decisionIdSchema = z.string().trim().min(1).max(120);
|
|
3
|
+
export const decisionSchema = z.object({
|
|
4
|
+
question: z.string().trim().min(1).max(500),
|
|
5
|
+
options: z
|
|
6
|
+
.array(z.object({
|
|
7
|
+
id: decisionIdSchema,
|
|
8
|
+
label: z.string().trim().min(1).max(200),
|
|
9
|
+
detail: z.string().trim().max(800).optional(),
|
|
10
|
+
recommended: z.boolean().optional(),
|
|
11
|
+
}))
|
|
12
|
+
.min(1)
|
|
13
|
+
.max(20),
|
|
14
|
+
});
|
|
15
|
+
/**
|
|
16
|
+
* MDX config: `question` and `options` are both attributes — exactly the legacy
|
|
17
|
+
* `<Decision question options />` form. `toAttrs` writes them in their historical
|
|
18
|
+
* order; `fromAttrs` tolerates missing attributes with the same `?? "Decision"` /
|
|
19
|
+
* `?? []` defaults the plan template used.
|
|
20
|
+
*/
|
|
21
|
+
export const decisionMdx = {
|
|
22
|
+
tag: "Decision",
|
|
23
|
+
toAttrs: (data) => ({
|
|
24
|
+
question: data.question,
|
|
25
|
+
options: data.options,
|
|
26
|
+
}),
|
|
27
|
+
fromAttrs: (attrs) => ({
|
|
28
|
+
question: attrs.string("question") ?? "Decision",
|
|
29
|
+
options: attrs.array("options") ?? [],
|
|
30
|
+
}),
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=decision.config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decision.config.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/decision.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAgCxB,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAE3D,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC;IACrC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;IAC3C,OAAO,EAAE,CAAC;SACP,KAAK,CACJ,CAAC,CAAC,MAAM,CAAC;QACP,EAAE,EAAE,gBAAgB;QACpB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;QACxC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;QAC7C,WAAW,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;KACpC,CAAC,CACH;SACA,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,EAAE,CAAC;CACX,CAAuC,CAAC;AAEzC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,WAAW,GAAiC;IACvD,GAAG,EAAE,UAAU;IACf,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAClB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,OAAO,EAAE,IAAI,CAAC,OAAO;KACtB,CAAC;IACF,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACrB,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,UAAU;QAChD,OAAO,EAAE,KAAK,CAAC,KAAK,CAAiB,SAAS,CAAC,IAAI,EAAE;KACtD,CAAC;CACH,CAAC","sourcesContent":["import { z } from \"zod\";\nimport type { BlockMdxConfig } from \"../types.js\";\n\n/**\n * Pure (React-free) part of the shared `decision` block: its data schema and MDX\n * round-trip config. Lives in core so BOTH apps' server/shared registries and\n * the client spec (`decision.tsx`) consume one definition. Keeping it React-free\n * means importing it into a server module never pulls React into the Nitro/SSR\n * bundle.\n *\n * The MDX `tag` + `question`/`options` attribute shape MUST match the legacy\n * `<Decision question options />` encoding so stored `.mdx` round-trips\n * byte-compatibly (the block originated in the plan template before moving here).\n */\n\nexport interface DecisionOption {\n id: string;\n label: string;\n detail?: string;\n /**\n * Authored recommendation only. A reviewer's actual selection does NOT live\n * here — responses belong in comments / events, never in the canonical\n * document body.\n */\n recommended?: boolean;\n}\n\nexport interface DecisionData {\n question: string;\n options: DecisionOption[];\n}\n\nconst decisionIdSchema = z.string().trim().min(1).max(120);\n\nexport const decisionSchema = z.object({\n question: z.string().trim().min(1).max(500),\n options: z\n .array(\n z.object({\n id: decisionIdSchema,\n label: z.string().trim().min(1).max(200),\n detail: z.string().trim().max(800).optional(),\n recommended: z.boolean().optional(),\n }),\n )\n .min(1)\n .max(20),\n}) as unknown as z.ZodType<DecisionData>;\n\n/**\n * MDX config: `question` and `options` are both attributes — exactly the legacy\n * `<Decision question options />` form. `toAttrs` writes them in their historical\n * order; `fromAttrs` tolerates missing attributes with the same `?? \"Decision\"` /\n * `?? []` defaults the plan template used.\n */\nexport const decisionMdx: BlockMdxConfig<DecisionData> = {\n tag: \"Decision\",\n toAttrs: (data) => ({\n question: data.question,\n options: data.options,\n }),\n fromAttrs: (attrs) => ({\n question: attrs.string(\"question\") ?? \"Decision\",\n options: attrs.array<DecisionOption>(\"options\") ?? [],\n }),\n};\n"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { BlockReadProps, BlockEditProps } from "../types.js";
|
|
2
|
+
import { type DecisionData } from "./decision.config.js";
|
|
3
|
+
/**
|
|
4
|
+
* Standard `decision` block — a decision prompt with inline-editable option
|
|
5
|
+
* cards and one authored "recommended" choice. Lives in core so any app can
|
|
6
|
+
* register it (it originated in the plan template).
|
|
7
|
+
*
|
|
8
|
+
* The root `<section>` keeps the app-neutral `an-block` class (document-flow
|
|
9
|
+
* spacing hook) alongside the legacy `plan-block` class (styled by the plan
|
|
10
|
+
* template's own stylesheet), so plan renders as before and any other app gets
|
|
11
|
+
* theme-token styling. All inner color comes from shadcn theme tokens
|
|
12
|
+
* (`text-muted-foreground`, `text-foreground`, `bg-muted`, `bg-background`,
|
|
13
|
+
* `border-border`, `ring`), so it reads correctly in any template palette.
|
|
14
|
+
*/
|
|
15
|
+
export declare function DecisionBlock({ data, blockId, title, }: BlockReadProps<DecisionData>): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
export declare function DecisionBlockEdit({ data, onChange, editable, blockId, title, summary, ctx, }: BlockEditProps<DecisionData>): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
/** Full client spec for the shared `decision` block (schema + MDX + Read/Edit). */
|
|
18
|
+
export declare const decisionBlock: import("../types.js").BlockSpec<DecisionData>;
|
|
19
|
+
//# sourceMappingURL=decision.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decision.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/decision.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAGL,KAAK,YAAY,EAElB,MAAM,sBAAsB,CAAC;AAE9B;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,OAAO,EACP,KAAK,GACN,EAAE,cAAc,CAAC,YAAY,CAAC,2CAsC9B;AAaD,wBAAgB,iBAAiB,CAAC,EAChC,IAAI,EACJ,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,KAAK,EACL,OAAO,EACP,GAAG,GACJ,EAAE,cAAc,CAAC,YAAY,CAAC,2CAiI9B;AA+GD,mFAAmF;AACnF,eAAO,MAAM,aAAa,+CAgCxB,CAAC"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { IconCheck, IconPencil, IconPlus, IconTrash, } from "@tabler/icons-react";
|
|
4
|
+
import { cn } from "../../utils.js";
|
|
5
|
+
import { defineBlock } from "../types.js";
|
|
6
|
+
import { decisionMdx, decisionSchema, } from "./decision.config.js";
|
|
7
|
+
/**
|
|
8
|
+
* Standard `decision` block — a decision prompt with inline-editable option
|
|
9
|
+
* cards and one authored "recommended" choice. Lives in core so any app can
|
|
10
|
+
* register it (it originated in the plan template).
|
|
11
|
+
*
|
|
12
|
+
* The root `<section>` keeps the app-neutral `an-block` class (document-flow
|
|
13
|
+
* spacing hook) alongside the legacy `plan-block` class (styled by the plan
|
|
14
|
+
* template's own stylesheet), so plan renders as before and any other app gets
|
|
15
|
+
* theme-token styling. All inner color comes from shadcn theme tokens
|
|
16
|
+
* (`text-muted-foreground`, `text-foreground`, `bg-muted`, `bg-background`,
|
|
17
|
+
* `border-border`, `ring`), so it reads correctly in any template palette.
|
|
18
|
+
*/
|
|
19
|
+
export function DecisionBlock({ data, blockId, title, }) {
|
|
20
|
+
return (_jsxs("section", { className: "an-block plan-block", "data-block-id": blockId, children: [title && _jsx("div", { className: "an-block-label plan-block-label", children: title }), _jsx("p", { className: "mt-3 max-w-3xl text-lg leading-8 text-muted-foreground", children: data.question }), _jsx("div", { className: "mt-6 grid gap-3 md:grid-cols-2", children: data.options.map((option) => (_jsxs("article", { className: cn("rounded-xl border border-border bg-muted p-4", option.recommended
|
|
21
|
+
? "shadow-[inset_3px_0_0_hsl(var(--ring))]"
|
|
22
|
+
: "opacity-85"), children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsx("h3", { className: "text-lg font-semibold tracking-tight text-foreground", children: option.label }), option.recommended && (_jsx("span", { className: "rounded-full border border-border px-2 py-1 text-[11px] font-semibold uppercase tracking-[0.08em] text-muted-foreground", children: "Recommended" }))] }), option.detail && (_jsx("p", { className: "mt-3 text-sm leading-6 text-muted-foreground", children: option.detail }))] }, option.id))) })] }));
|
|
23
|
+
}
|
|
24
|
+
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:border-ring";
|
|
25
|
+
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:border-ring";
|
|
26
|
+
const inlineLabelClass = "text-[11px] font-semibold uppercase tracking-[0.08em] text-muted-foreground";
|
|
27
|
+
function newLocalId(prefix) {
|
|
28
|
+
return `${prefix}-${Math.random().toString(36).slice(2, 10)}`;
|
|
29
|
+
}
|
|
30
|
+
export function DecisionBlockEdit({ data, onChange, editable, blockId, title, summary, ctx, }) {
|
|
31
|
+
const updateOption = (optionId, patch) => onChange({
|
|
32
|
+
...data,
|
|
33
|
+
options: data.options.map((option) => option.id === optionId ? { ...option, ...patch } : option),
|
|
34
|
+
});
|
|
35
|
+
const removeOption = (optionId) => {
|
|
36
|
+
if (data.options.length <= 1)
|
|
37
|
+
return;
|
|
38
|
+
onChange({
|
|
39
|
+
...data,
|
|
40
|
+
options: data.options.filter((option) => option.id !== optionId),
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
const addOption = () => {
|
|
44
|
+
if (data.options.length >= 20)
|
|
45
|
+
return;
|
|
46
|
+
onChange({
|
|
47
|
+
...data,
|
|
48
|
+
options: [
|
|
49
|
+
...data.options,
|
|
50
|
+
{ id: newLocalId("option"), label: "New option" },
|
|
51
|
+
],
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
const settings = editable
|
|
55
|
+
? (ctx.renderEditSurface?.({
|
|
56
|
+
title: "Decision",
|
|
57
|
+
blockId,
|
|
58
|
+
blockType: "decision",
|
|
59
|
+
blockTitle: title,
|
|
60
|
+
blockSummary: summary,
|
|
61
|
+
blockData: data,
|
|
62
|
+
trigger: (_jsx("button", { type: "button", "data-plan-interactive": true, "aria-label": "Edit decision options", className: "flex size-9 shrink-0 items-center justify-center rounded-md border border-border bg-muted text-muted-foreground transition-colors hover:bg-accent/60 hover:text-foreground", children: _jsx(IconPencil, { className: "size-4" }) })),
|
|
63
|
+
children: (_jsx(DecisionSettings, { options: data.options, onToggleRecommended: (option) => updateOption(option.id, { recommended: !option.recommended }), onRemove: removeOption, onAdd: addOption })),
|
|
64
|
+
}) ?? (_jsx(DecisionInlineSettings, { options: data.options, onToggleRecommended: (option) => updateOption(option.id, { recommended: !option.recommended }), onRemove: removeOption, onAdd: addOption })))
|
|
65
|
+
: null;
|
|
66
|
+
return (_jsxs("div", { className: "grid gap-5", "data-plan-interactive": true, children: [_jsxs("div", { className: "flex items-start gap-3", children: [_jsxs("label", { className: "grid min-w-0 flex-1 gap-1.5", children: [_jsx("span", { className: inlineLabelClass, children: "Question" }), _jsx("textarea", { className: inlineTextareaClass, rows: 2, value: data.question, disabled: !editable, onChange: (event) => onChange({ ...data, question: event.target.value }) })] }), settings] }), _jsx("div", { className: "grid gap-3", children: data.options.map((option) => (_jsxs("article", { className: cn("rounded-lg border border-border bg-muted p-4", option.recommended &&
|
|
67
|
+
"border-ring/60 shadow-[inset_3px_0_0_hsl(var(--ring))]"), children: [_jsx("div", { className: "grid gap-3", 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(option.id, { label: event.target.value }) })] }) }), _jsxs("label", { className: "mt-3 grid gap-1.5", children: [_jsx("span", { className: inlineLabelClass, children: "Detail" }), _jsx("textarea", { className: inlineTextareaClass, rows: 2, value: option.detail ?? "", disabled: !editable, onChange: (event) => updateOption(option.id, {
|
|
68
|
+
detail: event.target.value || undefined,
|
|
69
|
+
}) })] })] }, option.id))) })] }));
|
|
70
|
+
}
|
|
71
|
+
/** Option-management controls rendered inside the host's edit surface popover. */
|
|
72
|
+
function DecisionSettings({ options, onToggleRecommended, onRemove, onAdd, }) {
|
|
73
|
+
return (_jsxs("div", { className: "grid gap-3", children: [_jsx("div", { className: "text-sm font-semibold text-foreground", children: "Decision settings" }), _jsx("div", { className: "grid gap-2", children: options.map((option, index) => (_jsxs("div", { className: "grid gap-2 rounded-md border border-border bg-muted/20 p-2", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx("span", { className: "min-w-0 truncate text-xs font-medium text-foreground", children: option.label.trim() || `Option ${index + 1}` }), option.recommended && (_jsx("span", { className: "shrink-0 rounded-full border border-border px-2 py-0.5 text-[10px] font-semibold uppercase tracking-[0.08em] text-muted-foreground", children: "Recommended" }))] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("button", { type: "button", "data-plan-interactive": true, onClick: () => onToggleRecommended(option), className: cn("inline-flex h-8 flex-1 items-center justify-center gap-1.5 rounded-md border border-border px-2.5 text-xs font-medium transition-colors", option.recommended
|
|
74
|
+
? "bg-background text-foreground shadow-sm"
|
|
75
|
+
: "text-muted-foreground hover:bg-background/70 hover:text-foreground"), children: [option.recommended && _jsx(IconCheck, { className: "size-3.5" }), option.recommended ? "Recommended" : "Mark recommended"] }), _jsx("button", { type: "button", "data-plan-interactive": true, "aria-label": `Delete ${option.label || `option ${index + 1}`}`, disabled: options.length <= 1, onClick: () => onRemove(option.id), className: "inline-flex size-8 shrink-0 items-center justify-center rounded-md border border-border text-destructive transition-colors hover:bg-destructive/10 disabled:cursor-not-allowed disabled:opacity-50", children: _jsx(IconTrash, { className: "size-3.5" }) })] })] }, option.id))) }), _jsxs("button", { type: "button", "data-plan-interactive": true, disabled: options.length >= 20, onClick: onAdd, className: "inline-flex h-8 items-center justify-center gap-1.5 rounded-md border border-border px-2.5 text-xs font-medium text-foreground transition-colors hover:bg-accent disabled:cursor-not-allowed disabled:opacity-50", children: [_jsx(IconPlus, { className: "size-3.5" }), "Add option"] })] }));
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Fallback for hosts that do not provide `ctx.renderEditSurface`: a plain
|
|
79
|
+
* disclosure button that reveals the same settings inline (no overlay primitive,
|
|
80
|
+
* so core stays shadcn-free).
|
|
81
|
+
*/
|
|
82
|
+
function DecisionInlineSettings(props) {
|
|
83
|
+
const [open, setOpen] = useState(false);
|
|
84
|
+
return (_jsxs("div", { className: "grid gap-2", children: [_jsx("button", { type: "button", "data-plan-interactive": true, "aria-label": "Edit decision options", "aria-expanded": open, onClick: () => setOpen((value) => !value), className: "flex size-9 shrink-0 items-center justify-center rounded-md border border-border bg-muted text-muted-foreground transition-colors hover:bg-accent/60 hover:text-foreground", children: _jsx(IconPencil, { className: "size-4" }) }), open && (_jsx("div", { className: "w-80 rounded-md border border-border bg-background p-3 shadow-sm", children: _jsx(DecisionSettings, { ...props }) }))] }));
|
|
85
|
+
}
|
|
86
|
+
/** Full client spec for the shared `decision` block (schema + MDX + Read/Edit). */
|
|
87
|
+
export const decisionBlock = defineBlock({
|
|
88
|
+
type: "decision",
|
|
89
|
+
schema: decisionSchema,
|
|
90
|
+
mdx: decisionMdx,
|
|
91
|
+
Read: DecisionBlock,
|
|
92
|
+
Edit: DecisionBlockEdit,
|
|
93
|
+
placement: ["block"],
|
|
94
|
+
// `panel`: the document shows the clean read view (question + option cards with
|
|
95
|
+
// the recommended pick highlighted), and the corner pencil opens the editor in
|
|
96
|
+
// a popover. NOT `inline` — an inline schema-editing form (question + per-option
|
|
97
|
+
// textareas) rendered straight into the doc reads as a confusing data-entry wall
|
|
98
|
+
// rather than a decision. Mirrors how `question-form` / `visual-questions` edit.
|
|
99
|
+
editSurface: "panel",
|
|
100
|
+
label: "Decision",
|
|
101
|
+
description: "A decision prompt with option cards and an authored recommended choice. Shows a clean read view in the document; edit the question and options from the corner pencil.",
|
|
102
|
+
empty: () => ({
|
|
103
|
+
question: "Which implementation direction should we take?",
|
|
104
|
+
options: [
|
|
105
|
+
{
|
|
106
|
+
id: "recommended",
|
|
107
|
+
label: "Recommended path",
|
|
108
|
+
detail: "Smallest useful slice with clear rollback.",
|
|
109
|
+
recommended: true,
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
id: "alternative",
|
|
113
|
+
label: "Alternative",
|
|
114
|
+
detail: "Broader pass that touches more surfaces.",
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
}),
|
|
118
|
+
});
|
|
119
|
+
//# sourceMappingURL=decision.js.map
|