@tomehq/theme 0.3.4 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/chunk-2APCPR2Y.js +2110 -0
  3. package/dist/chunk-2AXAEADQ.js +2525 -0
  4. package/dist/chunk-2WNOJXK3.js +2581 -0
  5. package/dist/chunk-37JI6XGT.js +1720 -0
  6. package/dist/chunk-3A2LPGUL.js +1991 -0
  7. package/dist/chunk-3I2QTWTW.js +1948 -0
  8. package/dist/chunk-3I4SJMER.js +2538 -0
  9. package/dist/chunk-45M5UIAB.js +2110 -0
  10. package/dist/chunk-462AGU3S.js +1959 -0
  11. package/dist/chunk-5GVQFIPI.js +2581 -0
  12. package/dist/chunk-7MUTU5D4.js +1720 -0
  13. package/dist/chunk-7NQ4IMDY.js +2294 -0
  14. package/dist/chunk-ABNPB6BB.js +2133 -0
  15. package/dist/chunk-BZGWSKT2.js +573 -0
  16. package/dist/chunk-BZIB2LMI.js +2519 -0
  17. package/dist/chunk-CMQCNCSY.js +2127 -0
  18. package/dist/chunk-CTPOZMMK.js +1703 -0
  19. package/dist/chunk-DKSQZLWR.js +2569 -0
  20. package/dist/chunk-DO544M3G.js +1702 -0
  21. package/dist/chunk-DPKZBFQP.js +1777 -0
  22. package/dist/chunk-EK7PZUEB.js +2147 -0
  23. package/dist/chunk-FMOLIHQF.js +2182 -0
  24. package/dist/chunk-FWBTK5TL.js +1444 -0
  25. package/dist/chunk-GDQIBNX5.js +1962 -0
  26. package/dist/chunk-GHQ2MODM.js +2127 -0
  27. package/dist/chunk-GR2WCRGK.js +2182 -0
  28. package/dist/chunk-H5XZVNBW.js +2291 -0
  29. package/dist/chunk-HNLKDQ64.js +2139 -0
  30. package/dist/chunk-INUMUXN5.js +2095 -0
  31. package/dist/chunk-IW3NHNOQ.js +2187 -0
  32. package/dist/chunk-JA4PMX6M.js +1500 -0
  33. package/dist/chunk-JSPFS7G5.js +2102 -0
  34. package/dist/chunk-JZRT4WNC.js +1441 -0
  35. package/dist/chunk-KQBY2JDB.js +2112 -0
  36. package/dist/chunk-LIMYFTPC.js +1468 -0
  37. package/dist/chunk-LIY62BGC.js +2519 -0
  38. package/dist/chunk-MEP7P6A7.js +1500 -0
  39. package/dist/chunk-MHYKO7KM.js +2570 -0
  40. package/dist/chunk-MSXVVBDW.js +2542 -0
  41. package/dist/chunk-NOZBIES7.js +1948 -0
  42. package/dist/chunk-O4GH3KYX.js +1712 -0
  43. package/dist/chunk-OEDJTH5F.js +2569 -0
  44. package/dist/chunk-OEXM3BEC.js +1702 -0
  45. package/dist/chunk-PGKSFQ7A.js +2459 -0
  46. package/dist/chunk-PIV6CPY2.js +2395 -0
  47. package/dist/chunk-Q7PYTVW3.js +1771 -0
  48. package/dist/chunk-QCWZYABW.js +1468 -0
  49. package/dist/chunk-RDF25WB2.js +2085 -0
  50. package/dist/chunk-RKTT3ZEX.js +1500 -0
  51. package/dist/chunk-S47BRMNQ.js +1715 -0
  52. package/dist/chunk-S4ZH5F56.js +1949 -0
  53. package/dist/chunk-SRD7NJHS.js +1949 -0
  54. package/dist/chunk-SWFYJO5H.js +2187 -0
  55. package/dist/chunk-TQDWPSTO.js +2087 -0
  56. package/dist/chunk-TTRXRPP6.js +1941 -0
  57. package/dist/chunk-UKYFJSUA.js +509 -0
  58. package/dist/chunk-VKEQHP2E.js +2133 -0
  59. package/dist/chunk-VUT2FMSI.js +1937 -0
  60. package/dist/chunk-VVCC5JHK.js +1949 -0
  61. package/dist/chunk-W732TVBK.js +1944 -0
  62. package/dist/chunk-X4VQYPKO.js +1768 -0
  63. package/dist/chunk-YX7HV4EP.js +2568 -0
  64. package/dist/chunk-YXKONM3A.js +2192 -0
  65. package/dist/chunk-YZ3P3TNS.js +1760 -0
  66. package/dist/chunk-ZVZ7JN3V.js +2568 -0
  67. package/dist/chunk-ZXW4STTN.js +2568 -0
  68. package/dist/entry.js +1 -1
  69. package/dist/index.js +1 -1
  70. package/package.json +3 -3
  71. package/src/Shell.test.tsx +25 -0
  72. package/src/Shell.tsx +8 -8
  73. package/src/entry-helpers.test.ts +60 -12
  74. package/src/entry-helpers.ts +56 -30
  75. package/src/entry.test.tsx +208 -3
  76. package/src/entry.tsx +27 -4
