@agent-native/core 0.40.1 → 0.40.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/cli/skills.d.ts +1 -1
  2. package/dist/cli/skills.d.ts.map +1 -1
  3. package/dist/cli/skills.js +0 -1
  4. package/dist/cli/skills.js.map +1 -1
  5. package/dist/client/blocks/index.d.ts +0 -2
  6. package/dist/client/blocks/index.d.ts.map +1 -1
  7. package/dist/client/blocks/index.js +0 -2
  8. package/dist/client/blocks/index.js.map +1 -1
  9. package/dist/client/blocks/library/dev-doc-ui.d.ts +1 -2
  10. package/dist/client/blocks/library/dev-doc-ui.d.ts.map +1 -1
  11. package/dist/client/blocks/library/dev-doc-ui.js +1 -2
  12. package/dist/client/blocks/library/dev-doc-ui.js.map +1 -1
  13. package/dist/client/blocks/library/server-specs.d.ts.map +1 -1
  14. package/dist/client/blocks/library/server-specs.js +0 -10
  15. package/dist/client/blocks/library/server-specs.js.map +1 -1
  16. package/dist/client/blocks/library/specs.d.ts +1 -1
  17. package/dist/client/blocks/library/specs.d.ts.map +1 -1
  18. package/dist/client/blocks/library/specs.js +2 -28
  19. package/dist/client/blocks/library/specs.js.map +1 -1
  20. package/dist/client/blocks/server.d.ts +0 -1
  21. package/dist/client/blocks/server.d.ts.map +1 -1
  22. package/dist/client/blocks/server.js +0 -1
  23. package/dist/client/blocks/server.js.map +1 -1
  24. package/dist/client/blocks/types.d.ts +1 -1
  25. package/dist/client/blocks/types.js.map +1 -1
  26. package/dist/client/rich-markdown-editor/registrySlashCommands.d.ts.map +1 -1
  27. package/dist/client/rich-markdown-editor/registrySlashCommands.js +0 -1
  28. package/dist/client/rich-markdown-editor/registrySlashCommands.js.map +1 -1
  29. package/package.json +1 -1
  30. package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts +0 -6
  31. package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts.map +0 -1
  32. package/dist/client/blocks/library/AnnotatedCodeBlock.js +0 -135
  33. package/dist/client/blocks/library/AnnotatedCodeBlock.js.map +0 -1
  34. package/dist/client/blocks/library/annotated-code.config.d.ts +0 -58
  35. package/dist/client/blocks/library/annotated-code.config.d.ts.map +0 -1
  36. package/dist/client/blocks/library/annotated-code.config.js +0 -53
  37. package/dist/client/blocks/library/annotated-code.config.js.map +0 -1
@@ -17,7 +17,6 @@ const COMPACT_REGISTRY_BLOCK_DESCRIPTIONS = {
17
17
  diff: "Code diff",
18
18
  "file-tree": "File/change tree",
19
19
  "json-explorer": "JSON tree",
20
- "annotated-code": "Code walkthrough",
21
20
  };
