@agent-native/core 0.41.1 → 0.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -56
- package/dist/action.d.ts +13 -1
- package/dist/action.d.ts.map +1 -1
- package/dist/action.js.map +1 -1
- package/dist/agent/production-agent.d.ts +8 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +93 -0
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/cli/app-skill.d.ts +16 -0
- package/dist/cli/app-skill.d.ts.map +1 -1
- package/dist/cli/app-skill.js +33 -3
- package/dist/cli/app-skill.js.map +1 -1
- package/dist/cli/pr-visual-recap-workflow.d.ts +1 -1
- package/dist/cli/pr-visual-recap-workflow.d.ts.map +1 -1
- package/dist/cli/pr-visual-recap-workflow.js +1 -1
- package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
- package/dist/cli/recap.d.ts.map +1 -1
- package/dist/cli/recap.js +38 -16
- package/dist/cli/recap.js.map +1 -1
- package/dist/cli/skills.d.ts +30 -3
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +180 -114
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +2 -2
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +172 -5
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/blocks/index.d.ts +11 -0
- package/dist/client/blocks/index.d.ts.map +1 -1
- package/dist/client/blocks/index.js +11 -0
- package/dist/client/blocks/index.js.map +1 -1
- package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts +19 -0
- package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/AnnotatedCodeBlock.js +6 -58
- package/dist/client/blocks/library/AnnotatedCodeBlock.js.map +1 -1
- package/dist/client/blocks/library/ApiEndpointBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/ApiEndpointBlock.js +116 -7
- package/dist/client/blocks/library/ApiEndpointBlock.js.map +1 -1
- package/dist/client/blocks/library/DataModelBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/DataModelBlock.js +75 -9
- package/dist/client/blocks/library/DataModelBlock.js.map +1 -1
- package/dist/client/blocks/library/DiffBlock.d.ts +1 -1
- package/dist/client/blocks/library/DiffBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/DiffBlock.js +265 -39
- package/dist/client/blocks/library/DiffBlock.js.map +1 -1
- package/dist/client/blocks/library/FileTreeBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/FileTreeBlock.js +27 -4
- package/dist/client/blocks/library/FileTreeBlock.js.map +1 -1
- package/dist/client/blocks/library/HighlightedCode.d.ts +1 -1
- package/dist/client/blocks/library/HighlightedCode.js +1 -1
- package/dist/client/blocks/library/HighlightedCode.js.map +1 -1
- package/dist/client/blocks/library/JsonExplorerBlock.js +1 -1
- package/dist/client/blocks/library/JsonExplorerBlock.js.map +1 -1
- package/dist/client/blocks/library/MermaidBlock.js +1 -1
- package/dist/client/blocks/library/MermaidBlock.js.map +1 -1
- package/dist/client/blocks/library/annotation-rail.d.ts +115 -0
- package/dist/client/blocks/library/annotation-rail.d.ts.map +1 -0
- package/dist/client/blocks/library/annotation-rail.js +139 -0
- package/dist/client/blocks/library/annotation-rail.js.map +1 -0
- package/dist/client/blocks/library/api-endpoint.config.d.ts +31 -6
- package/dist/client/blocks/library/api-endpoint.config.d.ts.map +1 -1
- package/dist/client/blocks/library/api-endpoint.config.js +30 -6
- package/dist/client/blocks/library/api-endpoint.config.js.map +1 -1
- package/dist/client/blocks/library/callout.config.d.ts +29 -0
- package/dist/client/blocks/library/callout.config.d.ts.map +1 -0
- package/dist/client/blocks/library/callout.config.js +33 -0
- package/dist/client/blocks/library/callout.config.js.map +1 -0
- package/dist/client/blocks/library/callout.d.ts +20 -0
- package/dist/client/blocks/library/callout.d.ts.map +1 -0
- package/dist/client/blocks/library/callout.js +61 -0
- package/dist/client/blocks/library/callout.js.map +1 -0
- package/dist/client/blocks/library/checklist.d.ts.map +1 -1
- package/dist/client/blocks/library/checklist.js +3 -3
- package/dist/client/blocks/library/checklist.js.map +1 -1
- package/dist/client/blocks/library/code.d.ts.map +1 -1
- package/dist/client/blocks/library/code.js +32 -15
- package/dist/client/blocks/library/code.js.map +1 -1
- package/dist/client/blocks/library/columns.d.ts.map +1 -1
- package/dist/client/blocks/library/columns.js +56 -35
- package/dist/client/blocks/library/columns.js.map +1 -1
- package/dist/client/blocks/library/data-model.config.d.ts +17 -0
- package/dist/client/blocks/library/data-model.config.d.ts.map +1 -1
- package/dist/client/blocks/library/data-model.config.js +15 -0
- package/dist/client/blocks/library/data-model.config.js.map +1 -1
- package/dist/client/blocks/library/decision.config.d.ts +37 -0
- package/dist/client/blocks/library/decision.config.d.ts.map +1 -0
- package/dist/client/blocks/library/decision.config.js +32 -0
- package/dist/client/blocks/library/decision.config.js.map +1 -0
- package/dist/client/blocks/library/decision.d.ts +19 -0
- package/dist/client/blocks/library/decision.d.ts.map +1 -0
- package/dist/client/blocks/library/decision.js +119 -0
- package/dist/client/blocks/library/decision.js.map +1 -0
- package/dist/client/blocks/library/diagram.config.d.ts +64 -0
- package/dist/client/blocks/library/diagram.config.d.ts.map +1 -0
- package/dist/client/blocks/library/diagram.config.js +111 -0
- package/dist/client/blocks/library/diagram.config.js.map +1 -0
- package/dist/client/blocks/library/diagram.d.ts +16 -0
- package/dist/client/blocks/library/diagram.d.ts.map +1 -0
- package/dist/client/blocks/library/diagram.js +261 -0
- package/dist/client/blocks/library/diagram.js.map +1 -0
- package/dist/client/blocks/library/diff.config.d.ts +28 -6
- package/dist/client/blocks/library/diff.config.d.ts.map +1 -1
- package/dist/client/blocks/library/diff.config.js +30 -6
- package/dist/client/blocks/library/diff.config.js.map +1 -1
- package/dist/client/blocks/library/question-form.config.d.ts +69 -0
- package/dist/client/blocks/library/question-form.config.d.ts.map +1 -0
- package/dist/client/blocks/library/question-form.config.js +58 -0
- package/dist/client/blocks/library/question-form.config.js.map +1 -0
- package/dist/client/blocks/library/question-form.d.ts +20 -0
- package/dist/client/blocks/library/question-form.d.ts.map +1 -0
- package/dist/client/blocks/library/question-form.js +286 -0
- package/dist/client/blocks/library/question-form.js.map +1 -0
- package/dist/client/blocks/library/sanitize-html.d.ts +5 -0
- package/dist/client/blocks/library/sanitize-html.d.ts.map +1 -0
- package/dist/client/blocks/library/sanitize-html.js +240 -0
- package/dist/client/blocks/library/sanitize-html.js.map +1 -0
- package/dist/client/blocks/library/server-specs.d.ts.map +1 -1
- package/dist/client/blocks/library/server-specs.js +59 -0
- package/dist/client/blocks/library/server-specs.js.map +1 -1
- package/dist/client/blocks/library/specs.d.ts.map +1 -1
- package/dist/client/blocks/library/specs.js +11 -0
- package/dist/client/blocks/library/specs.js.map +1 -1
- package/dist/client/blocks/library/tabs.d.ts.map +1 -1
- package/dist/client/blocks/library/tabs.js +12 -12
- package/dist/client/blocks/library/tabs.js.map +1 -1
- package/dist/client/blocks/library/wireframe-kit.d.ts +260 -0
- package/dist/client/blocks/library/wireframe-kit.d.ts.map +1 -0
- package/dist/client/blocks/library/wireframe-kit.js +920 -0
- package/dist/client/blocks/library/wireframe-kit.js.map +1 -0
- package/dist/client/blocks/library/wireframe.config.d.ts +123 -0
- package/dist/client/blocks/library/wireframe.config.d.ts.map +1 -0
- package/dist/client/blocks/library/wireframe.config.js +294 -0
- package/dist/client/blocks/library/wireframe.config.js.map +1 -0
- package/dist/client/blocks/library/wireframe.d.ts +15 -0
- package/dist/client/blocks/library/wireframe.d.ts.map +1 -0
- package/dist/client/blocks/library/wireframe.js +206 -0
- package/dist/client/blocks/library/wireframe.js.map +1 -0
- package/dist/client/blocks/registry.d.ts +9 -0
- package/dist/client/blocks/registry.d.ts.map +1 -1
- package/dist/client/blocks/registry.js +12 -5
- package/dist/client/blocks/registry.js.map +1 -1
- package/dist/client/blocks/server.d.ts +1 -0
- package/dist/client/blocks/server.d.ts.map +1 -1
- package/dist/client/blocks/server.js +1 -0
- package/dist/client/blocks/server.js.map +1 -1
- package/dist/client/blocks/types.d.ts +10 -2
- package/dist/client/blocks/types.d.ts.map +1 -1
- package/dist/client/blocks/types.js.map +1 -1
- package/dist/client/rich-markdown-editor/DragHandle.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/DragHandle.js +152 -21
- package/dist/client/rich-markdown-editor/DragHandle.js.map +1 -1
- package/dist/client/rich-markdown-editor/RegistryBlockNode.d.ts +25 -1
- package/dist/client/rich-markdown-editor/RegistryBlockNode.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/RegistryBlockNode.js +29 -6
- package/dist/client/rich-markdown-editor/RegistryBlockNode.js.map +1 -1
- package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts +8 -1
- package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/SharedRichEditor.js +5 -1
- package/dist/client/rich-markdown-editor/SharedRichEditor.js.map +1 -1
- package/dist/extensions/actions.d.ts.map +1 -1
- package/dist/extensions/actions.js +159 -12
- package/dist/extensions/actions.js.map +1 -1
- package/dist/extensions/store.d.ts +21 -0
- package/dist/extensions/store.d.ts.map +1 -1
- package/dist/extensions/store.js +33 -1
- package/dist/extensions/store.js.map +1 -1
- package/dist/server/recap-image-route.d.ts.map +1 -1
- package/dist/server/recap-image-route.js +12 -3
- package/dist/server/recap-image-route.js.map +1 -1
- package/dist/styles/agent-native.css +1 -0
- package/dist/styles/blocks.css +1380 -0
- package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +30 -5
- package/docs/content/plan-plugin.md +107 -0
- package/docs/content/pr-visual-recap.md +2 -2
- package/docs/content/skills-guide.md +8 -0
- package/docs/content/template-plan.md +94 -17
- package/package.json +2 -1
- package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +30 -5
- package/docs/content/visual-plans.md +0 -80
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { IconPencil } from "@tabler/icons-react";
|
|
3
|
+
import { cn } from "../../utils.js";
|
|
4
|
+
import { defineBlock } from "../types.js";
|
|
5
|
+
import { CALLOUT_TONES, calloutMdx, calloutSchema, } from "./callout.config.js";
|
|
6
|
+
/**
|
|
7
|
+
* Standard `callout` block — an emphasized note with a tone (info / decision /
|
|
8
|
+
* risk / warning / success) and a markdown body. Lives in core so any app can
|
|
9
|
+
* register it (it originated in the plan template).
|
|
10
|
+
*
|
|
11
|
+
* The section carries BOTH the app-neutral `an-callout` classes (styled by
|
|
12
|
+
* core's `blocks.css` with shadcn theme tokens, so it looks right in any app)
|
|
13
|
+
* and the legacy `plan-callout` classes (styled by the plan template's own
|
|
14
|
+
* stylesheet). Plan therefore renders byte-identically to before; content (and
|
|
15
|
+
* any other app) gets the theme-token treatment. `data-tone` drives the accent
|
|
16
|
+
* in both. The body renders through `ctx.renderMarkdown` so each app supplies
|
|
17
|
+
* its own GFM renderer (plan's react-markdown reader, content's, etc.).
|
|
18
|
+
*/
|
|
19
|
+
export function CalloutBlock({ data, blockId, title, ctx, }) {
|
|
20
|
+
return (_jsxs("section", { className: "an-block an-callout plan-block plan-callout", "data-block-id": blockId, "data-tone": data.tone, children: [title && _jsx("div", { className: "an-block-label plan-block-label", children: title }), ctx.renderMarkdown?.(data.body) ?? (_jsx("div", { className: "an-callout-body whitespace-pre-wrap", children: data.body }))] }));
|
|
21
|
+
}
|
|
22
|
+
export function CalloutBlockEdit({ data, onChange, editable, blockId, title, summary, ctx, }) {
|
|
23
|
+
const activeTone = data.tone ?? "info";
|
|
24
|
+
const setTone = (tone) => onChange({ ...data, tone });
|
|
25
|
+
const toneSettings = editable
|
|
26
|
+
? ctx.renderEditSurface?.({
|
|
27
|
+
title: "Callout",
|
|
28
|
+
blockId,
|
|
29
|
+
blockType: "callout",
|
|
30
|
+
blockTitle: title,
|
|
31
|
+
blockSummary: summary,
|
|
32
|
+
blockData: data,
|
|
33
|
+
trigger: (_jsx("button", { type: "button", "data-plan-interactive": true, "aria-label": "Edit callout type", className: "an-block-edit-trigger flex size-7 items-center justify-center rounded-md text-muted-foreground opacity-0 transition-[color,opacity] hover:text-foreground focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring group-hover/callout:opacity-100", children: _jsx(IconPencil, { className: "size-4" }) })),
|
|
34
|
+
children: (_jsxs("div", { className: "grid gap-2", children: [_jsx("div", { className: "text-xs font-medium text-muted-foreground", children: "Type" }), _jsx("div", { className: "flex max-w-full flex-wrap items-center gap-1", children: CALLOUT_TONES.map((tone) => (_jsx("button", { type: "button", className: cn("rounded-md border border-transparent px-2 py-1 text-xs font-semibold capitalize transition-colors", activeTone === tone
|
|
35
|
+
? "border-border bg-muted text-foreground"
|
|
36
|
+
: "text-muted-foreground hover:bg-muted hover:text-foreground"), "aria-pressed": activeTone === tone, onClick: () => setTone(tone), children: tone }, tone))) })] })),
|
|
37
|
+
})
|
|
38
|
+
: null;
|
|
39
|
+
return (_jsxs("section", { className: "an-block an-callout plan-block plan-callout group/callout relative pr-10", "data-block-id": blockId, "data-tone": data.tone, children: [title && _jsx("div", { className: "an-block-label plan-block-label", children: title }), toneSettings && (_jsx("div", { className: "absolute right-2 top-2 z-10", children: toneSettings })), ctx.renderMarkdownEditor?.({
|
|
40
|
+
value: data.body,
|
|
41
|
+
onChange: (body) => onChange({ ...data, body }),
|
|
42
|
+
editable,
|
|
43
|
+
blockId,
|
|
44
|
+
}) ?? (_jsx("textarea", { "data-plan-interactive": true, className: "min-h-[120px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm leading-6 text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", value: data.body, disabled: !editable, onChange: (event) => onChange({ ...data, body: event.currentTarget.value }) }))] }));
|
|
45
|
+
}
|
|
46
|
+
/** Full client spec for the shared `callout` block (schema + MDX + Read/Edit). */
|
|
47
|
+
export const calloutBlock = defineBlock({
|
|
48
|
+
type: "callout",
|
|
49
|
+
schema: calloutSchema,
|
|
50
|
+
mdx: calloutMdx,
|
|
51
|
+
Read: CalloutBlock,
|
|
52
|
+
Edit: CalloutBlockEdit,
|
|
53
|
+
placement: ["block"],
|
|
54
|
+
editSurface: "inline",
|
|
55
|
+
label: "Callout",
|
|
56
|
+
description: "An emphasized note with a tone (info/decision/risk/warning/success) and a markdown body.",
|
|
57
|
+
// `body` is a `markdown(min(1))` field, so a fresh callout needs non-empty
|
|
58
|
+
// placeholder prose; `tone` defaults to the neutral "info" register.
|
|
59
|
+
empty: () => ({ tone: "info", body: "Callout text" }),
|
|
60
|
+
});
|
|
61
|
+
//# sourceMappingURL=callout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"callout.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/callout.tsx"],"names":[],"mappings":"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","sourcesContent":["import { IconPencil } from \"@tabler/icons-react\";\nimport { cn } from \"../../utils.js\";\nimport { defineBlock } from \"../types.js\";\nimport type { BlockReadProps, BlockEditProps } from \"../types.js\";\nimport {\n CALLOUT_TONES,\n calloutMdx,\n calloutSchema,\n type CalloutData,\n type CalloutTone,\n} from \"./callout.config.js\";\n\n/**\n * Standard `callout` block — an emphasized note with a tone (info / decision /\n * risk / warning / success) and a markdown body. Lives in core so any app can\n * register it (it originated in the plan template).\n *\n * The section carries BOTH the app-neutral `an-callout` classes (styled by\n * core's `blocks.css` with shadcn theme tokens, so it looks right in any app)\n * and the legacy `plan-callout` classes (styled by the plan template's own\n * stylesheet). Plan therefore renders byte-identically to before; content (and\n * any other app) gets the theme-token treatment. `data-tone` drives the accent\n * in both. The body renders through `ctx.renderMarkdown` so each app supplies\n * its own GFM renderer (plan's react-markdown reader, content's, etc.).\n */\nexport function CalloutBlock({\n data,\n blockId,\n title,\n ctx,\n}: BlockReadProps<CalloutData>) {\n return (\n <section\n className=\"an-block an-callout plan-block plan-callout\"\n data-block-id={blockId}\n data-tone={data.tone}\n >\n {title && <div className=\"an-block-label plan-block-label\">{title}</div>}\n {ctx.renderMarkdown?.(data.body) ?? (\n <div className=\"an-callout-body whitespace-pre-wrap\">{data.body}</div>\n )}\n </section>\n );\n}\n\nexport function CalloutBlockEdit({\n data,\n onChange,\n editable,\n blockId,\n title,\n summary,\n ctx,\n}: BlockEditProps<CalloutData>) {\n const activeTone = data.tone ?? \"info\";\n const setTone = (tone: CalloutTone) => onChange({ ...data, tone });\n const toneSettings = editable\n ? ctx.renderEditSurface?.({\n title: \"Callout\",\n blockId,\n blockType: \"callout\",\n blockTitle: title,\n blockSummary: summary,\n blockData: data,\n trigger: (\n <button\n type=\"button\"\n data-plan-interactive\n aria-label=\"Edit callout type\"\n className=\"an-block-edit-trigger flex size-7 items-center justify-center rounded-md text-muted-foreground opacity-0 transition-[color,opacity] hover:text-foreground focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring group-hover/callout:opacity-100\"\n >\n <IconPencil className=\"size-4\" />\n </button>\n ),\n children: (\n <div className=\"grid gap-2\">\n <div className=\"text-xs font-medium text-muted-foreground\">\n Type\n </div>\n <div className=\"flex max-w-full flex-wrap items-center gap-1\">\n {CALLOUT_TONES.map((tone) => (\n <button\n key={tone}\n type=\"button\"\n className={cn(\n \"rounded-md border border-transparent px-2 py-1 text-xs font-semibold capitalize transition-colors\",\n activeTone === tone\n ? \"border-border bg-muted text-foreground\"\n : \"text-muted-foreground hover:bg-muted hover:text-foreground\",\n )}\n aria-pressed={activeTone === tone}\n onClick={() => setTone(tone)}\n >\n {tone}\n </button>\n ))}\n </div>\n </div>\n ),\n })\n : null;\n\n return (\n <section\n className=\"an-block an-callout plan-block plan-callout group/callout relative pr-10\"\n data-block-id={blockId}\n data-tone={data.tone}\n >\n {title && <div className=\"an-block-label plan-block-label\">{title}</div>}\n {toneSettings && (\n <div className=\"absolute right-2 top-2 z-10\">{toneSettings}</div>\n )}\n {ctx.renderMarkdownEditor?.({\n value: data.body,\n onChange: (body) => onChange({ ...data, body }),\n editable,\n blockId,\n }) ?? (\n <textarea\n data-plan-interactive\n className=\"min-h-[120px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm leading-6 text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring\"\n value={data.body}\n disabled={!editable}\n onChange={(event) =>\n onChange({ ...data, body: event.currentTarget.value })\n }\n />\n )}\n </section>\n );\n}\n\n/** Full client spec for the shared `callout` block (schema + MDX + Read/Edit). */\nexport const calloutBlock = defineBlock<CalloutData>({\n type: \"callout\",\n schema: calloutSchema,\n mdx: calloutMdx,\n Read: CalloutBlock,\n Edit: CalloutBlockEdit,\n placement: [\"block\"],\n editSurface: \"inline\",\n label: \"Callout\",\n description:\n \"An emphasized note with a tone (info/decision/risk/warning/success) and a markdown body.\",\n // `body` is a `markdown(min(1))` field, so a fresh callout needs non-empty\n // placeholder prose; `tone` defaults to the neutral \"info\" register.\n empty: () => ({ tone: \"info\", body: \"Callout text\" }),\n});\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checklist.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/checklist.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAGL,KAAK,aAAa,EAEnB,MAAM,uBAAuB,CAAC;AAsB/B;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAC7B,IAAI,EACJ,OAAO,EACP,KAAK,EACL,QAAQ,GACT,EAAE,cAAc,CAAC,aAAa,CAAC,GAAG;IACjC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC,2CA8BA;
|
|
1
|
+
{"version":3,"file":"checklist.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/checklist.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAGL,KAAK,aAAa,EAEnB,MAAM,uBAAuB,CAAC;AAsB/B;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAC7B,IAAI,EACJ,OAAO,EACP,KAAK,EACL,QAAQ,GACT,EAAE,cAAc,CAAC,aAAa,CAAC,GAAG;IACjC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC,2CA8BA;AA4BD,6DAA6D;AAC7D,wBAAgB,eAAe,CAAC,EAC9B,IAAI,EACJ,QAAQ,EACR,QAAQ,GACT,EAAE,cAAc,CAAC,aAAa,CAAC,2CAuE/B;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,cAAc,gDAezB,CAAC"}
|
|
@@ -27,15 +27,15 @@ function newItemId() {
|
|
|
27
27
|
* the markers render statically.
|
|
28
28
|
*/
|
|
29
29
|
export function ChecklistBlock({ data, blockId, title, onToggle, }) {
|
|
30
|
-
return (_jsxs("section", { className: "plan-block", "data-block-id": blockId, children: [title && _jsx("div", { className: "plan-block-label", children: title }), _jsx("div", { className: "grid gap-
|
|
30
|
+
return (_jsxs("section", { className: "plan-block", "data-block-id": blockId, children: [title && _jsx("div", { className: "plan-block-label", children: title }), _jsx("div", { className: "grid gap-2", children: data.items.map((item) => onToggle ? (_jsxs("button", { type: "button", "data-plan-interactive": true, className: "flex w-full items-start gap-3 text-left text-plan-muted", onClick: () => onToggle(item.id), children: [_jsx(ChecklistMarker, { checked: item.checked }), _jsx(ChecklistItemBody, { item: item })] }, item.id)) : (_jsxs("div", { className: "flex w-full items-start gap-3 text-left text-plan-muted", children: [_jsx(ChecklistMarker, { checked: item.checked }), _jsx(ChecklistItemBody, { item: item })] }, item.id))) })] }));
|
|
31
31
|
}
|
|
32
32
|
function ChecklistMarker({ checked }) {
|
|
33
|
-
return (_jsx("span", { className: cn("mt-1 flex size-5 items-center justify-center rounded border", checked
|
|
33
|
+
return (_jsx("span", { className: cn("mt-1 flex size-5 shrink-0 items-center justify-center rounded border", checked
|
|
34
34
|
? "border-primary bg-primary text-primary-foreground"
|
|
35
35
|
: "border-plan-line"), children: checked && _jsx(IconCheck, { className: "size-3.5" }) }));
|
|
36
36
|
}
|
|
37
37
|
function ChecklistItemBody({ item }) {
|
|
38
|
-
return (_jsxs("span", { children: [_jsx("span", { className: "block text-plan-text", children: item.label }), item.note && _jsx("span", { className: "block text-sm", children: item.note })] }));
|
|
38
|
+
return (_jsxs("span", { className: "min-w-0 flex-1", children: [_jsx("span", { className: "block break-words text-plan-text", children: item.label }), item.note && (_jsx("span", { className: "block break-words text-sm", children: item.note }))] }));
|
|
39
39
|
}
|
|
40
40
|
/** Custom editor: toggle, relabel, add, and remove items. */
|
|
41
41
|
export function ChecklistEditor({ data, onChange, editable, }) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checklist.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/checklist.tsx"],"names":[],"mappings":"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","sourcesContent":["import { IconCheck, IconPlus, IconX } from \"@tabler/icons-react\";\nimport { cn } from \"../../utils.js\";\nimport { defineBlock } from \"../types.js\";\nimport type { BlockReadProps, BlockEditProps } from \"../types.js\";\nimport {\n checklistSchema,\n checklistMdx,\n type ChecklistData,\n type ChecklistItem,\n} from \"./checklist.config.js\";\n\n/**\n * Standard `checklist` block. A list of toggleable items, each with a label and\n * an optional note. Lives in core so any app can register it.\n *\n * `Read` mirrors the legacy plan `PlanBlockView` checklist branch byte-for-byte\n * (same `plan-block` section, toggle buttons, `IconCheck` marker, and the\n * existing toggle-via-`onChange` behavior) so converting the block to the\n * registry does not change rendered output. The plan CSS classes\n * (`plan-block`, `text-plan-*`, `border-plan-line`) resolve against the plan\n * app's stylesheet at render time, exactly as before.\n *\n * `Edit` is a custom editor (the schema auto-editor can't edit an array of\n * objects): it lets you add, remove, toggle, and relabel items inline.\n */\n\n/** Mint a reasonably-unique item id without pulling a dep into core. */\nfunction newItemId(): string {\n return `item-${Math.random().toString(36).slice(2, 10)}`;\n}\n\n/**\n * Read renderer. Note `onToggle` is supplied by the block dispatcher for the\n * historical click-to-toggle behavior; in pure read contexts it is omitted and\n * the markers render statically.\n */\nexport function ChecklistBlock({\n data,\n blockId,\n title,\n onToggle,\n}: BlockReadProps<ChecklistData> & {\n onToggle?: (itemId: string) => void;\n}) {\n return (\n <section className=\"plan-block\" data-block-id={blockId}>\n {title && <div className=\"plan-block-label\">{title}</div>}\n <div className=\"grid gap-3\">\n {data.items.map((item) =>\n onToggle ? (\n <button\n key={item.id}\n type=\"button\"\n data-plan-interactive\n className=\"flex items-start gap-3 text-left text-plan-muted\"\n onClick={() => onToggle(item.id)}\n >\n <ChecklistMarker checked={item.checked} />\n <ChecklistItemBody item={item} />\n </button>\n ) : (\n <div\n key={item.id}\n className=\"flex items-start gap-3 text-left text-plan-muted\"\n >\n <ChecklistMarker checked={item.checked} />\n <ChecklistItemBody item={item} />\n </div>\n ),\n )}\n </div>\n </section>\n );\n}\n\nfunction ChecklistMarker({ checked }: { checked?: boolean }) {\n return (\n <span\n className={cn(\n \"mt-1 flex size-5 items-center justify-center rounded border\",\n checked\n ? \"border-primary bg-primary text-primary-foreground\"\n : \"border-plan-line\",\n )}\n >\n {checked && <IconCheck className=\"size-3.5\" />}\n </span>\n );\n}\n\nfunction ChecklistItemBody({ item }: { item: ChecklistItem }) {\n return (\n <span>\n <span className=\"block text-plan-text\">{item.label}</span>\n {item.note && <span className=\"block text-sm\">{item.note}</span>}\n </span>\n );\n}\n\n/** Custom editor: toggle, relabel, add, and remove items. */\nexport function ChecklistEditor({\n data,\n onChange,\n editable,\n}: BlockEditProps<ChecklistData>) {\n const items = data.items;\n\n const update = (next: ChecklistItem[]) => onChange({ items: next });\n\n const toggle = (id: string) =>\n update(\n items.map((item) =>\n item.id === id ? { ...item, checked: !item.checked } : item,\n ),\n );\n\n const setLabel = (id: string, label: string) =>\n update(items.map((item) => (item.id === id ? { ...item, label } : item)));\n\n const remove = (id: string) => update(items.filter((item) => item.id !== id));\n\n const add = () =>\n update([...items, { id: newItemId(), label: \"\", checked: false }]);\n\n return (\n <div className=\"grid gap-2\">\n {items.map((item) => (\n <div key={item.id} className=\"group flex items-start gap-2\">\n <button\n type=\"button\"\n data-plan-interactive\n aria-label={item.checked ? \"Mark incomplete\" : \"Mark complete\"}\n className={cn(\n \"mt-1 flex size-5 shrink-0 items-center justify-center rounded border\",\n item.checked\n ? \"border-primary bg-primary text-primary-foreground\"\n : \"border-plan-line\",\n )}\n onClick={() => toggle(item.id)}\n >\n {item.checked && <IconCheck className=\"size-3.5\" />}\n </button>\n <input\n type=\"text\"\n data-plan-interactive\n className=\"flex h-9 w-full rounded-md bg-transparent px-3 py-1 text-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50\"\n placeholder=\"Checklist item\"\n value={item.label}\n disabled={!editable}\n onChange={(event) => setLabel(item.id, event.target.value)}\n />\n <button\n type=\"button\"\n data-plan-interactive\n aria-label=\"Remove item\"\n className=\"mt-1 flex size-7 shrink-0 items-center justify-center rounded text-muted-foreground opacity-0 transition-opacity hover:bg-muted hover:text-foreground focus-visible:opacity-100 group-hover:opacity-100 group-focus-within:opacity-100 disabled:opacity-50\"\n disabled={!editable}\n onClick={() => remove(item.id)}\n >\n <IconX className=\"size-4\" />\n </button>\n </div>\n ))}\n <button\n type=\"button\"\n data-plan-interactive\n className=\"flex items-center gap-1.5 self-start rounded-md px-2 py-1 text-sm text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-50\"\n disabled={!editable}\n onClick={add}\n >\n <IconPlus className=\"size-4\" />\n Add item\n </button>\n </div>\n );\n}\n\n/**\n * The standard checklist block spec (with React `Read`/`Edit`). Apps register\n * this in their browser registry. The schema + MDX config come from\n * `./checklist.config.ts`, the exact same object server / agent code registers,\n * so rendering and source round-trip never drift.\n *\n * `Read` is typed against `BlockReadProps<ChecklistData>`; the optional\n * `onToggle` the dispatcher injects for the legacy click-to-toggle behavior is\n * an extra prop the registry's `BlockView` passes through harmlessly when\n * present (it lives outside `BlockReadProps`, so it's wired by the app's block\n * dispatch rather than the generic `BlockView`).\n */\nexport const checklistBlock = defineBlock<ChecklistData>({\n type: \"checklist\",\n schema: checklistSchema,\n mdx: checklistMdx,\n Read: ChecklistBlock as never,\n Edit: ChecklistEditor,\n placement: [\"block\"],\n editSurface: \"inline\",\n // A checklist maps to NFM to-do items, so it round-trips to Notion.\n notionCompatible: true,\n label: \"Checklist\",\n icon: IconCheck,\n description:\n \"A list of toggleable items, each with a label and an optional note.\",\n empty: () => ({ items: [] }),\n});\n"]}
|
|
1
|
+
{"version":3,"file":"checklist.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/checklist.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,EACL,eAAe,EACf,YAAY,GAGb,MAAM,uBAAuB,CAAC;AAE/B;;;;;;;;;;;;;GAaG;AAEH,wEAAwE;AACxE,SAAS,SAAS;IAChB,OAAO,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;AAC3D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,EAC7B,IAAI,EACJ,OAAO,EACP,KAAK,EACL,QAAQ,GAGT;IACC,OAAO,CACL,mBAAS,SAAS,EAAC,YAAY,mBAAgB,OAAO,aACnD,KAAK,IAAI,cAAK,SAAS,EAAC,kBAAkB,YAAE,KAAK,GAAO,EACzD,cAAK,SAAS,EAAC,YAAY,YACxB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CACvB,QAAQ,CAAC,CAAC,CAAC,CACT,kBAEE,IAAI,EAAC,QAAQ,iCAEb,SAAS,EAAC,yDAAyD,EACnE,OAAO,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,aAEhC,KAAC,eAAe,IAAC,OAAO,EAAE,IAAI,CAAC,OAAO,GAAI,EAC1C,KAAC,iBAAiB,IAAC,IAAI,EAAE,IAAI,GAAI,KAP5B,IAAI,CAAC,EAAE,CAQL,CACV,CAAC,CAAC,CAAC,CACF,eAEE,SAAS,EAAC,yDAAyD,aAEnE,KAAC,eAAe,IAAC,OAAO,EAAE,IAAI,CAAC,OAAO,GAAI,EAC1C,KAAC,iBAAiB,IAAC,IAAI,EAAE,IAAI,GAAI,KAJ5B,IAAI,CAAC,EAAE,CAKR,CACP,CACF,GACG,IACE,CACX,CAAC;AACJ,CAAC;AAED,SAAS,eAAe,CAAC,EAAE,OAAO,EAAyB;IACzD,OAAO,CACL,eACE,SAAS,EAAE,EAAE,CACX,sEAAsE,EACtE,OAAO;YACL,CAAC,CAAC,mDAAmD;YACrD,CAAC,CAAC,kBAAkB,CACvB,YAEA,OAAO,IAAI,KAAC,SAAS,IAAC,SAAS,EAAC,UAAU,GAAG,GACzC,CACR,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CAAC,EAAE,IAAI,EAA2B;IAC1D,OAAO,CACL,gBAAM,SAAS,EAAC,gBAAgB,aAC9B,eAAM,SAAS,EAAC,kCAAkC,YAAE,IAAI,CAAC,KAAK,GAAQ,EACrE,IAAI,CAAC,IAAI,IAAI,CACZ,eAAM,SAAS,EAAC,2BAA2B,YAAE,IAAI,CAAC,IAAI,GAAQ,CAC/D,IACI,CACR,CAAC;AACJ,CAAC;AAED,6DAA6D;AAC7D,MAAM,UAAU,eAAe,CAAC,EAC9B,IAAI,EACJ,QAAQ,EACR,QAAQ,GACsB;IAC9B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;IAEzB,MAAM,MAAM,GAAG,CAAC,IAAqB,EAAE,EAAE,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAEpE,MAAM,MAAM,GAAG,CAAC,EAAU,EAAE,EAAE,CAC5B,MAAM,CACJ,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CACjB,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAC5D,CACF,CAAC;IAEJ,MAAM,QAAQ,GAAG,CAAC,EAAU,EAAE,KAAa,EAAE,EAAE,CAC7C,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAE5E,MAAM,MAAM,GAAG,CAAC,EAAU,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;IAE9E,MAAM,GAAG,GAAG,GAAG,EAAE,CACf,MAAM,CAAC,CAAC,GAAG,KAAK,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;IAErE,OAAO,CACL,eAAK,SAAS,EAAC,YAAY,aACxB,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CACnB,eAAmB,SAAS,EAAC,8BAA8B,aACzD,iBACE,IAAI,EAAC,QAAQ,+CAED,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,eAAe,EAC9D,SAAS,EAAE,EAAE,CACX,sEAAsE,EACtE,IAAI,CAAC,OAAO;4BACV,CAAC,CAAC,mDAAmD;4BACrD,CAAC,CAAC,kBAAkB,CACvB,EACD,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,YAE7B,IAAI,CAAC,OAAO,IAAI,KAAC,SAAS,IAAC,SAAS,EAAC,UAAU,GAAG,GAC5C,EACT,gBACE,IAAI,EAAC,MAAM,iCAEX,SAAS,EAAC,yOAAyO,EACnP,WAAW,EAAC,gBAAgB,EAC5B,KAAK,EAAE,IAAI,CAAC,KAAK,EACjB,QAAQ,EAAE,CAAC,QAAQ,EACnB,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,GAC1D,EACF,iBACE,IAAI,EAAC,QAAQ,+CAEF,aAAa,EACxB,SAAS,EAAC,4PAA4P,EACtQ,QAAQ,EAAE,CAAC,QAAQ,EACnB,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,YAE9B,KAAC,KAAK,IAAC,SAAS,EAAC,QAAQ,GAAG,GACrB,KAjCD,IAAI,CAAC,EAAE,CAkCX,CACP,CAAC,EACF,kBACE,IAAI,EAAC,QAAQ,iCAEb,SAAS,EAAC,kJAAkJ,EAC5J,QAAQ,EAAE,CAAC,QAAQ,EACnB,OAAO,EAAE,GAAG,aAEZ,KAAC,QAAQ,IAAC,SAAS,EAAC,QAAQ,GAAG,gBAExB,IACL,CACP,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,WAAW,CAAgB;IACvD,IAAI,EAAE,WAAW;IACjB,MAAM,EAAE,eAAe;IACvB,GAAG,EAAE,YAAY;IACjB,IAAI,EAAE,cAAuB;IAC7B,IAAI,EAAE,eAAe;IACrB,SAAS,EAAE,CAAC,OAAO,CAAC;IACpB,WAAW,EAAE,QAAQ;IACrB,oEAAoE;IACpE,gBAAgB,EAAE,IAAI;IACtB,KAAK,EAAE,WAAW;IAClB,IAAI,EAAE,SAAS;IACf,WAAW,EACT,qEAAqE;IACvE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;CAC7B,CAAC,CAAC","sourcesContent":["import { IconCheck, IconPlus, IconX } from \"@tabler/icons-react\";\nimport { cn } from \"../../utils.js\";\nimport { defineBlock } from \"../types.js\";\nimport type { BlockReadProps, BlockEditProps } from \"../types.js\";\nimport {\n checklistSchema,\n checklistMdx,\n type ChecklistData,\n type ChecklistItem,\n} from \"./checklist.config.js\";\n\n/**\n * Standard `checklist` block. A list of toggleable items, each with a label and\n * an optional note. Lives in core so any app can register it.\n *\n * `Read` mirrors the legacy plan `PlanBlockView` checklist branch byte-for-byte\n * (same `plan-block` section, toggle buttons, `IconCheck` marker, and the\n * existing toggle-via-`onChange` behavior) so converting the block to the\n * registry does not change rendered output. The plan CSS classes\n * (`plan-block`, `text-plan-*`, `border-plan-line`) resolve against the plan\n * app's stylesheet at render time, exactly as before.\n *\n * `Edit` is a custom editor (the schema auto-editor can't edit an array of\n * objects): it lets you add, remove, toggle, and relabel items inline.\n */\n\n/** Mint a reasonably-unique item id without pulling a dep into core. */\nfunction newItemId(): string {\n return `item-${Math.random().toString(36).slice(2, 10)}`;\n}\n\n/**\n * Read renderer. Note `onToggle` is supplied by the block dispatcher for the\n * historical click-to-toggle behavior; in pure read contexts it is omitted and\n * the markers render statically.\n */\nexport function ChecklistBlock({\n data,\n blockId,\n title,\n onToggle,\n}: BlockReadProps<ChecklistData> & {\n onToggle?: (itemId: string) => void;\n}) {\n return (\n <section className=\"plan-block\" data-block-id={blockId}>\n {title && <div className=\"plan-block-label\">{title}</div>}\n <div className=\"grid gap-2\">\n {data.items.map((item) =>\n onToggle ? (\n <button\n key={item.id}\n type=\"button\"\n data-plan-interactive\n className=\"flex w-full items-start gap-3 text-left text-plan-muted\"\n onClick={() => onToggle(item.id)}\n >\n <ChecklistMarker checked={item.checked} />\n <ChecklistItemBody item={item} />\n </button>\n ) : (\n <div\n key={item.id}\n className=\"flex w-full items-start gap-3 text-left text-plan-muted\"\n >\n <ChecklistMarker checked={item.checked} />\n <ChecklistItemBody item={item} />\n </div>\n ),\n )}\n </div>\n </section>\n );\n}\n\nfunction ChecklistMarker({ checked }: { checked?: boolean }) {\n return (\n <span\n className={cn(\n \"mt-1 flex size-5 shrink-0 items-center justify-center rounded border\",\n checked\n ? \"border-primary bg-primary text-primary-foreground\"\n : \"border-plan-line\",\n )}\n >\n {checked && <IconCheck className=\"size-3.5\" />}\n </span>\n );\n}\n\nfunction ChecklistItemBody({ item }: { item: ChecklistItem }) {\n return (\n <span className=\"min-w-0 flex-1\">\n <span className=\"block break-words text-plan-text\">{item.label}</span>\n {item.note && (\n <span className=\"block break-words text-sm\">{item.note}</span>\n )}\n </span>\n );\n}\n\n/** Custom editor: toggle, relabel, add, and remove items. */\nexport function ChecklistEditor({\n data,\n onChange,\n editable,\n}: BlockEditProps<ChecklistData>) {\n const items = data.items;\n\n const update = (next: ChecklistItem[]) => onChange({ items: next });\n\n const toggle = (id: string) =>\n update(\n items.map((item) =>\n item.id === id ? { ...item, checked: !item.checked } : item,\n ),\n );\n\n const setLabel = (id: string, label: string) =>\n update(items.map((item) => (item.id === id ? { ...item, label } : item)));\n\n const remove = (id: string) => update(items.filter((item) => item.id !== id));\n\n const add = () =>\n update([...items, { id: newItemId(), label: \"\", checked: false }]);\n\n return (\n <div className=\"grid gap-2\">\n {items.map((item) => (\n <div key={item.id} className=\"group flex items-start gap-2\">\n <button\n type=\"button\"\n data-plan-interactive\n aria-label={item.checked ? \"Mark incomplete\" : \"Mark complete\"}\n className={cn(\n \"mt-1 flex size-5 shrink-0 items-center justify-center rounded border\",\n item.checked\n ? \"border-primary bg-primary text-primary-foreground\"\n : \"border-plan-line\",\n )}\n onClick={() => toggle(item.id)}\n >\n {item.checked && <IconCheck className=\"size-3.5\" />}\n </button>\n <input\n type=\"text\"\n data-plan-interactive\n className=\"flex h-9 w-full rounded-md bg-transparent px-3 py-1 text-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50\"\n placeholder=\"Checklist item\"\n value={item.label}\n disabled={!editable}\n onChange={(event) => setLabel(item.id, event.target.value)}\n />\n <button\n type=\"button\"\n data-plan-interactive\n aria-label=\"Remove item\"\n className=\"mt-1 flex size-7 shrink-0 items-center justify-center rounded text-muted-foreground opacity-0 transition-opacity hover:bg-muted hover:text-foreground focus-visible:opacity-100 group-hover:opacity-100 group-focus-within:opacity-100 disabled:opacity-50\"\n disabled={!editable}\n onClick={() => remove(item.id)}\n >\n <IconX className=\"size-4\" />\n </button>\n </div>\n ))}\n <button\n type=\"button\"\n data-plan-interactive\n className=\"flex items-center gap-1.5 self-start rounded-md px-2 py-1 text-sm text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-50\"\n disabled={!editable}\n onClick={add}\n >\n <IconPlus className=\"size-4\" />\n Add item\n </button>\n </div>\n );\n}\n\n/**\n * The standard checklist block spec (with React `Read`/`Edit`). Apps register\n * this in their browser registry. The schema + MDX config come from\n * `./checklist.config.ts`, the exact same object server / agent code registers,\n * so rendering and source round-trip never drift.\n *\n * `Read` is typed against `BlockReadProps<ChecklistData>`; the optional\n * `onToggle` the dispatcher injects for the legacy click-to-toggle behavior is\n * an extra prop the registry's `BlockView` passes through harmlessly when\n * present (it lives outside `BlockReadProps`, so it's wired by the app's block\n * dispatch rather than the generic `BlockView`).\n */\nexport const checklistBlock = defineBlock<ChecklistData>({\n type: \"checklist\",\n schema: checklistSchema,\n mdx: checklistMdx,\n Read: ChecklistBlock as never,\n Edit: ChecklistEditor,\n placement: [\"block\"],\n editSurface: \"inline\",\n // A checklist maps to NFM to-do items, so it round-trips to Notion.\n notionCompatible: true,\n label: \"Checklist\",\n icon: IconCheck,\n description:\n \"A list of toggleable items, each with a label and an optional note.\",\n empty: () => ({ items: [] }),\n});\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/code.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"code.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/code.tsx"],"names":[],"mappings":"AAuBA,OAAO,EAAuB,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAiWtE,eAAO,MAAM,SAAS,2CAYpB,CAAC"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useId,
|
|
3
|
-
import { IconCheck, IconCode, IconCopy } from "@tabler/icons-react";
|
|
2
|
+
import { useId, useMemo, useRef, useState, } from "react";
|
|
3
|
+
import { IconCheck, IconCode, IconCopy, IconPencil } from "@tabler/icons-react";
|
|
4
4
|
import { cn } from "../../utils.js";
|
|
5
|
+
import { Popover, PopoverContent, PopoverTrigger, } from "../../components/ui/popover.js";
|
|
5
6
|
import { defineBlock } from "../types.js";
|
|
6
|
-
import { CodeSurface } from "./HighlightedCode.js";
|
|
7
|
+
import { CodeSurface, DEFAULT_CODE_MAX_LINES } from "./HighlightedCode.js";
|
|
7
8
|
import { highlightCode, inferLanguageFromFilename, normalizeCodeLanguage, } from "./code-highlight.js";
|
|
8
9
|
import { codeSchema, codeMdx } from "./code.config.js";
|
|
9
10
|
/**
|
|
@@ -54,30 +55,46 @@ function CodeRead({ data, blockId }) {
|
|
|
54
55
|
return (_jsx("section", { className: "plan-block", "data-block-id": blockId, children: _jsxs("div", { className: "plan-code group relative", children: [data.filename && (_jsxs("div", { className: "plan-code-head", children: [_jsxs("span", { className: "plan-code-filename", children: [_jsx(IconCode, { className: "size-4 shrink-0 opacity-70" }), data.filename] }), _jsx("span", { className: "plan-code-chrome", children: _jsx(CopyButton, { value: data.code }) })] })), _jsx(CodeSurface, { code: data.code, language: language, maxLines: data.maxLines, className: data.filename ? "mt-0" : "mt-0" }), !data.filename && (_jsx("span", { className: "plan-code-chrome plan-code-chrome-float", children: _jsx(CopyButton, { value: data.code }) })), data.caption && _jsx("p", { className: "plan-code-caption", children: data.caption })] }) }));
|
|
55
56
|
}
|
|
56
57
|
/* ── Edit (single border, no resize, auto-grow, hover chrome) ──────────────── */
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
const SETTINGS_INPUT = "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50";
|
|
59
|
+
/** Hover "settings" (pencil) → popover to edit the filename + max-lines cap. */
|
|
60
|
+
function CodeSettingsPopover({ filename, maxLines, onFilenameChange, onMaxLinesChange, }) {
|
|
61
|
+
return (_jsxs(Popover, { children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx("button", { type: "button", "data-plan-interactive": true, "aria-label": "Code block settings", title: "Code block settings", className: "plan-code-chip", children: _jsx(IconPencil, { className: "size-3.5" }) }) }), _jsxs(PopoverContent, { align: "end", side: "bottom", className: "w-64 p-0", "data-plan-interactive": true, children: [_jsx("div", { className: "border-b border-border px-3 py-2 text-sm font-semibold text-foreground", children: "Code block" }), _jsxs("div", { className: "grid gap-3 p-3", children: [_jsxs("label", { className: "grid gap-1.5", children: [_jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Filename" }), _jsx("input", { type: "text", "data-plan-interactive": true, className: SETTINGS_INPUT, placeholder: "src/file.ts", value: filename ?? "", onChange: (event) => onFilenameChange(event.target.value || undefined) })] }), _jsxs("label", { className: "grid gap-1.5", children: [_jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Max lines before expand" }), _jsx("input", { type: "number", min: 0, step: 1, "data-plan-interactive": true, className: SETTINGS_INPUT, placeholder: `${DEFAULT_CODE_MAX_LINES} (default) · 0 = no limit`, value: maxLines ?? "", onChange: (event) => {
|
|
62
|
+
const raw = event.target.value.trim();
|
|
63
|
+
const parsed = raw === "" ? undefined : Number(raw);
|
|
64
|
+
onMaxLinesChange(parsed === undefined || Number.isNaN(parsed)
|
|
65
|
+
? undefined
|
|
66
|
+
: Math.max(0, Math.min(2000, Math.floor(parsed))));
|
|
67
|
+
} })] })] })] })] }));
|
|
68
|
+
}
|
|
69
|
+
function CodeEditorSurface({ code, language, filename, maxLines, editable, onCodeChange, onLanguageChange, onFilenameChange, onMaxLinesChange, }) {
|
|
70
|
+
const [expanded, setExpanded] = useState(false);
|
|
59
71
|
const highlightLayerRef = useRef(null);
|
|
60
72
|
const selectId = useId();
|
|
61
73
|
const resolvedLanguage = normalizeCodeLanguage(language) ?? inferLanguageFromFilename(filename);
|
|
62
74
|
const highlighted = useMemo(() => highlightCode(code, resolvedLanguage), [resolvedLanguage, code]);
|
|
63
|
-
//
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
75
|
+
// Size the editor to its content by line count — deterministic, no layout
|
|
76
|
+
// measurement. `wrap="off"` means one row per line. Long snippets collapse to
|
|
77
|
+
// `cap` lines behind a "Show N more lines" toggle, matching the read surface
|
|
78
|
+
// and the file-tree block. `maxLines` omitted ⇒ DEFAULT (40); `0` ⇒ never
|
|
79
|
+
// collapse (show everything).
|
|
80
|
+
const lineCount = code ? code.split("\n").length : 1;
|
|
81
|
+
const cap = maxLines == null ? DEFAULT_CODE_MAX_LINES : maxLines > 0 ? maxLines : null;
|
|
82
|
+
const collapsible = cap != null && lineCount > cap;
|
|
83
|
+
const collapsed = collapsible && !expanded;
|
|
84
|
+
const hiddenLines = collapsible ? lineCount - cap : 0;
|
|
85
|
+
const rows = collapsed ? cap : lineCount + 1;
|
|
71
86
|
const syncScroll = (event) => {
|
|
72
87
|
const layer = highlightLayerRef.current;
|
|
73
88
|
if (!layer)
|
|
74
89
|
return;
|
|
75
90
|
layer.scrollLeft = event.currentTarget.scrollLeft;
|
|
76
91
|
};
|
|
77
|
-
return (_jsxs("div", { className: cn("plan-code plan-code-editing group relative", !editable && "opacity-60"), children: [_jsxs("div", { className: "plan-code-head", children: [_jsxs("span", { className: "plan-code-filename plan-code-muted", children: [_jsx(IconCode, { className: "size-4 shrink-0 opacity-70" }), filename || "Snippet"] }), _jsxs("span", { className: "plan-code-chrome", children: [_jsx("label", { htmlFor: selectId, className: "sr-only", children: "Code language" }), _jsx("select", { id: selectId, "data-plan-interactive": true, disabled: !editable, className: "plan-code-lang-select", value: normalizeCodeLanguage(language) ? (language ?? "") : "", onChange: (event) => onLanguageChange(event.target.value || undefined), children: CODE_LANGUAGES.map((option) => (_jsx("option", { value: option.value, children: option.label }, option.value || "auto"))) }), _jsx(CopyButton, { value: code })] })] }), _jsxs("div", { className: "plan-code-editor-body", children: [_jsx("pre", { ref: highlightLayerRef, "aria-hidden": "true", className: "plan-code-editor-layer", children: _jsxs("code", { children: [highlighted, code.endsWith("\n") ? " " : null] }) }), _jsx("textarea", {
|
|
92
|
+
return (_jsxs("div", { className: cn("plan-code plan-code-editing group relative", !editable && "opacity-60"), children: [_jsxs("div", { className: "plan-code-head", children: [_jsxs("span", { className: "plan-code-filename plan-code-muted", children: [_jsx(IconCode, { className: "size-4 shrink-0 opacity-70" }), filename || "Snippet"] }), _jsxs("span", { className: "plan-code-chrome", children: [_jsx("label", { htmlFor: selectId, className: "sr-only", children: "Code language" }), _jsx("select", { id: selectId, "data-plan-interactive": true, disabled: !editable, className: "plan-code-lang-select", value: normalizeCodeLanguage(language) ? (language ?? "") : "", onChange: (event) => onLanguageChange(event.target.value || undefined), children: CODE_LANGUAGES.map((option) => (_jsx("option", { value: option.value, children: option.label }, option.value || "auto"))) }), editable && (_jsx(CodeSettingsPopover, { filename: filename, maxLines: maxLines, onFilenameChange: onFilenameChange, onMaxLinesChange: onMaxLinesChange })), _jsx(CopyButton, { value: code })] })] }), _jsxs("div", { className: "plan-code-editor-body", children: [_jsx("pre", { ref: highlightLayerRef, "aria-hidden": "true", className: "plan-code-editor-layer", children: _jsxs("code", { children: [highlighted, code.endsWith("\n") ? " " : null] }) }), _jsx("textarea", { "data-plan-interactive": true, spellCheck: false, wrap: "off", rows: Math.max(3, rows), className: "plan-code-editor-input", value: code, disabled: !editable, onChange: (event) => onCodeChange(event.target.value), onScroll: syncScroll }), collapsed && (_jsx("div", { className: "plan-code-editor-fade", "aria-hidden": "true" }))] }), collapsible && (_jsx("button", { type: "button", "data-plan-interactive": true, className: "plan-code-surface-toggle", onClick: () => setExpanded((value) => !value), children: collapsed
|
|
93
|
+
? `Show ${hiddenLines} more line${hiddenLines === 1 ? "" : "s"}`
|
|
94
|
+
: "Show less" }))] }));
|
|
78
95
|
}
|
|
79
96
|
function CodeEdit({ data, onChange, editable }) {
|
|
80
|
-
return (_jsxs("div", { className: "flex min-w-0 flex-col gap-2", children: [_jsx(CodeEditorSurface, { code: data.code, language: data.language, filename: data.filename, editable: editable, onCodeChange: (code) => onChange({ ...data, code }), onLanguageChange: (language) => onChange({ ...data, language }) }), editable && (_jsx("input", { type: "text", "data-plan-interactive": true, className: "plan-code-caption-input", placeholder: "Caption (optional)", value: data.caption ?? "", onChange: (event) => onChange({ ...data, caption: event.target.value || undefined }) }))] }));
|
|
97
|
+
return (_jsxs("div", { className: "flex min-w-0 flex-col gap-2", children: [_jsx(CodeEditorSurface, { code: data.code, language: data.language, filename: data.filename, maxLines: data.maxLines, editable: editable, onCodeChange: (code) => onChange({ ...data, code }), onLanguageChange: (language) => onChange({ ...data, language }), onFilenameChange: (filename) => onChange({ ...data, filename }), onMaxLinesChange: (maxLines) => onChange({ ...data, maxLines }) }), editable && (_jsx("input", { type: "text", "data-plan-interactive": true, className: "plan-code-caption-input", placeholder: "Caption (optional)", value: data.caption ?? "", onChange: (event) => onChange({ ...data, caption: event.target.value || undefined }) }))] }));
|
|
81
98
|
}
|
|
82
99
|
/* ── Spec ──────────────────────────────────────────────────────────────────── */
|
|
83
100
|
export const codeBlock = defineBlock({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/code.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n useId,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type ChangeEvent,\n type UIEvent,\n} from \"react\";\nimport { IconCheck, IconCode, IconCopy } from \"@tabler/icons-react\";\nimport { cn } from \"../../utils.js\";\nimport { defineBlock } from \"../types.js\";\nimport type { BlockReadProps, BlockEditProps } from \"../types.js\";\nimport { CodeSurface } from \"./HighlightedCode.js\";\nimport {\n highlightCode,\n inferLanguageFromFilename,\n normalizeCodeLanguage,\n} from \"./code-highlight.js\";\nimport { codeSchema, codeMdx, type CodeData } from \"./code.config.js\";\n\n/**\n * Standard `code` block (STANDARD core library): THE primitive single code\n * snippet, used everywhere in plan + content. Notion-style — one border, a\n * hover-revealed language switcher + copy, and the shared collapse-to-N-lines\n * read surface. A \"file rail\" of several files is just the `tabs` primitive\n * holding `code` blocks; there is no bespoke \"code-tabs\" container.\n *\n * Read = the shared {@link CodeSurface} (Shiki, single border, language label,\n * \"Show N more lines\"). Edit = a clean, single-border editable surface (no\n * drag-to-resize; it auto-grows to its content) with the same hover chrome.\n */\n\n/** Language options for the hover switcher; \"\" is the Auto-detect sentinel. */\nconst CODE_LANGUAGES: ReadonlyArray<{ value: string; label: string }> = [\n { value: \"\", label: \"Auto\" },\n { value: \"typescript\", label: \"TypeScript\" },\n { value: \"javascript\", label: \"JavaScript\" },\n { value: \"tsx\", label: \"TSX\" },\n { value: \"jsx\", label: \"JSX\" },\n { value: \"json\", label: \"JSON\" },\n { value: \"html\", label: \"HTML\" },\n { value: \"css\", label: \"CSS\" },\n { value: \"bash\", label: \"Bash\" },\n { value: \"python\", label: \"Python\" },\n { value: \"sql\", label: \"SQL\" },\n { value: \"yaml\", label: \"YAML\" },\n { value: \"markdown\", label: \"Markdown\" },\n { value: \"graphql\", label: \"GraphQL\" },\n { value: \"go\", label: \"Go\" },\n { value: \"rust\", label: \"Rust\" },\n { value: \"diff\", label: \"Diff\" },\n];\n\nfunction CopyButton({ value }: { value: string }) {\n const [copied, setCopied] = useState(false);\n return (\n <button\n type=\"button\"\n data-plan-interactive\n aria-label={copied ? \"Copied\" : \"Copy code\"}\n title={copied ? \"Copied\" : \"Copy code\"}\n className=\"plan-code-chip\"\n onClick={() => {\n void navigator.clipboard?.writeText(value).then(\n () => {\n setCopied(true);\n setTimeout(() => setCopied(false), 1200);\n },\n () => {},\n );\n }}\n >\n {copied ? (\n <IconCheck className=\"size-3.5\" />\n ) : (\n <IconCopy className=\"size-3.5\" />\n )}\n </button>\n );\n}\n\n/* ── Read ──────────────────────────────────────────────────────────────────── */\n\nfunction CodeRead({ data, blockId }: BlockReadProps<CodeData>) {\n const language =\n normalizeCodeLanguage(data.language) ??\n inferLanguageFromFilename(data.filename) ??\n undefined;\n return (\n <section className=\"plan-block\" data-block-id={blockId}>\n <div className=\"plan-code group relative\">\n {data.filename && (\n <div className=\"plan-code-head\">\n <span className=\"plan-code-filename\">\n <IconCode className=\"size-4 shrink-0 opacity-70\" />\n {data.filename}\n </span>\n <span className=\"plan-code-chrome\">\n <CopyButton value={data.code} />\n </span>\n </div>\n )}\n <CodeSurface\n code={data.code}\n language={language}\n maxLines={data.maxLines}\n className={data.filename ? \"mt-0\" : \"mt-0\"}\n />\n {!data.filename && (\n <span className=\"plan-code-chrome plan-code-chrome-float\">\n <CopyButton value={data.code} />\n </span>\n )}\n {data.caption && <p className=\"plan-code-caption\">{data.caption}</p>}\n </div>\n </section>\n );\n}\n\n/* ── Edit (single border, no resize, auto-grow, hover chrome) ──────────────── */\n\nfunction CodeEditorSurface({\n code,\n language,\n filename,\n editable,\n onCodeChange,\n onLanguageChange,\n}: {\n code: string;\n language?: string;\n filename?: string;\n editable: boolean;\n onCodeChange: (code: string) => void;\n onLanguageChange: (language: string | undefined) => void;\n}) {\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n const highlightLayerRef = useRef<HTMLPreElement>(null);\n const selectId = useId();\n const resolvedLanguage =\n normalizeCodeLanguage(language) ?? inferLanguageFromFilename(filename);\n const highlighted = useMemo(\n () => highlightCode(code, resolvedLanguage),\n [resolvedLanguage, code],\n );\n\n // Auto-grow to content height — no drag-to-resize handle.\n useLayoutEffect(() => {\n const node = textareaRef.current;\n if (!node) return;\n node.style.height = \"auto\";\n node.style.height = `${node.scrollHeight}px`;\n }, [code]);\n\n const syncScroll = (event: UIEvent<HTMLTextAreaElement>) => {\n const layer = highlightLayerRef.current;\n if (!layer) return;\n layer.scrollLeft = event.currentTarget.scrollLeft;\n };\n\n return (\n <div\n className={cn(\n \"plan-code plan-code-editing group relative\",\n !editable && \"opacity-60\",\n )}\n >\n <div className=\"plan-code-head\">\n <span className=\"plan-code-filename plan-code-muted\">\n <IconCode className=\"size-4 shrink-0 opacity-70\" />\n {filename || \"Snippet\"}\n </span>\n <span className=\"plan-code-chrome\">\n <label htmlFor={selectId} className=\"sr-only\">\n Code language\n </label>\n <select\n id={selectId}\n data-plan-interactive\n disabled={!editable}\n className=\"plan-code-lang-select\"\n value={normalizeCodeLanguage(language) ? (language ?? \"\") : \"\"}\n onChange={(event) =>\n onLanguageChange(event.target.value || undefined)\n }\n >\n {CODE_LANGUAGES.map((option) => (\n <option key={option.value || \"auto\"} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n <CopyButton value={code} />\n </span>\n </div>\n <div className=\"plan-code-editor-body\">\n <pre\n ref={highlightLayerRef}\n aria-hidden=\"true\"\n className=\"plan-code-editor-layer\"\n >\n <code>\n {highlighted}\n {code.endsWith(\"\\n\") ? \" \" : null}\n </code>\n </pre>\n <textarea\n ref={textareaRef}\n data-plan-interactive\n spellCheck={false}\n wrap=\"off\"\n className=\"plan-code-editor-input\"\n value={code}\n disabled={!editable}\n onChange={(event: ChangeEvent<HTMLTextAreaElement>) =>\n onCodeChange(event.target.value)\n }\n onScroll={syncScroll}\n />\n </div>\n </div>\n );\n}\n\nfunction CodeEdit({ data, onChange, editable }: BlockEditProps<CodeData>) {\n return (\n <div className=\"flex min-w-0 flex-col gap-2\">\n <CodeEditorSurface\n code={data.code}\n language={data.language}\n filename={data.filename}\n editable={editable}\n onCodeChange={(code) => onChange({ ...data, code })}\n onLanguageChange={(language) => onChange({ ...data, language })}\n />\n {editable && (\n <input\n type=\"text\"\n data-plan-interactive\n className=\"plan-code-caption-input\"\n placeholder=\"Caption (optional)\"\n value={data.caption ?? \"\"}\n onChange={(event) =>\n onChange({ ...data, caption: event.target.value || undefined })\n }\n />\n )}\n </div>\n );\n}\n\n/* ── Spec ──────────────────────────────────────────────────────────────────── */\n\nexport const codeBlock = defineBlock<CodeData>({\n type: \"code\",\n schema: codeSchema,\n mdx: codeMdx,\n Read: CodeRead,\n Edit: CodeEdit,\n placement: [\"block\"],\n editSurface: \"inline\",\n label: \"Code\",\n icon: IconCode,\n description:\n \"A single syntax-highlighted code snippet, Notion-style: one border, a hover language switcher + copy, and collapse-to-N lines. Put several in a `tabs` block for a file rail.\",\n});\n"]}
|
|
1
|
+
{"version":3,"file":"code.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/code.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n useId,\n useMemo,\n useRef,\n useState,\n type ChangeEvent,\n type UIEvent,\n} from \"react\";\nimport { IconCheck, IconCode, IconCopy, IconPencil } from \"@tabler/icons-react\";\nimport { cn } from \"../../utils.js\";\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"../../components/ui/popover.js\";\nimport { defineBlock } from \"../types.js\";\nimport type { BlockReadProps, BlockEditProps } from \"../types.js\";\nimport { CodeSurface, DEFAULT_CODE_MAX_LINES } from \"./HighlightedCode.js\";\nimport {\n highlightCode,\n inferLanguageFromFilename,\n normalizeCodeLanguage,\n} from \"./code-highlight.js\";\nimport { codeSchema, codeMdx, type CodeData } from \"./code.config.js\";\n\n/**\n * Standard `code` block (STANDARD core library): THE primitive single code\n * snippet, used everywhere in plan + content. Notion-style — one border, a\n * hover-revealed language switcher + copy, and the shared collapse-to-N-lines\n * read surface. A \"file rail\" of several files is just the `tabs` primitive\n * holding `code` blocks; there is no bespoke \"code-tabs\" container.\n *\n * Read = the shared {@link CodeSurface} (Shiki, single border, language label,\n * \"Show N more lines\"). Edit = a clean, single-border editable surface (no\n * drag-to-resize; it auto-grows to its content) with the same hover chrome.\n */\n\n/** Language options for the hover switcher; \"\" is the Auto-detect sentinel. */\nconst CODE_LANGUAGES: ReadonlyArray<{ value: string; label: string }> = [\n { value: \"\", label: \"Auto\" },\n { value: \"typescript\", label: \"TypeScript\" },\n { value: \"javascript\", label: \"JavaScript\" },\n { value: \"tsx\", label: \"TSX\" },\n { value: \"jsx\", label: \"JSX\" },\n { value: \"json\", label: \"JSON\" },\n { value: \"html\", label: \"HTML\" },\n { value: \"css\", label: \"CSS\" },\n { value: \"bash\", label: \"Bash\" },\n { value: \"python\", label: \"Python\" },\n { value: \"sql\", label: \"SQL\" },\n { value: \"yaml\", label: \"YAML\" },\n { value: \"markdown\", label: \"Markdown\" },\n { value: \"graphql\", label: \"GraphQL\" },\n { value: \"go\", label: \"Go\" },\n { value: \"rust\", label: \"Rust\" },\n { value: \"diff\", label: \"Diff\" },\n];\n\nfunction CopyButton({ value }: { value: string }) {\n const [copied, setCopied] = useState(false);\n return (\n <button\n type=\"button\"\n data-plan-interactive\n aria-label={copied ? \"Copied\" : \"Copy code\"}\n title={copied ? \"Copied\" : \"Copy code\"}\n className=\"plan-code-chip\"\n onClick={() => {\n void navigator.clipboard?.writeText(value).then(\n () => {\n setCopied(true);\n setTimeout(() => setCopied(false), 1200);\n },\n () => {},\n );\n }}\n >\n {copied ? (\n <IconCheck className=\"size-3.5\" />\n ) : (\n <IconCopy className=\"size-3.5\" />\n )}\n </button>\n );\n}\n\n/* ── Read ──────────────────────────────────────────────────────────────────── */\n\nfunction CodeRead({ data, blockId }: BlockReadProps<CodeData>) {\n const language =\n normalizeCodeLanguage(data.language) ??\n inferLanguageFromFilename(data.filename) ??\n undefined;\n return (\n <section className=\"plan-block\" data-block-id={blockId}>\n <div className=\"plan-code group relative\">\n {data.filename && (\n <div className=\"plan-code-head\">\n <span className=\"plan-code-filename\">\n <IconCode className=\"size-4 shrink-0 opacity-70\" />\n {data.filename}\n </span>\n <span className=\"plan-code-chrome\">\n <CopyButton value={data.code} />\n </span>\n </div>\n )}\n <CodeSurface\n code={data.code}\n language={language}\n maxLines={data.maxLines}\n className={data.filename ? \"mt-0\" : \"mt-0\"}\n />\n {!data.filename && (\n <span className=\"plan-code-chrome plan-code-chrome-float\">\n <CopyButton value={data.code} />\n </span>\n )}\n {data.caption && <p className=\"plan-code-caption\">{data.caption}</p>}\n </div>\n </section>\n );\n}\n\n/* ── Edit (single border, no resize, auto-grow, hover chrome) ──────────────── */\n\nconst SETTINGS_INPUT =\n \"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50\";\n\n/** Hover \"settings\" (pencil) → popover to edit the filename + max-lines cap. */\nfunction CodeSettingsPopover({\n filename,\n maxLines,\n onFilenameChange,\n onMaxLinesChange,\n}: {\n filename?: string;\n maxLines?: number;\n onFilenameChange: (filename: string | undefined) => void;\n onMaxLinesChange: (maxLines: number | undefined) => void;\n}) {\n return (\n <Popover>\n <PopoverTrigger asChild>\n <button\n type=\"button\"\n data-plan-interactive\n aria-label=\"Code block settings\"\n title=\"Code block settings\"\n className=\"plan-code-chip\"\n >\n <IconPencil className=\"size-3.5\" />\n </button>\n </PopoverTrigger>\n <PopoverContent\n align=\"end\"\n side=\"bottom\"\n className=\"w-64 p-0\"\n data-plan-interactive\n >\n <div className=\"border-b border-border px-3 py-2 text-sm font-semibold text-foreground\">\n Code block\n </div>\n <div className=\"grid gap-3 p-3\">\n <label className=\"grid gap-1.5\">\n <span className=\"text-xs font-medium text-muted-foreground\">\n Filename\n </span>\n <input\n type=\"text\"\n data-plan-interactive\n className={SETTINGS_INPUT}\n placeholder=\"src/file.ts\"\n value={filename ?? \"\"}\n onChange={(event) =>\n onFilenameChange(event.target.value || undefined)\n }\n />\n </label>\n <label className=\"grid gap-1.5\">\n <span className=\"text-xs font-medium text-muted-foreground\">\n Max lines before expand\n </span>\n <input\n type=\"number\"\n min={0}\n step={1}\n data-plan-interactive\n className={SETTINGS_INPUT}\n placeholder={`${DEFAULT_CODE_MAX_LINES} (default) · 0 = no limit`}\n value={maxLines ?? \"\"}\n onChange={(event) => {\n const raw = event.target.value.trim();\n const parsed = raw === \"\" ? undefined : Number(raw);\n onMaxLinesChange(\n parsed === undefined || Number.isNaN(parsed)\n ? undefined\n : Math.max(0, Math.min(2000, Math.floor(parsed))),\n );\n }}\n />\n </label>\n </div>\n </PopoverContent>\n </Popover>\n );\n}\n\nfunction CodeEditorSurface({\n code,\n language,\n filename,\n maxLines,\n editable,\n onCodeChange,\n onLanguageChange,\n onFilenameChange,\n onMaxLinesChange,\n}: {\n code: string;\n language?: string;\n filename?: string;\n maxLines?: number;\n editable: boolean;\n onCodeChange: (code: string) => void;\n onLanguageChange: (language: string | undefined) => void;\n onFilenameChange: (filename: string | undefined) => void;\n onMaxLinesChange: (maxLines: number | undefined) => void;\n}) {\n const [expanded, setExpanded] = useState(false);\n const highlightLayerRef = useRef<HTMLPreElement>(null);\n const selectId = useId();\n const resolvedLanguage =\n normalizeCodeLanguage(language) ?? inferLanguageFromFilename(filename);\n const highlighted = useMemo(\n () => highlightCode(code, resolvedLanguage),\n [resolvedLanguage, code],\n );\n // Size the editor to its content by line count — deterministic, no layout\n // measurement. `wrap=\"off\"` means one row per line. Long snippets collapse to\n // `cap` lines behind a \"Show N more lines\" toggle, matching the read surface\n // and the file-tree block. `maxLines` omitted ⇒ DEFAULT (40); `0` ⇒ never\n // collapse (show everything).\n const lineCount = code ? code.split(\"\\n\").length : 1;\n const cap =\n maxLines == null ? DEFAULT_CODE_MAX_LINES : maxLines > 0 ? maxLines : null;\n const collapsible = cap != null && lineCount > cap;\n const collapsed = collapsible && !expanded;\n const hiddenLines = collapsible ? lineCount - (cap as number) : 0;\n const rows = collapsed ? (cap as number) : lineCount + 1;\n\n const syncScroll = (event: UIEvent<HTMLTextAreaElement>) => {\n const layer = highlightLayerRef.current;\n if (!layer) return;\n layer.scrollLeft = event.currentTarget.scrollLeft;\n };\n\n return (\n <div\n className={cn(\n \"plan-code plan-code-editing group relative\",\n !editable && \"opacity-60\",\n )}\n >\n <div className=\"plan-code-head\">\n <span className=\"plan-code-filename plan-code-muted\">\n <IconCode className=\"size-4 shrink-0 opacity-70\" />\n {filename || \"Snippet\"}\n </span>\n <span className=\"plan-code-chrome\">\n <label htmlFor={selectId} className=\"sr-only\">\n Code language\n </label>\n <select\n id={selectId}\n data-plan-interactive\n disabled={!editable}\n className=\"plan-code-lang-select\"\n value={normalizeCodeLanguage(language) ? (language ?? \"\") : \"\"}\n onChange={(event) =>\n onLanguageChange(event.target.value || undefined)\n }\n >\n {CODE_LANGUAGES.map((option) => (\n <option key={option.value || \"auto\"} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n {editable && (\n <CodeSettingsPopover\n filename={filename}\n maxLines={maxLines}\n onFilenameChange={onFilenameChange}\n onMaxLinesChange={onMaxLinesChange}\n />\n )}\n <CopyButton value={code} />\n </span>\n </div>\n <div className=\"plan-code-editor-body\">\n <pre\n ref={highlightLayerRef}\n aria-hidden=\"true\"\n className=\"plan-code-editor-layer\"\n >\n <code>\n {highlighted}\n {code.endsWith(\"\\n\") ? \" \" : null}\n </code>\n </pre>\n <textarea\n data-plan-interactive\n spellCheck={false}\n wrap=\"off\"\n rows={Math.max(3, rows)}\n className=\"plan-code-editor-input\"\n value={code}\n disabled={!editable}\n onChange={(event: ChangeEvent<HTMLTextAreaElement>) =>\n onCodeChange(event.target.value)\n }\n onScroll={syncScroll}\n />\n {collapsed && (\n <div className=\"plan-code-editor-fade\" aria-hidden=\"true\" />\n )}\n </div>\n {collapsible && (\n <button\n type=\"button\"\n data-plan-interactive\n className=\"plan-code-surface-toggle\"\n onClick={() => setExpanded((value) => !value)}\n >\n {collapsed\n ? `Show ${hiddenLines} more line${hiddenLines === 1 ? \"\" : \"s\"}`\n : \"Show less\"}\n </button>\n )}\n </div>\n );\n}\n\nfunction CodeEdit({ data, onChange, editable }: BlockEditProps<CodeData>) {\n return (\n <div className=\"flex min-w-0 flex-col gap-2\">\n <CodeEditorSurface\n code={data.code}\n language={data.language}\n filename={data.filename}\n maxLines={data.maxLines}\n editable={editable}\n onCodeChange={(code) => onChange({ ...data, code })}\n onLanguageChange={(language) => onChange({ ...data, language })}\n onFilenameChange={(filename) => onChange({ ...data, filename })}\n onMaxLinesChange={(maxLines) => onChange({ ...data, maxLines })}\n />\n {editable && (\n <input\n type=\"text\"\n data-plan-interactive\n className=\"plan-code-caption-input\"\n placeholder=\"Caption (optional)\"\n value={data.caption ?? \"\"}\n onChange={(event) =>\n onChange({ ...data, caption: event.target.value || undefined })\n }\n />\n )}\n </div>\n );\n}\n\n/* ── Spec ──────────────────────────────────────────────────────────────────── */\n\nexport const codeBlock = defineBlock<CodeData>({\n type: \"code\",\n schema: codeSchema,\n mdx: codeMdx,\n Read: CodeRead,\n Edit: CodeEdit,\n placement: [\"block\"],\n editSurface: \"inline\",\n label: \"Code\",\n icon: IconCode,\n description:\n \"A single syntax-highlighted code snippet, Notion-style: one border, a hover language switcher + copy, and collapse-to-N lines. Put several in a `tabs` block for a file rail.\",\n});\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"columns.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/columns.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAEV,cAAc,EACd,cAAc,EAEf,MAAM,aAAa,CAAC;AACrB,OAAO,EAGL,KAAK,WAAW,EAEjB,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"columns.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/columns.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAEV,cAAc,EACd,cAAc,EAEf,MAAM,aAAa,CAAC;AACrB,OAAO,EAGL,KAAK,WAAW,EAEjB,MAAM,qBAAqB,CAAC;AAsH7B,kFAAkF;AAClF,wBAAgB,kBAAkB,CAAC,EACjC,IAAI,EACJ,OAAO,EACP,KAAK,EACL,GAAG,GACJ,EAAE,cAAc,CAAC,WAAW,CAAC,2CAsB7B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,IAAI,EACJ,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,GAAG,GACJ,EAAE,cAAc,CAAC,WAAW,CAAC,2CAsE7B;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,8CA0DvB,CAAC"}
|
|
@@ -5,8 +5,9 @@ import { defineBlock } from "../types.js";
|
|
|
5
5
|
import { columnsSchema, columnsMdx, } from "./columns.config.js";
|
|
6
6
|
/**
|
|
7
7
|
* Standard `columns` block: a multi-column side-by-side container whose columns
|
|
8
|
-
* each hold a list of child blocks.
|
|
9
|
-
*
|
|
8
|
+
* each hold a list of child blocks. A column's optional `label` renders as a
|
|
9
|
+
* small heading above that column (e.g. `Before` / `After`), so a comparison
|
|
10
|
+
* names its states outside the content — never baked into a child wireframe.
|
|
10
11
|
*
|
|
11
12
|
* Like `tabs`, child rendering flows through `ctx.renderBlock` — the app's own
|
|
12
13
|
* block dispatcher — so registered children render via their spec and
|
|
@@ -37,6 +38,10 @@ function gridColsClass(count) {
|
|
|
37
38
|
const API_REFERENCE_BLOCK_TYPES = new Set(["api-endpoint", "openapi-spec"]);
|
|
38
39
|
const BEFORE_LABELS = new Set(["before", "old", "previous", "current"]);
|
|
39
40
|
const AFTER_LABELS = new Set(["after", "new", "next", "target"]);
|
|
41
|
+
// Wireframe surfaces that render too wide to survive a half-width comparison
|
|
42
|
+
// column: a full desktop page or browser frame shrinks and crops when squeezed
|
|
43
|
+
// side by side, so a comparison that holds one of these stacks vertically.
|
|
44
|
+
const WIDE_WIREFRAME_SURFACES = new Set(["desktop", "browser"]);
|
|
40
45
|
function normalizedLabel(column) {
|
|
41
46
|
return column.label?.trim().toLowerCase() ?? "";
|
|
42
47
|
}
|
|
@@ -45,6 +50,16 @@ function isComparisonGroup(columns) {
|
|
|
45
50
|
return (labels.some((label) => BEFORE_LABELS.has(label)) &&
|
|
46
51
|
labels.some((label) => AFTER_LABELS.has(label)));
|
|
47
52
|
}
|
|
53
|
+
/** A wireframe child whose `surface` is wide enough to need full document width. */
|
|
54
|
+
function isWideWireframeBlock(block) {
|
|
55
|
+
if (block.type !== "wireframe")
|
|
56
|
+
return false;
|
|
57
|
+
const surface = block.data?.surface;
|
|
58
|
+
return typeof surface === "string" && WIDE_WIREFRAME_SURFACES.has(surface);
|
|
59
|
+
}
|
|
60
|
+
function hasWideWireframe(columns) {
|
|
61
|
+
return columns.some((column) => column.blocks.some(isWideWireframeBlock));
|
|
62
|
+
}
|
|
48
63
|
function isApiReferenceGroup(columns) {
|
|
49
64
|
return (columns.length > 1 &&
|
|
50
65
|
!isComparisonGroup(columns) &&
|
|
@@ -52,9 +67,15 @@ function isApiReferenceGroup(columns) {
|
|
|
52
67
|
column.blocks.every((block) => API_REFERENCE_BLOCK_TYPES.has(block.type))));
|
|
53
68
|
}
|
|
54
69
|
function columnsLayoutClass(columns) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
70
|
+
// API reference groups always read as a single stacked column.
|
|
71
|
+
if (isApiReferenceGroup(columns))
|
|
72
|
+
return COLS_CLASS[1];
|
|
73
|
+
// Wide wireframes (full desktop / browser surfaces) crop when squeezed into a
|
|
74
|
+
// half-width comparison cell, so stack the states vertically and let each
|
|
75
|
+
// frame use the full document width. Narrow surfaces stay side by side.
|
|
76
|
+
if (hasWideWireframe(columns))
|
|
77
|
+
return COLS_CLASS[1];
|
|
78
|
+
return gridColsClass(columns.length);
|
|
58
79
|
}
|
|
59
80
|
function isBlankRichTextBlock(block) {
|
|
60
81
|
if (block.type !== "rich-text")
|
|
@@ -81,7 +102,7 @@ function areSameBlocks(a, b) {
|
|
|
81
102
|
}
|
|
82
103
|
/** Read renderer: a responsive grid of columns, each child rendered read-only. */
|
|
83
104
|
export function ColumnsBlockReader({ data, blockId, title, ctx, }) {
|
|
84
|
-
return (_jsxs("section", { className: "plan-block", "data-block-id": blockId, children: [title && _jsx("div", { className: "plan-block-label", children: title }), _jsx("div", { className: cn("grid gap-6", columnsLayoutClass(data.columns)), children: data.columns.map((column) => (
|
|
105
|
+
return (_jsxs("section", { className: "plan-block", "data-block-id": blockId, children: [title && _jsx("div", { className: "plan-block-label", children: title }), _jsx("div", { className: cn("grid gap-6", columnsLayoutClass(data.columns)), children: data.columns.map((column) => (_jsxs("div", { className: "min-w-0", children: [column.label && (_jsx("h4", { className: "plan-columns-label", children: column.label })), _jsx("div", { children: column.blocks.map((child) => (_jsx("div", { children: ctx.renderBlock?.({ block: child, editing: false }) }, child.id))) })] }, column.id))) })] }));
|
|
85
106
|
}
|
|
86
107
|
/**
|
|
87
108
|
* Editor: the same responsive grid, with child blocks rendered editable in
|
|
@@ -103,36 +124,36 @@ export function ColumnsBlockEditor({ data, onChange, editable, blockId, ctx, })
|
|
|
103
124
|
blocks: column.blocks.map((existing) => existing.id === child.id ? child : existing),
|
|
104
125
|
}
|
|
105
126
|
: column));
|
|
106
|
-
return (_jsx("div", { "data-columns-edit-block": blockId, className: "grid gap-3", children: _jsx("div", { className: cn("grid gap-6", columnsLayoutClass(data.columns)), children: data.columns.map((column) => (
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
127
|
+
return (_jsx("div", { "data-columns-edit-block": blockId, className: "grid gap-3", children: _jsx("div", { className: cn("grid gap-6", columnsLayoutClass(data.columns)), children: data.columns.map((column) => (_jsxs("div", { className: "min-w-0", children: [column.label && (_jsx("h4", { className: "plan-columns-label", children: column.label })), _jsx("div", { children: ctx.renderBlocksEditor
|
|
128
|
+
? ctx.renderBlocksEditor({
|
|
129
|
+
blocks: column.blocks,
|
|
130
|
+
onChange: (nextBlocks) => {
|
|
131
|
+
if (areSameBlocks(nextBlocks, column.blocks))
|
|
132
|
+
return;
|
|
133
|
+
onChange({
|
|
134
|
+
columns: data.columns.map((existing) => existing.id === column.id
|
|
135
|
+
? {
|
|
136
|
+
...existing,
|
|
137
|
+
blocks: nextBlocks,
|
|
138
|
+
}
|
|
139
|
+
: existing),
|
|
140
|
+
}, {
|
|
141
|
+
containerRegion: {
|
|
142
|
+
regionId: column.id,
|
|
116
143
|
blocks: nextBlocks,
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
},
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
})
|
|
131
|
-
: column.blocks.map((child) => (_jsx("div", { children: ctx.renderBlock?.({
|
|
132
|
-
block: child,
|
|
133
|
-
editing: true,
|
|
134
|
-
onChange: (next) => updateChild(column.id, next),
|
|
135
|
-
}) }, child.id))) }) }, column.id))) }) }));
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
},
|
|
147
|
+
editable,
|
|
148
|
+
containerBlockId: blockId,
|
|
149
|
+
regionId: column.id,
|
|
150
|
+
regionLabel: column.label,
|
|
151
|
+
})
|
|
152
|
+
: column.blocks.map((child) => (_jsx("div", { children: ctx.renderBlock?.({
|
|
153
|
+
block: child,
|
|
154
|
+
editing: true,
|
|
155
|
+
onChange: (next) => updateChild(column.id, next),
|
|
156
|
+
}) }, child.id))) })] }, column.id))) }) }));
|
|
136
157
|
}
|
|
137
158
|
/**
|
|
138
159
|
* The standard columns block spec (with React `Read`/`Edit`). Apps register this
|