@docubook/flame 1.0.0-beta.70 → 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.
@@ -34,7 +34,7 @@ export default function Anchor({
34
34
 
35
35
  const activeClass = isActive ? activeClassName : "";
36
36
  const baseClasses = cn(
37
- "text-blue-600 hover:text-blue-800 hover:underline transition-colors",
37
+ "hover:underline transition-colors",
38
38
  className,
39
39
  activeClass,
40
40
  disabled && "cursor-not-allowed opacity-50"
@@ -1,4 +1,9 @@
1
- import { Breadcrumb, BreadcrumbItem, BreadcrumbList, BreadcrumbPage } from "./base/breadcrumbs";
1
+ import {
2
+ Breadcrumb,
3
+ BreadcrumbItem,
4
+ BreadcrumbList,
5
+ BreadcrumbPage,
6
+ } from "@docubook/ui-react/breadcrumbs";
2
7
 
3
8
  export interface DocsBreadcrumbProps {
4
9
  paths: string[];
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { useState } from "react";
4
4
  import { cn } from "../node/utils";
5
- import { Dropdown, DropdownItem } from "./base/dropdown";
5
+ import { Dropdown, DropdownItem } from "@docubook/ui-react/dropdown";
6
6
  import { routes } from "../node/client-routes";
7
7
  import { ChevronsUpDown, Check, type LucideIcon } from "lucide-react";
8
8
  import * as LucideIcons from "lucide-react";
@@ -51,6 +51,7 @@ export function Context({ className }: ContextProps) {
51
51
  <Dropdown
52
52
  className={cn("w-full", className)}
53
53
  align="start"
54
+ menuClassName="w-full"
54
55
  trigger={
55
56
  <div className="border-base-300 flex items-center justify-between rounded-lg border px-3 py-2 text-sm font-semibold">
56
57
  <div className="flex min-w-0 items-center gap-2">
@@ -8,9 +8,9 @@ import {
8
8
  NavMenu as BaseNavMenu,
9
9
  NavMenuLink as BaseNavMenuLink,
10
10
  type NavMenuItem as BaseNavMenuItem,
11
- } from "./base/navbar";
11
+ } from "@docubook/ui-react/navbar";
12
12
 
13
- export type { NavMenuItem } from "./base/navbar";
13
+ export type { NavMenuItem } from "@docubook/ui-react/navbar";
14
14
 
15
15
  interface AppNavMenuItem {
16
16
  title: string;
@@ -1,14 +1,24 @@
1
1
  "use client";
2
2
 
3
+ import type { ReactNode } from "react";
3
4
  import { getPreviousNext } from "../node/route";
4
- import { PaginationDocs } from "./base/pagination";
5
+ import { PaginationDocs } from "@docubook/ui-react/pagination";
5
6
 
6
7
  interface PaginationProps {
7
8
  pathname: string;
8
9
  className?: string;
10
+ prevIcon?: ReactNode;
11
+ nextIcon?: ReactNode;
12
+ linkClassName?: string;
9
13
  }
10
14
 
11
- export default function Pagination({ pathname, className }: PaginationProps) {
15
+ export default function Pagination({
16
+ pathname,
17
+ className,
18
+ prevIcon,
19
+ nextIcon,
20
+ linkClassName,
21
+ }: PaginationProps) {
12
22
  const { prev, next } = getPreviousNext(pathname);
13
23
 
14
24
  if (!prev && !next) {
@@ -20,6 +30,9 @@ export default function Pagination({ pathname, className }: PaginationProps) {
20
30
  prev={prev ? { href: `/docs${prev.href}`, title: prev.title } : undefined}
21
31
  next={next ? { href: `/docs${next.href}`, title: next.title } : undefined}
22
32
  className={className}
33
+ prevIcon={prevIcon}
34
+ nextIcon={nextIcon}
35
+ linkClassName={linkClassName}
23
36
  />
24
37
  );
25
38
  }
@@ -2,8 +2,8 @@
2
2
 
3
3
  import { useState, useEffect, useRef, useCallback } from "react";
4
4
  import { Search as SearchIcon, FileText, CornerDownLeft, Hash, AlignLeft } from "lucide-react";
5
- import { Modal, useModal } from "./base/modal";
6
- import { Kbd, FnKey } from "./base/kbd";
5
+ import { Modal, useModal } from "@docubook/ui-react/modal";
6
+ import { Kbd, FnKey } from "@docubook/ui-react/kbd";
7
7
  import { cn } from "../node/utils";
8
8
  import { search, type SearchResult } from "../node/search";
9
9
  import type { SearchRecord } from "../node/search-indexer";
@@ -217,16 +217,10 @@ function SearchTrigger({ onClick }: { onClick: () => void }) {
217
217
  <SearchIcon className="h-4 w-4 shrink-0" />
218
218
  <span className="flex-1 text-left">Search</span>
219
219
  <div className="flex items-center gap-0.5">
220
- <Kbd
221
- size="s"
222
- className="bg-primary text-[12px text-base-300 dark:text-foreground rounded-md p-1.5 font-medium"
223
- >
220
+ <Kbd className="bg-primary text-[12px text-base-300 dark:text-foreground rounded-md p-1.5 font-medium">
224
221
  <FnKey.Cmd />
225
222
  </Kbd>
226
- <Kbd
227
- size="s"
228
- className="bg-primary text-base-300 dark:text-foreground rounded-md p-1.5 text-[12px] font-medium"
229
- >
223
+ <Kbd className="bg-primary text-base-300 dark:text-foreground rounded-md p-1.5 text-[12px] font-medium">
230
224
  K
231
225
  </Kbd>
232
226
  </div>
@@ -9,8 +9,9 @@ import {
9
9
  PanelRightClose,
10
10
  FileText,
11
11
  } from "lucide-react";
12
- import { Dropdown, DropdownLink } from "./base/dropdown";
12
+ import { Dropdown } from "@docubook/ui-react/dropdown";
13
13
  import { cn } from "../node/utils";
14
+ import Anchor from "./Anchor";
14
15
  import { Context } from "./Context";
15
16
  import Menu from "./Menu";
16
17
  import { ThemeToggle } from "./Theme";
@@ -132,6 +133,7 @@ export function MobileBar({
132
133
  <div className="flex items-center gap-1 p-2">
133
134
  <Dropdown
134
135
  align="start"
136
+ menuClassName="w-52 min-w-0"
135
137
  trigger={
136
138
  <span className="btn btn-ghost btn-sm btn-square">
137
139
  <EllipsisVertical className="text-base-content/60 h-5 w-5" />
@@ -139,9 +141,18 @@ export function MobileBar({
139
141
  }
140
142
  >
141
143
  {(docuConfig.navbar?.menu || []).map((item) => (
142
- <DropdownLink key={item.href} href={item.href}>
143
- {item.title}
144
- </DropdownLink>
144
+ <li key={item.href} className="text-base-content/80 hover:text-base-content">
145
+ <Anchor
146
+ href={item.href}
147
+ activeWhen={(path: string) =>
148
+ path === item.href || path.startsWith(item.href + "/")
149
+ }
150
+ activeClassName="text-primary font-semibold"
151
+ className="block rounded px-2 py-2 text-sm"
152
+ >
153
+ {item.title}
154
+ </Anchor>
155
+ </li>
145
156
  ))}
146
157
  </Dropdown>
147
158
 
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import { Moon, Sun } from "lucide-react";
4
- import { ThemeControllerToggle } from "./base/theme-controller";
4
+ import { ThemeControllerToggle } from "@docubook/ui-react/theme-controller";
5
5
 
6
6
  interface ThemeToggleProps {
7
7
  className?: string;
@@ -1,56 +1,32 @@
1
1
  import type { ComponentType } from "react";
2
2
  import { createMdxComponents } from "@docubook/mdx-content";
3
- import {
4
- Dropdown,
5
- DropdownItem,
6
- DropdownLink,
7
- DropdownLabel,
8
- DropdownDivider,
9
- } from "./base/dropdown";
10
- import {
11
- Pagination,
12
- PaginationItem,
13
- PaginationButtons,
14
- PaginationRange,
15
- PaginationInfo,
16
- PaginationFull,
17
- PaginationDocs,
18
- } from "./base/pagination";
19
- import { Toggle, ToggleGroup } from "./base/toggle";
20
- import {
21
- ThemeController,
22
- ThemeControllerToggle,
23
- ThemeControllerSelect,
24
- ThemeControllerRadio,
25
- } from "./base/theme-controller";
3
+ import { Dropdown, DropdownItem, DropdownLink } from "@docubook/ui-react/dropdown";
4
+ import { PaginationDocs } from "@docubook/ui-react/pagination";
5
+ import { Toggle, ToggleGroup } from "@docubook/ui-react/toggle";
6
+ import { ThemeControllerToggle } from "@docubook/ui-react/theme-controller";
26
7
  import {
27
8
  Breadcrumb,
28
9
  BreadcrumbItem,
29
- BreadcrumbLink,
30
10
  BreadcrumbPage,
31
- BreadcrumbSeparator,
32
11
  BreadcrumbList,
33
- } from "./base/breadcrumbs";
34
- import Collapse, { Accordion } from "./base/collapse";
12
+ } from "@docubook/ui-react/breadcrumbs";
13
+ import { Collapse, Accordion } from "@docubook/ui-react/collapse";
35
14
  import {
36
15
  Drawer,
37
16
  useDrawerState,
38
17
  DrawerTrigger,
39
18
  DrawerContent,
40
19
  DrawerSidePanel,
41
- } from "./base/drawer";
42
- import { Input, InputGroup } from "./base/input";
43
- import { Kbd, FnKey } from "./base/kbd";
44
- import { Modal, ModalAction, useModal } from "./base/modal";
20
+ } from "@docubook/ui-react/drawer";
21
+ import { Input, InputGroup } from "@docubook/ui-react/input";
22
+ import { Kbd, FnKey } from "@docubook/ui-react/kbd";
23
+ import { Modal, useModal } from "@docubook/ui-react/modal";
45
24
  import {
46
25
  Navbar as BaseNavbar,
47
- NavbarContainer,
48
26
  Logo as BaseLogo,
49
27
  NavMenu as BaseNavMenu,
50
28
  NavMenuLink,
51
- NavToggle,
52
- NavbarVersion,
53
- } from "./base/navbar";
29
+ } from "@docubook/ui-react/navbar";
54
30
  import PaginationComp from "./Pagination";
55
31
  import Anchor from "./Anchor";
56
32
  import { Footer } from "./Footer";
@@ -71,40 +47,17 @@ import {
71
47
  import Search from "./Search";
72
48
  import Sidebar from "./Sidebar";
73
49
 
74
- export { Dropdown, DropdownItem, DropdownLink, DropdownLabel, DropdownDivider };
75
- export {
76
- Pagination,
77
- PaginationItem,
78
- PaginationButtons,
79
- PaginationRange,
80
- PaginationInfo,
81
- PaginationFull,
82
- PaginationDocs,
83
- };
50
+ export { Dropdown, DropdownItem, DropdownLink };
51
+ export { PaginationDocs };
84
52
  export { Toggle, ToggleGroup };
85
- export { ThemeController, ThemeControllerToggle, ThemeControllerSelect, ThemeControllerRadio };
86
- export {
87
- Breadcrumb,
88
- BreadcrumbItem,
89
- BreadcrumbLink,
90
- BreadcrumbPage,
91
- BreadcrumbSeparator,
92
- BreadcrumbList,
93
- };
53
+ export { ThemeControllerToggle };
54
+ export { Breadcrumb, BreadcrumbItem, BreadcrumbPage, BreadcrumbList };
94
55
  export { Collapse, Accordion };
95
56
  export { Drawer, useDrawerState, DrawerTrigger, DrawerContent, DrawerSidePanel };
96
57
  export { Input, InputGroup };
97
58
  export { Kbd, FnKey };
98
- export { Modal, ModalAction, useModal };
99
- export {
100
- BaseNavbar,
101
- NavbarContainer,
102
- BaseLogo,
103
- BaseNavMenu,
104
- NavMenuLink,
105
- NavToggle,
106
- NavbarVersion,
107
- };
59
+ export { Modal, useModal };
60
+ export { BaseNavbar, BaseLogo, BaseNavMenu, NavMenuLink };
108
61
  export { Footer };
109
62
  export { ThemeToggle };
110
63
  export { Context };
@@ -122,16 +75,8 @@ const BASE_COMPONENTS = {
122
75
  Dropdown,
123
76
  DropdownItem,
124
77
  DropdownLink,
125
- DropdownLabel,
126
- DropdownDivider,
127
78
 
128
79
  // Pagination
129
- Pagination,
130
- PaginationItem,
131
- PaginationButtons,
132
- PaginationRange,
133
- PaginationInfo,
134
- PaginationFull,
135
80
  PaginationDocs,
136
81
 
137
82
  // Toggle
@@ -139,17 +84,12 @@ const BASE_COMPONENTS = {
139
84
  ToggleGroup,
140
85
 
141
86
  // Theme
142
- ThemeController,
143
87
  ThemeControllerToggle,
144
- ThemeControllerSelect,
145
- ThemeControllerRadio,
146
88
 
147
89
  // Breadcrumb
148
90
  Breadcrumb,
149
91
  BreadcrumbItem,
150
- BreadcrumbLink,
151
92
  BreadcrumbPage,
152
- BreadcrumbSeparator,
153
93
  BreadcrumbList,
154
94
 
155
95
  // Accordion
@@ -173,17 +113,13 @@ const BASE_COMPONENTS = {
173
113
 
174
114
  // Modal
175
115
  Modal,
176
- ModalAction,
177
116
  useModal,
178
117
 
179
118
  // Base Navbar
180
119
  BaseNavbar,
181
- NavbarContainer,
182
120
  BaseLogo,
183
121
  BaseNavMenu,
184
122
  NavMenuLink,
185
- NavToggle,
186
- NavbarVersion,
187
123
  } as const;
188
124
 
189
125
  const APP_COMPONENTS = {
@@ -216,15 +152,13 @@ export function createComponentsRegistry(): Record<string, unknown> {
216
152
  export async function createFullComponentsRegistry(): Promise<Record<string, unknown>> {
217
153
  const baseComponents = createComponentsRegistry();
218
154
 
219
- let mdxComponents: ComponentRegistry = {};
220
155
  try {
221
156
  const mdx = createMdxComponents() as unknown as ComponentRegistry;
222
- mdxComponents = mdx;
223
- } catch {
224
- // MDX components not available
157
+ return { ...baseComponents, ...mdx };
158
+ } catch (error) {
159
+ console.error("Failed to load MDX components:", error);
160
+ return { ...baseComponents };
225
161
  }
226
-
227
- return { ...baseComponents, ...mdxComponents };
228
162
  }
229
163
 
230
164
  export const componentsRegistry = createComponentsRegistry();
@@ -4,7 +4,7 @@ import { createHash } from "node:crypto";
4
4
  import { join, dirname } from "node:path";
5
5
  import React from "react";
6
6
  import { renderToString } from "react-dom/server";
7
- import { compileMdx } from "./mdx";
7
+ import { compileMdx, getGitLastModifiedBatch } from "./mdx";
8
8
  import {
9
9
  DOCS_DIR,
10
10
  DIST_DIR,
@@ -83,7 +83,11 @@ async function findMdxFiles(dir: string, baseDir = ""): Promise<{ path: string;
83
83
  return files;
84
84
  }
85
85
 
86
- function shouldRebuild(path: string, mtime: number, cache: BuildCache): boolean {
86
+ export function parseConcurrency(): number {
87
+ return Math.max(1, parseInt(process.env.BUILD_CONCURRENCY || "10", 10) || 10);
88
+ }
89
+
90
+ export function shouldRebuild(path: string, mtime: number, cache: BuildCache): boolean {
87
91
  const cached = cache[path];
88
92
  if (!cached) return true;
89
93
  return mtime > cached.builtAt;
@@ -103,10 +107,15 @@ function htmlShell(title: string, description: string, body: string): string {
103
107
  });
104
108
  }
105
109
 
106
- async function renderDocsPage(slug: string, rawMdx: string, filePath: string): Promise<string> {
110
+ async function renderDocsPage(
111
+ slug: string,
112
+ rawMdx: string,
113
+ filePath: string,
114
+ gitDates?: Map<string, string>
115
+ ): Promise<string> {
107
116
  let result;
108
117
  try {
109
- result = await compileMdx(rawMdx, filePath);
118
+ result = await compileMdx(rawMdx, filePath, gitDates);
110
119
  } catch (err) {
111
120
  const msg = err instanceof Error ? err.message : "Unknown MDX error";
112
121
  throw new Error(`MDX Error in: docs/${slug}.mdx\n${msg}`, { cause: err });
@@ -194,7 +203,20 @@ async function build() {
194
203
  logger.spinner.start("Building pages...");
195
204
  t = performance.now();
196
205
 
197
- const CONCURRENCY = Math.max(1, parseInt(process.env.BUILD_CONCURRENCY || "10", 10) || 10);
206
+ const allRelPaths = mdxFiles
207
+ .map((f) => {
208
+ const mdxPath1 = join(DOCS_DIR, f.path, "index.mdx");
209
+ const mdxPath2 = join(DOCS_DIR, `${f.path}.mdx`);
210
+ const mdxPath3 = join(DOCS_DIR, `${f.path}.md`);
211
+ for (const p of [mdxPath1, mdxPath2, mdxPath3]) {
212
+ if (existsSync(p)) return p.replace(PROJECT_ROOT + "/", "");
213
+ }
214
+ return null;
215
+ })
216
+ .filter((p): p is string => p !== null);
217
+ const gitDates = await getGitLastModifiedBatch(allRelPaths);
218
+
219
+ const CONCURRENCY = parseConcurrency();
198
220
  const buildTasks = [];
199
221
  const errors: string[] = [];
200
222
 
@@ -232,7 +254,7 @@ async function build() {
232
254
 
233
255
  buildTasks.push(async () => {
234
256
  try {
235
- const html = await renderDocsPage(capturedFile.path, capturedRawMdx, relPath);
257
+ const html = await renderDocsPage(capturedFile.path, capturedRawMdx, relPath, gitDates);
236
258
  const outputPath = join(DIST_DIR, "docs", `${capturedFile.path}.html`);
237
259
  await mkdir(dirname(outputPath), { recursive: true });
238
260
  await writeFile(outputPath, html);
@@ -258,7 +280,7 @@ async function build() {
258
280
  const indexMdxPath = join(DOCS_DIR, "index.mdx");
259
281
  const indexRaw = await readFile(indexMdxPath, "utf-8");
260
282
  const indexRelPath = indexMdxPath.replace(PROJECT_ROOT + "/", "");
261
- const indexHtml = await renderDocsPage("", indexRaw, indexRelPath);
283
+ const indexHtml = await renderDocsPage("", indexRaw, indexRelPath, gitDates);
262
284
  await mkdir(join(DIST_DIR, "docs"), { recursive: true });
263
285
  await writeFile(join(DIST_DIR, "docs", "index.html"), indexHtml);
264
286
  } catch (err) {
@@ -5,16 +5,9 @@ import { createMdxComponents } from "@docubook/mdx-content";
5
5
  import Sidebar, { MobileBar } from "../components/Sidebar";
6
6
  import Toc from "../components/Toc";
7
7
  import { ThemeToggle } from "../components/Theme";
8
+ import { safeParseTocs } from "./parse-tocs";
8
9
  import type { TocItem } from "./types";
9
10
 
10
- function safeParseTocs(raw: string | undefined): TocItem[] {
11
- try {
12
- return JSON.parse(raw || "[]");
13
- } catch {
14
- return [];
15
- }
16
- }
17
-
18
11
  function mountIsland(
19
12
  id: string,
20
13
  render: (el: HTMLElement) => React.ReactElement,
@@ -29,3 +29,15 @@ export function htmlShell(opts: HtmlShellOptions): string {
29
29
  </body>
30
30
  </html>`;
31
31
  }
32
+
33
+ export function hmrScript(nonce: string): string {
34
+ return `<script nonce="${Bun.escapeHTML(nonce)}">
35
+ (function(){
36
+ const es = new EventSource("/__hmr");
37
+ es.onmessage = function(e) {
38
+ if (e.data === "reload") window.location.reload();
39
+ };
40
+ es.onerror = function() { es.close(); setTimeout(() => { window.location.reload(); }, 2000); };
41
+ })();
42
+ </script>`;
43
+ }
package/.docu/node/mdx.ts CHANGED
@@ -8,7 +8,9 @@ import {
8
8
  MDXRemote,
9
9
  } from "@docubook/core";
10
10
  import { createMdxComponents } from "@docubook/mdx-content";
11
- import { getGitLastModified } from "./utils";
11
+ import { getGitLastModified, getGitLastModifiedBatch } from "./utils";
12
+
13
+ export { getGitLastModifiedBatch };
12
14
 
13
15
  export interface MdxResult {
14
16
  content: React.ReactElement;
@@ -17,7 +19,11 @@ export interface MdxResult {
17
19
  tocs: ReturnType<typeof extractTocsFromRawMdx>;
18
20
  }
19
21
 
20
- export async function compileMdx(rawMdx: string, filePath: string): Promise<MdxResult> {
22
+ export async function compileMdx(
23
+ rawMdx: string,
24
+ filePath: string,
25
+ gitDates?: Map<string, string>
26
+ ): Promise<MdxResult> {
21
27
  const tocs = extractTocsFromRawMdx(rawMdx);
22
28
  const { frontmatter, strippedContent } = extractFrontmatterWithContent<{
23
29
  title?: string;
@@ -40,7 +46,11 @@ export async function compileMdx(rawMdx: string, filePath: string): Promise<MdxR
40
46
  components,
41
47
  });
42
48
 
43
- const date = frontmatter.date || (await getGitLastModified(filePath)) || undefined;
49
+ const date =
50
+ frontmatter.date ||
51
+ gitDates?.get(filePath) ||
52
+ (await getGitLastModified(filePath)) ||
53
+ undefined;
44
54
 
45
55
  return {
46
56
  content,
@@ -0,0 +1,10 @@
1
+ import type { TocItem } from "./types";
2
+
3
+ export function safeParseTocs(raw: string | undefined): TocItem[] {
4
+ try {
5
+ const parsed = JSON.parse(raw || "[]");
6
+ return Array.isArray(parsed) ? parsed : [];
7
+ } catch {
8
+ return [];
9
+ }
10
+ }
@@ -13,6 +13,7 @@
13
13
 
14
14
  import { readFile, writeFile, readdir, mkdir } from "node:fs/promises";
15
15
  import { resolve, join } from "node:path";
16
+ import { extractFrontmatterWithContent } from "@docubook/core";
16
17
  import { DOCS_DIR, ASSETS_DIR, loadDocuConfig } from "./paths";
17
18
 
18
19
  const docuConfig = loadDocuConfig();
@@ -37,25 +38,6 @@ interface Frontmatter {
37
38
  description?: string;
38
39
  }
39
40
 
40
- function parseFrontmatter(raw: string): { frontmatter: Frontmatter; content: string } {
41
- if (!raw.startsWith("---")) return { frontmatter: {}, content: raw };
42
- const end = raw.indexOf("---", 3);
43
- if (end < 0) return { frontmatter: {}, content: raw };
44
-
45
- const fm: Frontmatter = {};
46
- const lines = raw.slice(3, end).trim().split("\n");
47
- for (const line of lines) {
48
- const match = line.match(/^(\w+):\s*(.+)$/);
49
- if (match) {
50
- const [, key, value] = match;
51
- if (key === "title" || key === "description") {
52
- fm[key] = value.trim();
53
- }
54
- }
55
- }
56
- return { frontmatter: fm, content: raw.slice(end + 3) };
57
- }
58
-
59
41
  function getSectionTitle(filePath: string): string {
60
42
  const parts = filePath.split("/");
61
43
  if (parts.length > 1) {
@@ -74,8 +56,20 @@ function slugify(text: string): string {
74
56
  .replace(/^-+|-+$/g, "");
75
57
  }
76
58
 
59
+ function stripJsx(content: string): string {
60
+ let result = content;
61
+ let prev = "";
62
+ while (result !== prev) {
63
+ prev = result;
64
+ result = result
65
+ .replace(/<[A-Z][\w.]*[\s\S]*?\/>/g, "")
66
+ .replace(/<[A-Z][\w.]*[\s\S]*?>([\s\S]*?)<\/[A-Z][\w.]*>/g, "$1");
67
+ }
68
+ return result;
69
+ }
70
+
77
71
  function extractRecords(filePath: string, raw: string): SearchRecord[] {
78
- const { frontmatter, content } = parseFrontmatter(raw);
72
+ const { frontmatter, strippedContent: content } = extractFrontmatterWithContent<Frontmatter>(raw);
79
73
  const records: SearchRecord[] = [];
80
74
  const url = `/docs/${filePath}`;
81
75
  const lvl0 = getSectionTitle(filePath);
@@ -100,7 +94,8 @@ function extractRecords(filePath: string, raw: string): SearchRecord[] {
100
94
  });
101
95
  }
102
96
 
103
- const lines = content.split("\n");
97
+ const plainContent = stripJsx(content);
98
+ const lines = plainContent.split("\n");
104
99
  let currentParagraph: string[] = [];
105
100
  let inCodeBlock = false;
106
101
 
@@ -137,7 +132,6 @@ function extractRecords(filePath: string, raw: string): SearchRecord[] {
137
132
  if (inCodeBlock) continue;
138
133
 
139
134
  if (/^import\s+[\w{*]/.test(trimmed) || /^export\s+[\w{*]/.test(trimmed)) continue;
140
- if (/^<\/?[A-Z][\w.]*[\s/>]/.test(trimmed) && !/>(.+)<\//.test(trimmed)) continue;
141
135
 
142
136
  const headingMatch = trimmed.match(/^(#{1,6})\s+(.+)$/);
143
137
  if (headingMatch) {
@@ -170,6 +164,8 @@ function extractRecords(filePath: string, raw: string): SearchRecord[] {
170
164
  continue;
171
165
  }
172
166
 
167
+ if (/^\|.+\|/.test(trimmed)) continue;
168
+
173
169
  const cleaned = trimmed
174
170
  .replace(/^[-*+]\s+/, "") // list markers
175
171
  .replace(/^\d+\.\s+/, "") // ordered list
@@ -16,7 +16,7 @@ import { generateSearchIndex } from "./search-indexer";
16
16
  import { logger } from "./logger";
17
17
  import { initSentry, captureException } from "./sentry";
18
18
  import { SECURITY_HEADERS, generateNonce, htmlResponse } from "./security";
19
- import { htmlShell as createHtmlShell } from "./html";
19
+ import { htmlShell as createHtmlShell, hmrScript } from "./html";
20
20
 
21
21
  const docuConfig = loadDocuConfig();
22
22
 
@@ -50,18 +50,6 @@ try {
50
50
 
51
51
  const hmrClients = new Set<ReadableStreamDefaultController>();
52
52
 
53
- function hmrScript(nonce: string): string {
54
- return `<script nonce="${Bun.escapeHTML(nonce)}">
55
- (function(){
56
- const es = new EventSource("/__hmr");
57
- es.onmessage = function(e) {
58
- if (e.data === "reload") window.location.reload();
59
- };
60
- es.onerror = function() { es.close(); setTimeout(() => { window.location.reload(); }, 2000); };
61
- })();
62
- </script>`;
63
- }
64
-
65
53
  let hmrTimeout: ReturnType<typeof setTimeout> | null = null;
66
54
  const watcher = watch(DOCS_DIR, { recursive: true }, (_event, filename) => {
67
55
  if (!filename || (!filename.endsWith(".mdx") && !filename.endsWith(".md"))) return;