@seoengine.ai/next-llm-ready 1.0.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.
@@ -0,0 +1,619 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+
5
+ // src/hooks/use-llm-copy.ts
6
+
7
+ // src/utils/clipboard.ts
8
+ async function copyToClipboard(text) {
9
+ if (navigator.clipboard && typeof navigator.clipboard.writeText === "function") {
10
+ try {
11
+ await navigator.clipboard.writeText(text);
12
+ return true;
13
+ } catch (err) {
14
+ console.warn("Clipboard API failed, using fallback:", err);
15
+ }
16
+ }
17
+ return fallbackCopyToClipboard(text);
18
+ }
19
+ function fallbackCopyToClipboard(text) {
20
+ const textarea = document.createElement("textarea");
21
+ textarea.value = text;
22
+ textarea.style.position = "fixed";
23
+ textarea.style.top = "-9999px";
24
+ textarea.style.left = "-9999px";
25
+ textarea.style.opacity = "0";
26
+ textarea.style.pointerEvents = "none";
27
+ textarea.style.fontSize = "16px";
28
+ document.body.appendChild(textarea);
29
+ textarea.focus();
30
+ textarea.select();
31
+ try {
32
+ const successful = document.execCommand("copy");
33
+ document.body.removeChild(textarea);
34
+ return successful;
35
+ } catch (err) {
36
+ console.error("Fallback copy failed:", err);
37
+ document.body.removeChild(textarea);
38
+ return false;
39
+ }
40
+ }
41
+
42
+ // src/utils/download.ts
43
+ var defaultOptions = {
44
+ filename: "content",
45
+ extension: "md",
46
+ includeMetadata: false
47
+ };
48
+ function downloadAsFile(content, options = {}) {
49
+ const opts = { ...defaultOptions, ...options };
50
+ const filename = sanitizeFilename(opts.filename || "content");
51
+ const fullFilename = `${filename}.${opts.extension}`;
52
+ const mimeType = opts.extension === "md" ? "text/markdown" : "text/plain";
53
+ const blob = new Blob([content], { type: `${mimeType};charset=utf-8` });
54
+ const url = URL.createObjectURL(blob);
55
+ const link = document.createElement("a");
56
+ link.href = url;
57
+ link.download = fullFilename;
58
+ document.body.appendChild(link);
59
+ link.click();
60
+ document.body.removeChild(link);
61
+ URL.revokeObjectURL(url);
62
+ }
63
+ function sanitizeFilename(name) {
64
+ return name.toLowerCase().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "").substring(0, 100);
65
+ }
66
+ function generateFilename(title) {
67
+ const base = sanitizeFilename(title);
68
+ const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
69
+ return `${base}-${date}`;
70
+ }
71
+
72
+ // src/utils/html-to-markdown.ts
73
+ var defaultOptions2 = {
74
+ preserveLineBreaks: true,
75
+ convertImages: true,
76
+ convertLinks: true,
77
+ stripScripts: true,
78
+ customHandlers: {}
79
+ };
80
+ function htmlToMarkdown(html, options = {}) {
81
+ const opts = { ...defaultOptions2, ...options };
82
+ if (typeof window === "undefined") {
83
+ return serverSideConvert(html, opts);
84
+ }
85
+ const doc = new DOMParser().parseFromString(html, "text/html");
86
+ return convertNode(doc.body, opts);
87
+ }
88
+ function serverSideConvert(html, opts) {
89
+ let markdown = html;
90
+ if (opts.stripScripts) {
91
+ markdown = markdown.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "");
92
+ markdown = markdown.replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi, "");
93
+ }
94
+ markdown = markdown.replace(/<h1[^>]*>(.*?)<\/h1>/gi, "\n# $1\n");
95
+ markdown = markdown.replace(/<h2[^>]*>(.*?)<\/h2>/gi, "\n## $1\n");
96
+ markdown = markdown.replace(/<h3[^>]*>(.*?)<\/h3>/gi, "\n### $1\n");
97
+ markdown = markdown.replace(/<h4[^>]*>(.*?)<\/h4>/gi, "\n#### $1\n");
98
+ markdown = markdown.replace(/<h5[^>]*>(.*?)<\/h5>/gi, "\n##### $1\n");
99
+ markdown = markdown.replace(/<h6[^>]*>(.*?)<\/h6>/gi, "\n###### $1\n");
100
+ markdown = markdown.replace(/<(strong|b)[^>]*>(.*?)<\/\1>/gi, "**$2**");
101
+ markdown = markdown.replace(/<(em|i)[^>]*>(.*?)<\/\1>/gi, "*$2*");
102
+ if (opts.convertLinks) {
103
+ markdown = markdown.replace(/<a[^>]*href=["']([^"']*)["'][^>]*>(.*?)<\/a>/gi, "[$2]($1)");
104
+ }
105
+ if (opts.convertImages) {
106
+ markdown = markdown.replace(
107
+ /<img[^>]*src=["']([^"']*)["'][^>]*alt=["']([^"']*)["'][^>]*\/?>/gi,
108
+ "![$2]($1)"
109
+ );
110
+ markdown = markdown.replace(
111
+ /<img[^>]*alt=["']([^"']*)["'][^>]*src=["']([^"']*)["'][^>]*\/?>/gi,
112
+ "![$1]($2)"
113
+ );
114
+ markdown = markdown.replace(/<img[^>]*src=["']([^"']*)["'][^>]*\/?>/gi, "![]($1)");
115
+ }
116
+ markdown = markdown.replace(/<ul[^>]*>/gi, "\n");
117
+ markdown = markdown.replace(/<\/ul>/gi, "\n");
118
+ markdown = markdown.replace(/<ol[^>]*>/gi, "\n");
119
+ markdown = markdown.replace(/<\/ol>/gi, "\n");
120
+ markdown = markdown.replace(/<li[^>]*>(.*?)<\/li>/gi, "- $1\n");
121
+ markdown = markdown.replace(/<blockquote[^>]*>(.*?)<\/blockquote>/gis, (_, content) => {
122
+ return "\n> " + content.trim().replace(/\n/g, "\n> ") + "\n";
123
+ });
124
+ markdown = markdown.replace(/<pre[^>]*><code[^>]*>(.*?)<\/code><\/pre>/gis, "\n```\n$1\n```\n");
125
+ markdown = markdown.replace(/<code[^>]*>(.*?)<\/code>/gi, "`$1`");
126
+ markdown = markdown.replace(/<p[^>]*>(.*?)<\/p>/gis, "\n$1\n");
127
+ markdown = markdown.replace(/<br\s*\/?>/gi, "\n");
128
+ markdown = markdown.replace(/<hr[^>]*\/?>/gi, "\n---\n");
129
+ markdown = markdown.replace(/<[^>]+>/g, "");
130
+ markdown = decodeHTMLEntities(markdown);
131
+ markdown = markdown.replace(/\n{3,}/g, "\n\n");
132
+ markdown = markdown.trim();
133
+ return markdown;
134
+ }
135
+ function convertNode(node, opts) {
136
+ if (node.nodeType === Node.TEXT_NODE) {
137
+ return node.textContent || "";
138
+ }
139
+ if (node.nodeType !== Node.ELEMENT_NODE) {
140
+ return "";
141
+ }
142
+ const element = node;
143
+ const tagName = element.tagName.toLowerCase();
144
+ if (opts.customHandlers?.[tagName]) {
145
+ return opts.customHandlers[tagName](element);
146
+ }
147
+ if (opts.stripScripts && (tagName === "script" || tagName === "style")) {
148
+ return "";
149
+ }
150
+ const childContent = Array.from(element.childNodes).map((child) => convertNode(child, opts)).join("");
151
+ switch (tagName) {
152
+ case "h1":
153
+ return `
154
+ # ${childContent.trim()}
155
+ `;
156
+ case "h2":
157
+ return `
158
+ ## ${childContent.trim()}
159
+ `;
160
+ case "h3":
161
+ return `
162
+ ### ${childContent.trim()}
163
+ `;
164
+ case "h4":
165
+ return `
166
+ #### ${childContent.trim()}
167
+ `;
168
+ case "h5":
169
+ return `
170
+ ##### ${childContent.trim()}
171
+ `;
172
+ case "h6":
173
+ return `
174
+ ###### ${childContent.trim()}
175
+ `;
176
+ case "p":
177
+ return `
178
+ ${childContent.trim()}
179
+ `;
180
+ case "br":
181
+ return opts.preserveLineBreaks ? "\n" : " ";
182
+ case "strong":
183
+ case "b":
184
+ return `**${childContent}**`;
185
+ case "em":
186
+ case "i":
187
+ return `*${childContent}*`;
188
+ case "u":
189
+ return `_${childContent}_`;
190
+ case "s":
191
+ case "strike":
192
+ case "del":
193
+ return `~~${childContent}~~`;
194
+ case "a":
195
+ if (opts.convertLinks) {
196
+ const href = element.getAttribute("href") || "";
197
+ return `[${childContent}](${href})`;
198
+ }
199
+ return childContent;
200
+ case "img":
201
+ if (opts.convertImages) {
202
+ const src = element.getAttribute("src") || "";
203
+ const alt = element.getAttribute("alt") || "";
204
+ return `![${alt}](${src})`;
205
+ }
206
+ return "";
207
+ case "ul":
208
+ return `
209
+ ${childContent}
210
+ `;
211
+ case "ol":
212
+ return `
213
+ ${childContent}
214
+ `;
215
+ case "li":
216
+ return `- ${childContent.trim()}
217
+ `;
218
+ case "blockquote":
219
+ return `
220
+ > ${childContent.trim().replace(/\n/g, "\n> ")}
221
+ `;
222
+ case "pre":
223
+ const codeElement = element.querySelector("code");
224
+ const lang = codeElement?.className.match(/language-(\w+)/)?.[1] || "";
225
+ const code = codeElement?.textContent || childContent;
226
+ return `
227
+ \`\`\`${lang}
228
+ ${code.trim()}
229
+ \`\`\`
230
+ `;
231
+ case "code":
232
+ if (element.parentElement?.tagName.toLowerCase() === "pre") {
233
+ return childContent;
234
+ }
235
+ return `\`${childContent}\``;
236
+ case "hr":
237
+ return "\n---\n";
238
+ case "table":
239
+ return convertTable(element);
240
+ case "div":
241
+ case "section":
242
+ case "article":
243
+ case "main":
244
+ case "aside":
245
+ case "header":
246
+ case "footer":
247
+ case "nav":
248
+ return childContent;
249
+ default:
250
+ return childContent;
251
+ }
252
+ }
253
+ function convertTable(table) {
254
+ const rows = table.querySelectorAll("tr");
255
+ if (rows.length === 0) return "";
256
+ let markdown = "\n";
257
+ let headerProcessed = false;
258
+ rows.forEach((row, index) => {
259
+ const cells = row.querySelectorAll("th, td");
260
+ const rowContent = Array.from(cells).map((cell) => cell.textContent?.trim() || "").join(" | ");
261
+ markdown += `| ${rowContent} |
262
+ `;
263
+ if (!headerProcessed && (row.querySelector("th") || index === 0)) {
264
+ const separator = Array.from(cells).map(() => "---").join(" | ");
265
+ markdown += `| ${separator} |
266
+ `;
267
+ headerProcessed = true;
268
+ }
269
+ });
270
+ return markdown + "\n";
271
+ }
272
+ function decodeHTMLEntities(text) {
273
+ const entities = {
274
+ "&amp;": "&",
275
+ "&lt;": "<",
276
+ "&gt;": ">",
277
+ "&quot;": '"',
278
+ "&#39;": "'",
279
+ "&apos;": "'",
280
+ "&nbsp;": " ",
281
+ "&mdash;": "\u2014",
282
+ "&ndash;": "\u2013",
283
+ "&hellip;": "\u2026",
284
+ "&copy;": "\xA9",
285
+ "&reg;": "\xAE",
286
+ "&trade;": "\u2122"
287
+ };
288
+ let result = text;
289
+ for (const [entity, char] of Object.entries(entities)) {
290
+ result = result.replace(new RegExp(entity, "g"), char);
291
+ }
292
+ result = result.replace(/&#(\d+);/g, (_, num) => String.fromCharCode(parseInt(num, 10)));
293
+ result = result.replace(
294
+ /&#x([0-9a-f]+);/gi,
295
+ (_, hex) => String.fromCharCode(parseInt(hex, 16))
296
+ );
297
+ return result;
298
+ }
299
+ function countWords(text) {
300
+ return text.replace(/[^\w\s]/g, "").split(/\s+/).filter((word) => word.length > 0).length;
301
+ }
302
+ function calculateReadingTime(text, wordsPerMinute = 200) {
303
+ const words = countWords(text);
304
+ return Math.ceil(words / wordsPerMinute);
305
+ }
306
+
307
+ // src/hooks/use-llm-copy.ts
308
+ function generateMarkdownFromContent(content) {
309
+ if (typeof content === "string") {
310
+ return content;
311
+ }
312
+ const parts = [];
313
+ if (content.promptPrefix?.trim()) {
314
+ parts.push(content.promptPrefix.trim());
315
+ parts.push("");
316
+ }
317
+ parts.push(`# ${content.title}`);
318
+ parts.push("");
319
+ if (content.excerpt) {
320
+ parts.push(`> ${content.excerpt}`);
321
+ parts.push("");
322
+ }
323
+ parts.push("---");
324
+ parts.push(`- **Source**: ${content.url}`);
325
+ if (content.date) {
326
+ parts.push(`- **Date**: ${content.date}`);
327
+ }
328
+ if (content.author) {
329
+ parts.push(`- **Author**: ${content.author}`);
330
+ }
331
+ if (content.categories?.length) {
332
+ parts.push(`- **Categories**: ${content.categories.join(", ")}`);
333
+ }
334
+ if (content.tags?.length) {
335
+ parts.push(`- **Tags**: ${content.tags.join(", ")}`);
336
+ }
337
+ parts.push("---");
338
+ parts.push("");
339
+ if (content.content) {
340
+ const contentMarkdown = content.content.includes("<") && content.content.includes(">") ? htmlToMarkdown(content.content) : content.content;
341
+ parts.push(contentMarkdown);
342
+ }
343
+ return parts.join("\n").trim();
344
+ }
345
+ function useLLMCopy(options) {
346
+ const [isCopying, setIsCopying] = react.useState(false);
347
+ const [isSuccess, setIsSuccess] = react.useState(false);
348
+ const [error, setError] = react.useState(null);
349
+ const markdown = react.useMemo(() => {
350
+ return generateMarkdownFromContent(options.content);
351
+ }, [options.content]);
352
+ const trackEvent = react.useCallback(
353
+ (action) => {
354
+ if (options.onAnalytics) {
355
+ const contentId = typeof options.content === "string" ? void 0 : options.content.url;
356
+ options.onAnalytics({
357
+ action,
358
+ contentId,
359
+ url: typeof window !== "undefined" ? window.location.href : void 0,
360
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
361
+ });
362
+ }
363
+ },
364
+ [options]
365
+ );
366
+ const copy = react.useCallback(async () => {
367
+ setIsCopying(true);
368
+ setError(null);
369
+ try {
370
+ const success = await copyToClipboard(markdown);
371
+ if (success) {
372
+ setIsSuccess(true);
373
+ trackEvent("copy");
374
+ options.onSuccess?.("copy");
375
+ setTimeout(() => setIsSuccess(false), 2e3);
376
+ return true;
377
+ } else {
378
+ throw new Error("Failed to copy to clipboard");
379
+ }
380
+ } catch (err) {
381
+ const error2 = err instanceof Error ? err : new Error("Copy failed");
382
+ setError(error2);
383
+ options.onError?.(error2);
384
+ return false;
385
+ } finally {
386
+ setIsCopying(false);
387
+ }
388
+ }, [markdown, options, trackEvent]);
389
+ const view = react.useCallback(() => {
390
+ trackEvent("view");
391
+ options.onSuccess?.("view");
392
+ return markdown;
393
+ }, [markdown, options, trackEvent]);
394
+ const download = react.useCallback(
395
+ (filename) => {
396
+ const title = typeof options.content === "string" ? "content" : options.content.title;
397
+ downloadAsFile(markdown, {
398
+ filename: filename || generateFilename(title),
399
+ extension: "md"
400
+ });
401
+ trackEvent("download");
402
+ options.onSuccess?.("download");
403
+ },
404
+ [markdown, options, trackEvent]
405
+ );
406
+ return {
407
+ copy,
408
+ view,
409
+ download,
410
+ markdown,
411
+ isCopying,
412
+ isSuccess,
413
+ error
414
+ };
415
+ }
416
+ function useContentStats(content) {
417
+ return react.useMemo(() => {
418
+ const markdown = generateMarkdownFromContent(content);
419
+ const wordCount = countWords(markdown);
420
+ const readingTime = calculateReadingTime(markdown);
421
+ return { wordCount, readingTime };
422
+ }, [content]);
423
+ }
424
+ function useTOC(options) {
425
+ const {
426
+ contentRef,
427
+ levels = ["h2", "h3", "h4"],
428
+ rootMargin = "-100px 0px -80% 0px",
429
+ threshold = 0
430
+ } = options;
431
+ const [headings, setHeadings] = react.useState([]);
432
+ const [activeId, setActiveId] = react.useState(null);
433
+ react.useEffect(() => {
434
+ if (!contentRef?.current) return;
435
+ const container = contentRef.current;
436
+ const selector = levels.join(", ");
437
+ const elements = container.querySelectorAll(selector);
438
+ const extracted = [];
439
+ let index = 0;
440
+ elements.forEach((el) => {
441
+ const tagName = el.tagName.toLowerCase();
442
+ const level = parseInt(tagName.charAt(1), 10);
443
+ const text = el.textContent?.trim() || "";
444
+ let id = el.id;
445
+ if (!id) {
446
+ id = generateHeadingId(text, index);
447
+ el.id = id;
448
+ }
449
+ extracted.push({ id, text, level });
450
+ index++;
451
+ });
452
+ setHeadings(extracted);
453
+ }, [contentRef, levels]);
454
+ react.useEffect(() => {
455
+ if (!contentRef?.current || headings.length === 0) return;
456
+ const container = contentRef.current;
457
+ const observerOptions = {
458
+ root: null,
459
+ rootMargin,
460
+ threshold
461
+ };
462
+ const observer = new IntersectionObserver((entries) => {
463
+ entries.forEach((entry) => {
464
+ if (entry.isIntersecting) {
465
+ setActiveId(entry.target.id);
466
+ }
467
+ });
468
+ }, observerOptions);
469
+ headings.forEach((heading) => {
470
+ const element = container.querySelector(`#${CSS.escape(heading.id)}`);
471
+ if (element) {
472
+ observer.observe(element);
473
+ }
474
+ });
475
+ return () => observer.disconnect();
476
+ }, [contentRef, headings, rootMargin, threshold]);
477
+ const scrollTo = react.useCallback((id) => {
478
+ const element = document.getElementById(id);
479
+ if (element) {
480
+ element.scrollIntoView({ behavior: "smooth", block: "start" });
481
+ setActiveId(id);
482
+ if (typeof window !== "undefined") {
483
+ window.history.pushState(null, "", `#${id}`);
484
+ }
485
+ }
486
+ }, []);
487
+ return { headings, activeId, scrollTo };
488
+ }
489
+ function generateHeadingId(text, index) {
490
+ const slug = text.toLowerCase().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "").substring(0, 50);
491
+ return slug || `heading-${index}`;
492
+ }
493
+ function buildNestedTOC(headings) {
494
+ if (headings.length === 0) return [];
495
+ const result = [];
496
+ const stack = [];
497
+ for (const heading of headings) {
498
+ const item = { ...heading, children: [] };
499
+ while (stack.length > 0 && stack[stack.length - 1].level >= heading.level) {
500
+ stack.pop();
501
+ }
502
+ if (stack.length === 0) {
503
+ result.push(item);
504
+ } else {
505
+ const parent = stack[stack.length - 1].heading;
506
+ if (!parent.children) parent.children = [];
507
+ parent.children.push(item);
508
+ }
509
+ stack.push({ heading: item, level: heading.level });
510
+ }
511
+ return result;
512
+ }
513
+ function useTOCKeyboard(headings, activeId, scrollTo) {
514
+ react.useEffect(() => {
515
+ const handleKeyDown = (e) => {
516
+ if (headings.length === 0) return;
517
+ const currentIndex = headings.findIndex((h) => h.id === activeId);
518
+ if (e.key === "ArrowDown" && e.altKey) {
519
+ e.preventDefault();
520
+ const nextIndex = Math.min(currentIndex + 1, headings.length - 1);
521
+ scrollTo(headings[nextIndex].id);
522
+ } else if (e.key === "ArrowUp" && e.altKey) {
523
+ e.preventDefault();
524
+ const prevIndex = Math.max(currentIndex - 1, 0);
525
+ scrollTo(headings[prevIndex].id);
526
+ }
527
+ };
528
+ document.addEventListener("keydown", handleKeyDown);
529
+ return () => document.removeEventListener("keydown", handleKeyDown);
530
+ }, [headings, activeId, scrollTo]);
531
+ }
532
+ function useAnalytics(options = {}) {
533
+ const {
534
+ endpoint = "/api/llm-analytics",
535
+ includeUrl = true,
536
+ includeTimestamp = true,
537
+ metadata = {}
538
+ } = options;
539
+ const pendingRef = react.useRef(/* @__PURE__ */ new Set());
540
+ const track = react.useCallback(
541
+ async (event) => {
542
+ const key = `${event.action}-${event.contentId || ""}-${Date.now()}`;
543
+ if (pendingRef.current.has(key)) {
544
+ return;
545
+ }
546
+ pendingRef.current.add(key);
547
+ try {
548
+ const payload = {
549
+ ...event,
550
+ ...includeUrl && { url: event.url || window.location.href },
551
+ ...includeTimestamp && { timestamp: event.timestamp || (/* @__PURE__ */ new Date()).toISOString() },
552
+ metadata: { ...metadata, ...event.metadata }
553
+ };
554
+ if (navigator.sendBeacon) {
555
+ const blob = new Blob([JSON.stringify(payload)], { type: "application/json" });
556
+ navigator.sendBeacon(endpoint, blob);
557
+ } else {
558
+ await fetch(endpoint, {
559
+ method: "POST",
560
+ headers: { "Content-Type": "application/json" },
561
+ body: JSON.stringify(payload),
562
+ keepalive: true
563
+ });
564
+ }
565
+ } catch (error) {
566
+ console.error("Analytics tracking failed:", error);
567
+ } finally {
568
+ pendingRef.current.delete(key);
569
+ }
570
+ },
571
+ [endpoint, includeUrl, includeTimestamp, metadata]
572
+ );
573
+ const trackCopy = react.useCallback(() => {
574
+ return track({ action: "copy" });
575
+ }, [track]);
576
+ const trackView = react.useCallback(() => {
577
+ return track({ action: "view" });
578
+ }, [track]);
579
+ const trackDownload = react.useCallback(() => {
580
+ return track({ action: "download" });
581
+ }, [track]);
582
+ return { track, trackCopy, trackView, trackDownload };
583
+ }
584
+ function createAnalyticsTracker(endpoint) {
585
+ return async function trackEvent(action, contentId, metadata) {
586
+ const payload = {
587
+ action,
588
+ contentId,
589
+ url: typeof window !== "undefined" ? window.location.href : void 0,
590
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
591
+ metadata
592
+ };
593
+ try {
594
+ if (typeof navigator !== "undefined" && navigator.sendBeacon) {
595
+ const blob = new Blob([JSON.stringify(payload)], { type: "application/json" });
596
+ navigator.sendBeacon(endpoint, blob);
597
+ } else if (typeof fetch !== "undefined") {
598
+ await fetch(endpoint, {
599
+ method: "POST",
600
+ headers: { "Content-Type": "application/json" },
601
+ body: JSON.stringify(payload),
602
+ keepalive: true
603
+ });
604
+ }
605
+ } catch (error) {
606
+ console.error("Analytics tracking failed:", error);
607
+ }
608
+ };
609
+ }
610
+
611
+ exports.buildNestedTOC = buildNestedTOC;
612
+ exports.createAnalyticsTracker = createAnalyticsTracker;
613
+ exports.useAnalytics = useAnalytics;
614
+ exports.useContentStats = useContentStats;
615
+ exports.useLLMCopy = useLLMCopy;
616
+ exports.useTOC = useTOC;
617
+ exports.useTOCKeyboard = useTOCKeyboard;
618
+ //# sourceMappingURL=index.cjs.map
619
+ //# sourceMappingURL=index.cjs.map