@docubook/flame 1.7.2 → 2.0.0-alpha.1
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/Context.tsx +1 -1
- package/.docu/components/Pagination.tsx +9 -1
- package/.docu/components/Search.tsx +6 -6
- package/.docu/components/Sidebar.tsx +37 -8
- package/.docu/components/Theme.tsx +2 -2
- package/.docu/components/Toc.tsx +52 -6
- package/.docu/components/Typography.tsx +1 -1
- package/.docu/components/home/Hero.tsx +4 -4
- package/.docu/components/registry.ts +1 -1
- package/.docu/lib/build.deno.js +3 -3
- package/.docu/lib/{build.impl-TSIF3F7O.js → build.impl-VXB4KL4D.js} +3 -3
- package/.docu/lib/build.node.js +3 -3
- package/.docu/lib/{chunk-SPHVBXRR.js → chunk-7PRQ3RQB.js} +6 -6
- package/.docu/lib/{chunk-DITXUPUV.js → chunk-A6FIEG3H.js} +61 -10
- package/.docu/lib/{chunk-PJIJEPNR.js → chunk-JRERMREW.js} +5 -4
- package/.docu/lib/{chunk-KMDGSD57.js → chunk-LZDEWK25.js} +3 -3
- package/.docu/lib/{chunk-HRO7ONJQ.js → chunk-MQWWCO6O.js} +70 -27
- package/.docu/lib/{chunk-B6LGUADD.js → chunk-TRT6WQZG.js} +346 -183
- package/.docu/lib/chunk-UISOJ4RW.js +114 -0
- 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 +5 -3
- package/.docu/lib/preview.node.js +5 -3
- package/.docu/lib/server.deno.js +6 -4
- package/.docu/lib/server.node.js +6 -4
- package/.docu/node/build.impl.ts +75 -26
- package/.docu/node/build.ts +70 -17
- package/.docu/node/client.ts +64 -27
- package/.docu/node/deploy.shared.ts +5 -5
- package/.docu/node/deploy.ts +1 -1
- package/.docu/node/html.shared.ts +7 -2
- package/.docu/node/html.ts +4 -2
- package/.docu/node/hydrate.node.ts +69 -4
- package/.docu/node/hydrate.ts +50 -1
- package/.docu/node/mdx-manifest.d.ts +5 -0
- package/.docu/node/mdx.ts +103 -17
- package/.docu/node/preview.deno.ts +1 -1
- package/.docu/node/preview.impl.ts +3 -3
- package/.docu/node/preview.node.ts +1 -1
- package/.docu/node/preview.ts +2 -2
- package/.docu/node/route.ts +57 -1
- package/.docu/node/runtime/bun.ts +28 -0
- package/.docu/node/runtime/deno.ts +34 -0
- package/.docu/node/runtime/index.ts +4 -0
- package/.docu/node/runtime/node.ts +107 -0
- package/.docu/node/runtime/types.ts +19 -0
- package/.docu/node/search-indexer.ts +3 -2
- package/.docu/node/seo.ts +2 -2
- package/.docu/node/server-routes.ts +32 -13
- package/.docu/node/server.deno.ts +1 -1
- package/.docu/node/server.impl.ts +49 -3
- package/.docu/node/server.node.ts +1 -1
- package/.docu/node/server.ts +45 -1
- package/.docu/pages/docs/[[...slug]].tsx +13 -4
- package/.docu/pages/index.tsx +1 -1
- package/.docu/styles/globals.css +38 -20
- package/README.md +7 -48
- package/bin/cli.js +25 -7
- package/package.json +7 -6
- package/template/README.md +7 -48
- 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
|
@@ -76,7 +76,7 @@ export function Context({ className }: ContextProps) {
|
|
|
76
76
|
onClick={() => navigate(contextPath)}
|
|
77
77
|
className={cn(
|
|
78
78
|
"relative flex cursor-pointer items-center gap-2 rounded px-2 py-2 text-sm",
|
|
79
|
-
"text-left outline-none
|
|
79
|
+
"text-left transition-colors outline-none",
|
|
80
80
|
isActive
|
|
81
81
|
? "bg-primary/20 text-primary"
|
|
82
82
|
: "hover:bg-base-200 text-base-content/80 hover:text-base-content"
|
|
@@ -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}
|
|
@@ -154,7 +154,7 @@ export default function Search({ className }: SearchProps) {
|
|
|
154
154
|
boxClassName="w-[calc(100%-2rem)] max-w-[640px] p-0 mx-auto relative z-10"
|
|
155
155
|
>
|
|
156
156
|
<div onKeyDown={handleKeyDown}>
|
|
157
|
-
<div className="px-4 pb-2
|
|
157
|
+
<div className="px-4 pt-4 pb-2">
|
|
158
158
|
<div className="border-base-300 flex items-center gap-3 rounded-lg border px-4 py-2.5">
|
|
159
159
|
<SearchIcon className="text-primary h-5 w-5 shrink-0" />
|
|
160
160
|
<input
|
|
@@ -163,7 +163,7 @@ export default function Search({ className }: SearchProps) {
|
|
|
163
163
|
onChange={handleQueryChange}
|
|
164
164
|
placeholder="Search documentation..."
|
|
165
165
|
autoFocus
|
|
166
|
-
className="placeholder:text-base-content/40 h-8 w-full border-none bg-transparent text-base outline-none ring-0 focus:outline-none
|
|
166
|
+
className="placeholder:text-base-content/40 h-8 w-full border-none bg-transparent text-base ring-0 outline-none focus:ring-0 focus:outline-none"
|
|
167
167
|
aria-label="Search documentation"
|
|
168
168
|
/>
|
|
169
169
|
{query && (
|
|
@@ -187,7 +187,7 @@ export default function Search({ className }: SearchProps) {
|
|
|
187
187
|
</div>
|
|
188
188
|
</div>
|
|
189
189
|
|
|
190
|
-
<div className="max-h-
|
|
190
|
+
<div className="max-h-100 overflow-y-auto px-3 py-2">
|
|
191
191
|
{query.trim().length >= 2 && results.length === 0 && (
|
|
192
192
|
<p className="text-base-content/50 py-8 text-center text-sm">
|
|
193
193
|
No results for “<span className="text-primary">{query}</span>”
|
|
@@ -248,10 +248,10 @@ function SearchTrigger({ onClick }: { onClick: () => void }) {
|
|
|
248
248
|
<SearchIcon className="h-4 w-4 shrink-0" />
|
|
249
249
|
<span className="flex-1 text-left">Search</span>
|
|
250
250
|
<div className="flex items-center gap-0.5">
|
|
251
|
-
<Kbd className="bg-primary text-
|
|
251
|
+
<Kbd className="bg-primary text-primary-foreground rounded-md p-1.5 text-[12px] font-medium">
|
|
252
252
|
<FnKey.Cmd />
|
|
253
253
|
</Kbd>
|
|
254
|
-
<Kbd className="bg-primary text-
|
|
254
|
+
<Kbd className="bg-primary text-primary-foreground rounded-md p-1.5 text-[12px] font-medium">
|
|
255
255
|
K
|
|
256
256
|
</Kbd>
|
|
257
257
|
</div>
|
|
@@ -289,7 +289,7 @@ function GroupedResults({
|
|
|
289
289
|
<div className="flex flex-col gap-1">
|
|
290
290
|
{groups.map((group) => (
|
|
291
291
|
<div key={group.section}>
|
|
292
|
-
<div className="text-base-content/50 px-2 pb-1
|
|
292
|
+
<div className="text-base-content/50 px-2 pt-3 pb-1 text-xs font-semibold">
|
|
293
293
|
{group.section}
|
|
294
294
|
</div>
|
|
295
295
|
{group.items.map(({ result, globalIndex }) => {
|
|
@@ -92,13 +92,21 @@ export function MobileBar({
|
|
|
92
92
|
useEffect(() => {
|
|
93
93
|
if (!tocs.length) return;
|
|
94
94
|
|
|
95
|
+
// Match the CSS scroll-margin-top (scroll-mt-54 = 216px) that anchor
|
|
96
|
+
// jumps rest at, so the highlight agrees with where a jump lands.
|
|
97
|
+
const firstEl = document.getElementById(tocs[0].href.slice(1));
|
|
98
|
+
const offset = firstEl ? parseFloat(getComputedStyle(firstEl).scrollMarginTop) || 100 : 100;
|
|
99
|
+
|
|
95
100
|
const handleScroll = () => {
|
|
96
101
|
let currentId: string | null = null;
|
|
97
102
|
for (const toc of tocs) {
|
|
98
103
|
const id = toc.href.slice(1);
|
|
99
104
|
const el = document.getElementById(id);
|
|
100
105
|
if (!el) continue;
|
|
101
|
-
|
|
106
|
+
// Round: smooth anchor scrolls land at sub-pixel positions (e.g.
|
|
107
|
+
// 216.4px), which would otherwise fail the <= offset comparison and
|
|
108
|
+
// leave the previous heading highlighted.
|
|
109
|
+
if (Math.round(el.getBoundingClientRect().top) <= offset) {
|
|
102
110
|
currentId = id;
|
|
103
111
|
} else {
|
|
104
112
|
break;
|
|
@@ -109,7 +117,13 @@ export function MobileBar({
|
|
|
109
117
|
|
|
110
118
|
handleScroll();
|
|
111
119
|
window.addEventListener("scroll", handleScroll, { passive: true });
|
|
112
|
-
|
|
120
|
+
// Smooth anchor scrolls may not emit a trailing scroll event at the
|
|
121
|
+
// target position — recompute once the animation actually settles.
|
|
122
|
+
window.addEventListener("scrollend", handleScroll, { passive: true });
|
|
123
|
+
return () => {
|
|
124
|
+
window.removeEventListener("scroll", handleScroll);
|
|
125
|
+
window.removeEventListener("scrollend", handleScroll);
|
|
126
|
+
};
|
|
113
127
|
}, [tocs]);
|
|
114
128
|
|
|
115
129
|
useEffect(() => {
|
|
@@ -192,7 +206,10 @@ export function MobileBar({
|
|
|
192
206
|
<MobileTocList
|
|
193
207
|
tocs={tocs}
|
|
194
208
|
activeId={activeId}
|
|
195
|
-
onSelect={() =>
|
|
209
|
+
onSelect={(id) => {
|
|
210
|
+
setTocExpanded(false);
|
|
211
|
+
setActiveId(id);
|
|
212
|
+
}}
|
|
196
213
|
/>
|
|
197
214
|
) : (
|
|
198
215
|
<div className="flex flex-col items-center py-6 text-center">
|
|
@@ -216,7 +233,7 @@ function MobileTocList({
|
|
|
216
233
|
}: {
|
|
217
234
|
tocs: TocItem[];
|
|
218
235
|
activeId: string | null;
|
|
219
|
-
onSelect: () => void;
|
|
236
|
+
onSelect: (id: string) => void;
|
|
220
237
|
}) {
|
|
221
238
|
return (
|
|
222
239
|
<ul className="flex flex-col gap-0.5">
|
|
@@ -230,9 +247,21 @@ function MobileTocList({
|
|
|
230
247
|
href={toc.href}
|
|
231
248
|
onClick={(e) => {
|
|
232
249
|
e.preventDefault();
|
|
233
|
-
onSelect();
|
|
234
|
-
|
|
235
|
-
|
|
250
|
+
onSelect(id);
|
|
251
|
+
history.replaceState(null, "", toc.href);
|
|
252
|
+
const targetId = id;
|
|
253
|
+
// Wait for the bar collapse re-render so the measurement is
|
|
254
|
+
// accurate, then jump with the same offset the observer uses
|
|
255
|
+
// (scrollIntoView ignores scroll-margin-top on iOS Safari).
|
|
256
|
+
requestAnimationFrame(() => {
|
|
257
|
+
const el = document.getElementById(targetId);
|
|
258
|
+
if (!el) return;
|
|
259
|
+
const margin = parseFloat(getComputedStyle(el).scrollMarginTop) || 100;
|
|
260
|
+
window.scrollTo({
|
|
261
|
+
top: el.getBoundingClientRect().top + window.scrollY - margin,
|
|
262
|
+
behavior: "smooth",
|
|
263
|
+
});
|
|
264
|
+
});
|
|
236
265
|
}}
|
|
237
266
|
className={cn(
|
|
238
267
|
"block rounded px-2 py-1.5 text-sm transition-colors",
|
|
@@ -276,7 +305,7 @@ function MobileDrawer({
|
|
|
276
305
|
return (
|
|
277
306
|
<div className="fixed inset-0 z-[100]">
|
|
278
307
|
<div className="absolute inset-0 bg-black/40" onClick={onClose} aria-hidden="true" />
|
|
279
|
-
<div className="bg-base-100 absolute
|
|
308
|
+
<div className="bg-base-100 absolute top-0 right-0 flex h-full w-80 max-w-[85vw] flex-col shadow-xl">
|
|
280
309
|
<div className="border-base-200 flex items-center justify-between border-b px-4 py-3">
|
|
281
310
|
<div className="flex items-center gap-2">
|
|
282
311
|
<GitHubLink repoUrl={repoUrl} />
|
|
@@ -24,7 +24,7 @@ export function ThemeToggle({ className }: ThemeToggleProps) {
|
|
|
24
24
|
>
|
|
25
25
|
<Sun
|
|
26
26
|
className={`h-4 w-4 transition-colors ${
|
|
27
|
-
!isDark ? "text-
|
|
27
|
+
!isDark ? "text-primary-foreground" : "text-foreground"
|
|
28
28
|
}`}
|
|
29
29
|
/>
|
|
30
30
|
</button>
|
|
@@ -38,7 +38,7 @@ export function ThemeToggle({ className }: ThemeToggleProps) {
|
|
|
38
38
|
>
|
|
39
39
|
<Moon
|
|
40
40
|
className={`h-4 w-4 transition-colors ${
|
|
41
|
-
isDark ? "text-foreground" : "text-base-content/60"
|
|
41
|
+
isDark ? "text-primary-foreground" : "text-base-content/60"
|
|
42
42
|
}`}
|
|
43
43
|
/>
|
|
44
44
|
</button>
|
package/.docu/components/Toc.tsx
CHANGED
|
@@ -20,13 +20,29 @@ export default function Toc({ tocs }: TocProps) {
|
|
|
20
20
|
activeIdRef.current = activeId;
|
|
21
21
|
}, [activeId]);
|
|
22
22
|
|
|
23
|
+
// Single source of truth for the anchor offset — used by BOTH the scroll
|
|
24
|
+
// observer and the TOC click scroll so they can never disagree. Reads the
|
|
25
|
+
// CSS scroll-margin-top (prose-headings:scroll-mt-4 desktop /
|
|
26
|
+
// scroll-mt-16 mobile) so a change in one place updates both, and the
|
|
27
|
+
// heading lands exactly where the browser would put a native anchor jump.
|
|
28
|
+
const getAnchorOffset = useCallback((): number => {
|
|
29
|
+
if (typeof window === "undefined") return 100;
|
|
30
|
+
const first = tocs[0] ? document.getElementById(tocs[0].href.slice(1)) : null;
|
|
31
|
+
const margin = first ? parseFloat(getComputedStyle(first).scrollMarginTop) : 0;
|
|
32
|
+
return margin || 100;
|
|
33
|
+
}, [tocs]);
|
|
34
|
+
|
|
23
35
|
useEffect(() => {
|
|
24
|
-
|
|
36
|
+
// Desktop-only TOC — on mobile this island is display:none but still
|
|
37
|
+
// mounted; letting its observer run would hijack the URL hash while the
|
|
38
|
+
// mobile bar owns TOC behavior there.
|
|
39
|
+
if (typeof window === "undefined" || window.innerWidth < 1024) return;
|
|
40
|
+
if (!tocs.length) return;
|
|
25
41
|
|
|
26
42
|
const isDesktop = window.innerWidth >= 1024;
|
|
27
43
|
const container = isDesktop ? document.getElementById("scroll-container") : null;
|
|
28
44
|
const scrollTarget = container || window;
|
|
29
|
-
const offset =
|
|
45
|
+
const offset = getAnchorOffset();
|
|
30
46
|
|
|
31
47
|
const handleScroll = () => {
|
|
32
48
|
if (clickedIdRef.current) return;
|
|
@@ -72,7 +88,7 @@ export default function Toc({ tocs }: TocProps) {
|
|
|
72
88
|
scrollTarget.removeEventListener("scroll", listener);
|
|
73
89
|
if (throttleTimer) clearTimeout(throttleTimer);
|
|
74
90
|
};
|
|
75
|
-
}, [tocs]);
|
|
91
|
+
}, [tocs, getAnchorOffset]);
|
|
76
92
|
|
|
77
93
|
const handleLinkClick = useCallback((id: string) => {
|
|
78
94
|
clickedIdRef.current = id;
|
|
@@ -101,7 +117,7 @@ export default function Toc({ tocs }: TocProps) {
|
|
|
101
117
|
|
|
102
118
|
<div className="relative">
|
|
103
119
|
<div className="relative text-sm">
|
|
104
|
-
<div className="bg-base-300 absolute
|
|
120
|
+
<div className="bg-base-300 absolute top-0 left-0 h-full w-px" />
|
|
105
121
|
|
|
106
122
|
<div className="flex flex-col">
|
|
107
123
|
{tocs.map(({ href, level, text }) => {
|
|
@@ -143,7 +159,37 @@ export default function Toc({ tocs }: TocProps) {
|
|
|
143
159
|
e.preventDefault();
|
|
144
160
|
handleLinkClick(id);
|
|
145
161
|
const el = document.getElementById(id);
|
|
146
|
-
if (el)
|
|
162
|
+
if (el) {
|
|
163
|
+
// Manual scroll with the same offset the observer uses —
|
|
164
|
+
// scrollIntoView ignores scroll-margin-top on iOS Safari
|
|
165
|
+
// window-scroll, landing the heading under the sticky
|
|
166
|
+
// mobile bar.
|
|
167
|
+
const offset = getAnchorOffset();
|
|
168
|
+
const scroller =
|
|
169
|
+
window.innerWidth >= 1024
|
|
170
|
+
? document.getElementById("scroll-container")
|
|
171
|
+
: null;
|
|
172
|
+
const elTop = el.getBoundingClientRect().top;
|
|
173
|
+
if (scroller) {
|
|
174
|
+
// Container scroll space: element offset within the
|
|
175
|
+
// container minus the anchor offset (the container
|
|
176
|
+
// itself may sit below the viewport top, e.g. under
|
|
177
|
+
// the navbar).
|
|
178
|
+
scroller.scrollTo({
|
|
179
|
+
top:
|
|
180
|
+
elTop -
|
|
181
|
+
scroller.getBoundingClientRect().top +
|
|
182
|
+
scroller.scrollTop -
|
|
183
|
+
offset,
|
|
184
|
+
behavior: "smooth",
|
|
185
|
+
});
|
|
186
|
+
} else {
|
|
187
|
+
window.scrollTo({
|
|
188
|
+
top: elTop + window.scrollY - offset,
|
|
189
|
+
behavior: "smooth",
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
147
193
|
}}
|
|
148
194
|
className={cn(
|
|
149
195
|
"flex flex-1 items-center py-2 transition-all duration-200",
|
|
@@ -153,7 +199,7 @@ export default function Toc({ tocs }: TocProps) {
|
|
|
153
199
|
)}
|
|
154
200
|
style={{ paddingLeft: `${levelPadding + 6}px` }}
|
|
155
201
|
>
|
|
156
|
-
<span className="line-clamp-2 break-words
|
|
202
|
+
<span className="line-clamp-2 text-sm break-words">{text}</span>
|
|
157
203
|
</a>
|
|
158
204
|
</div>
|
|
159
205
|
);
|
|
@@ -2,7 +2,7 @@ import { PropsWithChildren } from "react";
|
|
|
2
2
|
|
|
3
3
|
export function Typography({ children }: PropsWithChildren) {
|
|
4
4
|
return (
|
|
5
|
-
<div className="prose prose-zinc dark:prose-invert prose-code:font-code dark:prose-code:bg-stone-900/25 prose-code:bg-stone-50 prose-pre:bg-background max-lg:prose-headings:scroll-mt-
|
|
5
|
+
<div className="prose prose-zinc dark:prose-invert prose-code:font-code dark:prose-code:bg-stone-900/25 prose-code:bg-stone-50 prose-pre:bg-background max-lg:prose-headings:scroll-mt-16 prose-headings:scroll-mt-4 prose-code:text-sm prose-code:leading-6 dark:prose-code:text-white prose-code:text-stone-800 prose-code:p-1 prose-code:rounded-md prose-code:border prose-img:rounded-md prose-img:border prose-code:before:content-none prose-code:after:content-none prose-code:px-1.5 prose-code:overflow-x-auto prose-img:my-3 prose-h2:my-4 prose-h2:mt-8 w-full max-w-full! pt-2">
|
|
6
6
|
{children}
|
|
7
7
|
</div>
|
|
8
8
|
);
|
|
@@ -34,8 +34,8 @@ function renderActionButtonIcon(iconName: string | undefined, className?: string
|
|
|
34
34
|
|
|
35
35
|
function ActionButton({ action }: ActionButtonProps) {
|
|
36
36
|
const themeClasses = {
|
|
37
|
-
primary: "bg-primary text-primary-
|
|
38
|
-
secondary: "bg-secondary text-secondary-
|
|
37
|
+
primary: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
38
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/90",
|
|
39
39
|
ghost: "bg-transparent text-muted-foreground border border-base-300 hover:bg-base-200",
|
|
40
40
|
};
|
|
41
41
|
|
|
@@ -64,11 +64,11 @@ export function Hero({ hero, className }: HeroProps) {
|
|
|
64
64
|
<div className={cn("mx-auto max-w-4xl px-6 py-32 sm:py-44", className)}>
|
|
65
65
|
<div className="text-center">
|
|
66
66
|
{tagline && <p className="text-primary mb-4 text-lg font-semibold">{tagline}</p>}
|
|
67
|
-
<h1 className="text-
|
|
67
|
+
<h1 className="text-5xl font-semibold tracking-tight text-balance sm:text-7xl">
|
|
68
68
|
{headline}
|
|
69
69
|
</h1>
|
|
70
70
|
{description && (
|
|
71
|
-
<p className="text-muted-foreground mt-8 text-
|
|
71
|
+
<p className="text-muted-foreground mt-8 text-lg text-pretty sm:text-xl">{description}</p>
|
|
72
72
|
)}
|
|
73
73
|
{actions && actions.length > 0 && (
|
|
74
74
|
<div className="mt-10 flex flex-wrap items-center justify-center gap-4">
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ComponentType } from "react";
|
|
2
|
-
import { createMdxComponents } from "@docubook/
|
|
2
|
+
import { createMdxComponents } from "@docubook/markdown";
|
|
3
3
|
import { Dropdown, DropdownItem, DropdownLink } from "@docubook/ui-react/dropdown";
|
|
4
4
|
import { PaginationDocs } from "@docubook/ui-react/pagination";
|
|
5
5
|
import { Toggle, ToggleGroup } from "@docubook/ui-react/toggle";
|
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-MQWWCO6O.js";
|
|
4
|
+
import "./chunk-TRT6WQZG.js";
|
|
5
5
|
import "./chunk-EOK6KATZ.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-JRERMREW.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-MQWWCO6O.js";
|
|
5
|
+
import "./chunk-TRT6WQZG.js";
|
|
6
6
|
import "./chunk-EOK6KATZ.js";
|
|
7
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-JRERMREW.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-MQWWCO6O.js";
|
|
4
|
+
import "./chunk-TRT6WQZG.js";
|
|
5
5
|
import "./chunk-EOK6KATZ.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-JRERMREW.js";
|
|
7
7
|
import "./chunk-4IQXHHPF.js";
|
|
8
8
|
|
|
9
9
|
// .docu/node/build.node.ts
|
|
@@ -28,7 +28,7 @@ var NGINX_CONF = `server {
|
|
|
28
28
|
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
|
|
29
29
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
|
30
30
|
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
|
|
31
|
-
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'
|
|
31
|
+
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' https: data:; font-src 'self' data:; connect-src 'self' https:; frame-src https://www.youtube-nocookie.com; frame-ancestors 'none'" always;
|
|
32
32
|
|
|
33
33
|
location /assets/ {
|
|
34
34
|
expires 1y;
|
|
@@ -38,7 +38,7 @@ var NGINX_CONF = `server {
|
|
|
38
38
|
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
|
|
39
39
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
|
40
40
|
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
|
|
41
|
-
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'
|
|
41
|
+
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' https: data:; font-src 'self' data:; connect-src 'self' https:; frame-src https://www.youtube-nocookie.com; frame-ancestors 'none'" always;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
location /docs/assets/ {
|
|
@@ -49,7 +49,7 @@ var NGINX_CONF = `server {
|
|
|
49
49
|
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
|
|
50
50
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
|
51
51
|
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
|
|
52
|
-
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'
|
|
52
|
+
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' https: data:; font-src 'self' data:; connect-src 'self' https:; frame-src https://www.youtube-nocookie.com; frame-ancestors 'none'" always;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
location / {
|
|
@@ -113,7 +113,7 @@ var HEADERS_FILE = `/*
|
|
|
113
113
|
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
|
|
114
114
|
Referrer-Policy: strict-origin-when-cross-origin
|
|
115
115
|
Permissions-Policy: camera=(), microphone=(), geolocation=()
|
|
116
|
-
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'
|
|
116
|
+
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' https: data:; font-src 'self' data:; connect-src 'self' https:; frame-src https://www.youtube-nocookie.com; frame-ancestors 'none'
|
|
117
117
|
|
|
118
118
|
/assets/*
|
|
119
119
|
Cache-Control: public, max-age=31536000, immutable
|
|
@@ -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-VXB4KL4D.js");
|
|
141
141
|
await runBuildCli();
|
|
142
142
|
}
|
|
143
143
|
async function writeDockerFiles() {
|
|
@@ -145,7 +145,7 @@ async function writeDockerFiles() {
|
|
|
145
145
|
if (!existsSync(join(dockerDir, "Dockerfile"))) {
|
|
146
146
|
await writeFile(
|
|
147
147
|
join(dockerDir, "Dockerfile"),
|
|
148
|
-
`FROM ghcr.io/docubook/flame
|
|
148
|
+
`FROM ghcr.io/docubook/flame:${FLAME_MAJOR} AS builder
|
|
149
149
|
ENV NODE_ENV=production
|
|
150
150
|
WORKDIR /app
|
|
151
151
|
COPY . .
|
|
@@ -9,12 +9,13 @@ import {
|
|
|
9
9
|
compileMdx,
|
|
10
10
|
computeInlineThemeCss,
|
|
11
11
|
errorHtml,
|
|
12
|
+
frontmatterField,
|
|
12
13
|
generateSearchIndex,
|
|
13
14
|
hmrScript,
|
|
14
15
|
htmlShell,
|
|
15
16
|
initSentry,
|
|
16
17
|
loadPlugins
|
|
17
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-TRT6WQZG.js";
|
|
18
19
|
import {
|
|
19
20
|
SECURITY_HEADERS,
|
|
20
21
|
generateNonce,
|
|
@@ -27,7 +28,7 @@ import {
|
|
|
27
28
|
} from "./chunk-EOK6KATZ.js";
|
|
28
29
|
import {
|
|
29
30
|
logger
|
|
30
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-JRERMREW.js";
|
|
31
32
|
import {
|
|
32
33
|
DIST_DIR,
|
|
33
34
|
DOCS_DIR,
|
|
@@ -36,10 +37,12 @@ import {
|
|
|
36
37
|
} from "./chunk-4IQXHHPF.js";
|
|
37
38
|
|
|
38
39
|
// .docu/node/server.impl.ts
|
|
39
|
-
import { watch } from "node:fs";
|
|
40
|
+
import { watch, existsSync } from "node:fs";
|
|
41
|
+
import { dirname, join } from "node:path";
|
|
42
|
+
import { createRequire } from "node:module";
|
|
40
43
|
|
|
41
44
|
// .docu/node/server-routes.ts
|
|
42
|
-
import { readFile } from "node:fs/promises";
|
|
45
|
+
import { readFile, stat } from "node:fs/promises";
|
|
43
46
|
import { resolve } from "node:path";
|
|
44
47
|
import { readFileSync, statSync } from "node:fs";
|
|
45
48
|
import React from "react";
|
|
@@ -57,10 +60,15 @@ function createHtmlResponse(title, description, body, status, state, depth = 0)
|
|
|
57
60
|
nonce,
|
|
58
61
|
extraScripts: hmrScript(nonce),
|
|
59
62
|
themeCss: state.inlineThemeCss,
|
|
60
|
-
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
|
|
61
68
|
});
|
|
62
69
|
return htmlResponse(html, nonce, status, true);
|
|
63
70
|
}
|
|
71
|
+
var docsCache = /* @__PURE__ */ new Map();
|
|
64
72
|
async function getDocsForSlug(slug, state) {
|
|
65
73
|
if (!isSlugSafe(slug, DOCS_DIR)) return null;
|
|
66
74
|
const paths = [
|
|
@@ -87,6 +95,11 @@ async function getDocsForSlug(slug, state) {
|
|
|
87
95
|
}
|
|
88
96
|
if (!filePath || !raw) return null;
|
|
89
97
|
const relPath = filePath.replace(PROJECT_ROOT + "/", "");
|
|
98
|
+
const mtimeMs = (await stat(filePath)).mtimeMs;
|
|
99
|
+
const cached = docsCache.get(relPath);
|
|
100
|
+
if (cached && cached.mtimeMs === mtimeMs) {
|
|
101
|
+
return cached.doc;
|
|
102
|
+
}
|
|
90
103
|
let content = raw;
|
|
91
104
|
if (state.builder) {
|
|
92
105
|
const transformed = await state.builder.runOnLoad(relPath, content);
|
|
@@ -105,7 +118,7 @@ async function getDocsForSlug(slug, state) {
|
|
|
105
118
|
content
|
|
106
119
|
});
|
|
107
120
|
}
|
|
108
|
-
|
|
121
|
+
const doc = {
|
|
109
122
|
content: result.content,
|
|
110
123
|
compiledSource: result.compiledSource,
|
|
111
124
|
frontmatter,
|
|
@@ -113,10 +126,12 @@ async function getDocsForSlug(slug, state) {
|
|
|
113
126
|
filePath: relPath,
|
|
114
127
|
resolvedContent: content
|
|
115
128
|
};
|
|
129
|
+
docsCache.set(relPath, { mtimeMs, doc });
|
|
130
|
+
return doc;
|
|
116
131
|
}
|
|
117
132
|
async function renderDocsServerPage(doc, slug, pathname, state) {
|
|
118
|
-
const title = (
|
|
119
|
-
const description =
|
|
133
|
+
const title = frontmatterField(doc.frontmatter, "title") || slug.join("/") || "Docs";
|
|
134
|
+
const description = frontmatterField(doc.frontmatter, "description");
|
|
120
135
|
const page = React.createElement(
|
|
121
136
|
DocsLayout,
|
|
122
137
|
{ repoUrl: state.docuConfig.repo?.url, pathname },
|
|
@@ -124,8 +139,9 @@ async function renderDocsServerPage(doc, slug, pathname, state) {
|
|
|
124
139
|
slug,
|
|
125
140
|
title,
|
|
126
141
|
description,
|
|
127
|
-
date: doc.frontmatter
|
|
128
|
-
|
|
142
|
+
date: frontmatterField(doc.frontmatter, "date") || void 0,
|
|
143
|
+
// Same root-relative SSR as build — see build.ts.
|
|
144
|
+
content: renderToString(doc.content),
|
|
129
145
|
tocs: doc.tocs,
|
|
130
146
|
filePath: doc.filePath,
|
|
131
147
|
repoUrl: state.docuConfig.repo?.url,
|
|
@@ -283,6 +299,16 @@ async function runServer(adapter) {
|
|
|
283
299
|
builder
|
|
284
300
|
};
|
|
285
301
|
const hmrClients = /* @__PURE__ */ new Set();
|
|
302
|
+
const require2 = createRequire(import.meta.url);
|
|
303
|
+
function resolveMdxContentSrc() {
|
|
304
|
+
try {
|
|
305
|
+
const pkgDir = dirname(require2.resolve("@docubook/markdown/package.json"));
|
|
306
|
+
const src = join(pkgDir, "src");
|
|
307
|
+
return existsSync(src) ? src : null;
|
|
308
|
+
} catch {
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
286
312
|
let hmrTimeout = null;
|
|
287
313
|
const watcher = watch(DOCS_DIR, { recursive: true }, (_event, filename) => {
|
|
288
314
|
if (!filename || !filename.endsWith(".mdx") && !filename.endsWith(".md")) return;
|
|
@@ -297,12 +323,37 @@ async function runServer(adapter) {
|
|
|
297
323
|
}
|
|
298
324
|
}, 300);
|
|
299
325
|
});
|
|
326
|
+
let srcHmrTimeout = null;
|
|
327
|
+
const mdxContentSrc = resolveMdxContentSrc();
|
|
328
|
+
const srcWatcher = mdxContentSrc ? watch(mdxContentSrc, { recursive: true }, (_event, filename) => {
|
|
329
|
+
if (!filename || !/\.(ts|tsx|js|jsx|css)$/.test(filename)) return;
|
|
330
|
+
if (srcHmrTimeout) clearTimeout(srcHmrTimeout);
|
|
331
|
+
srcHmrTimeout = setTimeout(async () => {
|
|
332
|
+
try {
|
|
333
|
+
state.assetManifest = await buildClientBundle();
|
|
334
|
+
logger.warn("[hmr] client bundle rebuilt (component change)");
|
|
335
|
+
} catch (e) {
|
|
336
|
+
logger.warn(
|
|
337
|
+
`[hmr] bundle rebuild failed: ${e instanceof Error ? e.message : String(e)}`
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
for (const client of [...hmrClients]) {
|
|
341
|
+
try {
|
|
342
|
+
client.enqueue(new TextEncoder().encode("data: reload\n\n"));
|
|
343
|
+
} catch {
|
|
344
|
+
hmrClients.delete(client);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}, 300);
|
|
348
|
+
}) : null;
|
|
300
349
|
process.on("SIGINT", () => {
|
|
301
350
|
watcher.close();
|
|
351
|
+
srcWatcher?.close();
|
|
302
352
|
process.exit(0);
|
|
303
353
|
});
|
|
304
354
|
process.on("SIGTERM", () => {
|
|
305
355
|
watcher.close();
|
|
356
|
+
srcWatcher?.close();
|
|
306
357
|
process.exit(0);
|
|
307
358
|
});
|
|
308
359
|
let handle = null;
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
// package.json
|
|
6
6
|
var package_default = {
|
|
7
7
|
name: "@docubook/flame",
|
|
8
|
-
version: "
|
|
8
|
+
version: "2.0.0-alpha.1",
|
|
9
9
|
description: "A blazing-fast React + MDX framework powered by Bun, built for modern documentation experiences.",
|
|
10
10
|
type: "module",
|
|
11
11
|
bin: {
|
|
@@ -62,10 +62,10 @@ var package_default = {
|
|
|
62
62
|
license: "MIT",
|
|
63
63
|
dependencies: {
|
|
64
64
|
"@docubook/core": "workspace:^",
|
|
65
|
-
"@docubook/
|
|
66
|
-
"@docubook/runt": "workspace:^",
|
|
65
|
+
"@docubook/markdown": "workspace:^",
|
|
67
66
|
"@docubook/themes-colors": "workspace:^",
|
|
68
67
|
"@docubook/ui-react": "workspace:^",
|
|
68
|
+
"@mdx-js/react": "^3.0.1",
|
|
69
69
|
"@tailwindcss/cli": "4.3.0",
|
|
70
70
|
"@tailwindcss/typography": "0.5.16",
|
|
71
71
|
"bun-plugin-tailwind": "^0.1.2",
|
|
@@ -74,7 +74,8 @@ var package_default = {
|
|
|
74
74
|
"lucide-react": "^1.14.0",
|
|
75
75
|
react: "^19.2.7",
|
|
76
76
|
"react-dom": "^19.2.7",
|
|
77
|
-
unified: "^11.0.0"
|
|
77
|
+
unified: "^11.0.0",
|
|
78
|
+
zod: "^4.4.3"
|
|
78
79
|
},
|
|
79
80
|
peerDependencies: {
|
|
80
81
|
"@sentry/bun": "^10.0.0"
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from "./chunk-EOK6KATZ.js";
|
|
8
8
|
import {
|
|
9
9
|
logger
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-JRERMREW.js";
|
|
11
11
|
import {
|
|
12
12
|
DIST_DIR
|
|
13
13
|
} from "./chunk-4IQXHHPF.js";
|
|
@@ -58,7 +58,7 @@ async function runPreview(adapter) {
|
|
|
58
58
|
headers: {
|
|
59
59
|
"Content-Type": "text/html",
|
|
60
60
|
...SECURITY_HEADERS,
|
|
61
|
-
"Content-Security-Policy": cspHeader(nonce
|
|
61
|
+
"Content-Security-Policy": cspHeader(nonce)
|
|
62
62
|
}
|
|
63
63
|
});
|
|
64
64
|
}
|
|
@@ -75,7 +75,7 @@ async function runPreview(adapter) {
|
|
|
75
75
|
headers: {
|
|
76
76
|
"Content-Type": "text/html",
|
|
77
77
|
...SECURITY_HEADERS,
|
|
78
|
-
"Content-Security-Policy": cspHeader(nonce
|
|
78
|
+
"Content-Security-Policy": cspHeader(nonce)
|
|
79
79
|
}
|
|
80
80
|
});
|
|
81
81
|
}
|