@tomehq/theme 0.2.6 → 0.2.8

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.
Files changed (46) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/chunk-37JI6XGT.js +1720 -0
  3. package/dist/chunk-3A2LPGUL.js +1991 -0
  4. package/dist/chunk-3I2QTWTW.js +1948 -0
  5. package/dist/chunk-45M5UIAB.js +2110 -0
  6. package/dist/chunk-462AGU3S.js +1959 -0
  7. package/dist/chunk-7MUTU5D4.js +1720 -0
  8. package/dist/chunk-BZGWSKT2.js +573 -0
  9. package/dist/chunk-CTPOZMMK.js +1703 -0
  10. package/dist/chunk-DO544M3G.js +1702 -0
  11. package/dist/chunk-DPKZBFQP.js +1777 -0
  12. package/dist/chunk-FWBTK5TL.js +1444 -0
  13. package/dist/chunk-GDQIBNX5.js +1962 -0
  14. package/dist/chunk-INUMUXN5.js +2095 -0
  15. package/dist/chunk-JA4PMX6M.js +1500 -0
  16. package/dist/chunk-JZRT4WNC.js +1441 -0
  17. package/dist/chunk-LIMYFTPC.js +1468 -0
  18. package/dist/chunk-MEP7P6A7.js +1500 -0
  19. package/dist/chunk-NOZBIES7.js +1948 -0
  20. package/dist/chunk-O4GH3KYX.js +1712 -0
  21. package/dist/chunk-OEXM3BEC.js +1702 -0
  22. package/dist/chunk-Q7PYTVW3.js +1771 -0
  23. package/dist/chunk-QCWZYABW.js +1468 -0
  24. package/dist/chunk-RDF25WB2.js +2085 -0
  25. package/dist/chunk-RKTT3ZEX.js +1500 -0
  26. package/dist/chunk-S47BRMNQ.js +1715 -0
  27. package/dist/chunk-S4ZH5F56.js +1949 -0
  28. package/dist/chunk-SRD7NJHS.js +1949 -0
  29. package/dist/chunk-TQDWPSTO.js +2087 -0
  30. package/dist/chunk-TTRXRPP6.js +1941 -0
  31. package/dist/chunk-UKYFJSUA.js +509 -0
  32. package/dist/chunk-VUT2FMSI.js +1937 -0
  33. package/dist/chunk-VVCC5JHK.js +1949 -0
  34. package/dist/chunk-W732TVBK.js +1944 -0
  35. package/dist/chunk-X4VQYPKO.js +1768 -0
  36. package/dist/entry.js +1 -1
  37. package/dist/index.d.ts +7 -1
  38. package/dist/index.js +1 -1
  39. package/package.json +3 -3
  40. package/src/Shell.test.tsx +72 -0
  41. package/src/Shell.tsx +204 -16
  42. package/src/entry-helpers.test.ts +316 -0
  43. package/src/entry-helpers.ts +103 -0
  44. package/src/entry.tsx +160 -69
  45. package/src/routing.test.ts +124 -0
  46. package/src/routing.ts +45 -0
