@frontlit/text-editor 0.1.0-alpha.1 → 0.1.0-alpha.3

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.
@@ -0,0 +1,356 @@
1
+ // src/text-renderer.tsx
2
+ import { renderToReactElement } from "@tiptap/static-renderer";
3
+ import { getSchema } from "@tiptap/core";
4
+
5
+ // src/extract-text-from-node.ts
6
+ function extractTextFromNode(node) {
7
+ if (!node) {
8
+ return "";
9
+ }
10
+ if (typeof node.textContent === "string") {
11
+ return node.textContent;
12
+ }
13
+ if (node.content && node.content.content && Array.isArray(node.content.content)) {
14
+ return node.content.content.map((child) => {
15
+ if (child.text) {
16
+ return child.text;
17
+ }
18
+ return extractTextFromNode(child);
19
+ }).join("");
20
+ }
21
+ if (Array.isArray(node.content)) {
22
+ return node.content.map((child) => {
23
+ if (child.text) {
24
+ return child.text;
25
+ }
26
+ return extractTextFromNode(child);
27
+ }).join("");
28
+ }
29
+ return "";
30
+ }
31
+
32
+ // src/empty-doc.ts
33
+ var emptyDoc = {
34
+ type: "doc",
35
+ content: []
36
+ };
37
+ var empty_doc_default = emptyDoc;
38
+
39
+ // src/extensions.ts
40
+ import StarterKit from "@tiptap/starter-kit";
41
+ import Placeholder from "@tiptap/extension-placeholder";
42
+ import Link from "@tiptap/extension-link";
43
+ import { Table, TableCell, TableHeader, TableRow } from "@tiptap/extension-table";
44
+ import Image from "@tiptap/extension-image";
45
+ import Dropcursor from "@tiptap/extension-dropcursor";
46
+ import Gapcursor from "@tiptap/extension-gapcursor";
47
+ import CodeBlockLowlight from "@tiptap/extension-code-block-lowlight";
48
+ import Heading from "@tiptap/extension-heading";
49
+ import Highlight from "@tiptap/extension-highlight";
50
+ import { lowlight } from "lowlight";
51
+ import javascript from "highlight.js/lib/languages/javascript";
52
+ import typescript from "highlight.js/lib/languages/typescript";
53
+ import json from "highlight.js/lib/languages/json";
54
+ import css from "highlight.js/lib/languages/css";
55
+ import xml from "highlight.js/lib/languages/xml";
56
+
57
+ // src/create-id.ts
58
+ function createId(textContent) {
59
+ return textContent.replace(/\s/g, "-").toLowerCase();
60
+ }
61
+
62
+ // src/components/custom-code-mirror.tsx
63
+ import { Node } from "@tiptap/core";
64
+ var CodeMirrorNode = Node.create({
65
+ name: "codeMirror",
66
+ content: "block+",
67
+ code: true,
68
+ addOptions() {
69
+ return {
70
+ HTMLAttributes: {}
71
+ };
72
+ },
73
+ parseHTML() {
74
+ return [
75
+ {
76
+ tag: "pre"
77
+ }
78
+ ];
79
+ },
80
+ renderHTML({ HTMLAttributes }) {
81
+ return ["pre", HTMLAttributes, 0];
82
+ }
83
+ });
84
+
85
+ // src/extensions.ts
86
+ lowlight.registerLanguage("javascript", javascript);
87
+ lowlight.registerLanguage("typescript", typescript);
88
+ lowlight.registerLanguage("json", json);
89
+ lowlight.registerLanguage("css", css);
90
+ lowlight.registerLanguage("html", xml);
91
+ var createExtensions = ({
92
+ placeholder
93
+ } = {}) => [
94
+ StarterKit.configure({
95
+ codeBlock: false,
96
+ // Configured/replaced explicitly below (custom options or a
97
+ // `.extend()`) — disabled here to avoid tiptap's duplicate-extension
98
+ // warning and constructing throwaway instances of each.
99
+ link: false,
100
+ dropcursor: false,
101
+ gapcursor: false,
102
+ heading: false
103
+ }),
104
+ Placeholder.configure({
105
+ placeholder: placeholder || "Write something\u2026"
106
+ }),
107
+ Link.configure({
108
+ openOnClick: false,
109
+ autolink: true,
110
+ linkOnPaste: true
111
+ }),
112
+ Table.configure({
113
+ resizable: true
114
+ }),
115
+ TableRow,
116
+ TableHeader,
117
+ TableCell,
118
+ Image.configure({
119
+ // We embed uploaded images as data URLs (no backend/media library in
120
+ // this package), so base64 sources must be allowed.
121
+ allowBase64: true,
122
+ HTMLAttributes: {
123
+ class: "max-w-full h-auto rounded-md"
124
+ },
125
+ resize: {
126
+ enabled: true,
127
+ directions: ["top", "bottom", "left", "right"],
128
+ alwaysPreserveAspectRatio: true
129
+ }
130
+ }),
131
+ Dropcursor.configure({
132
+ color: "hsl(var(--foreground))",
133
+ width: 2
134
+ }),
135
+ Gapcursor,
136
+ CodeBlockLowlight.configure({
137
+ lowlight
138
+ }),
139
+ Heading.extend({
140
+ renderHTML({ node, HTMLAttributes }) {
141
+ const level = this.options.levels.includes(node.attrs.level) ? node.attrs.level : this.options.levels[0];
142
+ const id = typeof node.textContent === "string" ? createId(node.textContent) : void 0;
143
+ return [
144
+ `h${level}`,
145
+ {
146
+ ...HTMLAttributes,
147
+ id
148
+ },
149
+ 0
150
+ ];
151
+ }
152
+ }),
153
+ Highlight,
154
+ CodeMirrorNode
155
+ ];
156
+
157
+ // src/lib/cn.ts
158
+ import { clsx } from "clsx";
159
+ import { twMerge } from "tailwind-merge";
160
+ function cn(...inputs) {
161
+ return twMerge(clsx(inputs));
162
+ }
163
+
164
+ // src/components/rendered-code-block.tsx
165
+ import React from "react";
166
+ import { CopyCodeButton } from "./islands/copy-code-button.js";
167
+ import { jsx, jsxs } from "react/jsx-runtime";
168
+ function textFromChildren(node) {
169
+ if (node == null || typeof node === "boolean") return "";
170
+ if (typeof node === "string" || typeof node === "number") {
171
+ return String(node);
172
+ }
173
+ if (Array.isArray(node)) return node.map(textFromChildren).join("");
174
+ if (React.isValidElement(node)) {
175
+ return textFromChildren(node.props.children);
176
+ }
177
+ return "";
178
+ }
179
+ function RenderedCodeBlock({
180
+ node,
181
+ children,
182
+ language
183
+ }) {
184
+ const source = extractTextFromNode(node) || textFromChildren(children);
185
+ return /* @__PURE__ */ jsxs("div", { className: "te-code-block", children: [
186
+ /* @__PURE__ */ jsx("pre", { children: /* @__PURE__ */ jsx("code", { className: language ? `language-${language}` : void 0, children }) }),
187
+ /* @__PURE__ */ jsx(CopyCodeButton, { source })
188
+ ] });
189
+ }
190
+
191
+ // src/docs/headings.ts
192
+ function docsNodeText(node) {
193
+ if (typeof node.text === "string") return node.text;
194
+ if (node.type === "hardBreak") return "\n";
195
+ return (node.content ?? []).map(docsNodeText).join("");
196
+ }
197
+ function createHeadingIdGenerator() {
198
+ const used = /* @__PURE__ */ new Set();
199
+ return (text) => {
200
+ const base = text.normalize("NFKC").toLowerCase().trim().replace(/[^\p{L}\p{N}\s_-]/gu, "").replace(/\s+/g, "-") || "section";
201
+ let id = base;
202
+ let suffix = 2;
203
+ while (used.has(id)) id = `${base}-${suffix++}`;
204
+ used.add(id);
205
+ return id;
206
+ };
207
+ }
208
+ function extractDocsHeadings(json2) {
209
+ const nextId = createHeadingIdGenerator();
210
+ const headings = [];
211
+ function walk(node) {
212
+ if (node.type === "heading") {
213
+ const text = docsNodeText(node);
214
+ headings.push({ level: Number(node.attrs?.level ?? 1), text, id: nextId(text) });
215
+ }
216
+ for (const child of node.content ?? []) walk(child);
217
+ }
218
+ if (json2) walk(json2);
219
+ return headings;
220
+ }
221
+
222
+ // src/text-renderer.tsx
223
+ import { jsx as jsx2 } from "react/jsx-runtime";
224
+ var typographyClasses = (typography) => [
225
+ typography?.fontFamily,
226
+ typography?.fontSize,
227
+ typography?.fontWeight,
228
+ typography?.lineHeight,
229
+ typography?.letterSpacing,
230
+ typography?.textTransform,
231
+ typography?.textDecoration,
232
+ typography?.textOverflow
233
+ ];
234
+ function removeEmptyTextNodes(node) {
235
+ if (!node || typeof node !== "object") {
236
+ return node;
237
+ }
238
+ if (node.type === "text" && node.text === "") {
239
+ return void 0;
240
+ }
241
+ if (!Array.isArray(node.content)) {
242
+ return node;
243
+ }
244
+ const content = node.content.map(removeEmptyTextNodes).filter((child) => child !== void 0);
245
+ return {
246
+ ...node,
247
+ content
248
+ };
249
+ }
250
+ function TextRenderer({
251
+ json: json2,
252
+ className,
253
+ theme,
254
+ profile = "default"
255
+ }) {
256
+ const extensions = createExtensions();
257
+ const content = removeEmptyTextNodes(
258
+ json2 ?? empty_doc_default
259
+ );
260
+ const docsHeadings = profile === "documentation" ? extractDocsHeadings(content) : [];
261
+ const renderContent = profile === "documentation" ? getSchema(extensions).nodeFromJSON(content) : content;
262
+ const headingIds = /* @__PURE__ */ new WeakMap();
263
+ if (profile === "documentation") {
264
+ let headingIndex = 0;
265
+ renderContent.descendants((node) => {
266
+ if (node.type.name === "heading") {
267
+ headingIds.set(node, docsHeadings[headingIndex++].id);
268
+ }
269
+ });
270
+ }
271
+ const rendered = renderToReactElement({
272
+ extensions,
273
+ content: renderContent,
274
+ options: {
275
+ nodeMapping: {
276
+ paragraph: ({ children }) => {
277
+ const classes = cn(
278
+ "text-base",
279
+ ...typographyClasses(theme?.typography?.text1)
280
+ );
281
+ return /* @__PURE__ */ jsx2("p", { className: classes, children });
282
+ },
283
+ hardBreak: () => /* @__PURE__ */ jsx2("br", {}),
284
+ heading: ({ node, children }) => {
285
+ const level = node?.attrs?.level ?? 1;
286
+ const textContent = extractTextFromNode(node);
287
+ const id = profile === "documentation" ? headingIds.get(node) : createId(textContent);
288
+ if (!theme) {
289
+ const Tag2 = `h${level}`;
290
+ return /* @__PURE__ */ jsx2(Tag2, { id, children });
291
+ }
292
+ if (level === 1) {
293
+ const classes2 = cn(
294
+ "scroll-m-20 text-4xl font-bold tracking-tight",
295
+ ...typographyClasses(theme?.typography?.header1)
296
+ );
297
+ return /* @__PURE__ */ jsx2("h1", { id, className: classes2, children });
298
+ }
299
+ if (level === 2) {
300
+ const classes2 = cn(
301
+ "scroll-m-20 text-3xl font-bold tracking-tight",
302
+ ...typographyClasses(theme?.typography?.header2)
303
+ );
304
+ return /* @__PURE__ */ jsx2("h2", { id, className: classes2, children });
305
+ }
306
+ const classes = cn(
307
+ "scroll-m-20 text-2xl font-semibold tracking-normal",
308
+ ...typographyClasses(
309
+ profile === "documentation" && level >= 4 ? theme?.typography?.header4 ?? theme?.typography?.header3 : theme?.typography?.header3
310
+ )
311
+ );
312
+ const Tag = profile === "documentation" ? `h${level}` : "h3";
313
+ return /* @__PURE__ */ jsx2(Tag, { id, className: classes, children });
314
+ },
315
+ codeBlock: ({ node, children }) => /* @__PURE__ */ jsx2(RenderedCodeBlock, { node, language: node?.attrs?.language, children }),
316
+ codeMirror: ({ node, children }) => /* @__PURE__ */ jsx2(RenderedCodeBlock, { node, children })
317
+ },
318
+ markMapping: {
319
+ link: ({ mark, children }) => {
320
+ const href = mark?.attrs?.href;
321
+ const linkStyle = theme?.interactives?.link;
322
+ const classes = cn(
323
+ "font-medium cursor-pointer",
324
+ ...typographyClasses(theme?.typography?.link),
325
+ linkStyle?.padding?.x,
326
+ linkStyle?.padding?.y,
327
+ linkStyle?.border?.width,
328
+ linkStyle?.border?.radius,
329
+ linkStyle?.border?.style,
330
+ linkStyle?.custom
331
+ );
332
+ return /* @__PURE__ */ jsx2(
333
+ "a",
334
+ {
335
+ href,
336
+ target: "_blank",
337
+ rel: "noopener noreferrer",
338
+ className: classes,
339
+ style: {
340
+ textShadow: linkStyle?.textShadow || void 0
341
+ },
342
+ children
343
+ }
344
+ );
345
+ },
346
+ highlight: ({ children }) => /* @__PURE__ */ jsx2("mark", { className: "bg-accent text-accent-foreground", children })
347
+ }
348
+ }
349
+ });
350
+ const combinedClassName = ["tiptap-renderer", className].filter(Boolean).join(" ");
351
+ return /* @__PURE__ */ jsx2("div", { className: "text-editor flex flex-col gap-4", children: /* @__PURE__ */ jsx2("div", { className: combinedClassName, children: rendered }) });
352
+ }
353
+ export {
354
+ TextRenderer
355
+ };
356
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/text-renderer.tsx","../../src/extract-text-from-node.ts","../../src/empty-doc.ts","../../src/extensions.ts","../../src/create-id.ts","../../src/components/custom-code-mirror.tsx","../../src/lib/cn.ts","../../src/components/rendered-code-block.tsx","../../src/docs/headings.ts"],"sourcesContent":["import React from \"react\";\nimport { renderToReactElement } from \"@tiptap/static-renderer\";\nimport { getSchema } from \"@tiptap/core\";\nimport type { Node as ProseMirrorNode } from \"@tiptap/pm/model\";\nimport { extractTextFromNode } from \"./extract-text-from-node\";\nimport emptyDoc from \"./empty-doc\";\nimport { createExtensions } from \"./extensions\";\nimport { createId } from \"./create-id\";\nimport type { TextEditorContent } from \"./text-editor-content\";\nimport type { TextRendererTheme } from \"./renderer-theme\";\nimport { cn } from \"./lib/cn\";\nimport { RenderedCodeBlock } from \"./components/rendered-code-block\";\nimport { extractDocsHeadings } from \"./docs/headings\";\n\ninterface TextRendererProps {\n profile?: \"default\" | \"documentation\";\n json: TextEditorContent;\n className?: string;\n theme?: TextRendererTheme;\n}\n\nconst typographyClasses = (typography?: {\n fontFamily?: string;\n fontSize?: string;\n fontWeight?: string;\n lineHeight?: string;\n letterSpacing?: string;\n textTransform?: string;\n textDecoration?: string;\n textOverflow?: string;\n}) => [\n typography?.fontFamily,\n typography?.fontSize,\n typography?.fontWeight,\n typography?.lineHeight,\n typography?.letterSpacing,\n typography?.textTransform,\n typography?.textDecoration,\n typography?.textOverflow,\n];\n\nfunction removeEmptyTextNodes(node: any): any {\n if (!node || typeof node !== \"object\") {\n return node;\n }\n\n if (node.type === \"text\" && node.text === \"\") {\n return undefined;\n }\n\n if (!Array.isArray(node.content)) {\n return node;\n }\n\n const content = node.content\n .map(removeEmptyTextNodes)\n .filter((child: any) => child !== undefined);\n\n return {\n ...node,\n content,\n };\n}\n\n/**\n * Renders TipTap `TextEditorContent` JSON as static React elements — used to\n * display rich text saved by the `Editor` (e.g. inside a block's body). When\n * a `theme` is provided, headings/paragraphs/links pick up its typography;\n * otherwise plain semantic HTML tags are used.\n */\nexport function TextRenderer({\n json,\n className,\n theme,\n profile = \"default\",\n}: TextRendererProps) {\n const extensions = createExtensions();\n const content = removeEmptyTextNodes(\n (json as any) ?? (emptyDoc as any),\n ) as TextEditorContent;\n const docsHeadings = profile === \"documentation\" ? extractDocsHeadings(content) : [];\n // React can replay each mapped node (StrictMode, hydration, or a retry).\n // Bind IDs before rendering instead of consuming an iterator in a component.\n const renderContent =\n profile === \"documentation\" ? getSchema(extensions).nodeFromJSON(content) : content;\n const headingIds = new WeakMap<ProseMirrorNode, string>();\n if (profile === \"documentation\") {\n let headingIndex = 0;\n (renderContent as ProseMirrorNode).descendants((node) => {\n if (node.type.name === \"heading\") {\n headingIds.set(node, docsHeadings[headingIndex++].id);\n }\n });\n }\n\n const rendered = renderToReactElement({\n extensions,\n content: renderContent,\n options: {\n nodeMapping: {\n paragraph: ({ children }) => {\n const classes = cn(\n \"text-base\",\n ...typographyClasses(theme?.typography?.text1),\n );\n return <p className={classes}>{children}</p>;\n },\n hardBreak: () => <br />,\n heading: ({ node, children }) => {\n const level = node?.attrs?.level ?? 1;\n // Extract text from the node structure (same as extractHeadings does)\n const textContent = extractTextFromNode(node);\n const id =\n profile === \"documentation\" ? headingIds.get(node) : createId(textContent);\n\n if (!theme) {\n const Tag = `h${level}` as React.ElementType;\n return <Tag id={id}>{children}</Tag>;\n }\n\n if (level === 1) {\n const classes = cn(\n \"scroll-m-20 text-4xl font-bold tracking-tight\",\n ...typographyClasses(theme?.typography?.header1),\n );\n return (\n <h1 id={id} className={classes}>\n {children}\n </h1>\n );\n }\n if (level === 2) {\n const classes = cn(\n \"scroll-m-20 text-3xl font-bold tracking-tight\",\n ...typographyClasses(theme?.typography?.header2),\n );\n return (\n <h2 id={id} className={classes}>\n {children}\n </h2>\n );\n }\n const classes = cn(\n \"scroll-m-20 text-2xl font-semibold tracking-normal\",\n ...typographyClasses(\n profile === \"documentation\" && level >= 4\n ? (theme?.typography?.header4 ?? theme?.typography?.header3)\n : theme?.typography?.header3,\n ),\n );\n const Tag = (\n profile === \"documentation\" ? `h${level}` : \"h3\"\n ) as React.ElementType;\n return (\n <Tag id={id} className={classes}>\n {children}\n </Tag>\n );\n },\n codeBlock: ({ node, children }) => (\n <RenderedCodeBlock node={node} language={node?.attrs?.language}>\n {children}\n </RenderedCodeBlock>\n ),\n codeMirror: ({ node, children }) => (\n <RenderedCodeBlock node={node}>{children}</RenderedCodeBlock>\n ),\n },\n markMapping: {\n link: ({ mark, children }) => {\n const href = mark?.attrs?.href;\n const linkStyle = theme?.interactives?.link;\n const classes = cn(\n \"font-medium cursor-pointer\",\n ...typographyClasses(theme?.typography?.link),\n linkStyle?.padding?.x,\n linkStyle?.padding?.y,\n linkStyle?.border?.width,\n linkStyle?.border?.radius,\n linkStyle?.border?.style,\n linkStyle?.custom,\n );\n return (\n <a\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={classes}\n style={{\n textShadow: linkStyle?.textShadow || undefined,\n }}\n >\n {children}\n </a>\n );\n },\n highlight: ({ children }) => (\n <mark className=\"bg-accent text-accent-foreground\">{children}</mark>\n ),\n },\n },\n });\n\n const combinedClassName = [\"tiptap-renderer\", className].filter(Boolean).join(\" \");\n\n return (\n <div className=\"text-editor flex flex-col gap-4\">\n <div className={combinedClassName}>{rendered}</div>\n </div>\n );\n}\n\nexport default TextRenderer;\n","export function extractTextFromNode(node): string {\n if (!node) {\n return \"\";\n }\n\n // ProseMirror nodes have a textContent property that contains all text\n if (typeof node.textContent === \"string\") {\n return node.textContent;\n }\n\n // Fallback: if content is a Fragment, access its content array\n if (node.content && node.content.content && Array.isArray(node.content.content)) {\n return node.content.content\n .map((child: any) => {\n // Text nodes have a text property\n if (child.text) {\n return child.text;\n }\n // Recursively extract from child nodes\n return extractTextFromNode(child);\n })\n .join(\"\");\n }\n\n // Fallback: if content is directly an array\n if (Array.isArray(node.content)) {\n return node.content\n .map((child: any) => {\n if (child.text) {\n return child.text;\n }\n return extractTextFromNode(child);\n })\n .join(\"\");\n }\n\n return \"\";\n}\n","import type { TextEditorContent } from \"./text-editor-content\";\n\nconst emptyDoc: TextEditorContent = {\n type: \"doc\",\n content: [],\n};\n\nexport default emptyDoc;\n","import StarterKit from \"@tiptap/starter-kit\";\nimport Placeholder from \"@tiptap/extension-placeholder\";\nimport Link from \"@tiptap/extension-link\";\nimport { Table, TableCell, TableHeader, TableRow } from \"@tiptap/extension-table\";\nimport Image from \"@tiptap/extension-image\";\nimport Dropcursor from \"@tiptap/extension-dropcursor\";\nimport Gapcursor from \"@tiptap/extension-gapcursor\";\nimport CodeBlockLowlight from \"@tiptap/extension-code-block-lowlight\";\nimport Heading from \"@tiptap/extension-heading\";\nimport Highlight from \"@tiptap/extension-highlight\";\nimport { lowlight } from \"lowlight\";\nimport javascript from \"highlight.js/lib/languages/javascript\";\nimport typescript from \"highlight.js/lib/languages/typescript\";\nimport json from \"highlight.js/lib/languages/json\";\nimport css from \"highlight.js/lib/languages/css\";\nimport xml from \"highlight.js/lib/languages/xml\";\nimport type { Extensions } from \"@tiptap/core\";\nimport { createId } from \"./create-id\";\nimport { CodeMirrorNode } from \"./components/custom-code-mirror\";\n\nlowlight.registerLanguage(\"javascript\", javascript);\nlowlight.registerLanguage(\"typescript\", typescript);\nlowlight.registerLanguage(\"json\", json);\nlowlight.registerLanguage(\"css\", css);\nlowlight.registerLanguage(\"html\", xml);\n\ninterface ExtensionOptions {\n placeholder?: string;\n}\n\nexport const createExtensions = ({\n placeholder,\n}: ExtensionOptions = {}): Extensions => [\n StarterKit.configure({\n codeBlock: false,\n // Configured/replaced explicitly below (custom options or a\n // `.extend()`) — disabled here to avoid tiptap's duplicate-extension\n // warning and constructing throwaway instances of each.\n link: false,\n dropcursor: false,\n gapcursor: false,\n heading: false,\n }),\n Placeholder.configure({\n placeholder: placeholder || \"Write something…\",\n }),\n Link.configure({\n openOnClick: false,\n autolink: true,\n linkOnPaste: true,\n }),\n Table.configure({\n resizable: true,\n }),\n TableRow,\n TableHeader,\n TableCell,\n Image.configure({\n // We embed uploaded images as data URLs (no backend/media library in\n // this package), so base64 sources must be allowed.\n allowBase64: true,\n HTMLAttributes: {\n class: \"max-w-full h-auto rounded-md\",\n },\n resize: {\n enabled: true,\n directions: [\"top\", \"bottom\", \"left\", \"right\"],\n alwaysPreserveAspectRatio: true,\n },\n }),\n Dropcursor.configure({\n color: \"hsl(var(--foreground))\",\n width: 2,\n }),\n Gapcursor,\n CodeBlockLowlight.configure({\n lowlight,\n }),\n Heading.extend({\n renderHTML({ node, HTMLAttributes }) {\n const level = this.options.levels.includes(node.attrs.level)\n ? node.attrs.level\n : this.options.levels[0];\n const id =\n typeof node.textContent === \"string\" ? createId(node.textContent) : undefined;\n\n return [\n `h${level}`,\n {\n ...HTMLAttributes,\n id,\n },\n 0,\n ];\n },\n }),\n Highlight,\n CodeMirrorNode,\n];\n","export function createId(textContent: string) {\n return textContent.replace(/\\s/g, \"-\").toLowerCase();\n}\n","// This is only to maintain backward compatibility for codeMirror node type\n\nimport { Node } from \"@tiptap/core\";\n\nexport const CodeMirrorNode = Node.create({\n name: \"codeMirror\",\n content: \"block+\",\n code: true,\n\n addOptions() {\n return {\n HTMLAttributes: {},\n };\n },\n\n parseHTML() {\n return [\n {\n tag: \"pre\",\n },\n ];\n },\n\n renderHTML({ HTMLAttributes }) {\n return [\"pre\", HTMLAttributes, 0];\n },\n});\n","import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","import React from \"react\";\nimport { extractTextFromNode } from \"../extract-text-from-node\";\nimport { CopyCodeButton } from \"./copy-code-button\";\n\nfunction textFromChildren(node: React.ReactNode): string {\n if (node == null || typeof node === \"boolean\") return \"\";\n if (typeof node === \"string\" || typeof node === \"number\") {\n return String(node);\n }\n if (Array.isArray(node)) return node.map(textFromChildren).join(\"\");\n if (React.isValidElement<{ children?: React.ReactNode }>(node)) {\n return textFromChildren(node.props.children);\n }\n return \"\";\n}\n\nexport function RenderedCodeBlock({\n node,\n children,\n language,\n}: {\n node?: { textContent?: string; content?: unknown };\n children?: React.ReactNode;\n language?: string;\n}): React.JSX.Element {\n const source = extractTextFromNode(node) || textFromChildren(children);\n\n return (\n <div className=\"te-code-block\">\n <pre>\n <code className={language ? `language-${language}` : undefined}>\n {children}\n </code>\n </pre>\n <CopyCodeButton source={source} />\n </div>\n );\n}\n","export const DOCS_HEADING_POLICY_VERSION = 1;\n\nexport interface HeadingNode {\n type?: string;\n text?: string;\n attrs?: Record<string, unknown>;\n content?: HeadingNode[];\n}\n\nexport function docsNodeText(node: HeadingNode): string {\n if (typeof node.text === \"string\") return node.text;\n if (node.type === \"hardBreak\") return \"\\n\";\n return (node.content ?? []).map(docsNodeText).join(\"\");\n}\n\n/** DM-CONVERT: deterministic Unicode anchors, including duplicate-base collisions. */\nexport function createHeadingIdGenerator(): (text: string) => string {\n const used = new Set<string>();\n return (text) => {\n const base =\n text\n .normalize(\"NFKC\")\n .toLowerCase()\n .trim()\n .replace(/[^\\p{L}\\p{N}\\s_-]/gu, \"\")\n .replace(/\\s+/g, \"-\") || \"section\";\n let id = base;\n let suffix = 2;\n while (used.has(id)) id = `${base}-${suffix++}`;\n used.add(id);\n return id;\n };\n}\n\nexport function extractDocsHeadings(json: HeadingNode | null | undefined) {\n const nextId = createHeadingIdGenerator();\n const headings: { level: number; text: string; id: string }[] = [];\n function walk(node: HeadingNode) {\n if (node.type === \"heading\") {\n const text = docsNodeText(node);\n headings.push({ level: Number(node.attrs?.level ?? 1), text, id: nextId(text) });\n }\n for (const child of node.content ?? []) walk(child);\n }\n if (json) walk(json);\n return headings;\n}\n"],"mappings":"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","names":["json","jsx","json","Tag","classes"]}
@@ -0,0 +1,56 @@
1
+ "use client";
2
+ "use client";
3
+
4
+ // src/components/copy-code-button.tsx
5
+ import { useEffect, useState } from "react";
6
+ import { Check, Copy } from "lucide-react";
7
+ import { jsx } from "react/jsx-runtime";
8
+ async function writeClipboard(text) {
9
+ if (navigator.clipboard?.writeText) {
10
+ await navigator.clipboard.writeText(text);
11
+ return;
12
+ }
13
+ const area = document.createElement("textarea");
14
+ area.value = text;
15
+ area.setAttribute("readonly", "");
16
+ area.style.position = "fixed";
17
+ area.style.left = "-9999px";
18
+ document.body.appendChild(area);
19
+ area.select();
20
+ document.execCommand("copy");
21
+ area.remove();
22
+ }
23
+ function CopyCodeButton({ source }) {
24
+ const [copied, setCopied] = useState(false);
25
+ useEffect(() => {
26
+ if (!copied) return;
27
+ const timer = window.setTimeout(() => setCopied(false), 1500);
28
+ return () => window.clearTimeout(timer);
29
+ }, [copied]);
30
+ async function handleCopy() {
31
+ if (!source) return;
32
+ try {
33
+ await writeClipboard(source);
34
+ setCopied(true);
35
+ } catch {
36
+ setCopied(false);
37
+ }
38
+ }
39
+ return /* @__PURE__ */ jsx(
40
+ "button",
41
+ {
42
+ type: "button",
43
+ className: "te-code-copy",
44
+ "aria-label": copied ? "Copied" : "Copy code",
45
+ title: copied ? "Copied" : "Copy",
46
+ onClick: () => {
47
+ void handleCopy();
48
+ },
49
+ children: copied ? /* @__PURE__ */ jsx(Check, { className: "te-code-copy__icon", "aria-hidden": true }) : /* @__PURE__ */ jsx(Copy, { className: "te-code-copy__icon", "aria-hidden": true })
50
+ }
51
+ );
52
+ }
53
+ export {
54
+ CopyCodeButton
55
+ };
56
+ //# sourceMappingURL=copy-code-button.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/copy-code-button.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { Check, Copy } from \"lucide-react\";\n\nasync function writeClipboard(text: string): Promise<void> {\n if (navigator.clipboard?.writeText) {\n await navigator.clipboard.writeText(text);\n return;\n }\n const area = document.createElement(\"textarea\");\n area.value = text;\n area.setAttribute(\"readonly\", \"\");\n area.style.position = \"fixed\";\n area.style.left = \"-9999px\";\n document.body.appendChild(area);\n area.select();\n document.execCommand(\"copy\");\n area.remove();\n}\n\n/** The copy button of a rendered code block; the only client part of it. */\nexport function CopyCodeButton({ source }: { source: string }) {\n const [copied, setCopied] = useState(false);\n\n useEffect(() => {\n if (!copied) return;\n const timer = window.setTimeout(() => setCopied(false), 1500);\n return () => window.clearTimeout(timer);\n }, [copied]);\n\n async function handleCopy() {\n if (!source) return;\n try {\n await writeClipboard(source);\n setCopied(true);\n } catch {\n setCopied(false);\n }\n }\n\n return (\n <button\n type=\"button\"\n className=\"te-code-copy\"\n aria-label={copied ? \"Copied\" : \"Copy code\"}\n title={copied ? \"Copied\" : \"Copy\"}\n onClick={() => {\n void handleCopy();\n }}\n >\n {copied ? (\n <Check className=\"te-code-copy__icon\" aria-hidden />\n ) : (\n <Copy className=\"te-code-copy__icon\" aria-hidden />\n )}\n </button>\n );\n}\n"],"mappings":";;;;AAEA,SAAS,WAAW,gBAAgB;AACpC,SAAS,OAAO,YAAY;AAiDpB;AA/CR,eAAe,eAAe,MAA6B;AACzD,MAAI,UAAU,WAAW,WAAW;AAClC,UAAM,UAAU,UAAU,UAAU,IAAI;AACxC;AAAA,EACF;AACA,QAAM,OAAO,SAAS,cAAc,UAAU;AAC9C,OAAK,QAAQ;AACb,OAAK,aAAa,YAAY,EAAE;AAChC,OAAK,MAAM,WAAW;AACtB,OAAK,MAAM,OAAO;AAClB,WAAS,KAAK,YAAY,IAAI;AAC9B,OAAK,OAAO;AACZ,WAAS,YAAY,MAAM;AAC3B,OAAK,OAAO;AACd;AAGO,SAAS,eAAe,EAAE,OAAO,GAAuB;AAC7D,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,KAAK;AAE1C,YAAU,MAAM;AACd,QAAI,CAAC,OAAQ;AACb,UAAM,QAAQ,OAAO,WAAW,MAAM,UAAU,KAAK,GAAG,IAAI;AAC5D,WAAO,MAAM,OAAO,aAAa,KAAK;AAAA,EACxC,GAAG,CAAC,MAAM,CAAC;AAEX,iBAAe,aAAa;AAC1B,QAAI,CAAC,OAAQ;AACb,QAAI;AACF,YAAM,eAAe,MAAM;AAC3B,gBAAU,IAAI;AAAA,IAChB,QAAQ;AACN,gBAAU,KAAK;AAAA,IACjB;AAAA,EACF;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,WAAU;AAAA,MACV,cAAY,SAAS,WAAW;AAAA,MAChC,OAAO,SAAS,WAAW;AAAA,MAC3B,SAAS,MAAM;AACb,aAAK,WAAW;AAAA,MAClB;AAAA,MAEC,mBACC,oBAAC,SAAM,WAAU,sBAAqB,eAAW,MAAC,IAElD,oBAAC,QAAK,WAAU,sBAAqB,eAAW,MAAC;AAAA;AAAA,EAErD;AAEJ;","names":[]}