@@ -0,0 +1,2087 @@
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 accurately based on the documentation provided below. If the answer isn't in the documentation, say so clearly. Keep answers concise and reference specific sections when possible.";
129
+ if (context) {
130
+ const trimmed = context.length > 1e5 ? context.slice(0, 1e5) + "\n\n[Documentation truncated...]" : context;
131
+ prompt += `
132
+
133
+ Documentation:
134
+ ${trimmed}`;
135
+ }
136
+ return prompt;
137
+ }
138
+ async function callOpenAI(messages, apiKey, model, context) {
139
+ const res = await fetch("https://api.openai.com/v1/chat/completions", {
140
+ method: "POST",
141
+ headers: {
142
+ "Content-Type": "application/json",
143
+ "Authorization": `Bearer ${apiKey}`
144
+ },
145
+ body: JSON.stringify({
146
+ model,
147
+ messages: [
148
+ { role: "system", content: buildSystemPrompt(context) },
149
+ ...messages.map((m) => ({ role: m.role, content: m.content }))
150
+ ]
151
+ })
152
+ });
153
+ if (!res.ok) {
154
+ const err = await res.text();
155
+ throw new Error(`OpenAI API error (${res.status}): ${err}`);
156
+ }
157
+ const data = await res.json();
158
+ return data.choices?.[0]?.message?.content || "No response.";
159
+ }
160
+ async function callAnthropic(messages, apiKey, model, context) {
161
+ const res = await fetch("https://api.anthropic.com/v1/messages", {
162
+ method: "POST",
163
+ headers: {
164
+ "Content-Type": "application/json",
165
+ "x-api-key": apiKey,
166
+ "anthropic-version": "2023-06-01",
167
+ "anthropic-dangerous-direct-browser-access": "true"
168
+ },
169
+ body: JSON.stringify({
170
+ model,
171
+ max_tokens: 1024,
172
+ system: buildSystemPrompt(context),
173
+ messages: messages.map((m) => ({ role: m.role, content: m.content }))
174
+ })
175
+ });
176
+ if (!res.ok) {
177
+ const err = await res.text();
178
+ throw new Error(`Anthropic API error (${res.status}): ${err}`);
179
+ }
180
+ const data = await res.json();
181
+ return data.content?.[0]?.text || "No response.";
182
+ }
183
+ function getDefaultModel(provider) {
184
+ if (provider === "openai") return "gpt-4o-mini";
185
+ return "claude-sonnet-4-20250514";
186
+ }
187
+ function AiChat({ provider, model, apiKey, context }) {
188
+ const [open, setOpen] = useState(false);
189
+ const [messages, setMessages] = useState([]);
190
+ const [input, setInput] = useState("");
191
+ const [loading, setLoading] = useState(false);
192
+ const [error, setError] = useState(null);
193
+ const messagesEndRef = useRef(null);
194
+ const inputRef = useRef(null);
195
+ const resolvedKey = apiKey || (typeof window !== "undefined" ? window.__TOME_AI_API_KEY__ : void 0);
196
+ const resolvedModel = model || getDefaultModel(provider);
197
+ useEffect(() => {
198
+ messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
199
+ }, [messages]);
200
+ useEffect(() => {
201
+ if (open) {
202
+ setTimeout(() => inputRef.current?.focus(), 100);
203
+ }
204
+ }, [open]);
205
+ const sendMessage = useCallback(async () => {
206
+ const text = input.trim();
207
+ if (!text || loading) return;
208
+ if (!resolvedKey) return;
209
+ const userMsg = { role: "user", content: text };
210
+ const updatedMessages = [...messages, userMsg];
211
+ setMessages(updatedMessages);
212
+ setInput("");
213
+ setLoading(true);
214
+ setError(null);
215
+ try {
216
+ let response;
217
+ if (provider === "openai") {
218
+ response = await callOpenAI(updatedMessages, resolvedKey, resolvedModel, context);
219
+ } else {
220
+ response = await callAnthropic(updatedMessages, resolvedKey, resolvedModel, context);
221
+ }
222
+ setMessages((prev) => [...prev, { role: "assistant", content: response }]);
223
+ } catch (err) {
224
+ setError(err instanceof Error ? err.message : "Failed to get response");
225
+ } finally {
226
+ setLoading(false);
227
+ }
228
+ }, [input, loading, messages, provider, resolvedKey, resolvedModel, context]);
229
+ const handleKeyDown = useCallback(
230
+ (e) => {
231
+ if (e.key === "Enter" && !e.shiftKey) {
232
+ e.preventDefault();
233
+ sendMessage();
234
+ }
235
+ },
236
+ [sendMessage]
237
+ );
238
+ if (!open) {
239
+ return /* @__PURE__ */ jsx(
240
+ "button",
241
+ {
242
+ "data-testid": "ai-chat-button",
243
+ onClick: () => setOpen(true),
244
+ "aria-label": "Open AI chat",
245
+ style: {
246
+ position: "fixed",
247
+ bottom: 24,
248
+ right: 24,
249
+ zIndex: 900,
250
+ width: 48,
251
+ height: 48,
252
+ borderRadius: "50%",
253
+ background: "var(--ac)",
254
+ color: "#fff",
255
+ border: "none",
256
+ cursor: "pointer",
257
+ display: "flex",
258
+ alignItems: "center",
259
+ justifyContent: "center",
260
+ boxShadow: "0 4px 16px rgba(0,0,0,0.25)",
261
+ transition: "transform 0.15s"
262
+ },
263
+ children: /* @__PURE__ */ jsx(ChatIcon, {})
264
+ }
265
+ );
266
+ }
267
+ return /* @__PURE__ */ jsxs(
268
+ "div",
269
+ {
270
+ "data-testid": "ai-chat-panel",
271
+ style: {
272
+ position: "fixed",
273
+ bottom: 24,
274
+ right: 24,
275
+ zIndex: 900,
276
+ width: 380,
277
+ maxWidth: "calc(100vw - 48px)",
278
+ height: 520,
279
+ maxHeight: "calc(100vh - 48px)",
280
+ background: "var(--sf)",
281
+ border: "1px solid var(--bd)",
282
+ borderRadius: 12,
283
+ boxShadow: "0 16px 64px rgba(0,0,0,0.3)",
284
+ display: "flex",
285
+ flexDirection: "column",
286
+ overflow: "hidden",
287
+ fontFamily: "var(--font-body)"
288
+ },
289
+ children: [
290
+ /* @__PURE__ */ jsxs(
291
+ "div",
292
+ {
293
+ style: {
294
+ display: "flex",
295
+ alignItems: "center",
296
+ justifyContent: "space-between",
297
+ padding: "12px 16px",
298
+ borderBottom: "1px solid var(--bd)",
299
+ flexShrink: 0
300
+ },
301
+ children: [
302
+ /* @__PURE__ */ jsx(
303
+ "span",
304
+ {
305
+ style: {
306
+ fontSize: 14,
307
+ fontWeight: 600,
308
+ color: "var(--tx)"
309
+ },
310
+ children: "Ask AI"
311
+ }
312
+ ),
313
+ /* @__PURE__ */ jsx(
314
+ "button",
315
+ {
316
+ "data-testid": "ai-chat-close",
317
+ onClick: () => setOpen(false),
318
+ "aria-label": "Close AI chat",
319
+ style: {
320
+ background: "none",
321
+ border: "none",
322
+ color: "var(--txM)",
323
+ cursor: "pointer",
324
+ display: "flex",
325
+ padding: 4
326
+ },
327
+ children: /* @__PURE__ */ jsx(CloseIcon, {})
328
+ }
329
+ )
330
+ ]
331
+ }
332
+ ),
333
+ /* @__PURE__ */ jsxs(
334
+ "div",
335
+ {
336
+ style: {
337
+ flex: 1,
338
+ overflow: "auto",
339
+ padding: "12px 16px"
340
+ },
341
+ children: [
342
+ !resolvedKey && /* @__PURE__ */ jsxs(
343
+ "div",
344
+ {
345
+ "data-testid": "ai-chat-no-key",
346
+ style: {
347
+ textAlign: "center",
348
+ color: "var(--txM)",
349
+ fontSize: 13,
350
+ padding: "24px 8px",
351
+ lineHeight: 1.6
352
+ },
353
+ children: [
354
+ /* @__PURE__ */ jsx("p", { style: { marginBottom: 8, fontWeight: 500, color: "var(--tx)" }, children: "AI not configured" }),
355
+ /* @__PURE__ */ jsxs("p", { style: { marginBottom: 8 }, children: [
356
+ "To enable AI chat, set the ",
357
+ /* @__PURE__ */ jsx("code", { style: {
358
+ fontFamily: "var(--font-code)",
359
+ fontSize: "0.88em",
360
+ background: "var(--cdBg)",
361
+ padding: "0.15em 0.4em",
362
+ borderRadius: 4
363
+ }, children: "apiKeyEnv" }),
364
+ " in ",
365
+ /* @__PURE__ */ jsx("code", { style: {
366
+ fontFamily: "var(--font-code)",
367
+ fontSize: "0.88em",
368
+ background: "var(--cdBg)",
369
+ padding: "0.15em 0.4em",
370
+ borderRadius: 4
371
+ }, children: "tome.config.js" }),
372
+ " and provide the environment variable at build time."
373
+ ] }),
374
+ /* @__PURE__ */ jsxs("p", { style: { fontSize: 11.5, color: "var(--txM)" }, children: [
375
+ "Example: ",
376
+ /* @__PURE__ */ jsx("code", { style: {
377
+ fontFamily: "var(--font-code)",
378
+ fontSize: "0.88em",
379
+ background: "var(--cdBg)",
380
+ padding: "0.15em 0.4em",
381
+ borderRadius: 4
382
+ }, children: "TOME_AI_KEY=sk-... tome build" })
383
+ ] })
384
+ ]
385
+ }
386
+ ),
387
+ messages.map((msg, i) => /* @__PURE__ */ jsx(
388
+ "div",
389
+ {
390
+ "data-testid": `ai-chat-message-${msg.role}`,
391
+ style: {
392
+ marginBottom: 12,
393
+ display: "flex",
394
+ justifyContent: msg.role === "user" ? "flex-end" : "flex-start"
395
+ },
396
+ children: /* @__PURE__ */ jsx(
397
+ "div",
398
+ {
399
+ style: {
400
+ maxWidth: "85%",
401
+ padding: "8px 12px",
402
+ borderRadius: 10,
403
+ fontSize: 13,
404
+ lineHeight: 1.55,
405
+ whiteSpace: "pre-wrap",
406
+ wordBreak: "break-word",
407
+ background: msg.role === "user" ? "var(--ac)" : "var(--cdBg)",
408
+ color: msg.role === "user" ? "#fff" : "var(--tx)"
409
+ },
410
+ children: msg.content
411
+ }
412
+ )
413
+ },
414
+ i
415
+ )),
416
+ loading && /* @__PURE__ */ jsx(
417
+ "div",
418
+ {
419
+ "data-testid": "ai-chat-loading",
420
+ style: {
421
+ display: "flex",
422
+ justifyContent: "flex-start",
423
+ marginBottom: 12
424
+ },
425
+ children: /* @__PURE__ */ jsx(
426
+ "div",
427
+ {
428
+ style: {
429
+ padding: "8px 12px",
430
+ borderRadius: 10,
431
+ fontSize: 13,
432
+ background: "var(--cdBg)",
433
+ color: "var(--txM)"
434
+ },
435
+ children: "Thinking..."
436
+ }
437
+ )
438
+ }
439
+ ),
440
+ error && /* @__PURE__ */ jsx(
441
+ "div",
442
+ {
443
+ "data-testid": "ai-chat-error",
444
+ style: {
445
+ padding: "8px 12px",
446
+ borderRadius: 8,
447
+ fontSize: 12,
448
+ background: "rgba(220,50,50,0.1)",
449
+ color: "#d44",
450
+ marginBottom: 12
451
+ },
452
+ children: error
453
+ }
454
+ ),
455
+ /* @__PURE__ */ jsx("div", { ref: messagesEndRef })
456
+ ]
457
+ }
458
+ ),
459
+ /* @__PURE__ */ jsxs(
460
+ "div",
461
+ {
462
+ style: {
463
+ display: "flex",
464
+ alignItems: "center",
465
+ gap: 8,
466
+ padding: "10px 12px",
467
+ borderTop: "1px solid var(--bd)",
468
+ flexShrink: 0
469
+ },
470
+ children: [
471
+ /* @__PURE__ */ jsx(
472
+ "input",
473
+ {
474
+ ref: inputRef,
475
+ "data-testid": "ai-chat-input",
476
+ value: input,
477
+ onChange: (e) => setInput(e.target.value),
478
+ onKeyDown: handleKeyDown,
479
+ placeholder: resolvedKey ? "Ask a question..." : "API key required",
480
+ disabled: !resolvedKey,
481
+ style: {
482
+ flex: 1,
483
+ background: "var(--cdBg)",
484
+ border: "1px solid var(--bd)",
485
+ borderRadius: 8,
486
+ padding: "8px 12px",
487
+ color: "var(--tx)",
488
+ fontSize: 13,
489
+ fontFamily: "var(--font-body)",
490
+ outline: "none"
491
+ }
492
+ }
493
+ ),
494
+ /* @__PURE__ */ jsx(
495
+ "button",
496
+ {
497
+ "data-testid": "ai-chat-send",
498
+ onClick: sendMessage,
499
+ disabled: !resolvedKey || !input.trim() || loading,
500
+ "aria-label": "Send message",
501
+ style: {
502
+ width: 34,
503
+ height: 34,
504
+ borderRadius: 8,
505
+ background: resolvedKey && input.trim() ? "var(--ac)" : "var(--cdBg)",
506
+ color: resolvedKey && input.trim() ? "#fff" : "var(--txM)",
507
+ border: "none",
508
+ cursor: resolvedKey && input.trim() ? "pointer" : "default",
509
+ display: "flex",
510
+ alignItems: "center",
511
+ justifyContent: "center",
512
+ flexShrink: 0
513
+ },
514
+ children: /* @__PURE__ */ jsx(SendIcon, {})
515
+ }
516
+ )
517
+ ]
518
+ }
519
+ )
520
+ ]
521
+ }
522
+ );
523
+ }
524
+
525
+ // src/Shell.tsx
526
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
527
+ function hexToRgb(hex) {
528
+ const m = /^#([0-9a-f]{6})$/i.exec(hex.trim());
529
+ if (!m) return null;
530
+ const n = parseInt(m[1], 16);
531
+ return [n >> 16 & 255, n >> 8 & 255, n & 255];
532
+ }
533
+ function buildAccentOverride(hex, isDark) {
534
+ const rgb = hexToRgb(hex);
535
+ if (!rgb) return null;
536
+ const [r, g, b] = rgb;
537
+ const acD = `rgba(${r},${g},${b},${isDark ? 0.12 : 0.08})`;
538
+ const factor = isDark ? 1.15 : 0.85;
539
+ const tr = Math.min(255, Math.round(r * factor));
540
+ const tg = Math.min(255, Math.round(g * factor));
541
+ const tb = Math.min(255, Math.round(b * factor));
542
+ const acT = `rgb(${tr},${tg},${tb})`;
543
+ return { ac: hex, acD, acT };
544
+ }
545
+ var Icon = ({ d, size = 16 }) => /* @__PURE__ */ jsx2(
546
+ "svg",
547
+ {
548
+ width: size,
549
+ height: size,
550
+ viewBox: "0 0 24 24",
551
+ fill: "none",
552
+ stroke: "currentColor",
553
+ strokeWidth: "1.5",
554
+ strokeLinecap: "round",
555
+ strokeLinejoin: "round",
556
+ children: /* @__PURE__ */ jsx2("path", { d })
557
+ }
558
+ );
559
+ 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" });
560
+ var ChevRight = () => /* @__PURE__ */ jsx2(Icon, { d: "M9 18l6-6-6-6", size: 14 });
561
+ var ChevDown = () => /* @__PURE__ */ jsx2(Icon, { d: "M6 9l6 6 6-6", size: 14 });
562
+ var MenuIcon = () => /* @__PURE__ */ jsx2(Icon, { d: "M3 12h18M3 6h18M3 18h18", size: 20 });
563
+ var XIcon = () => /* @__PURE__ */ jsx2(Icon, { d: "M18 6L6 18M6 6l12 12", size: 18 });
564
+ var MoonIcon = () => /* @__PURE__ */ jsx2(Icon, { d: "M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" });
565
+ var SunIcon = () => /* @__PURE__ */ jsx2(Icon, { d: "M12 8a4 4 0 1 0 0 8 4 4 0 0 0 0-8Zm0-4a1 1 0 0 1 1-1v-1a1 1 0 0 1-2 0v1a1 1 0 0 1 1 1Zm0 16a1 1 0 0 1 1 1v1a1 1 0 0 1-2 0v-1a1 1 0 0 1 1-1ZM4 12a1 1 0 0 1-1 1H2a1 1 0 0 1 0-2h1a1 1 0 0 1 1 1Zm18-1h-1a1 1 0 0 1 0 2h1a1 1 0 0 1 0-2ZM6.34 6.34a1 1 0 0 1-1.41 0l-.71-.71a1 1 0 0 1 1.41-1.41l.71.71a1 1 0 0 1 0 1.41Zm12.73-2.12-.71.71a1 1 0 0 1-1.41-1.41l.71-.71a1 1 0 1 1 1.41 1.41ZM6.34 17.66l-.71.71a1 1 0 0 1-1.41-1.41l.71-.71a1 1 0 0 1 1.41 1.41Zm12.73 2.12-.71-.71a1 1 0 0 1 1.41-1.41l.71.71a1 1 0 0 1-1.41 1.41Z" });
566
+ var ArrowLeft = () => /* @__PURE__ */ jsx2(Icon, { d: "M19 12H5M12 19l-7-7 7-7", size: 14 });
567
+ var ArrowRight = () => /* @__PURE__ */ jsx2(Icon, { d: "M5 12h14M12 5l7 7-7 7", size: 14 });
568
+ var PencilIcon = () => /* @__PURE__ */ jsx2(Icon, { d: "M17 3a2.83 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z", size: 13 });
569
+ function formatRelativeDate(isoDate) {
570
+ const date = new Date(isoDate);
571
+ const now = /* @__PURE__ */ new Date();
572
+ const diffMs = now.getTime() - date.getTime();
573
+ if (isNaN(diffMs)) return "";
574
+ const seconds = Math.floor(diffMs / 1e3);
575
+ const minutes = Math.floor(seconds / 60);
576
+ const hours = Math.floor(minutes / 60);
577
+ const days = Math.floor(hours / 24);
578
+ const months = Math.floor(days / 30);
579
+ const years = Math.floor(days / 365);
580
+ if (seconds < 60) return "just now";
581
+ if (minutes < 60) return `${minutes} minute${minutes === 1 ? "" : "s"} ago`;
582
+ if (hours < 24) return `${hours} hour${hours === 1 ? "" : "s"} ago`;
583
+ if (days < 30) return `${days} day${days === 1 ? "" : "s"} ago`;
584
+ if (months < 12) return `${months} month${months === 1 ? "" : "s"} ago`;
585
+ if (years >= 1) return `${years} year${years === 1 ? "" : "s"} ago`;
586
+ return date.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
587
+ }
588
+ var pagefindInstance = null;
589
+ var PAGEFIND_PATH = "/_pagefind/pagefind.js";
590
+ async function initPagefind() {
591
+ if (pagefindInstance) return pagefindInstance;
592
+ try {
593
+ pagefindInstance = await import(
594
+ /* @vite-ignore */
595
+ PAGEFIND_PATH
596
+ );
597
+ await pagefindInstance.init();
598
+ return pagefindInstance;
599
+ } catch {
600
+ return null;
601
+ }
602
+ }
603
+ var docSearchLoaded = null;
604
+ function loadDocSearch() {
605
+ if (docSearchLoaded) return docSearchLoaded;
606
+ docSearchLoaded = import("@docsearch/react").catch(() => null);
607
+ return docSearchLoaded;
608
+ }
609
+ function AlgoliaSearchModal({
610
+ appId,
611
+ apiKey,
612
+ indexName,
613
+ onNavigate,
614
+ onClose,
615
+ basePath: basePath2 = ""
616
+ }) {
617
+ const [DocSearchComponent, setDocSearchComponent] = useState2(null);
618
+ const [loadFailed, setLoadFailed] = useState2(false);
619
+ useEffect2(() => {
620
+ loadDocSearch().then((mod) => {
621
+ if (mod && mod.DocSearch) {
622
+ setDocSearchComponent(() => mod.DocSearch);
623
+ } else if (mod && mod.default) {
624
+ setDocSearchComponent(() => mod.default);
625
+ } else {
626
+ setLoadFailed(true);
627
+ }
628
+ });
629
+ }, []);
630
+ const extractPageId = useCallback2((url) => {
631
+ try {
632
+ const parsed = new URL(url, "http://localhost");
633
+ let pathname = parsed.pathname;
634
+ if (basePath2) {
635
+ const bp = basePath2.replace(/\/$/, "");
636
+ if (pathname.startsWith(bp)) pathname = pathname.slice(bp.length);
637
+ }
638
+ return pathname.replace(/^\//, "").replace(/\/index\.html$/, "").replace(/\.html$/, "") || "index";
639
+ } catch {
640
+ return "index";
641
+ }
642
+ }, [basePath2]);
643
+ if (loadFailed) {
644
+ return /* @__PURE__ */ jsx2("div", { onClick: onClose, style: {
645
+ position: "fixed",
646
+ inset: 0,
647
+ zIndex: 1e3,
648
+ background: "rgba(0,0,0,0.55)",
649
+ backdropFilter: "blur(6px)",
650
+ display: "flex",
651
+ alignItems: "flex-start",
652
+ justifyContent: "center",
653
+ paddingTop: "12vh"
654
+ }, children: /* @__PURE__ */ jsx2("div", { onClick: (e) => e.stopPropagation(), style: {
655
+ background: "var(--sf)",
656
+ border: "1px solid var(--bd)",
657
+ borderRadius: 12,
658
+ width: "100%",
659
+ maxWidth: 520,
660
+ boxShadow: "0 24px 80px rgba(0,0,0,0.4)",
661
+ padding: "32px 18px",
662
+ textAlign: "center",
663
+ color: "var(--txM)",
664
+ fontSize: 14
665
+ }, children: "Algolia DocSearch is not available. Install @docsearch/react to enable it." }) });
666
+ }
667
+ if (!DocSearchComponent) {
668
+ return /* @__PURE__ */ jsx2("div", { onClick: onClose, style: {
669
+ position: "fixed",
670
+ inset: 0,
671
+ zIndex: 1e3,
672
+ background: "rgba(0,0,0,0.55)",
673
+ backdropFilter: "blur(6px)",
674
+ display: "flex",
675
+ alignItems: "flex-start",
676
+ justifyContent: "center",
677
+ paddingTop: "12vh"
678
+ }, children: /* @__PURE__ */ jsx2("div", { style: {
679
+ background: "var(--sf)",
680
+ border: "1px solid var(--bd)",
681
+ borderRadius: 12,
682
+ width: "100%",
683
+ maxWidth: 520,
684
+ boxShadow: "0 24px 80px rgba(0,0,0,0.4)",
685
+ padding: "32px 18px",
686
+ textAlign: "center",
687
+ color: "var(--txM)",
688
+ fontSize: 14
689
+ }, children: "Loading search..." }) });
690
+ }
691
+ return /* @__PURE__ */ jsx2("div", { "data-testid": "algolia-search-modal", children: /* @__PURE__ */ jsx2(
692
+ DocSearchComponent,
693
+ {
694
+ appId,
695
+ apiKey,
696
+ indexName,
697
+ navigator: {
698
+ navigate({ itemUrl }) {
699
+ const pageId = extractPageId(itemUrl);
700
+ onNavigate(pageId);
701
+ }
702
+ },
703
+ hitComponent: ({ hit, children }) => /* @__PURE__ */ jsx2("a", { href: hit.url, onClick: (e) => {
704
+ e.preventDefault();
705
+ const pageId = extractPageId(hit.url);
706
+ onNavigate(pageId);
707
+ }, children })
708
+ }
709
+ ) });
710
+ }
711
+ 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 });
712
+ 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 });
713
+ var ExtLinkIcon = () => /* @__PURE__ */ jsx2(Icon, { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6M15 3h6v6M10 14L21 3", size: 11 });
714
+ var CHANGELOG_SECTION_COLORS = {
715
+ Added: "#22c55e",
716
+ Changed: "#3b82f6",
717
+ Deprecated: "#f59e0b",
718
+ Removed: "#ef4444",
719
+ Fixed: "#8b5cf6",
720
+ Security: "#f97316"
721
+ };
722
+ function ChangelogView({ entries }) {
723
+ const [showAll, setShowAll] = useState2(entries.length <= 5);
724
+ const visible = showAll ? entries : entries.slice(0, 5);
725
+ return /* @__PURE__ */ jsxs2("div", { "data-testid": "changelog-timeline", style: { position: "relative" }, children: [
726
+ /* @__PURE__ */ jsx2("div", { style: { position: "absolute", left: 15, top: 8, bottom: 8, width: 2, background: "var(--bd)" } }),
727
+ visible.map((entry, i) => /* @__PURE__ */ jsxs2(
728
+ "div",
729
+ {
730
+ "data-testid": `changelog-entry-${entry.version}`,
731
+ style: { position: "relative", paddingLeft: 44, paddingBottom: i < visible.length - 1 ? 32 : 0 },
732
+ children: [
733
+ /* @__PURE__ */ jsx2("div", { style: {
734
+ position: "absolute",
735
+ left: 8,
736
+ top: 6,
737
+ width: 16,
738
+ height: 16,
739
+ borderRadius: "50%",
740
+ background: entry.version === "Unreleased" ? "var(--txM)" : "var(--ac)",
741
+ border: "3px solid var(--bg, #1a1a1a)"
742
+ } }),
743
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", alignItems: "baseline", gap: 12, marginBottom: 12 }, children: [
744
+ /* @__PURE__ */ jsx2("span", { style: { fontSize: 18, fontWeight: 700, color: "var(--tx)", fontFamily: "var(--font-heading, inherit)" }, children: entry.url ? /* @__PURE__ */ jsx2("a", { href: entry.url, target: "_blank", rel: "noopener noreferrer", style: { color: "inherit", textDecoration: "none" }, children: entry.version }) : entry.version }),
745
+ entry.date && /* @__PURE__ */ jsx2("span", { style: { fontSize: 13, color: "var(--txM)", fontFamily: "var(--font-code, monospace)" }, children: entry.date })
746
+ ] }),
747
+ entry.sections.map((section) => {
748
+ const sColor = CHANGELOG_SECTION_COLORS[section.type] || "#6b7280";
749
+ return /* @__PURE__ */ jsxs2("div", { style: { marginBottom: 16 }, children: [
750
+ /* @__PURE__ */ jsxs2("div", { style: { display: "inline-flex", alignItems: "center", gap: 6, marginBottom: 8 }, children: [
751
+ /* @__PURE__ */ jsx2("span", { style: { display: "inline-block", width: 8, height: 8, borderRadius: "50%", background: sColor } }),
752
+ /* @__PURE__ */ jsx2("span", { style: { fontSize: 12, fontWeight: 600, textTransform: "uppercase", letterSpacing: ".06em", color: sColor, fontFamily: "var(--font-code, monospace)" }, children: section.type })
753
+ ] }),
754
+ /* @__PURE__ */ jsx2("ul", { style: { margin: 0, paddingLeft: 18, listStyleType: "disc", color: "var(--tx2)" }, children: section.items.map((item, j) => /* @__PURE__ */ jsx2("li", { style: { fontSize: 14, lineHeight: 1.7, color: "var(--tx2)", marginBottom: 2 }, children: item }, j)) })
755
+ ] }, section.type);
756
+ })
757
+ ]
758
+ },
759
+ entry.version
760
+ )),
761
+ !showAll && entries.length > 5 && /* @__PURE__ */ jsx2("div", { style: { textAlign: "center", marginTop: 24 }, children: /* @__PURE__ */ jsxs2(
762
+ "button",
763
+ {
764
+ "data-testid": "changelog-show-more",
765
+ onClick: () => setShowAll(true),
766
+ style: { background: "none", border: "1px solid var(--bd)", borderRadius: 2, padding: "8px 20px", color: "var(--tx2)", fontSize: 13, fontFamily: "var(--font-body, inherit)", cursor: "pointer" },
767
+ children: [
768
+ "Show all ",
769
+ entries.length,
770
+ " releases"
771
+ ]
772
+ }
773
+ ) })
774
+ ] });
775
+ }
776
+ function Shell({
777
+ config: config2,
778
+ navigation: navigation2,
779
+ currentPageId,
780
+ pageHtml,
781
+ pageComponent,
782
+ mdxComponents,
783
+ pageTitle,
784
+ pageDescription,
785
+ headings,
786
+ tocEnabled = true,
787
+ editUrl,
788
+ lastUpdated,
789
+ changelogEntries,
790
+ onNavigate,
791
+ allPages,
792
+ versioning,
793
+ currentVersion,
794
+ i18n,
795
+ currentLocale,
796
+ docContext: docContext2,
797
+ basePath: basePath2 = ""
798
+ }) {
799
+ const themeMode = config2.theme?.mode || "auto";
800
+ const [isDark, setDark] = useState2(() => {
801
+ if (themeMode === "dark") return true;
802
+ if (themeMode === "light") return false;
803
+ return window.matchMedia?.("(prefers-color-scheme: dark)").matches ?? true;
804
+ });
805
+ const [mobile, setMobile] = useState2(() => typeof window !== "undefined" && window.innerWidth < 768);
806
+ const [sbOpen, setSb] = useState2(() => typeof window !== "undefined" && window.innerWidth >= 768);
807
+ const [searchOpen, setSearch] = useState2(false);
808
+ const [versionDropdownOpen, setVersionDropdown] = useState2(false);
809
+ const [localeDropdownOpen, setLocaleDropdown] = useState2(false);
810
+ const [zoomSrc, setZoomSrc] = useState2(null);
811
+ const [feedbackGiven, setFeedbackGiven] = useState2({});
812
+ const [bannerDismissed, setBannerDismissed] = useState2(() => {
813
+ if (!config2.banner?.text) return true;
814
+ try {
815
+ const hash = Array.from(config2.banner.text).reduce((h, c) => (h << 5) - h + c.charCodeAt(0) | 0, 0).toString(36);
816
+ return localStorage.getItem("tome-banner-dismissed") === hash;
817
+ } catch {
818
+ return false;
819
+ }
820
+ });
821
+ const isOldVersion = versioning && currentVersion && currentVersion !== versioning.current;
822
+ const [expanded, setExpanded] = useState2(navigation2.map((n) => n.section));
823
+ const contentRef = useRef2(null);
824
+ const [wide, setWide] = useState2(() => typeof window !== "undefined" && window.innerWidth > 1100);
825
+ const preset = config2.theme?.preset || "amber";
826
+ const baseTokens = THEME_PRESETS[preset]?.[isDark ? "dark" : "light"] || THEME_PRESETS.amber.dark;
827
+ const accentOverride = config2.theme?.accent ? buildAccentOverride(config2.theme.accent, isDark) : null;
828
+ const t = accentOverride ? { ...baseTokens, ...accentOverride } : baseTokens;
829
+ const presetFonts = THEME_PRESETS[preset]?.fonts || THEME_PRESETS.amber.fonts;
830
+ const fonts = {
831
+ heading: config2.theme?.fonts?.heading || presetFonts.heading,
832
+ body: config2.theme?.fonts?.body || presetFonts.body,
833
+ code: config2.theme?.fonts?.code || presetFonts.code
834
+ };
835
+ useEffect2(() => {
836
+ if (themeMode !== "auto") return;
837
+ const mq = window.matchMedia("(prefers-color-scheme: dark)");
838
+ const handler = (e) => setDark(e.matches);
839
+ mq.addEventListener("change", handler);
840
+ return () => mq.removeEventListener("change", handler);
841
+ }, [themeMode]);
842
+ useEffect2(() => {
843
+ document.documentElement.classList.toggle("dark", isDark);
844
+ }, [isDark]);
845
+ useEffect2(() => {
846
+ const handleResize = () => {
847
+ const w = window.innerWidth;
848
+ setWide(w > 1100);
849
+ setMobile(w < 768);
850
+ };
851
+ handleResize();
852
+ window.addEventListener("resize", handleResize);
853
+ return () => window.removeEventListener("resize", handleResize);
854
+ }, []);
855
+ useEffect2(() => {
856
+ if (mobile && sbOpen) {
857
+ document.body.style.overflow = "hidden";
858
+ return () => {
859
+ document.body.style.overflow = "";
860
+ };
861
+ }
862
+ }, [mobile, sbOpen]);
863
+ useEffect2(() => {
864
+ contentRef.current?.scrollTo(0, 0);
865
+ }, [currentPageId]);
866
+ useEffect2(() => {
867
+ const el = contentRef.current;
868
+ if (!el) return;
869
+ const handler = (e) => {
870
+ const target = e.target;
871
+ if (target.tagName === "IMG" && target.closest(".tome-content")) {
872
+ setZoomSrc(target.src);
873
+ }
874
+ };
875
+ el.addEventListener("click", handler);
876
+ return () => el.removeEventListener("click", handler);
877
+ }, []);
878
+ useEffect2(() => {
879
+ const el = contentRef.current;
880
+ if (!el) return;
881
+ const handler = (e) => {
882
+ const anchor = e.target.closest("a");
883
+ if (!anchor) return;
884
+ const href = anchor.getAttribute("href");
885
+ if (!href) return;
886
+ if (href.startsWith("http://") || href.startsWith("https://") || href.startsWith("mailto:") || href.startsWith("tel:") || href.startsWith("//")) return;
887
+ if (href.startsWith("#")) return;
888
+ e.preventDefault();
889
+ let pageId = href.replace(/^\.\//, "").replace(/^\//, "").replace(/\.mdx?$/, "").replace(/\/$/, "");
890
+ if (basePath2) {
891
+ const normalized = basePath2.replace(/^\//, "").replace(/\/$/, "");
892
+ if (normalized && pageId.startsWith(normalized + "/")) {
893
+ pageId = pageId.slice(normalized.length + 1);
894
+ } else if (normalized && pageId === normalized) {
895
+ pageId = "index";
896
+ }
897
+ }
898
+ if (!pageId) pageId = "index";
899
+ onNavigate(pageId);
900
+ };
901
+ el.addEventListener("click", handler);
902
+ return () => el.removeEventListener("click", handler);
903
+ }, [onNavigate, basePath2]);
904
+ useEffect2(() => {
905
+ if (!zoomSrc) return;
906
+ const handler = (e) => {
907
+ if (e.key === "Escape") setZoomSrc(null);
908
+ };
909
+ window.addEventListener("keydown", handler);
910
+ return () => window.removeEventListener("keydown", handler);
911
+ }, [zoomSrc]);
912
+ const tocConfig = config2.toc;
913
+ const tocDepth = tocConfig?.depth ?? 3;
914
+ const tocGlobalEnabled = tocConfig?.enabled !== false;
915
+ const showToc = tocGlobalEnabled && tocEnabled;
916
+ const filteredHeadings = headings.filter((h) => h.depth <= tocDepth);
917
+ const [activeHeadingId, setActiveHeadingId] = useState2("");
918
+ useEffect2(() => {
919
+ if (!showToc || filteredHeadings.length < 2) return;
920
+ const scrollRoot = contentRef.current;
921
+ if (!scrollRoot) return;
922
+ const timerId = setTimeout(() => {
923
+ const headingElements = [];
924
+ for (const h of filteredHeadings) {
925
+ const el = scrollRoot.querySelector(`#${CSS.escape(h.id)}`);
926
+ if (el) headingElements.push(el);
927
+ }
928
+ if (headingElements.length === 0) return;
929
+ const observer = new IntersectionObserver(
930
+ (entries) => {
931
+ const visible = entries.filter((e) => e.isIntersecting).sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top);
932
+ if (visible.length > 0) {
933
+ setActiveHeadingId(visible[0].target.id);
934
+ }
935
+ },
936
+ {
937
+ root: scrollRoot,
938
+ // Trigger when heading enters the top 20% of the scroll container
939
+ rootMargin: "0px 0px -80% 0px",
940
+ threshold: 0
941
+ }
942
+ );
943
+ for (const el of headingElements) observer.observe(el);
944
+ observerRef.current = observer;
945
+ }, 100);
946
+ return () => {
947
+ clearTimeout(timerId);
948
+ observerRef.current?.disconnect();
949
+ observerRef.current = null;
950
+ };
951
+ }, [currentPageId, showToc, filteredHeadings.map((h) => h.id).join(",")]);
952
+ const observerRef = useRef2(null);
953
+ useEffect2(() => {
954
+ setActiveHeadingId("");
955
+ }, [currentPageId]);
956
+ const scrollToHeading = useCallback2((e, id) => {
957
+ e.preventDefault();
958
+ const scrollRoot = contentRef.current;
959
+ if (!scrollRoot) return;
960
+ const target = scrollRoot.querySelector(`#${CSS.escape(id)}`);
961
+ if (target) {
962
+ target.scrollIntoView({ behavior: "smooth", block: "start" });
963
+ setActiveHeadingId(id);
964
+ }
965
+ }, []);
966
+ useEffect2(() => {
967
+ const h = (e) => {
968
+ if ((e.metaKey || e.ctrlKey) && e.key === "k") {
969
+ e.preventDefault();
970
+ setSearch(true);
971
+ }
972
+ if (e.key === "Escape") setSearch(false);
973
+ };
974
+ window.addEventListener("keydown", h);
975
+ return () => window.removeEventListener("keydown", h);
976
+ }, []);
977
+ const allNavPages = navigation2.flatMap((g) => g.pages);
978
+ const idx = allNavPages.findIndex((p) => p.id === currentPageId);
979
+ const prev = idx > 0 ? allNavPages[idx - 1] : null;
980
+ const next = idx < allNavPages.length - 1 ? allNavPages[idx + 1] : null;
981
+ const togSec = (s) => setExpanded((p) => p.includes(s) ? p.filter((x) => x !== s) : [...p, s]);
982
+ const cssVars = {
983
+ "--bg": t.bg,
984
+ "--sf": t.sf,
985
+ "--sfH": t.sfH,
986
+ "--bd": t.bd,
987
+ "--tx": t.tx,
988
+ "--tx2": t.tx2,
989
+ "--txM": t.txM,
990
+ "--ac": t.ac,
991
+ "--acD": t.acD,
992
+ "--acT": t.acT,
993
+ "--cdBg": t.cdBg,
994
+ "--cdTx": t.cdTx,
995
+ "--sbBg": t.sbBg,
996
+ "--hdBg": t.hdBg,
997
+ "--font-heading": `"${fonts.heading}", serif`,
998
+ "--font-body": `"${fonts.body}", sans-serif`,
999
+ "--font-code": `"${fonts.code}", monospace`
1000
+ };
1001
+ const PageComponent = pageComponent;
1002
+ const bannerLink = config2.banner?.link;
1003
+ const bannerIsInternal = bannerLink ? bannerLink.startsWith("#") || basePath2 && bannerLink.startsWith(basePath2 + "/") : false;
1004
+ return /* @__PURE__ */ jsxs2("div", { className: "tome-grain", style: { ...cssVars, color: "var(--tx)", background: "var(--bg)", fontFamily: "var(--font-body)", minHeight: "100vh", overflow: "hidden" }, children: [
1005
+ config2.banner?.text && !bannerDismissed && /* @__PURE__ */ jsxs2("div", { style: {
1006
+ display: "flex",
1007
+ alignItems: "center",
1008
+ justifyContent: "center",
1009
+ gap: 12,
1010
+ background: "var(--ac)",
1011
+ color: "#fff",
1012
+ padding: "8px 16px",
1013
+ fontSize: 13,
1014
+ fontFamily: "var(--font-body)",
1015
+ fontWeight: 500,
1016
+ textAlign: "center",
1017
+ width: "100%",
1018
+ boxSizing: "border-box"
1019
+ }, children: [
1020
+ config2.banner.link ? /* @__PURE__ */ jsx2(
1021
+ "a",
1022
+ {
1023
+ href: bannerIsInternal && bannerLink.startsWith("#") ? basePath2 + "/" + bannerLink.slice(1) : bannerLink,
1024
+ ...bannerIsInternal ? {} : { target: "_blank", rel: "noopener noreferrer" },
1025
+ style: { color: "#fff", textDecoration: "underline" },
1026
+ onClick: bannerIsInternal ? (e) => {
1027
+ e.preventDefault();
1028
+ const bp = basePath2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1029
+ const pageId = bannerLink.startsWith("#") ? bannerLink.slice(1) : bannerLink.replace(new RegExp("^" + bp + "/?"), "");
1030
+ onNavigate(pageId || "index");
1031
+ } : void 0,
1032
+ children: config2.banner.text
1033
+ }
1034
+ ) : /* @__PURE__ */ jsx2("span", { children: config2.banner.text }),
1035
+ config2.banner.dismissible !== false && /* @__PURE__ */ jsx2(
1036
+ "button",
1037
+ {
1038
+ onClick: () => {
1039
+ setBannerDismissed(true);
1040
+ try {
1041
+ const hash = Array.from(config2.banner.text).reduce((h, c) => (h << 5) - h + c.charCodeAt(0) | 0, 0).toString(36);
1042
+ localStorage.setItem("tome-banner-dismissed", hash);
1043
+ } catch {
1044
+ }
1045
+ },
1046
+ "aria-label": "Dismiss banner",
1047
+ style: { background: "none", border: "none", color: "#fff", cursor: "pointer", fontSize: 16, lineHeight: 1, padding: 0, opacity: 0.8 },
1048
+ children: "\xD7"
1049
+ }
1050
+ )
1051
+ ] }),
1052
+ searchOpen && config2.search?.provider === "algolia" && config2.search.appId && config2.search.apiKey && config2.search.indexName ? /* @__PURE__ */ jsx2(
1053
+ AlgoliaSearchModal,
1054
+ {
1055
+ appId: config2.search.appId,
1056
+ apiKey: config2.search.apiKey,
1057
+ indexName: config2.search.indexName,
1058
+ onNavigate: (id) => {
1059
+ onNavigate(id);
1060
+ setSearch(false);
1061
+ },
1062
+ onClose: () => setSearch(false),
1063
+ basePath: basePath2
1064
+ }
1065
+ ) : searchOpen ? /* @__PURE__ */ jsx2(
1066
+ SearchModal,
1067
+ {
1068
+ allPages,
1069
+ onNavigate: (id) => {
1070
+ onNavigate(id);
1071
+ setSearch(false);
1072
+ },
1073
+ onClose: () => setSearch(false),
1074
+ mobile
1075
+ }
1076
+ ) : null,
1077
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", flex: 1, height: config2.banner?.text && !bannerDismissed ? "calc(100vh - 32px)" : "100vh" }, children: [
1078
+ mobile && sbOpen && /* @__PURE__ */ jsx2("div", { onClick: () => setSb(false), style: {
1079
+ position: "fixed",
1080
+ inset: 0,
1081
+ zIndex: 200,
1082
+ background: "rgba(0,0,0,0.4)",
1083
+ backdropFilter: "blur(2px)"
1084
+ } }),
1085
+ /* @__PURE__ */ jsxs2("aside", { style: {
1086
+ width: sbOpen ? 270 : 0,
1087
+ minWidth: sbOpen ? 270 : 0,
1088
+ background: "var(--sbBg)",
1089
+ borderRight: "1px solid var(--bd)",
1090
+ display: "flex",
1091
+ flexDirection: "column",
1092
+ transition: "width .2s, min-width .2s",
1093
+ overflow: "hidden",
1094
+ ...mobile ? { position: "fixed", top: 0, left: 0, bottom: 0, zIndex: 201 } : {}
1095
+ }, children: [
1096
+ /* @__PURE__ */ jsxs2("a", { href: "/", style: { padding: "18px 20px", display: "flex", alignItems: "baseline", gap: 6, borderBottom: "1px solid var(--bd)", textDecoration: "none", color: "inherit" }, children: [
1097
+ /* @__PURE__ */ jsx2("span", { style: { fontFamily: "var(--font-heading)", fontSize: 22, fontWeight: 700, fontStyle: "italic" }, children: config2.name }),
1098
+ /* @__PURE__ */ jsx2("span", { style: { width: 5, height: 5, borderRadius: "50%", background: "var(--ac)", display: "inline-block" } })
1099
+ ] }),
1100
+ /* @__PURE__ */ jsx2("div", { style: { padding: "12px 14px" }, children: /* @__PURE__ */ jsxs2("button", { onClick: () => {
1101
+ setSearch(true);
1102
+ if (mobile) setSb(false);
1103
+ }, style: {
1104
+ display: "flex",
1105
+ alignItems: "center",
1106
+ gap: 8,
1107
+ width: "100%",
1108
+ background: "var(--cdBg)",
1109
+ border: "1px solid var(--bd)",
1110
+ borderRadius: 2,
1111
+ padding: "8px 12px",
1112
+ cursor: "pointer",
1113
+ color: "var(--txM)",
1114
+ fontSize: 12.5,
1115
+ fontFamily: "var(--font-body)"
1116
+ }, children: [
1117
+ /* @__PURE__ */ jsx2(SearchIcon, {}),
1118
+ /* @__PURE__ */ jsx2("span", { style: { flex: 1, textAlign: "left" }, children: "Search..." }),
1119
+ /* @__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" })
1120
+ ] }) }),
1121
+ /* @__PURE__ */ jsx2("nav", { style: { flex: 1, overflow: "auto", padding: "4px 10px 20px" }, children: navigation2.map((sec) => /* @__PURE__ */ jsxs2("div", { style: { marginBottom: 8 }, children: [
1122
+ /* @__PURE__ */ jsxs2("button", { onClick: () => togSec(sec.section), style: {
1123
+ display: "flex",
1124
+ alignItems: "center",
1125
+ gap: 6,
1126
+ width: "100%",
1127
+ background: "none",
1128
+ border: "none",
1129
+ padding: "8px 10px",
1130
+ cursor: "pointer",
1131
+ borderRadius: 2,
1132
+ color: "var(--txM)",
1133
+ fontSize: 10,
1134
+ fontWeight: 600,
1135
+ textTransform: "uppercase",
1136
+ letterSpacing: ".1em",
1137
+ fontFamily: "var(--font-code)"
1138
+ }, children: [
1139
+ expanded.includes(sec.section) ? /* @__PURE__ */ jsx2(ChevDown, {}) : /* @__PURE__ */ jsx2(ChevRight, {}),
1140
+ sec.section
1141
+ ] }),
1142
+ expanded.includes(sec.section) && /* @__PURE__ */ jsx2("div", { style: { marginLeft: 8, borderLeft: "1px solid var(--bd)", paddingLeft: 0 }, children: sec.pages.map((p) => {
1143
+ const active = currentPageId === p.id;
1144
+ return /* @__PURE__ */ jsx2("button", { onClick: () => {
1145
+ onNavigate(p.id);
1146
+ if (mobile) setSb(false);
1147
+ }, style: {
1148
+ display: "flex",
1149
+ alignItems: "center",
1150
+ gap: 10,
1151
+ width: "100%",
1152
+ textAlign: "left",
1153
+ background: "none",
1154
+ border: "none",
1155
+ borderRadius: 0,
1156
+ borderLeft: active ? "2px solid var(--ac)" : "2px solid transparent",
1157
+ padding: "7px 14px",
1158
+ cursor: "pointer",
1159
+ color: active ? "var(--ac)" : "var(--tx2)",
1160
+ fontSize: 13,
1161
+ fontWeight: active ? 500 : 400,
1162
+ fontFamily: "var(--font-body)",
1163
+ transition: "all .12s"
1164
+ }, children: p.title }, p.id);
1165
+ }) })
1166
+ ] }, sec.section)) }),
1167
+ versioning && mobile && /* @__PURE__ */ jsx2("div", { style: { padding: "8px 16px", borderTop: "1px solid var(--bd)", display: "flex", gap: 6 }, children: versioning.versions.map((v) => /* @__PURE__ */ jsxs2(
1168
+ "button",
1169
+ {
1170
+ onClick: () => {
1171
+ const targetId = v === versioning.current ? "index" : `${v}/index`;
1172
+ onNavigate(targetId);
1173
+ },
1174
+ style: {
1175
+ flex: 1,
1176
+ padding: "6px 0",
1177
+ textAlign: "center",
1178
+ background: v === (currentVersion || versioning.current) ? "var(--acD)" : "var(--sf)",
1179
+ border: "1px solid var(--bd)",
1180
+ borderRadius: 2,
1181
+ cursor: "pointer",
1182
+ color: v === (currentVersion || versioning.current) ? "var(--ac)" : "var(--tx2)",
1183
+ fontSize: 12,
1184
+ fontFamily: "var(--font-code)",
1185
+ fontWeight: v === versioning.current ? 600 : 400
1186
+ },
1187
+ children: [
1188
+ v,
1189
+ v === versioning.current ? " (latest)" : ""
1190
+ ]
1191
+ },
1192
+ v
1193
+ )) }),
1194
+ /* @__PURE__ */ jsxs2("div", { style: { padding: "12px 16px", borderTop: "1px solid var(--bd)", display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
1195
+ themeMode === "auto" ? /* @__PURE__ */ jsx2("button", { "aria-label": isDark ? "Switch to light mode" : "Switch to dark mode", 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", {}),
1196
+ /* @__PURE__ */ jsxs2("span", { style: { fontSize: 11, color: "var(--txM)", letterSpacing: 0.2 }, children: [
1197
+ "Built with ",
1198
+ "\u2661",
1199
+ " by Tome"
1200
+ ] }),
1201
+ /* @__PURE__ */ jsx2("span", { style: { fontFamily: "var(--font-code)", fontSize: 10, color: "var(--txM)" }, children: typeof __TOME_VERSION__ !== "undefined" && __TOME_VERSION__ ? `v${__TOME_VERSION__}` : "v0.1.0" })
1202
+ ] })
1203
+ ] }),
1204
+ /* @__PURE__ */ jsxs2("div", { style: { flex: 1, display: "flex", flexDirection: "column", overflow: "hidden" }, children: [
1205
+ /* @__PURE__ */ jsxs2("header", { style: {
1206
+ display: "flex",
1207
+ alignItems: "center",
1208
+ gap: mobile ? 8 : 12,
1209
+ padding: mobile ? "8px 12px" : "10px 24px",
1210
+ borderBottom: "1px solid var(--bd)",
1211
+ background: "var(--hdBg)",
1212
+ backdropFilter: "blur(12px)",
1213
+ maxWidth: "100vw",
1214
+ overflow: "visible",
1215
+ position: "relative",
1216
+ zIndex: 200
1217
+ }, children: [
1218
+ /* @__PURE__ */ jsx2("button", { "aria-label": sbOpen ? "Close sidebar" : "Open sidebar", onClick: () => setSb(!sbOpen), style: { background: "none", border: "none", color: "var(--txM)", cursor: "pointer", display: "flex" }, children: sbOpen ? /* @__PURE__ */ jsx2(XIcon, {}) : /* @__PURE__ */ jsx2(MenuIcon, {}) }),
1219
+ mobile ? /* @__PURE__ */ jsx2("span", { style: { fontSize: 13, color: "var(--ac)", fontFamily: "var(--font-code)", flex: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: navigation2.flatMap((s) => s.pages).find((p) => p.id === currentPageId)?.title || "" }) : /* @__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) => {
1220
+ const f = s.pages.find((p) => p.id === currentPageId);
1221
+ if (!f) return null;
1222
+ return /* @__PURE__ */ jsxs2("span", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
1223
+ /* @__PURE__ */ jsx2("span", { children: s.section }),
1224
+ /* @__PURE__ */ jsx2(ChevRight, {}),
1225
+ /* @__PURE__ */ jsx2("span", { style: { color: "var(--ac)" }, children: f.title })
1226
+ ] }, s.section);
1227
+ }) }),
1228
+ config2.topNav && config2.topNav.length > 0 && !mobile && /* @__PURE__ */ jsxs2("div", { style: { display: "flex", alignItems: "center", gap: 12 }, children: [
1229
+ config2.topNav.map((link) => {
1230
+ const isInternal = link.href.startsWith("#") || basePath2 && link.href.startsWith(basePath2 + "/");
1231
+ const isExternal = !isInternal;
1232
+ return /* @__PURE__ */ jsxs2(
1233
+ "a",
1234
+ {
1235
+ href: isInternal && link.href.startsWith("#") ? basePath2 + "/" + link.href.slice(1) : link.href,
1236
+ ...isExternal ? { target: "_blank", rel: "noopener noreferrer" } : {},
1237
+ onClick: isInternal ? (e) => {
1238
+ e.preventDefault();
1239
+ const pageId = link.href.startsWith("#") ? link.href.slice(1) : link.href.replace(new RegExp("^" + basePath2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "/?"), "");
1240
+ onNavigate(pageId);
1241
+ } : void 0,
1242
+ style: {
1243
+ display: "flex",
1244
+ alignItems: "center",
1245
+ gap: 4,
1246
+ color: "var(--txM)",
1247
+ textDecoration: "none",
1248
+ fontSize: 12,
1249
+ fontFamily: "var(--font-body)",
1250
+ fontWeight: 500,
1251
+ transition: "color .15s"
1252
+ },
1253
+ onMouseOver: (e) => e.currentTarget.style.color = "var(--ac)",
1254
+ onMouseOut: (e) => e.currentTarget.style.color = "var(--txM)",
1255
+ children: [
1256
+ link.label,
1257
+ isExternal && /* @__PURE__ */ jsx2(ExtLinkIcon, {})
1258
+ ]
1259
+ },
1260
+ link.label
1261
+ );
1262
+ }),
1263
+ /* @__PURE__ */ jsx2("span", { style: { width: 1, height: 16, background: "var(--bd)" } })
1264
+ ] }),
1265
+ mobile && themeMode === "auto" && /* @__PURE__ */ jsx2("button", { "aria-label": isDark ? "Switch to light mode" : "Switch to dark mode", onClick: () => setDark((d) => !d), style: { background: "none", border: "none", color: "var(--txM)", cursor: "pointer", display: "flex", flexShrink: 0 }, children: isDark ? /* @__PURE__ */ jsx2(SunIcon, {}) : /* @__PURE__ */ jsx2(MoonIcon, {}) }),
1266
+ versioning && !mobile && /* @__PURE__ */ jsxs2("div", { style: { position: "relative" }, children: [
1267
+ /* @__PURE__ */ jsxs2(
1268
+ "button",
1269
+ {
1270
+ "data-testid": "version-switcher",
1271
+ onClick: () => setVersionDropdown((o) => !o),
1272
+ style: {
1273
+ display: "flex",
1274
+ alignItems: "center",
1275
+ gap: 6,
1276
+ background: "var(--sf)",
1277
+ border: "1px solid var(--bd)",
1278
+ borderRadius: 2,
1279
+ padding: "5px 10px",
1280
+ cursor: "pointer",
1281
+ color: "var(--tx2)",
1282
+ fontSize: 12,
1283
+ fontFamily: "var(--font-code)"
1284
+ },
1285
+ children: [
1286
+ /* @__PURE__ */ jsx2(VersionIcon, {}),
1287
+ currentVersion || versioning.current,
1288
+ /* @__PURE__ */ jsx2(ChevDown, {})
1289
+ ]
1290
+ }
1291
+ ),
1292
+ versionDropdownOpen && /* @__PURE__ */ jsx2(
1293
+ "div",
1294
+ {
1295
+ "data-testid": "version-dropdown",
1296
+ style: {
1297
+ position: "absolute",
1298
+ top: "100%",
1299
+ right: 0,
1300
+ marginTop: 4,
1301
+ background: "var(--sf)",
1302
+ border: "1px solid var(--bd)",
1303
+ borderRadius: 2,
1304
+ boxShadow: "0 8px 32px rgba(0,0,0,0.2)",
1305
+ overflow: "hidden",
1306
+ zIndex: 100,
1307
+ minWidth: 120
1308
+ },
1309
+ children: versioning.versions.map((v) => /* @__PURE__ */ jsxs2(
1310
+ "button",
1311
+ {
1312
+ onClick: () => {
1313
+ setVersionDropdown(false);
1314
+ const targetId = v === versioning.current ? "index" : `${v}/index`;
1315
+ onNavigate(targetId);
1316
+ },
1317
+ style: {
1318
+ display: "block",
1319
+ width: "100%",
1320
+ textAlign: "left",
1321
+ background: v === (currentVersion || versioning.current) ? "var(--acD)" : "none",
1322
+ border: "none",
1323
+ padding: "8px 14px",
1324
+ cursor: "pointer",
1325
+ color: v === (currentVersion || versioning.current) ? "var(--ac)" : "var(--tx2)",
1326
+ fontSize: 12,
1327
+ fontFamily: "var(--font-code)",
1328
+ fontWeight: v === versioning.current ? 600 : 400
1329
+ },
1330
+ children: [
1331
+ v,
1332
+ v === versioning.current ? " (latest)" : ""
1333
+ ]
1334
+ },
1335
+ v
1336
+ ))
1337
+ }
1338
+ )
1339
+ ] }),
1340
+ i18n && i18n.locales.length > 1 && !mobile && /* @__PURE__ */ jsxs2("div", { style: { position: "relative" }, children: [
1341
+ /* @__PURE__ */ jsxs2(
1342
+ "button",
1343
+ {
1344
+ "data-testid": "language-switcher",
1345
+ onClick: () => setLocaleDropdown((o) => !o),
1346
+ style: {
1347
+ display: "flex",
1348
+ alignItems: "center",
1349
+ gap: 6,
1350
+ background: "var(--sf)",
1351
+ border: "1px solid var(--bd)",
1352
+ borderRadius: 2,
1353
+ padding: "5px 10px",
1354
+ cursor: "pointer",
1355
+ color: "var(--tx2)",
1356
+ fontSize: 12,
1357
+ fontFamily: "var(--font-body)"
1358
+ },
1359
+ children: [
1360
+ /* @__PURE__ */ jsx2(GlobeIcon, {}),
1361
+ i18n.localeNames?.[currentLocale || i18n.defaultLocale] || currentLocale || i18n.defaultLocale,
1362
+ /* @__PURE__ */ jsx2(ChevDown, {})
1363
+ ]
1364
+ }
1365
+ ),
1366
+ localeDropdownOpen && /* @__PURE__ */ jsx2(
1367
+ "div",
1368
+ {
1369
+ "data-testid": "language-dropdown",
1370
+ style: {
1371
+ position: "absolute",
1372
+ top: "100%",
1373
+ right: 0,
1374
+ marginTop: 4,
1375
+ background: "var(--sf)",
1376
+ border: "1px solid var(--bd)",
1377
+ borderRadius: 2,
1378
+ boxShadow: "0 8px 32px rgba(0,0,0,0.2)",
1379
+ overflow: "hidden",
1380
+ zIndex: 100,
1381
+ minWidth: 120
1382
+ },
1383
+ children: i18n.locales.map((locale) => {
1384
+ const isActive = locale === (currentLocale || i18n.defaultLocale);
1385
+ const displayName = i18n.localeNames?.[locale] || locale;
1386
+ const activeLocale = currentLocale || i18n.defaultLocale;
1387
+ let basePageId = currentPageId;
1388
+ if (activeLocale !== i18n.defaultLocale && currentPageId.startsWith(`${activeLocale}/`)) {
1389
+ basePageId = currentPageId.slice(activeLocale.length + 1);
1390
+ }
1391
+ const targetId = locale === i18n.defaultLocale ? basePageId : `${locale}/${basePageId}`;
1392
+ return /* @__PURE__ */ jsx2(
1393
+ "button",
1394
+ {
1395
+ onClick: () => {
1396
+ setLocaleDropdown(false);
1397
+ onNavigate(targetId);
1398
+ },
1399
+ style: {
1400
+ display: "block",
1401
+ width: "100%",
1402
+ textAlign: "left",
1403
+ background: isActive ? "var(--acD)" : "none",
1404
+ border: "none",
1405
+ padding: "8px 14px",
1406
+ cursor: "pointer",
1407
+ color: isActive ? "var(--ac)" : "var(--tx2)",
1408
+ fontSize: 12,
1409
+ fontFamily: "var(--font-body)",
1410
+ fontWeight: isActive ? 600 : 400
1411
+ },
1412
+ children: displayName
1413
+ },
1414
+ locale
1415
+ );
1416
+ })
1417
+ }
1418
+ )
1419
+ ] })
1420
+ ] }),
1421
+ isOldVersion && /* @__PURE__ */ jsxs2(
1422
+ "div",
1423
+ {
1424
+ "data-testid": "old-version-banner",
1425
+ style: {
1426
+ display: "flex",
1427
+ alignItems: "center",
1428
+ justifyContent: "center",
1429
+ gap: 8,
1430
+ background: "var(--acD)",
1431
+ borderBottom: "1px solid var(--bd)",
1432
+ padding: "8px 24px",
1433
+ fontSize: 13,
1434
+ color: "var(--tx2)"
1435
+ },
1436
+ children: [
1437
+ /* @__PURE__ */ jsxs2("span", { children: [
1438
+ "You're viewing docs for ",
1439
+ currentVersion,
1440
+ "."
1441
+ ] }),
1442
+ /* @__PURE__ */ jsx2(
1443
+ "button",
1444
+ {
1445
+ onClick: () => {
1446
+ onNavigate("index");
1447
+ },
1448
+ style: {
1449
+ background: "none",
1450
+ border: "none",
1451
+ color: "var(--ac)",
1452
+ cursor: "pointer",
1453
+ fontWeight: 600,
1454
+ fontSize: 13,
1455
+ fontFamily: "var(--font-body)",
1456
+ textDecoration: "underline"
1457
+ },
1458
+ children: "Switch to latest."
1459
+ }
1460
+ )
1461
+ ]
1462
+ }
1463
+ ),
1464
+ /* @__PURE__ */ jsxs2("div", { ref: contentRef, style: { flex: 1, overflow: "auto", display: "flex" }, children: [
1465
+ /* @__PURE__ */ jsxs2("main", { style: { flex: 1, maxWidth: mobile ? "100%" : 760, padding: mobile ? "24px 16px 60px" : "40px 48px 80px", margin: "0 auto", minWidth: 0 }, children: [
1466
+ /* @__PURE__ */ jsx2("h1", { style: { fontFamily: "var(--font-heading)", fontSize: mobile ? 26 : 38, fontWeight: 400, fontStyle: "italic", lineHeight: 1.15, marginBottom: 8 }, children: pageTitle }),
1467
+ pageDescription && /* @__PURE__ */ jsx2("p", { style: { fontSize: 16, color: "var(--tx2)", lineHeight: 1.6, marginBottom: 32 }, children: pageDescription }),
1468
+ /* @__PURE__ */ jsx2("div", { style: { borderTop: "1px solid var(--bd)", paddingTop: 28 }, children: changelogEntries && changelogEntries.length > 0 ? /* @__PURE__ */ jsx2(ChangelogView, { entries: changelogEntries }) : PageComponent ? /* @__PURE__ */ jsx2("div", { className: "tome-content", children: /* @__PURE__ */ jsx2(PageComponent, { components: mdxComponents || {} }) }) : /* @__PURE__ */ jsx2(
1469
+ "div",
1470
+ {
1471
+ className: "tome-content",
1472
+ dangerouslySetInnerHTML: { __html: (pageHtml || "").replace(/<h1[^>]*>[\s\S]*?<\/h1>\s*/, "") }
1473
+ }
1474
+ ) }),
1475
+ (editUrl || lastUpdated) && /* @__PURE__ */ jsxs2("div", { style: { marginTop: 40, display: "flex", flexDirection: mobile ? "column" : "row", alignItems: mobile ? "flex-start" : "center", justifyContent: "space-between", gap: mobile ? 8 : 16 }, children: [
1476
+ editUrl && /* @__PURE__ */ jsx2("div", { "data-testid": "edit-page-link", children: /* @__PURE__ */ jsxs2(
1477
+ "a",
1478
+ {
1479
+ href: editUrl,
1480
+ target: "_blank",
1481
+ rel: "noopener noreferrer",
1482
+ style: {
1483
+ display: "inline-flex",
1484
+ alignItems: "center",
1485
+ gap: 6,
1486
+ color: "var(--txM)",
1487
+ textDecoration: "none",
1488
+ fontSize: 13,
1489
+ fontFamily: "var(--font-body)",
1490
+ transition: "color .15s"
1491
+ },
1492
+ onMouseOver: (e) => e.currentTarget.style.color = "var(--ac)",
1493
+ onMouseOut: (e) => e.currentTarget.style.color = "var(--txM)",
1494
+ children: [
1495
+ /* @__PURE__ */ jsx2(PencilIcon, {}),
1496
+ " Edit this page on GitHub"
1497
+ ]
1498
+ }
1499
+ ) }),
1500
+ lastUpdated && /* @__PURE__ */ jsxs2("div", { "data-testid": "last-updated", style: { fontSize: 12, color: "var(--txM)", fontFamily: "var(--font-body)" }, children: [
1501
+ "Last updated ",
1502
+ formatRelativeDate(lastUpdated)
1503
+ ] })
1504
+ ] }),
1505
+ /* @__PURE__ */ jsx2("div", { style: { display: "flex", alignItems: "center", gap: 12, marginTop: 24, padding: "12px 0" }, children: feedbackGiven[currentPageId] ? /* @__PURE__ */ jsx2("span", { style: { fontSize: 13, color: "var(--txM)", fontFamily: "var(--font-body)" }, children: "Thanks for your feedback!" }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
1506
+ /* @__PURE__ */ jsx2("span", { style: { fontSize: 13, color: "var(--txM)", fontFamily: "var(--font-body)" }, children: "Was this helpful?" }),
1507
+ /* @__PURE__ */ jsx2("button", { onClick: () => {
1508
+ setFeedbackGiven((prev2) => ({ ...prev2, [currentPageId]: true }));
1509
+ try {
1510
+ localStorage.setItem(`tome-feedback-${currentPageId}`, "up");
1511
+ } catch {
1512
+ }
1513
+ }, style: {
1514
+ background: "none",
1515
+ border: "1px solid var(--bd)",
1516
+ borderRadius: 2,
1517
+ padding: "4px 10px",
1518
+ cursor: "pointer",
1519
+ fontSize: 13,
1520
+ color: "var(--txM)",
1521
+ transition: "border-color .15s"
1522
+ }, children: "\u{1F44D}" }),
1523
+ /* @__PURE__ */ jsx2("button", { onClick: () => {
1524
+ setFeedbackGiven((prev2) => ({ ...prev2, [currentPageId]: true }));
1525
+ try {
1526
+ localStorage.setItem(`tome-feedback-${currentPageId}`, "down");
1527
+ } catch {
1528
+ }
1529
+ }, style: {
1530
+ background: "none",
1531
+ border: "1px solid var(--bd)",
1532
+ borderRadius: 2,
1533
+ padding: "4px 10px",
1534
+ cursor: "pointer",
1535
+ fontSize: 13,
1536
+ color: "var(--txM)",
1537
+ transition: "border-color .15s"
1538
+ }, children: "\u{1F44E}" })
1539
+ ] }) }),
1540
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", flexDirection: mobile ? "column" : "row", justifyContent: "space-between", marginTop: 16, paddingTop: 24, borderTop: "1px solid var(--bd)", gap: mobile ? 12 : 16 }, children: [
1541
+ prev ? /* @__PURE__ */ jsxs2("button", { onClick: () => onNavigate(prev.id), style: {
1542
+ display: "flex",
1543
+ alignItems: "center",
1544
+ gap: 8,
1545
+ background: "none",
1546
+ border: "1px solid var(--bd)",
1547
+ borderRadius: 2,
1548
+ padding: "10px 16px",
1549
+ cursor: "pointer",
1550
+ color: "var(--tx2)",
1551
+ fontSize: 13,
1552
+ fontFamily: "var(--font-body)",
1553
+ transition: "border-color .15s, color .15s"
1554
+ }, children: [
1555
+ /* @__PURE__ */ jsx2(ArrowLeft, {}),
1556
+ " ",
1557
+ prev.title
1558
+ ] }) : /* @__PURE__ */ jsx2("div", {}),
1559
+ next ? /* @__PURE__ */ jsxs2("button", { onClick: () => onNavigate(next.id), style: {
1560
+ display: "flex",
1561
+ alignItems: "center",
1562
+ gap: 8,
1563
+ background: "none",
1564
+ border: "1px solid var(--bd)",
1565
+ borderRadius: 2,
1566
+ padding: "10px 16px",
1567
+ cursor: "pointer",
1568
+ color: "var(--tx2)",
1569
+ fontSize: 13,
1570
+ fontFamily: "var(--font-body)",
1571
+ transition: "border-color .15s, color .15s"
1572
+ }, children: [
1573
+ next.title,
1574
+ " ",
1575
+ /* @__PURE__ */ jsx2(ArrowRight, {})
1576
+ ] }) : /* @__PURE__ */ jsx2("div", {})
1577
+ ] })
1578
+ ] }),
1579
+ showToc && filteredHeadings.length >= 2 && wide && /* @__PURE__ */ jsxs2("aside", { "data-testid": "toc-sidebar", style: { width: 200, padding: "40px 16px 40px 0", position: "sticky", top: 0, alignSelf: "flex-start", flexShrink: 0 }, children: [
1580
+ /* @__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" }),
1581
+ /* @__PURE__ */ jsx2("nav", { "aria-label": "Table of contents", style: { borderLeft: "1px solid var(--bd)", paddingLeft: 0 }, children: filteredHeadings.map((h, i) => {
1582
+ const isActive = activeHeadingId === h.id;
1583
+ return /* @__PURE__ */ jsx2(
1584
+ "a",
1585
+ {
1586
+ href: `#${h.id}`,
1587
+ onClick: (e) => scrollToHeading(e, h.id),
1588
+ "data-testid": `toc-link-${h.id}`,
1589
+ style: {
1590
+ display: "block",
1591
+ fontSize: 12,
1592
+ color: isActive ? "var(--ac)" : "var(--txM)",
1593
+ fontWeight: isActive ? 500 : 400,
1594
+ textDecoration: "none",
1595
+ padding: "4px 12px",
1596
+ paddingLeft: 12 + (h.depth - 2) * 12,
1597
+ lineHeight: 1.4,
1598
+ transition: "color .15s, font-weight .15s",
1599
+ borderLeft: isActive ? "2px solid var(--ac)" : "2px solid transparent",
1600
+ marginLeft: -1
1601
+ },
1602
+ children: h.text
1603
+ },
1604
+ i
1605
+ );
1606
+ }) })
1607
+ ] })
1608
+ ] })
1609
+ ] })
1610
+ ] }),
1611
+ config2.ai?.enabled && /* @__PURE__ */ jsx2(
1612
+ AiChat,
1613
+ {
1614
+ provider: config2.ai.provider || "anthropic",
1615
+ model: config2.ai.model,
1616
+ apiKey: typeof __TOME_AI_API_KEY__ !== "undefined" && __TOME_AI_API_KEY__ ? __TOME_AI_API_KEY__ : void 0,
1617
+ context: docContext2?.map((d) => `## ${d.title}
1618
+ ${d.content}`).join("\n\n") ?? allPages.map((p) => `- ${p.title}${p.description ? ": " + p.description : ""}`).join("\n")
1619
+ }
1620
+ ),
1621
+ zoomSrc && /* @__PURE__ */ jsx2("div", { onClick: () => setZoomSrc(null), style: {
1622
+ position: "fixed",
1623
+ inset: 0,
1624
+ zIndex: 9999,
1625
+ display: "flex",
1626
+ alignItems: "center",
1627
+ justifyContent: "center",
1628
+ background: "rgba(0,0,0,0.7)",
1629
+ backdropFilter: "blur(8px)",
1630
+ cursor: "zoom-out"
1631
+ }, children: /* @__PURE__ */ jsx2("img", { src: zoomSrc, alt: "", style: { maxWidth: "90vw", maxHeight: "90vh", objectFit: "contain", borderRadius: 4, boxShadow: "0 16px 64px rgba(0,0,0,0.4)" } }) })
1632
+ ] });
1633
+ }
1634
+ function SearchModal({ allPages, onNavigate, onClose, mobile }) {
1635
+ const [q, setQ] = useState2("");
1636
+ const [results, setResults] = useState2([]);
1637
+ const [selected, setSelected] = useState2(0);
1638
+ const [pagefindReady, setPagefindReady] = useState2(null);
1639
+ const inputRef = useRef2(null);
1640
+ const debounceRef = useRef2(void 0);
1641
+ useEffect2(() => {
1642
+ initPagefind().then((pf) => setPagefindReady(!!pf));
1643
+ setTimeout(() => inputRef.current?.focus(), 50);
1644
+ }, []);
1645
+ const fallbackSearch = useCallback2((query) => {
1646
+ if (!query.trim()) return [];
1647
+ const ql = query.toLowerCase();
1648
+ 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 }));
1649
+ }, [allPages]);
1650
+ const doSearch = useCallback2(async (query) => {
1651
+ if (!query.trim()) {
1652
+ setResults([]);
1653
+ setSelected(0);
1654
+ return;
1655
+ }
1656
+ const pf = pagefindInstance;
1657
+ if (pf) {
1658
+ try {
1659
+ const search = await pf.search(query);
1660
+ const items = [];
1661
+ for (const result of search.results.slice(0, 8)) {
1662
+ const data = await result.data();
1663
+ const url = data.url || "";
1664
+ const id = url.replace(/^\//, "").replace(/\/index\.html$/, "").replace(/\.html$/, "") || "index";
1665
+ items.push({
1666
+ id,
1667
+ title: data.meta?.title || id,
1668
+ excerpt: data.excerpt || void 0
1669
+ });
1670
+ }
1671
+ setResults(items);
1672
+ setSelected(0);
1673
+ return;
1674
+ } catch {
1675
+ }
1676
+ }
1677
+ setResults(fallbackSearch(query));
1678
+ setSelected(0);
1679
+ }, [fallbackSearch]);
1680
+ useEffect2(() => {
1681
+ if (debounceRef.current) clearTimeout(debounceRef.current);
1682
+ debounceRef.current = setTimeout(() => doSearch(q), 120);
1683
+ return () => {
1684
+ if (debounceRef.current) clearTimeout(debounceRef.current);
1685
+ };
1686
+ }, [q, doSearch]);
1687
+ const handleKeyDown = useCallback2((e) => {
1688
+ if (e.key === "ArrowDown") {
1689
+ e.preventDefault();
1690
+ setSelected((i) => Math.min(i + 1, results.length - 1));
1691
+ } else if (e.key === "ArrowUp") {
1692
+ e.preventDefault();
1693
+ setSelected((i) => Math.max(i - 1, 0));
1694
+ } else if (e.key === "Enter" && results.length > 0) {
1695
+ e.preventDefault();
1696
+ onNavigate(results[selected].id);
1697
+ }
1698
+ }, [results, selected, onNavigate]);
1699
+ return /* @__PURE__ */ jsx2("div", { onClick: onClose, style: {
1700
+ position: "fixed",
1701
+ inset: 0,
1702
+ zIndex: 1e3,
1703
+ background: "rgba(0,0,0,0.55)",
1704
+ backdropFilter: "blur(6px)",
1705
+ display: "flex",
1706
+ alignItems: mobile ? "stretch" : "flex-start",
1707
+ justifyContent: "center",
1708
+ paddingTop: mobile ? 0 : "12vh"
1709
+ }, children: /* @__PURE__ */ jsxs2("div", { onClick: (e) => e.stopPropagation(), style: {
1710
+ background: "var(--sf)",
1711
+ border: mobile ? "none" : "1px solid var(--bd)",
1712
+ borderRadius: mobile ? 0 : 2,
1713
+ width: "100%",
1714
+ maxWidth: mobile ? "100%" : 520,
1715
+ boxShadow: mobile ? "none" : "0 24px 80px rgba(0,0,0,0.4)",
1716
+ overflow: "hidden",
1717
+ display: "flex",
1718
+ flexDirection: "column",
1719
+ ...mobile ? { height: "100%" } : {}
1720
+ }, children: [
1721
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", alignItems: "center", gap: 10, padding: "14px 18px", borderBottom: "1px solid var(--bd)" }, children: [
1722
+ /* @__PURE__ */ jsx2(SearchIcon, {}),
1723
+ /* @__PURE__ */ jsx2(
1724
+ "input",
1725
+ {
1726
+ ref: inputRef,
1727
+ value: q,
1728
+ onChange: (e) => setQ(e.target.value),
1729
+ onKeyDown: handleKeyDown,
1730
+ placeholder: "Search documentation...",
1731
+ style: { flex: 1, background: "none", border: "none", outline: "none", color: "var(--tx)", fontSize: 15, fontFamily: "var(--font-body)" }
1732
+ }
1733
+ ),
1734
+ /* @__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" })
1735
+ ] }),
1736
+ results.length > 0 && /* @__PURE__ */ jsx2("div", { style: { padding: 6, maxHeight: mobile ? "none" : 360, overflow: "auto", flex: mobile ? 1 : void 0 }, children: results.map((r, i) => /* @__PURE__ */ jsxs2(
1737
+ "button",
1738
+ {
1739
+ onClick: () => onNavigate(r.id),
1740
+ style: {
1741
+ display: "block",
1742
+ width: "100%",
1743
+ textAlign: "left",
1744
+ background: i === selected ? "var(--acD)" : "none",
1745
+ border: "none",
1746
+ borderRadius: 2,
1747
+ padding: "10px 14px",
1748
+ cursor: "pointer",
1749
+ color: "var(--tx)",
1750
+ fontFamily: "var(--font-body)"
1751
+ },
1752
+ onMouseEnter: () => setSelected(i),
1753
+ children: [
1754
+ /* @__PURE__ */ jsx2("div", { style: { fontWeight: 500, fontSize: 14, marginBottom: 2 }, children: r.title }),
1755
+ r.excerpt && /* @__PURE__ */ jsx2("div", { style: {
1756
+ fontSize: 12,
1757
+ color: "var(--txM)",
1758
+ lineHeight: 1.3
1759
+ }, dangerouslySetInnerHTML: { __html: r.excerpt } })
1760
+ ]
1761
+ },
1762
+ r.id + i
1763
+ )) }),
1764
+ q && !results.length && /* @__PURE__ */ jsx2("div", { style: { padding: "32px 18px", textAlign: "center", color: "var(--txM)", fontSize: 14 }, children: "No results found" }),
1765
+ 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." })
1766
+ ] }) });
1767
+ }
1768
+
1769
+ // src/entry.tsx
1770
+ import config from "virtual:tome/config";
1771
+ import { routes, navigation, versions } from "virtual:tome/routes";
1772
+ import loadPageModule from "virtual:tome/page-loader";
1773
+ import docContext from "virtual:tome/doc-context";
1774
+ import {
1775
+ Callout,
1776
+ Tabs,
1777
+ Card,
1778
+ CardGroup,
1779
+ Steps,
1780
+ Accordion,
1781
+ ChangelogTimeline,
1782
+ PackageManager,
1783
+ TypeTable,
1784
+ FileTree
1785
+ } from "@tomehq/components";
1786
+ import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1787
+ var MDX_COMPONENTS = {
1788
+ Callout,
1789
+ Tabs,
1790
+ Card,
1791
+ CardGroup,
1792
+ Steps,
1793
+ Accordion,
1794
+ ChangelogTimeline,
1795
+ PackageManager,
1796
+ TypeTable,
1797
+ FileTree
1798
+ // Sub-components accessible as <FileTree.File /> and <FileTree.Folder /> in MDX
1799
+ };
1800
+ var contentStyles = `
1801
+ @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');
1802
+
1803
+ .tome-content h1 { display: none; }
1804
+ .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; }
1805
+ .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; }
1806
+ .tome-content h3 { font-family: var(--font-body); font-size: 1.15em; font-weight: 600; margin-top: 1.5em; margin-bottom: 0.5em; }
1807
+ .tome-content h4 { font-family: var(--font-body); font-size: 1.05em; font-weight: 600; margin-top: 1.2em; margin-bottom: 0.5em; }
1808
+ .tome-content p { color: var(--tx2); line-height: 1.8; margin-bottom: 1em; font-size: 14.5px; }
1809
+ .tome-content a { color: var(--ac); text-decoration: none; }
1810
+ .tome-content a:hover { text-decoration: underline; }
1811
+ .tome-content .heading-anchor { display: none; }
1812
+ .tome-content ul, .tome-content ol { color: var(--tx2); padding-left: 1.5em; margin-bottom: 1em; }
1813
+ .tome-content li { margin-bottom: 0.3em; line-height: 1.7; }
1814
+ .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); }
1815
+ .tome-content pre { margin-bottom: 1.2em; border-radius: 2px; overflow-x: auto; border: 1px solid var(--bd); }
1816
+ .tome-content pre code { background: none; padding: 1em 1.2em; display: block; font-size: 12.5px; line-height: 1.7; color: var(--cdTx); }
1817
+ .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; }
1818
+ .tome-content blockquote p { color: var(--tx2); margin: 0; }
1819
+ .tome-content table { width: 100%; border-collapse: collapse; margin-bottom: 1em; }
1820
+ .tome-content th, .tome-content td { padding: 0.5em 0.8em; border: 1px solid var(--bd); text-align: left; font-size: 0.9em; }
1821
+ .tome-content th { background: var(--sf); font-weight: 600; }
1822
+ .tome-content img { max-width: 100%; border-radius: 2px; cursor: zoom-in; }
1823
+ .tome-content hr { border: none; border-top: 1px solid var(--bd); margin: 2em 0; }
1824
+ .tome-mermaid { margin: 1.2em 0; text-align: center; overflow-x: auto; }
1825
+ .tome-mermaid svg { max-width: 100%; height: auto; overflow: visible; }
1826
+ .tome-mermaid svg .nodeLabel { overflow: visible; white-space: nowrap; }
1827
+
1828
+ /* Mobile responsive content */
1829
+ @media (max-width: 767px) {
1830
+ .tome-content h2 { font-size: 1.2em; margin-top: 1.5em; }
1831
+ .tome-content h3 { font-size: 1.05em; }
1832
+ .tome-content pre code { font-size: 12px; padding: 0.8em 1em; }
1833
+ .tome-content table { display: block; overflow-x: auto; -webkit-overflow-scrolling: touch; }
1834
+ .tome-content blockquote { margin: 0.8em 0; }
1835
+ }
1836
+
1837
+ /* Selection style */
1838
+ ::selection { background: var(--acD); color: var(--ac); }
1839
+
1840
+ /* Scrollbar style */
1841
+ ::-webkit-scrollbar { width: 5px; height: 5px; }
1842
+ ::-webkit-scrollbar-track { background: transparent; }
1843
+ ::-webkit-scrollbar-thumb { background: var(--bd); border-radius: 10px; }
1844
+
1845
+ /* Grain overlay */
1846
+ .tome-grain::before {
1847
+ content: ""; position: fixed; inset: 0; z-index: 9999; pointer-events: none;
1848
+ opacity: .35;
1849
+ 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");
1850
+ background-repeat: repeat; background-size: 256px;
1851
+ }
1852
+
1853
+ /* Shiki dual-theme support */
1854
+ .shiki { background: var(--cdBg) !important; }
1855
+
1856
+ /* Dark mode: switch Shiki tokens from light-theme inline colors to --shiki-dark CSS vars */
1857
+ html.dark .shiki,
1858
+ html.dark .shiki span {
1859
+ color: var(--shiki-dark) !important;
1860
+ }
1861
+
1862
+ /* Brighten dim comment tokens (github-dark #6A737D is too low-contrast on dark backgrounds) */
1863
+ html.dark .shiki span[style*="--shiki-dark:#6A737D"] {
1864
+ --shiki-dark: #a0aab5 !important;
1865
+ }
1866
+
1867
+ /* Light mode: darken low-contrast github-light tokens for WCAG AA on --cdBg backgrounds */
1868
+ html:not(.dark) .shiki span[style*="color:#6A737D"] { color: #57606a !important; }
1869
+ html:not(.dark) .shiki span[style*="color:#E36209"] { color: #b35405 !important; }
1870
+ html:not(.dark) .shiki span[style*="color:#6F42C1"] { color: #5a32a3 !important; }
1871
+ html:not(.dark) .shiki span[style*="color:#22863A"] { color: #1a6e2e !important; }
1872
+ html:not(.dark) .shiki span[style*="color:#D73A49"] { color: #b62324 !important; }
1873
+ html:not(.dark) .shiki span[style*="color:#005CC5"] { color: #0349b4 !important; }
1874
+ `;
1875
+ async function loadPage(id) {
1876
+ try {
1877
+ const route = routes.find((r) => r.id === id);
1878
+ const mod = await loadPageModule(id);
1879
+ if (route?.isMdx && mod.meta) {
1880
+ return {
1881
+ isMdx: true,
1882
+ component: mod.default,
1883
+ frontmatter: mod.meta.frontmatter,
1884
+ headings: mod.meta.headings
1885
+ };
1886
+ }
1887
+ if (!mod.default) return null;
1888
+ if (mod.isChangelog && mod.changelogEntries) {
1889
+ return { isMdx: false, ...mod.default, changelogEntries: mod.changelogEntries };
1890
+ }
1891
+ return { isMdx: false, ...mod.default };
1892
+ } catch (err) {
1893
+ console.error(`Failed to load page: ${id}`, err);
1894
+ return null;
1895
+ }
1896
+ }
1897
+ var basePath = (config.basePath || "/").replace(/\/$/, "");
1898
+ function pathnameToPageId(pathname) {
1899
+ let relative = pathname;
1900
+ if (basePath && relative.startsWith(basePath)) {
1901
+ relative = relative.slice(basePath.length);
1902
+ }
1903
+ const id = relative.replace(/^\//, "").replace(/\/index\.html$/, "").replace(/\.html$/, "").replace(/\/$/, "") || "index";
1904
+ const route = routes.find((r) => r.id === id);
1905
+ return route ? id : null;
1906
+ }
1907
+ function pageIdToPath(id) {
1908
+ const route = routes.find((r) => r.id === id);
1909
+ if (route) return basePath + route.urlPath;
1910
+ return basePath + "/" + id;
1911
+ }
1912
+ function App() {
1913
+ const [currentPageId, setCurrentPageId] = useState3(() => {
1914
+ const resolved = pathnameToPageId(window.location.pathname);
1915
+ if (resolved) return resolved;
1916
+ const hash = window.location.hash.slice(1);
1917
+ if (hash && routes.some((r) => r.id === hash)) return hash;
1918
+ return routes[0]?.id || "index";
1919
+ });
1920
+ const [pageData, setPageData] = useState3(null);
1921
+ const [loading, setLoading] = useState3(true);
1922
+ const navigateTo = useCallback3(async (id, opts) => {
1923
+ setLoading(true);
1924
+ setCurrentPageId(id);
1925
+ const fullPath = pageIdToPath(id);
1926
+ if (opts?.replace) {
1927
+ window.history.replaceState(null, "", fullPath);
1928
+ } else {
1929
+ window.history.pushState(null, "", fullPath);
1930
+ }
1931
+ const data = await loadPage(id);
1932
+ setPageData(data);
1933
+ setLoading(false);
1934
+ if (!opts?.skipScroll) {
1935
+ const anchor = window.location.hash.slice(1);
1936
+ if (anchor) {
1937
+ requestAnimationFrame(() => {
1938
+ const el = document.getElementById(anchor);
1939
+ if (el) el.scrollIntoView({ behavior: "smooth", block: "start" });
1940
+ });
1941
+ } else {
1942
+ window.scrollTo(0, 0);
1943
+ }
1944
+ }
1945
+ }, []);
1946
+ useEffect3(() => {
1947
+ const hash = window.location.hash.slice(1);
1948
+ if (hash && routes.some((r) => r.id === hash)) {
1949
+ const fullPath = pageIdToPath(hash);
1950
+ window.history.replaceState(null, "", fullPath);
1951
+ navigateTo(hash, { replace: true });
1952
+ } else {
1953
+ navigateTo(currentPageId, { replace: true, skipScroll: true });
1954
+ }
1955
+ }, []);
1956
+ useEffect3(() => {
1957
+ const onPopState = () => {
1958
+ const id = pathnameToPageId(window.location.pathname);
1959
+ if (id && id !== currentPageId) {
1960
+ navigateTo(id, { replace: true, skipScroll: true });
1961
+ }
1962
+ };
1963
+ window.addEventListener("popstate", onPopState);
1964
+ return () => window.removeEventListener("popstate", onPopState);
1965
+ }, [currentPageId, navigateTo]);
1966
+ useEffect3(() => {
1967
+ const els = document.querySelectorAll(".tome-mermaid[data-mermaid]");
1968
+ if (els.length === 0) return;
1969
+ let cancelled = false;
1970
+ const MERMAID_CDN = "https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs";
1971
+ (async () => {
1972
+ try {
1973
+ const { default: mermaid } = await import(
1974
+ /* @vite-ignore */
1975
+ MERMAID_CDN
1976
+ );
1977
+ if (cancelled) return;
1978
+ const isDark = document.documentElement.classList.contains("dark");
1979
+ const resolvedFont = getComputedStyle(document.documentElement).getPropertyValue("--font-body").trim() || "sans-serif";
1980
+ mermaid.initialize({
1981
+ startOnLoad: false,
1982
+ theme: isDark ? "dark" : "default",
1983
+ fontFamily: resolvedFont,
1984
+ flowchart: { padding: 15, nodeSpacing: 30, rankSpacing: 40 }
1985
+ });
1986
+ for (let i = 0; i < els.length; i++) {
1987
+ const el = els[i];
1988
+ if (el.querySelector("svg")) continue;
1989
+ const encoded = el.getAttribute("data-mermaid");
1990
+ if (!encoded) continue;
1991
+ try {
1992
+ const code = atob(encoded);
1993
+ const { svg } = await mermaid.render(`tome-mermaid-${i}-${Date.now()}`, code);
1994
+ if (!cancelled) {
1995
+ try {
1996
+ const DOMPurify = (await import(
1997
+ /* @vite-ignore */
1998
+ "https://cdn.jsdelivr.net/npm/dompurify@3/dist/purify.es.mjs"
1999
+ )).default;
2000
+ el.innerHTML = DOMPurify.sanitize(svg, { USE_PROFILES: { svg: true, svgFilters: true } });
2001
+ } catch {
2002
+ el.innerHTML = svg;
2003
+ }
2004
+ }
2005
+ } catch (err) {
2006
+ console.warn("[tome] Mermaid render failed:", err);
2007
+ el.textContent = "Diagram rendering failed";
2008
+ el.style.cssText = "padding:16px;color:var(--txM);font-size:13px;border:1px dashed var(--bd);border-radius:2px;text-align:center;";
2009
+ }
2010
+ }
2011
+ } catch (err) {
2012
+ console.warn("[tome] Failed to load mermaid from CDN:", err);
2013
+ els.forEach((el) => {
2014
+ el.textContent = "Failed to load diagram renderer";
2015
+ el.style.cssText = "padding:16px;color:var(--txM);font-size:13px;border:1px dashed var(--bd);border-radius:2px;text-align:center;";
2016
+ });
2017
+ }
2018
+ })();
2019
+ return () => {
2020
+ cancelled = true;
2021
+ };
2022
+ }, [pageData, loading]);
2023
+ const allPages = routes.map((r) => ({
2024
+ id: r.id,
2025
+ title: r.frontmatter.title,
2026
+ description: r.frontmatter.description
2027
+ }));
2028
+ const currentRoute = routes.find((r) => r.id === currentPageId);
2029
+ const currentVersion = currentRoute?.version || (versions?.current ?? void 0);
2030
+ let editUrl;
2031
+ if (config.editLink && currentRoute?.filePath) {
2032
+ const { repo, branch = "main", dir = "" } = config.editLink;
2033
+ const dirPrefix = dir ? `${dir.replace(/\/$/, "")}/` : "";
2034
+ editUrl = `https://github.com/${repo}/edit/${branch}/${dirPrefix}${currentRoute.filePath}`;
2035
+ }
2036
+ useEffect3(() => {
2037
+ if (!config.math) return;
2038
+ const id = "tome-katex-css";
2039
+ if (document.getElementById(id)) return;
2040
+ const link = document.createElement("link");
2041
+ link.id = id;
2042
+ link.rel = "stylesheet";
2043
+ link.href = "https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css";
2044
+ link.crossOrigin = "anonymous";
2045
+ document.head.appendChild(link);
2046
+ }, []);
2047
+ return /* @__PURE__ */ jsxs3(Fragment2, { children: [
2048
+ /* @__PURE__ */ jsx3("style", { children: contentStyles }),
2049
+ /* @__PURE__ */ jsx3(
2050
+ Shell,
2051
+ {
2052
+ config,
2053
+ navigation,
2054
+ currentPageId,
2055
+ pageHtml: !pageData?.isMdx ? loading ? "<p>Loading...</p>" : pageData?.html || "<p>Page not found</p>" : void 0,
2056
+ pageComponent: pageData?.isMdx ? pageData.component : void 0,
2057
+ mdxComponents: MDX_COMPONENTS,
2058
+ pageTitle: pageData?.frontmatter.title || (loading ? "Loading..." : "Not Found"),
2059
+ pageDescription: pageData?.frontmatter.description,
2060
+ headings: pageData?.headings || [],
2061
+ tocEnabled: pageData?.frontmatter.toc !== false,
2062
+ editUrl,
2063
+ lastUpdated: currentRoute?.lastUpdated,
2064
+ changelogEntries: !pageData?.isMdx ? pageData?.changelogEntries : void 0,
2065
+ onNavigate: navigateTo,
2066
+ allPages,
2067
+ docContext,
2068
+ versioning: versions || void 0,
2069
+ currentVersion,
2070
+ basePath
2071
+ }
2072
+ )
2073
+ ] });
2074
+ }
2075
+ var container = document.getElementById("tome-root");
2076
+ if (container) {
2077
+ const root = createRoot(container);
2078
+ root.render(/* @__PURE__ */ jsx3(App, {}));
2079
+ }
2080
+ var entry_default = App;
2081
+
2082
+ export {
2083
+ THEME_PRESETS,
2084
+ AiChat,
2085
+ Shell,
2086
+ entry_default
2087
+ };