22
21
  /**
23
22
  * Compact, user-facing slash-menu copy for structured registry blocks. The full
@@ -1 +1 @@
1
- {"version":3,"file":"registrySlashCommands.js","sourceRoot":"","sources":["../../../src/client/rich-markdown-editor/registrySlashCommands.ts"],"names":[],"mappings":"AAEA,MAAM,mCAAmC,GAA2B;IAClE,OAAO,EAAE,iBAAiB;IAC1B,OAAO,EAAE,gBAAgB;IACzB,SAAS,EAAE,eAAe;IAC1B,eAAe,EAAE,uBAAuB;IACxC,SAAS,EAAE,iBAAiB;IAC5B,KAAK,EAAE,eAAe;IACtB,aAAa,EAAE,eAAe;IAC9B,WAAW,EAAE,sBAAsB;IACnC,aAAa,EAAE,gBAAgB;IAC/B,IAAI,EAAE,oBAAoB;IAC1B,OAAO,EAAE,sBAAsB;IAC/B,OAAO,EAAE,iBAAiB;IAC1B,cAAc,EAAE,eAAe;IAC/B,cAAc,EAAE,kBAAkB;IAClC,YAAY,EAAE,YAAY;IAC1B,IAAI,EAAE,WAAW;IACjB,WAAW,EAAE,kBAAkB;IAC/B,eAAe,EAAE,WAAW;IAC5B,gBAAgB,EAAE,kBAAkB;CACrC,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,gCAAgC,CAC9C,IAA6C;IAE7C,OAAO,CACL,mCAAmC,CAAC,IAAI,CAAC,IAAI,CAAC;QAC9C,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAC7C,CAAC;AACJ,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,+BAA+B,CAC7C,IAAuD;IAEvD,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC;SAC7C,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,GAAG,CAAC;SACT,WAAW,EAAE,CAAC;AACnB,CAAC;AA+CD;;;;GAIG;AACH,MAAM,UAAU,4BAA4B,CAC1C,QAAuB,EACvB,OAA4D;IAE5D,MAAM,YAAY,GAChB,OAAO,CAAC,kBAAkB,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAC3E,OAAO,QAAQ;SACZ,IAAI,CAAC,OAAO,CAAC;SACb,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;SACrD,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,oBAAoB,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC;SACrE,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CACZ,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CACpE,CAAC;AACN,CAAC","sourcesContent":["import type { BlockRegistry, BlockSpec } from \"../blocks/index.js\";\n\nconst COMPACT_REGISTRY_BLOCK_DESCRIPTIONS: Record<string, string> = {\n callout: \"Emphasized note\",\n diagram: \"Inline diagram\",\n wireframe: \"Screen mockup\",\n \"question-form\": \"Interactive questions\",\n checklist: \"Checklist items\",\n table: \"Editable grid\",\n \"table-block\": \"Editable grid\",\n \"code-tabs\": \"Tabbed code snippets\",\n \"custom-html\": \"Sandboxed HTML\",\n tabs: \"Tabbed block group\",\n columns: \"Side-by-side columns\",\n mermaid: \"Mermaid diagram\",\n \"api-endpoint\": \"API reference\",\n \"openapi-spec\": \"OpenAPI document\",\n \"data-model\": \"ERD schema\",\n diff: \"Code diff\",\n \"file-tree\": \"File/change tree\",\n \"json-explorer\": \"JSON tree\",\n \"annotated-code\": \"Code walkthrough\",\n};\n\n/**\n * Compact, user-facing slash-menu copy for structured registry blocks. The full\n * registry description remains available through search text, but the visible\n * row should scan like a command palette, not a block reference page.\n */\nexport function getRegistryBlockSlashDescription(\n spec: Pick<BlockSpec, \"type\" | \"description\">,\n): string {\n return (\n COMPACT_REGISTRY_BLOCK_DESCRIPTIONS[spec.type] ??\n spec.description.trim().replace(/\\s+/g, \" \")\n );\n}\n\n/** Searchable text for registry block slash items, including raw type keywords. */\nexport function getRegistryBlockSlashSearchText(\n spec: Pick<BlockSpec, \"type\" | \"label\" | \"description\">,\n): string {\n return [spec.label, spec.description, spec.type]\n .filter(Boolean)\n .join(\" \")\n .toLowerCase();\n}\n\n/**\n * Shared builder for the registry-derived block slash commands both the plan and\n * content editors offer. Both apps take every `BlockSpec` whose `placement`\n * includes `\"block\"`, gate it by Notion-compatibility when the open document is\n * linked to a Notion page, and emit one slash item per surviving spec that\n * inserts that block's atom node. The only legitimate per-app differences are:\n *\n * - the ITEM SHAPE (plan uses a text-glyph `icon`, content a React component),\n * - the Notion-compat PREDICATE (plan unions in prose-only NFM analogs, content\n * reads the registry `notionCompatible` flag directly), and\n * - the INSERT behavior (plan inserts a `planBlock` node, content a\n * `registryBlock` node seeded with inline `__raw`).\n *\n * Those three are injected; everything else (the `list(\"block\")` source, the\n * Notion filter wiring, the one-item-per-spec mapping) lives here so adding a\n * new library block only touches the registry, never the slash builders.\n */\nexport interface BuildRegistryBlockSlashItemsOptions<TItem, TEditor> {\n /**\n * When `true`, only specs the predicate accepts are offered (the open document\n * is linked to a Notion page, so blocks that can't round-trip to NFM are\n * hidden). When unset/false, every block-placed spec is offered.\n */\n notionCompatibleOnly?: boolean;\n /**\n * Decide whether a spec round-trips to Notion. Defaults to the spec's own\n * `notionCompatible` flag (content's rule). Plan passes a predicate that unions\n * in prose-only NFM analogs not carried as registry flags.\n */\n isNotionCompatible?: (spec: BlockSpec) => boolean;\n /** Build one app-shaped slash item from a surviving block spec. */\n toItem: (spec: BlockSpec, insert: (editor: TEditor) => void) => TItem;\n /**\n * Optional app-level capability gate. Use this for blocks whose schema is\n * registered for parse/render compatibility but whose authoring experience is\n * not available in this editor yet.\n */\n includeSpec?: (spec: BlockSpec) => boolean;\n /**\n * Insert this spec's block atom into the editor. Plan inserts a `planBlock`\n * node; content inserts a `registryBlock` node seeded with inline `__raw`.\n */\n insertBlock: (editor: TEditor, spec: BlockSpec) => void;\n}\n\n/**\n * Build the registry-derived block slash items, shared by plan and content. Each\n * app prepends its own prose/base commands and wraps the result in its own item\n * type via {@link BuildRegistryBlockSlashItemsOptions.toItem}.\n */\nexport function buildRegistryBlockSlashItems<TItem, TEditor>(\n registry: BlockRegistry,\n options: BuildRegistryBlockSlashItemsOptions<TItem, TEditor>,\n): TItem[] {\n const isCompatible =\n options.isNotionCompatible ?? ((spec) => Boolean(spec.notionCompatible));\n return registry\n .list(\"block\")\n .filter((spec) => options.includeSpec?.(spec) ?? true)\n .filter((spec) => !options.notionCompatibleOnly || isCompatible(spec))\n .map((spec) =>\n options.toItem(spec, (editor) => options.insertBlock(editor, spec)),\n );\n}\n"]}
1
+ {"version":3,"file":"registrySlashCommands.js","sourceRoot":"","sources":["../../../src/client/rich-markdown-editor/registrySlashCommands.ts"],"names":[],"mappings":"AAEA,MAAM,mCAAmC,GAA2B;IAClE,OAAO,EAAE,iBAAiB;IAC1B,OAAO,EAAE,gBAAgB;IACzB,SAAS,EAAE,eAAe;IAC1B,eAAe,EAAE,uBAAuB;IACxC,SAAS,EAAE,iBAAiB;IAC5B,KAAK,EAAE,eAAe;IACtB,aAAa,EAAE,eAAe;IAC9B,WAAW,EAAE,sBAAsB;IACnC,aAAa,EAAE,gBAAgB;IAC/B,IAAI,EAAE,oBAAoB;IAC1B,OAAO,EAAE,sBAAsB;IAC/B,OAAO,EAAE,iBAAiB;IAC1B,cAAc,EAAE,eAAe;IAC/B,cAAc,EAAE,kBAAkB;IAClC,YAAY,EAAE,YAAY;IAC1B,IAAI,EAAE,WAAW;IACjB,WAAW,EAAE,kBAAkB;IAC/B,eAAe,EAAE,WAAW;CAC7B,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,gCAAgC,CAC9C,IAA6C;IAE7C,OAAO,CACL,mCAAmC,CAAC,IAAI,CAAC,IAAI,CAAC;QAC9C,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAC7C,CAAC;AACJ,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,+BAA+B,CAC7C,IAAuD;IAEvD,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC;SAC7C,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,GAAG,CAAC;SACT,WAAW,EAAE,CAAC;AACnB,CAAC;AA+CD;;;;GAIG;AACH,MAAM,UAAU,4BAA4B,CAC1C,QAAuB,EACvB,OAA4D;IAE5D,MAAM,YAAY,GAChB,OAAO,CAAC,kBAAkB,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAC3E,OAAO,QAAQ;SACZ,IAAI,CAAC,OAAO,CAAC;SACb,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;SACrD,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,oBAAoB,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC;SACrE,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CACZ,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CACpE,CAAC;AACN,CAAC","sourcesContent":["import type { BlockRegistry, BlockSpec } from \"../blocks/index.js\";\n\nconst COMPACT_REGISTRY_BLOCK_DESCRIPTIONS: Record<string, string> = {\n callout: \"Emphasized note\",\n diagram: \"Inline diagram\",\n wireframe: \"Screen mockup\",\n \"question-form\": \"Interactive questions\",\n checklist: \"Checklist items\",\n table: \"Editable grid\",\n \"table-block\": \"Editable grid\",\n \"code-tabs\": \"Tabbed code snippets\",\n \"custom-html\": \"Sandboxed HTML\",\n tabs: \"Tabbed block group\",\n columns: \"Side-by-side columns\",\n mermaid: \"Mermaid diagram\",\n \"api-endpoint\": \"API reference\",\n \"openapi-spec\": \"OpenAPI document\",\n \"data-model\": \"ERD schema\",\n diff: \"Code diff\",\n \"file-tree\": \"File/change tree\",\n \"json-explorer\": \"JSON tree\",\n};\n\n/**\n * Compact, user-facing slash-menu copy for structured registry blocks. The full\n * registry description remains available through search text, but the visible\n * row should scan like a command palette, not a block reference page.\n */\nexport function getRegistryBlockSlashDescription(\n spec: Pick<BlockSpec, \"type\" | \"description\">,\n): string {\n return (\n COMPACT_REGISTRY_BLOCK_DESCRIPTIONS[spec.type] ??\n spec.description.trim().replace(/\\s+/g, \" \")\n );\n}\n\n/** Searchable text for registry block slash items, including raw type keywords. */\nexport function getRegistryBlockSlashSearchText(\n spec: Pick<BlockSpec, \"type\" | \"label\" | \"description\">,\n): string {\n return [spec.label, spec.description, spec.type]\n .filter(Boolean)\n .join(\" \")\n .toLowerCase();\n}\n\n/**\n * Shared builder for the registry-derived block slash commands both the plan and\n * content editors offer. Both apps take every `BlockSpec` whose `placement`\n * includes `\"block\"`, gate it by Notion-compatibility when the open document is\n * linked to a Notion page, and emit one slash item per surviving spec that\n * inserts that block's atom node. The only legitimate per-app differences are:\n *\n * - the ITEM SHAPE (plan uses a text-glyph `icon`, content a React component),\n * - the Notion-compat PREDICATE (plan unions in prose-only NFM analogs, content\n * reads the registry `notionCompatible` flag directly), and\n * - the INSERT behavior (plan inserts a `planBlock` node, content a\n * `registryBlock` node seeded with inline `__raw`).\n *\n * Those three are injected; everything else (the `list(\"block\")` source, the\n * Notion filter wiring, the one-item-per-spec mapping) lives here so adding a\n * new library block only touches the registry, never the slash builders.\n */\nexport interface BuildRegistryBlockSlashItemsOptions<TItem, TEditor> {\n /**\n * When `true`, only specs the predicate accepts are offered (the open document\n * is linked to a Notion page, so blocks that can't round-trip to NFM are\n * hidden). When unset/false, every block-placed spec is offered.\n */\n notionCompatibleOnly?: boolean;\n /**\n * Decide whether a spec round-trips to Notion. Defaults to the spec's own\n * `notionCompatible` flag (content's rule). Plan passes a predicate that unions\n * in prose-only NFM analogs not carried as registry flags.\n */\n isNotionCompatible?: (spec: BlockSpec) => boolean;\n /** Build one app-shaped slash item from a surviving block spec. */\n toItem: (spec: BlockSpec, insert: (editor: TEditor) => void) => TItem;\n /**\n * Optional app-level capability gate. Use this for blocks whose schema is\n * registered for parse/render compatibility but whose authoring experience is\n * not available in this editor yet.\n */\n includeSpec?: (spec: BlockSpec) => boolean;\n /**\n * Insert this spec's block atom into the editor. Plan inserts a `planBlock`\n * node; content inserts a `registryBlock` node seeded with inline `__raw`.\n */\n insertBlock: (editor: TEditor, spec: BlockSpec) => void;\n}\n\n/**\n * Build the registry-derived block slash items, shared by plan and content. Each\n * app prepends its own prose/base commands and wraps the result in its own item\n * type via {@link BuildRegistryBlockSlashItemsOptions.toItem}.\n */\nexport function buildRegistryBlockSlashItems<TItem, TEditor>(\n registry: BlockRegistry,\n options: BuildRegistryBlockSlashItemsOptions<TItem, TEditor>,\n): TItem[] {\n const isCompatible =\n options.isNotionCompatible ?? ((spec) => Boolean(spec.notionCompatible));\n return registry\n .list(\"block\")\n .filter((spec) => options.includeSpec?.(spec) ?? true)\n .filter((spec) => !options.notionCompatibleOnly || isCompatible(spec))\n .map((spec) =>\n options.toItem(spec, (editor) => options.insertBlock(editor, spec)),\n );\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.40.1",
3
+ "version": "0.40.2",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=22"
@@ -1,6 +0,0 @@
1
- import type { BlockEditProps, BlockReadProps } from "../types.js";
2
- import type { AnnotatedCodeData } from "./annotated-code.config.js";
3
- declare function AnnotatedCodeRead({ data, blockId, title, summary, ctx, }: BlockReadProps<AnnotatedCodeData>): import("react/jsx-runtime").JSX.Element;
4
- declare function AnnotatedCodeEdit({ data, onChange, editable, }: BlockEditProps<AnnotatedCodeData>): import("react/jsx-runtime").JSX.Element;
5
- export { AnnotatedCodeRead, AnnotatedCodeEdit };
6
- //# sourceMappingURL=AnnotatedCodeBlock.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AnnotatedCodeBlock.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/AnnotatedCodeBlock.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,KAAK,EAEV,iBAAiB,EAClB,MAAM,4BAA4B,CAAC;AAsDpC,iBAAS,iBAAiB,CAAC,EACzB,IAAI,EACJ,OAAO,EACP,KAAK,EACL,OAAO,EACP,GAAG,GACJ,EAAE,cAAc,CAAC,iBAAiB,CAAC,2CAmOnC;AAMD,iBAAS,iBAAiB,CAAC,EACzB,IAAI,EACJ,QAAQ,EACR,QAAQ,GACT,EAAE,cAAc,CAAC,iBAAiB,CAAC,2CAiJnC;AAED,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,CAAC"}
@@ -1,135 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useMemo, useState } from "react";
3
- import { IconChevronRight, IconCode, IconPlus, IconTrash, } from "@tabler/icons-react";
4
- import { cn } from "../../utils.js";
5
- import { DevInput, DevLabel, DevTextarea } from "./dev-doc-ui.js";
6
- /**
7
- * Stripe-docs / Sourcegraph "explain this code" walkthrough block. The read
8
- * renderer shows the `code` as a line-numbered monospace surface; lines covered
9
- * by an annotation get a subtle highlight band and a numbered gutter marker. The
10
- * annotation notes render below, each with its `lines` range, optional `label`,
11
- * and markdown `note` (via `ctx.renderMarkdown`). Hovering a note highlights its
12
- * line range and vice-versa, so the line-anchoring is the differentiator. Lives
13
- * in core so any app can register the dev-doc block (no shadcn import).
14
- *
15
- * All colors are theme-aware: the surface uses the plan `--plan-code*`/`--plan-*`
16
- * tokens and the highlight/marker accents use Tailwind `light`/`dark:` pairs, so
17
- * the block reads correctly in BOTH light and dark mode.
18
- *
19
- * Editing is panel-driven (config-style, like the diff/HTML blocks): a monospace
20
- * code Textarea, filename/language Inputs, and add/remove-able annotation rows.
21
- */
22
- /* ── Line-ref parsing ──────────────────────────────────────────────────────── */
23
- /**
24
- * Parse a 1-based `lines` ref (`"3"` or `"3-5"`) into an inclusive `[start,end]`
25
- * pair, clamped to `[1, lineCount]`. Returns `null` for malformed or fully
26
- * out-of-range refs so callers can ignore them gracefully. A reversed range
27
- * (`"5-3"`) is normalized; a partially out-of-range range is clamped.
28
- */
29
- function parseLineRange(ref, lineCount) {
30
- const match = /^\s*(\d+)\s*(?:-\s*(\d+)\s*)?$/.exec(ref);
31
- if (!match)
32
- return null;
33
- let start = Number.parseInt(match[1], 10);
34
- let end = match[2] != null ? Number.parseInt(match[2], 10) : start;
35
- if (!Number.isFinite(start) || !Number.isFinite(end))
36
- return null;
37
- if (start > end)
38
- [start, end] = [end, start];
39
- // Fully outside the file → ignore.
40
- if (end < 1 || start > lineCount)
41
- return null;
42
- return { start: Math.max(1, start), end: Math.min(lineCount, end) };
43
- }
44
- /* ── Read ──────────────────────────────────────────────────────────────────── */
45
- function AnnotatedCodeRead({ data, blockId, title, summary, ctx, }) {
46
- const [activeIndex, setActiveIndex] = useState(null);
47
- const [notesOpen, setNotesOpen] = useState(true);
48
- const [revealed, setRevealed] = useState(() => new Set());
49
- const lines = useMemo(() => data.code.split("\n"), [data.code]);
50
- const lineCount = lines.length;
51
- const resolved = useMemo(() => (data.annotations ?? []).map((annotation, index) => ({
52
- index,
53
- marker: index + 1,
54
- annotation,
55
- range: parseLineRange(annotation.lines, lineCount),
56
- })), [data.annotations, lineCount]);
57
- // line number (1-based) → resolved annotations covering it (marker, active).
58
- const lineMarkers = useMemo(() => {
59
- const map = new Map();
60
- for (const item of resolved) {
61
- if (!item.range)
62
- continue;
63
- for (let n = item.range.start; n <= item.range.end; n += 1) {
64
- const list = map.get(n) ?? [];
65
- list.push(item);
66
- map.set(n, list);
67
- }
68
- }
69
- return map;
70
- }, [resolved]);
71
- const hasAnnotations = resolved.some((item) => item.range);
72
- const toggleRevealed = (index) => setRevealed((prev) => {
73
- const next = new Set(prev);
74
- if (next.has(index))
75
- next.delete(index);
76
- else
77
- next.add(index);
78
- return next;
79
- });
80
- return (_jsxs("section", { className: "plan-block", "data-block-id": blockId, children: [title && _jsx("div", { className: "plan-block-label", children: title }), _jsxs("div", { className: "overflow-hidden rounded-lg border border-plan-line bg-plan-code", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2 border-b border-plan-line bg-plan-block px-3 py-2", children: [_jsx(IconCode, { className: "size-4 shrink-0 text-plan-muted" }), _jsx("span", { className: "min-w-0 truncate font-mono text-sm font-medium text-plan-code-text", children: data.filename || "snippet" }), data.language && (_jsx("span", { className: "shrink-0 rounded border border-plan-line px-1.5 py-0.5 font-mono text-[11px] uppercase tracking-wide text-plan-muted", children: data.language }))] }), _jsx("div", { className: "overflow-x-auto", children: _jsx("div", { className: "min-w-full font-mono text-[13px] leading-6", children: lines.map((text, idx) => {
81
- const lineNo = idx + 1;
82
- const markers = lineMarkers.get(lineNo);
83
- const isAnnotated = !!markers?.length;
84
- const isActive = activeIndex != null &&
85
- !!markers?.some((m) => m.index === activeIndex);
86
- return (_jsxs("div", { className: cn("group flex w-full transition-colors", isActive
87
- ? "bg-amber-400/25 dark:bg-amber-300/20"
88
- : isAnnotated
89
- ? "bg-amber-400/10 dark:bg-amber-300/10"
90
- : ""), onMouseEnter: isAnnotated && markers
91
- ? () => setActiveIndex(markers[0].index)
92
- : undefined, onMouseLeave: isAnnotated ? () => setActiveIndex(null) : undefined, children: [_jsx("span", { className: "w-10 shrink-0 select-none px-2 text-right text-[11px] leading-6 tabular-nums text-plan-muted/70", children: lineNo }), _jsx("span", { className: cn("flex w-6 shrink-0 select-none items-center justify-center", isActive &&
93
- "shadow-[inset_2px_0_0_#f59e0b] dark:shadow-[inset_2px_0_0_#fcd34d]"), children: isAnnotated && markers && (_jsx("button", { type: "button", "data-plan-interactive": true, "aria-label": `Annotation ${markers[0].marker}`, onClick: () => {
94
- setNotesOpen(true);
95
- setRevealed((prev) => new Set(prev).add(markers[0].index));
96
- setActiveIndex(markers[0].index);
97
- }, onMouseEnter: () => setActiveIndex(markers[0].index), className: cn("flex size-[18px] cursor-pointer items-center justify-center rounded-full text-[10px] font-semibold leading-none transition-colors", isActive
98
- ? "bg-amber-500 text-white dark:bg-amber-400 dark:text-amber-950"
99
- : "bg-amber-500/20 text-amber-700 hover:bg-amber-500/30 dark:bg-amber-300/20 dark:text-amber-200 dark:hover:bg-amber-300/30"), children: markers[0].marker })) }), _jsx("pre", { className: "m-0 flex-1 overflow-visible whitespace-pre px-3 text-plan-code-text", children: text || " " })] }, lineNo));
100
- }) }) })] }), hasAnnotations && (_jsxs("div", { className: "mt-3", children: [_jsxs("button", { type: "button", "data-plan-interactive": true, onClick: () => setNotesOpen((open) => !open), "aria-expanded": notesOpen, className: "flex cursor-pointer items-center gap-1.5 text-xs font-medium text-plan-muted transition-colors hover:text-plan-text", children: [_jsx(IconChevronRight, { className: cn("size-3.5 transition-transform", notesOpen && "rotate-90") }), resolved.length, " annotation", resolved.length === 1 ? "" : "s"] }), notesOpen && (_jsx("ol", { className: "mt-2 flex flex-col gap-2", children: resolved.map((item) => {
101
- const isActive = activeIndex === item.index;
102
- const isRevealed = revealed.has(item.index);
103
- return (_jsxs("li", { onMouseEnter: () => setActiveIndex(item.index), onMouseLeave: () => setActiveIndex(null), className: cn("flex gap-3 rounded-md border px-3 py-2 transition-colors", isActive
104
- ? "border-amber-400/60 bg-amber-400/10 dark:border-amber-300/40 dark:bg-amber-300/10"
105
- : "border-plan-line bg-plan-block/40 hover:border-plan-line"), children: [_jsx("span", { "aria-hidden": true, className: cn("mt-0.5 flex size-[18px] shrink-0 items-center justify-center rounded-full text-[10px] font-semibold leading-none transition-colors", isActive
106
- ? "bg-amber-500 text-white dark:bg-amber-400 dark:text-amber-950"
107
- : "bg-amber-500/20 text-amber-700 dark:bg-amber-300/20 dark:text-amber-200"), children: item.marker }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("span", { className: "rounded bg-plan-block px-1.5 py-0.5 font-mono text-[11px] text-plan-muted", children: item.range
108
- ? item.range.start === item.range.end
109
- ? `line ${item.range.start}`
110
- : `lines ${item.range.start}–${item.range.end}`
111
- : `lines ${item.annotation.lines}` }), item.annotation.label && (_jsx("span", { className: "text-sm font-semibold text-plan-text", children: item.annotation.label })), !isRevealed && (_jsx("button", { type: "button", "data-plan-interactive": true, onClick: () => toggleRevealed(item.index), className: "ml-auto cursor-pointer text-[11px] font-medium text-plan-muted underline-offset-2 hover:text-plan-text hover:underline", children: "Show note" }))] }), isRevealed && (_jsx("div", { className: "plan-annotation-note mt-1 text-sm text-plan-text", children: ctx.renderMarkdown ? (ctx.renderMarkdown(item.annotation.note)) : (_jsx("p", { children: item.annotation.note })) }))] })] }, item.index));
112
- }) }))] })), summary && _jsx("p", { className: "mt-5 text-plan-muted", children: summary })] }));
113
- }
114
- /* ── Edit (panel) ──────────────────────────────────────────────────────────── */
115
- const codeAreaClass = "min-h-[160px] font-mono text-xs leading-5";
116
- function AnnotatedCodeEdit({ data, onChange, editable, }) {
117
- const annotations = data.annotations ?? [];
118
- const patch = (next) => onChange({ ...data, ...next });
119
- const updateAnnotation = (index, next) => patch({
120
- annotations: annotations.map((annotation, i) => i === index ? { ...annotation, ...next } : annotation),
121
- });
122
- const removeAnnotation = (index) => patch({ annotations: annotations.filter((_, i) => i !== index) });
123
- const addAnnotation = () => {
124
- if (annotations.length >= 80)
125
- return; // schema max
126
- patch({
127
- annotations: [...annotations, { lines: "1", label: "", note: "" }],
128
- });
129
- };
130
- return (_jsxs("div", { className: "flex flex-col gap-3", "data-plan-interactive": true, children: [_jsxs("div", { className: "grid gap-3 sm:grid-cols-2", children: [_jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(DevLabel, { htmlFor: "annotated-code-filename", className: "text-xs", children: "Filename" }), _jsx(DevInput, { id: "annotated-code-filename", value: data.filename ?? "", placeholder: "src/server/auth.ts", disabled: !editable, onChange: (event) => patch({ filename: event.target.value || undefined }) })] }), _jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(DevLabel, { htmlFor: "annotated-code-language", className: "text-xs", children: "Language" }), _jsx(DevInput, { id: "annotated-code-language", value: data.language ?? "", placeholder: "ts", disabled: !editable, onChange: (event) => patch({ language: event.target.value || undefined }) })] })] }), _jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(DevLabel, { htmlFor: "annotated-code-code", className: "text-xs", children: "Code" }), _jsx(DevTextarea, { id: "annotated-code-code", spellCheck: false, className: codeAreaClass, value: data.code, disabled: !editable, onChange: (event) => patch({ code: event.target.value }) })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx(DevLabel, { className: "text-xs", children: "Annotations" }), editable && annotations.length < 80 && (_jsxs("button", { type: "button", "data-plan-interactive": true, onClick: addAnnotation, className: "flex cursor-pointer items-center gap-1 rounded-md px-2 py-1 text-xs font-medium text-plan-muted transition-colors hover:bg-plan-block/60 hover:text-plan-text", children: [_jsx(IconPlus, { className: "size-3.5" }), "Add annotation"] }))] }), annotations.length === 0 && (_jsx("p", { className: "text-xs text-plan-muted", children: "No annotations yet. Add one to anchor a note to a line range." })), annotations.map((annotation, index) => (_jsxs("div", { className: "flex flex-col gap-2 rounded-md border border-plan-line bg-plan-block/30 p-2", children: [_jsxs("div", { className: "grid gap-2 sm:grid-cols-[120px_minmax(0,1fr)_auto]", children: [_jsx(DevInput, { "aria-label": `Annotation ${index + 1} lines`, value: annotation.lines, placeholder: "3-5", disabled: !editable, onChange: (event) => updateAnnotation(index, { lines: event.target.value }) }), _jsx(DevInput, { "aria-label": `Annotation ${index + 1} label`, value: annotation.label ?? "", placeholder: "Label (optional)", disabled: !editable, onChange: (event) => updateAnnotation(index, {
131
- label: event.target.value || undefined,
132
- }) }), editable && (_jsx("button", { type: "button", "data-plan-interactive": true, "aria-label": `Remove annotation ${index + 1}`, onClick: () => removeAnnotation(index), className: "flex size-9 shrink-0 cursor-pointer items-center justify-center rounded-md text-plan-muted transition-colors hover:bg-muted hover:text-foreground", children: _jsx(IconTrash, { className: "size-4" }) }))] }), _jsx(DevTextarea, { "aria-label": `Annotation ${index + 1} note`, className: "min-h-[60px] text-sm", value: annotation.note, placeholder: "Explain what these lines do\u2026", disabled: !editable, onChange: (event) => updateAnnotation(index, { note: event.target.value }) })] }, index)))] })] }));
133
- }
134
- export { AnnotatedCodeRead, AnnotatedCodeEdit };
135
- //# sourceMappingURL=AnnotatedCodeBlock.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AnnotatedCodeBlock.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/AnnotatedCodeBlock.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useMemo, useState } from \"react\";\nimport {\n IconChevronRight,\n IconCode,\n IconPlus,\n IconTrash,\n} from \"@tabler/icons-react\";\nimport { cn } from \"../../utils.js\";\nimport type { BlockEditProps, BlockReadProps } from \"../types.js\";\nimport type {\n AnnotatedCodeAnnotation,\n AnnotatedCodeData,\n} from \"./annotated-code.config.js\";\nimport { DevInput, DevLabel, DevTextarea } from \"./dev-doc-ui.js\";\n\n/**\n * Stripe-docs / Sourcegraph \"explain this code\" walkthrough block. The read\n * renderer shows the `code` as a line-numbered monospace surface; lines covered\n * by an annotation get a subtle highlight band and a numbered gutter marker. The\n * annotation notes render below, each with its `lines` range, optional `label`,\n * and markdown `note` (via `ctx.renderMarkdown`). Hovering a note highlights its\n * line range and vice-versa, so the line-anchoring is the differentiator. Lives\n * in core so any app can register the dev-doc block (no shadcn import).\n *\n * All colors are theme-aware: the surface uses the plan `--plan-code*`/`--plan-*`\n * tokens and the highlight/marker accents use Tailwind `light`/`dark:` pairs, so\n * the block reads correctly in BOTH light and dark mode.\n *\n * Editing is panel-driven (config-style, like the diff/HTML blocks): a monospace\n * code Textarea, filename/language Inputs, and add/remove-able annotation rows.\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 */\nfunction 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\ninterface ResolvedAnnotation {\n /** Index in the original `annotations` array (stable hover key). */\n index: number;\n /** 1-based marker number shown in the gutter + note list. */\n marker: number;\n annotation: AnnotatedCodeAnnotation;\n range: { start: number; end: number } | null;\n}\n\n/* ── Read ──────────────────────────────────────────────────────────────────── */\n\nfunction AnnotatedCodeRead({\n data,\n blockId,\n title,\n summary,\n ctx,\n}: BlockReadProps<AnnotatedCodeData>) {\n const [activeIndex, setActiveIndex] = useState<number | null>(null);\n const [notesOpen, setNotesOpen] = useState(true);\n const [revealed, setRevealed] = useState<Set<number>>(() => new Set());\n\n const lines = useMemo(() => data.code.split(\"\\n\"), [data.code]);\n const lineCount = lines.length;\n\n const resolved = useMemo<ResolvedAnnotation[]>(\n () =>\n (data.annotations ?? []).map((annotation, index) => ({\n index,\n marker: index + 1,\n annotation,\n range: parseLineRange(annotation.lines, lineCount),\n })),\n [data.annotations, lineCount],\n );\n\n // line number (1-based) → resolved annotations covering it (marker, active).\n const lineMarkers = useMemo(() => {\n const map = new Map<number, ResolvedAnnotation[]>();\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 }, [resolved]);\n\n const hasAnnotations = resolved.some((item) => item.range);\n\n const toggleRevealed = (index: number) =>\n setRevealed((prev) => {\n const next = new Set(prev);\n if (next.has(index)) next.delete(index);\n else next.add(index);\n return next;\n });\n\n return (\n <section className=\"plan-block\" data-block-id={blockId}>\n {title && <div className=\"plan-block-label\">{title}</div>}\n <div className=\"overflow-hidden rounded-lg border border-plan-line bg-plan-code\">\n {/* Header: filename + optional language chip. */}\n <div className=\"flex flex-wrap items-center gap-2 border-b border-plan-line bg-plan-block px-3 py-2\">\n <IconCode className=\"size-4 shrink-0 text-plan-muted\" />\n <span className=\"min-w-0 truncate font-mono text-sm font-medium text-plan-code-text\">\n {data.filename || \"snippet\"}\n </span>\n {data.language && (\n <span className=\"shrink-0 rounded border border-plan-line px-1.5 py-0.5 font-mono text-[11px] uppercase tracking-wide text-plan-muted\">\n {data.language}\n </span>\n )}\n </div>\n\n {/* Code surface — line-numbered, with highlight bands + gutter markers. */}\n <div className=\"overflow-x-auto\">\n <div className=\"min-w-full font-mono text-[13px] leading-6\">\n {lines.map((text, idx) => {\n const lineNo = idx + 1;\n const markers = lineMarkers.get(lineNo);\n const isAnnotated = !!markers?.length;\n const isActive =\n activeIndex != null &&\n !!markers?.some((m) => m.index === activeIndex);\n return (\n <div\n key={lineNo}\n className={cn(\n \"group flex w-full transition-colors\",\n isActive\n ? \"bg-amber-400/25 dark:bg-amber-300/20\"\n : isAnnotated\n ? \"bg-amber-400/10 dark:bg-amber-300/10\"\n : \"\",\n )}\n onMouseEnter={\n isAnnotated && markers\n ? () => setActiveIndex(markers[0].index)\n : undefined\n }\n onMouseLeave={\n isAnnotated ? () => setActiveIndex(null) : undefined\n }\n >\n {/* Gutter: line number. */}\n <span className=\"w-10 shrink-0 select-none px-2 text-right text-[11px] leading-6 tabular-nums text-plan-muted/70\">\n {lineNo}\n </span>\n {/* Marker rail: numbered dot when annotated, accent bar when active. */}\n <span\n className={cn(\n \"flex w-6 shrink-0 select-none items-center justify-center\",\n isActive &&\n \"shadow-[inset_2px_0_0_#f59e0b] dark:shadow-[inset_2px_0_0_#fcd34d]\",\n )}\n >\n {isAnnotated && markers && (\n <button\n type=\"button\"\n data-plan-interactive\n aria-label={`Annotation ${markers[0].marker}`}\n onClick={() => {\n setNotesOpen(true);\n setRevealed((prev) =>\n new Set(prev).add(markers[0].index),\n );\n setActiveIndex(markers[0].index);\n }}\n onMouseEnter={() => setActiveIndex(markers[0].index)}\n className={cn(\n \"flex size-[18px] cursor-pointer items-center justify-center rounded-full text-[10px] font-semibold leading-none transition-colors\",\n isActive\n ? \"bg-amber-500 text-white dark:bg-amber-400 dark:text-amber-950\"\n : \"bg-amber-500/20 text-amber-700 hover:bg-amber-500/30 dark:bg-amber-300/20 dark:text-amber-200 dark:hover:bg-amber-300/30\",\n )}\n >\n {markers[0].marker}\n </button>\n )}\n </span>\n <pre className=\"m-0 flex-1 overflow-visible whitespace-pre px-3 text-plan-code-text\">\n {text || \" \"}\n </pre>\n </div>\n );\n })}\n </div>\n </div>\n </div>\n\n {/* Annotation notes — line-anchored, collapsible (progressive disclosure). */}\n {hasAnnotations && (\n <div className=\"mt-3\">\n <button\n type=\"button\"\n data-plan-interactive\n onClick={() => setNotesOpen((open) => !open)}\n aria-expanded={notesOpen}\n className=\"flex cursor-pointer items-center gap-1.5 text-xs font-medium text-plan-muted transition-colors hover:text-plan-text\"\n >\n <IconChevronRight\n className={cn(\n \"size-3.5 transition-transform\",\n notesOpen && \"rotate-90\",\n )}\n />\n {resolved.length} annotation{resolved.length === 1 ? \"\" : \"s\"}\n </button>\n {notesOpen && (\n <ol className=\"mt-2 flex flex-col gap-2\">\n {resolved.map((item) => {\n const isActive = activeIndex === item.index;\n const isRevealed = revealed.has(item.index);\n return (\n <li\n key={item.index}\n onMouseEnter={() => setActiveIndex(item.index)}\n onMouseLeave={() => setActiveIndex(null)}\n className={cn(\n \"flex gap-3 rounded-md border px-3 py-2 transition-colors\",\n isActive\n ? \"border-amber-400/60 bg-amber-400/10 dark:border-amber-300/40 dark:bg-amber-300/10\"\n : \"border-plan-line bg-plan-block/40 hover:border-plan-line\",\n )}\n >\n <span\n aria-hidden\n className={cn(\n \"mt-0.5 flex size-[18px] shrink-0 items-center justify-center rounded-full text-[10px] font-semibold leading-none transition-colors\",\n isActive\n ? \"bg-amber-500 text-white dark:bg-amber-400 dark:text-amber-950\"\n : \"bg-amber-500/20 text-amber-700 dark:bg-amber-300/20 dark:text-amber-200\",\n )}\n >\n {item.marker}\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"rounded bg-plan-block px-1.5 py-0.5 font-mono text-[11px] text-plan-muted\">\n {item.range\n ? item.range.start === item.range.end\n ? `line ${item.range.start}`\n : `lines ${item.range.start}–${item.range.end}`\n : `lines ${item.annotation.lines}`}\n </span>\n {item.annotation.label && (\n <span className=\"text-sm font-semibold text-plan-text\">\n {item.annotation.label}\n </span>\n )}\n {!isRevealed && (\n <button\n type=\"button\"\n data-plan-interactive\n onClick={() => toggleRevealed(item.index)}\n className=\"ml-auto cursor-pointer text-[11px] font-medium text-plan-muted underline-offset-2 hover:text-plan-text hover:underline\"\n >\n Show note\n </button>\n )}\n </div>\n {isRevealed && (\n <div className=\"plan-annotation-note mt-1 text-sm text-plan-text\">\n {ctx.renderMarkdown ? (\n ctx.renderMarkdown(item.annotation.note)\n ) : (\n <p>{item.annotation.note}</p>\n )}\n </div>\n )}\n </div>\n </li>\n );\n })}\n </ol>\n )}\n </div>\n )}\n {summary && <p className=\"mt-5 text-plan-muted\">{summary}</p>}\n </section>\n );\n}\n\n/* ── Edit (panel) ──────────────────────────────────────────────────────────── */\n\nconst codeAreaClass = \"min-h-[160px] font-mono text-xs leading-5\";\n\nfunction AnnotatedCodeEdit({\n data,\n onChange,\n editable,\n}: BlockEditProps<AnnotatedCodeData>) {\n const annotations = data.annotations ?? [];\n const patch = (next: Partial<AnnotatedCodeData>) =>\n onChange({ ...data, ...next });\n\n const updateAnnotation = (\n index: number,\n next: Partial<AnnotatedCodeAnnotation>,\n ) =>\n patch({\n annotations: annotations.map((annotation, i) =>\n i === index ? { ...annotation, ...next } : annotation,\n ),\n });\n\n const removeAnnotation = (index: number) =>\n patch({ annotations: annotations.filter((_, i) => i !== index) });\n\n const addAnnotation = () => {\n if (annotations.length >= 80) return; // schema max\n patch({\n annotations: [...annotations, { lines: \"1\", label: \"\", note: \"\" }],\n });\n };\n\n return (\n <div className=\"flex flex-col gap-3\" data-plan-interactive>\n <div className=\"grid gap-3 sm:grid-cols-2\">\n <div className=\"flex flex-col gap-1.5\">\n <DevLabel htmlFor=\"annotated-code-filename\" className=\"text-xs\">\n Filename\n </DevLabel>\n <DevInput\n id=\"annotated-code-filename\"\n value={data.filename ?? \"\"}\n placeholder=\"src/server/auth.ts\"\n disabled={!editable}\n onChange={(event) =>\n patch({ filename: event.target.value || undefined })\n }\n />\n </div>\n <div className=\"flex flex-col gap-1.5\">\n <DevLabel htmlFor=\"annotated-code-language\" className=\"text-xs\">\n Language\n </DevLabel>\n <DevInput\n id=\"annotated-code-language\"\n value={data.language ?? \"\"}\n placeholder=\"ts\"\n disabled={!editable}\n onChange={(event) =>\n patch({ language: event.target.value || undefined })\n }\n />\n </div>\n </div>\n\n <div className=\"flex flex-col gap-1.5\">\n <DevLabel htmlFor=\"annotated-code-code\" className=\"text-xs\">\n Code\n </DevLabel>\n <DevTextarea\n id=\"annotated-code-code\"\n spellCheck={false}\n className={codeAreaClass}\n value={data.code}\n disabled={!editable}\n onChange={(event) => patch({ code: event.target.value })}\n />\n </div>\n\n <div className=\"flex flex-col gap-2\">\n <div className=\"flex items-center justify-between\">\n <DevLabel className=\"text-xs\">Annotations</DevLabel>\n {editable && annotations.length < 80 && (\n <button\n type=\"button\"\n data-plan-interactive\n onClick={addAnnotation}\n className=\"flex cursor-pointer items-center gap-1 rounded-md px-2 py-1 text-xs font-medium text-plan-muted transition-colors hover:bg-plan-block/60 hover:text-plan-text\"\n >\n <IconPlus className=\"size-3.5\" />\n Add annotation\n </button>\n )}\n </div>\n {annotations.length === 0 && (\n <p className=\"text-xs text-plan-muted\">\n No annotations yet. Add one to anchor a note to a line range.\n </p>\n )}\n {annotations.map((annotation, index) => (\n <div\n key={index}\n className=\"flex flex-col gap-2 rounded-md border border-plan-line bg-plan-block/30 p-2\"\n >\n <div className=\"grid gap-2 sm:grid-cols-[120px_minmax(0,1fr)_auto]\">\n <DevInput\n aria-label={`Annotation ${index + 1} lines`}\n value={annotation.lines}\n placeholder=\"3-5\"\n disabled={!editable}\n onChange={(event) =>\n updateAnnotation(index, { lines: event.target.value })\n }\n />\n <DevInput\n aria-label={`Annotation ${index + 1} label`}\n value={annotation.label ?? \"\"}\n placeholder=\"Label (optional)\"\n disabled={!editable}\n onChange={(event) =>\n updateAnnotation(index, {\n label: event.target.value || undefined,\n })\n }\n />\n {editable && (\n <button\n type=\"button\"\n data-plan-interactive\n aria-label={`Remove annotation ${index + 1}`}\n onClick={() => removeAnnotation(index)}\n className=\"flex size-9 shrink-0 cursor-pointer items-center justify-center rounded-md text-plan-muted transition-colors hover:bg-muted hover:text-foreground\"\n >\n <IconTrash className=\"size-4\" />\n </button>\n )}\n </div>\n <DevTextarea\n aria-label={`Annotation ${index + 1} note`}\n className=\"min-h-[60px] text-sm\"\n value={annotation.note}\n placeholder=\"Explain what these lines do…\"\n disabled={!editable}\n onChange={(event) =>\n updateAnnotation(index, { note: event.target.value })\n }\n />\n </div>\n ))}\n </div>\n </div>\n );\n}\n\nexport { AnnotatedCodeRead, AnnotatedCodeEdit };\n"]}
@@ -1,58 +0,0 @@
1
- import { z } from "zod";
2
- import type { BlockMdxConfig } from "../types.js";
3
- /**
4
- * Pure (React-free) part of the PLAN-SPECIFIC `annotated-code` block: its data
5
- * schema and MDX round-trip config. Shared by the server MDX adapter
6
- * (`plan-mdx.ts` via `plan-block-registry.ts`) and the client spec
7
- * (`planBlocks.tsx`). Keeping this React-free means importing it into a server
8
- * module never pulls React into the Nitro/SSR bundle.
9
- *
10
- * The block renders a Stripe-docs / Sourcegraph "explain this code" style
11
- * walkthrough: a line-numbered monospace code surface where annotated line
12
- * ranges get a highlight band + numbered gutter marker, paired with a list of
13
- * line-anchored notes (each note targets a 1-based `lines` ref like `"3"` or
14
- * `"3-5"`). Hovering a note highlights its lines, and vice-versa.
15
- *
16
- * The schema MUST stay data-compatible with the `annotated-code` branch of
17
- * `planBlockSchema` (`plan-content.ts`), and the MDX `tag` (`AnnotatedCode`) +
18
- * flat attribute shape MUST match that inline member so stored `.mdx`
19
- * round-trips. `code` is a multiline string ATTRIBUTE (the shared `prop()`
20
- * encoder round-trips multiline strings cleanly, and keeping it an attribute —
21
- * not MDX children — avoids the source being reflowed as prose); `annotations`
22
- * is a JSON array attribute.
23
- */
24
- /** One line-anchored note over the code. */
25
- export interface AnnotatedCodeAnnotation {
26
- /** 1-based line reference: a single line `"3"` or an inclusive range `"3-5"`. */
27
- lines: string;
28
- /** Optional short label shown before the note (e.g. "Lookup"). */
29
- label?: string;
30
- /** The note prose (markdown), rendered through `ctx.renderMarkdown`. */
31
- note: string;
32
- }
33
- export interface AnnotatedCodeData {
34
- /** Optional file path shown in the header (e.g. `src/server/auth.ts`). */
35
- filename?: string;
36
- /** Optional language label (e.g. `ts`). Cosmetic chip + future highlighting. */
37
- language?: string;
38
- /** The source the walkthrough annotates. Rendered line-numbered. */
39
- code: string;
40
- /** Line-anchored notes. */
41
- annotations?: AnnotatedCodeAnnotation[];
42
- }
43
- /**
44
- * Data-compatible with the inline `annotated-code` member of `planBlockSchema`
45
- * (`plan-content.ts`). `code` is the only required field; `annotations` defaults
46
- * to omitted so a fresh block validates from `{ code: "…" }`.
47
- */
48
- export declare const annotatedCodeSchema: z.ZodType<AnnotatedCodeData>;
49
- /**
50
- * MDX config: `<AnnotatedCode filename language code annotations />` self-closing
51
- * form. Insertion order of `toAttrs` is the on-disk attribute order. `code` is a
52
- * multiline string attribute (round-trips through the shared `prop()` encoder);
53
- * `annotations` is a JSON array attribute. `fromAttrs` mirrors a forgiving parse
54
- * (`code ?? ""`, `annotations ?? []`, optional `filename`/`language` undefined
55
- * when absent) so a plan missing an attribute still parses.
56
- */
57
- export declare const annotatedCodeMdx: BlockMdxConfig<AnnotatedCodeData>;
58
- //# sourceMappingURL=annotated-code.config.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"annotated-code.config.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/annotated-code.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,4CAA4C;AAC5C,MAAM,WAAW,uBAAuB;IACtC,iFAAiF;IACjF,KAAK,EAAE,MAAM,CAAC;IACd,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,iBAAiB;IAChC,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAC;IACb,2BAA2B;IAC3B,WAAW,CAAC,EAAE,uBAAuB,EAAE,CAAC;CACzC;AAqBD;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAKf,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAE9C;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,EAAE,cAAc,CAAC,iBAAiB,CAc9D,CAAC"}
@@ -1,53 +0,0 @@
1
- import { z } from "zod";
2
- /**
3
- * A 1-based line reference: `"3"` or `"3-5"` (inclusive). Whitespace tolerant.
4
- * The renderer parses this defensively too, but the schema rejects clearly
5
- * malformed refs so authored/agent-generated data stays clean.
6
- */
7
- const lineRefSchema = z
8
- .string()
9
- .trim()
10
- .regex(/^\d+(\s*-\s*\d+)?$/, {
11
- message: 'lines must be a 1-based line ref like "3" or "3-5"',
12
- })
13
- .max(40);
14
- const annotationSchema = z.object({
15
- lines: lineRefSchema,
16
- label: z.string().trim().max(160).optional(),
17
- note: z.string().trim().min(1).max(4_000),
18
- });
19
- /**
20
- * Data-compatible with the inline `annotated-code` member of `planBlockSchema`
21
- * (`plan-content.ts`). `code` is the only required field; `annotations` defaults
22
- * to omitted so a fresh block validates from `{ code: "…" }`.
23
- */
24
- export const annotatedCodeSchema = z.object({
25
- filename: z.string().trim().max(400).optional(),
26
- language: z.string().trim().max(40).optional(),
27
- code: z.string().max(100_000),
28
- annotations: z.array(annotationSchema).max(80).optional(),
29
- });
30
- /**
31
- * MDX config: `<AnnotatedCode filename language code annotations />` self-closing
32
- * form. Insertion order of `toAttrs` is the on-disk attribute order. `code` is a
33
- * multiline string attribute (round-trips through the shared `prop()` encoder);
34
- * `annotations` is a JSON array attribute. `fromAttrs` mirrors a forgiving parse
35
- * (`code ?? ""`, `annotations ?? []`, optional `filename`/`language` undefined
36
- * when absent) so a plan missing an attribute still parses.
37
- */
38
- export const annotatedCodeMdx = {
39
- tag: "AnnotatedCode",
40
- toAttrs: (data) => ({
41
- filename: data.filename,
42
- language: data.language,
43
- code: data.code,
44
- annotations: data.annotations,
45
- }),
46
- fromAttrs: (attrs) => ({
47
- filename: attrs.string("filename"),
48
- language: attrs.string("language"),
49
- code: attrs.string("code") ?? "",
50
- annotations: attrs.array("annotations") ?? [],
51
- }),
52
- };
53
- //# sourceMappingURL=annotated-code.config.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"annotated-code.config.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/annotated-code.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA8CxB;;;;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,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChC,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,CAAuC,CAAC;AAEzC;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,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,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC;IAC7B,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE;CAC1D,CAA4C,CAAC;AAE9C;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAsC;IACjE,GAAG,EAAE,eAAe;IACpB,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,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,CAAC,IAAI,EAAE;QAChC,WAAW,EAAE,KAAK,CAAC,KAAK,CAA0B,aAAa,CAAC,IAAI,EAAE;KACvE,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 `annotated-code` block: its data\n * schema and MDX round-trip config. Shared by the server MDX adapter\n * (`plan-mdx.ts` via `plan-block-registry.ts`) and the client spec\n * (`planBlocks.tsx`). Keeping this React-free means importing it into a server\n * module never pulls React into the Nitro/SSR bundle.\n *\n * The block renders a Stripe-docs / Sourcegraph \"explain this code\" style\n * walkthrough: a line-numbered monospace code surface where annotated line\n * ranges get a highlight band + numbered gutter marker, paired with a list of\n * line-anchored notes (each note targets a 1-based `lines` ref like `\"3\"` or\n * `\"3-5\"`). Hovering a note highlights its lines, and vice-versa.\n *\n * The schema MUST stay data-compatible with the `annotated-code` branch of\n * `planBlockSchema` (`plan-content.ts`), and the MDX `tag` (`AnnotatedCode`) +\n * flat attribute shape MUST match that inline member so stored `.mdx`\n * round-trips. `code` is a multiline string ATTRIBUTE (the shared `prop()`\n * encoder round-trips multiline strings cleanly, and keeping it an attribute —\n * not MDX children — avoids the source being reflowed as prose); `annotations`\n * is a JSON array attribute.\n */\n\n/** One line-anchored note over the code. */\nexport interface AnnotatedCodeAnnotation {\n /** 1-based line reference: a single line `\"3\"` or an inclusive range `\"3-5\"`. */\n lines: string;\n /** Optional short label shown before the note (e.g. \"Lookup\"). */\n label?: string;\n /** The note prose (markdown), rendered through `ctx.renderMarkdown`. */\n note: string;\n}\n\nexport interface AnnotatedCodeData {\n /** Optional file path shown in the header (e.g. `src/server/auth.ts`). */\n filename?: string;\n /** Optional language label (e.g. `ts`). Cosmetic chip + future highlighting. */\n language?: string;\n /** The source the walkthrough annotates. Rendered line-numbered. */\n code: string;\n /** Line-anchored notes. */\n annotations?: AnnotatedCodeAnnotation[];\n}\n\n/**\n * A 1-based line reference: `\"3\"` or `\"3-5\"` (inclusive). Whitespace tolerant.\n * The renderer parses this defensively too, but the schema rejects clearly\n * malformed refs so authored/agent-generated data stays clean.\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 annotationSchema = z.object({\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<AnnotatedCodeAnnotation>;\n\n/**\n * Data-compatible with the inline `annotated-code` member of `planBlockSchema`\n * (`plan-content.ts`). `code` is the only required field; `annotations` defaults\n * to omitted so a fresh block validates from `{ code: \"…\" }`.\n */\nexport const annotatedCodeSchema = z.object({\n filename: z.string().trim().max(400).optional(),\n language: z.string().trim().max(40).optional(),\n code: z.string().max(100_000),\n annotations: z.array(annotationSchema).max(80).optional(),\n}) as unknown as z.ZodType<AnnotatedCodeData>;\n\n/**\n * MDX config: `<AnnotatedCode filename language code annotations />` self-closing\n * form. Insertion order of `toAttrs` is the on-disk attribute order. `code` is a\n * multiline string attribute (round-trips through the shared `prop()` encoder);\n * `annotations` is a JSON array attribute. `fromAttrs` mirrors a forgiving parse\n * (`code ?? \"\"`, `annotations ?? []`, optional `filename`/`language` undefined\n * when absent) so a plan missing an attribute still parses.\n */\nexport const annotatedCodeMdx: BlockMdxConfig<AnnotatedCodeData> = {\n tag: \"AnnotatedCode\",\n toAttrs: (data) => ({\n filename: data.filename,\n language: data.language,\n code: data.code,\n annotations: data.annotations,\n }),\n fromAttrs: (attrs) => ({\n filename: attrs.string(\"filename\"),\n language: attrs.string(\"language\"),\n code: attrs.string(\"code\") ?? \"\",\n annotations: attrs.array<AnnotatedCodeAnnotation>(\"annotations\") ?? [],\n }),\n};\n"]}