@@ -0,0 +1,1444 @@
1
+ // src/entry.tsx
2
+ import { useState as useState3, useEffect as useEffect3, useCallback as useCallback3 } from "react";
3
+ import { createRoot } from "react-dom/client";
4
+
5
+ // src/Shell.tsx
6
+ import { useState as useState2, useEffect as useEffect2, useRef as useRef2, useCallback as useCallback2 } from "react";
7
+
8
+ // src/presets.ts
9
+ var THEME_PRESETS = {
10
+ amber: {
11
+ dark: {
12
+ bg: "#09090b",
13
+ sf: "#111114",
14
+ sfH: "#18181c",
15
+ bd: "#1e1e24",
16
+ tx: "#e4e4e7",
17
+ tx2: "#a1a1aa",
18
+ txM: "#919199",
19
+ ac: "#e8a845",
20
+ acD: "rgba(232,168,69,0.12)",
21
+ acT: "#fbbf24",
22
+ cdBg: "#0c0c0f",
23
+ cdTx: "#c4c4cc",
24
+ sbBg: "#0c0c0e",
25
+ hdBg: "rgba(9,9,11,0.85)"
26
+ },
27
+ light: {
28
+ bg: "#fafaf9",
29
+ sf: "#ffffff",
30
+ sfH: "#f5f5f4",
31
+ bd: "#e7e5e4",
32
+ tx: "#1c1917",
33
+ tx2: "#57534e",
34
+ txM: "#706b66",
35
+ ac: "#96640a",
36
+ acD: "rgba(150,100,10,0.08)",
37
+ acT: "#7a5208",
38
+ cdBg: "#f5f3f0",
39
+ cdTx: "#2c2520",
40
+ sbBg: "#f5f5f3",
41
+ hdBg: "rgba(250,250,249,0.85)"
42
+ },
43
+ fonts: { heading: "Instrument Serif", body: "DM Sans", code: "JetBrains Mono" }
44
+ },
45
+ editorial: {
46
+ dark: {
47
+ bg: "#080c1f",
48
+ sf: "#0e1333",
49
+ sfH: "#141940",
50
+ bd: "#1a2050",
51
+ tx: "#e8e6f0",
52
+ tx2: "#b5b1c8",
53
+ txM: "#9490ae",
54
+ ac: "#ff6b4a",
55
+ acD: "rgba(255,107,74,0.1)",
56
+ acT: "#ff8a70",
57
+ cdBg: "#0a0e27",
58
+ cdTx: "#b8b4cc",
59
+ sbBg: "#0a0e27",
60
+ hdBg: "rgba(8,12,31,0.9)"
61
+ },
62
+ light: {
63
+ bg: "#f6f4f0",
64
+ sf: "#ffffff",
65
+ sfH: "#eeece6",
66
+ bd: "#ddd9d0",
67
+ tx: "#1a1716",
68
+ tx2: "#4a443e",
69
+ txM: "#706960",
70
+ ac: "#b83d22",
71
+ acD: "rgba(184,61,34,0.07)",
72
+ acT: "#9c3019",
73
+ cdBg: "#edeae4",
74
+ cdTx: "#3a3530",
75
+ sbBg: "#f0ede8",
76
+ hdBg: "rgba(246,244,240,0.92)"
77
+ },
78
+ fonts: { heading: "Cormorant Garamond", body: "Bricolage Grotesque", code: "Fira Code" }
79
+ }
80
+ };
81
+
82
+ // src/AiChat.tsx
83
+ import { useState, useRef, useEffect, useCallback } from "react";
84
+ import { jsx, jsxs } from "react/jsx-runtime";
85
+ var ChatIcon = () => /* @__PURE__ */ jsx(
86
+ "svg",
87
+ {
88
+ width: 22,
89
+ height: 22,
90
+ viewBox: "0 0 24 24",
91
+ fill: "none",
92
+ stroke: "currentColor",
93
+ strokeWidth: "1.5",
94
+ strokeLinecap: "round",
95
+ strokeLinejoin: "round",
96
+ children: /* @__PURE__ */ jsx("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" })
97
+ }
98
+ );
99
+ var CloseIcon = () => /* @__PURE__ */ jsx(
100
+ "svg",
101
+ {
102
+ width: 18,
103
+ height: 18,
104
+ viewBox: "0 0 24 24",
105
+ fill: "none",
106
+ stroke: "currentColor",
107
+ strokeWidth: "1.5",
108
+ strokeLinecap: "round",
109
+ strokeLinejoin: "round",
110
+ children: /* @__PURE__ */ jsx("path", { d: "M18 6L6 18M6 6l12 12" })
111
+ }
112
+ );
113
+ var SendIcon = () => /* @__PURE__ */ jsx(
114
+ "svg",
115
+ {
116
+ width: 16,
117
+ height: 16,
118
+ viewBox: "0 0 24 24",
119
+ fill: "none",
120
+ stroke: "currentColor",
121
+ strokeWidth: "2",
122
+ strokeLinecap: "round",
123
+ strokeLinejoin: "round",
124
+ children: /* @__PURE__ */ jsx("path", { d: "M22 2L11 13M22 2l-7 20-4-9-9-4z" })
125
+ }
126
+ );
127
+ function buildSystemPrompt(context) {
128
+ let prompt = "You are a helpful documentation assistant. Answer questions based on the documentation provided.";
129
+ if (context) {
130
+ prompt += `
131
+
132
+ Documentation context:
133
+ ${context}`;
134
+ }
135
+ return prompt;
136
+ }
137
+ async function callOpenAI(messages, apiKey, model, context) {
138
+ const res = await fetch("https://api.openai.com/v1/chat/completions", {
139
+ method: "POST",
140
+ headers: {
141
+ "Content-Type": "application/json",
142
+ "Authorization": `Bearer ${apiKey}`
143
+ },
144
+ body: JSON.stringify({
145
+ model,
146
+ messages: [
147
+ { role: "system", content: buildSystemPrompt(context) },
148
+ ...messages.map((m) => ({ role: m.role, content: m.content }))
149
+ ]
150
+ })
151
+ });
152
+ if (!res.ok) {
153
+ const err = await res.text();
154
+ throw new Error(`OpenAI API error (${res.status}): ${err}`);
155
+ }
156
+ const data = await res.json();
157
+ return data.choices?.[0]?.message?.content || "No response.";
158
+ }
159
+ async function callAnthropic(messages, apiKey, model, context) {
160
+ const res = await fetch("https://api.anthropic.com/v1/messages", {
161
+ method: "POST",
162
+ headers: {
163
+ "Content-Type": "application/json",
164
+ "x-api-key": apiKey,
165
+ "anthropic-version": "2023-06-01",
166
+ "anthropic-dangerous-direct-browser-access": "true"
167
+ },
168
+ body: JSON.stringify({
169
+ model,
170
+ max_tokens: 1024,
171
+ system: buildSystemPrompt(context),
172
+ messages: messages.map((m) => ({ role: m.role, content: m.content }))
173
+ })
174
+ });
175
+ if (!res.ok) {
176
+ const err = await res.text();
177
+ throw new Error(`Anthropic API error (${res.status}): ${err}`);
178
+ }
179
+ const data = await res.json();
180
+ return data.content?.[0]?.text || "No response.";
181
+ }
182
+ function getDefaultModel(provider) {
183
+ if (provider === "openai") return "gpt-4o-mini";
184
+ return "claude-sonnet-4-20250514";
185
+ }
186
+ function AiChat({ provider, model, apiKey, context }) {
187
+ const [open, setOpen] = useState(false);
188
+ const [messages, setMessages] = useState([]);
189
+ const [input, setInput] = useState("");
190
+ const [loading, setLoading] = useState(false);
191
+ const [error, setError] = useState(null);
192
+ const messagesEndRef = useRef(null);
193
+ const inputRef = useRef(null);
194
+ const resolvedKey = apiKey || (typeof window !== "undefined" ? window.__TOME_AI_KEY__ : void 0);
195
+ const resolvedModel = model || getDefaultModel(provider);
196
+ useEffect(() => {
197
+ messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
198
+ }, [messages]);
199
+ useEffect(() => {
200
+ if (open) {
201
+ setTimeout(() => inputRef.current?.focus(), 100);
202
+ }
203
+ }, [open]);
204
+ const sendMessage = useCallback(async () => {
205
+ const text = input.trim();
206
+ if (!text || loading) return;
207
+ if (!resolvedKey) return;
208
+ const userMsg = { role: "user", content: text };
209
+ const updatedMessages = [...messages, userMsg];
210
+ setMessages(updatedMessages);
211
+ setInput("");
212
+ setLoading(true);
213
+ setError(null);
214
+ try {
215
+ let response;
216
+ if (provider === "openai") {
217
+ response = await callOpenAI(updatedMessages, resolvedKey, resolvedModel, context);
218
+ } else {
219
+ response = await callAnthropic(updatedMessages, resolvedKey, resolvedModel, context);
220
+ }
221
+ setMessages((prev) => [...prev, { role: "assistant", content: response }]);
222
+ } catch (err) {
223
+ setError(err instanceof Error ? err.message : "Failed to get response");
224
+ } finally {
225
+ setLoading(false);
226
+ }
227
+ }, [input, loading, messages, provider, resolvedKey, resolvedModel, context]);
228
+ const handleKeyDown = useCallback(
229
+ (e) => {
230
+ if (e.key === "Enter" && !e.shiftKey) {
231
+ e.preventDefault();
232
+ sendMessage();
233
+ }
234
+ },
235
+ [sendMessage]
236
+ );
237
+ if (!open) {
238
+ return /* @__PURE__ */ jsx(
239
+ "button",
240
+ {
241
+ "data-testid": "ai-chat-button",
242
+ onClick: () => setOpen(true),
243
+ "aria-label": "Open AI chat",
244
+ style: {
245
+ position: "fixed",
246
+ bottom: 24,
247
+ right: 24,
248
+ zIndex: 900,
249
+ width: 48,
250
+ height: 48,
251
+ borderRadius: "50%",
252
+ background: "var(--ac)",
253
+ color: "#fff",
254
+ border: "none",
255
+ cursor: "pointer",
256
+ display: "flex",
257
+ alignItems: "center",
258
+ justifyContent: "center",
259
+ boxShadow: "0 4px 16px rgba(0,0,0,0.25)",
260
+ transition: "transform 0.15s"
261
+ },
262
+ children: /* @__PURE__ */ jsx(ChatIcon, {})
263
+ }
264
+ );
265
+ }
266
+ return /* @__PURE__ */ jsxs(
267
+ "div",
268
+ {
269
+ "data-testid": "ai-chat-panel",
270
+ style: {
271
+ position: "fixed",
272
+ bottom: 24,
273
+ right: 24,
274
+ zIndex: 900,
275
+ width: 380,
276
+ maxWidth: "calc(100vw - 48px)",
277
+ height: 520,
278
+ maxHeight: "calc(100vh - 48px)",
279
+ background: "var(--sf)",
280
+ border: "1px solid var(--bd)",
281
+ borderRadius: 12,
282
+ boxShadow: "0 16px 64px rgba(0,0,0,0.3)",
283
+ display: "flex",
284
+ flexDirection: "column",
285
+ overflow: "hidden",
286
+ fontFamily: "var(--font-body)"
287
+ },
288
+ children: [
289
+ /* @__PURE__ */ jsxs(
290
+ "div",
291
+ {
292
+ style: {
293
+ display: "flex",
294
+ alignItems: "center",
295
+ justifyContent: "space-between",
296
+ padding: "12px 16px",
297
+ borderBottom: "1px solid var(--bd)",
298
+ flexShrink: 0
299
+ },
300
+ children: [
301
+ /* @__PURE__ */ jsx(
302
+ "span",
303
+ {
304
+ style: {
305
+ fontSize: 14,
306
+ fontWeight: 600,
307
+ color: "var(--tx)"
308
+ },
309
+ children: "Ask AI"
310
+ }
311
+ ),
312
+ /* @__PURE__ */ jsx(
313
+ "button",
314
+ {
315
+ "data-testid": "ai-chat-close",
316
+ onClick: () => setOpen(false),
317
+ "aria-label": "Close AI chat",
318
+ style: {
319
+ background: "none",
320
+ border: "none",
321
+ color: "var(--txM)",
322
+ cursor: "pointer",
323
+ display: "flex",
324
+ padding: 4
325
+ },
326
+ children: /* @__PURE__ */ jsx(CloseIcon, {})
327
+ }
328
+ )
329
+ ]
330
+ }
331
+ ),
332
+ /* @__PURE__ */ jsxs(
333
+ "div",
334
+ {
335
+ style: {
336
+ flex: 1,
337
+ overflow: "auto",
338
+ padding: "12px 16px"
339
+ },
340
+ children: [
341
+ !resolvedKey && /* @__PURE__ */ jsxs(
342
+ "div",
343
+ {
344
+ "data-testid": "ai-chat-no-key",
345
+ style: {
346
+ textAlign: "center",
347
+ color: "var(--txM)",
348
+ fontSize: 13,
349
+ padding: "24px 8px",
350
+ lineHeight: 1.6
351
+ },
352
+ children: [
353
+ /* @__PURE__ */ jsx("p", { style: { marginBottom: 8, fontWeight: 500, color: "var(--tx)" }, children: "Set API key" }),
354
+ /* @__PURE__ */ jsxs("p", { children: [
355
+ "Set ",
356
+ /* @__PURE__ */ jsx("code", { style: {
357
+ fontFamily: "var(--font-code)",
358
+ fontSize: "0.88em",
359
+ background: "var(--cdBg)",
360
+ padding: "0.15em 0.4em",
361
+ borderRadius: 4
362
+ }, children: "window.__TOME_AI_KEY__" }),
363
+ " in your browser console or pass it via config."
364
+ ] })
365
+ ]
366
+ }
367
+ ),
368
+ messages.map((msg, i) => /* @__PURE__ */ jsx(
369
+ "div",
370
+ {
371
+ "data-testid": `ai-chat-message-${msg.role}`,
372
+ style: {
373
+ marginBottom: 12,
374
+ display: "flex",
375
+ justifyContent: msg.role === "user" ? "flex-end" : "flex-start"
376
+ },
377
+ children: /* @__PURE__ */ jsx(
378
+ "div",
379
+ {
380
+ style: {
381
+ maxWidth: "85%",
382
+ padding: "8px 12px",
383
+ borderRadius: 10,
384
+ fontSize: 13,
385
+ lineHeight: 1.55,
386
+ whiteSpace: "pre-wrap",
387
+ wordBreak: "break-word",
388
+ background: msg.role === "user" ? "var(--ac)" : "var(--cdBg)",
389
+ color: msg.role === "user" ? "#fff" : "var(--tx)"
390
+ },
391
+ children: msg.content
392
+ }
393
+ )
394
+ },
395
+ i
396
+ )),
397
+ loading && /* @__PURE__ */ jsx(
398
+ "div",
399
+ {
400
+ "data-testid": "ai-chat-loading",
401
+ style: {
402
+ display: "flex",
403
+ justifyContent: "flex-start",
404
+ marginBottom: 12
405
+ },
406
+ children: /* @__PURE__ */ jsx(
407
+ "div",
408
+ {
409
+ style: {
410
+ padding: "8px 12px",
411
+ borderRadius: 10,
412
+ fontSize: 13,
413
+ background: "var(--cdBg)",
414
+ color: "var(--txM)"
415
+ },
416
+ children: "Thinking..."
417
+ }
418
+ )
419
+ }
420
+ ),
421
+ error && /* @__PURE__ */ jsx(
422
+ "div",
423
+ {
424
+ "data-testid": "ai-chat-error",
425
+ style: {
426
+ padding: "8px 12px",
427
+ borderRadius: 8,
428
+ fontSize: 12,
429
+ background: "rgba(220,50,50,0.1)",
430
+ color: "#d44",
431
+ marginBottom: 12
432
+ },
433
+ children: error
434
+ }
435
+ ),
436
+ /* @__PURE__ */ jsx("div", { ref: messagesEndRef })
437
+ ]
438
+ }
439
+ ),
440
+ /* @__PURE__ */ jsxs(
441
+ "div",
442
+ {
443
+ style: {
444
+ display: "flex",
445
+ alignItems: "center",
446
+ gap: 8,
447
+ padding: "10px 12px",
448
+ borderTop: "1px solid var(--bd)",
449
+ flexShrink: 0
450
+ },
451
+ children: [
452
+ /* @__PURE__ */ jsx(
453
+ "input",
454
+ {
455
+ ref: inputRef,
456
+ "data-testid": "ai-chat-input",
457
+ value: input,
458
+ onChange: (e) => setInput(e.target.value),
459
+ onKeyDown: handleKeyDown,
460
+ placeholder: resolvedKey ? "Ask a question..." : "API key required",
461
+ disabled: !resolvedKey,
462
+ style: {
463
+ flex: 1,
464
+ background: "var(--cdBg)",
465
+ border: "1px solid var(--bd)",
466
+ borderRadius: 8,
467
+ padding: "8px 12px",
468
+ color: "var(--tx)",
469
+ fontSize: 13,
470
+ fontFamily: "var(--font-body)",
471
+ outline: "none"
472
+ }
473
+ }
474
+ ),
475
+ /* @__PURE__ */ jsx(
476
+ "button",
477
+ {
478
+ "data-testid": "ai-chat-send",
479
+ onClick: sendMessage,
480
+ disabled: !resolvedKey || !input.trim() || loading,
481
+ "aria-label": "Send message",
482
+ style: {
483
+ width: 34,
484
+ height: 34,
485
+ borderRadius: 8,
486
+ background: resolvedKey && input.trim() ? "var(--ac)" : "var(--cdBg)",
487
+ color: resolvedKey && input.trim() ? "#fff" : "var(--txM)",
488
+ border: "none",
489
+ cursor: resolvedKey && input.trim() ? "pointer" : "default",
490
+ display: "flex",
491
+ alignItems: "center",
492
+ justifyContent: "center",
493
+ flexShrink: 0
494
+ },
495
+ children: /* @__PURE__ */ jsx(SendIcon, {})
496
+ }
497
+ )
498
+ ]
499
+ }
500
+ )
501
+ ]
502
+ }
503
+ );
504
+ }
505
+
506
+ // src/Shell.tsx
507
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
508
+ function hexToRgb(hex) {
509
+ const m = /^#([0-9a-f]{6})$/i.exec(hex.trim());
510
+ if (!m) return null;
511
+ const n = parseInt(m[1], 16);
512
+ return [n >> 16 & 255, n >> 8 & 255, n & 255];
513
+ }
514
+ function buildAccentOverride(hex, isDark) {
515
+ const rgb = hexToRgb(hex);
516
+ if (!rgb) return null;
517
+ const [r, g, b] = rgb;
518
+ const acD = `rgba(${r},${g},${b},${isDark ? 0.12 : 0.08})`;
519
+ const factor = isDark ? 1.15 : 0.85;
520
+ const tr = Math.min(255, Math.round(r * factor));
521
+ const tg = Math.min(255, Math.round(g * factor));
522
+ const tb = Math.min(255, Math.round(b * factor));
523
+ const acT = `rgb(${tr},${tg},${tb})`;
524
+ return { ac: hex, acD, acT };
525
+ }
526
+ var Icon = ({ d, size = 16 }) => /* @__PURE__ */ jsx2(
527
+ "svg",
528
+ {
529
+ width: size,
530
+ height: size,
531
+ viewBox: "0 0 24 24",
532
+ fill: "none",
533
+ stroke: "currentColor",
534
+ strokeWidth: "1.5",
535
+ strokeLinecap: "round",
536
+ strokeLinejoin: "round",
537
+ children: /* @__PURE__ */ jsx2("path", { d })
538
+ }
539
+ );
540
+ var SearchIcon = () => /* @__PURE__ */ jsx2(Icon, { d: "M11 19a8 8 0 1 0 0-16 8 8 0 0 0 0 16ZM21 21l-4.3-4.3" });
541
+ var ChevRight = () => /* @__PURE__ */ jsx2(Icon, { d: "M9 18l6-6-6-6", size: 14 });
542
+ var ChevDown = () => /* @__PURE__ */ jsx2(Icon, { d: "M6 9l6 6 6-6", size: 14 });
543
+ var MenuIcon = () => /* @__PURE__ */ jsx2(Icon, { d: "M3 12h18M3 6h18M3 18h18", size: 20 });
544
+ var XIcon = () => /* @__PURE__ */ jsx2(Icon, { d: "M18 6L6 18M6 6l12 12", size: 18 });
545
+ var MoonIcon = () => /* @__PURE__ */ jsx2(Icon, { d: "M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" });
546
+ var SunIcon = () => /* @__PURE__ */ jsx2(Icon, { d: "M12 7a5 5 0 1 0 0 10 5 5 0 0 0 0-10Z" });
547
+ var ArrowLeft = () => /* @__PURE__ */ jsx2(Icon, { d: "M19 12H5M12 19l-7-7 7-7", size: 14 });
548
+ var ArrowRight = () => /* @__PURE__ */ jsx2(Icon, { d: "M5 12h14M12 5l7 7-7 7", size: 14 });
549
+ var pagefindInstance = null;
550
+ var PAGEFIND_PATH = "/_pagefind/pagefind.js";
551
+ async function initPagefind() {
552
+ if (pagefindInstance) return pagefindInstance;
553
+ try {
554
+ pagefindInstance = await import(
555
+ /* @vite-ignore */
556
+ PAGEFIND_PATH
557
+ );
558
+ await pagefindInstance.init();
559
+ return pagefindInstance;
560
+ } catch {
561
+ return null;
562
+ }
563
+ }
564
+ var docSearchLoaded = null;
565
+ function loadDocSearch() {
566
+ if (docSearchLoaded) return docSearchLoaded;
567
+ docSearchLoaded = import("@docsearch/react").catch(() => null);
568
+ return docSearchLoaded;
569
+ }
570
+ function AlgoliaSearchModal({
571
+ appId,
572
+ apiKey,
573
+ indexName,
574
+ onNavigate,
575
+ onClose
576
+ }) {
577
+ const [DocSearchComponent, setDocSearchComponent] = useState2(null);
578
+ const [loadFailed, setLoadFailed] = useState2(false);
579
+ useEffect2(() => {
580
+ loadDocSearch().then((mod) => {
581
+ if (mod && mod.DocSearch) {
582
+ setDocSearchComponent(() => mod.DocSearch);
583
+ } else if (mod && mod.default) {
584
+ setDocSearchComponent(() => mod.default);
585
+ } else {
586
+ setLoadFailed(true);
587
+ }
588
+ });
589
+ }, []);
590
+ const extractPageId = useCallback2((url) => {
591
+ try {
592
+ const parsed = new URL(url, "http://localhost");
593
+ const pathname = parsed.pathname;
594
+ return pathname.replace(/^\//, "").replace(/\/index\.html$/, "").replace(/\.html$/, "") || "index";
595
+ } catch {
596
+ return "index";
597
+ }
598
+ }, []);
599
+ if (loadFailed) {
600
+ return /* @__PURE__ */ jsx2("div", { onClick: onClose, style: {
601
+ position: "fixed",
602
+ inset: 0,
603
+ zIndex: 1e3,
604
+ background: "rgba(0,0,0,0.55)",
605
+ backdropFilter: "blur(6px)",
606
+ display: "flex",
607
+ alignItems: "flex-start",
608
+ justifyContent: "center",
609
+ paddingTop: "12vh"
610
+ }, children: /* @__PURE__ */ jsx2("div", { onClick: (e) => e.stopPropagation(), style: {
611
+ background: "var(--sf)",
612
+ border: "1px solid var(--bd)",
613
+ borderRadius: 12,
614
+ width: "100%",
615
+ maxWidth: 520,
616
+ boxShadow: "0 24px 80px rgba(0,0,0,0.4)",
617
+ padding: "32px 18px",
618
+ textAlign: "center",
619
+ color: "var(--txM)",
620
+ fontSize: 14
621
+ }, children: "Algolia DocSearch is not available. Install @docsearch/react to enable it." }) });
622
+ }
623
+ if (!DocSearchComponent) {
624
+ return /* @__PURE__ */ jsx2("div", { onClick: onClose, style: {
625
+ position: "fixed",
626
+ inset: 0,
627
+ zIndex: 1e3,
628
+ background: "rgba(0,0,0,0.55)",
629
+ backdropFilter: "blur(6px)",
630
+ display: "flex",
631
+ alignItems: "flex-start",
632
+ justifyContent: "center",
633
+ paddingTop: "12vh"
634
+ }, children: /* @__PURE__ */ jsx2("div", { style: {
635
+ background: "var(--sf)",
636
+ border: "1px solid var(--bd)",
637
+ borderRadius: 12,
638
+ width: "100%",
639
+ maxWidth: 520,
640
+ boxShadow: "0 24px 80px rgba(0,0,0,0.4)",
641
+ padding: "32px 18px",
642
+ textAlign: "center",
643
+ color: "var(--txM)",
644
+ fontSize: 14
645
+ }, children: "Loading search..." }) });
646
+ }
647
+ return /* @__PURE__ */ jsx2("div", { "data-testid": "algolia-search-modal", children: /* @__PURE__ */ jsx2(
648
+ DocSearchComponent,
649
+ {
650
+ appId,
651
+ apiKey,
652
+ indexName,
653
+ navigator: {
654
+ navigate({ itemUrl }) {
655
+ const pageId = extractPageId(itemUrl);
656
+ onNavigate(pageId);
657
+ }
658
+ },
659
+ hitComponent: ({ hit, children }) => /* @__PURE__ */ jsx2("a", { href: hit.url, onClick: (e) => {
660
+ e.preventDefault();
661
+ const pageId = extractPageId(hit.url);
662
+ onNavigate(pageId);
663
+ }, children })
664
+ }
665
+ ) });
666
+ }
667
+ var VersionIcon = () => /* @__PURE__ */ jsx2(Icon, { d: "M12 8v4l3 3m6-3a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z", size: 14 });
668
+ var GlobeIcon = () => /* @__PURE__ */ jsx2(Icon, { d: "M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18ZM3.6 9h16.8M3.6 15h16.8M12 3a15 15 0 0 1 4 9 15 15 0 0 1-4 9 15 15 0 0 1-4-9 15 15 0 0 1 4-9Z", size: 14 });
669
+ function Shell({
670
+ config: config2,
671
+ navigation: navigation2,
672
+ currentPageId,
673
+ pageHtml,
674
+ pageComponent,
675
+ mdxComponents,
676
+ pageTitle,
677
+ pageDescription,
678
+ headings,
679
+ onNavigate,
680
+ allPages,
681
+ versioning,
682
+ currentVersion,
683
+ i18n,
684
+ currentLocale
685
+ }) {
686
+ const themeMode = config2.theme?.mode || "auto";
687
+ const [isDark, setDark] = useState2(() => {
688
+ if (themeMode === "dark") return true;
689
+ if (themeMode === "light") return false;
690
+ return window.matchMedia?.("(prefers-color-scheme: dark)").matches ?? true;
691
+ });
692
+ const [sbOpen, setSb] = useState2(true);
693
+ const [searchOpen, setSearch] = useState2(false);
694
+ const [versionDropdownOpen, setVersionDropdown] = useState2(false);
695
+ const [localeDropdownOpen, setLocaleDropdown] = useState2(false);
696
+ const isOldVersion = versioning && currentVersion && currentVersion !== versioning.current;
697
+ const [expanded, setExpanded] = useState2(navigation2.map((n) => n.section));
698
+ const contentRef = useRef2(null);
699
+ const [wide, setWide] = useState2(true);
700
+ const preset = config2.theme?.preset || "amber";
701
+ const baseTokens = THEME_PRESETS[preset]?.[isDark ? "dark" : "light"] || THEME_PRESETS.amber.dark;
702
+ const accentOverride = config2.theme?.accent ? buildAccentOverride(config2.theme.accent, isDark) : null;
703
+ const t = accentOverride ? { ...baseTokens, ...accentOverride } : baseTokens;
704
+ const presetFonts = THEME_PRESETS[preset]?.fonts || THEME_PRESETS.amber.fonts;
705
+ const fonts = {
706
+ heading: config2.theme?.fonts?.heading || presetFonts.heading,
707
+ body: config2.theme?.fonts?.body || presetFonts.body,
708
+ code: config2.theme?.fonts?.code || presetFonts.code
709
+ };
710
+ useEffect2(() => {
711
+ if (themeMode !== "auto") return;
712
+ const mq = window.matchMedia("(prefers-color-scheme: dark)");
713
+ const handler = (e) => setDark(e.matches);
714
+ mq.addEventListener("change", handler);
715
+ return () => mq.removeEventListener("change", handler);
716
+ }, [themeMode]);
717
+ useEffect2(() => {
718
+ const c = () => setWide(window.innerWidth > 1100);
719
+ c();
720
+ window.addEventListener("resize", c);
721
+ return () => window.removeEventListener("resize", c);
722
+ }, []);
723
+ useEffect2(() => {
724
+ contentRef.current?.scrollTo(0, 0);
725
+ }, [currentPageId]);
726
+ useEffect2(() => {
727
+ const h = (e) => {
728
+ if ((e.metaKey || e.ctrlKey) && e.key === "k") {
729
+ e.preventDefault();
730
+ setSearch(true);
731
+ }
732
+ if (e.key === "Escape") setSearch(false);
733
+ };
734
+ window.addEventListener("keydown", h);
735
+ return () => window.removeEventListener("keydown", h);
736
+ }, []);
737
+ const allNavPages = navigation2.flatMap((g) => g.pages);
738
+ const idx = allNavPages.findIndex((p) => p.id === currentPageId);
739
+ const prev = idx > 0 ? allNavPages[idx - 1] : null;
740
+ const next = idx < allNavPages.length - 1 ? allNavPages[idx + 1] : null;
741
+ const togSec = (s) => setExpanded((p) => p.includes(s) ? p.filter((x) => x !== s) : [...p, s]);
742
+ const cssVars = {
743
+ "--bg": t.bg,
744
+ "--sf": t.sf,
745
+ "--sfH": t.sfH,
746
+ "--bd": t.bd,
747
+ "--tx": t.tx,
748
+ "--tx2": t.tx2,
749
+ "--txM": t.txM,
750
+ "--ac": t.ac,
751
+ "--acD": t.acD,
752
+ "--acT": t.acT,
753
+ "--cdBg": t.cdBg,
754
+ "--cdTx": t.cdTx,
755
+ "--sbBg": t.sbBg,
756
+ "--hdBg": t.hdBg,
757
+ "--font-heading": `"${fonts.heading}", serif`,
758
+ "--font-body": `"${fonts.body}", sans-serif`,
759
+ "--font-code": `"${fonts.code}", monospace`
760
+ };
761
+ const PageComponent = pageComponent;
762
+ return /* @__PURE__ */ jsxs2("div", { className: "tome-grain", style: { ...cssVars, color: "var(--tx)", background: "var(--bg)", fontFamily: "var(--font-body)", minHeight: "100vh" }, children: [
763
+ searchOpen && config2.search?.provider === "algolia" && config2.search.appId && config2.search.apiKey && config2.search.indexName ? /* @__PURE__ */ jsx2(
764
+ AlgoliaSearchModal,
765
+ {
766
+ appId: config2.search.appId,
767
+ apiKey: config2.search.apiKey,
768
+ indexName: config2.search.indexName,
769
+ onNavigate: (id) => {
770
+ onNavigate(id);
771
+ setSearch(false);
772
+ },
773
+ onClose: () => setSearch(false)
774
+ }
775
+ ) : searchOpen ? /* @__PURE__ */ jsx2(
776
+ SearchModal,
777
+ {
778
+ allPages,
779
+ onNavigate: (id) => {
780
+ onNavigate(id);
781
+ setSearch(false);
782
+ },
783
+ onClose: () => setSearch(false)
784
+ }
785
+ ) : null,
786
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", height: "100vh" }, children: [
787
+ /* @__PURE__ */ jsxs2("aside", { style: {
788
+ width: sbOpen ? 270 : 0,
789
+ minWidth: sbOpen ? 270 : 0,
790
+ background: "var(--sbBg)",
791
+ borderRight: "1px solid var(--bd)",
792
+ display: "flex",
793
+ flexDirection: "column",
794
+ transition: "width .2s, min-width .2s",
795
+ overflow: "hidden"
796
+ }, children: [
797
+ /* @__PURE__ */ jsxs2("div", { style: { padding: "18px 20px", display: "flex", alignItems: "baseline", gap: 6, borderBottom: "1px solid var(--bd)" }, children: [
798
+ /* @__PURE__ */ jsx2("span", { style: { fontFamily: "var(--font-heading)", fontSize: 22, fontWeight: 700, fontStyle: "italic" }, children: config2.name }),
799
+ /* @__PURE__ */ jsx2("span", { style: { width: 5, height: 5, borderRadius: "50%", background: "var(--ac)", display: "inline-block" } })
800
+ ] }),
801
+ /* @__PURE__ */ jsx2("div", { style: { padding: "12px 14px" }, children: /* @__PURE__ */ jsxs2("button", { onClick: () => setSearch(true), style: {
802
+ display: "flex",
803
+ alignItems: "center",
804
+ gap: 8,
805
+ width: "100%",
806
+ background: "var(--cdBg)",
807
+ border: "1px solid var(--bd)",
808
+ borderRadius: 2,
809
+ padding: "8px 12px",
810
+ cursor: "pointer",
811
+ color: "var(--txM)",
812
+ fontSize: 12.5,
813
+ fontFamily: "var(--font-body)"
814
+ }, children: [
815
+ /* @__PURE__ */ jsx2(SearchIcon, {}),
816
+ /* @__PURE__ */ jsx2("span", { style: { flex: 1, textAlign: "left" }, children: "Search..." }),
817
+ /* @__PURE__ */ jsx2("kbd", { style: { fontFamily: "var(--font-code)", fontSize: 9, background: "var(--sf)", border: "1px solid var(--bd)", borderRadius: 2, padding: "2px 6px" }, children: "\u2318K" })
818
+ ] }) }),
819
+ /* @__PURE__ */ jsx2("nav", { style: { flex: 1, overflow: "auto", padding: "4px 10px 20px" }, children: navigation2.map((sec) => /* @__PURE__ */ jsxs2("div", { style: { marginBottom: 8 }, children: [
820
+ /* @__PURE__ */ jsxs2("button", { onClick: () => togSec(sec.section), style: {
821
+ display: "flex",
822
+ alignItems: "center",
823
+ gap: 6,
824
+ width: "100%",
825
+ background: "none",
826
+ border: "none",
827
+ padding: "8px 10px",
828
+ cursor: "pointer",
829
+ borderRadius: 2,
830
+ color: "var(--txM)",
831
+ fontSize: 10,
832
+ fontWeight: 600,
833
+ textTransform: "uppercase",
834
+ letterSpacing: ".1em",
835
+ fontFamily: "var(--font-code)"
836
+ }, children: [
837
+ expanded.includes(sec.section) ? /* @__PURE__ */ jsx2(ChevDown, {}) : /* @__PURE__ */ jsx2(ChevRight, {}),
838
+ sec.section
839
+ ] }),
840
+ expanded.includes(sec.section) && /* @__PURE__ */ jsx2("div", { style: { marginLeft: 8, borderLeft: "1px solid var(--bd)", paddingLeft: 0 }, children: sec.pages.map((p) => {
841
+ const active = currentPageId === p.id;
842
+ return /* @__PURE__ */ jsx2("button", { onClick: () => onNavigate(p.id), style: {
843
+ display: "flex",
844
+ alignItems: "center",
845
+ gap: 10,
846
+ width: "100%",
847
+ textAlign: "left",
848
+ background: "none",
849
+ border: "none",
850
+ borderRadius: 0,
851
+ borderLeft: active ? "2px solid var(--ac)" : "2px solid transparent",
852
+ padding: "7px 14px",
853
+ cursor: "pointer",
854
+ color: active ? "var(--ac)" : "var(--tx2)",
855
+ fontSize: 13,
856
+ fontWeight: active ? 500 : 400,
857
+ fontFamily: "var(--font-body)",
858
+ transition: "all .12s"
859
+ }, children: p.title }, p.id);
860
+ }) })
861
+ ] }, sec.section)) }),
862
+ /* @__PURE__ */ jsxs2("div", { style: { padding: "12px 16px", borderTop: "1px solid var(--bd)", display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
863
+ themeMode === "auto" ? /* @__PURE__ */ jsx2("button", { onClick: () => setDark((d) => !d), style: { background: "none", border: "none", color: "var(--txM)", cursor: "pointer", display: "flex" }, children: isDark ? /* @__PURE__ */ jsx2(SunIcon, {}) : /* @__PURE__ */ jsx2(MoonIcon, {}) }) : /* @__PURE__ */ jsx2("div", {}),
864
+ /* @__PURE__ */ jsx2("span", { style: { fontFamily: "var(--font-code)", fontSize: 10, color: "var(--txM)" }, children: "v0.1.0" })
865
+ ] })
866
+ ] }),
867
+ /* @__PURE__ */ jsxs2("div", { style: { flex: 1, display: "flex", flexDirection: "column", overflow: "hidden" }, children: [
868
+ /* @__PURE__ */ jsxs2("header", { style: {
869
+ display: "flex",
870
+ alignItems: "center",
871
+ gap: 12,
872
+ padding: "10px 24px",
873
+ borderBottom: "1px solid var(--bd)",
874
+ background: "var(--hdBg)",
875
+ backdropFilter: "blur(12px)"
876
+ }, children: [
877
+ /* @__PURE__ */ jsx2("button", { onClick: () => setSb(!sbOpen), style: { background: "none", border: "none", color: "var(--txM)", cursor: "pointer", display: "flex" }, children: sbOpen ? /* @__PURE__ */ jsx2(XIcon, {}) : /* @__PURE__ */ jsx2(MenuIcon, {}) }),
878
+ /* @__PURE__ */ jsx2("div", { style: { display: "flex", alignItems: "center", gap: 8, fontFamily: "var(--font-code)", fontSize: 11, color: "var(--txM)", letterSpacing: ".03em", flex: 1 }, children: navigation2.map((s) => {
879
+ const f = s.pages.find((p) => p.id === currentPageId);
880
+ if (!f) return null;
881
+ return /* @__PURE__ */ jsxs2("span", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
882
+ /* @__PURE__ */ jsx2("span", { children: s.section }),
883
+ /* @__PURE__ */ jsx2(ChevRight, {}),
884
+ /* @__PURE__ */ jsx2("span", { style: { color: "var(--ac)" }, children: f.title })
885
+ ] }, s.section);
886
+ }) }),
887
+ versioning && /* @__PURE__ */ jsxs2("div", { style: { position: "relative" }, children: [
888
+ /* @__PURE__ */ jsxs2(
889
+ "button",
890
+ {
891
+ "data-testid": "version-switcher",
892
+ onClick: () => setVersionDropdown((o) => !o),
893
+ style: {
894
+ display: "flex",
895
+ alignItems: "center",
896
+ gap: 6,
897
+ background: "var(--sf)",
898
+ border: "1px solid var(--bd)",
899
+ borderRadius: 2,
900
+ padding: "5px 10px",
901
+ cursor: "pointer",
902
+ color: "var(--tx2)",
903
+ fontSize: 12,
904
+ fontFamily: "var(--font-code)"
905
+ },
906
+ children: [
907
+ /* @__PURE__ */ jsx2(VersionIcon, {}),
908
+ currentVersion || versioning.current,
909
+ /* @__PURE__ */ jsx2(ChevDown, {})
910
+ ]
911
+ }
912
+ ),
913
+ versionDropdownOpen && /* @__PURE__ */ jsx2(
914
+ "div",
915
+ {
916
+ "data-testid": "version-dropdown",
917
+ style: {
918
+ position: "absolute",
919
+ top: "100%",
920
+ right: 0,
921
+ marginTop: 4,
922
+ background: "var(--sf)",
923
+ border: "1px solid var(--bd)",
924
+ borderRadius: 2,
925
+ boxShadow: "0 8px 32px rgba(0,0,0,0.2)",
926
+ overflow: "hidden",
927
+ zIndex: 100,
928
+ minWidth: 120
929
+ },
930
+ children: versioning.versions.map((v) => /* @__PURE__ */ jsxs2(
931
+ "button",
932
+ {
933
+ onClick: () => {
934
+ setVersionDropdown(false);
935
+ const targetUrl = v === versioning.current ? "/" : `/${v}`;
936
+ window.location.href = targetUrl;
937
+ },
938
+ style: {
939
+ display: "block",
940
+ width: "100%",
941
+ textAlign: "left",
942
+ background: v === (currentVersion || versioning.current) ? "var(--acD)" : "none",
943
+ border: "none",
944
+ padding: "8px 14px",
945
+ cursor: "pointer",
946
+ color: v === (currentVersion || versioning.current) ? "var(--ac)" : "var(--tx2)",
947
+ fontSize: 12,
948
+ fontFamily: "var(--font-code)",
949
+ fontWeight: v === versioning.current ? 600 : 400
950
+ },
951
+ children: [
952
+ v,
953
+ v === versioning.current ? " (latest)" : ""
954
+ ]
955
+ },
956
+ v
957
+ ))
958
+ }
959
+ )
960
+ ] }),
961
+ i18n && i18n.locales.length > 1 && /* @__PURE__ */ jsxs2("div", { style: { position: "relative" }, children: [
962
+ /* @__PURE__ */ jsxs2(
963
+ "button",
964
+ {
965
+ "data-testid": "language-switcher",
966
+ onClick: () => setLocaleDropdown((o) => !o),
967
+ style: {
968
+ display: "flex",
969
+ alignItems: "center",
970
+ gap: 6,
971
+ background: "var(--sf)",
972
+ border: "1px solid var(--bd)",
973
+ borderRadius: 2,
974
+ padding: "5px 10px",
975
+ cursor: "pointer",
976
+ color: "var(--tx2)",
977
+ fontSize: 12,
978
+ fontFamily: "var(--font-body)"
979
+ },
980
+ children: [
981
+ /* @__PURE__ */ jsx2(GlobeIcon, {}),
982
+ i18n.localeNames?.[currentLocale || i18n.defaultLocale] || currentLocale || i18n.defaultLocale,
983
+ /* @__PURE__ */ jsx2(ChevDown, {})
984
+ ]
985
+ }
986
+ ),
987
+ localeDropdownOpen && /* @__PURE__ */ jsx2(
988
+ "div",
989
+ {
990
+ "data-testid": "language-dropdown",
991
+ style: {
992
+ position: "absolute",
993
+ top: "100%",
994
+ right: 0,
995
+ marginTop: 4,
996
+ background: "var(--sf)",
997
+ border: "1px solid var(--bd)",
998
+ borderRadius: 2,
999
+ boxShadow: "0 8px 32px rgba(0,0,0,0.2)",
1000
+ overflow: "hidden",
1001
+ zIndex: 100,
1002
+ minWidth: 120
1003
+ },
1004
+ children: i18n.locales.map((locale) => {
1005
+ const isActive = locale === (currentLocale || i18n.defaultLocale);
1006
+ const displayName = i18n.localeNames?.[locale] || locale;
1007
+ const activeLocale = currentLocale || i18n.defaultLocale;
1008
+ let basePageId = currentPageId;
1009
+ if (activeLocale !== i18n.defaultLocale && currentPageId.startsWith(`${activeLocale}/`)) {
1010
+ basePageId = currentPageId.slice(activeLocale.length + 1);
1011
+ }
1012
+ const targetId = locale === i18n.defaultLocale ? basePageId : `${locale}/${basePageId}`;
1013
+ return /* @__PURE__ */ jsx2(
1014
+ "button",
1015
+ {
1016
+ onClick: () => {
1017
+ setLocaleDropdown(false);
1018
+ onNavigate(targetId);
1019
+ },
1020
+ style: {
1021
+ display: "block",
1022
+ width: "100%",
1023
+ textAlign: "left",
1024
+ background: isActive ? "var(--acD)" : "none",
1025
+ border: "none",
1026
+ padding: "8px 14px",
1027
+ cursor: "pointer",
1028
+ color: isActive ? "var(--ac)" : "var(--tx2)",
1029
+ fontSize: 12,
1030
+ fontFamily: "var(--font-body)",
1031
+ fontWeight: isActive ? 600 : 400
1032
+ },
1033
+ children: displayName
1034
+ },
1035
+ locale
1036
+ );
1037
+ })
1038
+ }
1039
+ )
1040
+ ] })
1041
+ ] }),
1042
+ isOldVersion && /* @__PURE__ */ jsxs2(
1043
+ "div",
1044
+ {
1045
+ "data-testid": "old-version-banner",
1046
+ style: {
1047
+ display: "flex",
1048
+ alignItems: "center",
1049
+ justifyContent: "center",
1050
+ gap: 8,
1051
+ background: "var(--acD)",
1052
+ borderBottom: "1px solid var(--bd)",
1053
+ padding: "8px 24px",
1054
+ fontSize: 13,
1055
+ color: "var(--tx2)"
1056
+ },
1057
+ children: [
1058
+ /* @__PURE__ */ jsxs2("span", { children: [
1059
+ "You're viewing docs for ",
1060
+ currentVersion,
1061
+ "."
1062
+ ] }),
1063
+ /* @__PURE__ */ jsx2(
1064
+ "button",
1065
+ {
1066
+ onClick: () => {
1067
+ window.location.href = "/";
1068
+ },
1069
+ style: {
1070
+ background: "none",
1071
+ border: "none",
1072
+ color: "var(--ac)",
1073
+ cursor: "pointer",
1074
+ fontWeight: 600,
1075
+ fontSize: 13,
1076
+ fontFamily: "var(--font-body)",
1077
+ textDecoration: "underline"
1078
+ },
1079
+ children: "Switch to latest."
1080
+ }
1081
+ )
1082
+ ]
1083
+ }
1084
+ ),
1085
+ /* @__PURE__ */ jsxs2("div", { ref: contentRef, style: { flex: 1, overflow: "auto", display: "flex" }, children: [
1086
+ /* @__PURE__ */ jsxs2("main", { style: { flex: 1, maxWidth: 760, padding: "40px 48px 80px", margin: "0 auto" }, children: [
1087
+ /* @__PURE__ */ jsx2("h1", { style: { fontFamily: "var(--font-heading)", fontSize: 38, fontWeight: 400, fontStyle: "italic", lineHeight: 1.15, marginBottom: 8 }, children: pageTitle }),
1088
+ pageDescription && /* @__PURE__ */ jsx2("p", { style: { fontSize: 16, color: "var(--tx2)", lineHeight: 1.6, marginBottom: 32 }, children: pageDescription }),
1089
+ /* @__PURE__ */ jsx2("div", { style: { borderTop: "1px solid var(--bd)", paddingTop: 28 }, children: PageComponent ? /* @__PURE__ */ jsx2("div", { className: "tome-content", children: /* @__PURE__ */ jsx2(PageComponent, { components: mdxComponents || {} }) }) : /* @__PURE__ */ jsx2(
1090
+ "div",
1091
+ {
1092
+ className: "tome-content",
1093
+ dangerouslySetInnerHTML: { __html: pageHtml || "" }
1094
+ }
1095
+ ) }),
1096
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", justifyContent: "space-between", marginTop: 48, paddingTop: 24, borderTop: "1px solid var(--bd)", gap: 16 }, children: [
1097
+ prev ? /* @__PURE__ */ jsxs2("button", { onClick: () => onNavigate(prev.id), style: {
1098
+ display: "flex",
1099
+ alignItems: "center",
1100
+ gap: 8,
1101
+ background: "none",
1102
+ border: "1px solid var(--bd)",
1103
+ borderRadius: 2,
1104
+ padding: "10px 16px",
1105
+ cursor: "pointer",
1106
+ color: "var(--tx2)",
1107
+ fontSize: 13,
1108
+ fontFamily: "var(--font-body)",
1109
+ transition: "border-color .15s, color .15s"
1110
+ }, children: [
1111
+ /* @__PURE__ */ jsx2(ArrowLeft, {}),
1112
+ " ",
1113
+ prev.title
1114
+ ] }) : /* @__PURE__ */ jsx2("div", {}),
1115
+ next ? /* @__PURE__ */ jsxs2("button", { onClick: () => onNavigate(next.id), style: {
1116
+ display: "flex",
1117
+ alignItems: "center",
1118
+ gap: 8,
1119
+ background: "none",
1120
+ border: "1px solid var(--bd)",
1121
+ borderRadius: 2,
1122
+ padding: "10px 16px",
1123
+ cursor: "pointer",
1124
+ color: "var(--tx2)",
1125
+ fontSize: 13,
1126
+ fontFamily: "var(--font-body)",
1127
+ transition: "border-color .15s, color .15s"
1128
+ }, children: [
1129
+ next.title,
1130
+ " ",
1131
+ /* @__PURE__ */ jsx2(ArrowRight, {})
1132
+ ] }) : /* @__PURE__ */ jsx2("div", {})
1133
+ ] })
1134
+ ] }),
1135
+ headings.length > 0 && wide && /* @__PURE__ */ jsxs2("aside", { style: { width: 200, padding: "40px 16px 40px 0", position: "sticky", top: 0, alignSelf: "flex-start", flexShrink: 0 }, children: [
1136
+ /* @__PURE__ */ jsx2("div", { style: { fontSize: 10, fontWeight: 600, textTransform: "uppercase", letterSpacing: ".1em", color: "var(--txM)", marginBottom: 12, fontFamily: "var(--font-code)" }, children: "On this page" }),
1137
+ /* @__PURE__ */ jsx2("div", { style: { borderLeft: "1px solid var(--bd)", paddingLeft: 0 }, children: headings.map((h, i) => /* @__PURE__ */ jsx2("a", { href: `#${h.id}`, style: {
1138
+ display: "block",
1139
+ fontSize: 12,
1140
+ color: "var(--txM)",
1141
+ textDecoration: "none",
1142
+ padding: "4px 12px",
1143
+ paddingLeft: 12 + (h.depth - 2) * 12,
1144
+ lineHeight: 1.4,
1145
+ transition: "color .12s"
1146
+ }, children: h.text }, i)) })
1147
+ ] })
1148
+ ] })
1149
+ ] })
1150
+ ] }),
1151
+ config2.ai?.enabled && /* @__PURE__ */ jsx2(
1152
+ AiChat,
1153
+ {
1154
+ provider: config2.ai.provider || "anthropic",
1155
+ model: config2.ai.model,
1156
+ context: allPages.map((p) => `- ${p.title}${p.description ? ": " + p.description : ""}`).join("\n")
1157
+ }
1158
+ )
1159
+ ] });
1160
+ }
1161
+ function SearchModal({ allPages, onNavigate, onClose }) {
1162
+ const [q, setQ] = useState2("");
1163
+ const [results, setResults] = useState2([]);
1164
+ const [selected, setSelected] = useState2(0);
1165
+ const [pagefindReady, setPagefindReady] = useState2(null);
1166
+ const inputRef = useRef2(null);
1167
+ const debounceRef = useRef2();
1168
+ useEffect2(() => {
1169
+ initPagefind().then((pf) => setPagefindReady(!!pf));
1170
+ setTimeout(() => inputRef.current?.focus(), 50);
1171
+ }, []);
1172
+ const fallbackSearch = useCallback2((query) => {
1173
+ if (!query.trim()) return [];
1174
+ const ql = query.toLowerCase();
1175
+ return allPages.filter((p) => p.title.toLowerCase().includes(ql) || (p.description || "").toLowerCase().includes(ql)).slice(0, 8).map((p) => ({ id: p.id, title: p.title, excerpt: p.description }));
1176
+ }, [allPages]);
1177
+ const doSearch = useCallback2(async (query) => {
1178
+ if (!query.trim()) {
1179
+ setResults([]);
1180
+ setSelected(0);
1181
+ return;
1182
+ }
1183
+ const pf = pagefindInstance;
1184
+ if (pf) {
1185
+ try {
1186
+ const search = await pf.search(query);
1187
+ const items = [];
1188
+ for (const result of search.results.slice(0, 8)) {
1189
+ const data = await result.data();
1190
+ const url = data.url || "";
1191
+ const id = url.replace(/^\//, "").replace(/\/index\.html$/, "").replace(/\.html$/, "") || "index";
1192
+ items.push({
1193
+ id,
1194
+ title: data.meta?.title || id,
1195
+ excerpt: data.excerpt || void 0
1196
+ });
1197
+ }
1198
+ setResults(items);
1199
+ setSelected(0);
1200
+ return;
1201
+ } catch {
1202
+ }
1203
+ }
1204
+ setResults(fallbackSearch(query));
1205
+ setSelected(0);
1206
+ }, [fallbackSearch]);
1207
+ useEffect2(() => {
1208
+ if (debounceRef.current) clearTimeout(debounceRef.current);
1209
+ debounceRef.current = setTimeout(() => doSearch(q), 120);
1210
+ return () => {
1211
+ if (debounceRef.current) clearTimeout(debounceRef.current);
1212
+ };
1213
+ }, [q, doSearch]);
1214
+ const handleKeyDown = useCallback2((e) => {
1215
+ if (e.key === "ArrowDown") {
1216
+ e.preventDefault();
1217
+ setSelected((i) => Math.min(i + 1, results.length - 1));
1218
+ } else if (e.key === "ArrowUp") {
1219
+ e.preventDefault();
1220
+ setSelected((i) => Math.max(i - 1, 0));
1221
+ } else if (e.key === "Enter" && results.length > 0) {
1222
+ e.preventDefault();
1223
+ onNavigate(results[selected].id);
1224
+ }
1225
+ }, [results, selected, onNavigate]);
1226
+ return /* @__PURE__ */ jsx2("div", { onClick: onClose, style: {
1227
+ position: "fixed",
1228
+ inset: 0,
1229
+ zIndex: 1e3,
1230
+ background: "rgba(0,0,0,0.55)",
1231
+ backdropFilter: "blur(6px)",
1232
+ display: "flex",
1233
+ alignItems: "flex-start",
1234
+ justifyContent: "center",
1235
+ paddingTop: "12vh"
1236
+ }, children: /* @__PURE__ */ jsxs2("div", { onClick: (e) => e.stopPropagation(), style: {
1237
+ background: "var(--sf)",
1238
+ border: "1px solid var(--bd)",
1239
+ borderRadius: 2,
1240
+ width: "100%",
1241
+ maxWidth: 520,
1242
+ boxShadow: "0 24px 80px rgba(0,0,0,0.4)",
1243
+ overflow: "hidden"
1244
+ }, children: [
1245
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", alignItems: "center", gap: 10, padding: "14px 18px", borderBottom: "1px solid var(--bd)" }, children: [
1246
+ /* @__PURE__ */ jsx2(SearchIcon, {}),
1247
+ /* @__PURE__ */ jsx2(
1248
+ "input",
1249
+ {
1250
+ ref: inputRef,
1251
+ value: q,
1252
+ onChange: (e) => setQ(e.target.value),
1253
+ onKeyDown: handleKeyDown,
1254
+ placeholder: "Search documentation...",
1255
+ style: { flex: 1, background: "none", border: "none", outline: "none", color: "var(--tx)", fontSize: 15, fontFamily: "var(--font-body)" }
1256
+ }
1257
+ ),
1258
+ /* @__PURE__ */ jsx2("kbd", { style: { fontFamily: "var(--font-code)", fontSize: 10, color: "var(--txM)", background: "var(--cdBg)", padding: "2px 6px", borderRadius: 2, border: "1px solid var(--bd)" }, children: "ESC" })
1259
+ ] }),
1260
+ results.length > 0 && /* @__PURE__ */ jsx2("div", { style: { padding: 6, maxHeight: 360, overflow: "auto" }, children: results.map((r, i) => /* @__PURE__ */ jsxs2(
1261
+ "button",
1262
+ {
1263
+ onClick: () => onNavigate(r.id),
1264
+ style: {
1265
+ display: "block",
1266
+ width: "100%",
1267
+ textAlign: "left",
1268
+ background: i === selected ? "var(--acD)" : "none",
1269
+ border: "none",
1270
+ borderRadius: 2,
1271
+ padding: "10px 14px",
1272
+ cursor: "pointer",
1273
+ color: "var(--tx)",
1274
+ fontFamily: "var(--font-body)"
1275
+ },
1276
+ onMouseEnter: () => setSelected(i),
1277
+ children: [
1278
+ /* @__PURE__ */ jsx2("div", { style: { fontWeight: 500, fontSize: 14, marginBottom: 2 }, children: r.title }),
1279
+ r.excerpt && /* @__PURE__ */ jsx2("div", { style: {
1280
+ fontSize: 12,
1281
+ color: "var(--txM)",
1282
+ lineHeight: 1.3
1283
+ }, dangerouslySetInnerHTML: { __html: r.excerpt } })
1284
+ ]
1285
+ },
1286
+ r.id + i
1287
+ )) }),
1288
+ q && !results.length && /* @__PURE__ */ jsx2("div", { style: { padding: "32px 18px", textAlign: "center", color: "var(--txM)", fontSize: 14 }, children: "No results found" }),
1289
+ pagefindReady === false && q && results.length > 0 && /* @__PURE__ */ jsx2("div", { style: { padding: "6px 18px 10px", fontSize: 11, color: "var(--txM)", textAlign: "center" }, children: "Showing title matches. Build your site for full-text search." })
1290
+ ] }) });
1291
+ }
1292
+
1293
+ // src/entry.tsx
1294
+ import config from "virtual:tome/config";
1295
+ import { routes, navigation } from "virtual:tome/routes";
1296
+ import loadPageModule from "virtual:tome/page-loader";
1297
+ import {
1298
+ Callout,
1299
+ Tabs,
1300
+ Card,
1301
+ CardGroup,
1302
+ Steps,
1303
+ Accordion
1304
+ } from "@tome/components";
1305
+ import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1306
+ var MDX_COMPONENTS = {
1307
+ Callout,
1308
+ Tabs,
1309
+ Card,
1310
+ CardGroup,
1311
+ Steps,
1312
+ Accordion
1313
+ };
1314
+ var contentStyles = `
1315
+ @import url('https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:wght@300;400;500;600;700&family=Cormorant+Garamond:ital,wght@0,300;0,400;0,600;0,700;1,300;1,400;1,700&family=Fira+Code:wght@400;500;600&display=swap');
1316
+
1317
+ .tome-content h2 { font-family: var(--font-body); font-size: 1.35em; font-weight: 600; margin-top: 2em; margin-bottom: 0.5em; display: flex; align-items: center; gap: 10px; letter-spacing: 0.01em; }
1318
+ .tome-content h2::before { content: "#"; font-family: var(--font-heading); font-size: 1.2em; font-weight: 300; font-style: italic; color: var(--ac); opacity: 0.5; }
1319
+ .tome-content h3 { font-family: var(--font-body); font-size: 1.15em; font-weight: 600; margin-top: 1.5em; margin-bottom: 0.5em; }
1320
+ .tome-content h4 { font-family: var(--font-body); font-size: 1.05em; font-weight: 600; margin-top: 1.2em; margin-bottom: 0.5em; }
1321
+ .tome-content p { color: var(--tx2); line-height: 1.8; margin-bottom: 1em; font-size: 14.5px; }
1322
+ .tome-content a { color: var(--ac); text-decoration: none; }
1323
+ .tome-content a:hover { text-decoration: underline; }
1324
+ .tome-content .heading-anchor { display: none; }
1325
+ .tome-content ul, .tome-content ol { color: var(--tx2); padding-left: 1.5em; margin-bottom: 1em; }
1326
+ .tome-content li { margin-bottom: 0.3em; line-height: 1.7; }
1327
+ .tome-content code { font-family: var(--font-code); font-size: 0.88em; background: var(--cdBg); padding: 0.15em 0.4em; border-radius: 2px; color: var(--ac); }
1328
+ .tome-content pre { margin-bottom: 1.2em; border-radius: 2px; overflow-x: auto; border: 1px solid var(--bd); }
1329
+ .tome-content pre code { background: none; padding: 1em 1.2em; display: block; font-size: 12.5px; line-height: 1.7; color: var(--cdTx); }
1330
+ .tome-content blockquote { border-left: 3px solid var(--ac); padding: 0.5em 1em; margin: 1em 0; background: var(--acD); border-radius: 0 2px 2px 0; }
1331
+ .tome-content blockquote p { color: var(--tx2); margin: 0; }
1332
+ .tome-content table { width: 100%; border-collapse: collapse; margin-bottom: 1em; }
1333
+ .tome-content th, .tome-content td { padding: 0.5em 0.8em; border: 1px solid var(--bd); text-align: left; font-size: 0.9em; }
1334
+ .tome-content th { background: var(--sf); font-weight: 600; }
1335
+ .tome-content img { max-width: 100%; border-radius: 2px; }
1336
+ .tome-content hr { border: none; border-top: 1px solid var(--bd); margin: 2em 0; }
1337
+
1338
+ /* Selection style */
1339
+ ::selection { background: var(--acD); color: var(--ac); }
1340
+
1341
+ /* Scrollbar style */
1342
+ ::-webkit-scrollbar { width: 5px; height: 5px; }
1343
+ ::-webkit-scrollbar-track { background: transparent; }
1344
+ ::-webkit-scrollbar-thumb { background: var(--bd); border-radius: 10px; }
1345
+
1346
+ /* Grain overlay */
1347
+ .tome-grain::before {
1348
+ content: ""; position: fixed; inset: 0; z-index: 9999; pointer-events: none;
1349
+ opacity: .35;
1350
+ background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.04'/%3E%3C/svg%3E");
1351
+ background-repeat: repeat; background-size: 256px;
1352
+ }
1353
+
1354
+ /* Shiki dual-theme support */
1355
+ .shiki { background: var(--cdBg) !important; }
1356
+ html.dark .shiki .shiki-light { display: none; }
1357
+ html.light .shiki .shiki-dark { display: none; }
1358
+ `;
1359
+ async function loadPage(id) {
1360
+ try {
1361
+ const route = routes.find((r) => r.id === id);
1362
+ const mod = await loadPageModule(id);
1363
+ if (route?.isMdx && mod.meta) {
1364
+ return {
1365
+ isMdx: true,
1366
+ component: mod.default,
1367
+ frontmatter: mod.meta.frontmatter,
1368
+ headings: mod.meta.headings
1369
+ };
1370
+ }
1371
+ if (!mod.default) return null;
1372
+ return { isMdx: false, ...mod.default };
1373
+ } catch (err) {
1374
+ console.error(`Failed to load page: ${id}`, err);
1375
+ return null;
1376
+ }
1377
+ }
1378
+ function App() {
1379
+ const [currentPageId, setCurrentPageId] = useState3(() => {
1380
+ const hash = window.location.hash.slice(1);
1381
+ if (hash && routes.some((r) => r.id === hash)) return hash;
1382
+ return routes[0]?.id || "index";
1383
+ });
1384
+ const [pageData, setPageData] = useState3(null);
1385
+ const [loading, setLoading] = useState3(true);
1386
+ const navigateTo = useCallback3(async (id) => {
1387
+ setLoading(true);
1388
+ setCurrentPageId(id);
1389
+ window.location.hash = id;
1390
+ const data = await loadPage(id);
1391
+ setPageData(data);
1392
+ setLoading(false);
1393
+ }, []);
1394
+ useEffect3(() => {
1395
+ navigateTo(currentPageId);
1396
+ }, []);
1397
+ useEffect3(() => {
1398
+ const onHashChange = () => {
1399
+ const hash = window.location.hash.slice(1);
1400
+ if (hash && hash !== currentPageId && routes.some((r) => r.id === hash)) {
1401
+ navigateTo(hash);
1402
+ }
1403
+ };
1404
+ window.addEventListener("hashchange", onHashChange);
1405
+ return () => window.removeEventListener("hashchange", onHashChange);
1406
+ }, [currentPageId, navigateTo]);
1407
+ const allPages = routes.map((r) => ({
1408
+ id: r.id,
1409
+ title: r.frontmatter.title,
1410
+ description: r.frontmatter.description
1411
+ }));
1412
+ return /* @__PURE__ */ jsxs3(Fragment, { children: [
1413
+ /* @__PURE__ */ jsx3("style", { children: contentStyles }),
1414
+ /* @__PURE__ */ jsx3(
1415
+ Shell,
1416
+ {
1417
+ config,
1418
+ navigation,
1419
+ currentPageId,
1420
+ pageHtml: !pageData?.isMdx ? loading ? "<p>Loading...</p>" : pageData?.html || "<p>Page not found</p>" : void 0,
1421
+ pageComponent: pageData?.isMdx ? pageData.component : void 0,
1422
+ mdxComponents: MDX_COMPONENTS,
1423
+ pageTitle: pageData?.frontmatter.title || (loading ? "Loading..." : "Not Found"),
1424
+ pageDescription: pageData?.frontmatter.description,
1425
+ headings: pageData?.headings || [],
1426
+ onNavigate: navigateTo,
1427
+ allPages
1428
+ }
1429
+ )
1430
+ ] });
1431
+ }
1432
+ var container = document.getElementById("tome-root");
1433
+ if (container) {
1434
+ const root = createRoot(container);
1435
+ root.render(/* @__PURE__ */ jsx3(App, {}));
1436
+ }
1437
+ var entry_default = App;
1438
+
1439
+ export {
1440
+ THEME_PRESETS,
1441
+ AiChat,
1442
+ Shell,
1443
+ entry_default
1444
+ };