@docubook/flame 2.0.0-alpha.0 → 2.0.0-alpha.2
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/.docu/components/Menu.tsx +38 -32
- package/.docu/components/Pagination.tsx +9 -1
- package/.docu/components/Sublink.tsx +72 -9
- package/.docu/components/Toc.tsx +3 -3
- package/.docu/lib/build.deno.js +3 -3
- package/.docu/lib/{build.impl-WR24HDUT.js → build.impl-AZBZN66Q.js} +3 -3
- package/.docu/lib/build.node.js +3 -3
- package/.docu/lib/{chunk-2EYRWRDP.js → chunk-3LRUTZZD.js} +6 -3
- package/.docu/lib/{chunk-QPF4DPZQ.js → chunk-42JQLAKP.js} +100 -20
- package/.docu/lib/{chunk-VPV7KP7K.js → chunk-HF3KKRQL.js} +7 -3
- package/.docu/lib/{chunk-AJV2GEDF.js → chunk-IN2QAGDZ.js} +1 -1
- package/.docu/lib/{chunk-KEWRVASF.js → chunk-PGIHPW5L.js} +1 -1
- package/.docu/lib/{chunk-X7KPVCDN.js → chunk-PJVCY4FK.js} +1 -1
- package/.docu/lib/clean.js +1 -1
- package/.docu/lib/deploy.deno.js +1 -1
- package/.docu/lib/deploy.node.js +1 -1
- package/.docu/lib/preview.deno.js +2 -2
- package/.docu/lib/preview.node.js +2 -2
- package/.docu/lib/server.deno.js +3 -3
- package/.docu/lib/server.node.js +3 -3
- package/.docu/node/build.impl.ts +3 -0
- package/.docu/node/build.ts +3 -0
- package/.docu/node/html.shared.ts +7 -2
- package/.docu/node/html.ts +4 -2
- package/.docu/node/route.ts +57 -1
- package/.docu/node/server-routes.ts +4 -0
- package/.docu/styles/globals.css +9 -0
- package/package.json +5 -5
- package/template/README.md +1 -1
- package/template/docs/getting-started/configuration.mdx +16 -4
- package/template/docs/getting-started/overview.mdx +34 -42
- package/template/docs/guide/components.mdx +46 -228
- package/template/docs/guide/routing.mdx +8 -6
- package/template/docs/index.mdx +9 -8
- package/template/docu.json +4 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { useState } from "react";
|
|
4
|
-
import Sublink from "./Sublink";
|
|
4
|
+
import Sublink, { GroupAccordionProvider } from "./Sublink";
|
|
5
5
|
import SidebarGroupHeader from "./SidebarGroupHeader";
|
|
6
6
|
import type { DocuRoute } from "../node/types";
|
|
7
7
|
import { cn } from "../node/utils";
|
|
@@ -76,28 +76,32 @@ export default function Menu({ onNavigate, className = "", pathname, routes = []
|
|
|
76
76
|
// No context routes defined — fall back to flat list of all routes
|
|
77
77
|
if (contextRoutes.length === 0) {
|
|
78
78
|
return (
|
|
79
|
-
<
|
|
80
|
-
<
|
|
81
|
-
{
|
|
82
|
-
|
|
83
|
-
|
|
79
|
+
<GroupAccordionProvider>
|
|
80
|
+
<nav {...navProps}>
|
|
81
|
+
<ul className={sharedUlClasses}>
|
|
82
|
+
{menuRoutes.map((route) => renderBorderItem(route, "", route.href))}
|
|
83
|
+
</ul>
|
|
84
|
+
</nav>
|
|
85
|
+
</GroupAccordionProvider>
|
|
84
86
|
);
|
|
85
87
|
}
|
|
86
88
|
|
|
87
89
|
return (
|
|
88
|
-
<
|
|
89
|
-
{
|
|
90
|
-
|
|
91
|
-
<
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
{
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
90
|
+
<GroupAccordionProvider>
|
|
91
|
+
<nav {...navProps}>
|
|
92
|
+
{contextRoutes.map((route, i) => (
|
|
93
|
+
<div key={route.href} className={i > 0 ? "mt-6 lg:mt-8" : ""}>
|
|
94
|
+
<SidebarGroupHeader
|
|
95
|
+
icon={route.context?.icon}
|
|
96
|
+
title={route.context?.title || route.title}
|
|
97
|
+
/>
|
|
98
|
+
<ul className={sharedUlClasses}>
|
|
99
|
+
{route.items?.map((item) => renderBorderItem(item, route.href, item.href))}
|
|
100
|
+
</ul>
|
|
101
|
+
</div>
|
|
102
|
+
))}
|
|
103
|
+
</nav>
|
|
104
|
+
</GroupAccordionProvider>
|
|
101
105
|
);
|
|
102
106
|
}
|
|
103
107
|
|
|
@@ -119,18 +123,20 @@ export default function Menu({ onNavigate, className = "", pathname, routes = []
|
|
|
119
123
|
if (!contextRoute) return null;
|
|
120
124
|
|
|
121
125
|
return (
|
|
122
|
-
<
|
|
123
|
-
<
|
|
124
|
-
<
|
|
125
|
-
<
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
126
|
+
<GroupAccordionProvider>
|
|
127
|
+
<nav {...navProps}>
|
|
128
|
+
<ul className="flex flex-col gap-0.5 py-4">
|
|
129
|
+
<li key={contextRoute.title}>
|
|
130
|
+
<Sublink
|
|
131
|
+
{...contextRoute}
|
|
132
|
+
href={contextRoute.href}
|
|
133
|
+
level={0}
|
|
134
|
+
onNavigate={onNavigate}
|
|
135
|
+
parentHref="/docs"
|
|
136
|
+
/>
|
|
137
|
+
</li>
|
|
138
|
+
</ul>
|
|
139
|
+
</nav>
|
|
140
|
+
</GroupAccordionProvider>
|
|
135
141
|
);
|
|
136
142
|
}
|
|
@@ -29,7 +29,15 @@ export default function Pagination({
|
|
|
29
29
|
return (
|
|
30
30
|
<PaginationDocs
|
|
31
31
|
prev={prev ? { href: docsHtmlHref(`/docs${prev.href}`), title: prev.title } : undefined}
|
|
32
|
-
next={
|
|
32
|
+
next={
|
|
33
|
+
next
|
|
34
|
+
? {
|
|
35
|
+
href: docsHtmlHref(`/docs${next.href}`),
|
|
36
|
+
title: next.title,
|
|
37
|
+
description: next.description,
|
|
38
|
+
}
|
|
39
|
+
: undefined
|
|
40
|
+
}
|
|
33
41
|
className={className}
|
|
34
42
|
prevIcon={prevIcon}
|
|
35
43
|
nextIcon={nextIcon}
|
|
@@ -1,12 +1,43 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
createContext,
|
|
5
|
+
useContext,
|
|
6
|
+
useState,
|
|
7
|
+
useRef,
|
|
8
|
+
useEffect,
|
|
9
|
+
useCallback,
|
|
10
|
+
type ReactNode,
|
|
11
|
+
} from "react";
|
|
4
12
|
import { ChevronDown } from "lucide-react";
|
|
5
13
|
import Anchor from "./Anchor";
|
|
6
14
|
import type { DocuRoute } from "../node/types";
|
|
7
15
|
import { cn, docsHtmlHref } from "../node/utils";
|
|
8
16
|
import { config as docuConfig } from "../node/client-routes";
|
|
9
17
|
|
|
18
|
+
/** Exclusive accordion for level >= 2 sidebar groups — opening one group
|
|
19
|
+
* closes the previously open one. All level >= 2 groups default to closed
|
|
20
|
+
* and expand only when the header is clicked. `open` is used to auto-expand
|
|
21
|
+
* the group containing the active page. */
|
|
22
|
+
export const GroupAccordionContext = createContext<{
|
|
23
|
+
openId: string | null;
|
|
24
|
+
open: (id: string) => void;
|
|
25
|
+
toggle: (id: string) => void;
|
|
26
|
+
}>({ openId: null, open: () => {}, toggle: () => {} });
|
|
27
|
+
|
|
28
|
+
export function GroupAccordionProvider({ children }: { children: ReactNode }) {
|
|
29
|
+
const [openId, setOpenId] = useState<string | null>(null);
|
|
30
|
+
// Exclusive: expanding B closes A — only one group stays open at a time;
|
|
31
|
+
// clicking the open group again collapses it.
|
|
32
|
+
const open = useCallback((id: string) => setOpenId(id), []);
|
|
33
|
+
const toggle = useCallback((id: string) => setOpenId((prev) => (prev === id ? null : id)), []);
|
|
34
|
+
return (
|
|
35
|
+
<GroupAccordionContext.Provider value={{ openId, open, toggle }}>
|
|
36
|
+
{children}
|
|
37
|
+
</GroupAccordionContext.Provider>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
10
41
|
interface SublinkProps extends DocuRoute {
|
|
11
42
|
level: number;
|
|
12
43
|
onNavigate?: () => void;
|
|
@@ -28,12 +59,39 @@ export default function Sublink({
|
|
|
28
59
|
const currentPathname =
|
|
29
60
|
pathnameProp || (typeof window !== "undefined" ? window.location.pathname : "/docs");
|
|
30
61
|
|
|
62
|
+
// Groups with children are exclusive accordions (default closed, expand on
|
|
63
|
+
// click). In separator mode every nav item renders at level 0 (sections are
|
|
64
|
+
// SidebarGroupHeader, not Sublinks), so depth can't tell them apart — the
|
|
65
|
+
// mode does. In dropdown mode the top section is level 0 (stays open) and
|
|
66
|
+
// everything deeper is an accordion.
|
|
67
|
+
const isSeparator = docuConfig.sidebar?.context === "separator";
|
|
68
|
+
const { openId, open, toggle } = useContext(GroupAccordionContext);
|
|
69
|
+
const isAccordionGroup = Boolean(items) && (isSeparator || level >= 1);
|
|
70
|
+
// Routes-tree level: separator mode renders every item at level 0 (sections
|
|
71
|
+
// are SidebarGroupHeader), dropdown starts the context section at level 0.
|
|
72
|
+
const treeLevel = level + (isSeparator ? 2 : 1);
|
|
73
|
+
|
|
31
74
|
const [isOpen, setIsOpen] = useState(() => {
|
|
32
|
-
if (
|
|
33
|
-
if (
|
|
34
|
-
return
|
|
75
|
+
if (isAccordionGroup) return false; // default closed — context controls it
|
|
76
|
+
if (level === 0) return true; // top-level section stays open
|
|
77
|
+
return false; // leaves
|
|
35
78
|
});
|
|
36
79
|
|
|
80
|
+
const effectiveOpen = isAccordionGroup ? openId === fullHref : isOpen;
|
|
81
|
+
const handleToggle = () => {
|
|
82
|
+
if (isAccordionGroup) toggle(fullHref);
|
|
83
|
+
else setIsOpen((o) => !o);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// Auto-expand the accordion group that contains the active page — on mount
|
|
87
|
+
// and whenever the current path changes. `open` is stable (useCallback), so
|
|
88
|
+
// this only fires on real path changes; a manual collapse by the user is
|
|
89
|
+
// respected until the path changes again.
|
|
90
|
+
const isInsideActive = currentPathname.startsWith(fullHref) && currentPathname !== fullHref;
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
if (isAccordionGroup && isInsideActive) open(fullHref);
|
|
93
|
+
}, [isAccordionGroup, isInsideActive, fullHref, open]);
|
|
94
|
+
|
|
37
95
|
// Shared padding based on nesting level
|
|
38
96
|
const levelPadding = cn(level === 1 && "pl-4", level === 2 && "pl-8", level >= 3 && "pl-12");
|
|
39
97
|
const isActive = currentPathname === fullHref || currentPathname === `${fullHref}.html`;
|
|
@@ -113,12 +171,15 @@ export default function Sublink({
|
|
|
113
171
|
{/* Section header */}
|
|
114
172
|
<button
|
|
115
173
|
type="button"
|
|
116
|
-
onClick={
|
|
174
|
+
onClick={handleToggle}
|
|
117
175
|
className={cn(
|
|
118
176
|
"flex w-full cursor-pointer items-center justify-between py-1 text-left text-sm transition-colors",
|
|
119
|
-
|
|
177
|
+
// Only the top-level section label (routes-tree level 1) is bold.
|
|
178
|
+
// Deeper groups (e.g. Search at level 2) are children that happen to
|
|
179
|
+
// have items — style them like links, no header weight.
|
|
180
|
+
noLink && treeLevel === 1
|
|
120
181
|
? "text-base-content font-semibold"
|
|
121
|
-
: "text-
|
|
182
|
+
: "text-foreground hover:text-foreground/80"
|
|
122
183
|
)}
|
|
123
184
|
>
|
|
124
185
|
{noLink ? (
|
|
@@ -137,13 +198,15 @@ export default function Sublink({
|
|
|
137
198
|
<ChevronDown
|
|
138
199
|
className={cn(
|
|
139
200
|
"text-base-content/40 h-4 w-4 shrink-0 transition-transform duration-200",
|
|
140
|
-
|
|
201
|
+
// Tree convention: closed = chevron pointing right (expandable),
|
|
202
|
+
// open = pointing down — a 90° turn instead of the 180° flip.
|
|
203
|
+
effectiveOpen ? "rotate-0" : "-rotate-90"
|
|
141
204
|
)}
|
|
142
205
|
/>
|
|
143
206
|
</button>
|
|
144
207
|
|
|
145
208
|
{/* Children */}
|
|
146
|
-
{
|
|
209
|
+
{effectiveOpen && (
|
|
147
210
|
<div className="flex flex-col py-1">
|
|
148
211
|
{items.map((item) => (
|
|
149
212
|
<Sublink
|
package/.docu/components/Toc.tsx
CHANGED
|
@@ -25,12 +25,12 @@ export default function Toc({ tocs }: TocProps) {
|
|
|
25
25
|
// CSS scroll-margin-top (prose-headings:scroll-mt-4 desktop /
|
|
26
26
|
// scroll-mt-16 mobile) so a change in one place updates both, and the
|
|
27
27
|
// heading lands exactly where the browser would put a native anchor jump.
|
|
28
|
-
|
|
28
|
+
const getAnchorOffset = useCallback((): number => {
|
|
29
29
|
if (typeof window === "undefined") return 100;
|
|
30
30
|
const first = tocs[0] ? document.getElementById(tocs[0].href.slice(1)) : null;
|
|
31
31
|
const margin = first ? parseFloat(getComputedStyle(first).scrollMarginTop) : 0;
|
|
32
32
|
return margin || 100;
|
|
33
|
-
}
|
|
33
|
+
}, [tocs]);
|
|
34
34
|
|
|
35
35
|
useEffect(() => {
|
|
36
36
|
// Desktop-only TOC — on mobile this island is display:none but still
|
|
@@ -88,7 +88,7 @@ export default function Toc({ tocs }: TocProps) {
|
|
|
88
88
|
scrollTarget.removeEventListener("scroll", listener);
|
|
89
89
|
if (throttleTimer) clearTimeout(throttleTimer);
|
|
90
90
|
};
|
|
91
|
-
}, [tocs]);
|
|
91
|
+
}, [tocs, getAnchorOffset]);
|
|
92
92
|
|
|
93
93
|
const handleLinkClick = useCallback((id: string) => {
|
|
94
94
|
clickedIdRef.current = id;
|
package/.docu/lib/build.deno.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
runBuildCli
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-3LRUTZZD.js";
|
|
4
|
+
import "./chunk-42JQLAKP.js";
|
|
5
5
|
import "./chunk-EOK6KATZ.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-IN2QAGDZ.js";
|
|
7
7
|
import "./chunk-4IQXHHPF.js";
|
|
8
8
|
|
|
9
9
|
// .docu/node/build.deno.ts
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
runBuild,
|
|
3
3
|
runBuildCli
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-3LRUTZZD.js";
|
|
5
|
+
import "./chunk-42JQLAKP.js";
|
|
6
6
|
import "./chunk-EOK6KATZ.js";
|
|
7
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-IN2QAGDZ.js";
|
|
8
8
|
import "./chunk-4IQXHHPF.js";
|
|
9
9
|
export {
|
|
10
10
|
runBuild,
|
package/.docu/lib/build.node.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
runBuildCli
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-3LRUTZZD.js";
|
|
4
|
+
import "./chunk-42JQLAKP.js";
|
|
5
5
|
import "./chunk-EOK6KATZ.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-IN2QAGDZ.js";
|
|
7
7
|
import "./chunk-4IQXHHPF.js";
|
|
8
8
|
|
|
9
9
|
// .docu/node/build.node.ts
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
htmlShell,
|
|
16
16
|
initSentry,
|
|
17
17
|
loadPlugins
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-42JQLAKP.js";
|
|
19
19
|
import {
|
|
20
20
|
cspHeader,
|
|
21
21
|
generateNonce,
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
} from "./chunk-EOK6KATZ.js";
|
|
24
24
|
import {
|
|
25
25
|
logger
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-IN2QAGDZ.js";
|
|
27
27
|
import {
|
|
28
28
|
ASSETS_DIR,
|
|
29
29
|
CACHE_FILE,
|
|
@@ -398,7 +398,10 @@ async function runBuild() {
|
|
|
398
398
|
css: assetManifest.css,
|
|
399
399
|
js: assetManifest.js,
|
|
400
400
|
nonce: notFoundNonce,
|
|
401
|
-
themeCss: inlineThemeCss
|
|
401
|
+
themeCss: inlineThemeCss,
|
|
402
|
+
// Served as the static-host fallback at ANY requested path — relative
|
|
403
|
+
// depth can never be right there, so use root-absolute asset URLs.
|
|
404
|
+
absoluteAssets: true
|
|
402
405
|
});
|
|
403
406
|
await writeFile(join(DIST_DIR, "404.html"), notFoundHtml);
|
|
404
407
|
logger.spinner.stop(
|
|
@@ -1333,6 +1333,9 @@ function DocsBreadcrumb({ paths }) {
|
|
|
1333
1333
|
}
|
|
1334
1334
|
|
|
1335
1335
|
// .docu/node/route.ts
|
|
1336
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
1337
|
+
import { join as join4 } from "node:path";
|
|
1338
|
+
import { extractFrontmatter } from "@docubook/core";
|
|
1336
1339
|
var docuConfig2 = loadDocuConfig();
|
|
1337
1340
|
var routes = resolveRoutes(docuConfig2.routes);
|
|
1338
1341
|
function flattenRoutes() {
|
|
@@ -1361,8 +1364,42 @@ function getRouteMap() {
|
|
|
1361
1364
|
routes.forEach((route) => traverse(route));
|
|
1362
1365
|
return map;
|
|
1363
1366
|
}
|
|
1367
|
+
var descriptionCache = /* @__PURE__ */ new Map();
|
|
1368
|
+
function readDescription(href) {
|
|
1369
|
+
const cached = descriptionCache.get(href);
|
|
1370
|
+
if (cached !== void 0) return cached;
|
|
1371
|
+
let description = "";
|
|
1372
|
+
const rel = href.replace(/^\/|$/g, "");
|
|
1373
|
+
for (const ext of [".mdx", ".md"]) {
|
|
1374
|
+
for (const file of [join4(DOCS_DIR, `${rel}${ext}`), join4(DOCS_DIR, `${rel}/index${ext}`)]) {
|
|
1375
|
+
try {
|
|
1376
|
+
const fm = extractFrontmatter(readFileSync2(file, "utf-8"));
|
|
1377
|
+
description = typeof fm.description === "string" ? fm.description : "";
|
|
1378
|
+
if (description) break;
|
|
1379
|
+
} catch {
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
if (description) break;
|
|
1383
|
+
}
|
|
1384
|
+
descriptionCache.set(href, description);
|
|
1385
|
+
return description;
|
|
1386
|
+
}
|
|
1364
1387
|
function getPreviousNext(pathname) {
|
|
1365
1388
|
const normalizedPath = pathname.replace(/^\/|$/g, "");
|
|
1389
|
+
if (normalizedPath === "docs" || normalizedPath === "") {
|
|
1390
|
+
const paths2 = flattenRoutes();
|
|
1391
|
+
const routeMap2 = getRouteMap();
|
|
1392
|
+
const first = paths2[0];
|
|
1393
|
+
if (!first) return { prev: null, next: null };
|
|
1394
|
+
return {
|
|
1395
|
+
prev: null,
|
|
1396
|
+
next: {
|
|
1397
|
+
href: first,
|
|
1398
|
+
title: routeMap2.get(first) || "",
|
|
1399
|
+
description: readDescription(first)
|
|
1400
|
+
}
|
|
1401
|
+
};
|
|
1402
|
+
}
|
|
1366
1403
|
const paths = flattenRoutes();
|
|
1367
1404
|
const index = paths.findIndex((href) => href === `/${normalizedPath}` || href === normalizedPath);
|
|
1368
1405
|
if (index === -1) {
|
|
@@ -1373,7 +1410,11 @@ function getPreviousNext(pathname) {
|
|
|
1373
1410
|
const nextHref = index < paths.length - 1 ? paths[index + 1] : null;
|
|
1374
1411
|
return {
|
|
1375
1412
|
prev: prevHref ? { href: prevHref, title: routeMap.get(prevHref) || "" } : null,
|
|
1376
|
-
next: nextHref ? {
|
|
1413
|
+
next: nextHref ? {
|
|
1414
|
+
href: nextHref,
|
|
1415
|
+
title: routeMap.get(nextHref) || "",
|
|
1416
|
+
description: readDescription(nextHref)
|
|
1417
|
+
} : null
|
|
1377
1418
|
};
|
|
1378
1419
|
}
|
|
1379
1420
|
|
|
@@ -1395,7 +1436,11 @@ function Pagination({
|
|
|
1395
1436
|
PaginationDocs,
|
|
1396
1437
|
{
|
|
1397
1438
|
prev: prev ? { href: docsHtmlHref(`/docs${prev.href}`), title: prev.title } : void 0,
|
|
1398
|
-
next: next ? {
|
|
1439
|
+
next: next ? {
|
|
1440
|
+
href: docsHtmlHref(`/docs${next.href}`),
|
|
1441
|
+
title: next.title,
|
|
1442
|
+
description: next.description
|
|
1443
|
+
} : void 0,
|
|
1399
1444
|
className,
|
|
1400
1445
|
prevIcon,
|
|
1401
1446
|
nextIcon,
|
|
@@ -1671,12 +1716,12 @@ function Toc({ tocs }) {
|
|
|
1671
1716
|
useEffect2(() => {
|
|
1672
1717
|
activeIdRef.current = activeId;
|
|
1673
1718
|
}, [activeId]);
|
|
1674
|
-
|
|
1719
|
+
const getAnchorOffset = useCallback2(() => {
|
|
1675
1720
|
if (typeof window === "undefined") return 100;
|
|
1676
1721
|
const first = tocs[0] ? document.getElementById(tocs[0].href.slice(1)) : null;
|
|
1677
1722
|
const margin = first ? parseFloat(getComputedStyle(first).scrollMarginTop) : 0;
|
|
1678
1723
|
return margin || 100;
|
|
1679
|
-
}
|
|
1724
|
+
}, [tocs]);
|
|
1680
1725
|
useEffect2(() => {
|
|
1681
1726
|
if (typeof window === "undefined" || window.innerWidth < 1024) return;
|
|
1682
1727
|
if (!tocs.length) return;
|
|
@@ -1717,7 +1762,7 @@ function Toc({ tocs }) {
|
|
|
1717
1762
|
scrollTarget.removeEventListener("scroll", listener);
|
|
1718
1763
|
if (throttleTimer) clearTimeout(throttleTimer);
|
|
1719
1764
|
};
|
|
1720
|
-
}, [tocs]);
|
|
1765
|
+
}, [tocs, getAnchorOffset]);
|
|
1721
1766
|
const handleLinkClick = useCallback2((id) => {
|
|
1722
1767
|
clickedIdRef.current = id;
|
|
1723
1768
|
setActiveId(id);
|
|
@@ -2116,7 +2161,14 @@ import React2 from "react";
|
|
|
2116
2161
|
import { useState as useState4 } from "react";
|
|
2117
2162
|
|
|
2118
2163
|
// .docu/components/Sublink.tsx
|
|
2119
|
-
import {
|
|
2164
|
+
import {
|
|
2165
|
+
createContext,
|
|
2166
|
+
useContext,
|
|
2167
|
+
useState as useState3,
|
|
2168
|
+
useRef as useRef2,
|
|
2169
|
+
useEffect as useEffect3,
|
|
2170
|
+
useCallback as useCallback3
|
|
2171
|
+
} from "react";
|
|
2120
2172
|
import { ChevronDown } from "lucide-react";
|
|
2121
2173
|
|
|
2122
2174
|
// .docu/components/Anchor.tsx
|
|
@@ -2167,6 +2219,15 @@ var config = docuConfig5;
|
|
|
2167
2219
|
|
|
2168
2220
|
// .docu/components/Sublink.tsx
|
|
2169
2221
|
import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2222
|
+
var GroupAccordionContext = createContext({ openId: null, open: () => {
|
|
2223
|
+
}, toggle: () => {
|
|
2224
|
+
} });
|
|
2225
|
+
function GroupAccordionProvider({ children }) {
|
|
2226
|
+
const [openId, setOpenId] = useState3(null);
|
|
2227
|
+
const open = useCallback3((id) => setOpenId(id), []);
|
|
2228
|
+
const toggle = useCallback3((id) => setOpenId((prev) => prev === id ? null : id), []);
|
|
2229
|
+
return /* @__PURE__ */ jsx16(GroupAccordionContext.Provider, { value: { openId, open, toggle }, children });
|
|
2230
|
+
}
|
|
2170
2231
|
function Sublink({
|
|
2171
2232
|
title,
|
|
2172
2233
|
href,
|
|
@@ -2179,11 +2240,24 @@ function Sublink({
|
|
|
2179
2240
|
}) {
|
|
2180
2241
|
const fullHref = parentHref ? `${parentHref}${href}` : `/docs${href}`;
|
|
2181
2242
|
const currentPathname = pathnameProp || (typeof window !== "undefined" ? window.location.pathname : "/docs");
|
|
2243
|
+
const isSeparator = config.sidebar?.context === "separator";
|
|
2244
|
+
const { openId, open, toggle } = useContext(GroupAccordionContext);
|
|
2245
|
+
const isAccordionGroup = Boolean(items) && (isSeparator || level >= 1);
|
|
2246
|
+
const treeLevel = level + (isSeparator ? 2 : 1);
|
|
2182
2247
|
const [isOpen, setIsOpen] = useState3(() => {
|
|
2248
|
+
if (isAccordionGroup) return false;
|
|
2183
2249
|
if (level === 0) return true;
|
|
2184
|
-
|
|
2185
|
-
return currentPathname.startsWith(fullHref) && currentPathname !== fullHref;
|
|
2250
|
+
return false;
|
|
2186
2251
|
});
|
|
2252
|
+
const effectiveOpen = isAccordionGroup ? openId === fullHref : isOpen;
|
|
2253
|
+
const handleToggle = () => {
|
|
2254
|
+
if (isAccordionGroup) toggle(fullHref);
|
|
2255
|
+
else setIsOpen((o) => !o);
|
|
2256
|
+
};
|
|
2257
|
+
const isInsideActive = currentPathname.startsWith(fullHref) && currentPathname !== fullHref;
|
|
2258
|
+
useEffect3(() => {
|
|
2259
|
+
if (isAccordionGroup && isInsideActive) open(fullHref);
|
|
2260
|
+
}, [isAccordionGroup, isInsideActive, fullHref, open]);
|
|
2187
2261
|
const levelPadding = cn(level === 1 && "pl-4", level === 2 && "pl-8", level >= 3 && "pl-12");
|
|
2188
2262
|
const isActive = currentPathname === fullHref || currentPathname === `${fullHref}.html`;
|
|
2189
2263
|
const activeRef = useRef2(null);
|
|
@@ -2242,10 +2316,13 @@ function Sublink({
|
|
|
2242
2316
|
"button",
|
|
2243
2317
|
{
|
|
2244
2318
|
type: "button",
|
|
2245
|
-
onClick:
|
|
2319
|
+
onClick: handleToggle,
|
|
2246
2320
|
className: cn(
|
|
2247
2321
|
"flex w-full cursor-pointer items-center justify-between py-1 text-left text-sm transition-colors",
|
|
2248
|
-
|
|
2322
|
+
// Only the top-level section label (routes-tree level 1) is bold.
|
|
2323
|
+
// Deeper groups (e.g. Search at level 2) are children that happen to
|
|
2324
|
+
// have items — style them like links, no header weight.
|
|
2325
|
+
noLink && treeLevel === 1 ? "text-base-content font-semibold" : "text-foreground hover:text-foreground/80"
|
|
2249
2326
|
),
|
|
2250
2327
|
children: [
|
|
2251
2328
|
noLink ? /* @__PURE__ */ jsx16("span", { children: title }) : /* @__PURE__ */ jsx16(
|
|
@@ -2264,14 +2341,16 @@ function Sublink({
|
|
|
2264
2341
|
{
|
|
2265
2342
|
className: cn(
|
|
2266
2343
|
"text-base-content/40 h-4 w-4 shrink-0 transition-transform duration-200",
|
|
2267
|
-
|
|
2344
|
+
// Tree convention: closed = chevron pointing right (expandable),
|
|
2345
|
+
// open = pointing down — a 90° turn instead of the 180° flip.
|
|
2346
|
+
effectiveOpen ? "rotate-0" : "-rotate-90"
|
|
2268
2347
|
)
|
|
2269
2348
|
}
|
|
2270
2349
|
)
|
|
2271
2350
|
]
|
|
2272
2351
|
}
|
|
2273
2352
|
),
|
|
2274
|
-
|
|
2353
|
+
effectiveOpen && /* @__PURE__ */ jsx16("div", { className: "flex flex-col py-1", children: items.map((item) => /* @__PURE__ */ jsx16(
|
|
2275
2354
|
Sublink,
|
|
2276
2355
|
{
|
|
2277
2356
|
...item,
|
|
@@ -2346,9 +2425,9 @@ function Menu({ onNavigate, className = "", pathname, routes: routes3 = [] }) {
|
|
|
2346
2425
|
if (mode === "separator") {
|
|
2347
2426
|
const contextRoutes = menuRoutes.filter((r) => r.context);
|
|
2348
2427
|
if (contextRoutes.length === 0) {
|
|
2349
|
-
return /* @__PURE__ */ jsx18("nav", { ...navProps, children: /* @__PURE__ */ jsx18("ul", { className: sharedUlClasses, children: menuRoutes.map((route) => renderBorderItem(route, "", route.href)) }) });
|
|
2428
|
+
return /* @__PURE__ */ jsx18(GroupAccordionProvider, { children: /* @__PURE__ */ jsx18("nav", { ...navProps, children: /* @__PURE__ */ jsx18("ul", { className: sharedUlClasses, children: menuRoutes.map((route) => renderBorderItem(route, "", route.href)) }) }) });
|
|
2350
2429
|
}
|
|
2351
|
-
return /* @__PURE__ */ jsx18("nav", { ...navProps, children: contextRoutes.map((route, i) => /* @__PURE__ */ jsxs14("div", { className: i > 0 ? "mt-6 lg:mt-8" : "", children: [
|
|
2430
|
+
return /* @__PURE__ */ jsx18(GroupAccordionProvider, { children: /* @__PURE__ */ jsx18("nav", { ...navProps, children: contextRoutes.map((route, i) => /* @__PURE__ */ jsxs14("div", { className: i > 0 ? "mt-6 lg:mt-8" : "", children: [
|
|
2352
2431
|
/* @__PURE__ */ jsx18(
|
|
2353
2432
|
SidebarGroupHeader,
|
|
2354
2433
|
{
|
|
@@ -2357,13 +2436,13 @@ function Menu({ onNavigate, className = "", pathname, routes: routes3 = [] }) {
|
|
|
2357
2436
|
}
|
|
2358
2437
|
),
|
|
2359
2438
|
/* @__PURE__ */ jsx18("ul", { className: sharedUlClasses, children: route.items?.map((item) => renderBorderItem(item, route.href, item.href)) })
|
|
2360
|
-
] }, route.href)) });
|
|
2439
|
+
] }, route.href)) }) });
|
|
2361
2440
|
}
|
|
2362
2441
|
const isDocsRoot = currentPath === "/docs" || currentPath === "/docs/";
|
|
2363
2442
|
const currentContext = isDocsRoot ? menuRoutes[0]?.href.replace(/^\/+|\/+$/, "") : getCurrentContext(currentPath);
|
|
2364
2443
|
const contextRoute = isDocsRoot && menuRoutes[0] ? currentContext ? getContextRoute(currentContext, menuRoutes) : menuRoutes[0] : currentContext ? getContextRoute(currentContext, menuRoutes) : void 0;
|
|
2365
2444
|
if (!contextRoute) return null;
|
|
2366
|
-
return /* @__PURE__ */ jsx18("nav", { ...navProps, children: /* @__PURE__ */ jsx18("ul", { className: "flex flex-col gap-0.5 py-4", children: /* @__PURE__ */ jsx18("li", { children: /* @__PURE__ */ jsx18(
|
|
2445
|
+
return /* @__PURE__ */ jsx18(GroupAccordionProvider, { children: /* @__PURE__ */ jsx18("nav", { ...navProps, children: /* @__PURE__ */ jsx18("ul", { className: "flex flex-col gap-0.5 py-4", children: /* @__PURE__ */ jsx18("li", { children: /* @__PURE__ */ jsx18(
|
|
2367
2446
|
Sublink,
|
|
2368
2447
|
{
|
|
2369
2448
|
...contextRoute,
|
|
@@ -2372,7 +2451,7 @@ function Menu({ onNavigate, className = "", pathname, routes: routes3 = [] }) {
|
|
|
2372
2451
|
onNavigate,
|
|
2373
2452
|
parentHref: "/docs"
|
|
2374
2453
|
}
|
|
2375
|
-
) }, contextRoute.title) }) });
|
|
2454
|
+
) }, contextRoute.title) }) }) });
|
|
2376
2455
|
}
|
|
2377
2456
|
|
|
2378
2457
|
// .docu/components/DocsLayout.tsx
|
|
@@ -2475,7 +2554,8 @@ function htmlShell(opts) {
|
|
|
2475
2554
|
themeCss,
|
|
2476
2555
|
depth = 0,
|
|
2477
2556
|
headExtra,
|
|
2478
|
-
bodyExtra
|
|
2557
|
+
bodyExtra,
|
|
2558
|
+
absoluteAssets = false
|
|
2479
2559
|
} = opts;
|
|
2480
2560
|
const nonceAttr = nonce ? ` nonce="${escapeHtml(nonce)}"` : "";
|
|
2481
2561
|
const themeStyle = themeCss ? `
|
|
@@ -2485,8 +2565,8 @@ function htmlShell(opts) {
|
|
|
2485
2565
|
const bodyInjection = bodyExtra?.length ? `
|
|
2486
2566
|
${bodyExtra.join("\n ")}` : "";
|
|
2487
2567
|
const depthPrefix = depth === 0 ? "" : "../".repeat(depth);
|
|
2488
|
-
const assetPrefix = depthPrefix + "assets/";
|
|
2489
|
-
const resolvePath = (path) => path.startsWith("/") ? depthPrefix + path.slice(1) : path;
|
|
2568
|
+
const assetPrefix = absoluteAssets ? "/assets/" : depthPrefix + "assets/";
|
|
2569
|
+
const resolvePath = (path) => absoluteAssets ? path : path.startsWith("/") ? depthPrefix + path.slice(1) : path;
|
|
2490
2570
|
let seoTags = "";
|
|
2491
2571
|
if (opts.seo) {
|
|
2492
2572
|
const s = opts.seo;
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
htmlShell,
|
|
16
16
|
initSentry,
|
|
17
17
|
loadPlugins
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-42JQLAKP.js";
|
|
19
19
|
import {
|
|
20
20
|
SECURITY_HEADERS,
|
|
21
21
|
generateNonce,
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
} from "./chunk-EOK6KATZ.js";
|
|
29
29
|
import {
|
|
30
30
|
logger
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-IN2QAGDZ.js";
|
|
32
32
|
import {
|
|
33
33
|
DIST_DIR,
|
|
34
34
|
DOCS_DIR,
|
|
@@ -60,7 +60,11 @@ function createHtmlResponse(title, description, body, status, state, depth = 0)
|
|
|
60
60
|
nonce,
|
|
61
61
|
extraScripts: hmrScript(nonce),
|
|
62
62
|
themeCss: state.inlineThemeCss,
|
|
63
|
-
depth
|
|
63
|
+
depth,
|
|
64
|
+
// 404 pages can be requested at arbitrary depths (e.g. a noLink section
|
|
65
|
+
// path typed in the address bar) — relative asset paths would resolve
|
|
66
|
+
// against the wrong directory and break CSS/JS.
|
|
67
|
+
absoluteAssets: status === 404
|
|
64
68
|
});
|
|
65
69
|
return htmlResponse(html, nonce, status, true);
|
|
66
70
|
}
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
// package.json
|
|
6
6
|
var package_default = {
|
|
7
7
|
name: "@docubook/flame",
|
|
8
|
-
version: "2.0.0-alpha.
|
|
8
|
+
version: "2.0.0-alpha.2",
|
|
9
9
|
description: "A blazing-fast React + MDX framework powered by Bun, built for modern documentation experiences.",
|
|
10
10
|
type: "module",
|
|
11
11
|
bin: {
|
|
@@ -137,7 +137,7 @@ async function runBuild() {
|
|
|
137
137
|
process.env.FLAME_BUILD_SILENT = "1";
|
|
138
138
|
process.env.LOG_LEVEL = "error";
|
|
139
139
|
}
|
|
140
|
-
const { runBuildCli } = await import("./build.impl-
|
|
140
|
+
const { runBuildCli } = await import("./build.impl-AZBZN66Q.js");
|
|
141
141
|
await runBuildCli();
|
|
142
142
|
}
|
|
143
143
|
async function writeDockerFiles() {
|
package/.docu/lib/clean.js
CHANGED