@agent-native/core 0.40.2 → 0.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -1
- package/dist/cli/index.js +16 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/pr-visual-recap-workflow.d.ts +11 -0
- package/dist/cli/pr-visual-recap-workflow.d.ts.map +1 -0
- package/dist/cli/pr-visual-recap-workflow.js +11 -0
- package/dist/cli/pr-visual-recap-workflow.js.map +1 -0
- package/dist/cli/recap.d.ts +52 -0
- package/dist/cli/recap.d.ts.map +1 -0
- package/dist/cli/recap.js +581 -0
- package/dist/cli/recap.js.map +1 -0
- package/dist/cli/skills.d.ts +17 -4
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +60 -16
- package/dist/cli/skills.js.map +1 -1
- package/dist/cli/templates-meta.js +1 -1
- package/dist/cli/templates-meta.js.map +1 -1
- package/dist/client/blocks/index.d.ts +3 -0
- package/dist/client/blocks/index.d.ts.map +1 -1
- package/dist/client/blocks/index.js +3 -0
- package/dist/client/blocks/index.js.map +1 -1
- package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts +6 -0
- package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts.map +1 -0
- package/dist/client/blocks/library/AnnotatedCodeBlock.js +134 -0
- package/dist/client/blocks/library/AnnotatedCodeBlock.js.map +1 -0
- package/dist/client/blocks/library/HighlightedCode.d.ts +21 -1
- package/dist/client/blocks/library/HighlightedCode.d.ts.map +1 -1
- package/dist/client/blocks/library/HighlightedCode.js +86 -4
- package/dist/client/blocks/library/HighlightedCode.js.map +1 -1
- package/dist/client/blocks/library/annotated-code.config.d.ts +58 -0
- package/dist/client/blocks/library/annotated-code.config.d.ts.map +1 -0
- package/dist/client/blocks/library/annotated-code.config.js +53 -0
- package/dist/client/blocks/library/annotated-code.config.js.map +1 -0
- package/dist/client/blocks/library/checklist.js +2 -2
- package/dist/client/blocks/library/checklist.js.map +1 -1
- package/dist/client/blocks/library/code-highlight.d.ts +16 -0
- package/dist/client/blocks/library/code-highlight.d.ts.map +1 -0
- package/dist/client/blocks/library/code-highlight.js +160 -0
- package/dist/client/blocks/library/code-highlight.js.map +1 -0
- package/dist/client/blocks/library/code-tabs.config.d.ts +6 -0
- package/dist/client/blocks/library/code-tabs.config.d.ts.map +1 -1
- package/dist/client/blocks/library/code-tabs.config.js +1 -0
- package/dist/client/blocks/library/code-tabs.config.js.map +1 -1
- package/dist/client/blocks/library/code-tabs.d.ts.map +1 -1
- package/dist/client/blocks/library/code-tabs.js +35 -5
- package/dist/client/blocks/library/code-tabs.js.map +1 -1
- package/dist/client/blocks/library/code.config.d.ts +43 -0
- package/dist/client/blocks/library/code.config.d.ts.map +1 -0
- package/dist/client/blocks/library/code.config.js +34 -0
- package/dist/client/blocks/library/code.config.js.map +1 -0
- package/dist/client/blocks/library/code.d.ts +3 -0
- package/dist/client/blocks/library/code.d.ts.map +1 -0
- package/dist/client/blocks/library/code.js +95 -0
- package/dist/client/blocks/library/code.js.map +1 -0
- package/dist/client/blocks/library/dev-doc-ui.d.ts +2 -1
- package/dist/client/blocks/library/dev-doc-ui.d.ts.map +1 -1
- package/dist/client/blocks/library/dev-doc-ui.js +2 -1
- package/dist/client/blocks/library/dev-doc-ui.js.map +1 -1
- package/dist/client/blocks/library/server-specs.d.ts.map +1 -1
- package/dist/client/blocks/library/server-specs.js +21 -0
- package/dist/client/blocks/library/server-specs.js.map +1 -1
- package/dist/client/blocks/library/specs.d.ts +1 -1
- package/dist/client/blocks/library/specs.d.ts.map +1 -1
- package/dist/client/blocks/library/specs.js +30 -2
- package/dist/client/blocks/library/specs.js.map +1 -1
- package/dist/client/blocks/server.d.ts +1 -0
- package/dist/client/blocks/server.d.ts.map +1 -1
- package/dist/client/blocks/server.js +1 -0
- package/dist/client/blocks/server.js.map +1 -1
- package/dist/client/blocks/types.d.ts +1 -1
- package/dist/client/blocks/types.js.map +1 -1
- package/dist/client/extensions/ExtensionsListPage.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionsListPage.js +28 -13
- package/dist/client/extensions/ExtensionsListPage.js.map +1 -1
- package/dist/client/extensions/ExtensionsSidebarSection.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionsSidebarSection.js +31 -9
- package/dist/client/extensions/ExtensionsSidebarSection.js.map +1 -1
- package/dist/client/rich-markdown-editor/CodeBlockNode.d.ts +49 -0
- package/dist/client/rich-markdown-editor/CodeBlockNode.d.ts.map +1 -0
- package/dist/client/rich-markdown-editor/CodeBlockNode.js +126 -0
- package/dist/client/rich-markdown-editor/CodeBlockNode.js.map +1 -0
- package/dist/client/rich-markdown-editor/RegistryBlockNode.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/RegistryBlockNode.js +26 -3
- package/dist/client/rich-markdown-editor/RegistryBlockNode.js.map +1 -1
- package/dist/client/rich-markdown-editor/RichMarkdownEditor.d.ts +1 -1
- package/dist/client/rich-markdown-editor/extensions.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/extensions.js +8 -8
- package/dist/client/rich-markdown-editor/extensions.js.map +1 -1
- package/dist/client/rich-markdown-editor/index.d.ts +1 -0
- package/dist/client/rich-markdown-editor/index.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/index.js +1 -0
- package/dist/client/rich-markdown-editor/index.js.map +1 -1
- package/dist/client/rich-markdown-editor/registrySlashCommands.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/registrySlashCommands.js +1 -0
- package/dist/client/rich-markdown-editor/registrySlashCommands.js.map +1 -1
- package/dist/extensions/actions.d.ts.map +1 -1
- package/dist/extensions/actions.js +63 -2
- package/dist/extensions/actions.js.map +1 -1
- package/dist/extensions/routes.d.ts.map +1 -1
- package/dist/extensions/routes.js +24 -3
- package/dist/extensions/routes.js.map +1 -1
- package/dist/extensions/schema.d.ts +43 -2
- package/dist/extensions/schema.d.ts.map +1 -1
- package/dist/extensions/schema.js +12 -0
- package/dist/extensions/schema.js.map +1 -1
- package/dist/extensions/store.d.ts +20 -0
- package/dist/extensions/store.d.ts.map +1 -1
- package/dist/extensions/store.js +82 -3
- package/dist/extensions/store.js.map +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +13 -0
- package/dist/server/auth.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +11 -0
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/recap-image-route.d.ts +8 -0
- package/dist/server/recap-image-route.d.ts.map +1 -0
- package/dist/server/recap-image-route.js +200 -0
- package/dist/server/recap-image-route.js.map +1 -0
- package/dist/server/recap-image-store.d.ts +41 -0
- package/dist/server/recap-image-store.d.ts.map +1 -0
- package/dist/server/recap-image-store.js +138 -0
- package/dist/server/recap-image-store.js.map +1 -0
- package/dist/styles/rich-markdown-editor.css +66 -17
- package/docs/content/cloneable-saas.md +10 -0
- package/docs/content/external-agents.md +4 -7
- package/docs/content/faq.md +10 -0
- package/docs/content/getting-started.md +11 -0
- package/docs/content/pr-visual-recap.md +103 -0
- package/docs/content/skills-guide.md +1 -3
- package/docs/content/template-assets.md +1 -4
- package/docs/content/template-design.md +0 -57
- package/docs/content/template-plan.md +22 -18
- package/docs/content/visual-plans.md +10 -7
- package/docs/content/what-is-agent-native.md +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,58 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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"]}
|
|
@@ -45,9 +45,9 @@ export function ChecklistEditor({ data, onChange, editable, }) {
|
|
|
45
45
|
const setLabel = (id, label) => update(items.map((item) => (item.id === id ? { ...item, label } : item)));
|
|
46
46
|
const remove = (id) => update(items.filter((item) => item.id !== id));
|
|
47
47
|
const add = () => update([...items, { id: newItemId(), label: "", checked: false }]);
|
|
48
|
-
return (_jsxs("div", { className: "grid gap-2", children: [items.map((item) => (_jsxs("div", { className: "flex items-start gap-2", children: [_jsx("button", { type: "button", "data-plan-interactive": true, "aria-label": item.checked ? "Mark incomplete" : "Mark complete", className: cn("mt-1 flex size-5 shrink-0 items-center justify-center rounded border", item.checked
|
|
48
|
+
return (_jsxs("div", { className: "grid gap-2", children: [items.map((item) => (_jsxs("div", { className: "group flex items-start gap-2", children: [_jsx("button", { type: "button", "data-plan-interactive": true, "aria-label": item.checked ? "Mark incomplete" : "Mark complete", className: cn("mt-1 flex size-5 shrink-0 items-center justify-center rounded border", item.checked
|
|
49
49
|
? "border-primary bg-primary text-primary-foreground"
|
|
50
|
-
: "border-plan-line"), onClick: () => toggle(item.id), children: item.checked && _jsx(IconCheck, { className: "size-3.5" }) }), _jsx("input", { type: "text", "data-plan-interactive": true, className: "flex h-9 w-full rounded-md
|
|
50
|
+
: "border-plan-line"), onClick: () => toggle(item.id), children: item.checked && _jsx(IconCheck, { className: "size-3.5" }) }), _jsx("input", { type: "text", "data-plan-interactive": true, className: "flex h-9 w-full rounded-md bg-transparent px-3 py-1 text-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50", placeholder: "Checklist item", value: item.label, disabled: !editable, onChange: (event) => setLabel(item.id, event.target.value) }), _jsx("button", { type: "button", "data-plan-interactive": true, "aria-label": "Remove item", className: "mt-1 flex size-7 shrink-0 items-center justify-center rounded text-muted-foreground opacity-0 transition-opacity hover:bg-muted hover:text-foreground focus-visible:opacity-100 group-hover:opacity-100 group-focus-within:opacity-100 disabled:opacity-50", disabled: !editable, onClick: () => remove(item.id), children: _jsx(IconX, { className: "size-4" }) })] }, item.id))), _jsxs("button", { type: "button", "data-plan-interactive": true, className: "flex items-center gap-1.5 self-start rounded-md px-2 py-1 text-sm text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-50", disabled: !editable, onClick: add, children: [_jsx(IconPlus, { className: "size-4" }), "Add item"] })] }));
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
53
53
|
* The standard checklist block spec (with React `Read`/`Edit`). Apps register
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checklist.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/checklist.tsx"],"names":[],"mappings":"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","sourcesContent":["import { IconCheck, IconPlus, IconX } from \"@tabler/icons-react\";\nimport { cn } from \"../../utils.js\";\nimport { defineBlock } from \"../types.js\";\nimport type { BlockReadProps, BlockEditProps } from \"../types.js\";\nimport {\n checklistSchema,\n checklistMdx,\n type ChecklistData,\n type ChecklistItem,\n} from \"./checklist.config.js\";\n\n/**\n * Standard `checklist` block. A list of toggleable items, each with a label and\n * an optional note. Lives in core so any app can register it.\n *\n * `Read` mirrors the legacy plan `PlanBlockView` checklist branch byte-for-byte\n * (same `plan-block` section, toggle buttons, `IconCheck` marker, and the\n * existing toggle-via-`onChange` behavior) so converting the block to the\n * registry does not change rendered output. The plan CSS classes\n * (`plan-block`, `text-plan-*`, `border-plan-line`) resolve against the plan\n * app's stylesheet at render time, exactly as before.\n *\n * `Edit` is a custom editor (the schema auto-editor can't edit an array of\n * objects): it lets you add, remove, toggle, and relabel items inline.\n */\n\n/** Mint a reasonably-unique item id without pulling a dep into core. */\nfunction newItemId(): string {\n return `item-${Math.random().toString(36).slice(2, 10)}`;\n}\n\n/**\n * Read renderer. Note `onToggle` is supplied by the block dispatcher for the\n * historical click-to-toggle behavior; in pure read contexts it is omitted and\n * the markers render statically.\n */\nexport function ChecklistBlock({\n data,\n blockId,\n title,\n onToggle,\n}: BlockReadProps<ChecklistData> & {\n onToggle?: (itemId: string) => void;\n}) {\n return (\n <section className=\"plan-block\" data-block-id={blockId}>\n {title && <div className=\"plan-block-label\">{title}</div>}\n <div className=\"grid gap-3\">\n {data.items.map((item) =>\n onToggle ? (\n <button\n key={item.id}\n type=\"button\"\n data-plan-interactive\n className=\"flex items-start gap-3 text-left text-plan-muted\"\n onClick={() => onToggle(item.id)}\n >\n <ChecklistMarker checked={item.checked} />\n <ChecklistItemBody item={item} />\n </button>\n ) : (\n <div\n key={item.id}\n className=\"flex items-start gap-3 text-left text-plan-muted\"\n >\n <ChecklistMarker checked={item.checked} />\n <ChecklistItemBody item={item} />\n </div>\n ),\n )}\n </div>\n </section>\n );\n}\n\nfunction ChecklistMarker({ checked }: { checked?: boolean }) {\n return (\n <span\n className={cn(\n \"mt-1 flex size-5 items-center justify-center rounded border\",\n checked\n ? \"border-primary bg-primary text-primary-foreground\"\n : \"border-plan-line\",\n )}\n >\n {checked && <IconCheck className=\"size-3.5\" />}\n </span>\n );\n}\n\nfunction ChecklistItemBody({ item }: { item: ChecklistItem }) {\n return (\n <span>\n <span className=\"block text-plan-text\">{item.label}</span>\n {item.note && <span className=\"block text-sm\">{item.note}</span>}\n </span>\n );\n}\n\n/** Custom editor: toggle, relabel, add, and remove items. */\nexport function ChecklistEditor({\n data,\n onChange,\n editable,\n}: BlockEditProps<ChecklistData>) {\n const items = data.items;\n\n const update = (next: ChecklistItem[]) => onChange({ items: next });\n\n const toggle = (id: string) =>\n update(\n items.map((item) =>\n item.id === id ? { ...item, checked: !item.checked } : item,\n ),\n );\n\n const setLabel = (id: string, label: string) =>\n update(items.map((item) => (item.id === id ? { ...item, label } : item)));\n\n const remove = (id: string) => update(items.filter((item) => item.id !== id));\n\n const add = () =>\n update([...items, { id: newItemId(), label: \"\", checked: false }]);\n\n return (\n <div className=\"grid gap-2\">\n {items.map((item) => (\n <div key={item.id} className=\"flex items-start gap-2\">\n <button\n type=\"button\"\n data-plan-interactive\n aria-label={item.checked ? \"Mark incomplete\" : \"Mark complete\"}\n className={cn(\n \"mt-1 flex size-5 shrink-0 items-center justify-center rounded border\",\n item.checked\n ? \"border-primary bg-primary text-primary-foreground\"\n : \"border-plan-line\",\n )}\n onClick={() => toggle(item.id)}\n >\n {item.checked && <IconCheck className=\"size-3.5\" />}\n </button>\n <input\n type=\"text\"\n data-plan-interactive\n className=\"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50\"\n placeholder=\"Checklist item\"\n value={item.label}\n disabled={!editable}\n onChange={(event) => setLabel(item.id, event.target.value)}\n />\n <button\n type=\"button\"\n data-plan-interactive\n aria-label=\"Remove item\"\n className=\"mt-1 flex size-7 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-50\"\n disabled={!editable}\n onClick={() => remove(item.id)}\n >\n <IconX className=\"size-4\" />\n </button>\n </div>\n ))}\n <button\n type=\"button\"\n data-plan-interactive\n className=\"flex items-center gap-1.5 self-start rounded-md px-2 py-1 text-sm text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-50\"\n disabled={!editable}\n onClick={add}\n >\n <IconPlus className=\"size-4\" />\n Add item\n </button>\n </div>\n );\n}\n\n/**\n * The standard checklist block spec (with React `Read`/`Edit`). Apps register\n * this in their browser registry. The schema + MDX config come from\n * `./checklist.config.ts`, the exact same object server / agent code registers,\n * so rendering and source round-trip never drift.\n *\n * `Read` is typed against `BlockReadProps<ChecklistData>`; the optional\n * `onToggle` the dispatcher injects for the legacy click-to-toggle behavior is\n * an extra prop the registry's `BlockView` passes through harmlessly when\n * present (it lives outside `BlockReadProps`, so it's wired by the app's block\n * dispatch rather than the generic `BlockView`).\n */\nexport const checklistBlock = defineBlock<ChecklistData>({\n type: \"checklist\",\n schema: checklistSchema,\n mdx: checklistMdx,\n Read: ChecklistBlock as never,\n Edit: ChecklistEditor,\n placement: [\"block\"],\n editSurface: \"inline\",\n // A checklist maps to NFM to-do items, so it round-trips to Notion.\n notionCompatible: true,\n label: \"Checklist\",\n icon: IconCheck,\n description:\n \"A list of toggleable items, each with a label and an optional note.\",\n empty: () => ({ items: [] }),\n});\n"]}
|
|
1
|
+
{"version":3,"file":"checklist.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/checklist.tsx"],"names":[],"mappings":"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","sourcesContent":["import { IconCheck, IconPlus, IconX } from \"@tabler/icons-react\";\nimport { cn } from \"../../utils.js\";\nimport { defineBlock } from \"../types.js\";\nimport type { BlockReadProps, BlockEditProps } from \"../types.js\";\nimport {\n checklistSchema,\n checklistMdx,\n type ChecklistData,\n type ChecklistItem,\n} from \"./checklist.config.js\";\n\n/**\n * Standard `checklist` block. A list of toggleable items, each with a label and\n * an optional note. Lives in core so any app can register it.\n *\n * `Read` mirrors the legacy plan `PlanBlockView` checklist branch byte-for-byte\n * (same `plan-block` section, toggle buttons, `IconCheck` marker, and the\n * existing toggle-via-`onChange` behavior) so converting the block to the\n * registry does not change rendered output. The plan CSS classes\n * (`plan-block`, `text-plan-*`, `border-plan-line`) resolve against the plan\n * app's stylesheet at render time, exactly as before.\n *\n * `Edit` is a custom editor (the schema auto-editor can't edit an array of\n * objects): it lets you add, remove, toggle, and relabel items inline.\n */\n\n/** Mint a reasonably-unique item id without pulling a dep into core. */\nfunction newItemId(): string {\n return `item-${Math.random().toString(36).slice(2, 10)}`;\n}\n\n/**\n * Read renderer. Note `onToggle` is supplied by the block dispatcher for the\n * historical click-to-toggle behavior; in pure read contexts it is omitted and\n * the markers render statically.\n */\nexport function ChecklistBlock({\n data,\n blockId,\n title,\n onToggle,\n}: BlockReadProps<ChecklistData> & {\n onToggle?: (itemId: string) => void;\n}) {\n return (\n <section className=\"plan-block\" data-block-id={blockId}>\n {title && <div className=\"plan-block-label\">{title}</div>}\n <div className=\"grid gap-3\">\n {data.items.map((item) =>\n onToggle ? (\n <button\n key={item.id}\n type=\"button\"\n data-plan-interactive\n className=\"flex items-start gap-3 text-left text-plan-muted\"\n onClick={() => onToggle(item.id)}\n >\n <ChecklistMarker checked={item.checked} />\n <ChecklistItemBody item={item} />\n </button>\n ) : (\n <div\n key={item.id}\n className=\"flex items-start gap-3 text-left text-plan-muted\"\n >\n <ChecklistMarker checked={item.checked} />\n <ChecklistItemBody item={item} />\n </div>\n ),\n )}\n </div>\n </section>\n );\n}\n\nfunction ChecklistMarker({ checked }: { checked?: boolean }) {\n return (\n <span\n className={cn(\n \"mt-1 flex size-5 items-center justify-center rounded border\",\n checked\n ? \"border-primary bg-primary text-primary-foreground\"\n : \"border-plan-line\",\n )}\n >\n {checked && <IconCheck className=\"size-3.5\" />}\n </span>\n );\n}\n\nfunction ChecklistItemBody({ item }: { item: ChecklistItem }) {\n return (\n <span>\n <span className=\"block text-plan-text\">{item.label}</span>\n {item.note && <span className=\"block text-sm\">{item.note}</span>}\n </span>\n );\n}\n\n/** Custom editor: toggle, relabel, add, and remove items. */\nexport function ChecklistEditor({\n data,\n onChange,\n editable,\n}: BlockEditProps<ChecklistData>) {\n const items = data.items;\n\n const update = (next: ChecklistItem[]) => onChange({ items: next });\n\n const toggle = (id: string) =>\n update(\n items.map((item) =>\n item.id === id ? { ...item, checked: !item.checked } : item,\n ),\n );\n\n const setLabel = (id: string, label: string) =>\n update(items.map((item) => (item.id === id ? { ...item, label } : item)));\n\n const remove = (id: string) => update(items.filter((item) => item.id !== id));\n\n const add = () =>\n update([...items, { id: newItemId(), label: \"\", checked: false }]);\n\n return (\n <div className=\"grid gap-2\">\n {items.map((item) => (\n <div key={item.id} className=\"group flex items-start gap-2\">\n <button\n type=\"button\"\n data-plan-interactive\n aria-label={item.checked ? \"Mark incomplete\" : \"Mark complete\"}\n className={cn(\n \"mt-1 flex size-5 shrink-0 items-center justify-center rounded border\",\n item.checked\n ? \"border-primary bg-primary text-primary-foreground\"\n : \"border-plan-line\",\n )}\n onClick={() => toggle(item.id)}\n >\n {item.checked && <IconCheck className=\"size-3.5\" />}\n </button>\n <input\n type=\"text\"\n data-plan-interactive\n className=\"flex h-9 w-full rounded-md bg-transparent px-3 py-1 text-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50\"\n placeholder=\"Checklist item\"\n value={item.label}\n disabled={!editable}\n onChange={(event) => setLabel(item.id, event.target.value)}\n />\n <button\n type=\"button\"\n data-plan-interactive\n aria-label=\"Remove item\"\n className=\"mt-1 flex size-7 shrink-0 items-center justify-center rounded text-muted-foreground opacity-0 transition-opacity hover:bg-muted hover:text-foreground focus-visible:opacity-100 group-hover:opacity-100 group-focus-within:opacity-100 disabled:opacity-50\"\n disabled={!editable}\n onClick={() => remove(item.id)}\n >\n <IconX className=\"size-4\" />\n </button>\n </div>\n ))}\n <button\n type=\"button\"\n data-plan-interactive\n className=\"flex items-center gap-1.5 self-start rounded-md px-2 py-1 text-sm text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-50\"\n disabled={!editable}\n onClick={add}\n >\n <IconPlus className=\"size-4\" />\n Add item\n </button>\n </div>\n );\n}\n\n/**\n * The standard checklist block spec (with React `Read`/`Edit`). Apps register\n * this in their browser registry. The schema + MDX config come from\n * `./checklist.config.ts`, the exact same object server / agent code registers,\n * so rendering and source round-trip never drift.\n *\n * `Read` is typed against `BlockReadProps<ChecklistData>`; the optional\n * `onToggle` the dispatcher injects for the legacy click-to-toggle behavior is\n * an extra prop the registry's `BlockView` passes through harmlessly when\n * present (it lives outside `BlockReadProps`, so it's wired by the app's block\n * dispatch rather than the generic `BlockView`).\n */\nexport const checklistBlock = defineBlock<ChecklistData>({\n type: \"checklist\",\n schema: checklistSchema,\n mdx: checklistMdx,\n Read: ChecklistBlock as never,\n Edit: ChecklistEditor,\n placement: [\"block\"],\n editSurface: \"inline\",\n // A checklist maps to NFM to-do items, so it round-trips to Notion.\n notionCompatible: true,\n label: \"Checklist\",\n icon: IconCheck,\n description:\n \"A list of toggleable items, each with a label and an optional note.\",\n empty: () => ({ items: [] }),\n});\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Normalize a user-supplied language hint to a registered highlight.js grammar,
|
|
4
|
+
* or `null` when empty / unknown so callers fall back to plain text.
|
|
5
|
+
*/
|
|
6
|
+
export declare function normalizeCodeLanguage(value?: string | null): string | null;
|
|
7
|
+
/** Best-effort language from a filename / path extension (e.g. `auth.ts` → ts). */
|
|
8
|
+
export declare function inferLanguageFromFilename(filename?: string | null): string | null;
|
|
9
|
+
/**
|
|
10
|
+
* Syntax-highlight `code` for an already-resolved language, returning React
|
|
11
|
+
* token nodes. Pass a single line to highlight per-line (the annotated-code use)
|
|
12
|
+
* or a whole snippet. Falls back to the raw string for empty / plaintext /
|
|
13
|
+
* unknown languages or any grammar error, so it is always safe to render.
|
|
14
|
+
*/
|
|
15
|
+
export declare function highlightCode(code: string, language?: string | null): ReactNode;
|
|
16
|
+
//# sourceMappingURL=code-highlight.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-highlight.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/code-highlight.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAkGvC;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAQ1E;AAED,mFAAmF;AACnF,wBAAgB,yBAAyB,CACvC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GACvB,MAAM,GAAG,IAAI,CASf;AAiCD;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GACvB,SAAS,CAWX"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { common, createLowlight } from "lowlight";
|
|
3
|
+
/**
|
|
4
|
+
* Shared synchronous syntax highlighter for the dev-doc code blocks. Wraps
|
|
5
|
+
* `lowlight` (highlight.js grammars over a HAST tree) and converts the tree to
|
|
6
|
+
* React nodes whose tokens carry theme-aware Tailwind classes, so a caller can
|
|
7
|
+
* highlight a whole snippet OR a single line and drop the result straight into
|
|
8
|
+
* JSX. This is the same colorful palette the `code-tabs` block uses, extracted so
|
|
9
|
+
* blocks that need per-line control (annotated-code's highlight bands, future
|
|
10
|
+
* line-anchored surfaces) render identical syntax colors instead of forking yet
|
|
11
|
+
* another token map. `DiffBlock` keeps its own muted palette on purpose (its
|
|
12
|
+
* syntax colors must not fight the add/removed line tints), so it is not a
|
|
13
|
+
* consumer here.
|
|
14
|
+
*
|
|
15
|
+
* Per-line use is the reason this is sync (lowlight, not async Shiki): a block
|
|
16
|
+
* that puts a band/gutter marker on specific lines highlights each line on its
|
|
17
|
+
* own with no loading state.
|
|
18
|
+
*/
|
|
19
|
+
const lowlight = createLowlight(common);
|
|
20
|
+
/** Common extension / shorthand → registered highlight.js language name. */
|
|
21
|
+
const LANGUAGE_ALIASES = {
|
|
22
|
+
cjs: "javascript",
|
|
23
|
+
cts: "typescript",
|
|
24
|
+
htm: "html",
|
|
25
|
+
js: "javascript",
|
|
26
|
+
jsonc: "json",
|
|
27
|
+
jsx: "jsx",
|
|
28
|
+
md: "markdown",
|
|
29
|
+
mdx: "markdown",
|
|
30
|
+
mjs: "javascript",
|
|
31
|
+
mts: "typescript",
|
|
32
|
+
py: "python",
|
|
33
|
+
rb: "ruby",
|
|
34
|
+
rs: "rust",
|
|
35
|
+
sh: "bash",
|
|
36
|
+
shell: "bash",
|
|
37
|
+
ts: "typescript",
|
|
38
|
+
tsx: "tsx",
|
|
39
|
+
yml: "yaml",
|
|
40
|
+
zsh: "bash",
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* highlight.js token class → Tailwind classes. Colorful palette (keywords rose,
|
|
44
|
+
* numbers/attrs sky, literals/titles violet, strings/tags emerald, types/builtins
|
|
45
|
+
* amber) with `dark:` pairs so it reads in both themes. Kept value-identical to
|
|
46
|
+
* the `code-tabs` block's map.
|
|
47
|
+
*/
|
|
48
|
+
const TOKEN_CLASS_NAMES = {
|
|
49
|
+
"hljs-addition": "text-emerald-700 dark:text-emerald-300",
|
|
50
|
+
"hljs-attr": "text-sky-700 dark:text-sky-300",
|
|
51
|
+
"hljs-attribute": "text-sky-700 dark:text-sky-300",
|
|
52
|
+
"hljs-built_in": "text-amber-700 dark:text-amber-300",
|
|
53
|
+
"hljs-bullet": "text-primary",
|
|
54
|
+
"hljs-comment": "text-muted-foreground italic",
|
|
55
|
+
"hljs-deletion": "text-destructive",
|
|
56
|
+
"hljs-doctag": "text-destructive",
|
|
57
|
+
"hljs-emphasis": "italic",
|
|
58
|
+
"hljs-formula": "text-destructive",
|
|
59
|
+
"hljs-keyword": "text-rose-700 dark:text-rose-300",
|
|
60
|
+
"hljs-link": "text-primary underline-offset-2",
|
|
61
|
+
"hljs-literal": "text-violet-700 dark:text-violet-300",
|
|
62
|
+
"hljs-meta": "text-primary",
|
|
63
|
+
"hljs-meta-string": "text-emerald-700 dark:text-emerald-300",
|
|
64
|
+
"hljs-name": "text-emerald-700 dark:text-emerald-300",
|
|
65
|
+
"hljs-number": "text-sky-700 dark:text-sky-300",
|
|
66
|
+
"hljs-params": "text-sky-700 dark:text-sky-300",
|
|
67
|
+
"hljs-property": "text-sky-700 dark:text-sky-300",
|
|
68
|
+
"hljs-quote": "text-muted-foreground italic",
|
|
69
|
+
"hljs-regexp": "text-emerald-700 dark:text-emerald-300",
|
|
70
|
+
"hljs-section": "text-violet-700 dark:text-violet-300",
|
|
71
|
+
"hljs-selector-attr": "text-primary",
|
|
72
|
+
"hljs-selector-class": "text-emerald-700 dark:text-emerald-300",
|
|
73
|
+
"hljs-selector-id": "text-emerald-700 dark:text-emerald-300",
|
|
74
|
+
"hljs-selector-pseudo": "text-primary",
|
|
75
|
+
"hljs-selector-tag": "text-emerald-700 dark:text-emerald-300",
|
|
76
|
+
"hljs-string": "text-emerald-700 dark:text-emerald-300",
|
|
77
|
+
"hljs-strong": "font-semibold",
|
|
78
|
+
"hljs-subst": "text-destructive",
|
|
79
|
+
"hljs-symbol": "text-primary",
|
|
80
|
+
"hljs-tag": "text-emerald-700 dark:text-emerald-300",
|
|
81
|
+
"hljs-template-variable": "text-amber-700 dark:text-amber-300",
|
|
82
|
+
"hljs-title": "text-violet-700 dark:text-violet-300",
|
|
83
|
+
"hljs-type": "text-amber-700 dark:text-amber-300",
|
|
84
|
+
"hljs-variable": "text-amber-700 dark:text-amber-300",
|
|
85
|
+
language_: "text-amber-700 dark:text-amber-300",
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Normalize a user-supplied language hint to a registered highlight.js grammar,
|
|
89
|
+
* or `null` when empty / unknown so callers fall back to plain text.
|
|
90
|
+
*/
|
|
91
|
+
export function normalizeCodeLanguage(value) {
|
|
92
|
+
const raw = value
|
|
93
|
+
?.trim()
|
|
94
|
+
.toLowerCase()
|
|
95
|
+
.replace(/^language-/, "");
|
|
96
|
+
if (!raw)
|
|
97
|
+
return null;
|
|
98
|
+
const normalized = LANGUAGE_ALIASES[raw] ?? raw;
|
|
99
|
+
return lowlight.registered(normalized) ? normalized : null;
|
|
100
|
+
}
|
|
101
|
+
/** Best-effort language from a filename / path extension (e.g. `auth.ts` → ts). */
|
|
102
|
+
export function inferLanguageFromFilename(filename) {
|
|
103
|
+
const basename = filename?.split("/").pop()?.toLowerCase();
|
|
104
|
+
if (!basename)
|
|
105
|
+
return null;
|
|
106
|
+
if (basename === "dockerfile")
|
|
107
|
+
return normalizeCodeLanguage("bash");
|
|
108
|
+
if (basename === "makefile")
|
|
109
|
+
return normalizeCodeLanguage("makefile");
|
|
110
|
+
const extension = basename.includes(".")
|
|
111
|
+
? basename.split(".").pop()
|
|
112
|
+
: undefined;
|
|
113
|
+
return normalizeCodeLanguage(extension);
|
|
114
|
+
}
|
|
115
|
+
function tokenClassName(value) {
|
|
116
|
+
const classes = Array.isArray(value)
|
|
117
|
+
? value
|
|
118
|
+
: typeof value === "string"
|
|
119
|
+
? value.split(/\s+/)
|
|
120
|
+
: [];
|
|
121
|
+
return classes
|
|
122
|
+
.map((className) => TOKEN_CLASS_NAMES[className] ?? "")
|
|
123
|
+
.filter(Boolean)
|
|
124
|
+
.join(" ");
|
|
125
|
+
}
|
|
126
|
+
function hastToReact(children, keyPrefix) {
|
|
127
|
+
return children.map((node, index) => {
|
|
128
|
+
if (node.type === "text")
|
|
129
|
+
return node.value ?? "";
|
|
130
|
+
if (node.type === "element") {
|
|
131
|
+
const key = `${keyPrefix}${index}`;
|
|
132
|
+
const renderedChildren = node.children?.length
|
|
133
|
+
? hastToReact(node.children, `${key}-`)
|
|
134
|
+
: null;
|
|
135
|
+
const className = tokenClassName(node.properties?.className);
|
|
136
|
+
return (_jsx("span", { className: className || undefined, children: renderedChildren }, key));
|
|
137
|
+
}
|
|
138
|
+
return null;
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Syntax-highlight `code` for an already-resolved language, returning React
|
|
143
|
+
* token nodes. Pass a single line to highlight per-line (the annotated-code use)
|
|
144
|
+
* or a whole snippet. Falls back to the raw string for empty / plaintext /
|
|
145
|
+
* unknown languages or any grammar error, so it is always safe to render.
|
|
146
|
+
*/
|
|
147
|
+
export function highlightCode(code, language) {
|
|
148
|
+
const normalized = normalizeCodeLanguage(language);
|
|
149
|
+
if (!normalized || normalized === "plaintext" || normalized === "text") {
|
|
150
|
+
return code;
|
|
151
|
+
}
|
|
152
|
+
try {
|
|
153
|
+
const tree = lowlight.highlight(normalized, code);
|
|
154
|
+
return hastToReact(tree.children ?? [], `${normalized}-`);
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return code;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=code-highlight.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-highlight.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/code-highlight.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { ReactNode } from \"react\";\nimport { common, createLowlight } from \"lowlight\";\n\n/**\n * Shared synchronous syntax highlighter for the dev-doc code blocks. Wraps\n * `lowlight` (highlight.js grammars over a HAST tree) and converts the tree to\n * React nodes whose tokens carry theme-aware Tailwind classes, so a caller can\n * highlight a whole snippet OR a single line and drop the result straight into\n * JSX. This is the same colorful palette the `code-tabs` block uses, extracted so\n * blocks that need per-line control (annotated-code's highlight bands, future\n * line-anchored surfaces) render identical syntax colors instead of forking yet\n * another token map. `DiffBlock` keeps its own muted palette on purpose (its\n * syntax colors must not fight the add/removed line tints), so it is not a\n * consumer here.\n *\n * Per-line use is the reason this is sync (lowlight, not async Shiki): a block\n * that puts a band/gutter marker on specific lines highlights each line on its\n * own with no loading state.\n */\n\nconst lowlight = createLowlight(common);\n\ntype LowlightNode = {\n type: string;\n value?: string;\n properties?: { className?: string[] | string };\n children?: LowlightNode[];\n};\n\n/** Common extension / shorthand → registered highlight.js language name. */\nconst LANGUAGE_ALIASES: Record<string, string> = {\n cjs: \"javascript\",\n cts: \"typescript\",\n htm: \"html\",\n js: \"javascript\",\n jsonc: \"json\",\n jsx: \"jsx\",\n md: \"markdown\",\n mdx: \"markdown\",\n mjs: \"javascript\",\n mts: \"typescript\",\n py: \"python\",\n rb: \"ruby\",\n rs: \"rust\",\n sh: \"bash\",\n shell: \"bash\",\n ts: \"typescript\",\n tsx: \"tsx\",\n yml: \"yaml\",\n zsh: \"bash\",\n};\n\n/**\n * highlight.js token class → Tailwind classes. Colorful palette (keywords rose,\n * numbers/attrs sky, literals/titles violet, strings/tags emerald, types/builtins\n * amber) with `dark:` pairs so it reads in both themes. Kept value-identical to\n * the `code-tabs` block's map.\n */\nconst TOKEN_CLASS_NAMES: Record<string, string> = {\n \"hljs-addition\": \"text-emerald-700 dark:text-emerald-300\",\n \"hljs-attr\": \"text-sky-700 dark:text-sky-300\",\n \"hljs-attribute\": \"text-sky-700 dark:text-sky-300\",\n \"hljs-built_in\": \"text-amber-700 dark:text-amber-300\",\n \"hljs-bullet\": \"text-primary\",\n \"hljs-comment\": \"text-muted-foreground italic\",\n \"hljs-deletion\": \"text-destructive\",\n \"hljs-doctag\": \"text-destructive\",\n \"hljs-emphasis\": \"italic\",\n \"hljs-formula\": \"text-destructive\",\n \"hljs-keyword\": \"text-rose-700 dark:text-rose-300\",\n \"hljs-link\": \"text-primary underline-offset-2\",\n \"hljs-literal\": \"text-violet-700 dark:text-violet-300\",\n \"hljs-meta\": \"text-primary\",\n \"hljs-meta-string\": \"text-emerald-700 dark:text-emerald-300\",\n \"hljs-name\": \"text-emerald-700 dark:text-emerald-300\",\n \"hljs-number\": \"text-sky-700 dark:text-sky-300\",\n \"hljs-params\": \"text-sky-700 dark:text-sky-300\",\n \"hljs-property\": \"text-sky-700 dark:text-sky-300\",\n \"hljs-quote\": \"text-muted-foreground italic\",\n \"hljs-regexp\": \"text-emerald-700 dark:text-emerald-300\",\n \"hljs-section\": \"text-violet-700 dark:text-violet-300\",\n \"hljs-selector-attr\": \"text-primary\",\n \"hljs-selector-class\": \"text-emerald-700 dark:text-emerald-300\",\n \"hljs-selector-id\": \"text-emerald-700 dark:text-emerald-300\",\n \"hljs-selector-pseudo\": \"text-primary\",\n \"hljs-selector-tag\": \"text-emerald-700 dark:text-emerald-300\",\n \"hljs-string\": \"text-emerald-700 dark:text-emerald-300\",\n \"hljs-strong\": \"font-semibold\",\n \"hljs-subst\": \"text-destructive\",\n \"hljs-symbol\": \"text-primary\",\n \"hljs-tag\": \"text-emerald-700 dark:text-emerald-300\",\n \"hljs-template-variable\": \"text-amber-700 dark:text-amber-300\",\n \"hljs-title\": \"text-violet-700 dark:text-violet-300\",\n \"hljs-type\": \"text-amber-700 dark:text-amber-300\",\n \"hljs-variable\": \"text-amber-700 dark:text-amber-300\",\n language_: \"text-amber-700 dark:text-amber-300\",\n};\n\n/**\n * Normalize a user-supplied language hint to a registered highlight.js grammar,\n * or `null` when empty / unknown so callers fall back to plain text.\n */\nexport function normalizeCodeLanguage(value?: string | null): string | null {\n const raw = value\n ?.trim()\n .toLowerCase()\n .replace(/^language-/, \"\");\n if (!raw) return null;\n const normalized = LANGUAGE_ALIASES[raw] ?? raw;\n return lowlight.registered(normalized) ? normalized : null;\n}\n\n/** Best-effort language from a filename / path extension (e.g. `auth.ts` → ts). */\nexport function inferLanguageFromFilename(\n filename?: string | null,\n): string | null {\n const basename = filename?.split(\"/\").pop()?.toLowerCase();\n if (!basename) return null;\n if (basename === \"dockerfile\") return normalizeCodeLanguage(\"bash\");\n if (basename === \"makefile\") return normalizeCodeLanguage(\"makefile\");\n const extension = basename.includes(\".\")\n ? basename.split(\".\").pop()\n : undefined;\n return normalizeCodeLanguage(extension);\n}\n\nfunction tokenClassName(value: string[] | string | undefined): string {\n const classes = Array.isArray(value)\n ? value\n : typeof value === \"string\"\n ? value.split(/\\s+/)\n : [];\n return classes\n .map((className) => TOKEN_CLASS_NAMES[className] ?? \"\")\n .filter(Boolean)\n .join(\" \");\n}\n\nfunction hastToReact(children: LowlightNode[], keyPrefix: string): ReactNode[] {\n return children.map((node, index) => {\n if (node.type === \"text\") return node.value ?? \"\";\n if (node.type === \"element\") {\n const key = `${keyPrefix}${index}`;\n const renderedChildren = node.children?.length\n ? hastToReact(node.children, `${key}-`)\n : null;\n const className = tokenClassName(node.properties?.className);\n return (\n <span key={key} className={className || undefined}>\n {renderedChildren}\n </span>\n );\n }\n return null;\n });\n}\n\n/**\n * Syntax-highlight `code` for an already-resolved language, returning React\n * token nodes. Pass a single line to highlight per-line (the annotated-code use)\n * or a whole snippet. Falls back to the raw string for empty / plaintext /\n * unknown languages or any grammar error, so it is always safe to render.\n */\nexport function highlightCode(\n code: string,\n language?: string | null,\n): ReactNode {\n const normalized = normalizeCodeLanguage(language);\n if (!normalized || normalized === \"plaintext\" || normalized === \"text\") {\n return code;\n }\n try {\n const tree = lowlight.highlight(normalized, code) as LowlightNode;\n return hastToReact(tree.children ?? [], `${normalized}-`);\n } catch {\n return code;\n }\n}\n"]}
|
|
@@ -20,6 +20,12 @@ export interface CodeTabsTab {
|
|
|
20
20
|
language?: string;
|
|
21
21
|
code: string;
|
|
22
22
|
caption?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Lines shown before the pane collapses behind a "Show N more lines" toggle.
|
|
25
|
+
* Omitted ⇒ the default cap (`DEFAULT_CODE_MAX_LINES`, 30). `0` ⇒ never
|
|
26
|
+
* collapse (always show the whole snippet).
|
|
27
|
+
*/
|
|
28
|
+
maxLines?: number;
|
|
23
29
|
}
|
|
24
30
|
export interface CodeTabsData {
|
|
25
31
|
tabs: CodeTabsTab[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code-tabs.config.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/code-tabs.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"code-tabs.config.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/code-tabs.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,WAAW,EAAE,CAAC;CACrB;AAKD,eAAO,MAAM,cAAc,EAcV,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;AAEzC;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,EAAE,cAAc,CAAC,YAAY,CAMpD,CAAC"}
|
|
@@ -9,6 +9,7 @@ export const codeTabsSchema = z.object({
|
|
|
9
9
|
language: z.string().trim().max(40).optional(),
|
|
10
10
|
code: z.string().max(100_000),
|
|
11
11
|
caption: z.string().trim().max(400).optional(),
|
|
12
|
+
maxLines: z.number().int().min(0).max(2000).optional(),
|
|
12
13
|
}))
|
|
13
14
|
.min(1)
|
|
14
15
|
.max(12),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code-tabs.config.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/code-tabs.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"code-tabs.config.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/code-tabs.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAoCxB,yEAAyE;AACzE,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAEtD,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC;IACrC,IAAI,EAAE,CAAC;SACJ,KAAK,CACJ,CAAC,CAAC,MAAM,CAAC;QACP,EAAE,EAAE,WAAW;QACf,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;QACxC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE;QAC9C,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC;QAC7B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;QAC9C,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE;KACvD,CAAC,CACH;SACA,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,EAAE,CAAC;CACX,CAAuC,CAAC;AAEzC;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,WAAW,GAAiC;IACvD,GAAG,EAAE,UAAU;IACf,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;IACxC,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACrB,IAAI,EAAE,KAAK,CAAC,KAAK,CAAc,MAAM,CAAC,IAAI,EAAE;KAC7C,CAAC;CACH,CAAC","sourcesContent":["import { z } from \"zod\";\nimport type { BlockMdxConfig } from \"../types.js\";\n\n/**\n * Pure (React-free) part of the standard `code-tabs` block: its data schema and\n * MDX round-trip config. Shared by the server MDX adapter (a plan/content app\n * registers it via `@agent-native/core/blocks/server`) and the full client spec\n * (`code-tabs.tsx`). Keeping this React-free means importing it into a server\n * module never pulls React into the Nitro/SSR bundle.\n *\n * `code-tabs` is a STANDARD library block (a vertical file tab rail of Shiki-\n * highlighted code), shareable by any app. Its schema MUST stay data-compatible\n * with the legacy plan `code-tabs` branch of `planBlockSchema`, and the MDX\n * `tag` + attribute shape MUST match the legacy\n * `<CodeTabs … tabs={[…]} />` encoding (`plan-mdx.ts` `serializeBlock`/\n * `parseBlock`) so stored `.mdx` round-trips byte-compatibly.\n */\n\nexport interface CodeTabsTab {\n id: string;\n label: string;\n language?: string;\n code: string;\n caption?: string;\n /**\n * Lines shown before the pane collapses behind a \"Show N more lines\" toggle.\n * Omitted ⇒ the default cap (`DEFAULT_CODE_MAX_LINES`, 30). `0` ⇒ never\n * collapse (always show the whole snippet).\n */\n maxLines?: number;\n}\n\nexport interface CodeTabsData {\n tabs: CodeTabsTab[];\n}\n\n/** Matches the plan `idSchema` (`z.string().trim().min(1).max(120)`). */\nconst tabIdSchema = z.string().trim().min(1).max(120);\n\nexport const codeTabsSchema = z.object({\n tabs: z\n .array(\n z.object({\n id: tabIdSchema,\n label: z.string().trim().min(1).max(120),\n language: z.string().trim().max(40).optional(),\n code: z.string().max(100_000),\n caption: z.string().trim().max(400).optional(),\n maxLines: z.number().int().min(0).max(2000).optional(),\n }),\n )\n .min(1)\n .max(12),\n}) as unknown as z.ZodType<CodeTabsData>;\n\n/**\n * MDX config: `tabs` is a single JSON-encoded attribute and the block is\n * self-closing — exactly the legacy `<CodeTabs id … tabs={[…]} />` form.\n * `toAttrs` returns only `tabs`; `fromAttrs` reads the `tabs` array (defaulting\n * to `[]` for backward-compat with malformed/empty stored blocks, mirroring the\n * legacy `arrayAttr(node, \"tabs\") ?? []`).\n */\nexport const codeTabsMdx: BlockMdxConfig<CodeTabsData> = {\n tag: \"CodeTabs\",\n toAttrs: (data) => ({ tabs: data.tabs }),\n fromAttrs: (attrs) => ({\n tabs: attrs.array<CodeTabsTab>(\"tabs\") ?? [],\n }),\n};\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code-tabs.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/code-tabs.tsx"],"names":[],"mappings":"AAmBA,OAAO,EAGL,KAAK,YAAY,EAElB,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"code-tabs.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/code-tabs.tsx"],"names":[],"mappings":"AAmBA,OAAO,EAGL,KAAK,YAAY,EAElB,MAAM,uBAAuB,CAAC;AAmjB/B,eAAO,MAAM,aAAa,+CAYxB,CAAC"}
|
|
@@ -5,7 +5,7 @@ import { common, createLowlight } from "lowlight";
|
|
|
5
5
|
import { cn } from "../../utils.js";
|
|
6
6
|
import { defineBlock } from "../types.js";
|
|
7
7
|
import { Popover, PopoverContent, PopoverTrigger, } from "../../components/ui/popover.js";
|
|
8
|
-
import { CodeSurface } from "./HighlightedCode.js";
|
|
8
|
+
import { CodeSurface, DEFAULT_CODE_MAX_LINES } from "./HighlightedCode.js";
|
|
9
9
|
import { codeTabsSchema, codeTabsMdx, } from "./code-tabs.config.js";
|
|
10
10
|
/**
|
|
11
11
|
* Standard `code-tabs` block (STANDARD core library): a vertical file tab rail
|
|
@@ -151,10 +151,30 @@ function CodeTabsRead({ data, blockId, title }) {
|
|
|
151
151
|
const active = data.tabs.find((tab) => tab.id === activeId) ?? data.tabs[0];
|
|
152
152
|
return (_jsxs("section", { className: "plan-block", "data-block-id": blockId, children: [title && _jsx("div", { className: "plan-block-label", children: title }), _jsxs("div", { className: "grid overflow-hidden border-y border-plan-line md:grid-cols-[300px_minmax(0,1fr)]", children: [_jsx("div", { className: "border-plan-line md:border-r", children: data.tabs.map((tab) => (_jsxs("button", { type: "button", "data-plan-interactive": true, className: cn("flex w-full items-start gap-3 border-b border-plan-line px-4 py-4 text-left", tab.id === active?.id
|
|
153
153
|
? "bg-primary/10 text-plan-text dark:bg-primary/20"
|
|
154
|
-
: "text-plan-muted hover:bg-accent/30"), onClick: () => setActiveId(tab.id), children: [_jsx(IconCode, { className: "mt-0.5 size-4 shrink-0" }), _jsxs("span", { className: "min-w-0", children: [_jsx("span", { className: "block truncate font-mono text-sm font-semibold", children: tab.label }), tab.caption && (_jsx("span", { className: "mt-1 block text-xs leading-5", children: tab.caption }))] })] }, tab.id))) }), _jsx("div", { className: "min-w-0 p-5", children: active && (_jsxs(_Fragment, { children: [_jsx("h3", { className: "text-2xl font-semibold tracking-tight", children: active.label }), active.caption && (_jsx("p", { className: "mt-2 text-plan-muted", children: active.caption })), _jsx(CodeSurface, { code: active.code, language: codeTabLanguage(active) })] })) })] })] }));
|
|
154
|
+
: "text-plan-muted hover:bg-accent/30"), onClick: () => setActiveId(tab.id), children: [_jsx(IconCode, { className: "mt-0.5 size-4 shrink-0" }), _jsxs("span", { className: "min-w-0", children: [_jsx("span", { className: "block truncate font-mono text-sm font-semibold", children: tab.label }), tab.caption && (_jsx("span", { className: "mt-1 block text-xs leading-5", children: tab.caption }))] })] }, tab.id))) }), _jsx("div", { className: "min-w-0 p-5", children: active && (_jsxs(_Fragment, { children: [_jsx("h3", { className: "text-2xl font-semibold tracking-tight", children: active.label }), active.caption && (_jsx("p", { className: "mt-2 text-plan-muted", children: active.caption })), _jsx(CodeSurface, { code: active.code, language: codeTabLanguage(active), maxLines: active.maxLines })] })) })] })] }));
|
|
155
155
|
}
|
|
156
156
|
/* ── Edit (code text areas per tab) ────────────────────────────────────────── */
|
|
157
157
|
const inputClass = "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";
|
|
158
|
+
/** Language options for the per-tab picker; "" is the Auto-detect sentinel. */
|
|
159
|
+
const CODE_TAB_LANGUAGES = [
|
|
160
|
+
{ value: "", label: "Auto" },
|
|
161
|
+
{ value: "typescript", label: "TypeScript" },
|
|
162
|
+
{ value: "javascript", label: "JavaScript" },
|
|
163
|
+
{ value: "tsx", label: "TSX" },
|
|
164
|
+
{ value: "jsx", label: "JSX" },
|
|
165
|
+
{ value: "json", label: "JSON" },
|
|
166
|
+
{ value: "html", label: "HTML" },
|
|
167
|
+
{ value: "css", label: "CSS" },
|
|
168
|
+
{ value: "bash", label: "Bash" },
|
|
169
|
+
{ value: "python", label: "Python" },
|
|
170
|
+
{ value: "sql", label: "SQL" },
|
|
171
|
+
{ value: "yaml", label: "YAML" },
|
|
172
|
+
{ value: "markdown", label: "Markdown" },
|
|
173
|
+
{ value: "graphql", label: "GraphQL" },
|
|
174
|
+
{ value: "go", label: "Go" },
|
|
175
|
+
{ value: "rust", label: "Rust" },
|
|
176
|
+
{ value: "diff", label: "Diff" },
|
|
177
|
+
];
|
|
158
178
|
/** Mint a reasonably-unique code-tab id without pulling a dep into core. */
|
|
159
179
|
function newCodeTabId() {
|
|
160
180
|
return `code-tab-${Math.random().toString(36).slice(2, 10)}`;
|
|
@@ -205,25 +225,35 @@ function CodeTabsEdit({ data, onChange, editable, }) {
|
|
|
205
225
|
return (_jsxs("button", { type: "button", role: "tab", "aria-selected": selected, onClick: () => setActiveId(tab.id), className: cn("flex shrink-0 items-center gap-2 whitespace-nowrap rounded-lg border border-transparent px-3 py-2 font-mono text-sm font-semibold transition-colors", selected
|
|
206
226
|
? "bg-primary/10 text-plan-text dark:bg-primary/20"
|
|
207
227
|
: "text-plan-muted hover:bg-plan-block/60 hover:text-plan-text"), children: [_jsx(IconCode, { className: "size-4 shrink-0" }), tab.label] }, tab.id));
|
|
208
|
-
}) }), editable && (_jsx(CodeTabsSettingsPopover, { active: active, tabs: data.tabs, onUpdate: updateTab, onAdd: addTab, onRemove: removeTab }))] }), active && (
|
|
228
|
+
}) }), editable && (_jsx(CodeTabsSettingsPopover, { active: active, tabs: data.tabs, onUpdate: updateTab, onAdd: addTab, onRemove: removeTab }))] }), active && (_jsx(HighlightedCodeTextarea, { value: active.code, editable: editable, label: active.label, language: active.language, onChange: (event) => updateTab(active.id, { code: event.target.value }) }))] }));
|
|
209
229
|
}
|
|
210
230
|
function CodeTabsSettingsPopover({ active, tabs, onUpdate, onAdd, onRemove, }) {
|
|
211
231
|
return (_jsxs(Popover, { children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx("button", { type: "button", "data-plan-interactive": true, "aria-label": "Edit code tabs", className: "flex size-8 shrink-0 items-center justify-center rounded-md text-plan-muted transition-colors hover:text-plan-text focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", children: _jsx(IconPencil, { className: "size-4" }) }) }), _jsxs(PopoverContent, { align: "end", side: "bottom", className: "w-80 p-0", "data-plan-interactive": true, children: [_jsxs("div", { className: "border-b border-border px-3 py-2", children: [_jsx("div", { className: "text-sm font-semibold text-foreground", children: "Code tab settings" }), _jsx("div", { className: "text-xs text-muted-foreground", children: "Rename the active tab or manage its metadata." })] }), _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: "Active tab label" }), _jsx("input", { type: "text", "data-plan-interactive": true, className: inputClass, value: active?.label ?? "", disabled: !active, onChange: (event) => {
|
|
212
232
|
if (!active)
|
|
213
233
|
return;
|
|
214
234
|
onUpdate(active.id, { label: event.target.value });
|
|
215
|
-
} })] }), _jsxs("label", { className: "grid gap-1.5", children: [_jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Language" }), _jsx("
|
|
235
|
+
} })] }), _jsxs("label", { className: "grid gap-1.5", children: [_jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Language" }), _jsx("select", { "data-plan-interactive": true, className: inputClass, value: active?.language ?? "", disabled: !active, onChange: (event) => {
|
|
216
236
|
if (!active)
|
|
217
237
|
return;
|
|
218
238
|
onUpdate(active.id, {
|
|
219
239
|
language: event.target.value || undefined,
|
|
220
240
|
});
|
|
221
|
-
} })] }), _jsxs("label", { className: "grid gap-1.5", children: [_jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Caption" }), _jsx("input", { type: "text", "data-plan-interactive": true, className: inputClass, value: active?.caption ?? "", disabled: !active, onChange: (event) => {
|
|
241
|
+
}, children: CODE_TAB_LANGUAGES.map((option) => (_jsx("option", { value: option.value, children: option.label }, option.value || "auto"))) })] }), _jsxs("label", { className: "grid gap-1.5", children: [_jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Caption" }), _jsx("input", { type: "text", "data-plan-interactive": true, className: inputClass, value: active?.caption ?? "", disabled: !active, onChange: (event) => {
|
|
222
242
|
if (!active)
|
|
223
243
|
return;
|
|
224
244
|
onUpdate(active.id, {
|
|
225
245
|
caption: event.target.value || undefined,
|
|
226
246
|
});
|
|
247
|
+
} })] }), _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: inputClass, placeholder: `${DEFAULT_CODE_MAX_LINES} (default) · 0 = no limit`, value: active?.maxLines ?? "", disabled: !active, onChange: (event) => {
|
|
248
|
+
if (!active)
|
|
249
|
+
return;
|
|
250
|
+
const raw = event.target.value.trim();
|
|
251
|
+
const parsed = raw === "" ? undefined : Number(raw);
|
|
252
|
+
onUpdate(active.id, {
|
|
253
|
+
maxLines: parsed === undefined || Number.isNaN(parsed)
|
|
254
|
+
? undefined
|
|
255
|
+
: Math.max(0, Math.min(2000, Math.floor(parsed))),
|
|
256
|
+
});
|
|
227
257
|
} })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("button", { type: "button", "data-plan-interactive": true, disabled: tabs.length >= 12, onClick: onAdd, className: "inline-flex h-8 items-center gap-1.5 rounded-md border border-border px-2.5 text-xs font-medium text-foreground transition-colors hover:bg-accent disabled:cursor-not-allowed disabled:opacity-50", children: [_jsx(IconPlus, { className: "size-3.5" }), "Add tab"] }), _jsxs("button", { type: "button", "data-plan-interactive": true, disabled: !active || tabs.length <= 1, onClick: () => {
|
|
228
258
|
if (!active)
|
|
229
259
|
return;
|