@tomehq/theme 0.6.3 → 0.7.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.
@@ -1,3 +1,10 @@
1
+ import {
2
+ buildSystemPrompt,
3
+ callAnthropic,
4
+ callOpenAI,
5
+ getDefaultModel
6
+ } from "./chunk-LNWYFDZ4.js";
7
+
1
8
  // src/entry.tsx
2
9
  import { useState as useState3, useEffect as useEffect3, useCallback as useCallback3, useRef as useRef3 } from "react";
3
10
  import { createRoot } from "react-dom/client";
@@ -85,7 +92,7 @@ var THEME_PRESETS = {
85
92
  bd: "#1a1a25",
86
93
  tx: "#d4ff00",
87
94
  tx2: "#8a90a0",
88
- txM: "#6a7080",
95
+ txM: "#7e8494",
89
96
  ac: "#6666ff",
90
97
  acD: "rgba(102,102,255,0.10)",
91
98
  acT: "#8080ff",
@@ -101,7 +108,7 @@ var THEME_PRESETS = {
101
108
  bd: "#d0d4db",
102
109
  tx: "#0f1219",
103
110
  tx2: "#4a5060",
104
- txM: "#6a7080",
111
+ txM: "#5c6272",
105
112
  ac: "#2020cc",
106
113
  acD: "rgba(32,32,204,0.08)",
107
114
  acT: "#1a1aa8",
@@ -120,7 +127,7 @@ var THEME_PRESETS = {
120
127
  bd: "#21262d",
121
128
  tx: "#e6edf3",
122
129
  tx2: "#8b949e",
123
- txM: "#6e7681",
130
+ txM: "#7d858f",
124
131
  ac: "#0ea371",
125
132
  acD: "rgba(14,163,113,0.10)",
126
133
  acT: "#2dd4a0",
@@ -146,6 +153,216 @@ var THEME_PRESETS = {
146
153
  hdBg: "rgba(255,255,255,0.90)"
147
154
  },
148
155
  fonts: { heading: "Inter", body: "Inter", code: "Fira Code" }
156
+ },
157
+ ocean: {
158
+ dark: {
159
+ bg: "#0a1628",
160
+ sf: "#0f1d33",
161
+ sfH: "#142540",
162
+ bd: "#1a2e50",
163
+ tx: "#e0e8f0",
164
+ tx2: "#8ca0b8",
165
+ txM: "#7b92aa",
166
+ ac: "#0ea5e9",
167
+ acD: "rgba(14,165,233,0.10)",
168
+ acT: "#38bdf8",
169
+ cdBg: "#081320",
170
+ cdTx: "#94b4d0",
171
+ sbBg: "#0a1628",
172
+ hdBg: "rgba(10,22,40,0.90)"
173
+ },
174
+ light: {
175
+ bg: "#f0f7ff",
176
+ sf: "#ffffff",
177
+ sfH: "#e8f0fa",
178
+ bd: "#ccdcef",
179
+ tx: "#0c1929",
180
+ tx2: "#3d5a78",
181
+ txM: "#4a6a88",
182
+ ac: "#0369a1",
183
+ acD: "rgba(3,105,161,0.07)",
184
+ acT: "#025e8f",
185
+ cdBg: "#e4edf7",
186
+ cdTx: "#1a3050",
187
+ sbBg: "#eef4fc",
188
+ hdBg: "rgba(240,247,255,0.92)"
189
+ },
190
+ fonts: { heading: "Outfit", body: "Inter", code: "JetBrains Mono" }
191
+ },
192
+ rose: {
193
+ dark: {
194
+ bg: "#0f0a10",
195
+ sf: "#171118",
196
+ sfH: "#1e1620",
197
+ bd: "#2a1f2c",
198
+ tx: "#f0e4f0",
199
+ tx2: "#b09ab0",
200
+ txM: "#8a7890",
201
+ ac: "#f43f5e",
202
+ acD: "rgba(244,63,94,0.10)",
203
+ acT: "#fb7185",
204
+ cdBg: "#0d080e",
205
+ cdTx: "#c8aec8",
206
+ sbBg: "#0f0a10",
207
+ hdBg: "rgba(15,10,16,0.88)"
208
+ },
209
+ light: {
210
+ bg: "#fef7f7",
211
+ sf: "#ffffff",
212
+ sfH: "#fceef0",
213
+ bd: "#f0d4d8",
214
+ tx: "#1a0f12",
215
+ tx2: "#6b4048",
216
+ txM: "#8a5a64",
217
+ ac: "#c81e3e",
218
+ acD: "rgba(200,30,62,0.06)",
219
+ acT: "#a81835",
220
+ cdBg: "#f8eaec",
221
+ cdTx: "#3a1a22",
222
+ sbBg: "#fdf2f4",
223
+ hdBg: "rgba(254,247,247,0.92)"
224
+ },
225
+ fonts: { heading: "Playfair Display", body: "Source Sans 3", code: "Fira Code" }
226
+ },
227
+ forest: {
228
+ dark: {
229
+ bg: "#091209",
230
+ sf: "#0f1a0f",
231
+ sfH: "#152215",
232
+ bd: "#1e2e1e",
233
+ tx: "#e0f0e0",
234
+ tx2: "#8aaa8a",
235
+ txM: "#6a8a6a",
236
+ ac: "#22c55e",
237
+ acD: "rgba(34,197,94,0.10)",
238
+ acT: "#4ade80",
239
+ cdBg: "#070e07",
240
+ cdTx: "#a0c4a0",
241
+ sbBg: "#091209",
242
+ hdBg: "rgba(9,18,9,0.90)"
243
+ },
244
+ light: {
245
+ bg: "#f4faf4",
246
+ sf: "#ffffff",
247
+ sfH: "#e8f4e8",
248
+ bd: "#c8e0c8",
249
+ tx: "#0a1a0a",
250
+ tx2: "#3a5a3a",
251
+ txM: "#5a7a5a",
252
+ ac: "#15803d",
253
+ acD: "rgba(21,128,61,0.07)",
254
+ acT: "#116d34",
255
+ cdBg: "#e4f2e4",
256
+ cdTx: "#1a3a1a",
257
+ sbBg: "#eef6ee",
258
+ hdBg: "rgba(244,250,244,0.92)"
259
+ },
260
+ fonts: { heading: "Merriweather", body: "Nunito Sans", code: "Source Code Pro" }
261
+ },
262
+ slate: {
263
+ dark: {
264
+ bg: "#0f1115",
265
+ sf: "#16181e",
266
+ sfH: "#1c1f26",
267
+ bd: "#24272e",
268
+ tx: "#e2e4e8",
269
+ tx2: "#9498a0",
270
+ txM: "#808690",
271
+ ac: "#94a3b8",
272
+ acD: "rgba(148,163,184,0.10)",
273
+ acT: "#b0bec8",
274
+ cdBg: "#0c0e12",
275
+ cdTx: "#a8acb4",
276
+ sbBg: "#0f1115",
277
+ hdBg: "rgba(15,17,21,0.88)"
278
+ },
279
+ light: {
280
+ bg: "#f8fafc",
281
+ sf: "#ffffff",
282
+ sfH: "#f1f5f9",
283
+ bd: "#d8dfe7",
284
+ tx: "#0f172a",
285
+ tx2: "#475569",
286
+ txM: "#64748b",
287
+ ac: "#475569",
288
+ acD: "rgba(71,85,105,0.07)",
289
+ acT: "#3a4a5c",
290
+ cdBg: "#f0f4f8",
291
+ cdTx: "#1e293b",
292
+ sbBg: "#f5f7fa",
293
+ hdBg: "rgba(248,250,252,0.92)"
294
+ },
295
+ fonts: { heading: "Inter", body: "Inter", code: "JetBrains Mono" }
296
+ },
297
+ sunset: {
298
+ dark: {
299
+ bg: "#120c06",
300
+ sf: "#1a1208",
301
+ sfH: "#22180c",
302
+ bd: "#2e2010",
303
+ tx: "#f0e4d4",
304
+ tx2: "#b0986a",
305
+ txM: "#907850",
306
+ ac: "#f97316",
307
+ acD: "rgba(249,115,22,0.10)",
308
+ acT: "#fb923c",
309
+ cdBg: "#0e0a05",
310
+ cdTx: "#c8aa78",
311
+ sbBg: "#120c06",
312
+ hdBg: "rgba(18,12,6,0.90)"
313
+ },
314
+ light: {
315
+ bg: "#fffbf5",
316
+ sf: "#ffffff",
317
+ sfH: "#fef3e6",
318
+ bd: "#f0d8b8",
319
+ tx: "#1a1008",
320
+ tx2: "#6a5030",
321
+ txM: "#8a6840",
322
+ ac: "#c2410c",
323
+ acD: "rgba(194,65,12,0.06)",
324
+ acT: "#a63a0a",
325
+ cdBg: "#faf0e2",
326
+ cdTx: "#3a2810",
327
+ sbBg: "#fdf6ec",
328
+ hdBg: "rgba(255,251,245,0.92)"
329
+ },
330
+ fonts: { heading: "Sora", body: "DM Sans", code: "Fira Code" }
331
+ },
332
+ carbon: {
333
+ dark: {
334
+ bg: "#080808",
335
+ sf: "#101010",
336
+ sfH: "#171717",
337
+ bd: "#1f1f1f",
338
+ tx: "#d4d4d4",
339
+ tx2: "#888888",
340
+ txM: "#787878",
341
+ ac: "#e4e4e4",
342
+ acD: "rgba(228,228,228,0.08)",
343
+ acT: "#f0f0f0",
344
+ cdBg: "#0a0a0a",
345
+ cdTx: "#a0a0a0",
346
+ sbBg: "#080808",
347
+ hdBg: "rgba(8,8,8,0.90)"
348
+ },
349
+ light: {
350
+ bg: "#f5f5f5",
351
+ sf: "#ffffff",
352
+ sfH: "#ebebeb",
353
+ bd: "#d4d4d4",
354
+ tx: "#171717",
355
+ tx2: "#525252",
356
+ txM: "#6b6b6b",
357
+ ac: "#262626",
358
+ acD: "rgba(38,38,38,0.06)",
359
+ acT: "#1a1a1a",
360
+ cdBg: "#eaeaea",
361
+ cdTx: "#1a1a1a",
362
+ sbBg: "#f0f0f0",
363
+ hdBg: "rgba(245,245,245,0.92)"
364
+ },
365
+ fonts: { heading: "Geist", body: "Geist", code: "Geist Mono" }
149
366
  }
150
367
  };
151
368
 
@@ -194,66 +411,6 @@ var SendIcon = () => /* @__PURE__ */ jsx(
194
411
  children: /* @__PURE__ */ jsx("path", { d: "M22 2L11 13M22 2l-7 20-4-9-9-4z" })
195
412
  }
196
413
  );
