@agent-native/core 0.44.3 → 0.45.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/dist/cli/connect.d.ts +2 -1
- package/dist/cli/connect.d.ts.map +1 -1
- package/dist/cli/connect.js +185 -5
- package/dist/cli/connect.js.map +1 -1
- package/dist/cli/index.js +27 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/plan-local.d.ts +43 -0
- package/dist/cli/plan-local.d.ts.map +1 -0
- package/dist/cli/plan-local.js +477 -0
- package/dist/cli/plan-local.js.map +1 -0
- package/dist/cli/pr-visual-recap-workflow.d.ts +1 -1
- package/dist/cli/pr-visual-recap-workflow.d.ts.map +1 -1
- package/dist/cli/pr-visual-recap-workflow.js +1 -1
- package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
- package/dist/cli/recap.d.ts +164 -0
- package/dist/cli/recap.d.ts.map +1 -1
- package/dist/cli/recap.js +657 -10
- package/dist/cli/recap.js.map +1 -1
- package/dist/cli/skills.d.ts +6 -2
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +440 -37
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/AnnotatedCodeBlock.js +18 -4
- package/dist/client/blocks/library/AnnotatedCodeBlock.js.map +1 -1
- package/dist/client/blocks/library/ApiEndpointBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/ApiEndpointBlock.js +11 -7
- package/dist/client/blocks/library/ApiEndpointBlock.js.map +1 -1
- package/dist/client/blocks/library/DiffBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/DiffBlock.js +90 -7
- package/dist/client/blocks/library/DiffBlock.js.map +1 -1
- package/dist/client/blocks/library/HighlightedCode.d.ts +2 -1
- package/dist/client/blocks/library/HighlightedCode.d.ts.map +1 -1
- package/dist/client/blocks/library/HighlightedCode.js +2 -2
- package/dist/client/blocks/library/HighlightedCode.js.map +1 -1
- package/dist/client/blocks/library/JsonExplorerBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/JsonExplorerBlock.js +57 -13
- package/dist/client/blocks/library/JsonExplorerBlock.js.map +1 -1
- package/dist/client/blocks/library/annotation-rail.d.ts +15 -5
- package/dist/client/blocks/library/annotation-rail.d.ts.map +1 -1
- package/dist/client/blocks/library/annotation-rail.js +35 -24
- package/dist/client/blocks/library/annotation-rail.js.map +1 -1
- package/dist/client/blocks/library/code-filename-label.d.ts +8 -0
- package/dist/client/blocks/library/code-filename-label.d.ts.map +1 -0
- package/dist/client/blocks/library/code-filename-label.js +15 -0
- package/dist/client/blocks/library/code-filename-label.js.map +1 -0
- package/dist/client/blocks/library/code.d.ts.map +1 -1
- package/dist/client/blocks/library/code.js +28 -6
- package/dist/client/blocks/library/code.js.map +1 -1
- package/dist/client/blocks/library/columns.d.ts.map +1 -1
- package/dist/client/blocks/library/columns.js +11 -1
- package/dist/client/blocks/library/columns.js.map +1 -1
- package/dist/client/blocks/library/diff.config.d.ts +1 -1
- package/dist/client/blocks/library/diff.config.js.map +1 -1
- package/dist/client/blocks/library/narrow-container.d.ts +13 -0
- package/dist/client/blocks/library/narrow-container.d.ts.map +1 -0
- package/dist/client/blocks/library/narrow-container.js +32 -0
- package/dist/client/blocks/library/narrow-container.js.map +1 -0
- package/dist/client/blocks/library/question-form.d.ts.map +1 -1
- package/dist/client/blocks/library/question-form.js +8 -9
- package/dist/client/blocks/library/question-form.js.map +1 -1
- package/dist/client/blocks/library/tabs.d.ts.map +1 -1
- package/dist/client/blocks/library/tabs.js +11 -5
- package/dist/client/blocks/library/tabs.js.map +1 -1
- package/dist/client/blocks/types.d.ts +2 -0
- package/dist/client/blocks/types.d.ts.map +1 -1
- package/dist/client/blocks/types.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +4 -1
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/db-admin/TableEditor.d.ts.map +1 -1
- package/dist/client/db-admin/TableEditor.js +3 -1
- package/dist/client/db-admin/TableEditor.js.map +1 -1
- package/dist/db/client.d.ts +19 -0
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +43 -2
- package/dist/db/client.js.map +1 -1
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/migrations.js +9 -2
- package/dist/db/migrations.js.map +1 -1
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +8 -0
- package/dist/deploy/build.js.map +1 -1
- package/dist/extensions/html-shell.js +1 -1
- package/dist/extensions/html-shell.js.map +1 -1
- package/dist/jobs/scheduler.d.ts.map +1 -1
- package/dist/jobs/scheduler.js +5 -1
- package/dist/jobs/scheduler.js.map +1 -1
- package/dist/mcp/build-server.d.ts +1 -0
- package/dist/mcp/build-server.d.ts.map +1 -1
- package/dist/mcp/build-server.js +7 -3
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/mcp/oauth-route.d.ts.map +1 -1
- package/dist/mcp/oauth-route.js +56 -19
- package/dist/mcp/oauth-route.js.map +1 -1
- package/dist/mcp/oauth-store.d.ts +1 -0
- package/dist/mcp/oauth-store.d.ts.map +1 -1
- package/dist/mcp/oauth-store.js +9 -0
- package/dist/mcp/oauth-store.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +9 -4
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp-client/errors.js +3 -3
- package/dist/mcp-client/errors.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +3 -1
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-teams.d.ts.map +1 -1
- package/dist/server/agent-teams.js +10 -2
- package/dist/server/agent-teams.js.map +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +7 -3
- package/dist/server/auth.js.map +1 -1
- package/dist/server/recap-image-route.d.ts.map +1 -1
- package/dist/server/recap-image-route.js +3 -6
- package/dist/server/recap-image-route.js.map +1 -1
- package/dist/server/sentry.d.ts.map +1 -1
- package/dist/server/sentry.js +12 -5
- package/dist/server/sentry.js.map +1 -1
- package/dist/server/social-og-image.d.ts.map +1 -1
- package/dist/server/social-og-image.js +3 -1
- package/dist/server/social-og-image.js.map +1 -1
- package/dist/styles/blocks.css +36 -10
- package/dist/templates/workspace-core/.agents/skills/external-agents/SKILL.md +22 -6
- package/docs/content/plan-plugin.md +18 -1
- package/docs/content/pr-visual-recap.md +37 -10
- package/docs/content/template-plan.md +45 -1
- package/package.json +1 -1
- package/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +22 -6
|
@@ -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 {\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\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 card ─────────────────────────────────────────────────────────────── */\n\n/**\n * One line-anchored note card: marker pip (when `showMarker`), the resolved line\n * span (\"Line 8\"), an optional label, and the markdown `note` (via\n * `ctx.renderMarkdown`). This is the single source of card markup, rendered both\n * by the visually-hidden a11y/test stack and by the on-hover portal popover.\n */\nexport function AnnotationCard<A extends RailAnnotation>({\n item,\n ctx,\n active = false,\n showMarker = false,\n className,\n onMouseEnter,\n onMouseLeave,\n}: {\n item: ResolvedAnnotation<A>;\n ctx: BlockRenderContext;\n active?: boolean;\n showMarker?: boolean;\n className?: string;\n onMouseEnter?: () => void;\n onMouseLeave?: () => void;\n}) {\n return (\n <div\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n className={cn(\n \"rounded-lg border px-3.5 py-2.5 transition-colors\",\n active\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 className,\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 marker={item.marker} active={active} />\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\n/**\n * Visually-hidden stack of every resolved note. It is NOT a visible column — it\n * sits in the flow but clipped to a 1px box (the `sr-only` pattern) so the note\n * text and the per-card marker pips stay in the accessibility tree and in the\n * DOM (assistive tech can reach them, and tests that read `textContent`/count\n * pips still see them) WITHOUT painting a persistent rail beside the code. The\n * visible card appears only on hover via {@link AnnotationHoverCard}.\n */\nexport function AnnotationHiddenStack<A extends RailAnnotation>({\n items,\n ctx,\n showMarker = false,\n}: {\n items: ResolvedAnnotation<A>[];\n ctx: BlockRenderContext;\n showMarker?: boolean;\n}) {\n const resolved = useMemo(() => items.filter((item) => item.range), [items]);\n if (resolved.length === 0) return null;\n return (\n <div\n className=\"absolute size-px overflow-hidden whitespace-nowrap border-0 p-0 [clip:rect(0,0,0,0)] [clip-path:inset(50%)]\"\n data-annotation-hidden-stack\n >\n {resolved.map((item) => (\n <AnnotationCard\n key={item.index}\n item={item}\n ctx={ctx}\n showMarker={showMarker}\n />\n ))}\n </div>\n );\n}\n\n/* ── Hover popover (portal, anchored RIGHT of the code) ────────────────────── */\n\n/** The geometry the hover card anchors to (in viewport coordinates). */\nexport interface AnnotationAnchor {\n /** Right edge of the code block (where the card should start). */\n codeRight: number;\n /** Left edge of the code block (for the below-fallback alignment). */\n codeLeft: number;\n /** Vertical center of the hovered line. */\n lineCenter: number;\n /** Bottom of the hovered line (for the below-fallback placement). */\n lineBottom: number;\n}\n\nconst HOVER_CARD_WIDTH = 280;\nconst HOVER_CARD_GAP = 12;\nconst VIEWPORT_MARGIN = 8;\n\n/**\n * The single on-hover note card, portaled to `document.body` and positioned\n * `fixed` so it escapes the code block's `overflow` and never reflows the code.\n *\n * Placement: by default it sits to the RIGHT of the code block's right edge,\n * vertically centered on the hovered line — so it never overlaps the code text.\n * If there isn't room to the right (it would overflow the viewport), it clamps\n * within the viewport, and if the right gutter is too narrow for the card it\n * falls back to BELOW the hovered line (left-aligned to the code block). The card\n * keeps itself open while hovered (`onMouseEnter`/`onMouseLeave` forwarded) so it\n * stays readable; the caller adds the small hover-intent close delay.\n */\nexport function AnnotationHoverCard<A extends RailAnnotation>({\n item,\n anchor,\n ctx,\n showMarker = false,\n onMouseEnter,\n onMouseLeave,\n}: {\n item: ResolvedAnnotation<A>;\n anchor: AnnotationAnchor;\n ctx: BlockRenderContext;\n showMarker?: boolean;\n onMouseEnter?: () => void;\n onMouseLeave?: () => void;\n}) {\n const cardRef = useRef<HTMLDivElement | null>(null);\n const [pos, setPos] = useState<{ top: number; left: number } | null>(null);\n\n // Measure the rendered card, then resolve a non-overlapping position: right of\n // the code if it fits, else clamp into the viewport, else drop below the line.\n useLayoutEffect(() => {\n if (typeof window === \"undefined\") return;\n const el = cardRef.current;\n const rect = el?.getBoundingClientRect();\n const width = rect && rect.width > 0 ? rect.width : HOVER_CARD_WIDTH;\n const height = rect && rect.height > 0 ? rect.height : 0;\n const vw = window.innerWidth || 0;\n const vh = window.innerHeight || 0;\n\n const rightLeft = anchor.codeRight + HOVER_CARD_GAP;\n const fitsRight = rightLeft + width + VIEWPORT_MARGIN <= vw;\n\n let left: number;\n let top: number;\n if (fitsRight) {\n // Default: to the right of the code, centered on the hovered line.\n left = rightLeft;\n top = anchor.lineCenter - height / 2;\n } else {\n // No room to the right → drop below the line, aligned to the code's left.\n left = anchor.codeLeft;\n top = anchor.lineBottom + HOVER_CARD_GAP;\n }\n // Clamp within the viewport so the card is never cut off.\n left = Math.max(\n VIEWPORT_MARGIN,\n Math.min(left, vw - width - VIEWPORT_MARGIN),\n );\n top = Math.max(\n VIEWPORT_MARGIN,\n Math.min(top, vh - height - VIEWPORT_MARGIN),\n );\n setPos({ top, left });\n }, [\n anchor.codeRight,\n anchor.codeLeft,\n anchor.lineCenter,\n anchor.lineBottom,\n item.index,\n ]);\n\n if (typeof document === \"undefined\") return null;\n\n return createPortal(\n <div\n ref={cardRef}\n role=\"tooltip\"\n data-annotation-hover-card\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n className=\"pointer-events-auto fixed z-50\"\n style={{\n top: pos?.top ?? anchor.lineCenter,\n left: pos?.left ?? anchor.codeRight + HOVER_CARD_GAP,\n width: HOVER_CARD_WIDTH,\n // Hide until measured to avoid a one-frame jump from the fallback spot.\n visibility: pos ? \"visible\" : \"hidden\",\n }}\n >\n <AnnotationCard\n item={item}\n ctx={ctx}\n active\n showMarker={showMarker}\n className=\"shadow-lg shadow-black/10 dark:shadow-black/40\"\n />\n </div>,\n document.body,\n );\n}\n\n/**\n * Hover-intent controller for the on-hover note card. Exposes `activeIndex` +\n * the captured `anchor`, plus `open`/`scheduleClose`/`cancelClose` handlers.\n *\n * - `open(index, anchor)` shows a card immediately (cancels any pending close).\n * - `scheduleClose()` hides after a short delay, so moving the pointer from the\n * code line across the gap into the card itself keeps it open.\n * - `cancelClose()` (call on card mouse-enter) keeps it open while reading.\n */\nexport function useAnnotationHover(delay = 130) {\n const [active, setActive] = useState<{\n index: number;\n anchor: AnnotationAnchor;\n } | null>(null);\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const cancelClose = () => {\n if (timer.current) {\n clearTimeout(timer.current);\n timer.current = null;\n }\n };\n const open = (index: number, anchor: AnnotationAnchor) => {\n cancelClose();\n setActive({ index, anchor });\n };\n const scheduleClose = () => {\n cancelClose();\n timer.current = setTimeout(() => setActive(null), delay);\n };\n\n useEffect(() => () => cancelClose(), []);\n\n return {\n activeIndex: active?.index ?? null,\n anchor: active?.anchor ?? null,\n open,\n scheduleClose,\n cancelClose,\n } as const;\n}\n\n/**\n * Build an {@link AnnotationAnchor} from the code block element and the hovered\n * row element. The card anchors to the code block's RIGHT edge and the row's\n * vertical center, both in viewport coordinates (so a `fixed` portal lines up).\n */\nexport function anchorFromElements(\n codeEl: HTMLElement | null,\n rowEl: HTMLElement | null,\n): AnnotationAnchor | null {\n if (!codeEl || !rowEl) return null;\n const code = codeEl.getBoundingClientRect();\n const row = rowEl.getBoundingClientRect();\n return {\n codeRight: code.right,\n codeLeft: code.left,\n lineCenter: row.top + row.height / 2,\n lineBottom: row.bottom,\n };\n}\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 *\n * @deprecated Superseded by the on-hover {@link AnnotationHoverCard}; kept for\n * back-compat with any external importer. Both block read renderers now use the\n * hover popover anchored to the right of the code instead of a persistent rail.\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 <AnnotationCard\n key={item.index}\n item={item}\n ctx={ctx}\n active={activeIndex === item.index}\n showMarker={showMarker}\n onMouseEnter={() => onActiveChange(item.index)}\n onMouseLeave={() => onActiveChange(null)}\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 {\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\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 card ─────────────────────────────────────────────────────────────── */\n\n/**\n * One line-anchored note card: marker pip (when `showMarker`), the resolved line\n * span (\"Line 8\"), an optional label, and the markdown `note` (via\n * `ctx.renderMarkdown`). This is the single source of card markup, rendered both\n * by the visually-hidden a11y/test stack and by the on-hover portal popover.\n */\nexport function AnnotationCard<A extends RailAnnotation>({\n item,\n ctx,\n active = false,\n showMarker = false,\n className,\n onMouseEnter,\n onMouseLeave,\n}: {\n item: ResolvedAnnotation<A>;\n ctx: BlockRenderContext;\n active?: boolean;\n showMarker?: boolean;\n className?: string;\n onMouseEnter?: () => void;\n onMouseLeave?: () => void;\n}) {\n return (\n <div\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n className={cn(\n \"rounded-lg border px-3.5 py-2.5 transition-colors\",\n active\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 className,\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 marker={item.marker} active={active} />\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\n/**\n * Visually-hidden stack of every resolved note. It is NOT a visible column — it\n * sits in the flow but clipped to a 1px box (the `sr-only` pattern) so the note\n * text and the per-card marker pips stay in the accessibility tree and in the\n * DOM (assistive tech can reach them, and tests that read `textContent`/count\n * pips still see them) WITHOUT painting a persistent rail beside the code. The\n * visible card appears only on hover via {@link AnnotationHoverCard}.\n */\nexport function AnnotationHiddenStack<A extends RailAnnotation>({\n items,\n ctx,\n showMarker = false,\n}: {\n items: ResolvedAnnotation<A>[];\n ctx: BlockRenderContext;\n showMarker?: boolean;\n}) {\n const resolved = useMemo(() => items.filter((item) => item.range), [items]);\n if (resolved.length === 0) return null;\n return (\n <div\n className=\"absolute size-px overflow-hidden whitespace-nowrap border-0 p-0 [clip:rect(0,0,0,0)] [clip-path:inset(50%)]\"\n data-annotation-hidden-stack\n >\n {resolved.map((item) => (\n <AnnotationCard\n key={item.index}\n item={item}\n ctx={ctx}\n showMarker={showMarker}\n />\n ))}\n </div>\n );\n}\n\n/* ── Hover popover (portal, anchored beside the code) ──────────────────────── */\n\n/** The geometry the hover card anchors to (in viewport coordinates). */\nexport interface AnnotationAnchor {\n /** Right edge of the code block (where the card should start). */\n codeRight: number;\n /** Left edge of the code block (for the below-fallback alignment). */\n codeLeft: number;\n /** Vertical center of the hovered line. */\n lineCenter: number;\n /** Bottom of the hovered line (for the below-fallback placement). */\n lineBottom: number;\n}\n\nconst HOVER_CARD_WIDTH = 280;\nconst HOVER_CARD_GAP = 12;\nconst VIEWPORT_MARGIN = 8;\n\nexport function resolveAnnotationHoverCardPosition(\n anchor: AnnotationAnchor,\n card: { width: number; height: number },\n viewport: { width: number; height: number },\n): { top: number; left: number } {\n const rightLeft = anchor.codeRight + HOVER_CARD_GAP;\n const fitsRight = rightLeft + card.width + VIEWPORT_MARGIN <= viewport.width;\n const leftLeft = anchor.codeLeft - HOVER_CARD_GAP - card.width;\n const fitsLeft = leftLeft >= VIEWPORT_MARGIN;\n\n let left: number;\n let top: number;\n if (fitsRight) {\n // Default: to the right of the code, centered on the hovered line.\n left = rightLeft;\n top = anchor.lineCenter - card.height / 2;\n } else if (fitsLeft) {\n // Prefer the left gutter over covering the code below the hovered line.\n left = leftLeft;\n top = anchor.lineCenter - card.height / 2;\n } else {\n // No clean side gutter → drop below the line, aligned to the code's left.\n left = anchor.codeLeft;\n top = anchor.lineBottom + HOVER_CARD_GAP;\n }\n\n // Clamp within the viewport so the card is never cut off.\n left = Math.max(\n VIEWPORT_MARGIN,\n Math.min(left, viewport.width - card.width - VIEWPORT_MARGIN),\n );\n top = Math.max(\n VIEWPORT_MARGIN,\n Math.min(top, viewport.height - card.height - VIEWPORT_MARGIN),\n );\n\n return { top, left };\n}\n\n/**\n * The single on-hover note card, portaled to `document.body` and positioned\n * `fixed` so it escapes the code block's `overflow` and never reflows the code.\n *\n * Placement: by default it sits to the RIGHT of the code block's right edge,\n * vertically centered on the hovered line — so it never overlaps the code text.\n * If there isn't room to the right, it uses the LEFT of the code block when the\n * card can fit there without covering code. Only when neither side fits does it\n * fall back to BELOW the hovered line (left-aligned to the code block). The card\n * keeps itself open while hovered (`onMouseEnter`/`onMouseLeave` forwarded) so\n * it stays readable; the caller adds the small hover-intent close delay.\n */\nexport function AnnotationHoverCard<A extends RailAnnotation>({\n item,\n anchor,\n ctx,\n showMarker = false,\n onMouseEnter,\n onMouseLeave,\n}: {\n item: ResolvedAnnotation<A>;\n anchor: AnnotationAnchor;\n ctx: BlockRenderContext;\n showMarker?: boolean;\n onMouseEnter?: () => void;\n onMouseLeave?: () => void;\n}) {\n const cardRef = useRef<HTMLDivElement | null>(null);\n const [pos, setPos] = useState<{ top: number; left: number } | null>(null);\n\n // Measure the rendered card, then resolve a non-overlapping position: right of\n // the code if it fits, then left if that side has a clean gutter, otherwise\n // below the line.\n useLayoutEffect(() => {\n if (typeof window === \"undefined\") return;\n const el = cardRef.current;\n const rect = el?.getBoundingClientRect();\n const width = rect && rect.width > 0 ? rect.width : HOVER_CARD_WIDTH;\n const height = rect && rect.height > 0 ? rect.height : 0;\n const vw = window.innerWidth || 0;\n const vh = window.innerHeight || 0;\n setPos(\n resolveAnnotationHoverCardPosition(\n anchor,\n { width, height },\n { width: vw, height: vh },\n ),\n );\n }, [\n anchor.codeRight,\n anchor.codeLeft,\n anchor.lineCenter,\n anchor.lineBottom,\n item.index,\n ]);\n\n if (typeof document === \"undefined\") return null;\n\n return createPortal(\n <div\n ref={cardRef}\n role=\"tooltip\"\n data-annotation-hover-card\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n className=\"pointer-events-auto fixed z-50\"\n style={{\n top: pos?.top ?? anchor.lineCenter,\n left: pos?.left ?? anchor.codeRight + HOVER_CARD_GAP,\n width: HOVER_CARD_WIDTH,\n // Hide until measured to avoid a one-frame jump from the fallback spot.\n visibility: pos ? \"visible\" : \"hidden\",\n }}\n >\n <AnnotationCard\n item={item}\n ctx={ctx}\n active\n showMarker={showMarker}\n className=\"shadow-lg shadow-black/10 dark:shadow-black/40\"\n />\n </div>,\n document.body,\n );\n}\n\n/**\n * Hover-intent controller for the on-hover note card. Exposes `activeIndex` +\n * the captured `anchor`, plus `open`/`scheduleClose`/`cancelClose` handlers.\n *\n * - `open(index, anchor)` shows a card immediately (cancels any pending close).\n * - `scheduleClose()` hides after a short delay, so moving the pointer from the\n * code line across the gap into the card itself keeps it open.\n * - `cancelClose()` (call on card mouse-enter) keeps it open while reading.\n */\nexport function useAnnotationHover(delay = 130) {\n const [active, setActive] = useState<{\n index: number;\n anchor: AnnotationAnchor;\n } | null>(null);\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const cancelClose = () => {\n if (timer.current) {\n clearTimeout(timer.current);\n timer.current = null;\n }\n };\n const open = (index: number, anchor: AnnotationAnchor) => {\n cancelClose();\n setActive({ index, anchor });\n };\n const scheduleClose = () => {\n cancelClose();\n timer.current = setTimeout(() => setActive(null), delay);\n };\n\n useEffect(() => () => cancelClose(), []);\n\n return {\n activeIndex: active?.index ?? null,\n anchor: active?.anchor ?? null,\n open,\n scheduleClose,\n cancelClose,\n } as const;\n}\n\n/**\n * Build an {@link AnnotationAnchor} from the code block element and the hovered\n * row element. The card anchors to the code block's RIGHT edge and the row's\n * vertical center, both in viewport coordinates (so a `fixed` portal lines up).\n */\nexport function anchorFromElements(\n codeEl: HTMLElement | null,\n rowEl: HTMLElement | null,\n): AnnotationAnchor | null {\n if (!codeEl || !rowEl) return null;\n const code = codeEl.getBoundingClientRect();\n const row = rowEl.getBoundingClientRect();\n return {\n codeRight: code.right,\n codeLeft: code.left,\n lineCenter: row.top + row.height / 2,\n lineBottom: row.bottom,\n };\n}\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 *\n * @deprecated Superseded by the on-hover {@link AnnotationHoverCard}; kept for\n * back-compat with any external importer. Both block read renderers now use the\n * hover popover anchored to the right of the code instead of a persistent rail.\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 <AnnotationCard\n key={item.index}\n item={item}\n ctx={ctx}\n active={activeIndex === item.index}\n showMarker={showMarker}\n onMouseEnter={() => onActiveChange(item.index)}\n onMouseLeave={() => onActiveChange(null)}\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"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function CodeFilenameLabel({ filename, fallback, className, directoryClassName, basenameClassName, }: {
|
|
2
|
+
filename?: string | null;
|
|
3
|
+
fallback?: string;
|
|
4
|
+
className?: string;
|
|
5
|
+
directoryClassName?: string;
|
|
6
|
+
basenameClassName?: string;
|
|
7
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
//# sourceMappingURL=code-filename-label.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-filename-label.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/code-filename-label.tsx"],"names":[],"mappings":"AAcA,wBAAgB,iBAAiB,CAAC,EAChC,QAAQ,EACR,QAAoB,EACpB,SAAS,EACT,kBAAkB,EAClB,iBAAiB,GAClB,EAAE;IACD,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,2CAgCA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../../utils.js";
|
|
3
|
+
function splitFilename(filename) {
|
|
4
|
+
const value = filename?.trim() || "snippet";
|
|
5
|
+
const segments = value.split("/").filter(Boolean);
|
|
6
|
+
const basename = segments[segments.length - 1] ?? value;
|
|
7
|
+
const directory = segments.length > 1 ? `${segments.slice(0, -1).join("/")}/` : null;
|
|
8
|
+
return { basename, directory };
|
|
9
|
+
}
|
|
10
|
+
export function CodeFilenameLabel({ filename, fallback = "snippet", className, directoryClassName, basenameClassName, }) {
|
|
11
|
+
const value = filename?.trim() || fallback;
|
|
12
|
+
const { basename, directory } = splitFilename(value);
|
|
13
|
+
return (_jsxs("span", { className: cn("inline-flex min-w-0 flex-1 items-baseline font-mono", className), title: value, children: [directory && (_jsx("span", { "data-code-filename-directory": true, className: cn("min-w-0 shrink truncate", directoryClassName), children: directory })), _jsx("span", { "data-code-filename-basename": true, className: cn("min-w-0 truncate", directory ? "max-w-[60%] shrink-0" : "flex-1", basenameClassName), children: basename })] }));
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=code-filename-label.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-filename-label.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/code-filename-label.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AAEpC,SAAS,aAAa,CAAC,QAAwB;IAI7C,MAAM,KAAK,GAAG,QAAQ,EAAE,IAAI,EAAE,IAAI,SAAS,CAAC;IAC5C,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAClD,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC;IACxD,MAAM,SAAS,GACb,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;IACrE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,EAChC,QAAQ,EACR,QAAQ,GAAG,SAAS,EACpB,SAAS,EACT,kBAAkB,EAClB,iBAAiB,GAOlB;IACC,MAAM,KAAK,GAAG,QAAQ,EAAE,IAAI,EAAE,IAAI,QAAQ,CAAC;IAC3C,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAErD,OAAO,CACL,gBACE,SAAS,EAAE,EAAE,CACX,qDAAqD,EACrD,SAAS,CACV,EACD,KAAK,EAAE,KAAK,aAEX,SAAS,IAAI,CACZ,qDAEE,SAAS,EAAE,EAAE,CAAC,yBAAyB,EAAE,kBAAkB,CAAC,YAE3D,SAAS,GACL,CACR,EACD,oDAEE,SAAS,EAAE,EAAE,CACX,kBAAkB,EAClB,SAAS,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,QAAQ,EAC7C,iBAAiB,CAClB,YAEA,QAAQ,GACJ,IACF,CACR,CAAC;AACJ,CAAC","sourcesContent":["import { cn } from \"../../utils.js\";\n\nfunction splitFilename(filename?: string | null): {\n basename: string;\n directory: string | null;\n} {\n const value = filename?.trim() || \"snippet\";\n const segments = value.split(\"/\").filter(Boolean);\n const basename = segments[segments.length - 1] ?? value;\n const directory =\n segments.length > 1 ? `${segments.slice(0, -1).join(\"/\")}/` : null;\n return { basename, directory };\n}\n\nexport function CodeFilenameLabel({\n filename,\n fallback = \"snippet\",\n className,\n directoryClassName,\n basenameClassName,\n}: {\n filename?: string | null;\n fallback?: string;\n className?: string;\n directoryClassName?: string;\n basenameClassName?: string;\n}) {\n const value = filename?.trim() || fallback;\n const { basename, directory } = splitFilename(value);\n\n return (\n <span\n className={cn(\n \"inline-flex min-w-0 flex-1 items-baseline font-mono\",\n className,\n )}\n title={value}\n >\n {directory && (\n <span\n data-code-filename-directory\n className={cn(\"min-w-0 shrink truncate\", directoryClassName)}\n >\n {directory}\n </span>\n )}\n <span\n data-code-filename-basename\n className={cn(\n \"min-w-0 truncate\",\n directory ? \"max-w-[60%] shrink-0\" : \"flex-1\",\n basenameClassName,\n )}\n >\n {basename}\n </span>\n </span>\n );\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/code.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"code.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/code.tsx"],"names":[],"mappings":"AAyBA,OAAO,EAAuB,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AA2YtE,eAAO,MAAM,SAAS,2CAYpB,CAAC"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useId, useMemo, useRef, useState, } from "react";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useId, useEffect, useMemo, useRef, useState, } from "react";
|
|
3
3
|
import { IconCheck, IconCode, IconCopy, IconPencil } from "@tabler/icons-react";
|
|
4
4
|
import { cn } from "../../utils.js";
|
|
5
5
|
import { Popover, PopoverContent, PopoverTrigger, } from "../../components/ui/popover.js";
|
|
6
6
|
import { defineBlock } from "../types.js";
|
|
7
7
|
import { CodeSurface, DEFAULT_CODE_MAX_LINES } from "./HighlightedCode.js";
|
|
8
8
|
import { highlightCode, inferLanguageFromFilename, normalizeCodeLanguage, } from "./code-highlight.js";
|
|
9
|
+
import { CodeFilenameLabel } from "./code-filename-label.js";
|
|
9
10
|
import { codeSchema, codeMdx } from "./code.config.js";
|
|
10
11
|
/**
|
|
11
12
|
* Standard `code` block (STANDARD core library): THE primitive single code
|
|
@@ -52,13 +53,31 @@ function CodeRead({ data, blockId }) {
|
|
|
52
53
|
const language = normalizeCodeLanguage(data.language) ??
|
|
53
54
|
inferLanguageFromFilename(data.filename) ??
|
|
54
55
|
undefined;
|
|
55
|
-
|
|
56
|
+
const hasFilename = Boolean(data.filename?.trim());
|
|
57
|
+
return (_jsx("section", { className: "plan-block", "data-block-id": blockId, children: _jsxs("div", { className: "plan-code group relative", children: [hasFilename && (_jsxs("div", { className: "plan-code-head", children: [_jsxs("span", { className: "plan-code-filename", children: [_jsx(IconCode, { className: "size-4 shrink-0 opacity-70" }), _jsx(CodeFilenameLabel, { filename: data.filename, directoryClassName: "text-plan-muted", basenameClassName: "text-plan-text" })] }), _jsx("span", { className: "plan-code-chrome", children: _jsx(CopyButton, { value: data.code }) })] })), _jsx(CodeSurface, { code: data.code, language: language, maxLines: data.maxLines, className: data.filename ? "mt-0" : "mt-0", showLanguageLabel: false }), !hasFilename && (_jsx("span", { className: "plan-code-chrome plan-code-chrome-float", children: _jsx(CopyButton, { value: data.code }) })), data.caption && _jsx("p", { className: "plan-code-caption", children: data.caption })] }) }));
|
|
56
58
|
}
|
|
57
59
|
/* ── Edit (single border, no resize, auto-grow, hover chrome) ──────────────── */
|
|
58
60
|
const SETTINGS_INPUT = "flex h-9 w-full rounded-md border border-input 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";
|
|
59
61
|
/** Hover "settings" (pencil) → popover to edit the filename + max-lines cap. */
|
|
60
62
|
function CodeSettingsPopover({ filename, maxLines, onFilenameChange, onMaxLinesChange, }) {
|
|
61
|
-
|
|
63
|
+
const [filenameDraft, setFilenameDraft] = useState(filename ?? "");
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
setFilenameDraft(filename ?? "");
|
|
66
|
+
}, [filename]);
|
|
67
|
+
const commitFilename = () => {
|
|
68
|
+
const next = filenameDraft.trim();
|
|
69
|
+
onFilenameChange(next || undefined);
|
|
70
|
+
};
|
|
71
|
+
return (_jsxs(Popover, { children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx("button", { type: "button", "data-plan-interactive": true, "aria-label": "Code block settings", title: "Code block settings", className: "plan-code-chip", children: _jsx(IconPencil, { className: "size-3.5" }) }) }), _jsxs(PopoverContent, { align: "end", side: "bottom", className: "w-64 p-0", "data-plan-interactive": true, children: [_jsx("div", { className: "border-b border-border px-3 py-2 text-sm font-semibold text-foreground", children: "Code block" }), _jsxs("div", { className: "grid gap-3 p-3", children: [_jsxs("label", { className: "grid gap-1.5", children: [_jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Filename" }), _jsx("input", { type: "text", "data-plan-interactive": true, className: SETTINGS_INPUT, placeholder: "src/file.ts", value: filenameDraft, onBlur: commitFilename, onChange: (event) => setFilenameDraft(event.target.value), onKeyDown: (event) => {
|
|
72
|
+
if (event.key === "Enter") {
|
|
73
|
+
commitFilename();
|
|
74
|
+
event.currentTarget.blur();
|
|
75
|
+
}
|
|
76
|
+
if (event.key === "Escape") {
|
|
77
|
+
setFilenameDraft(filename ?? "");
|
|
78
|
+
event.currentTarget.blur();
|
|
79
|
+
}
|
|
80
|
+
} })] }), _jsxs("label", { className: "grid gap-1.5", children: [_jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Max lines before expand" }), _jsx("input", { type: "number", min: 0, step: 1, "data-plan-interactive": true, className: SETTINGS_INPUT, placeholder: `${DEFAULT_CODE_MAX_LINES} (default) · 0 = no limit`, value: maxLines ?? "", onChange: (event) => {
|
|
62
81
|
const raw = event.target.value.trim();
|
|
63
82
|
const parsed = raw === "" ? undefined : Number(raw);
|
|
64
83
|
onMaxLinesChange(parsed === undefined || Number.isNaN(parsed)
|
|
@@ -83,18 +102,21 @@ function CodeEditorSurface({ code, language, filename, maxLines, editable, onCod
|
|
|
83
102
|
const collapsed = collapsible && !expanded;
|
|
84
103
|
const hiddenLines = collapsible ? lineCount - cap : 0;
|
|
85
104
|
const rows = collapsed ? cap : lineCount + 1;
|
|
105
|
+
const hasFilename = Boolean(filename?.trim());
|
|
86
106
|
const syncScroll = (event) => {
|
|
87
107
|
const layer = highlightLayerRef.current;
|
|
88
108
|
if (!layer)
|
|
89
109
|
return;
|
|
90
110
|
layer.scrollLeft = event.currentTarget.scrollLeft;
|
|
111
|
+
layer.scrollTop = event.currentTarget.scrollTop;
|
|
91
112
|
};
|
|
92
|
-
|
|
113
|
+
const chrome = (_jsxs(_Fragment, { children: [_jsx("label", { htmlFor: selectId, className: "sr-only", children: "Code language" }), _jsx("select", { id: selectId, "data-plan-interactive": true, disabled: !editable, className: "plan-code-lang-select", value: normalizeCodeLanguage(language) ? (language ?? "") : "", onChange: (event) => onLanguageChange(event.target.value || undefined), children: CODE_LANGUAGES.map((option) => (_jsx("option", { value: option.value, children: option.label }, option.value || "auto"))) }), editable && (_jsx(CodeSettingsPopover, { filename: filename, maxLines: maxLines, onFilenameChange: onFilenameChange, onMaxLinesChange: onMaxLinesChange })), _jsx(CopyButton, { value: code })] }));
|
|
114
|
+
return (_jsxs("div", { className: cn("plan-code plan-code-editing group relative", !hasFilename && "plan-code-editing--unlabeled", !editable && "opacity-60"), children: [hasFilename && (_jsxs("div", { className: "plan-code-head", children: [_jsxs("span", { className: "plan-code-filename plan-code-muted", children: [_jsx(IconCode, { className: "size-4 shrink-0 opacity-70" }), _jsx(CodeFilenameLabel, { filename: filename, directoryClassName: "text-plan-muted", basenameClassName: "text-plan-text" })] }), _jsx("span", { className: "plan-code-chrome", children: chrome })] })), _jsxs("div", { className: "plan-code-editor-body", children: [_jsx("pre", { ref: highlightLayerRef, "aria-hidden": "true", className: "plan-code-editor-layer", children: _jsxs("code", { children: [highlighted, code.endsWith("\n") ? " " : null] }) }), _jsx("textarea", { "data-plan-interactive": true, spellCheck: false, wrap: "off", rows: Math.max(3, rows), className: "plan-code-editor-input", value: code, disabled: !editable, onChange: (event) => onCodeChange(event.target.value), onScroll: syncScroll }), collapsed && (_jsx("div", { className: "plan-code-editor-fade", "aria-hidden": "true" }))] }), !hasFilename && (_jsx("span", { className: "plan-code-chrome plan-code-chrome-float", children: chrome })), collapsible && (_jsx("button", { type: "button", "data-plan-interactive": true, className: "plan-code-surface-toggle", onClick: () => setExpanded((value) => !value), children: collapsed
|
|
93
115
|
? `Show ${hiddenLines} more line${hiddenLines === 1 ? "" : "s"}`
|
|
94
116
|
: "Show less" }))] }));
|
|
95
117
|
}
|
|
96
118
|
function CodeEdit({ data, onChange, editable }) {
|
|
97
|
-
return (_jsxs("div", { className: "flex min-w-0 flex-col gap-2", children: [_jsx(CodeEditorSurface, { code: data.code, language: data.language, filename: data.filename, maxLines: data.maxLines, editable: editable, onCodeChange: (code) => onChange({ ...data, code }), onLanguageChange: (language) => onChange({ ...data, language }), onFilenameChange: (filename) => onChange({ ...data, filename }), onMaxLinesChange: (maxLines) => onChange({ ...data, maxLines }) }), editable && (_jsx("input", { type: "text", "data-plan-interactive": true, className: "plan-code-caption-input", placeholder: "Caption
|
|
119
|
+
return (_jsxs("div", { className: "flex min-w-0 flex-col gap-2", children: [_jsx(CodeEditorSurface, { code: data.code, language: data.language, filename: data.filename, maxLines: data.maxLines, editable: editable, onCodeChange: (code) => onChange({ ...data, code }), onLanguageChange: (language) => onChange({ ...data, language }), onFilenameChange: (filename) => onChange({ ...data, filename }), onMaxLinesChange: (maxLines) => onChange({ ...data, maxLines }) }), editable && (_jsx("input", { type: "text", "data-plan-interactive": true, className: "plan-code-caption-input", placeholder: "Caption", value: data.caption ?? "", onChange: (event) => onChange({ ...data, caption: event.target.value || undefined }) }))] }));
|
|
98
120
|
}
|
|
99
121
|
/* ── Spec ──────────────────────────────────────────────────────────────────── */
|
|
100
122
|
export const codeBlock = defineBlock({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/code.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n useId,\n useMemo,\n useRef,\n useState,\n type ChangeEvent,\n type UIEvent,\n} from \"react\";\nimport { IconCheck, IconCode, IconCopy, IconPencil } from \"@tabler/icons-react\";\nimport { cn } from \"../../utils.js\";\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"../../components/ui/popover.js\";\nimport { defineBlock } from \"../types.js\";\nimport type { BlockReadProps, BlockEditProps } from \"../types.js\";\nimport { CodeSurface, DEFAULT_CODE_MAX_LINES } from \"./HighlightedCode.js\";\nimport {\n highlightCode,\n inferLanguageFromFilename,\n normalizeCodeLanguage,\n} from \"./code-highlight.js\";\nimport { codeSchema, codeMdx, type CodeData } from \"./code.config.js\";\n\n/**\n * Standard `code` block (STANDARD core library): THE primitive single code\n * snippet, used everywhere in plan + content. Notion-style — one border, a\n * hover-revealed language switcher + copy, and the shared collapse-to-N-lines\n * read surface. A \"file rail\" of several files is just the `tabs` primitive\n * holding `code` blocks; there is no bespoke \"code-tabs\" container.\n *\n * Read = the shared {@link CodeSurface} (Shiki, single border, language label,\n * \"Show N more lines\"). Edit = a clean, single-border editable surface (no\n * drag-to-resize; it auto-grows to its content) with the same hover chrome.\n */\n\n/** Language options for the hover switcher; \"\" is the Auto-detect sentinel. */\nconst CODE_LANGUAGES: ReadonlyArray<{ value: string; label: string }> = [\n { value: \"\", label: \"Auto\" },\n { value: \"typescript\", label: \"TypeScript\" },\n { value: \"javascript\", label: \"JavaScript\" },\n { value: \"tsx\", label: \"TSX\" },\n { value: \"jsx\", label: \"JSX\" },\n { value: \"json\", label: \"JSON\" },\n { value: \"html\", label: \"HTML\" },\n { value: \"css\", label: \"CSS\" },\n { value: \"bash\", label: \"Bash\" },\n { value: \"python\", label: \"Python\" },\n { value: \"sql\", label: \"SQL\" },\n { value: \"yaml\", label: \"YAML\" },\n { value: \"markdown\", label: \"Markdown\" },\n { value: \"graphql\", label: \"GraphQL\" },\n { value: \"go\", label: \"Go\" },\n { value: \"rust\", label: \"Rust\" },\n { value: \"diff\", label: \"Diff\" },\n];\n\nfunction CopyButton({ value }: { value: string }) {\n const [copied, setCopied] = useState(false);\n return (\n <button\n type=\"button\"\n data-plan-interactive\n aria-label={copied ? \"Copied\" : \"Copy code\"}\n title={copied ? \"Copied\" : \"Copy code\"}\n className=\"plan-code-chip\"\n onClick={() => {\n void navigator.clipboard?.writeText(value).then(\n () => {\n setCopied(true);\n setTimeout(() => setCopied(false), 1200);\n },\n () => {},\n );\n }}\n >\n {copied ? (\n <IconCheck className=\"size-3.5\" />\n ) : (\n <IconCopy className=\"size-3.5\" />\n )}\n </button>\n );\n}\n\n/* ── Read ──────────────────────────────────────────────────────────────────── */\n\nfunction CodeRead({ data, blockId }: BlockReadProps<CodeData>) {\n const language =\n normalizeCodeLanguage(data.language) ??\n inferLanguageFromFilename(data.filename) ??\n undefined;\n return (\n <section className=\"plan-block\" data-block-id={blockId}>\n <div className=\"plan-code group relative\">\n {data.filename && (\n <div className=\"plan-code-head\">\n <span className=\"plan-code-filename\">\n <IconCode className=\"size-4 shrink-0 opacity-70\" />\n {data.filename}\n </span>\n <span className=\"plan-code-chrome\">\n <CopyButton value={data.code} />\n </span>\n </div>\n )}\n <CodeSurface\n code={data.code}\n language={language}\n maxLines={data.maxLines}\n className={data.filename ? \"mt-0\" : \"mt-0\"}\n />\n {!data.filename && (\n <span className=\"plan-code-chrome plan-code-chrome-float\">\n <CopyButton value={data.code} />\n </span>\n )}\n {data.caption && <p className=\"plan-code-caption\">{data.caption}</p>}\n </div>\n </section>\n );\n}\n\n/* ── Edit (single border, no resize, auto-grow, hover chrome) ──────────────── */\n\nconst SETTINGS_INPUT =\n \"flex h-9 w-full rounded-md border border-input 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\n/** Hover \"settings\" (pencil) → popover to edit the filename + max-lines cap. */\nfunction CodeSettingsPopover({\n filename,\n maxLines,\n onFilenameChange,\n onMaxLinesChange,\n}: {\n filename?: string;\n maxLines?: number;\n onFilenameChange: (filename: string | undefined) => void;\n onMaxLinesChange: (maxLines: number | undefined) => void;\n}) {\n return (\n <Popover>\n <PopoverTrigger asChild>\n <button\n type=\"button\"\n data-plan-interactive\n aria-label=\"Code block settings\"\n title=\"Code block settings\"\n className=\"plan-code-chip\"\n >\n <IconPencil className=\"size-3.5\" />\n </button>\n </PopoverTrigger>\n <PopoverContent\n align=\"end\"\n side=\"bottom\"\n className=\"w-64 p-0\"\n data-plan-interactive\n >\n <div className=\"border-b border-border px-3 py-2 text-sm font-semibold text-foreground\">\n Code block\n </div>\n <div className=\"grid gap-3 p-3\">\n <label className=\"grid gap-1.5\">\n <span className=\"text-xs font-medium text-muted-foreground\">\n Filename\n </span>\n <input\n type=\"text\"\n data-plan-interactive\n className={SETTINGS_INPUT}\n placeholder=\"src/file.ts\"\n value={filename ?? \"\"}\n onChange={(event) =>\n onFilenameChange(event.target.value || undefined)\n }\n />\n </label>\n <label className=\"grid gap-1.5\">\n <span className=\"text-xs font-medium text-muted-foreground\">\n Max lines before expand\n </span>\n <input\n type=\"number\"\n min={0}\n step={1}\n data-plan-interactive\n className={SETTINGS_INPUT}\n placeholder={`${DEFAULT_CODE_MAX_LINES} (default) · 0 = no limit`}\n value={maxLines ?? \"\"}\n onChange={(event) => {\n const raw = event.target.value.trim();\n const parsed = raw === \"\" ? undefined : Number(raw);\n onMaxLinesChange(\n parsed === undefined || Number.isNaN(parsed)\n ? undefined\n : Math.max(0, Math.min(2000, Math.floor(parsed))),\n );\n }}\n />\n </label>\n </div>\n </PopoverContent>\n </Popover>\n );\n}\n\nfunction CodeEditorSurface({\n code,\n language,\n filename,\n maxLines,\n editable,\n onCodeChange,\n onLanguageChange,\n onFilenameChange,\n onMaxLinesChange,\n}: {\n code: string;\n language?: string;\n filename?: string;\n maxLines?: number;\n editable: boolean;\n onCodeChange: (code: string) => void;\n onLanguageChange: (language: string | undefined) => void;\n onFilenameChange: (filename: string | undefined) => void;\n onMaxLinesChange: (maxLines: number | undefined) => void;\n}) {\n const [expanded, setExpanded] = useState(false);\n const highlightLayerRef = useRef<HTMLPreElement>(null);\n const selectId = useId();\n const resolvedLanguage =\n normalizeCodeLanguage(language) ?? inferLanguageFromFilename(filename);\n const highlighted = useMemo(\n () => highlightCode(code, resolvedLanguage),\n [resolvedLanguage, code],\n );\n // Size the editor to its content by line count — deterministic, no layout\n // measurement. `wrap=\"off\"` means one row per line. Long snippets collapse to\n // `cap` lines behind a \"Show N more lines\" toggle, matching the read surface\n // and the file-tree block. `maxLines` omitted ⇒ DEFAULT (40); `0` ⇒ never\n // collapse (show everything).\n const lineCount = code ? code.split(\"\\n\").length : 1;\n const cap =\n maxLines == null ? DEFAULT_CODE_MAX_LINES : maxLines > 0 ? maxLines : null;\n const collapsible = cap != null && lineCount > cap;\n const collapsed = collapsible && !expanded;\n const hiddenLines = collapsible ? lineCount - (cap as number) : 0;\n const rows = collapsed ? (cap as number) : lineCount + 1;\n\n const syncScroll = (event: UIEvent<HTMLTextAreaElement>) => {\n const layer = highlightLayerRef.current;\n if (!layer) return;\n layer.scrollLeft = event.currentTarget.scrollLeft;\n };\n\n return (\n <div\n className={cn(\n \"plan-code plan-code-editing group relative\",\n !editable && \"opacity-60\",\n )}\n >\n <div className=\"plan-code-head\">\n <span className=\"plan-code-filename plan-code-muted\">\n <IconCode className=\"size-4 shrink-0 opacity-70\" />\n {filename || \"Snippet\"}\n </span>\n <span className=\"plan-code-chrome\">\n <label htmlFor={selectId} className=\"sr-only\">\n Code language\n </label>\n <select\n id={selectId}\n data-plan-interactive\n disabled={!editable}\n className=\"plan-code-lang-select\"\n value={normalizeCodeLanguage(language) ? (language ?? \"\") : \"\"}\n onChange={(event) =>\n onLanguageChange(event.target.value || undefined)\n }\n >\n {CODE_LANGUAGES.map((option) => (\n <option key={option.value || \"auto\"} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n {editable && (\n <CodeSettingsPopover\n filename={filename}\n maxLines={maxLines}\n onFilenameChange={onFilenameChange}\n onMaxLinesChange={onMaxLinesChange}\n />\n )}\n <CopyButton value={code} />\n </span>\n </div>\n <div className=\"plan-code-editor-body\">\n <pre\n ref={highlightLayerRef}\n aria-hidden=\"true\"\n className=\"plan-code-editor-layer\"\n >\n <code>\n {highlighted}\n {code.endsWith(\"\\n\") ? \" \" : null}\n </code>\n </pre>\n <textarea\n data-plan-interactive\n spellCheck={false}\n wrap=\"off\"\n rows={Math.max(3, rows)}\n className=\"plan-code-editor-input\"\n value={code}\n disabled={!editable}\n onChange={(event: ChangeEvent<HTMLTextAreaElement>) =>\n onCodeChange(event.target.value)\n }\n onScroll={syncScroll}\n />\n {collapsed && (\n <div className=\"plan-code-editor-fade\" aria-hidden=\"true\" />\n )}\n </div>\n {collapsible && (\n <button\n type=\"button\"\n data-plan-interactive\n className=\"plan-code-surface-toggle\"\n onClick={() => setExpanded((value) => !value)}\n >\n {collapsed\n ? `Show ${hiddenLines} more line${hiddenLines === 1 ? \"\" : \"s\"}`\n : \"Show less\"}\n </button>\n )}\n </div>\n );\n}\n\nfunction CodeEdit({ data, onChange, editable }: BlockEditProps<CodeData>) {\n return (\n <div className=\"flex min-w-0 flex-col gap-2\">\n <CodeEditorSurface\n code={data.code}\n language={data.language}\n filename={data.filename}\n maxLines={data.maxLines}\n editable={editable}\n onCodeChange={(code) => onChange({ ...data, code })}\n onLanguageChange={(language) => onChange({ ...data, language })}\n onFilenameChange={(filename) => onChange({ ...data, filename })}\n onMaxLinesChange={(maxLines) => onChange({ ...data, maxLines })}\n />\n {editable && (\n <input\n type=\"text\"\n data-plan-interactive\n className=\"plan-code-caption-input\"\n placeholder=\"Caption (optional)\"\n value={data.caption ?? \"\"}\n onChange={(event) =>\n onChange({ ...data, caption: event.target.value || undefined })\n }\n />\n )}\n </div>\n );\n}\n\n/* ── Spec ──────────────────────────────────────────────────────────────────── */\n\nexport const codeBlock = defineBlock<CodeData>({\n type: \"code\",\n schema: codeSchema,\n mdx: codeMdx,\n Read: CodeRead,\n Edit: CodeEdit,\n placement: [\"block\"],\n editSurface: \"inline\",\n label: \"Code\",\n icon: IconCode,\n description:\n \"A single syntax-highlighted code snippet, Notion-style: one border, a hover language switcher + copy, and collapse-to-N lines. Put several in a `tabs` block for a file rail.\",\n});\n"]}
|
|
1
|
+
{"version":3,"file":"code.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/code.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n useId,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ChangeEvent,\n type UIEvent,\n} from \"react\";\nimport { IconCheck, IconCode, IconCopy, IconPencil } from \"@tabler/icons-react\";\nimport { cn } from \"../../utils.js\";\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"../../components/ui/popover.js\";\nimport { defineBlock } from \"../types.js\";\nimport type { BlockReadProps, BlockEditProps } from \"../types.js\";\nimport { CodeSurface, DEFAULT_CODE_MAX_LINES } from \"./HighlightedCode.js\";\nimport {\n highlightCode,\n inferLanguageFromFilename,\n normalizeCodeLanguage,\n} from \"./code-highlight.js\";\nimport { CodeFilenameLabel } from \"./code-filename-label.js\";\nimport { codeSchema, codeMdx, type CodeData } from \"./code.config.js\";\n\n/**\n * Standard `code` block (STANDARD core library): THE primitive single code\n * snippet, used everywhere in plan + content. Notion-style — one border, a\n * hover-revealed language switcher + copy, and the shared collapse-to-N-lines\n * read surface. A \"file rail\" of several files is just the `tabs` primitive\n * holding `code` blocks; there is no bespoke \"code-tabs\" container.\n *\n * Read = the shared {@link CodeSurface} (Shiki, single border, language label,\n * \"Show N more lines\"). Edit = a clean, single-border editable surface (no\n * drag-to-resize; it auto-grows to its content) with the same hover chrome.\n */\n\n/** Language options for the hover switcher; \"\" is the Auto-detect sentinel. */\nconst CODE_LANGUAGES: ReadonlyArray<{ value: string; label: string }> = [\n { value: \"\", label: \"Auto\" },\n { value: \"typescript\", label: \"TypeScript\" },\n { value: \"javascript\", label: \"JavaScript\" },\n { value: \"tsx\", label: \"TSX\" },\n { value: \"jsx\", label: \"JSX\" },\n { value: \"json\", label: \"JSON\" },\n { value: \"html\", label: \"HTML\" },\n { value: \"css\", label: \"CSS\" },\n { value: \"bash\", label: \"Bash\" },\n { value: \"python\", label: \"Python\" },\n { value: \"sql\", label: \"SQL\" },\n { value: \"yaml\", label: \"YAML\" },\n { value: \"markdown\", label: \"Markdown\" },\n { value: \"graphql\", label: \"GraphQL\" },\n { value: \"go\", label: \"Go\" },\n { value: \"rust\", label: \"Rust\" },\n { value: \"diff\", label: \"Diff\" },\n];\n\nfunction CopyButton({ value }: { value: string }) {\n const [copied, setCopied] = useState(false);\n return (\n <button\n type=\"button\"\n data-plan-interactive\n aria-label={copied ? \"Copied\" : \"Copy code\"}\n title={copied ? \"Copied\" : \"Copy code\"}\n className=\"plan-code-chip\"\n onClick={() => {\n void navigator.clipboard?.writeText(value).then(\n () => {\n setCopied(true);\n setTimeout(() => setCopied(false), 1200);\n },\n () => {},\n );\n }}\n >\n {copied ? (\n <IconCheck className=\"size-3.5\" />\n ) : (\n <IconCopy className=\"size-3.5\" />\n )}\n </button>\n );\n}\n\n/* ── Read ──────────────────────────────────────────────────────────────────── */\n\nfunction CodeRead({ data, blockId }: BlockReadProps<CodeData>) {\n const language =\n normalizeCodeLanguage(data.language) ??\n inferLanguageFromFilename(data.filename) ??\n undefined;\n const hasFilename = Boolean(data.filename?.trim());\n return (\n <section className=\"plan-block\" data-block-id={blockId}>\n <div className=\"plan-code group relative\">\n {hasFilename && (\n <div className=\"plan-code-head\">\n <span className=\"plan-code-filename\">\n <IconCode className=\"size-4 shrink-0 opacity-70\" />\n <CodeFilenameLabel\n filename={data.filename}\n directoryClassName=\"text-plan-muted\"\n basenameClassName=\"text-plan-text\"\n />\n </span>\n <span className=\"plan-code-chrome\">\n <CopyButton value={data.code} />\n </span>\n </div>\n )}\n <CodeSurface\n code={data.code}\n language={language}\n maxLines={data.maxLines}\n className={data.filename ? \"mt-0\" : \"mt-0\"}\n showLanguageLabel={false}\n />\n {!hasFilename && (\n <span className=\"plan-code-chrome plan-code-chrome-float\">\n <CopyButton value={data.code} />\n </span>\n )}\n {data.caption && <p className=\"plan-code-caption\">{data.caption}</p>}\n </div>\n </section>\n );\n}\n\n/* ── Edit (single border, no resize, auto-grow, hover chrome) ──────────────── */\n\nconst SETTINGS_INPUT =\n \"flex h-9 w-full rounded-md border border-input 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\n/** Hover \"settings\" (pencil) → popover to edit the filename + max-lines cap. */\nfunction CodeSettingsPopover({\n filename,\n maxLines,\n onFilenameChange,\n onMaxLinesChange,\n}: {\n filename?: string;\n maxLines?: number;\n onFilenameChange: (filename: string | undefined) => void;\n onMaxLinesChange: (maxLines: number | undefined) => void;\n}) {\n const [filenameDraft, setFilenameDraft] = useState(filename ?? \"\");\n\n useEffect(() => {\n setFilenameDraft(filename ?? \"\");\n }, [filename]);\n\n const commitFilename = () => {\n const next = filenameDraft.trim();\n onFilenameChange(next || undefined);\n };\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <button\n type=\"button\"\n data-plan-interactive\n aria-label=\"Code block settings\"\n title=\"Code block settings\"\n className=\"plan-code-chip\"\n >\n <IconPencil className=\"size-3.5\" />\n </button>\n </PopoverTrigger>\n <PopoverContent\n align=\"end\"\n side=\"bottom\"\n className=\"w-64 p-0\"\n data-plan-interactive\n >\n <div className=\"border-b border-border px-3 py-2 text-sm font-semibold text-foreground\">\n Code block\n </div>\n <div className=\"grid gap-3 p-3\">\n <label className=\"grid gap-1.5\">\n <span className=\"text-xs font-medium text-muted-foreground\">\n Filename\n </span>\n <input\n type=\"text\"\n data-plan-interactive\n className={SETTINGS_INPUT}\n placeholder=\"src/file.ts\"\n value={filenameDraft}\n onBlur={commitFilename}\n onChange={(event) => setFilenameDraft(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === \"Enter\") {\n commitFilename();\n event.currentTarget.blur();\n }\n if (event.key === \"Escape\") {\n setFilenameDraft(filename ?? \"\");\n event.currentTarget.blur();\n }\n }}\n />\n </label>\n <label className=\"grid gap-1.5\">\n <span className=\"text-xs font-medium text-muted-foreground\">\n Max lines before expand\n </span>\n <input\n type=\"number\"\n min={0}\n step={1}\n data-plan-interactive\n className={SETTINGS_INPUT}\n placeholder={`${DEFAULT_CODE_MAX_LINES} (default) · 0 = no limit`}\n value={maxLines ?? \"\"}\n onChange={(event) => {\n const raw = event.target.value.trim();\n const parsed = raw === \"\" ? undefined : Number(raw);\n onMaxLinesChange(\n parsed === undefined || Number.isNaN(parsed)\n ? undefined\n : Math.max(0, Math.min(2000, Math.floor(parsed))),\n );\n }}\n />\n </label>\n </div>\n </PopoverContent>\n </Popover>\n );\n}\n\nfunction CodeEditorSurface({\n code,\n language,\n filename,\n maxLines,\n editable,\n onCodeChange,\n onLanguageChange,\n onFilenameChange,\n onMaxLinesChange,\n}: {\n code: string;\n language?: string;\n filename?: string;\n maxLines?: number;\n editable: boolean;\n onCodeChange: (code: string) => void;\n onLanguageChange: (language: string | undefined) => void;\n onFilenameChange: (filename: string | undefined) => void;\n onMaxLinesChange: (maxLines: number | undefined) => void;\n}) {\n const [expanded, setExpanded] = useState(false);\n const highlightLayerRef = useRef<HTMLPreElement>(null);\n const selectId = useId();\n const resolvedLanguage =\n normalizeCodeLanguage(language) ?? inferLanguageFromFilename(filename);\n const highlighted = useMemo(\n () => highlightCode(code, resolvedLanguage),\n [resolvedLanguage, code],\n );\n // Size the editor to its content by line count — deterministic, no layout\n // measurement. `wrap=\"off\"` means one row per line. Long snippets collapse to\n // `cap` lines behind a \"Show N more lines\" toggle, matching the read surface\n // and the file-tree block. `maxLines` omitted ⇒ DEFAULT (40); `0` ⇒ never\n // collapse (show everything).\n const lineCount = code ? code.split(\"\\n\").length : 1;\n const cap =\n maxLines == null ? DEFAULT_CODE_MAX_LINES : maxLines > 0 ? maxLines : null;\n const collapsible = cap != null && lineCount > cap;\n const collapsed = collapsible && !expanded;\n const hiddenLines = collapsible ? lineCount - (cap as number) : 0;\n const rows = collapsed ? (cap as number) : lineCount + 1;\n const hasFilename = Boolean(filename?.trim());\n\n const syncScroll = (event: UIEvent<HTMLTextAreaElement>) => {\n const layer = highlightLayerRef.current;\n if (!layer) return;\n layer.scrollLeft = event.currentTarget.scrollLeft;\n layer.scrollTop = event.currentTarget.scrollTop;\n };\n\n const chrome = (\n <>\n <label htmlFor={selectId} className=\"sr-only\">\n Code language\n </label>\n <select\n id={selectId}\n data-plan-interactive\n disabled={!editable}\n className=\"plan-code-lang-select\"\n value={normalizeCodeLanguage(language) ? (language ?? \"\") : \"\"}\n onChange={(event) => onLanguageChange(event.target.value || undefined)}\n >\n {CODE_LANGUAGES.map((option) => (\n <option key={option.value || \"auto\"} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n {editable && (\n <CodeSettingsPopover\n filename={filename}\n maxLines={maxLines}\n onFilenameChange={onFilenameChange}\n onMaxLinesChange={onMaxLinesChange}\n />\n )}\n <CopyButton value={code} />\n </>\n );\n\n return (\n <div\n className={cn(\n \"plan-code plan-code-editing group relative\",\n !hasFilename && \"plan-code-editing--unlabeled\",\n !editable && \"opacity-60\",\n )}\n >\n {hasFilename && (\n <div className=\"plan-code-head\">\n <span className=\"plan-code-filename plan-code-muted\">\n <IconCode className=\"size-4 shrink-0 opacity-70\" />\n <CodeFilenameLabel\n filename={filename}\n directoryClassName=\"text-plan-muted\"\n basenameClassName=\"text-plan-text\"\n />\n </span>\n <span className=\"plan-code-chrome\">{chrome}</span>\n </div>\n )}\n <div className=\"plan-code-editor-body\">\n <pre\n ref={highlightLayerRef}\n aria-hidden=\"true\"\n className=\"plan-code-editor-layer\"\n >\n <code>\n {highlighted}\n {code.endsWith(\"\\n\") ? \" \" : null}\n </code>\n </pre>\n <textarea\n data-plan-interactive\n spellCheck={false}\n wrap=\"off\"\n rows={Math.max(3, rows)}\n className=\"plan-code-editor-input\"\n value={code}\n disabled={!editable}\n onChange={(event: ChangeEvent<HTMLTextAreaElement>) =>\n onCodeChange(event.target.value)\n }\n onScroll={syncScroll}\n />\n {collapsed && (\n <div className=\"plan-code-editor-fade\" aria-hidden=\"true\" />\n )}\n </div>\n {!hasFilename && (\n <span className=\"plan-code-chrome plan-code-chrome-float\">\n {chrome}\n </span>\n )}\n {collapsible && (\n <button\n type=\"button\"\n data-plan-interactive\n className=\"plan-code-surface-toggle\"\n onClick={() => setExpanded((value) => !value)}\n >\n {collapsed\n ? `Show ${hiddenLines} more line${hiddenLines === 1 ? \"\" : \"s\"}`\n : \"Show less\"}\n </button>\n )}\n </div>\n );\n}\n\nfunction CodeEdit({ data, onChange, editable }: BlockEditProps<CodeData>) {\n return (\n <div className=\"flex min-w-0 flex-col gap-2\">\n <CodeEditorSurface\n code={data.code}\n language={data.language}\n filename={data.filename}\n maxLines={data.maxLines}\n editable={editable}\n onCodeChange={(code) => onChange({ ...data, code })}\n onLanguageChange={(language) => onChange({ ...data, language })}\n onFilenameChange={(filename) => onChange({ ...data, filename })}\n onMaxLinesChange={(maxLines) => onChange({ ...data, maxLines })}\n />\n {editable && (\n <input\n type=\"text\"\n data-plan-interactive\n className=\"plan-code-caption-input\"\n placeholder=\"Caption\"\n value={data.caption ?? \"\"}\n onChange={(event) =>\n onChange({ ...data, caption: event.target.value || undefined })\n }\n />\n )}\n </div>\n );\n}\n\n/* ── Spec ──────────────────────────────────────────────────────────────────── */\n\nexport const codeBlock = defineBlock<CodeData>({\n type: \"code\",\n schema: codeSchema,\n mdx: codeMdx,\n Read: CodeRead,\n Edit: CodeEdit,\n placement: [\"block\"],\n editSurface: \"inline\",\n label: \"Code\",\n icon: IconCode,\n description:\n \"A single syntax-highlighted code snippet, Notion-style: one border, a hover language switcher + copy, and collapse-to-N lines. Put several in a `tabs` block for a file rail.\",\n});\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"columns.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/columns.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAEV,cAAc,EACd,cAAc,EAEf,MAAM,aAAa,CAAC;AACrB,OAAO,EAGL,KAAK,WAAW,EAEjB,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"columns.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/columns.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAEV,cAAc,EACd,cAAc,EAEf,MAAM,aAAa,CAAC;AACrB,OAAO,EAGL,KAAK,WAAW,EAEjB,MAAM,qBAAqB,CAAC;AAuH7B,kFAAkF;AAClF,wBAAgB,kBAAkB,CAAC,EACjC,IAAI,EACJ,OAAO,EACP,KAAK,EACL,GAAG,GACJ,EAAE,cAAc,CAAC,WAAW,CAAC,2CAqC7B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,IAAI,EACJ,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,GAAG,GACJ,EAAE,cAAc,CAAC,WAAW,CAAC,2CAsE7B;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,8CA0DvB,CAAC"}
|
|
@@ -3,6 +3,7 @@ import { IconColumns } from "@tabler/icons-react";
|
|
|
3
3
|
import { cn } from "../../utils.js";
|
|
4
4
|
import { defineBlock } from "../types.js";
|
|
5
5
|
import { columnsSchema, columnsMdx, } from "./columns.config.js";
|
|
6
|
+
import { NarrowContainerProvider } from "./narrow-container.js";
|
|
6
7
|
/**
|
|
7
8
|
* Standard `columns` block: a multi-column side-by-side container whose columns
|
|
8
9
|
* each hold a list of child blocks. A column's optional `label` renders as a
|
|
@@ -102,7 +103,16 @@ function areSameBlocks(a, b) {
|
|
|
102
103
|
}
|
|
103
104
|
/** Read renderer: a responsive grid of columns, each child rendered read-only. */
|
|
104
105
|
export function ColumnsBlockReader({ data, blockId, title, ctx, }) {
|
|
105
|
-
|
|
106
|
+
// A column cell is width-constrained only when the layout actually renders
|
|
107
|
+
// more than one column side by side; single-column / stacked layouts (API
|
|
108
|
+
// reference groups, wide wireframes) keep full document width, so a nested
|
|
109
|
+
// width-sensitive block (e.g. a diff) should NOT be forced to its narrow
|
|
110
|
+
// default there.
|
|
111
|
+
const narrow = columnsLayoutClass(data.columns) !== COLS_CLASS[1];
|
|
112
|
+
return (_jsxs("section", { className: "plan-block", "data-block-id": blockId, children: [title && _jsx("div", { className: "plan-block-label", children: title }), _jsx("div", { className: cn("grid gap-6", columnsLayoutClass(data.columns)), children: data.columns.map((column) => {
|
|
113
|
+
const body = (_jsx("div", { children: column.blocks.map((child) => (_jsx("div", { children: ctx.renderBlock?.({ block: child, editing: false }) }, child.id))) }));
|
|
114
|
+
return (_jsxs("div", { className: "min-w-0", children: [column.label && (_jsx("h4", { className: "plan-columns-label", children: column.label })), narrow ? (_jsx(NarrowContainerProvider, { children: body })) : (body)] }, column.id));
|
|
115
|
+
}) })] }));
|
|
106
116
|
}
|
|
107
117
|
/**
|
|
108
118
|
* Editor: the same responsive grid, with child blocks rendered editable in
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"columns.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/columns.tsx"],"names":[],"mappings":"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","sourcesContent":["import { IconColumns } from \"@tabler/icons-react\";\nimport { cn } from \"../../utils.js\";\nimport { defineBlock } from \"../types.js\";\nimport type {\n BlockContainerRegion,\n BlockEditProps,\n BlockReadProps,\n NestedBlock,\n} from \"../types.js\";\nimport {\n columnsSchema,\n columnsMdx,\n type ColumnsData,\n type ColumnsColumn,\n} from \"./columns.config.js\";\n\n/**\n * Standard `columns` block: a multi-column side-by-side container whose columns\n * each hold a list of child blocks. A column's optional `label` renders as a\n * small heading above that column (e.g. `Before` / `After`), so a comparison\n * names its states outside the content — never baked into a child wireframe.\n *\n * Like `tabs`, child rendering flows through `ctx.renderBlock` — the app's own\n * block dispatcher — so registered children render via their spec and\n * unconverted children fall through the app's legacy switch. This is the\n * coexistence seam: the core columns block never has to know app-specific child\n * block types. The plan CSS classes (`plan-block`, `text-plan-*`) resolve\n * against the plan app's stylesheet at render time.\n */\n\n/** Mint a reasonably-unique column id without pulling a dep into core. */\nfunction newColId(): string {\n return `col-${Math.random().toString(36).slice(2, 10)}`;\n}\n\n/**\n * Tailwind grid-column classes keyed by the column count. The grid collapses to\n * a single column on small screens and fans out to the column count at `md`+, so\n * narrow viewports stack the panels instead of crushing them.\n */\nconst COLS_CLASS: Record<number, string> = {\n 1: \"grid-cols-1\",\n 2: \"grid-cols-1 md:grid-cols-2\",\n 3: \"grid-cols-1 md:grid-cols-3\",\n 4: \"grid-cols-1 md:grid-cols-4\",\n};\n\n/** Resolve the responsive grid class for a column count (clamped to 1–4). */\nfunction gridColsClass(count: number): string {\n return COLS_CLASS[Math.min(4, Math.max(1, count))] ?? COLS_CLASS[1];\n}\n\nconst API_REFERENCE_BLOCK_TYPES = new Set([\"api-endpoint\", \"openapi-spec\"]);\nconst BEFORE_LABELS = new Set([\"before\", \"old\", \"previous\", \"current\"]);\nconst AFTER_LABELS = new Set([\"after\", \"new\", \"next\", \"target\"]);\n\n// Wireframe surfaces that render too wide to survive a half-width comparison\n// column: a full desktop page or browser frame shrinks and crops when squeezed\n// side by side, so a comparison that holds one of these stacks vertically.\nconst WIDE_WIREFRAME_SURFACES = new Set([\"desktop\", \"browser\"]);\n\nfunction normalizedLabel(column: ColumnsColumn): string {\n return column.label?.trim().toLowerCase() ?? \"\";\n}\n\nfunction isComparisonGroup(columns: ColumnsColumn[]): boolean {\n const labels = columns.map(normalizedLabel);\n return (\n labels.some((label) => BEFORE_LABELS.has(label)) &&\n labels.some((label) => AFTER_LABELS.has(label))\n );\n}\n\n/** A wireframe child whose `surface` is wide enough to need full document width. */\nfunction isWideWireframeBlock(block: NestedBlock): boolean {\n if (block.type !== \"wireframe\") return false;\n const surface = (block.data as { surface?: unknown } | undefined)?.surface;\n return typeof surface === \"string\" && WIDE_WIREFRAME_SURFACES.has(surface);\n}\n\nfunction hasWideWireframe(columns: ColumnsColumn[]): boolean {\n return columns.some((column) => column.blocks.some(isWideWireframeBlock));\n}\n\nfunction isApiReferenceGroup(columns: ColumnsColumn[]): boolean {\n return (\n columns.length > 1 &&\n !isComparisonGroup(columns) &&\n columns.every(\n (column) =>\n column.blocks.length > 0 &&\n column.blocks.every((block) =>\n API_REFERENCE_BLOCK_TYPES.has(block.type),\n ),\n )\n );\n}\n\nfunction columnsLayoutClass(columns: ColumnsColumn[]): string {\n // API reference groups always read as a single stacked column.\n if (isApiReferenceGroup(columns)) return COLS_CLASS[1];\n // Wide wireframes (full desktop / browser surfaces) crop when squeezed into a\n // half-width comparison cell, so stack the states vertically and let each\n // frame use the full document width. Narrow surfaces stay side by side.\n if (hasWideWireframe(columns)) return COLS_CLASS[1];\n return gridColsClass(columns.length);\n}\n\nfunction isBlankRichTextBlock(block: NestedBlock): boolean {\n if (block.type !== \"rich-text\") return false;\n const data = block.data;\n if (!data || typeof data !== \"object\" || Array.isArray(data)) return false;\n const markdown = (data as { markdown?: unknown }).markdown;\n return typeof markdown === \"string\" && markdown.trim().length === 0;\n}\n\nfunction isEffectivelyEmptyRegion(blocks: NestedBlock[]): boolean {\n return (\n blocks.length === 0 ||\n (blocks.length === 1 && isBlankRichTextBlock(blocks[0]!))\n );\n}\n\nfunction areSameBlocks(a: NestedBlock[], b: NestedBlock[]): boolean {\n if (a === b) return true;\n try {\n return JSON.stringify(a) === JSON.stringify(b);\n } catch {\n return false;\n }\n}\n\n/** Read renderer: a responsive grid of columns, each child rendered read-only. */\nexport function ColumnsBlockReader({\n data,\n blockId,\n title,\n ctx,\n}: BlockReadProps<ColumnsData>) {\n return (\n <section className=\"plan-block\" data-block-id={blockId}>\n {title && <div className=\"plan-block-label\">{title}</div>}\n <div className={cn(\"grid gap-6\", columnsLayoutClass(data.columns))}>\n {data.columns.map((column) => (\n <div key={column.id} className=\"min-w-0\">\n {column.label && (\n <h4 className=\"plan-columns-label\">{column.label}</h4>\n )}\n <div>\n {column.blocks.map((child) => (\n <div key={child.id}>\n {ctx.renderBlock?.({ block: child, editing: false })}\n </div>\n ))}\n </div>\n </div>\n ))}\n </div>\n </section>\n );\n}\n\n/**\n * Editor: the same responsive grid, with child blocks rendered editable in\n * place through the app dispatcher. A child change updates that child within\n * its column and commits the whole\n * columns block — mirroring the legacy `tabs` onChange bubbling so the plan's\n * recursive `updateBlocks`/`findBlock` (`PlanContentRenderer`) keeps working.\n *\n * Renders BARE (no `plan-block` section / title): in edit mode the app's block\n * dispatcher already wraps registered editors in a titled `plan-block` section,\n * so wrapping again here would double-nest. The read renderer owns its own\n * section because read mode renders the spec directly.\n */\nexport function ColumnsBlockEditor({\n data,\n onChange,\n editable,\n blockId,\n ctx,\n}: BlockEditProps<ColumnsData>) {\n const commit = (columns: ColumnsColumn[]) => onChange({ columns });\n\n const updateChild = (columnId: string, child: NestedBlock) =>\n commit(\n data.columns.map((column) =>\n column.id === columnId\n ? {\n ...column,\n blocks: column.blocks.map((existing) =>\n existing.id === child.id ? child : existing,\n ),\n }\n : column,\n ),\n );\n\n return (\n <div data-columns-edit-block={blockId} className=\"grid gap-3\">\n <div className={cn(\"grid gap-6\", columnsLayoutClass(data.columns))}>\n {data.columns.map((column) => (\n <div key={column.id} className=\"min-w-0\">\n {column.label && (\n <h4 className=\"plan-columns-label\">{column.label}</h4>\n )}\n <div>\n {ctx.renderBlocksEditor\n ? ctx.renderBlocksEditor({\n blocks: column.blocks,\n onChange: (nextBlocks) => {\n if (areSameBlocks(nextBlocks, column.blocks)) return;\n onChange(\n {\n columns: data.columns.map((existing) =>\n existing.id === column.id\n ? {\n ...existing,\n blocks: nextBlocks,\n }\n : existing,\n ),\n },\n {\n containerRegion: {\n regionId: column.id,\n blocks: nextBlocks,\n },\n },\n );\n },\n editable,\n containerBlockId: blockId,\n regionId: column.id,\n regionLabel: column.label,\n })\n : column.blocks.map((child) => (\n <div key={child.id}>\n {ctx.renderBlock?.({\n block: child,\n editing: true,\n onChange: (next) => updateChild(column.id, next),\n })}\n </div>\n ))}\n </div>\n </div>\n ))}\n </div>\n </div>\n );\n}\n\n/**\n * The standard columns block spec (with React `Read`/`Edit`). Apps register this\n * in their browser registry. The schema + MDX config come from\n * `./columns.config.ts`, the exact same object server / agent code registers, so\n * rendering and source round-trip never drift.\n */\nexport const columnsBlock = defineBlock<ColumnsData>({\n type: \"columns\",\n schema: columnsSchema,\n mdx: columnsMdx,\n Read: ColumnsBlockReader,\n Edit: ColumnsBlockEditor,\n placement: [\"block\"],\n editSurface: \"container\",\n container: {\n regions: (data): BlockContainerRegion[] => data.columns,\n updateRegion: (data, regionId, blocks) => {\n const shouldRemoveRegion =\n data.columns.length > 1 && isEffectivelyEmptyRegion(blocks);\n\n return {\n columns: data.columns\n .map((column) =>\n column.id === regionId ? { ...column, blocks } : column,\n )\n .filter((column) => column.id !== regionId || !shouldRemoveRegion),\n };\n },\n addRegion: (data, afterRegionId) => {\n if (data.columns.length >= 4) return data;\n const nextColumn: ColumnsColumn = {\n id: newColId(),\n blocks: [],\n };\n if (!afterRegionId) return { columns: [...data.columns, nextColumn] };\n const afterIndex = data.columns.findIndex(\n (column) => column.id === afterRegionId,\n );\n if (afterIndex < 0) return { columns: [...data.columns, nextColumn] };\n return {\n columns: [\n ...data.columns.slice(0, afterIndex + 1),\n nextColumn,\n ...data.columns.slice(afterIndex + 1),\n ],\n };\n },\n removeRegion: (data, regionId) => {\n if (data.columns.length <= 1) return data;\n return {\n columns: data.columns.filter((column) => column.id !== regionId),\n };\n },\n },\n label: \"Columns\",\n icon: IconColumns,\n description:\n \"A multi-column side-by-side layout container; each column holds its own list of blocks. Ideal for before/after or current/target comparisons.\",\n empty: () => ({\n columns: [\n { id: newColId(), blocks: [] },\n { id: newColId(), blocks: [] },\n ],\n }),\n});\n"]}
|
|
1
|
+
{"version":3,"file":"columns.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/columns.tsx"],"names":[],"mappings":"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","sourcesContent":["import { IconColumns } from \"@tabler/icons-react\";\nimport { cn } from \"../../utils.js\";\nimport { defineBlock } from \"../types.js\";\nimport type {\n BlockContainerRegion,\n BlockEditProps,\n BlockReadProps,\n NestedBlock,\n} from \"../types.js\";\nimport {\n columnsSchema,\n columnsMdx,\n type ColumnsData,\n type ColumnsColumn,\n} from \"./columns.config.js\";\nimport { NarrowContainerProvider } from \"./narrow-container.js\";\n\n/**\n * Standard `columns` block: a multi-column side-by-side container whose columns\n * each hold a list of child blocks. A column's optional `label` renders as a\n * small heading above that column (e.g. `Before` / `After`), so a comparison\n * names its states outside the content — never baked into a child wireframe.\n *\n * Like `tabs`, child rendering flows through `ctx.renderBlock` — the app's own\n * block dispatcher — so registered children render via their spec and\n * unconverted children fall through the app's legacy switch. This is the\n * coexistence seam: the core columns block never has to know app-specific child\n * block types. The plan CSS classes (`plan-block`, `text-plan-*`) resolve\n * against the plan app's stylesheet at render time.\n */\n\n/** Mint a reasonably-unique column id without pulling a dep into core. */\nfunction newColId(): string {\n return `col-${Math.random().toString(36).slice(2, 10)}`;\n}\n\n/**\n * Tailwind grid-column classes keyed by the column count. The grid collapses to\n * a single column on small screens and fans out to the column count at `md`+, so\n * narrow viewports stack the panels instead of crushing them.\n */\nconst COLS_CLASS: Record<number, string> = {\n 1: \"grid-cols-1\",\n 2: \"grid-cols-1 md:grid-cols-2\",\n 3: \"grid-cols-1 md:grid-cols-3\",\n 4: \"grid-cols-1 md:grid-cols-4\",\n};\n\n/** Resolve the responsive grid class for a column count (clamped to 1–4). */\nfunction gridColsClass(count: number): string {\n return COLS_CLASS[Math.min(4, Math.max(1, count))] ?? COLS_CLASS[1];\n}\n\nconst API_REFERENCE_BLOCK_TYPES = new Set([\"api-endpoint\", \"openapi-spec\"]);\nconst BEFORE_LABELS = new Set([\"before\", \"old\", \"previous\", \"current\"]);\nconst AFTER_LABELS = new Set([\"after\", \"new\", \"next\", \"target\"]);\n\n// Wireframe surfaces that render too wide to survive a half-width comparison\n// column: a full desktop page or browser frame shrinks and crops when squeezed\n// side by side, so a comparison that holds one of these stacks vertically.\nconst WIDE_WIREFRAME_SURFACES = new Set([\"desktop\", \"browser\"]);\n\nfunction normalizedLabel(column: ColumnsColumn): string {\n return column.label?.trim().toLowerCase() ?? \"\";\n}\n\nfunction isComparisonGroup(columns: ColumnsColumn[]): boolean {\n const labels = columns.map(normalizedLabel);\n return (\n labels.some((label) => BEFORE_LABELS.has(label)) &&\n labels.some((label) => AFTER_LABELS.has(label))\n );\n}\n\n/** A wireframe child whose `surface` is wide enough to need full document width. */\nfunction isWideWireframeBlock(block: NestedBlock): boolean {\n if (block.type !== \"wireframe\") return false;\n const surface = (block.data as { surface?: unknown } | undefined)?.surface;\n return typeof surface === \"string\" && WIDE_WIREFRAME_SURFACES.has(surface);\n}\n\nfunction hasWideWireframe(columns: ColumnsColumn[]): boolean {\n return columns.some((column) => column.blocks.some(isWideWireframeBlock));\n}\n\nfunction isApiReferenceGroup(columns: ColumnsColumn[]): boolean {\n return (\n columns.length > 1 &&\n !isComparisonGroup(columns) &&\n columns.every(\n (column) =>\n column.blocks.length > 0 &&\n column.blocks.every((block) =>\n API_REFERENCE_BLOCK_TYPES.has(block.type),\n ),\n )\n );\n}\n\nfunction columnsLayoutClass(columns: ColumnsColumn[]): string {\n // API reference groups always read as a single stacked column.\n if (isApiReferenceGroup(columns)) return COLS_CLASS[1];\n // Wide wireframes (full desktop / browser surfaces) crop when squeezed into a\n // half-width comparison cell, so stack the states vertically and let each\n // frame use the full document width. Narrow surfaces stay side by side.\n if (hasWideWireframe(columns)) return COLS_CLASS[1];\n return gridColsClass(columns.length);\n}\n\nfunction isBlankRichTextBlock(block: NestedBlock): boolean {\n if (block.type !== \"rich-text\") return false;\n const data = block.data;\n if (!data || typeof data !== \"object\" || Array.isArray(data)) return false;\n const markdown = (data as { markdown?: unknown }).markdown;\n return typeof markdown === \"string\" && markdown.trim().length === 0;\n}\n\nfunction isEffectivelyEmptyRegion(blocks: NestedBlock[]): boolean {\n return (\n blocks.length === 0 ||\n (blocks.length === 1 && isBlankRichTextBlock(blocks[0]!))\n );\n}\n\nfunction areSameBlocks(a: NestedBlock[], b: NestedBlock[]): boolean {\n if (a === b) return true;\n try {\n return JSON.stringify(a) === JSON.stringify(b);\n } catch {\n return false;\n }\n}\n\n/** Read renderer: a responsive grid of columns, each child rendered read-only. */\nexport function ColumnsBlockReader({\n data,\n blockId,\n title,\n ctx,\n}: BlockReadProps<ColumnsData>) {\n // A column cell is width-constrained only when the layout actually renders\n // more than one column side by side; single-column / stacked layouts (API\n // reference groups, wide wireframes) keep full document width, so a nested\n // width-sensitive block (e.g. a diff) should NOT be forced to its narrow\n // default there.\n const narrow = columnsLayoutClass(data.columns) !== COLS_CLASS[1];\n return (\n <section className=\"plan-block\" data-block-id={blockId}>\n {title && <div className=\"plan-block-label\">{title}</div>}\n <div className={cn(\"grid gap-6\", columnsLayoutClass(data.columns))}>\n {data.columns.map((column) => {\n const body = (\n <div>\n {column.blocks.map((child) => (\n <div key={child.id}>\n {ctx.renderBlock?.({ block: child, editing: false })}\n </div>\n ))}\n </div>\n );\n return (\n <div key={column.id} className=\"min-w-0\">\n {column.label && (\n <h4 className=\"plan-columns-label\">{column.label}</h4>\n )}\n {narrow ? (\n <NarrowContainerProvider>{body}</NarrowContainerProvider>\n ) : (\n body\n )}\n </div>\n );\n })}\n </div>\n </section>\n );\n}\n\n/**\n * Editor: the same responsive grid, with child blocks rendered editable in\n * place through the app dispatcher. A child change updates that child within\n * its column and commits the whole\n * columns block — mirroring the legacy `tabs` onChange bubbling so the plan's\n * recursive `updateBlocks`/`findBlock` (`PlanContentRenderer`) keeps working.\n *\n * Renders BARE (no `plan-block` section / title): in edit mode the app's block\n * dispatcher already wraps registered editors in a titled `plan-block` section,\n * so wrapping again here would double-nest. The read renderer owns its own\n * section because read mode renders the spec directly.\n */\nexport function ColumnsBlockEditor({\n data,\n onChange,\n editable,\n blockId,\n ctx,\n}: BlockEditProps<ColumnsData>) {\n const commit = (columns: ColumnsColumn[]) => onChange({ columns });\n\n const updateChild = (columnId: string, child: NestedBlock) =>\n commit(\n data.columns.map((column) =>\n column.id === columnId\n ? {\n ...column,\n blocks: column.blocks.map((existing) =>\n existing.id === child.id ? child : existing,\n ),\n }\n : column,\n ),\n );\n\n return (\n <div data-columns-edit-block={blockId} className=\"grid gap-3\">\n <div className={cn(\"grid gap-6\", columnsLayoutClass(data.columns))}>\n {data.columns.map((column) => (\n <div key={column.id} className=\"min-w-0\">\n {column.label && (\n <h4 className=\"plan-columns-label\">{column.label}</h4>\n )}\n <div>\n {ctx.renderBlocksEditor\n ? ctx.renderBlocksEditor({\n blocks: column.blocks,\n onChange: (nextBlocks) => {\n if (areSameBlocks(nextBlocks, column.blocks)) return;\n onChange(\n {\n columns: data.columns.map((existing) =>\n existing.id === column.id\n ? {\n ...existing,\n blocks: nextBlocks,\n }\n : existing,\n ),\n },\n {\n containerRegion: {\n regionId: column.id,\n blocks: nextBlocks,\n },\n },\n );\n },\n editable,\n containerBlockId: blockId,\n regionId: column.id,\n regionLabel: column.label,\n })\n : column.blocks.map((child) => (\n <div key={child.id}>\n {ctx.renderBlock?.({\n block: child,\n editing: true,\n onChange: (next) => updateChild(column.id, next),\n })}\n </div>\n ))}\n </div>\n </div>\n ))}\n </div>\n </div>\n );\n}\n\n/**\n * The standard columns block spec (with React `Read`/`Edit`). Apps register this\n * in their browser registry. The schema + MDX config come from\n * `./columns.config.ts`, the exact same object server / agent code registers, so\n * rendering and source round-trip never drift.\n */\nexport const columnsBlock = defineBlock<ColumnsData>({\n type: \"columns\",\n schema: columnsSchema,\n mdx: columnsMdx,\n Read: ColumnsBlockReader,\n Edit: ColumnsBlockEditor,\n placement: [\"block\"],\n editSurface: \"container\",\n container: {\n regions: (data): BlockContainerRegion[] => data.columns,\n updateRegion: (data, regionId, blocks) => {\n const shouldRemoveRegion =\n data.columns.length > 1 && isEffectivelyEmptyRegion(blocks);\n\n return {\n columns: data.columns\n .map((column) =>\n column.id === regionId ? { ...column, blocks } : column,\n )\n .filter((column) => column.id !== regionId || !shouldRemoveRegion),\n };\n },\n addRegion: (data, afterRegionId) => {\n if (data.columns.length >= 4) return data;\n const nextColumn: ColumnsColumn = {\n id: newColId(),\n blocks: [],\n };\n if (!afterRegionId) return { columns: [...data.columns, nextColumn] };\n const afterIndex = data.columns.findIndex(\n (column) => column.id === afterRegionId,\n );\n if (afterIndex < 0) return { columns: [...data.columns, nextColumn] };\n return {\n columns: [\n ...data.columns.slice(0, afterIndex + 1),\n nextColumn,\n ...data.columns.slice(afterIndex + 1),\n ],\n };\n },\n removeRegion: (data, regionId) => {\n if (data.columns.length <= 1) return data;\n return {\n columns: data.columns.filter((column) => column.id !== regionId),\n };\n },\n },\n label: \"Columns\",\n icon: IconColumns,\n description:\n \"A multi-column side-by-side layout container; each column holds its own list of blocks. Ideal for before/after or current/target comparisons.\",\n empty: () => ({\n columns: [\n { id: newColId(), blocks: [] },\n { id: newColId(), blocks: [] },\n ],\n }),\n});\n"]}
|
|
@@ -42,7 +42,7 @@ export interface DiffData {
|
|
|
42
42
|
before: string;
|
|
43
43
|
/** New ("after") source. */
|
|
44
44
|
after: string;
|
|
45
|
-
/** Layout:
|
|
45
|
+
/** Layout: split (default, side-by-side) or unified (one column). */
|
|
46
46
|
mode?: DiffMode;
|
|
47
47
|
/** Line-anchored notes over the before/after sides. */
|
|
48
48
|
annotations?: DiffAnnotation[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff.config.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/diff.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAsDxB;;;;GAIG;AACH,MAAM,aAAa,GAAG,CAAC;KACpB,MAAM,EAAE;KACR,IAAI,EAAE;KACN,KAAK,CAAC,oBAAoB,EAAE;IAC3B,OAAO,EAAE,oDAAoD;CAC9D,CAAC;KACD,GAAG,CAAC,EAAE,CAAC,CAAC;AAEX,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC5C,KAAK,EAAE,aAAa;IACpB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;IAC5C,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC;CAC1C,CAA8B,CAAC;AAEhC,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC;IACjC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;IAC/C,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE;IAC9C,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC;IAC/B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC;IAC9B,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC7C,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE;CAC9D,CAAmC,CAAC;AAErC;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,OAAO,GAA6B;IAC/C,GAAG,EAAE,MAAM;IACX,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAClB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,WAAW,EAAE,IAAI,CAAC,WAAW;KAC9B,CAAC;IACF,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACrB,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC;QAClC,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC;QAClC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,CAAyB;QAClD,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE;QACpC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE;QAClC,WAAW,EAAE,KAAK,CAAC,KAAK,CAAiB,aAAa,CAAC,IAAI,EAAE;KAC9D,CAAC;CACH,CAAC","sourcesContent":["import { z } from \"zod\";\nimport type { BlockMdxConfig } from \"../types.js\";\n\n/**\n * Pure (React-free) part of the PLAN-SPECIFIC diff block: its data schema and MDX\n * round-trip config. Shared by the server MDX adapter (`plan-mdx.ts` via\n * `plan-block-registry.ts`) and the client spec (`planBlocks.tsx`). Keeping this\n * React-free means importing it into a server module never pulls React (or the\n * `diff` line-differ used only by the renderer) into the Nitro/SSR bundle.\n *\n * The schema MUST stay data-compatible with the `diff` branch of `planBlockSchema`\n * (`plan-content.ts`), and the MDX `tag` + flat attribute shape MUST match the\n * `<Diff filename language mode before after />` self-closing encoding so stored\n * `.mdx` round-trips. The `before`/`after` source lives in ATTRIBUTES (not MDX\n * children) — the shared `prop()` encoder round-trips multiline strings cleanly,\n * and keeping them attributes avoids the code being reflowed as prose.\n */\n\n/** Rendering layout for the diff body. */\nexport type DiffMode = \"unified\" | \"split\";\n\n/**\n * One line-anchored note attached to a diff, mirroring the `annotated-code`\n * annotation shape but adding `side`. The `lines` ref is 1-based against the\n * chosen side's source: `side: \"after\"` (the default) targets the new file's\n * line numbers, `side: \"before\"` the old file's. Optional ⇒ a diff without\n * annotations renders exactly as before.\n */\nexport interface DiffAnnotation {\n /** Which side the line ref targets; defaults to \"after\". */\n side?: \"before\" | \"after\";\n /** 1-based line ref against that side's text: \"13\" or \"13-15\" (inclusive). */\n lines: string;\n /** Optional short label shown before the note (e.g. \"Validation\"). */\n label?: string;\n /** The note prose (markdown), rendered through `ctx.renderMarkdown`. */\n note: string;\n}\n\nexport interface DiffData {\n /** Optional file path shown in the header (e.g. `src/add.ts`). */\n filename?: string;\n /** Optional language label rendered as a chip (e.g. `ts`). Purely cosmetic. */\n language?: string;\n /** Original (\"before\") source. */\n before: string;\n /** New (\"after\") source. */\n after: string;\n /** Layout:
|
|
1
|
+
{"version":3,"file":"diff.config.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/diff.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAsDxB;;;;GAIG;AACH,MAAM,aAAa,GAAG,CAAC;KACpB,MAAM,EAAE;KACR,IAAI,EAAE;KACN,KAAK,CAAC,oBAAoB,EAAE;IAC3B,OAAO,EAAE,oDAAoD;CAC9D,CAAC;KACD,GAAG,CAAC,EAAE,CAAC,CAAC;AAEX,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC5C,KAAK,EAAE,aAAa;IACpB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;IAC5C,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC;CAC1C,CAA8B,CAAC;AAEhC,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC;IACjC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;IAC/C,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE;IAC9C,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC;IAC/B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC;IAC9B,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC7C,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE;CAC9D,CAAmC,CAAC;AAErC;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,OAAO,GAA6B;IAC/C,GAAG,EAAE,MAAM;IACX,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAClB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,WAAW,EAAE,IAAI,CAAC,WAAW;KAC9B,CAAC;IACF,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACrB,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC;QAClC,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC;QAClC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,CAAyB;QAClD,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE;QACpC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE;QAClC,WAAW,EAAE,KAAK,CAAC,KAAK,CAAiB,aAAa,CAAC,IAAI,EAAE;KAC9D,CAAC;CACH,CAAC","sourcesContent":["import { z } from \"zod\";\nimport type { BlockMdxConfig } from \"../types.js\";\n\n/**\n * Pure (React-free) part of the PLAN-SPECIFIC diff block: its data schema and MDX\n * round-trip config. Shared by the server MDX adapter (`plan-mdx.ts` via\n * `plan-block-registry.ts`) and the client spec (`planBlocks.tsx`). Keeping this\n * React-free means importing it into a server module never pulls React (or the\n * `diff` line-differ used only by the renderer) into the Nitro/SSR bundle.\n *\n * The schema MUST stay data-compatible with the `diff` branch of `planBlockSchema`\n * (`plan-content.ts`), and the MDX `tag` + flat attribute shape MUST match the\n * `<Diff filename language mode before after />` self-closing encoding so stored\n * `.mdx` round-trips. The `before`/`after` source lives in ATTRIBUTES (not MDX\n * children) — the shared `prop()` encoder round-trips multiline strings cleanly,\n * and keeping them attributes avoids the code being reflowed as prose.\n */\n\n/** Rendering layout for the diff body. */\nexport type DiffMode = \"unified\" | \"split\";\n\n/**\n * One line-anchored note attached to a diff, mirroring the `annotated-code`\n * annotation shape but adding `side`. The `lines` ref is 1-based against the\n * chosen side's source: `side: \"after\"` (the default) targets the new file's\n * line numbers, `side: \"before\"` the old file's. Optional ⇒ a diff without\n * annotations renders exactly as before.\n */\nexport interface DiffAnnotation {\n /** Which side the line ref targets; defaults to \"after\". */\n side?: \"before\" | \"after\";\n /** 1-based line ref against that side's text: \"13\" or \"13-15\" (inclusive). */\n lines: string;\n /** Optional short label shown before the note (e.g. \"Validation\"). */\n label?: string;\n /** The note prose (markdown), rendered through `ctx.renderMarkdown`. */\n note: string;\n}\n\nexport interface DiffData {\n /** Optional file path shown in the header (e.g. `src/add.ts`). */\n filename?: string;\n /** Optional language label rendered as a chip (e.g. `ts`). Purely cosmetic. */\n language?: string;\n /** Original (\"before\") source. */\n before: string;\n /** New (\"after\") source. */\n after: string;\n /** Layout: split (default, side-by-side) or unified (one column). */\n mode?: DiffMode;\n /** Line-anchored notes over the before/after sides. */\n annotations?: DiffAnnotation[];\n}\n\n/**\n * A 1-based line reference: `\"3\"` or `\"3-5\"` (inclusive). Whitespace tolerant.\n * Matches the `annotated-code` line-ref schema so both blocks validate refs\n * identically.\n */\nconst lineRefSchema = z\n .string()\n .trim()\n .regex(/^\\d+(\\s*-\\s*\\d+)?$/, {\n message: 'lines must be a 1-based line ref like \"3\" or \"3-5\"',\n })\n .max(40);\n\nconst diffAnnotationSchema = z.object({\n side: z.enum([\"before\", \"after\"]).optional(),\n lines: lineRefSchema,\n label: z.string().trim().max(160).optional(),\n note: z.string().trim().min(1).max(4_000),\n}) as z.ZodType<DiffAnnotation>;\n\nexport const diffSchema = z.object({\n filename: z.string().trim().max(400).optional(),\n language: z.string().trim().max(40).optional(),\n before: z.string().max(100_000),\n after: z.string().max(100_000),\n mode: z.enum([\"unified\", \"split\"]).optional(),\n annotations: z.array(diffAnnotationSchema).max(80).optional(),\n}) as unknown as z.ZodType<DiffData>;\n\n/**\n * MDX config: `filename`, `language`, `mode`, `before`, `after`, and\n * `annotations` are flat attributes — the\n * `<Diff id … filename language mode before after annotations />` self-closing\n * form. Insertion order of `toAttrs` is the on-disk attribute order. `before`/\n * `after` are multiline string attributes (round-trip through the shared `prop()`\n * encoder); `annotations` is a JSON array attribute, encoded the same way as the\n * `annotated-code` block. `fromAttrs` mirrors a forgiving parse (`before ?? \"\"`,\n * `after ?? \"\"`, `annotations ?? []`, optional `filename`/`language`/`mode`\n * undefined when absent) so a plan missing an attribute still parses.\n */\nexport const diffMdx: BlockMdxConfig<DiffData> = {\n tag: \"Diff\",\n toAttrs: (data) => ({\n filename: data.filename,\n language: data.language,\n mode: data.mode,\n before: data.before,\n after: data.after,\n annotations: data.annotations,\n }),\n fromAttrs: (attrs) => ({\n filename: attrs.string(\"filename\"),\n language: attrs.string(\"language\"),\n mode: attrs.string(\"mode\") as DiffMode | undefined,\n before: attrs.string(\"before\") ?? \"\",\n after: attrs.string(\"after\") ?? \"\",\n annotations: attrs.array<DiffAnnotation>(\"annotations\") ?? [],\n }),\n};\n"]}
|