@agent-native/core 0.38.0 → 0.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +8 -1
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/skills.d.ts +5 -4
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +450 -125
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/blocks/BlockView.d.ts +13 -4
- package/dist/client/blocks/BlockView.d.ts.map +1 -1
- package/dist/client/blocks/BlockView.js +34 -13
- package/dist/client/blocks/BlockView.js.map +1 -1
- package/dist/client/blocks/SchemaBlockEditor.d.ts.map +1 -1
- package/dist/client/blocks/SchemaBlockEditor.js +96 -3
- package/dist/client/blocks/SchemaBlockEditor.js.map +1 -1
- package/dist/client/blocks/index.d.ts +18 -1
- package/dist/client/blocks/index.d.ts.map +1 -1
- package/dist/client/blocks/index.js +26 -1
- 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 +135 -0
- package/dist/client/blocks/library/AnnotatedCodeBlock.js.map +1 -0
- package/dist/client/blocks/library/ApiEndpointBlock.d.ts +20 -0
- package/dist/client/blocks/library/ApiEndpointBlock.d.ts.map +1 -0
- package/dist/client/blocks/library/ApiEndpointBlock.js +131 -0
- package/dist/client/blocks/library/ApiEndpointBlock.js.map +1 -0
- package/dist/client/blocks/library/DataModelBlock.d.ts +28 -0
- package/dist/client/blocks/library/DataModelBlock.d.ts.map +1 -0
- package/dist/client/blocks/library/DataModelBlock.js +222 -0
- package/dist/client/blocks/library/DataModelBlock.js.map +1 -0
- package/dist/client/blocks/library/DiffBlock.d.ts +6 -0
- package/dist/client/blocks/library/DiffBlock.d.ts.map +1 -0
- package/dist/client/blocks/library/DiffBlock.js +293 -0
- package/dist/client/blocks/library/DiffBlock.js.map +1 -0
- package/dist/client/blocks/library/FileTreeBlock.d.ts +23 -0
- package/dist/client/blocks/library/FileTreeBlock.d.ts.map +1 -0
- package/dist/client/blocks/library/FileTreeBlock.js +225 -0
- package/dist/client/blocks/library/FileTreeBlock.js.map +1 -0
- package/dist/client/blocks/library/JsonExplorerBlock.d.ts +19 -0
- package/dist/client/blocks/library/JsonExplorerBlock.d.ts.map +1 -0
- package/dist/client/blocks/library/JsonExplorerBlock.js +171 -0
- package/dist/client/blocks/library/JsonExplorerBlock.js.map +1 -0
- package/dist/client/blocks/library/MermaidBlock.d.ts +17 -0
- package/dist/client/blocks/library/MermaidBlock.d.ts.map +1 -0
- package/dist/client/blocks/library/MermaidBlock.js +131 -0
- package/dist/client/blocks/library/MermaidBlock.js.map +1 -0
- package/dist/client/blocks/library/OpenApiSpecBlock.d.ts +19 -0
- package/dist/client/blocks/library/OpenApiSpecBlock.d.ts.map +1 -0
- package/dist/client/blocks/library/OpenApiSpecBlock.js +494 -0
- package/dist/client/blocks/library/OpenApiSpecBlock.js.map +1 -0
- 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/api-endpoint.config.d.ts +71 -0
- package/dist/client/blocks/library/api-endpoint.config.d.ts.map +1 -0
- package/dist/client/blocks/library/api-endpoint.config.js +91 -0
- package/dist/client/blocks/library/api-endpoint.config.js.map +1 -0
- package/dist/client/blocks/library/checklist.d.ts.map +1 -1
- package/dist/client/blocks/library/checklist.js +3 -1
- package/dist/client/blocks/library/checklist.js.map +1 -1
- package/dist/client/blocks/library/code-tabs.js +1 -1
- package/dist/client/blocks/library/code-tabs.js.map +1 -1
- package/dist/client/blocks/library/data-model.config.d.ts +72 -0
- package/dist/client/blocks/library/data-model.config.d.ts.map +1 -0
- package/dist/client/blocks/library/data-model.config.js +59 -0
- package/dist/client/blocks/library/data-model.config.js.map +1 -0
- package/dist/client/blocks/library/dev-doc-ui.d.ts +49 -0
- package/dist/client/blocks/library/dev-doc-ui.d.ts.map +1 -0
- package/dist/client/blocks/library/dev-doc-ui.js +50 -0
- package/dist/client/blocks/library/dev-doc-ui.js.map +1 -0
- package/dist/client/blocks/library/diff.config.d.ts +41 -0
- package/dist/client/blocks/library/diff.config.d.ts.map +1 -0
- package/dist/client/blocks/library/diff.config.js +34 -0
- package/dist/client/blocks/library/diff.config.js.map +1 -0
- package/dist/client/blocks/library/file-tree.config.d.ts +59 -0
- package/dist/client/blocks/library/file-tree.config.d.ts.map +1 -0
- package/dist/client/blocks/library/file-tree.config.js +45 -0
- package/dist/client/blocks/library/file-tree.config.js.map +1 -0
- package/dist/client/blocks/library/html.d.ts.map +1 -1
- package/dist/client/blocks/library/html.js +4 -1
- package/dist/client/blocks/library/html.js.map +1 -1
- package/dist/client/blocks/library/json-explorer.config.d.ts +46 -0
- package/dist/client/blocks/library/json-explorer.config.d.ts.map +1 -0
- package/dist/client/blocks/library/json-explorer.config.js +28 -0
- package/dist/client/blocks/library/json-explorer.config.js.map +1 -0
- package/dist/client/blocks/library/mermaid.config.d.ts +32 -0
- package/dist/client/blocks/library/mermaid.config.d.ts.map +1 -0
- package/dist/client/blocks/library/mermaid.config.js +24 -0
- package/dist/client/blocks/library/mermaid.config.js.map +1 -0
- package/dist/client/blocks/library/openapi-spec.config.d.ts +49 -0
- package/dist/client/blocks/library/openapi-spec.config.d.ts.map +1 -0
- package/dist/client/blocks/library/openapi-spec.config.js +24 -0
- package/dist/client/blocks/library/openapi-spec.config.js.map +1 -0
- package/dist/client/blocks/library/server-specs.d.ts +35 -0
- package/dist/client/blocks/library/server-specs.d.ts.map +1 -0
- package/dist/client/blocks/library/server-specs.js +171 -0
- package/dist/client/blocks/library/server-specs.js.map +1 -0
- package/dist/client/blocks/library/specs.d.ts +29 -0
- package/dist/client/blocks/library/specs.d.ts.map +1 -0
- package/dist/client/blocks/library/specs.js +229 -0
- package/dist/client/blocks/library/specs.js.map +1 -0
- package/dist/client/blocks/library/table.d.ts.map +1 -1
- package/dist/client/blocks/library/table.js +3 -1
- package/dist/client/blocks/library/table.js.map +1 -1
- package/dist/client/blocks/library/tabs.js +1 -1
- package/dist/client/blocks/library/tabs.js.map +1 -1
- package/dist/client/blocks/registry.d.ts +8 -0
- package/dist/client/blocks/registry.d.ts.map +1 -1
- package/dist/client/blocks/registry.js +15 -0
- package/dist/client/blocks/registry.js.map +1 -1
- package/dist/client/blocks/server.d.ts +9 -0
- package/dist/client/blocks/server.d.ts.map +1 -1
- package/dist/client/blocks/server.js +16 -0
- package/dist/client/blocks/server.js.map +1 -1
- package/dist/client/blocks/types.d.ts +40 -0
- package/dist/client/blocks/types.d.ts.map +1 -1
- package/dist/client/blocks/types.js.map +1 -1
- package/dist/client/index.d.ts +2 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +10 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/rich-markdown-editor/DragHandle.d.ts +52 -0
- package/dist/client/rich-markdown-editor/DragHandle.d.ts.map +1 -0
- package/dist/client/rich-markdown-editor/DragHandle.js +403 -0
- package/dist/client/rich-markdown-editor/DragHandle.js.map +1 -0
- package/dist/client/rich-markdown-editor/RegistryBlockNode.d.ts +97 -0
- package/dist/client/rich-markdown-editor/RegistryBlockNode.d.ts.map +1 -0
- package/dist/client/rich-markdown-editor/RegistryBlockNode.js +214 -0
- package/dist/client/rich-markdown-editor/RegistryBlockNode.js.map +1 -0
- package/dist/client/rich-markdown-editor/RunId.d.ts +28 -0
- package/dist/client/rich-markdown-editor/RunId.d.ts.map +1 -0
- package/dist/client/rich-markdown-editor/RunId.js +60 -0
- package/dist/client/rich-markdown-editor/RunId.js.map +1 -0
- package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts +25 -1
- package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/SharedRichEditor.js +14 -5
- package/dist/client/rich-markdown-editor/SharedRichEditor.js.map +1 -1
- package/dist/client/rich-markdown-editor/gfmDoc.d.ts +24 -0
- package/dist/client/rich-markdown-editor/gfmDoc.d.ts.map +1 -0
- package/dist/client/rich-markdown-editor/gfmDoc.js +83 -0
- package/dist/client/rich-markdown-editor/gfmDoc.js.map +1 -0
- package/dist/client/rich-markdown-editor/index.d.ts +5 -0
- package/dist/client/rich-markdown-editor/index.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/index.js +5 -0
- package/dist/client/rich-markdown-editor/index.js.map +1 -1
- package/dist/client/rich-markdown-editor/registrySlashCommands.d.ts +46 -0
- package/dist/client/rich-markdown-editor/registrySlashCommands.d.ts.map +1 -0
- package/dist/client/rich-markdown-editor/registrySlashCommands.js +13 -0
- package/dist/client/rich-markdown-editor/registrySlashCommands.js.map +1 -0
- package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/useCollabReconcile.js +33 -0
- package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
- package/docs/content/template-plan.md +19 -4
- package/docs/content/visual-plans.md +3 -1
- package/package.json +1 -1
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo, useState } from "react";
|
|
3
|
+
import { IconChevronRight, IconCode, IconPlus, IconTrash, } from "@tabler/icons-react";
|
|
4
|
+
import { cn } from "../../utils.js";
|
|
5
|
+
import { DevInput, DevLabel, DevTextarea } from "./dev-doc-ui.js";
|
|
6
|
+
/**
|
|
7
|
+
* Stripe-docs / Sourcegraph "explain this code" walkthrough block. The read
|
|
8
|
+
* renderer shows the `code` as a line-numbered monospace surface; lines covered
|
|
9
|
+
* by an annotation get a subtle highlight band and a numbered gutter marker. The
|
|
10
|
+
* annotation notes render below, each with its `lines` range, optional `label`,
|
|
11
|
+
* and markdown `note` (via `ctx.renderMarkdown`). Hovering a note highlights its
|
|
12
|
+
* line range and vice-versa, so the line-anchoring is the differentiator. Lives
|
|
13
|
+
* in core so any app can register the dev-doc block (no shadcn import).
|
|
14
|
+
*
|
|
15
|
+
* All colors are theme-aware: the surface uses the plan `--plan-code*`/`--plan-*`
|
|
16
|
+
* tokens and the highlight/marker accents use Tailwind `light`/`dark:` pairs, so
|
|
17
|
+
* the block reads correctly in BOTH light and dark mode.
|
|
18
|
+
*
|
|
19
|
+
* Editing is panel-driven (config-style, like the diff/HTML blocks): a monospace
|
|
20
|
+
* code Textarea, filename/language Inputs, and add/remove-able annotation rows.
|
|
21
|
+
*/
|
|
22
|
+
/* ── Line-ref parsing ──────────────────────────────────────────────────────── */
|
|
23
|
+
/**
|
|
24
|
+
* Parse a 1-based `lines` ref (`"3"` or `"3-5"`) into an inclusive `[start,end]`
|
|
25
|
+
* pair, clamped to `[1, lineCount]`. Returns `null` for malformed or fully
|
|
26
|
+
* out-of-range refs so callers can ignore them gracefully. A reversed range
|
|
27
|
+
* (`"5-3"`) is normalized; a partially out-of-range range is clamped.
|
|
28
|
+
*/
|
|
29
|
+
function parseLineRange(ref, lineCount) {
|
|
30
|
+
const match = /^\s*(\d+)\s*(?:-\s*(\d+)\s*)?$/.exec(ref);
|
|
31
|
+
if (!match)
|
|
32
|
+
return null;
|
|
33
|
+
let start = Number.parseInt(match[1], 10);
|
|
34
|
+
let end = match[2] != null ? Number.parseInt(match[2], 10) : start;
|
|
35
|
+
if (!Number.isFinite(start) || !Number.isFinite(end))
|
|
36
|
+
return null;
|
|
37
|
+
if (start > end)
|
|
38
|
+
[start, end] = [end, start];
|
|
39
|
+
// Fully outside the file → ignore.
|
|
40
|
+
if (end < 1 || start > lineCount)
|
|
41
|
+
return null;
|
|
42
|
+
return { start: Math.max(1, start), end: Math.min(lineCount, end) };
|
|
43
|
+
}
|
|
44
|
+
/* ── Read ──────────────────────────────────────────────────────────────────── */
|
|
45
|
+
function AnnotatedCodeRead({ data, blockId, title, summary, ctx, }) {
|
|
46
|
+
const [activeIndex, setActiveIndex] = useState(null);
|
|
47
|
+
const [notesOpen, setNotesOpen] = useState(true);
|
|
48
|
+
const [revealed, setRevealed] = useState(() => new Set());
|
|
49
|
+
const lines = useMemo(() => data.code.split("\n"), [data.code]);
|
|
50
|
+
const lineCount = lines.length;
|
|
51
|
+
const resolved = useMemo(() => (data.annotations ?? []).map((annotation, index) => ({
|
|
52
|
+
index,
|
|
53
|
+
marker: index + 1,
|
|
54
|
+
annotation,
|
|
55
|
+
range: parseLineRange(annotation.lines, lineCount),
|
|
56
|
+
})), [data.annotations, lineCount]);
|
|
57
|
+
// line number (1-based) → resolved annotations covering it (marker, active).
|
|
58
|
+
const lineMarkers = useMemo(() => {
|
|
59
|
+
const map = new Map();
|
|
60
|
+
for (const item of resolved) {
|
|
61
|
+
if (!item.range)
|
|
62
|
+
continue;
|
|
63
|
+
for (let n = item.range.start; n <= item.range.end; n += 1) {
|
|
64
|
+
const list = map.get(n) ?? [];
|
|
65
|
+
list.push(item);
|
|
66
|
+
map.set(n, list);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return map;
|
|
70
|
+
}, [resolved]);
|
|
71
|
+
const hasAnnotations = resolved.some((item) => item.range);
|
|
72
|
+
const toggleRevealed = (index) => setRevealed((prev) => {
|
|
73
|
+
const next = new Set(prev);
|
|
74
|
+
if (next.has(index))
|
|
75
|
+
next.delete(index);
|
|
76
|
+
else
|
|
77
|
+
next.add(index);
|
|
78
|
+
return next;
|
|
79
|
+
});
|
|
80
|
+
return (_jsxs("section", { className: "plan-block", "data-block-id": blockId, children: [title && _jsx("div", { className: "plan-block-label", children: title }), _jsxs("div", { className: "overflow-hidden rounded-lg border border-plan-line bg-plan-code", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2 border-b border-plan-line bg-plan-block px-3 py-2", children: [_jsx(IconCode, { className: "size-4 shrink-0 text-plan-muted" }), _jsx("span", { className: "min-w-0 truncate font-mono text-sm font-medium text-plan-code-text", children: data.filename || "snippet" }), data.language && (_jsx("span", { className: "shrink-0 rounded border border-plan-line px-1.5 py-0.5 font-mono text-[11px] uppercase tracking-wide text-plan-muted", children: data.language }))] }), _jsx("div", { className: "overflow-x-auto", children: _jsx("div", { className: "min-w-full font-mono text-[13px] leading-6", children: lines.map((text, idx) => {
|
|
81
|
+
const lineNo = idx + 1;
|
|
82
|
+
const markers = lineMarkers.get(lineNo);
|
|
83
|
+
const isAnnotated = !!markers?.length;
|
|
84
|
+
const isActive = activeIndex != null &&
|
|
85
|
+
!!markers?.some((m) => m.index === activeIndex);
|
|
86
|
+
return (_jsxs("div", { className: cn("group flex w-full transition-colors", isActive
|
|
87
|
+
? "bg-amber-400/25 dark:bg-amber-300/20"
|
|
88
|
+
: isAnnotated
|
|
89
|
+
? "bg-amber-400/10 dark:bg-amber-300/10"
|
|
90
|
+
: ""), onMouseEnter: isAnnotated && markers
|
|
91
|
+
? () => setActiveIndex(markers[0].index)
|
|
92
|
+
: undefined, onMouseLeave: isAnnotated ? () => setActiveIndex(null) : undefined, children: [_jsx("span", { className: "w-10 shrink-0 select-none px-2 text-right text-[11px] leading-6 tabular-nums text-plan-muted/70", children: lineNo }), _jsx("span", { className: cn("flex w-6 shrink-0 select-none items-center justify-center", isActive &&
|
|
93
|
+
"shadow-[inset_2px_0_0_#f59e0b] dark:shadow-[inset_2px_0_0_#fcd34d]"), children: isAnnotated && markers && (_jsx("button", { type: "button", "data-plan-interactive": true, "aria-label": `Annotation ${markers[0].marker}`, onClick: () => {
|
|
94
|
+
setNotesOpen(true);
|
|
95
|
+
setRevealed((prev) => new Set(prev).add(markers[0].index));
|
|
96
|
+
setActiveIndex(markers[0].index);
|
|
97
|
+
}, onMouseEnter: () => setActiveIndex(markers[0].index), className: cn("flex size-[18px] cursor-pointer items-center justify-center rounded-full text-[10px] font-semibold leading-none transition-colors", isActive
|
|
98
|
+
? "bg-amber-500 text-white dark:bg-amber-400 dark:text-amber-950"
|
|
99
|
+
: "bg-amber-500/20 text-amber-700 hover:bg-amber-500/30 dark:bg-amber-300/20 dark:text-amber-200 dark:hover:bg-amber-300/30"), children: markers[0].marker })) }), _jsx("pre", { className: "m-0 flex-1 overflow-visible whitespace-pre px-3 text-plan-code-text", children: text || " " })] }, lineNo));
|
|
100
|
+
}) }) })] }), hasAnnotations && (_jsxs("div", { className: "mt-3", children: [_jsxs("button", { type: "button", "data-plan-interactive": true, onClick: () => setNotesOpen((open) => !open), "aria-expanded": notesOpen, className: "flex cursor-pointer items-center gap-1.5 text-xs font-medium text-plan-muted transition-colors hover:text-plan-text", children: [_jsx(IconChevronRight, { className: cn("size-3.5 transition-transform", notesOpen && "rotate-90") }), resolved.length, " annotation", resolved.length === 1 ? "" : "s"] }), notesOpen && (_jsx("ol", { className: "mt-2 flex flex-col gap-2", children: resolved.map((item) => {
|
|
101
|
+
const isActive = activeIndex === item.index;
|
|
102
|
+
const isRevealed = revealed.has(item.index);
|
|
103
|
+
return (_jsxs("li", { onMouseEnter: () => setActiveIndex(item.index), onMouseLeave: () => setActiveIndex(null), className: cn("flex gap-3 rounded-md border px-3 py-2 transition-colors", isActive
|
|
104
|
+
? "border-amber-400/60 bg-amber-400/10 dark:border-amber-300/40 dark:bg-amber-300/10"
|
|
105
|
+
: "border-plan-line bg-plan-block/40 hover:border-plan-line"), children: [_jsx("span", { "aria-hidden": true, className: cn("mt-0.5 flex size-[18px] shrink-0 items-center justify-center rounded-full text-[10px] font-semibold leading-none transition-colors", isActive
|
|
106
|
+
? "bg-amber-500 text-white dark:bg-amber-400 dark:text-amber-950"
|
|
107
|
+
: "bg-amber-500/20 text-amber-700 dark:bg-amber-300/20 dark:text-amber-200"), children: item.marker }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("span", { className: "rounded bg-plan-block px-1.5 py-0.5 font-mono text-[11px] text-plan-muted", children: item.range
|
|
108
|
+
? item.range.start === item.range.end
|
|
109
|
+
? `line ${item.range.start}`
|
|
110
|
+
: `lines ${item.range.start}–${item.range.end}`
|
|
111
|
+
: `lines ${item.annotation.lines}` }), item.annotation.label && (_jsx("span", { className: "text-sm font-semibold text-plan-text", children: item.annotation.label })), !isRevealed && (_jsx("button", { type: "button", "data-plan-interactive": true, onClick: () => toggleRevealed(item.index), className: "ml-auto cursor-pointer text-[11px] font-medium text-plan-muted underline-offset-2 hover:text-plan-text hover:underline", children: "Show note" }))] }), isRevealed && (_jsx("div", { className: "plan-annotation-note mt-1 text-sm text-plan-text", children: ctx.renderMarkdown ? (ctx.renderMarkdown(item.annotation.note)) : (_jsx("p", { children: item.annotation.note })) }))] })] }, item.index));
|
|
112
|
+
}) }))] })), summary && _jsx("p", { className: "mt-5 text-plan-muted", children: summary })] }));
|
|
113
|
+
}
|
|
114
|
+
/* ── Edit (panel) ──────────────────────────────────────────────────────────── */
|
|
115
|
+
const codeAreaClass = "min-h-[160px] font-mono text-xs leading-5";
|
|
116
|
+
function AnnotatedCodeEdit({ data, onChange, editable, }) {
|
|
117
|
+
const annotations = data.annotations ?? [];
|
|
118
|
+
const patch = (next) => onChange({ ...data, ...next });
|
|
119
|
+
const updateAnnotation = (index, next) => patch({
|
|
120
|
+
annotations: annotations.map((annotation, i) => i === index ? { ...annotation, ...next } : annotation),
|
|
121
|
+
});
|
|
122
|
+
const removeAnnotation = (index) => patch({ annotations: annotations.filter((_, i) => i !== index) });
|
|
123
|
+
const addAnnotation = () => {
|
|
124
|
+
if (annotations.length >= 80)
|
|
125
|
+
return; // schema max
|
|
126
|
+
patch({
|
|
127
|
+
annotations: [...annotations, { lines: "1", label: "", note: "" }],
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
return (_jsxs("div", { className: "flex flex-col gap-3", "data-plan-interactive": true, children: [_jsxs("div", { className: "grid gap-3 sm:grid-cols-2", children: [_jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(DevLabel, { htmlFor: "annotated-code-filename", className: "text-xs", children: "Filename" }), _jsx(DevInput, { id: "annotated-code-filename", value: data.filename ?? "", placeholder: "src/server/auth.ts", disabled: !editable, onChange: (event) => patch({ filename: event.target.value || undefined }) })] }), _jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(DevLabel, { htmlFor: "annotated-code-language", className: "text-xs", children: "Language" }), _jsx(DevInput, { id: "annotated-code-language", value: data.language ?? "", placeholder: "ts", disabled: !editable, onChange: (event) => patch({ language: event.target.value || undefined }) })] })] }), _jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(DevLabel, { htmlFor: "annotated-code-code", className: "text-xs", children: "Code" }), _jsx(DevTextarea, { id: "annotated-code-code", spellCheck: false, className: codeAreaClass, value: data.code, disabled: !editable, onChange: (event) => patch({ code: event.target.value }) })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx(DevLabel, { className: "text-xs", children: "Annotations" }), editable && annotations.length < 80 && (_jsxs("button", { type: "button", "data-plan-interactive": true, onClick: addAnnotation, className: "flex cursor-pointer items-center gap-1 rounded-md px-2 py-1 text-xs font-medium text-plan-muted transition-colors hover:bg-plan-block/60 hover:text-plan-text", children: [_jsx(IconPlus, { className: "size-3.5" }), "Add annotation"] }))] }), annotations.length === 0 && (_jsx("p", { className: "text-xs text-plan-muted", children: "No annotations yet. Add one to anchor a note to a line range." })), annotations.map((annotation, index) => (_jsxs("div", { className: "flex flex-col gap-2 rounded-md border border-plan-line bg-plan-block/30 p-2", children: [_jsxs("div", { className: "grid gap-2 sm:grid-cols-[120px_minmax(0,1fr)_auto]", children: [_jsx(DevInput, { "aria-label": `Annotation ${index + 1} lines`, value: annotation.lines, placeholder: "3-5", disabled: !editable, onChange: (event) => updateAnnotation(index, { lines: event.target.value }) }), _jsx(DevInput, { "aria-label": `Annotation ${index + 1} label`, value: annotation.label ?? "", placeholder: "Label (optional)", disabled: !editable, onChange: (event) => updateAnnotation(index, {
|
|
131
|
+
label: event.target.value || undefined,
|
|
132
|
+
}) }), editable && (_jsx("button", { type: "button", "data-plan-interactive": true, "aria-label": `Remove annotation ${index + 1}`, onClick: () => removeAnnotation(index), className: "flex size-9 shrink-0 cursor-pointer items-center justify-center rounded-md text-plan-muted transition-colors hover:bg-muted hover:text-foreground", children: _jsx(IconTrash, { className: "size-4" }) }))] }), _jsx(DevTextarea, { "aria-label": `Annotation ${index + 1} note`, className: "min-h-[60px] text-sm", value: annotation.note, placeholder: "Explain what these lines do\u2026", disabled: !editable, onChange: (event) => updateAnnotation(index, { note: event.target.value }) })] }, index)))] })] }));
|
|
133
|
+
}
|
|
134
|
+
export { AnnotatedCodeRead, AnnotatedCodeEdit };
|
|
135
|
+
//# sourceMappingURL=AnnotatedCodeBlock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AnnotatedCodeBlock.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/AnnotatedCodeBlock.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useMemo, useState } from \"react\";\nimport {\n IconChevronRight,\n IconCode,\n IconPlus,\n IconTrash,\n} from \"@tabler/icons-react\";\nimport { cn } from \"../../utils.js\";\nimport type { BlockEditProps, BlockReadProps } from \"../types.js\";\nimport type {\n AnnotatedCodeAnnotation,\n AnnotatedCodeData,\n} from \"./annotated-code.config.js\";\nimport { DevInput, DevLabel, DevTextarea } from \"./dev-doc-ui.js\";\n\n/**\n * Stripe-docs / Sourcegraph \"explain this code\" walkthrough block. The read\n * renderer shows the `code` as a line-numbered monospace surface; lines covered\n * by an annotation get a subtle highlight band and a numbered gutter marker. The\n * annotation notes render below, each with its `lines` range, optional `label`,\n * and markdown `note` (via `ctx.renderMarkdown`). Hovering a note highlights its\n * line range and vice-versa, so the line-anchoring is the differentiator. Lives\n * in core so any app can register the dev-doc block (no shadcn import).\n *\n * All colors are theme-aware: the surface uses the plan `--plan-code*`/`--plan-*`\n * tokens and the highlight/marker accents use Tailwind `light`/`dark:` pairs, so\n * the block reads correctly in BOTH light and dark mode.\n *\n * Editing is panel-driven (config-style, like the diff/HTML blocks): a monospace\n * code Textarea, filename/language Inputs, and add/remove-able annotation rows.\n */\n\n/* ── Line-ref parsing ──────────────────────────────────────────────────────── */\n\n/**\n * Parse a 1-based `lines` ref (`\"3\"` or `\"3-5\"`) into an inclusive `[start,end]`\n * pair, clamped to `[1, lineCount]`. Returns `null` for malformed or fully\n * out-of-range refs so callers can ignore them gracefully. A reversed range\n * (`\"5-3\"`) is normalized; a partially out-of-range range is clamped.\n */\nfunction parseLineRange(\n ref: string,\n lineCount: number,\n): { start: number; end: number } | null {\n const match = /^\\s*(\\d+)\\s*(?:-\\s*(\\d+)\\s*)?$/.exec(ref);\n if (!match) return null;\n let start = Number.parseInt(match[1], 10);\n let end = match[2] != null ? Number.parseInt(match[2], 10) : start;\n if (!Number.isFinite(start) || !Number.isFinite(end)) return null;\n if (start > end) [start, end] = [end, start];\n // Fully outside the file → ignore.\n if (end < 1 || start > lineCount) return null;\n return { start: Math.max(1, start), end: Math.min(lineCount, end) };\n}\n\ninterface ResolvedAnnotation {\n /** Index in the original `annotations` array (stable hover key). */\n index: number;\n /** 1-based marker number shown in the gutter + note list. */\n marker: number;\n annotation: AnnotatedCodeAnnotation;\n range: { start: number; end: number } | null;\n}\n\n/* ── Read ──────────────────────────────────────────────────────────────────── */\n\nfunction AnnotatedCodeRead({\n data,\n blockId,\n title,\n summary,\n ctx,\n}: BlockReadProps<AnnotatedCodeData>) {\n const [activeIndex, setActiveIndex] = useState<number | null>(null);\n const [notesOpen, setNotesOpen] = useState(true);\n const [revealed, setRevealed] = useState<Set<number>>(() => new Set());\n\n const lines = useMemo(() => data.code.split(\"\\n\"), [data.code]);\n const lineCount = lines.length;\n\n const resolved = useMemo<ResolvedAnnotation[]>(\n () =>\n (data.annotations ?? []).map((annotation, index) => ({\n index,\n marker: index + 1,\n annotation,\n range: parseLineRange(annotation.lines, lineCount),\n })),\n [data.annotations, lineCount],\n );\n\n // line number (1-based) → resolved annotations covering it (marker, active).\n const lineMarkers = useMemo(() => {\n const map = new Map<number, ResolvedAnnotation[]>();\n for (const item of resolved) {\n if (!item.range) continue;\n for (let n = item.range.start; n <= item.range.end; n += 1) {\n const list = map.get(n) ?? [];\n list.push(item);\n map.set(n, list);\n }\n }\n return map;\n }, [resolved]);\n\n const hasAnnotations = resolved.some((item) => item.range);\n\n const toggleRevealed = (index: number) =>\n setRevealed((prev) => {\n const next = new Set(prev);\n if (next.has(index)) next.delete(index);\n else next.add(index);\n return next;\n });\n\n return (\n <section className=\"plan-block\" data-block-id={blockId}>\n {title && <div className=\"plan-block-label\">{title}</div>}\n <div className=\"overflow-hidden rounded-lg border border-plan-line bg-plan-code\">\n {/* Header: filename + optional language chip. */}\n <div className=\"flex flex-wrap items-center gap-2 border-b border-plan-line bg-plan-block px-3 py-2\">\n <IconCode className=\"size-4 shrink-0 text-plan-muted\" />\n <span className=\"min-w-0 truncate font-mono text-sm font-medium text-plan-code-text\">\n {data.filename || \"snippet\"}\n </span>\n {data.language && (\n <span className=\"shrink-0 rounded border border-plan-line px-1.5 py-0.5 font-mono text-[11px] uppercase tracking-wide text-plan-muted\">\n {data.language}\n </span>\n )}\n </div>\n\n {/* Code surface — line-numbered, with highlight bands + gutter markers. */}\n <div className=\"overflow-x-auto\">\n <div className=\"min-w-full font-mono text-[13px] leading-6\">\n {lines.map((text, idx) => {\n const lineNo = idx + 1;\n const markers = lineMarkers.get(lineNo);\n const isAnnotated = !!markers?.length;\n const isActive =\n activeIndex != null &&\n !!markers?.some((m) => m.index === activeIndex);\n return (\n <div\n key={lineNo}\n className={cn(\n \"group flex w-full transition-colors\",\n isActive\n ? \"bg-amber-400/25 dark:bg-amber-300/20\"\n : isAnnotated\n ? \"bg-amber-400/10 dark:bg-amber-300/10\"\n : \"\",\n )}\n onMouseEnter={\n isAnnotated && markers\n ? () => setActiveIndex(markers[0].index)\n : undefined\n }\n onMouseLeave={\n isAnnotated ? () => setActiveIndex(null) : undefined\n }\n >\n {/* Gutter: line number. */}\n <span className=\"w-10 shrink-0 select-none px-2 text-right text-[11px] leading-6 tabular-nums text-plan-muted/70\">\n {lineNo}\n </span>\n {/* Marker rail: numbered dot when annotated, accent bar when active. */}\n <span\n className={cn(\n \"flex w-6 shrink-0 select-none items-center justify-center\",\n isActive &&\n \"shadow-[inset_2px_0_0_#f59e0b] dark:shadow-[inset_2px_0_0_#fcd34d]\",\n )}\n >\n {isAnnotated && markers && (\n <button\n type=\"button\"\n data-plan-interactive\n aria-label={`Annotation ${markers[0].marker}`}\n onClick={() => {\n setNotesOpen(true);\n setRevealed((prev) =>\n new Set(prev).add(markers[0].index),\n );\n setActiveIndex(markers[0].index);\n }}\n onMouseEnter={() => setActiveIndex(markers[0].index)}\n className={cn(\n \"flex size-[18px] cursor-pointer items-center justify-center rounded-full text-[10px] font-semibold leading-none transition-colors\",\n isActive\n ? \"bg-amber-500 text-white dark:bg-amber-400 dark:text-amber-950\"\n : \"bg-amber-500/20 text-amber-700 hover:bg-amber-500/30 dark:bg-amber-300/20 dark:text-amber-200 dark:hover:bg-amber-300/30\",\n )}\n >\n {markers[0].marker}\n </button>\n )}\n </span>\n <pre className=\"m-0 flex-1 overflow-visible whitespace-pre px-3 text-plan-code-text\">\n {text || \" \"}\n </pre>\n </div>\n );\n })}\n </div>\n </div>\n </div>\n\n {/* Annotation notes — line-anchored, collapsible (progressive disclosure). */}\n {hasAnnotations && (\n <div className=\"mt-3\">\n <button\n type=\"button\"\n data-plan-interactive\n onClick={() => setNotesOpen((open) => !open)}\n aria-expanded={notesOpen}\n className=\"flex cursor-pointer items-center gap-1.5 text-xs font-medium text-plan-muted transition-colors hover:text-plan-text\"\n >\n <IconChevronRight\n className={cn(\n \"size-3.5 transition-transform\",\n notesOpen && \"rotate-90\",\n )}\n />\n {resolved.length} annotation{resolved.length === 1 ? \"\" : \"s\"}\n </button>\n {notesOpen && (\n <ol className=\"mt-2 flex flex-col gap-2\">\n {resolved.map((item) => {\n const isActive = activeIndex === item.index;\n const isRevealed = revealed.has(item.index);\n return (\n <li\n key={item.index}\n onMouseEnter={() => setActiveIndex(item.index)}\n onMouseLeave={() => setActiveIndex(null)}\n className={cn(\n \"flex gap-3 rounded-md border px-3 py-2 transition-colors\",\n isActive\n ? \"border-amber-400/60 bg-amber-400/10 dark:border-amber-300/40 dark:bg-amber-300/10\"\n : \"border-plan-line bg-plan-block/40 hover:border-plan-line\",\n )}\n >\n <span\n aria-hidden\n className={cn(\n \"mt-0.5 flex size-[18px] shrink-0 items-center justify-center rounded-full text-[10px] font-semibold leading-none transition-colors\",\n isActive\n ? \"bg-amber-500 text-white dark:bg-amber-400 dark:text-amber-950\"\n : \"bg-amber-500/20 text-amber-700 dark:bg-amber-300/20 dark:text-amber-200\",\n )}\n >\n {item.marker}\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"rounded bg-plan-block px-1.5 py-0.5 font-mono text-[11px] text-plan-muted\">\n {item.range\n ? item.range.start === item.range.end\n ? `line ${item.range.start}`\n : `lines ${item.range.start}–${item.range.end}`\n : `lines ${item.annotation.lines}`}\n </span>\n {item.annotation.label && (\n <span className=\"text-sm font-semibold text-plan-text\">\n {item.annotation.label}\n </span>\n )}\n {!isRevealed && (\n <button\n type=\"button\"\n data-plan-interactive\n onClick={() => toggleRevealed(item.index)}\n className=\"ml-auto cursor-pointer text-[11px] font-medium text-plan-muted underline-offset-2 hover:text-plan-text hover:underline\"\n >\n Show note\n </button>\n )}\n </div>\n {isRevealed && (\n <div className=\"plan-annotation-note mt-1 text-sm text-plan-text\">\n {ctx.renderMarkdown ? (\n ctx.renderMarkdown(item.annotation.note)\n ) : (\n <p>{item.annotation.note}</p>\n )}\n </div>\n )}\n </div>\n </li>\n );\n })}\n </ol>\n )}\n </div>\n )}\n {summary && <p className=\"mt-5 text-plan-muted\">{summary}</p>}\n </section>\n );\n}\n\n/* ── Edit (panel) ──────────────────────────────────────────────────────────── */\n\nconst codeAreaClass = \"min-h-[160px] font-mono text-xs leading-5\";\n\nfunction AnnotatedCodeEdit({\n data,\n onChange,\n editable,\n}: BlockEditProps<AnnotatedCodeData>) {\n const annotations = data.annotations ?? [];\n const patch = (next: Partial<AnnotatedCodeData>) =>\n onChange({ ...data, ...next });\n\n const updateAnnotation = (\n index: number,\n next: Partial<AnnotatedCodeAnnotation>,\n ) =>\n patch({\n annotations: annotations.map((annotation, i) =>\n i === index ? { ...annotation, ...next } : annotation,\n ),\n });\n\n const removeAnnotation = (index: number) =>\n patch({ annotations: annotations.filter((_, i) => i !== index) });\n\n const addAnnotation = () => {\n if (annotations.length >= 80) return; // schema max\n patch({\n annotations: [...annotations, { lines: \"1\", label: \"\", note: \"\" }],\n });\n };\n\n return (\n <div className=\"flex flex-col gap-3\" data-plan-interactive>\n <div className=\"grid gap-3 sm:grid-cols-2\">\n <div className=\"flex flex-col gap-1.5\">\n <DevLabel htmlFor=\"annotated-code-filename\" className=\"text-xs\">\n Filename\n </DevLabel>\n <DevInput\n id=\"annotated-code-filename\"\n value={data.filename ?? \"\"}\n placeholder=\"src/server/auth.ts\"\n disabled={!editable}\n onChange={(event) =>\n patch({ filename: event.target.value || undefined })\n }\n />\n </div>\n <div className=\"flex flex-col gap-1.5\">\n <DevLabel htmlFor=\"annotated-code-language\" className=\"text-xs\">\n Language\n </DevLabel>\n <DevInput\n id=\"annotated-code-language\"\n value={data.language ?? \"\"}\n placeholder=\"ts\"\n disabled={!editable}\n onChange={(event) =>\n patch({ language: event.target.value || undefined })\n }\n />\n </div>\n </div>\n\n <div className=\"flex flex-col gap-1.5\">\n <DevLabel htmlFor=\"annotated-code-code\" className=\"text-xs\">\n Code\n </DevLabel>\n <DevTextarea\n id=\"annotated-code-code\"\n spellCheck={false}\n className={codeAreaClass}\n value={data.code}\n disabled={!editable}\n onChange={(event) => patch({ code: event.target.value })}\n />\n </div>\n\n <div className=\"flex flex-col gap-2\">\n <div className=\"flex items-center justify-between\">\n <DevLabel className=\"text-xs\">Annotations</DevLabel>\n {editable && annotations.length < 80 && (\n <button\n type=\"button\"\n data-plan-interactive\n onClick={addAnnotation}\n className=\"flex cursor-pointer items-center gap-1 rounded-md px-2 py-1 text-xs font-medium text-plan-muted transition-colors hover:bg-plan-block/60 hover:text-plan-text\"\n >\n <IconPlus className=\"size-3.5\" />\n Add annotation\n </button>\n )}\n </div>\n {annotations.length === 0 && (\n <p className=\"text-xs text-plan-muted\">\n No annotations yet. Add one to anchor a note to a line range.\n </p>\n )}\n {annotations.map((annotation, index) => (\n <div\n key={index}\n className=\"flex flex-col gap-2 rounded-md border border-plan-line bg-plan-block/30 p-2\"\n >\n <div className=\"grid gap-2 sm:grid-cols-[120px_minmax(0,1fr)_auto]\">\n <DevInput\n aria-label={`Annotation ${index + 1} lines`}\n value={annotation.lines}\n placeholder=\"3-5\"\n disabled={!editable}\n onChange={(event) =>\n updateAnnotation(index, { lines: event.target.value })\n }\n />\n <DevInput\n aria-label={`Annotation ${index + 1} label`}\n value={annotation.label ?? \"\"}\n placeholder=\"Label (optional)\"\n disabled={!editable}\n onChange={(event) =>\n updateAnnotation(index, {\n label: event.target.value || undefined,\n })\n }\n />\n {editable && (\n <button\n type=\"button\"\n data-plan-interactive\n aria-label={`Remove annotation ${index + 1}`}\n onClick={() => removeAnnotation(index)}\n className=\"flex size-9 shrink-0 cursor-pointer items-center justify-center rounded-md text-plan-muted transition-colors hover:bg-muted hover:text-foreground\"\n >\n <IconTrash className=\"size-4\" />\n </button>\n )}\n </div>\n <DevTextarea\n aria-label={`Annotation ${index + 1} note`}\n className=\"min-h-[60px] text-sm\"\n value={annotation.note}\n placeholder=\"Explain what these lines do…\"\n disabled={!editable}\n onChange={(event) =>\n updateAnnotation(index, { note: event.target.value })\n }\n />\n </div>\n ))}\n </div>\n </div>\n );\n}\n\nexport { AnnotatedCodeRead, AnnotatedCodeEdit };\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { BlockEditProps, BlockReadProps } from "../types.js";
|
|
2
|
+
import type { ApiEndpointData } from "./api-endpoint.config.js";
|
|
3
|
+
/**
|
|
4
|
+
* Read-only renderer for an `api-endpoint` block. Collapsed by default: a single
|
|
5
|
+
* row with a colored method pill, monospace path, muted summary, and a chevron.
|
|
6
|
+
* Clicking the row expands the full reference (description, params table,
|
|
7
|
+
* request body, responses) — the Swagger / Stripe house style. Every colored
|
|
8
|
+
* element is theme-aware (`dark:` variants), so it reads correctly in both the
|
|
9
|
+
* `.dark` plan theme and light mode.
|
|
10
|
+
*/
|
|
11
|
+
export declare function ApiEndpointRead({ data, blockId, title, summary, ctx, }: BlockReadProps<ApiEndpointData>): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
/**
|
|
13
|
+
* Panel editor for an `api-endpoint` block. A property form: method (Select),
|
|
14
|
+
* path/summary/auth (Input), description (Textarea), deprecated (Switch), plus
|
|
15
|
+
* repeatable rows for params and responses (add/remove) and a request-body
|
|
16
|
+
* textarea. Renders BARE content (no `<section>`); the registry's panel surface
|
|
17
|
+
* supplies the popover chrome.
|
|
18
|
+
*/
|
|
19
|
+
export declare function ApiEndpointEdit({ data, onChange, editable, }: BlockEditProps<ApiEndpointData>): import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
//# sourceMappingURL=ApiEndpointBlock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ApiEndpointBlock.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/ApiEndpointBlock.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,KAAK,EACV,eAAe,EAKhB,MAAM,0BAA0B,CAAC;AA+ElC;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,EAC9B,IAAI,EACJ,OAAO,EACP,KAAK,EACL,OAAO,EACP,GAAG,GACJ,EAAE,cAAc,CAAC,eAAe,CAAC,2CAuNjC;AAMD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,EAC9B,IAAI,EACJ,QAAQ,EACR,QAAQ,GACT,EAAE,cAAc,CAAC,eAAe,CAAC,2CAsTjC"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { IconChevronRight, IconLock, IconPlus, IconTrash, } from "@tabler/icons-react";
|
|
4
|
+
import { cn } from "../../utils.js";
|
|
5
|
+
import { API_ENDPOINT_METHODS, API_PARAM_LOCATIONS, } from "./api-endpoint.config.js";
|
|
6
|
+
import { DevBadge, DevInput, DevSwitch, DevTextarea, DevSelect, } from "./dev-doc-ui.js";
|
|
7
|
+
/**
|
|
8
|
+
* Read + Edit renderers for an `api-endpoint` block — a Swagger / Stripe-style
|
|
9
|
+
* API reference. Lives in core so any app can register the dev-doc block (no
|
|
10
|
+
* shadcn import).
|
|
11
|
+
*/
|
|
12
|
+
/* ── Theme-aware color tokens ──────────────────────────────────────────────── */
|
|
13
|
+
/**
|
|
14
|
+
* Method-pill palette. Tinted background + saturated text in BOTH modes (the
|
|
15
|
+
* reference HTML hardcoded a dark-only palette — we deliberately avoid that).
|
|
16
|
+
* Each entry keeps legible contrast against the plan surface under `.dark` and
|
|
17
|
+
* light via Tailwind `dark:` variants.
|
|
18
|
+
*/
|
|
19
|
+
const METHOD_PILL = {
|
|
20
|
+
GET: "bg-emerald-100 text-emerald-700 dark:bg-emerald-500/15 dark:text-emerald-300",
|
|
21
|
+
POST: "bg-blue-100 text-blue-700 dark:bg-blue-500/15 dark:text-blue-300",
|
|
22
|
+
PUT: "bg-amber-100 text-amber-700 dark:bg-amber-500/15 dark:text-amber-300",
|
|
23
|
+
PATCH: "bg-violet-100 text-violet-700 dark:bg-violet-500/15 dark:text-violet-300",
|
|
24
|
+
DELETE: "bg-red-100 text-red-700 dark:bg-red-500/15 dark:text-red-300",
|
|
25
|
+
HEAD: "bg-slate-200 text-slate-700 dark:bg-slate-500/20 dark:text-slate-300",
|
|
26
|
+
OPTIONS: "bg-slate-200 text-slate-700 dark:bg-slate-500/20 dark:text-slate-300",
|
|
27
|
+
};
|
|
28
|
+
/** Location-badge palette for the params table `in` column. */
|
|
29
|
+
const PARAM_IN_BADGE = {
|
|
30
|
+
path: "bg-violet-100 text-violet-700 dark:bg-violet-500/15 dark:text-violet-300",
|
|
31
|
+
query: "bg-blue-100 text-blue-700 dark:bg-blue-500/15 dark:text-blue-300",
|
|
32
|
+
header: "bg-amber-100 text-amber-700 dark:bg-amber-500/15 dark:text-amber-300",
|
|
33
|
+
body: "bg-emerald-100 text-emerald-700 dark:bg-emerald-500/15 dark:text-emerald-300",
|
|
34
|
+
};
|
|
35
|
+
/** Status-pill palette keyed by the leading status digit (2xx/3xx/4xx/5xx). */
|
|
36
|
+
function statusPillClass(status) {
|
|
37
|
+
const lead = status.trim().charAt(0);
|
|
38
|
+
if (lead === "2")
|
|
39
|
+
return "bg-emerald-100 text-emerald-700 dark:bg-emerald-500/15 dark:text-emerald-300";
|
|
40
|
+
if (lead === "4")
|
|
41
|
+
return "bg-amber-100 text-amber-700 dark:bg-amber-500/15 dark:text-amber-300";
|
|
42
|
+
if (lead === "5")
|
|
43
|
+
return "bg-red-100 text-red-700 dark:bg-red-500/15 dark:text-red-300";
|
|
44
|
+
// 3xx and everything else → neutral slate.
|
|
45
|
+
return "bg-slate-200 text-slate-700 dark:bg-slate-500/20 dark:text-slate-300";
|
|
46
|
+
}
|
|
47
|
+
/** Guess a fence language from a content type so examples highlight nicely. */
|
|
48
|
+
function fenceLangForContentType(contentType) {
|
|
49
|
+
const ct = (contentType ?? "").toLowerCase();
|
|
50
|
+
if (ct.includes("json"))
|
|
51
|
+
return "json";
|
|
52
|
+
if (ct.includes("xml") || ct.includes("html"))
|
|
53
|
+
return "html";
|
|
54
|
+
if (ct.includes("yaml") || ct.includes("yml"))
|
|
55
|
+
return "yaml";
|
|
56
|
+
return "json";
|
|
57
|
+
}
|
|
58
|
+
/** Wrap a raw example string in a fenced code block for `ctx.renderMarkdown`. */
|
|
59
|
+
function fence(example, lang) {
|
|
60
|
+
// Never let the example's own content break out of the fence.
|
|
61
|
+
const safe = example.replace(/```/g, "ʼʼʼ");
|
|
62
|
+
return `\`\`\`${lang}\n${safe.trim()}\n\`\`\``;
|
|
63
|
+
}
|
|
64
|
+
/* ── Read (collapsed-by-default swagger row) ───────────────────────────────── */
|
|
65
|
+
/**
|
|
66
|
+
* Read-only renderer for an `api-endpoint` block. Collapsed by default: a single
|
|
67
|
+
* row with a colored method pill, monospace path, muted summary, and a chevron.
|
|
68
|
+
* Clicking the row expands the full reference (description, params table,
|
|
69
|
+
* request body, responses) — the Swagger / Stripe house style. Every colored
|
|
70
|
+
* element is theme-aware (`dark:` variants), so it reads correctly in both the
|
|
71
|
+
* `.dark` plan theme and light mode.
|
|
72
|
+
*/
|
|
73
|
+
export function ApiEndpointRead({ data, blockId, title, summary, ctx, }) {
|
|
74
|
+
const [open, setOpen] = useState(false);
|
|
75
|
+
const params = data.params ?? [];
|
|
76
|
+
const responses = data.responses ?? [];
|
|
77
|
+
const hasRequest = Boolean(data.request?.example || data.request?.contentType);
|
|
78
|
+
const hasBody = Boolean(data.description?.trim()) ||
|
|
79
|
+
params.length > 0 ||
|
|
80
|
+
hasRequest ||
|
|
81
|
+
responses.length > 0 ||
|
|
82
|
+
Boolean(data.auth);
|
|
83
|
+
return (_jsxs("section", { className: "plan-block", "data-block-id": blockId, children: [title && _jsx("div", { className: "plan-block-label", children: title }), _jsxs("div", { className: "overflow-hidden rounded-xl border border-plan-line bg-plan-block", children: [_jsxs("button", { type: "button", "data-plan-interactive": true, "aria-expanded": open, onClick: () => setOpen((value) => !value), className: cn("flex w-full items-center gap-3 px-4 py-3 text-left transition-colors", "hover:bg-accent/40"), children: [_jsx(IconChevronRight, { className: cn("size-4 shrink-0 text-plan-muted transition-transform", open && "rotate-90") }), _jsx("span", { className: cn("shrink-0 rounded-md px-2 py-1 font-mono text-xs font-bold uppercase tracking-wide", METHOD_PILL[data.method]), children: data.method }), _jsx("span", { className: cn("min-w-0 truncate font-mono text-sm font-semibold text-plan-text", data.deprecated && "text-plan-muted line-through"), children: data.path }), data.deprecated && (_jsx(DevBadge, { className: "shrink-0 border-amber-500/40 text-amber-600 dark:text-amber-300", children: "Deprecated" })), (data.summary || summary) && (_jsx("span", { className: "ml-1 min-w-0 flex-1 truncate text-sm text-plan-muted", children: data.summary || summary })), data.auth && (_jsx(IconLock, { className: "size-3.5 shrink-0 text-plan-muted", "aria-label": "Requires authentication" }))] }), open && hasBody && (_jsxs("div", { className: "border-t border-plan-line px-4 py-4", children: [data.auth && (_jsxs("div", { className: "mb-4 flex items-center gap-2 text-xs text-plan-muted", children: [_jsx(IconLock, { className: "size-3.5 shrink-0" }), _jsxs("span", { children: [_jsx("span", { className: "font-medium text-plan-text", children: "Auth:" }), " ", data.auth] })] })), data.description?.trim() && (_jsx("div", { className: "an-api-endpoint-desc", children: ctx.renderMarkdown?.(data.description) })), params.length > 0 && (_jsxs("div", { className: "mt-5", children: [_jsx("div", { className: "text-xs font-semibold uppercase tracking-wide text-plan-muted", children: "Parameters" }), _jsx("div", { className: "mt-2 overflow-hidden rounded-lg border border-plan-line", children: _jsxs("table", { className: "w-full border-collapse text-sm", children: [_jsx("thead", { children: _jsxs("tr", { className: "bg-accent/30 text-left text-xs uppercase tracking-wide text-plan-muted", children: [_jsx("th", { className: "px-3 py-2 font-medium", children: "Name" }), _jsx("th", { className: "px-3 py-2 font-medium", children: "In" }), _jsx("th", { className: "px-3 py-2 font-medium", children: "Type" }), _jsx("th", { className: "px-3 py-2 font-medium", children: "Required" }), _jsx("th", { className: "px-3 py-2 font-medium", children: "Description" })] }) }), _jsx("tbody", { children: params.map((param, index) => (_jsxs("tr", { className: "border-t border-plan-line align-top", children: [_jsx("td", { className: "px-3 py-2 font-mono text-xs font-semibold text-plan-text", children: param.name }), _jsx("td", { className: "px-3 py-2", children: _jsx("span", { className: cn("rounded px-1.5 py-0.5 font-mono text-[11px] font-semibold", PARAM_IN_BADGE[param.in]), children: param.in }) }), _jsx("td", { className: "px-3 py-2 font-mono text-xs text-plan-muted", children: param.type || "—" }), _jsx("td", { className: "px-3 py-2 text-xs", children: param.required ? (_jsx("span", { className: "font-medium text-red-600 dark:text-red-300", children: "required" })) : (_jsx("span", { className: "text-plan-muted", children: "optional" })) }), _jsx("td", { className: "px-3 py-2 text-xs text-plan-muted", children: param.description || "—" })] }, `${param.name}-${index}`))) })] }) })] })), hasRequest && (_jsxs("div", { className: "mt-5", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-xs font-semibold uppercase tracking-wide text-plan-muted", children: "Request body" }), data.request?.contentType && (_jsx("span", { className: "rounded bg-accent/40 px-1.5 py-0.5 font-mono text-[11px] text-plan-muted", children: data.request.contentType }))] }), data.request?.example && (_jsx("div", { className: "mt-2 an-api-endpoint-example", children: ctx.renderMarkdown?.(fence(data.request.example, fenceLangForContentType(data.request.contentType))) }))] })), responses.length > 0 && (_jsxs("div", { className: "mt-5", children: [_jsx("div", { className: "text-xs font-semibold uppercase tracking-wide text-plan-muted", children: "Responses" }), _jsx("div", { className: "mt-2 flex flex-col gap-3", children: responses.map((response, index) => (_jsxs("div", { className: "rounded-lg border border-plan-line", children: [_jsxs("div", { className: "flex items-center gap-2 px-3 py-2", children: [_jsx("span", { className: cn("rounded px-2 py-0.5 font-mono text-xs font-bold", statusPillClass(response.status)), children: response.status }), response.description && (_jsx("span", { className: "text-sm text-plan-muted", children: response.description }))] }), response.example && (_jsx("div", { className: "border-t border-plan-line px-3 pb-3 pt-1 an-api-endpoint-example", children: ctx.renderMarkdown?.(fence(response.example, "json")) }))] }, `${response.status}-${index}`))) })] }))] }))] })] }));
|
|
84
|
+
}
|
|
85
|
+
/* ── Edit (panel form) ─────────────────────────────────────────────────────── */
|
|
86
|
+
const fieldLabelClass = "text-xs font-medium text-muted-foreground";
|
|
87
|
+
/**
|
|
88
|
+
* Panel editor for an `api-endpoint` block. A property form: method (Select),
|
|
89
|
+
* path/summary/auth (Input), description (Textarea), deprecated (Switch), plus
|
|
90
|
+
* repeatable rows for params and responses (add/remove) and a request-body
|
|
91
|
+
* textarea. Renders BARE content (no `<section>`); the registry's panel surface
|
|
92
|
+
* supplies the popover chrome.
|
|
93
|
+
*/
|
|
94
|
+
export function ApiEndpointEdit({ data, onChange, editable, }) {
|
|
95
|
+
const params = data.params ?? [];
|
|
96
|
+
const responses = data.responses ?? [];
|
|
97
|
+
const patch = (next) => onChange({ ...data, ...next });
|
|
98
|
+
const updateParam = (index, next) => patch({
|
|
99
|
+
params: params.map((param, i) => i === index ? { ...param, ...next } : param),
|
|
100
|
+
});
|
|
101
|
+
const removeParam = (index) => patch({ params: params.filter((_, i) => i !== index) });
|
|
102
|
+
const addParam = () => patch({
|
|
103
|
+
params: [...params, { name: "param", in: "query" }],
|
|
104
|
+
});
|
|
105
|
+
const updateResponse = (index, next) => patch({
|
|
106
|
+
responses: responses.map((response, i) => i === index ? { ...response, ...next } : response),
|
|
107
|
+
});
|
|
108
|
+
const removeResponse = (index) => patch({ responses: responses.filter((_, i) => i !== index) });
|
|
109
|
+
const addResponse = () => patch({ responses: [...responses, { status: "200" }] });
|
|
110
|
+
const updateRequest = (next) => {
|
|
111
|
+
const merged = { ...(data.request ?? {}), ...next };
|
|
112
|
+
const empty = !merged.contentType && !merged.example;
|
|
113
|
+
patch({ request: empty ? undefined : merged });
|
|
114
|
+
};
|
|
115
|
+
return (_jsxs("div", { className: "flex flex-col gap-4", "data-plan-interactive": true, children: [_jsxs("div", { className: "grid grid-cols-[120px_minmax(0,1fr)] gap-2", children: [_jsxs("label", { className: "flex flex-col gap-1.5", children: [_jsx("span", { className: fieldLabelClass, children: "Method" }), _jsx(DevSelect, { className: "h-9", value: data.method, disabled: !editable, onValueChange: (value) => patch({ method: value }), options: API_ENDPOINT_METHODS.map((method) => ({
|
|
116
|
+
value: method,
|
|
117
|
+
label: method,
|
|
118
|
+
})) })] }), _jsxs("label", { className: "flex flex-col gap-1.5", children: [_jsx("span", { className: fieldLabelClass, children: "Path" }), _jsx(DevInput, { className: "h-9 font-mono", value: data.path, disabled: !editable, placeholder: "/api/resource", onChange: (event) => patch({ path: event.target.value }) })] })] }), _jsxs("label", { className: "flex flex-col gap-1.5", children: [_jsx("span", { className: fieldLabelClass, children: "Summary" }), _jsx(DevInput, { className: "h-9", value: data.summary ?? "", disabled: !editable, placeholder: "Short one-line description", onChange: (event) => patch({ summary: event.target.value || undefined }) })] }), _jsxs("label", { className: "flex flex-col gap-1.5", children: [_jsx("span", { className: fieldLabelClass, children: "Description (markdown)" }), _jsx(DevTextarea, { className: "min-h-[80px]", value: data.description ?? "", disabled: !editable, placeholder: "Longer description, rendered as markdown", onChange: (event) => patch({ description: event.target.value || undefined }) })] }), _jsxs("div", { className: "grid grid-cols-[minmax(0,1fr)_auto] items-end gap-3", children: [_jsxs("label", { className: "flex flex-col gap-1.5", children: [_jsx("span", { className: fieldLabelClass, children: "Auth" }), _jsx(DevInput, { className: "h-9", value: data.auth ?? "", disabled: !editable, placeholder: "e.g. Bearer token", onChange: (event) => patch({ auth: event.target.value || undefined }) })] }), _jsxs("label", { className: "flex items-center gap-2 pb-2", children: [_jsx(DevSwitch, { checked: Boolean(data.deprecated), disabled: !editable, onCheckedChange: (checked) => patch({ deprecated: checked || undefined }) }), _jsx("span", { className: fieldLabelClass, children: "Deprecated" })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: fieldLabelClass, children: "Parameters" }), editable && (_jsxs("button", { type: "button", "data-plan-interactive": true, className: "flex items-center gap-1 rounded-md px-2 py-1 text-xs text-muted-foreground hover:bg-accent/60 hover:text-foreground", onClick: addParam, children: [_jsx(IconPlus, { className: "size-3.5" }), "Add"] }))] }), params.map((param, index) => (_jsxs("div", { className: "flex flex-col gap-2 rounded-md border border-input p-2", children: [_jsxs("div", { className: "grid grid-cols-[minmax(0,1fr)_96px_auto] gap-2", children: [_jsx(DevInput, { className: "h-8 font-mono text-xs", value: param.name, disabled: !editable, placeholder: "name", onChange: (event) => updateParam(index, { name: event.target.value }) }), _jsx(DevSelect, { className: "h-8", value: param.in, disabled: !editable, onValueChange: (value) => updateParam(index, { in: value }), options: API_PARAM_LOCATIONS.map((location) => ({
|
|
119
|
+
value: location,
|
|
120
|
+
label: location,
|
|
121
|
+
})) }), editable && (_jsx("button", { type: "button", "data-plan-interactive": true, "aria-label": "Remove parameter", className: "flex size-8 items-center justify-center rounded-md text-muted-foreground hover:bg-accent/60 hover:text-foreground", onClick: () => removeParam(index), children: _jsx(IconTrash, { className: "size-4" }) }))] }), _jsxs("div", { className: "grid grid-cols-[minmax(0,1fr)_auto] items-center gap-2", children: [_jsx(DevInput, { className: "h-8 font-mono text-xs", value: param.type ?? "", disabled: !editable, placeholder: "type (e.g. string)", onChange: (event) => updateParam(index, { type: event.target.value || undefined }) }), _jsxs("label", { className: "flex items-center gap-1.5 whitespace-nowrap text-xs text-muted-foreground", children: [_jsx("input", { type: "checkbox", className: "size-3.5 cursor-pointer accent-primary", checked: Boolean(param.required), disabled: !editable, onChange: (event) => updateParam(index, {
|
|
122
|
+
required: event.target.checked || undefined,
|
|
123
|
+
}) }), "Required"] })] }), _jsx(DevInput, { className: "h-8 text-xs", value: param.description ?? "", disabled: !editable, placeholder: "description", onChange: (event) => updateParam(index, {
|
|
124
|
+
description: event.target.value || undefined,
|
|
125
|
+
}) })] }, index)))] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("span", { className: fieldLabelClass, children: "Request body" }), _jsx(DevInput, { className: "h-8 font-mono text-xs", value: data.request?.contentType ?? "", disabled: !editable, placeholder: "content type (e.g. application/json)", onChange: (event) => updateRequest({ contentType: event.target.value || undefined }) }), _jsx(DevTextarea, { className: "min-h-[80px] font-mono text-xs", value: data.request?.example ?? "", disabled: !editable, placeholder: '{ "example": "request body" }', onChange: (event) => updateRequest({ example: event.target.value || undefined }) })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: fieldLabelClass, children: "Responses" }), editable && (_jsxs("button", { type: "button", "data-plan-interactive": true, className: "flex items-center gap-1 rounded-md px-2 py-1 text-xs text-muted-foreground hover:bg-accent/60 hover:text-foreground", onClick: addResponse, children: [_jsx(IconPlus, { className: "size-3.5" }), "Add"] }))] }), responses.map((response, index) => (_jsxs("div", { className: "flex flex-col gap-2 rounded-md border border-input p-2", children: [_jsxs("div", { className: "grid grid-cols-[96px_minmax(0,1fr)_auto] gap-2", children: [_jsx(DevInput, { className: "h-8 font-mono text-xs", value: response.status, disabled: !editable, placeholder: "200", onChange: (event) => updateResponse(index, { status: event.target.value }) }), _jsx(DevInput, { className: "h-8 text-xs", value: response.description ?? "", disabled: !editable, placeholder: "description", onChange: (event) => updateResponse(index, {
|
|
126
|
+
description: event.target.value || undefined,
|
|
127
|
+
}) }), editable && (_jsx("button", { type: "button", "data-plan-interactive": true, "aria-label": "Remove response", className: "flex size-8 items-center justify-center rounded-md text-muted-foreground hover:bg-accent/60 hover:text-foreground", onClick: () => removeResponse(index), children: _jsx(IconTrash, { className: "size-4" }) }))] }), _jsx(DevTextarea, { className: "min-h-[64px] font-mono text-xs", value: response.example ?? "", disabled: !editable, placeholder: '{ "example": "response body" }', onChange: (event) => updateResponse(index, {
|
|
128
|
+
example: event.target.value || undefined,
|
|
129
|
+
}) })] }, index)))] })] }));
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=ApiEndpointBlock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ApiEndpointBlock.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/ApiEndpointBlock.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useState } from \"react\";\nimport {\n IconChevronRight,\n IconLock,\n IconPlus,\n IconTrash,\n} from \"@tabler/icons-react\";\nimport { cn } from \"../../utils.js\";\nimport type { BlockEditProps, BlockReadProps } from \"../types.js\";\nimport type {\n ApiEndpointData,\n ApiEndpointMethod,\n ApiEndpointParam,\n ApiEndpointResponse,\n ApiParamLocation,\n} from \"./api-endpoint.config.js\";\nimport {\n API_ENDPOINT_METHODS,\n API_PARAM_LOCATIONS,\n} from \"./api-endpoint.config.js\";\nimport {\n DevBadge,\n DevInput,\n DevSwitch,\n DevTextarea,\n DevSelect,\n} from \"./dev-doc-ui.js\";\n\n/**\n * Read + Edit renderers for an `api-endpoint` block — a Swagger / Stripe-style\n * API reference. Lives in core so any app can register the dev-doc block (no\n * shadcn import).\n */\n\n/* ── Theme-aware color tokens ──────────────────────────────────────────────── */\n\n/**\n * Method-pill palette. Tinted background + saturated text in BOTH modes (the\n * reference HTML hardcoded a dark-only palette — we deliberately avoid that).\n * Each entry keeps legible contrast against the plan surface under `.dark` and\n * light via Tailwind `dark:` variants.\n */\nconst METHOD_PILL: Record<ApiEndpointMethod, string> = {\n GET: \"bg-emerald-100 text-emerald-700 dark:bg-emerald-500/15 dark:text-emerald-300\",\n POST: \"bg-blue-100 text-blue-700 dark:bg-blue-500/15 dark:text-blue-300\",\n PUT: \"bg-amber-100 text-amber-700 dark:bg-amber-500/15 dark:text-amber-300\",\n PATCH:\n \"bg-violet-100 text-violet-700 dark:bg-violet-500/15 dark:text-violet-300\",\n DELETE: \"bg-red-100 text-red-700 dark:bg-red-500/15 dark:text-red-300\",\n HEAD: \"bg-slate-200 text-slate-700 dark:bg-slate-500/20 dark:text-slate-300\",\n OPTIONS:\n \"bg-slate-200 text-slate-700 dark:bg-slate-500/20 dark:text-slate-300\",\n};\n\n/** Location-badge palette for the params table `in` column. */\nconst PARAM_IN_BADGE: Record<ApiParamLocation, string> = {\n path: \"bg-violet-100 text-violet-700 dark:bg-violet-500/15 dark:text-violet-300\",\n query: \"bg-blue-100 text-blue-700 dark:bg-blue-500/15 dark:text-blue-300\",\n header:\n \"bg-amber-100 text-amber-700 dark:bg-amber-500/15 dark:text-amber-300\",\n body: \"bg-emerald-100 text-emerald-700 dark:bg-emerald-500/15 dark:text-emerald-300\",\n};\n\n/** Status-pill palette keyed by the leading status digit (2xx/3xx/4xx/5xx). */\nfunction statusPillClass(status: string): string {\n const lead = status.trim().charAt(0);\n if (lead === \"2\")\n return \"bg-emerald-100 text-emerald-700 dark:bg-emerald-500/15 dark:text-emerald-300\";\n if (lead === \"4\")\n return \"bg-amber-100 text-amber-700 dark:bg-amber-500/15 dark:text-amber-300\";\n if (lead === \"5\")\n return \"bg-red-100 text-red-700 dark:bg-red-500/15 dark:text-red-300\";\n // 3xx and everything else → neutral slate.\n return \"bg-slate-200 text-slate-700 dark:bg-slate-500/20 dark:text-slate-300\";\n}\n\n/** Guess a fence language from a content type so examples highlight nicely. */\nfunction fenceLangForContentType(contentType?: string): string {\n const ct = (contentType ?? \"\").toLowerCase();\n if (ct.includes(\"json\")) return \"json\";\n if (ct.includes(\"xml\") || ct.includes(\"html\")) return \"html\";\n if (ct.includes(\"yaml\") || ct.includes(\"yml\")) return \"yaml\";\n return \"json\";\n}\n\n/** Wrap a raw example string in a fenced code block for `ctx.renderMarkdown`. */\nfunction fence(example: string, lang: string): string {\n // Never let the example's own content break out of the fence.\n const safe = example.replace(/```/g, \"ʼʼʼ\");\n return `\\`\\`\\`${lang}\\n${safe.trim()}\\n\\`\\`\\``;\n}\n\n/* ── Read (collapsed-by-default swagger row) ───────────────────────────────── */\n\n/**\n * Read-only renderer for an `api-endpoint` block. Collapsed by default: a single\n * row with a colored method pill, monospace path, muted summary, and a chevron.\n * Clicking the row expands the full reference (description, params table,\n * request body, responses) — the Swagger / Stripe house style. Every colored\n * element is theme-aware (`dark:` variants), so it reads correctly in both the\n * `.dark` plan theme and light mode.\n */\nexport function ApiEndpointRead({\n data,\n blockId,\n title,\n summary,\n ctx,\n}: BlockReadProps<ApiEndpointData>) {\n const [open, setOpen] = useState(false);\n\n const params = data.params ?? [];\n const responses = data.responses ?? [];\n const hasRequest = Boolean(\n data.request?.example || data.request?.contentType,\n );\n const hasBody =\n Boolean(data.description?.trim()) ||\n params.length > 0 ||\n hasRequest ||\n responses.length > 0 ||\n Boolean(data.auth);\n\n return (\n <section className=\"plan-block\" data-block-id={blockId}>\n {title && <div className=\"plan-block-label\">{title}</div>}\n <div className=\"overflow-hidden rounded-xl border border-plan-line bg-plan-block\">\n {/* Collapsed summary row — the whole row toggles. */}\n <button\n type=\"button\"\n data-plan-interactive\n aria-expanded={open}\n onClick={() => setOpen((value) => !value)}\n className={cn(\n \"flex w-full items-center gap-3 px-4 py-3 text-left transition-colors\",\n \"hover:bg-accent/40\",\n )}\n >\n <IconChevronRight\n className={cn(\n \"size-4 shrink-0 text-plan-muted transition-transform\",\n open && \"rotate-90\",\n )}\n />\n <span\n className={cn(\n \"shrink-0 rounded-md px-2 py-1 font-mono text-xs font-bold uppercase tracking-wide\",\n METHOD_PILL[data.method],\n )}\n >\n {data.method}\n </span>\n <span\n className={cn(\n \"min-w-0 truncate font-mono text-sm font-semibold text-plan-text\",\n data.deprecated && \"text-plan-muted line-through\",\n )}\n >\n {data.path}\n </span>\n {data.deprecated && (\n <DevBadge className=\"shrink-0 border-amber-500/40 text-amber-600 dark:text-amber-300\">\n Deprecated\n </DevBadge>\n )}\n {(data.summary || summary) && (\n <span className=\"ml-1 min-w-0 flex-1 truncate text-sm text-plan-muted\">\n {data.summary || summary}\n </span>\n )}\n {data.auth && (\n <IconLock\n className=\"size-3.5 shrink-0 text-plan-muted\"\n aria-label=\"Requires authentication\"\n />\n )}\n </button>\n\n {/* Expanded body. */}\n {open && hasBody && (\n <div className=\"border-t border-plan-line px-4 py-4\">\n {data.auth && (\n <div className=\"mb-4 flex items-center gap-2 text-xs text-plan-muted\">\n <IconLock className=\"size-3.5 shrink-0\" />\n <span>\n <span className=\"font-medium text-plan-text\">Auth:</span>{\" \"}\n {data.auth}\n </span>\n </div>\n )}\n\n {data.description?.trim() && (\n <div className=\"an-api-endpoint-desc\">\n {ctx.renderMarkdown?.(data.description)}\n </div>\n )}\n\n {params.length > 0 && (\n <div className=\"mt-5\">\n <div className=\"text-xs font-semibold uppercase tracking-wide text-plan-muted\">\n Parameters\n </div>\n <div className=\"mt-2 overflow-hidden rounded-lg border border-plan-line\">\n <table className=\"w-full border-collapse text-sm\">\n <thead>\n <tr className=\"bg-accent/30 text-left text-xs uppercase tracking-wide text-plan-muted\">\n <th className=\"px-3 py-2 font-medium\">Name</th>\n <th className=\"px-3 py-2 font-medium\">In</th>\n <th className=\"px-3 py-2 font-medium\">Type</th>\n <th className=\"px-3 py-2 font-medium\">Required</th>\n <th className=\"px-3 py-2 font-medium\">Description</th>\n </tr>\n </thead>\n <tbody>\n {params.map((param, index) => (\n <tr\n key={`${param.name}-${index}`}\n className=\"border-t border-plan-line align-top\"\n >\n <td className=\"px-3 py-2 font-mono text-xs font-semibold text-plan-text\">\n {param.name}\n </td>\n <td className=\"px-3 py-2\">\n <span\n className={cn(\n \"rounded px-1.5 py-0.5 font-mono text-[11px] font-semibold\",\n PARAM_IN_BADGE[param.in],\n )}\n >\n {param.in}\n </span>\n </td>\n <td className=\"px-3 py-2 font-mono text-xs text-plan-muted\">\n {param.type || \"—\"}\n </td>\n <td className=\"px-3 py-2 text-xs\">\n {param.required ? (\n <span className=\"font-medium text-red-600 dark:text-red-300\">\n required\n </span>\n ) : (\n <span className=\"text-plan-muted\">optional</span>\n )}\n </td>\n <td className=\"px-3 py-2 text-xs text-plan-muted\">\n {param.description || \"—\"}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </div>\n )}\n\n {hasRequest && (\n <div className=\"mt-5\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs font-semibold uppercase tracking-wide text-plan-muted\">\n Request body\n </span>\n {data.request?.contentType && (\n <span className=\"rounded bg-accent/40 px-1.5 py-0.5 font-mono text-[11px] text-plan-muted\">\n {data.request.contentType}\n </span>\n )}\n </div>\n {data.request?.example && (\n <div className=\"mt-2 an-api-endpoint-example\">\n {ctx.renderMarkdown?.(\n fence(\n data.request.example,\n fenceLangForContentType(data.request.contentType),\n ),\n )}\n </div>\n )}\n </div>\n )}\n\n {responses.length > 0 && (\n <div className=\"mt-5\">\n <div className=\"text-xs font-semibold uppercase tracking-wide text-plan-muted\">\n Responses\n </div>\n <div className=\"mt-2 flex flex-col gap-3\">\n {responses.map((response, index) => (\n <div\n key={`${response.status}-${index}`}\n className=\"rounded-lg border border-plan-line\"\n >\n <div className=\"flex items-center gap-2 px-3 py-2\">\n <span\n className={cn(\n \"rounded px-2 py-0.5 font-mono text-xs font-bold\",\n statusPillClass(response.status),\n )}\n >\n {response.status}\n </span>\n {response.description && (\n <span className=\"text-sm text-plan-muted\">\n {response.description}\n </span>\n )}\n </div>\n {response.example && (\n <div className=\"border-t border-plan-line px-3 pb-3 pt-1 an-api-endpoint-example\">\n {ctx.renderMarkdown?.(\n fence(response.example, \"json\"),\n )}\n </div>\n )}\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n )}\n </div>\n </section>\n );\n}\n\n/* ── Edit (panel form) ─────────────────────────────────────────────────────── */\n\nconst fieldLabelClass = \"text-xs font-medium text-muted-foreground\";\n\n/**\n * Panel editor for an `api-endpoint` block. A property form: method (Select),\n * path/summary/auth (Input), description (Textarea), deprecated (Switch), plus\n * repeatable rows for params and responses (add/remove) and a request-body\n * textarea. Renders BARE content (no `<section>`); the registry's panel surface\n * supplies the popover chrome.\n */\nexport function ApiEndpointEdit({\n data,\n onChange,\n editable,\n}: BlockEditProps<ApiEndpointData>) {\n const params = data.params ?? [];\n const responses = data.responses ?? [];\n\n const patch = (next: Partial<ApiEndpointData>) =>\n onChange({ ...data, ...next });\n\n const updateParam = (index: number, next: Partial<ApiEndpointParam>) =>\n patch({\n params: params.map((param, i) =>\n i === index ? { ...param, ...next } : param,\n ),\n });\n\n const removeParam = (index: number) =>\n patch({ params: params.filter((_, i) => i !== index) });\n\n const addParam = () =>\n patch({\n params: [...params, { name: \"param\", in: \"query\" as ApiParamLocation }],\n });\n\n const updateResponse = (index: number, next: Partial<ApiEndpointResponse>) =>\n patch({\n responses: responses.map((response, i) =>\n i === index ? { ...response, ...next } : response,\n ),\n });\n\n const removeResponse = (index: number) =>\n patch({ responses: responses.filter((_, i) => i !== index) });\n\n const addResponse = () =>\n patch({ responses: [...responses, { status: \"200\" }] });\n\n const updateRequest = (next: Partial<ApiEndpointData[\"request\"]>) => {\n const merged = { ...(data.request ?? {}), ...next };\n const empty = !merged.contentType && !merged.example;\n patch({ request: empty ? undefined : merged });\n };\n\n return (\n <div className=\"flex flex-col gap-4\" data-plan-interactive>\n <div className=\"grid grid-cols-[120px_minmax(0,1fr)] gap-2\">\n <label className=\"flex flex-col gap-1.5\">\n <span className={fieldLabelClass}>Method</span>\n <DevSelect\n className=\"h-9\"\n value={data.method}\n disabled={!editable}\n onValueChange={(value) =>\n patch({ method: value as ApiEndpointMethod })\n }\n options={API_ENDPOINT_METHODS.map((method) => ({\n value: method,\n label: method,\n }))}\n />\n </label>\n <label className=\"flex flex-col gap-1.5\">\n <span className={fieldLabelClass}>Path</span>\n <DevInput\n className=\"h-9 font-mono\"\n value={data.path}\n disabled={!editable}\n placeholder=\"/api/resource\"\n onChange={(event) => patch({ path: event.target.value })}\n />\n </label>\n </div>\n\n <label className=\"flex flex-col gap-1.5\">\n <span className={fieldLabelClass}>Summary</span>\n <DevInput\n className=\"h-9\"\n value={data.summary ?? \"\"}\n disabled={!editable}\n placeholder=\"Short one-line description\"\n onChange={(event) =>\n patch({ summary: event.target.value || undefined })\n }\n />\n </label>\n\n <label className=\"flex flex-col gap-1.5\">\n <span className={fieldLabelClass}>Description (markdown)</span>\n <DevTextarea\n className=\"min-h-[80px]\"\n value={data.description ?? \"\"}\n disabled={!editable}\n placeholder=\"Longer description, rendered as markdown\"\n onChange={(event) =>\n patch({ description: event.target.value || undefined })\n }\n />\n </label>\n\n <div className=\"grid grid-cols-[minmax(0,1fr)_auto] items-end gap-3\">\n <label className=\"flex flex-col gap-1.5\">\n <span className={fieldLabelClass}>Auth</span>\n <DevInput\n className=\"h-9\"\n value={data.auth ?? \"\"}\n disabled={!editable}\n placeholder=\"e.g. Bearer token\"\n onChange={(event) =>\n patch({ auth: event.target.value || undefined })\n }\n />\n </label>\n <label className=\"flex items-center gap-2 pb-2\">\n <DevSwitch\n checked={Boolean(data.deprecated)}\n disabled={!editable}\n onCheckedChange={(checked) =>\n patch({ deprecated: checked || undefined })\n }\n />\n <span className={fieldLabelClass}>Deprecated</span>\n </label>\n </div>\n\n {/* Params */}\n <div className=\"flex flex-col gap-2\">\n <div className=\"flex items-center justify-between\">\n <span className={fieldLabelClass}>Parameters</span>\n {editable && (\n <button\n type=\"button\"\n data-plan-interactive\n className=\"flex items-center gap-1 rounded-md px-2 py-1 text-xs text-muted-foreground hover:bg-accent/60 hover:text-foreground\"\n onClick={addParam}\n >\n <IconPlus className=\"size-3.5\" />\n Add\n </button>\n )}\n </div>\n {params.map((param, index) => (\n <div\n key={index}\n className=\"flex flex-col gap-2 rounded-md border border-input p-2\"\n >\n <div className=\"grid grid-cols-[minmax(0,1fr)_96px_auto] gap-2\">\n <DevInput\n className=\"h-8 font-mono text-xs\"\n value={param.name}\n disabled={!editable}\n placeholder=\"name\"\n onChange={(event) =>\n updateParam(index, { name: event.target.value })\n }\n />\n <DevSelect\n className=\"h-8\"\n value={param.in}\n disabled={!editable}\n onValueChange={(value) =>\n updateParam(index, { in: value as ApiParamLocation })\n }\n options={API_PARAM_LOCATIONS.map((location) => ({\n value: location,\n label: location,\n }))}\n />\n {editable && (\n <button\n type=\"button\"\n data-plan-interactive\n aria-label=\"Remove parameter\"\n className=\"flex size-8 items-center justify-center rounded-md text-muted-foreground hover:bg-accent/60 hover:text-foreground\"\n onClick={() => removeParam(index)}\n >\n <IconTrash className=\"size-4\" />\n </button>\n )}\n </div>\n <div className=\"grid grid-cols-[minmax(0,1fr)_auto] items-center gap-2\">\n <DevInput\n className=\"h-8 font-mono text-xs\"\n value={param.type ?? \"\"}\n disabled={!editable}\n placeholder=\"type (e.g. string)\"\n onChange={(event) =>\n updateParam(index, { type: event.target.value || undefined })\n }\n />\n <label className=\"flex items-center gap-1.5 whitespace-nowrap text-xs text-muted-foreground\">\n <input\n type=\"checkbox\"\n className=\"size-3.5 cursor-pointer accent-primary\"\n checked={Boolean(param.required)}\n disabled={!editable}\n onChange={(event) =>\n updateParam(index, {\n required: event.target.checked || undefined,\n })\n }\n />\n Required\n </label>\n </div>\n <DevInput\n className=\"h-8 text-xs\"\n value={param.description ?? \"\"}\n disabled={!editable}\n placeholder=\"description\"\n onChange={(event) =>\n updateParam(index, {\n description: event.target.value || undefined,\n })\n }\n />\n </div>\n ))}\n </div>\n\n {/* Request body */}\n <div className=\"flex flex-col gap-2\">\n <span className={fieldLabelClass}>Request body</span>\n <DevInput\n className=\"h-8 font-mono text-xs\"\n value={data.request?.contentType ?? \"\"}\n disabled={!editable}\n placeholder=\"content type (e.g. application/json)\"\n onChange={(event) =>\n updateRequest({ contentType: event.target.value || undefined })\n }\n />\n <DevTextarea\n className=\"min-h-[80px] font-mono text-xs\"\n value={data.request?.example ?? \"\"}\n disabled={!editable}\n placeholder='{ \"example\": \"request body\" }'\n onChange={(event) =>\n updateRequest({ example: event.target.value || undefined })\n }\n />\n </div>\n\n {/* Responses */}\n <div className=\"flex flex-col gap-2\">\n <div className=\"flex items-center justify-between\">\n <span className={fieldLabelClass}>Responses</span>\n {editable && (\n <button\n type=\"button\"\n data-plan-interactive\n className=\"flex items-center gap-1 rounded-md px-2 py-1 text-xs text-muted-foreground hover:bg-accent/60 hover:text-foreground\"\n onClick={addResponse}\n >\n <IconPlus className=\"size-3.5\" />\n Add\n </button>\n )}\n </div>\n {responses.map((response, index) => (\n <div\n key={index}\n className=\"flex flex-col gap-2 rounded-md border border-input p-2\"\n >\n <div className=\"grid grid-cols-[96px_minmax(0,1fr)_auto] gap-2\">\n <DevInput\n className=\"h-8 font-mono text-xs\"\n value={response.status}\n disabled={!editable}\n placeholder=\"200\"\n onChange={(event) =>\n updateResponse(index, { status: event.target.value })\n }\n />\n <DevInput\n className=\"h-8 text-xs\"\n value={response.description ?? \"\"}\n disabled={!editable}\n placeholder=\"description\"\n onChange={(event) =>\n updateResponse(index, {\n description: event.target.value || undefined,\n })\n }\n />\n {editable && (\n <button\n type=\"button\"\n data-plan-interactive\n aria-label=\"Remove response\"\n className=\"flex size-8 items-center justify-center rounded-md text-muted-foreground hover:bg-accent/60 hover:text-foreground\"\n onClick={() => removeResponse(index)}\n >\n <IconTrash className=\"size-4\" />\n </button>\n )}\n </div>\n <DevTextarea\n className=\"min-h-[64px] font-mono text-xs\"\n value={response.example ?? \"\"}\n disabled={!editable}\n placeholder='{ \"example\": \"response body\" }'\n onChange={(event) =>\n updateResponse(index, {\n example: event.target.value || undefined,\n })\n }\n />\n </div>\n ))}\n </div>\n </div>\n );\n}\n"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { BlockEditProps, BlockReadProps } from "../types.js";
|
|
2
|
+
import type { DataModelData } from "./data-model.config.js";
|
|
3
|
+
/**
|
|
4
|
+
* Read-only renderer for a `data-model` block — a dbdiagram / Prisma-style
|
|
5
|
+
* entity-relationship diagram. Each entity is a collapsible card: the header
|
|
6
|
+
* shows the entity name + field count, and expanding it reveals a compact field
|
|
7
|
+
* table (Field · Type · flags) with PK / FK / nullable indicators.
|
|
8
|
+
*
|
|
9
|
+
* INTERACTIVITY (the reason this is a custom block, not a plain table): hovering
|
|
10
|
+
* or clicking a foreign-key field highlights the referenced entity card — it
|
|
11
|
+
* scrolls into view, expands, and gets a temporary accent ring — so a reader can
|
|
12
|
+
* trace a relationship across the whole model. Explicit `relations` (or relations
|
|
13
|
+
* inferred from `fk` fields) render as a labeled connector list below the cards.
|
|
14
|
+
*
|
|
15
|
+
* Every color is theme-aware via Tailwind `dark:` variants or plan CSS vars, so
|
|
16
|
+
* the diagram reads correctly in both the `.dark` plan theme and light mode.
|
|
17
|
+
*/
|
|
18
|
+
export declare function DataModelRead({ data, blockId, title, summary, }: BlockReadProps<DataModelData>): import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
/**
|
|
20
|
+
* Panel editor for a `data-model` block. A structured form: a list of entities
|
|
21
|
+
* (add/remove), each with a name Input, an optional note, and repeatable field
|
|
22
|
+
* rows (add/remove) carrying name / type / PK checkbox / FK input / nullable
|
|
23
|
+
* checkbox. Relations are derived from `fk` in v1, so the form focuses on the
|
|
24
|
+
* entities + fields. Renders BARE content (no `<section>`); the registry's panel
|
|
25
|
+
* surface supplies the popover chrome.
|
|
26
|
+
*/
|
|
27
|
+
export declare function DataModelEdit({ data, onChange, editable, }: BlockEditProps<DataModelData>): import("react/jsx-runtime").JSX.Element;
|
|
28
|
+
//# sourceMappingURL=DataModelBlock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DataModelBlock.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/DataModelBlock.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,KAAK,EACV,aAAa,EAKd,MAAM,wBAAwB,CAAC;AA+EhC;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,OAAO,EACP,KAAK,EACL,OAAO,GACR,EAAE,cAAc,CAAC,aAAa,CAAC,2CA6Q/B;AAWD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,QAAQ,EACR,QAAQ,GACT,EAAE,cAAc,CAAC,aAAa,CAAC,2CAwM/B"}
|