197
- function buildSystemPrompt(context) {
198
- 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.";
199
- if (context) {
200
- const trimmed = context.length > 1e5 ? context.slice(0, 1e5) + "\n\n[Documentation truncated...]" : context;
201
- prompt += `
202
-
203
- Documentation:
204
- ${trimmed}`;
205
- }
206
- return prompt;
207
- }
208
- async function callOpenAI(messages, apiKey, model, context) {
209
- const res = await fetch("https://api.openai.com/v1/chat/completions", {
210
- method: "POST",
211
- headers: {
212
- "Content-Type": "application/json",
213
- "Authorization": `Bearer ${apiKey}`
214
- },
215
- body: JSON.stringify({
216
- model,
217
- messages: [
218
- { role: "system", content: buildSystemPrompt(context) },
219
- ...messages.map((m) => ({ role: m.role, content: m.content }))
220
- ]
221
- })
222
- });
223
- if (!res.ok) {
224
- const err = await res.text();
225
- throw new Error(`OpenAI API error (${res.status}): ${err}`);
226
- }
227
- const data = await res.json();
228
- return data.choices?.[0]?.message?.content || "No response.";
229
- }
230
- async function callAnthropic(messages, apiKey, model, context) {
231
- const res = await fetch("https://api.anthropic.com/v1/messages", {
232
- method: "POST",
233
- headers: {
234
- "Content-Type": "application/json",
235
- "x-api-key": apiKey,
236
- "anthropic-version": "2023-06-01",
237
- "anthropic-dangerous-direct-browser-access": "true"
238
- },
239
- body: JSON.stringify({
240
- model,
241
- max_tokens: 1024,
242
- system: buildSystemPrompt(context),
243
- messages: messages.map((m) => ({ role: m.role, content: m.content }))
244
- })
245
- });
246
- if (!res.ok) {
247
- const err = await res.text();
248
- throw new Error(`Anthropic API error (${res.status}): ${err}`);
249
- }
250
- const data = await res.json();
251
- return data.content?.[0]?.text || "No response.";
252
- }
253
- function getDefaultModel(provider) {
254
- if (provider === "openai") return "gpt-4o-mini";
255
- return "claude-sonnet-4-20250514";
256
- }
257
414
  function AiChat({ provider, model, apiKey, context }) {
258
415
  const [open, setOpen] = useState(false);
259
416
  const [messages, setMessages] = useState([]);
@@ -284,10 +441,11 @@ function AiChat({ provider, model, apiKey, context }) {
284
441
  setError(null);
285
442
  try {
286
443
  let response;
444
+ const sysPrompt = buildSystemPrompt(context);
287
445
  if (provider === "openai") {
288
- response = await callOpenAI(updatedMessages, resolvedKey, resolvedModel, context);
446
+ response = await callOpenAI(updatedMessages, resolvedKey, resolvedModel, sysPrompt);
289
447
  } else {
290
- response = await callAnthropic(updatedMessages, resolvedKey, resolvedModel, context);
448
+ response = await callAnthropic(updatedMessages, resolvedKey, resolvedModel, sysPrompt);
291
449
  }
292
450
  setMessages((prev) => [...prev, { role: "assistant", content: response }]);
293
451
  } catch (err) {
@@ -889,10 +1047,12 @@ function Shell({
889
1047
  lastUpdated,
890
1048
  changelogEntries,
891
1049
  apiManifest,
1050
+ asyncApiManifest,
892
1051
  apiBaseUrl,
893
1052
  apiPlayground,
894
1053
  apiAuth,
895
1054
  ApiReferenceComponent,
1055
+ AsyncApiReferenceComponent,
896
1056
  onNavigate,
897
1057
  allPages,
898
1058
  versioning,
@@ -921,6 +1081,9 @@ function Shell({
921
1081
  const [localeDropdownOpen, setLocaleDropdown] = useState2(false);
922
1082
  const [zoomSrc, setZoomSrc] = useState2(null);
923
1083
  const [feedbackGiven, setFeedbackGiven] = useState2({});
1084
+ const [feedbackRating, setFeedbackRating] = useState2({});
1085
+ const [feedbackComment, setFeedbackComment] = useState2("");
1086
+ const [feedbackSubmitted, setFeedbackSubmitted] = useState2({});
924
1087
  const [bannerDismissed, setBannerDismissed] = useState2(() => {
925
1088
  if (!config2.banner?.text) return true;
926
1089
  try {
@@ -1192,7 +1355,14 @@ function Shell({
1192
1355
  setSearch(false);
1193
1356
  },
1194
1357
  onClose: () => setSearch(false),
1195
- mobile
1358
+ mobile,
1359
+ aiSearch: config2.search?.ai && config2.ai?.enabled ? {
1360
+ provider: config2.ai.provider || "anthropic",
1361
+ model: config2.ai.model,
1362
+ apiKey: window.__TOME_AI_API_KEY__ || void 0,
1363
+ context: docContext2?.map((d) => `## ${d.title}
1364
+ ${d.content}`).join("\n\n")
1365
+ } : void 0
1196
1366
  }
1197
1367
  ) : null,
1198
1368
  /* @__PURE__ */ jsxs2("div", { style: { display: "flex", flexDirection: isRtl ? "row-reverse" : "row", flex: 1, height: config2.banner?.text && !bannerDismissed ? "calc(100vh - 32px)" : "100vh" }, children: [
@@ -1349,7 +1519,7 @@ function Shell({
1349
1519
  )) }),
1350
1520
  /* @__PURE__ */ jsxs2("div", { style: { padding: "12px 16px", borderTop: "1px solid var(--bd)", display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
1351
1521
  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", {}),
1352
- /* @__PURE__ */ jsxs2("span", { style: { fontSize: 11, color: "var(--txM)", letterSpacing: 0.2 }, children: [
1522
+ config2.branding?.powered !== false && /* @__PURE__ */ jsxs2("span", { style: { fontSize: 11, color: "var(--txM)", letterSpacing: 0.2 }, children: [
1353
1523
  "Built with ",
1354
1524
  "\u2661",
1355
1525
  " by Tome"
@@ -1659,7 +1829,7 @@ function Shell({
1659
1829
  }
1660
1830
  ),
1661
1831
  /* @__PURE__ */ jsxs2("div", { ref: contentRef, style: { flex: 1, overflow: "auto", display: "flex" }, children: [
1662
- /* @__PURE__ */ jsxs2("main", { style: { flex: 1, maxWidth: mobile ? "100%" : apiManifest ? 1100 : 760, padding: mobile ? "24px 16px 60px" : "40px 48px 80px", margin: "0 auto", minWidth: 0 }, children: [
1832
+ /* @__PURE__ */ jsxs2("main", { style: { flex: 1, maxWidth: mobile ? "100%" : apiManifest || asyncApiManifest ? 1100 : 760, padding: mobile ? "24px 16px 60px" : "40px 48px 80px", margin: "0 auto", minWidth: 0 }, children: [
1663
1833
  breadcrumbs.length > 0 && /* @__PURE__ */ jsx2("nav", { "aria-label": "Breadcrumbs", "data-testid": "breadcrumbs", style: {
1664
1834
  display: "flex",
1665
1835
  alignItems: "center",
@@ -1686,7 +1856,13 @@ function Shell({
1686
1856
  /* @__PURE__ */ jsx2("h1", { style: { fontFamily: "var(--font-heading)", fontSize: mobile ? 26 : 38, fontWeight: 400, fontStyle: "italic", lineHeight: 1.15, marginBottom: 8 }, children: pageTitle }),
1687
1857
  isDraft && /* @__PURE__ */ jsx2("div", { "data-testid": "draft-banner", style: { background: "#fef3c7", color: "#92400e", padding: "8px 16px", borderRadius: 6, fontSize: 13, marginBottom: 16 }, children: "Draft \u2014 This page is only visible in development" }),
1688
1858
  pageDescription && /* @__PURE__ */ jsx2("p", { style: { fontSize: 16, color: "var(--tx2)", lineHeight: 1.6, marginBottom: 32 }, children: pageDescription }),
1689
- /* @__PURE__ */ jsx2("div", { style: { borderTop: "1px solid var(--bd)", paddingTop: 28 }, children: apiManifest && ApiReferenceComponent ? /* @__PURE__ */ jsx2(ApiReferenceComponent, { manifest: apiManifest, baseUrl: apiBaseUrl, showPlayground: apiPlayground, playgroundAuth: apiAuth }) : (
1859
+ /* @__PURE__ */ jsx2("div", { style: { borderTop: "1px solid var(--bd)", paddingTop: 28 }, children: (apiManifest || asyncApiManifest) && (ApiReferenceComponent || AsyncApiReferenceComponent) ? /* @__PURE__ */ jsxs2(Fragment, { children: [
1860
+ apiManifest && ApiReferenceComponent && /* @__PURE__ */ jsx2(ApiReferenceComponent, { manifest: apiManifest, baseUrl: apiBaseUrl, showPlayground: apiPlayground, playgroundAuth: apiAuth }),
1861
+ asyncApiManifest && AsyncApiReferenceComponent && /* @__PURE__ */ jsxs2(Fragment, { children: [
1862
+ apiManifest && /* @__PURE__ */ jsx2("div", { style: { borderTop: "1px solid var(--bd)", margin: "40px 0" } }),
1863
+ /* @__PURE__ */ jsx2(AsyncApiReferenceComponent, { manifest: asyncApiManifest })
1864
+ ] })
1865
+ ] }) : (
1690
1866
  /* TOM-49: Changelog page type */
1691
1867
  changelogEntries && changelogEntries.length > 0 ? /* @__PURE__ */ jsx2(ChangelogView, { entries: changelogEntries }) : PageComponent ? /* @__PURE__ */ jsx2("div", { className: "tome-content", children: /* @__PURE__ */ jsx2(PageComponent, { components: mdxComponents || {} }) }) : /* @__PURE__ */ jsx2(
1692
1868
  "div",
@@ -1739,13 +1915,100 @@ function Shell({
1739
1915
  formatRelativeDate(lastUpdated)
1740
1916
  ] })
1741
1917
  ] }),
1742
- /* @__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: [
1918
+ config2.feedback?.enabled !== false && /* @__PURE__ */ jsx2("div", { "data-testid": "feedback-widget", style: { marginTop: 24, padding: "12px 0" }, children: feedbackSubmitted[currentPageId] ? /* @__PURE__ */ jsx2("span", { style: { fontSize: 13, color: "var(--txM)", fontFamily: "var(--font-body)" }, children: "Thanks for your feedback!" }) : feedbackGiven[currentPageId] && config2.feedback?.textInput ? /* @__PURE__ */ jsxs2("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
1919
+ /* @__PURE__ */ jsx2("span", { style: { fontSize: 13, color: "var(--txM)", fontFamily: "var(--font-body)" }, children: "Any additional feedback? (optional)" }),
1920
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", gap: 8 }, children: [
1921
+ /* @__PURE__ */ jsx2(
1922
+ "input",
1923
+ {
1924
+ "data-testid": "feedback-text-input",
1925
+ type: "text",
1926
+ value: feedbackComment,
1927
+ onChange: (e) => setFeedbackComment(e.target.value),
1928
+ placeholder: "Tell us more...",
1929
+ style: {
1930
+ flex: 1,
1931
+ padding: "6px 10px",
1932
+ fontSize: 13,
1933
+ fontFamily: "var(--font-body)",
1934
+ background: "var(--sf)",
1935
+ border: "1px solid var(--bd)",
1936
+ borderRadius: 2,
1937
+ color: "var(--tx)",
1938
+ outline: "none"
1939
+ },
1940
+ onKeyDown: (e) => {
1941
+ if (e.key === "Enter") {
1942
+ window.__tome?.trackFeedback(currentPageId, feedbackRating[currentPageId], feedbackComment);
1943
+ try {
1944
+ localStorage.setItem(`tome-feedback-${currentPageId}`, feedbackRating[currentPageId]);
1945
+ } catch {
1946
+ }
1947
+ setFeedbackSubmitted((prev2) => ({ ...prev2, [currentPageId]: true }));
1948
+ setFeedbackComment("");
1949
+ }
1950
+ }
1951
+ }
1952
+ ),
1953
+ /* @__PURE__ */ jsx2(
1954
+ "button",
1955
+ {
1956
+ "data-testid": "feedback-submit",
1957
+ onClick: () => {
1958
+ window.__tome?.trackFeedback(currentPageId, feedbackRating[currentPageId], feedbackComment);
1959
+ try {
1960
+ localStorage.setItem(`tome-feedback-${currentPageId}`, feedbackRating[currentPageId]);
1961
+ } catch {
1962
+ }
1963
+ setFeedbackSubmitted((prev2) => ({ ...prev2, [currentPageId]: true }));
1964
+ setFeedbackComment("");
1965
+ },
1966
+ style: {
1967
+ background: "none",
1968
+ border: "1px solid var(--bd)",
1969
+ borderRadius: 2,
1970
+ padding: "6px 14px",
1971
+ cursor: "pointer",
1972
+ fontSize: 13,
1973
+ color: "var(--txM)"
1974
+ },
1975
+ children: "Submit"
1976
+ }
1977
+ ),
1978
+ /* @__PURE__ */ jsx2(
1979
+ "button",
1980
+ {
1981
+ onClick: () => {
1982
+ window.__tome?.trackFeedback(currentPageId, feedbackRating[currentPageId]);
1983
+ try {
1984
+ localStorage.setItem(`tome-feedback-${currentPageId}`, feedbackRating[currentPageId]);
1985
+ } catch {
1986
+ }
1987
+ setFeedbackSubmitted((prev2) => ({ ...prev2, [currentPageId]: true }));
1988
+ },
1989
+ style: {
1990
+ background: "none",
1991
+ border: "none",
1992
+ cursor: "pointer",
1993
+ fontSize: 13,
1994
+ color: "var(--txM)"
1995
+ },
1996
+ children: "Skip"
1997
+ }
1998
+ )
1999
+ ] })
2000
+ ] }) : feedbackGiven[currentPageId] ? /* @__PURE__ */ jsx2("span", { style: { fontSize: 13, color: "var(--txM)", fontFamily: "var(--font-body)" }, children: "Thanks for your feedback!" }) : /* @__PURE__ */ jsxs2("div", { style: { display: "flex", alignItems: "center", gap: 12 }, children: [
1743
2001
  /* @__PURE__ */ jsx2("span", { style: { fontSize: 13, color: "var(--txM)", fontFamily: "var(--font-body)" }, children: "Was this helpful?" }),
1744
- /* @__PURE__ */ jsx2("button", { onClick: () => {
2002
+ /* @__PURE__ */ jsx2("button", { "data-testid": "feedback-up", onClick: () => {
2003
+ setFeedbackRating((prev2) => ({ ...prev2, [currentPageId]: "up" }));
1745
2004
  setFeedbackGiven((prev2) => ({ ...prev2, [currentPageId]: true }));
1746
- try {
1747
- localStorage.setItem(`tome-feedback-${currentPageId}`, "up");
1748
- } catch {
2005
+ if (!config2.feedback?.textInput) {
2006
+ window.__tome?.trackFeedback(currentPageId, "up");
2007
+ try {
2008
+ localStorage.setItem(`tome-feedback-${currentPageId}`, "up");
2009
+ } catch {
2010
+ }
2011
+ setFeedbackSubmitted((prev2) => ({ ...prev2, [currentPageId]: true }));
1749
2012
  }
1750
2013
  }, style: {
1751
2014
  background: "none",
@@ -1757,11 +2020,16 @@ function Shell({
1757
2020
  color: "var(--txM)",
1758
2021
  transition: "border-color .15s"
1759
2022
  }, children: "\u{1F44D}" }),
1760
- /* @__PURE__ */ jsx2("button", { onClick: () => {
2023
+ /* @__PURE__ */ jsx2("button", { "data-testid": "feedback-down", onClick: () => {
2024
+ setFeedbackRating((prev2) => ({ ...prev2, [currentPageId]: "down" }));
1761
2025
  setFeedbackGiven((prev2) => ({ ...prev2, [currentPageId]: true }));
1762
- try {
1763
- localStorage.setItem(`tome-feedback-${currentPageId}`, "down");
1764
- } catch {
2026
+ if (!config2.feedback?.textInput) {
2027
+ window.__tome?.trackFeedback(currentPageId, "down");
2028
+ try {
2029
+ localStorage.setItem(`tome-feedback-${currentPageId}`, "down");
2030
+ } catch {
2031
+ }
2032
+ setFeedbackSubmitted((prev2) => ({ ...prev2, [currentPageId]: true }));
1765
2033
  }
1766
2034
  }, style: {
1767
2035
  background: "none",
@@ -1921,13 +2189,16 @@ ${d.content}`).join("\n\n") ?? allPages.map((p) => `- ${p.title}${p.description
1921
2189
  }, 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)" } }) })
1922
2190
  ] });
1923
2191
  }
1924
- function SearchModal({ allPages, onNavigate, onClose, mobile }) {
2192
+ function SearchModal({ allPages, onNavigate, onClose, mobile, aiSearch }) {
1925
2193
  const [q, setQ] = useState2("");
1926
2194
  const [results, setResults] = useState2([]);
1927
2195
  const [selected, setSelected] = useState2(0);
1928
2196
  const [pagefindReady, setPagefindReady] = useState2(null);
2197
+ const [aiAnswer, setAiAnswer] = useState2(null);
2198
+ const [aiLoading, setAiLoading] = useState2(false);
1929
2199
  const inputRef = useRef2(null);
1930
2200
  const debounceRef = useRef2(void 0);
2201
+ const aiDebounceRef = useRef2(void 0);
1931
2202
  useEffect2(() => {
1932
2203
  initPagefind().then((pf) => setPagefindReady(!!pf));
1933
2204
  setTimeout(() => inputRef.current?.focus(), 50);
@@ -1960,12 +2231,15 @@ function SearchModal({ allPages, onNavigate, onClose, mobile }) {
1960
2231
  }
1961
2232
  setResults(items);
1962
2233
  setSelected(0);
2234
+ window.__tome?.trackSearch(query, items.length);
1963
2235
  return;
1964
2236
  } catch {
1965
2237
  }
1966
2238
  }
1967
- setResults(fallbackSearch(query));
2239
+ const fallbackResults = fallbackSearch(query);
2240
+ setResults(fallbackResults);
1968
2241
  setSelected(0);
2242
+ window.__tome?.trackSearch(query, fallbackResults.length);
1969
2243
  }, [fallbackSearch]);
1970
2244
  useEffect2(() => {
1971
2245
  if (debounceRef.current) clearTimeout(debounceRef.current);
@@ -1974,6 +2248,46 @@ function SearchModal({ allPages, onNavigate, onClose, mobile }) {
1974
2248
  if (debounceRef.current) clearTimeout(debounceRef.current);
1975
2249
  };
1976
2250
  }, [q, doSearch]);
2251
+ useEffect2(() => {
2252
+ if (!aiSearch?.apiKey || !q.trim() || q.length < 3) {
2253
+ setAiAnswer(null);
2254
+ return;
2255
+ }
2256
+ if (aiDebounceRef.current) clearTimeout(aiDebounceRef.current);
2257
+ aiDebounceRef.current = setTimeout(async () => {
2258
+ setAiLoading(true);
2259
+ try {
2260
+ const { callAiProvider, buildSystemPrompt: buildSystemPrompt2, getDefaultModel: getDefaultModel2 } = await import("./ai-api-6KVITVN6.js");
2261
+ const model = aiSearch.model || getDefaultModel2(aiSearch.provider);
2262
+ const searchContext = results.slice(0, 5).map((r) => `Page: ${r.title}
2263
+ ${r.excerpt || ""}`).join("\n\n");
2264
+ const fullContext = aiSearch.context ? `${searchContext}
2265
+
2266
+ ---
2267
+
2268
+ ${aiSearch.context}` : searchContext;
2269
+ const sysPrompt = buildSystemPrompt2(
2270
+ fullContext,
2271
+ "You are a documentation search assistant. Answer the user's question concisely (2-3 sentences max) based on the documentation. Cite specific page names when relevant. If you can't answer from the docs, say so briefly."
2272
+ );
2273
+ const answer = await callAiProvider(
2274
+ aiSearch.provider,
2275
+ [{ role: "user", content: q }],
2276
+ aiSearch.apiKey || "",
2277
+ model,
2278
+ sysPrompt
2279
+ );
2280
+ setAiAnswer(answer);
2281
+ } catch {
2282
+ setAiAnswer(null);
2283
+ } finally {
2284
+ setAiLoading(false);
2285
+ }
2286
+ }, 500);
2287
+ return () => {
2288
+ if (aiDebounceRef.current) clearTimeout(aiDebounceRef.current);
2289
+ };
2290
+ }, [q, results, aiSearch]);
1977
2291
  const handleKeyDown = useCallback2((e) => {
1978
2292
  if (e.key === "ArrowDown") {
1979
2293
  e.preventDefault();
@@ -2023,6 +2337,17 @@ function SearchModal({ allPages, onNavigate, onClose, mobile }) {
2023
2337
  ),
2024
2338
  /* @__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" })
2025
2339
  ] }),
2340
+ aiSearch && q.length >= 3 && (aiLoading || aiAnswer) && /* @__PURE__ */ jsxs2("div", { style: {
2341
+ padding: "12px 18px",
2342
+ borderBottom: "1px solid var(--bd)",
2343
+ background: "var(--acT)",
2344
+ fontSize: 13,
2345
+ lineHeight: 1.5,
2346
+ color: "var(--tx)"
2347
+ }, children: [
2348
+ /* @__PURE__ */ jsx2("div", { style: { fontSize: 10, fontWeight: 600, textTransform: "uppercase", letterSpacing: 0.5, color: "var(--ac)", marginBottom: 6 }, children: "AI Answer" }),
2349
+ aiLoading ? /* @__PURE__ */ jsx2("div", { style: { color: "var(--txM)", fontStyle: "italic" }, children: "Thinking..." }) : aiAnswer ? /* @__PURE__ */ jsx2("div", { children: aiAnswer }) : null
2350
+ ] }),
2026
2351
  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(
2027
2352
  "button",
2028
2353
  {
@@ -2059,12 +2384,17 @@ function SearchModal({ allPages, onNavigate, onClose, mobile }) {
2059
2384
  // src/routing.ts
2060
2385
  function pathnameToPageId(pathname, basePath2, routes2) {
2061
2386
  let relative = pathname;
2062
- if (basePath2 && relative.startsWith(basePath2)) {
2063
- relative = relative.slice(basePath2.length);
2387
+ const normalizedBase = basePath2 === "/" ? "" : basePath2.replace(/\/+$/, "");
2388
+ if (normalizedBase && relative.startsWith(normalizedBase)) {
2389
+ relative = relative.slice(normalizedBase.length);
2064
2390
  }
2065
2391
  const id = relative.replace(/^\//, "").replace(/\/index\.html$/, "").replace(/\.html$/, "").replace(/\/$/, "") || "index";
2066
- const route = routes2.find((r) => r.id === id);
2067
- return route ? id : null;
2392
+ const routeById = routes2.find((r) => r.id === id);
2393
+ if (routeById) return id;
2394
+ const urlPath = "/" + id;
2395
+ const urlPathWithSlash = urlPath + "/";
2396
+ const routeByUrl = routes2.find((r) => r.urlPath === urlPath || r.urlPath === urlPathWithSlash);
2397
+ return routeByUrl ? routeByUrl.id : null;
2068
2398
  }
2069
2399
  function pageIdToPath(id, basePath2, routes2) {
2070
2400
  const route = routes2.find((r) => r.id === id);
@@ -2118,8 +2448,14 @@ async function loadPage(id, routes2, loadPageModule2) {
2118
2448
  };
2119
2449
  }
2120
2450
  if (!mod.default) throw new PageNotFoundError(id);
2121
- if (mod.isApiReference && mod.apiManifest) {
2122
- return { isMdx: false, isApiReference: true, ...mod.default, apiManifest: mod.apiManifest };
2451
+ if (mod.isApiReference && (mod.apiManifest || mod.asyncApiManifest)) {
2452
+ return {
2453
+ isMdx: false,
2454
+ isApiReference: true,
2455
+ ...mod.default,
2456
+ apiManifest: mod.apiManifest,
2457
+ asyncApiManifest: mod.asyncApiManifest
2458
+ };
2123
2459
  }
2124
2460
  if (mod.isChangelog && mod.changelogEntries) {
2125
2461
  return { isMdx: false, ...mod.default, changelogEntries: mod.changelogEntries };
@@ -2150,7 +2486,8 @@ import {
2150
2486
  CodeSamples,
2151
2487
  LinkCard,
2152
2488
  CardGrid,
2153
- ApiReference
2489
+ ApiReference,
2490
+ AsyncApiReference
2154
2491
  } from "@tomehq/components";
2155
2492
  import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
2156
2493
  var MDX_COMPONENTS = {
@@ -2640,10 +2977,12 @@ function App() {
2640
2977
  lastUpdated: currentRoute?.lastUpdated,
2641
2978
  changelogEntries: !pageData?.isMdx ? pageData?.changelogEntries : void 0,
2642
2979
  apiManifest: !pageData?.isMdx && pageData?.isApiReference ? pageData.apiManifest : void 0,
2980
+ asyncApiManifest: !pageData?.isMdx && pageData?.isApiReference ? pageData.asyncApiManifest : void 0,
2643
2981
  apiBaseUrl: config.api?.baseUrl,
2644
2982
  apiPlayground: config.api?.playground,
2645
2983
  apiAuth: config.api?.auth,
2646
2984
  ApiReferenceComponent: ApiReference,
2985
+ AsyncApiReferenceComponent: AsyncApiReference,
2647
2986
  onNavigate: navigateTo,
2648
2987
  allPages,
2649
2988
  docContext,