deepspace 0.33.1 → 0.35.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 (38) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/README.md +5 -1
  3. package/dist/{chunk-PZDVKSLI.js → chunk-ESEI3MMY.js} +2 -2
  4. package/dist/{chunk-PZDVKSLI.js.map → chunk-ESEI3MMY.js.map} +1 -1
  5. package/dist/chunk-ESYXTIV2.js +1957 -0
  6. package/dist/chunk-ESYXTIV2.js.map +1 -0
  7. package/dist/cli.js +151 -53
  8. package/dist/cli.js.map +1 -1
  9. package/dist/documentation-client-core.js +1 -1
  10. package/dist/documentation-default-renderer.cjs +1 -1
  11. package/dist/documentation-react.js +1 -1
  12. package/dist/documentation-runtime.js +10 -10
  13. package/dist/documentation-server-core.js +1 -1
  14. package/dist/documentation.js +47 -22
  15. package/dist/documentation.js.map +1 -1
  16. package/dist/expo.d.ts +83 -0
  17. package/dist/expo.js +242 -0
  18. package/dist/expo.js.map +1 -0
  19. package/dist/index.d.ts +110 -23
  20. package/dist/index.js +134 -53
  21. package/dist/index.js.map +1 -1
  22. package/dist/server.d.ts +205 -37
  23. package/dist/server.js +290 -97
  24. package/dist/server.js.map +1 -1
  25. package/dist/testing.js +45 -3
  26. package/dist/testing.js.map +1 -1
  27. package/dist/worker.d.ts +348 -48
  28. package/dist/worker.js +547 -110
  29. package/dist/worker.js.map +1 -1
  30. package/features/documents/feature.json +15 -0
  31. package/features/documents/src/DocumentsListPage.tsx +22 -8
  32. package/features/documents/src/InviteDialog.tsx +63 -42
  33. package/features/documents/src/__tests__/documents-find-invitee-action.test.ts +131 -0
  34. package/features/documents/src/__tests__/documents-invitee-lookup.test.ts +67 -0
  35. package/features/documents/src/documents-find-invitee-action.ts +56 -0
  36. package/features/documents/src/documents-invitee-lookup.ts +38 -0
  37. package/features/documents/src/documents-ui.css +10 -0
  38. package/package.json +29 -9
@@ -0,0 +1,1957 @@
1
+ // src/documentation/runtime/app.tsx
2
+ import {
3
+ useCallback,
4
+ useEffect as useEffect8,
5
+ useState as useState7
6
+ } from "react";
7
+
8
+ // src/documentation/runtime/article.tsx
9
+ import {
10
+ useEffect as useEffect3,
11
+ useMemo,
12
+ useRef as useRef3,
13
+ useState as useState3
14
+ } from "react";
15
+ import { createPortal } from "react-dom";
16
+
17
+ // src/documentation/types.ts
18
+ var DocumentationError = class extends Error {
19
+ code;
20
+ diagnostics;
21
+ constructor(message, code, diagnostics = []) {
22
+ super(message);
23
+ this.name = "DocumentationError";
24
+ this.code = code;
25
+ this.diagnostics = diagnostics;
26
+ }
27
+ };
28
+
29
+ // src/documentation/routing.ts
30
+ function collapseRoute(value) {
31
+ let route = `/${value}`.replace(/\/{2,}/g, "/");
32
+ if (route.length > 1) route = route.replace(/\/$/, "");
33
+ return route;
34
+ }
35
+ function normalizeRoute(value) {
36
+ assertSafeRoute(value);
37
+ let route = collapseRoute(value);
38
+ route = route.replace(/\.(?:md|mdx)$/i, "");
39
+ route = route.replace(/\/index$/i, "");
40
+ return route || "/";
41
+ }
42
+ function normalizeDocumentationBasePath(value) {
43
+ if (!value || value === "/") return "";
44
+ const normalized = normalizeRoute(value);
45
+ if (normalized === "/_documentation") {
46
+ throw new DocumentationError("The internal /_documentation namespace cannot be a public documentation base", "documentation_base_invalid");
47
+ }
48
+ return normalized;
49
+ }
50
+ function documentationPublicPath(basePath, path) {
51
+ const base = normalizeDocumentationBasePath(basePath);
52
+ const suffix = path.startsWith("/") ? path : `/${path}`;
53
+ if (suffix === "/") return base || "/";
54
+ return `${base}${suffix}`;
55
+ }
56
+ function documentationLogicalPath(basePath, pathname) {
57
+ const base = normalizeDocumentationBasePath(basePath);
58
+ if (!base) return pathname.startsWith("/") ? pathname : `/${pathname}`;
59
+ if (pathname === base) return "/";
60
+ if (!pathname.startsWith(`${base}/`)) return null;
61
+ return pathname.slice(base.length) || "/";
62
+ }
63
+ function assertSafeRoute(value) {
64
+ if (value.includes("\\") || value.includes("\0")) throw unsafeRoute(value);
65
+ let decoded = value;
66
+ for (let pass = 0; pass < 5; pass++) {
67
+ let next;
68
+ try {
69
+ next = decodeURIComponent(decoded);
70
+ } catch {
71
+ throw unsafeRoute(value);
72
+ }
73
+ if (next === decoded) break;
74
+ decoded = next;
75
+ }
76
+ if (decoded.includes("\\") || decoded.includes("\0") || decoded.split("/").some((segment) => segment === "." || segment === "..")) throw unsafeRoute(value);
77
+ }
78
+ function unsafeRoute(value) {
79
+ return new DocumentationError(
80
+ `Documentation route contains an unsafe path segment: ${value}`,
81
+ "documentation_route_invalid",
82
+ [{ code: "route_path_escape", message: value }]
83
+ );
84
+ }
85
+
86
+ // src/documentation/runtime/browser.ts
87
+ async function writeClipboardText(text) {
88
+ try {
89
+ if (navigator.clipboard?.writeText) {
90
+ await navigator.clipboard.writeText(text);
91
+ return;
92
+ }
93
+ } catch {
94
+ }
95
+ const activeElement = document.activeElement instanceof HTMLElement ? document.activeElement : null;
96
+ const textarea = document.createElement("textarea");
97
+ textarea.value = text;
98
+ textarea.readOnly = true;
99
+ textarea.style.position = "fixed";
100
+ textarea.style.inset = "0 auto auto 0";
101
+ textarea.style.opacity = "0";
102
+ textarea.style.pointerEvents = "none";
103
+ document.body.appendChild(textarea);
104
+ textarea.focus({ preventScroll: true });
105
+ textarea.select();
106
+ const copied = document.execCommand("copy");
107
+ textarea.remove();
108
+ activeElement?.focus({ preventScroll: true });
109
+ if (!copied) throw new Error("Unable to copy text");
110
+ }
111
+ function externalProps(href) {
112
+ return isExternal(href) ? { target: "_blank", rel: "noopener noreferrer" } : {};
113
+ }
114
+ function isExternal(href) {
115
+ return /^https?:\/\//i.test(href);
116
+ }
117
+ function safeStorageGet(key) {
118
+ try {
119
+ return localStorage.getItem(key);
120
+ } catch {
121
+ return null;
122
+ }
123
+ }
124
+ function safeStorageSet(key, value) {
125
+ try {
126
+ localStorage.setItem(key, value);
127
+ } catch {
128
+ }
129
+ }
130
+ function errorMessage(error) {
131
+ return error instanceof Error ? error.message : String(error);
132
+ }
133
+
134
+ // src/documentation/runtime/code-block.tsx
135
+ import {
136
+ createContext,
137
+ useContext,
138
+ useEffect,
139
+ useId,
140
+ useRef,
141
+ useState
142
+ } from "react";
143
+
144
+ // src/documentation/runtime/icons.tsx
145
+ import { jsx, jsxs } from "react/jsx-runtime";
146
+ function Icon({ children, ...props }) {
147
+ return /* @__PURE__ */ jsx(
148
+ "svg",
149
+ {
150
+ "aria-hidden": "true",
151
+ fill: "none",
152
+ height: "18",
153
+ viewBox: "0 0 24 24",
154
+ width: "18",
155
+ ...props,
156
+ children
157
+ }
158
+ );
159
+ }
160
+ function OrbitMark(props) {
161
+ return /* @__PURE__ */ jsxs(Icon, { viewBox: "0 0 32 32", ...props, children: [
162
+ /* @__PURE__ */ jsx("path", { d: "M16 2.5c.5 7.7 5.8 13 13.5 13.5-7.7.5-13 5.8-13.5 13.5C15.5 21.8 10.2 16.5 2.5 16 10.2 15.5 15.5 10.2 16 2.5Z", fill: "currentColor" }),
163
+ /* @__PURE__ */ jsx("circle", { cx: "16", cy: "16", fill: "var(--documentation-bg)", r: "3.25" }),
164
+ /* @__PURE__ */ jsx("path", { d: "M4.5 22.7c5.6-2 16.4-8.3 22.7-14.1", opacity: ".45", stroke: "currentColor", strokeLinecap: "round" })
165
+ ] });
166
+ }
167
+ function SearchIcon(props) {
168
+ return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
169
+ /* @__PURE__ */ jsx("circle", { cx: "11", cy: "11", r: "6.5", stroke: "currentColor", strokeWidth: "1.8" }),
170
+ /* @__PURE__ */ jsx("path", { d: "m16 16 4 4", stroke: "currentColor", strokeLinecap: "round", strokeWidth: "1.8" })
171
+ ] });
172
+ }
173
+ function SparkIcon(props) {
174
+ return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
175
+ /* @__PURE__ */ jsx("path", { d: "M12 2.8c.3 5 3.7 8.4 8.7 8.7-5 .3-8.4 3.7-8.7 8.7-.3-5-3.7-8.4-8.7-8.7 5-.3 8.4-3.7 8.7-8.7Z", stroke: "currentColor", strokeLinejoin: "round", strokeWidth: "1.6" }),
176
+ /* @__PURE__ */ jsx("path", { d: "M19 3v4M21 5h-4", stroke: "currentColor", strokeLinecap: "round", strokeWidth: "1.4" })
177
+ ] });
178
+ }
179
+ function MenuIcon(props) {
180
+ return /* @__PURE__ */ jsx(Icon, { ...props, children: /* @__PURE__ */ jsx("path", { d: "M4 7h16M4 12h16M4 17h16", stroke: "currentColor", strokeLinecap: "round", strokeWidth: "1.8" }) });
181
+ }
182
+ function CloseIcon(props) {
183
+ return /* @__PURE__ */ jsx(Icon, { ...props, children: /* @__PURE__ */ jsx("path", { d: "m6 6 12 12M18 6 6 18", stroke: "currentColor", strokeLinecap: "round", strokeWidth: "1.8" }) });
184
+ }
185
+ function SunIcon(props) {
186
+ return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
187
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "3.5", stroke: "currentColor", strokeWidth: "1.6" }),
188
+ /* @__PURE__ */ jsx("path", { d: "M12 2.5v2M12 19.5v2M2.5 12h2M19.5 12h2M5.3 5.3l1.4 1.4M17.3 17.3l1.4 1.4M18.7 5.3l-1.4 1.4M6.7 17.3l-1.4 1.4", stroke: "currentColor", strokeLinecap: "round", strokeWidth: "1.6" })
189
+ ] });
190
+ }
191
+ function MoonIcon(props) {
192
+ return /* @__PURE__ */ jsx(Icon, { ...props, children: /* @__PURE__ */ jsx("path", { d: "M19.5 14.8A8 8 0 0 1 9.2 4.5 8 8 0 1 0 19.5 14.8Z", stroke: "currentColor", strokeLinejoin: "round", strokeWidth: "1.6" }) });
193
+ }
194
+ function SystemIcon(props) {
195
+ return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
196
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "4", width: "18", height: "13", rx: "2", stroke: "currentColor", strokeWidth: "1.6" }),
197
+ /* @__PURE__ */ jsx("path", { d: "M8 21h8M12 17v4", stroke: "currentColor", strokeLinecap: "round", strokeWidth: "1.6" })
198
+ ] });
199
+ }
200
+ function ChevronRightIcon(props) {
201
+ return /* @__PURE__ */ jsx(Icon, { ...props, children: /* @__PURE__ */ jsx("path", { d: "m9 5 7 7-7 7", stroke: "currentColor", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "1.8" }) });
202
+ }
203
+ function ChevronDownIcon(props) {
204
+ return /* @__PURE__ */ jsx(Icon, { ...props, children: /* @__PURE__ */ jsx("path", { d: "m5 9 7 7 7-7", stroke: "currentColor", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "1.8" }) });
205
+ }
206
+ function ExternalIcon(props) {
207
+ return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
208
+ /* @__PURE__ */ jsx("path", { d: "M14 4h6v6M20 4l-9 9", stroke: "currentColor", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "1.6" }),
209
+ /* @__PURE__ */ jsx("path", { d: "M18 13v5a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h5", stroke: "currentColor", strokeLinecap: "round", strokeWidth: "1.6" })
210
+ ] });
211
+ }
212
+ function CopyIcon(props) {
213
+ return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
214
+ /* @__PURE__ */ jsx("rect", { x: "8", y: "8", width: "11", height: "11", rx: "2", stroke: "currentColor", strokeWidth: "1.6" }),
215
+ /* @__PURE__ */ jsx("path", { d: "M16 8V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h2", stroke: "currentColor", strokeWidth: "1.6" })
216
+ ] });
217
+ }
218
+ function CheckIcon(props) {
219
+ return /* @__PURE__ */ jsx(Icon, { ...props, children: /* @__PURE__ */ jsx("path", { d: "m5 12.5 4.2 4L19 7", stroke: "currentColor", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "1.9" }) });
220
+ }
221
+ function FileIcon(props) {
222
+ return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
223
+ /* @__PURE__ */ jsx("path", { d: "M6 3.5h8l4 4V20a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V4.5a1 1 0 0 1 1-1Z", stroke: "currentColor", strokeLinejoin: "round", strokeWidth: "1.6" }),
224
+ /* @__PURE__ */ jsx("path", { d: "M14 3.5v4h4M8 12h7M8 16h7", stroke: "currentColor", strokeLinecap: "round", strokeWidth: "1.6" })
225
+ ] });
226
+ }
227
+ function MessageIcon(props) {
228
+ return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
229
+ /* @__PURE__ */ jsx("path", { d: "M5.5 18.5 4 21l3.5-1.2A8.5 8.5 0 1 0 5.5 18.5Z", stroke: "currentColor", strokeLinejoin: "round", strokeWidth: "1.6" }),
230
+ /* @__PURE__ */ jsx("path", { d: "M8 10h8M8 14h5", stroke: "currentColor", strokeLinecap: "round", strokeWidth: "1.6" })
231
+ ] });
232
+ }
233
+ function TerminalIcon(props) {
234
+ return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
235
+ /* @__PURE__ */ jsx("rect", { x: "3.5", y: "4.5", width: "17", height: "15", rx: "2", stroke: "currentColor", strokeWidth: "1.6" }),
236
+ /* @__PURE__ */ jsx("path", { d: "m7 9 3 3-3 3M12.5 15H17", stroke: "currentColor", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "1.6" })
237
+ ] });
238
+ }
239
+ function EditorIcon(props) {
240
+ return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
241
+ /* @__PURE__ */ jsx("rect", { x: "3.5", y: "4.5", width: "17", height: "15", rx: "2", stroke: "currentColor", strokeWidth: "1.6" }),
242
+ /* @__PURE__ */ jsx("path", { d: "M8 4.5v15M11.5 9h5M11.5 12h5M11.5 15h3", stroke: "currentColor", strokeLinecap: "round", strokeWidth: "1.6" })
243
+ ] });
244
+ }
245
+ function SendIcon(props) {
246
+ return /* @__PURE__ */ jsx(Icon, { ...props, children: /* @__PURE__ */ jsx("path", { d: "M12 18V6M7.5 10.5 12 6l4.5 4.5", stroke: "currentColor", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "1.8" }) });
247
+ }
248
+ function StopIcon(props) {
249
+ return /* @__PURE__ */ jsx(Icon, { ...props, children: /* @__PURE__ */ jsx("rect", { x: "6", y: "6", width: "12", height: "12", rx: "2", fill: "currentColor" }) });
250
+ }
251
+ function HashIcon(props) {
252
+ return /* @__PURE__ */ jsx(Icon, { ...props, children: /* @__PURE__ */ jsx("path", { d: "M9.5 4 7.5 20M16.5 4l-2 16M4.5 9h15M3.5 15h15", stroke: "currentColor", strokeLinecap: "round", strokeWidth: "1.6" }) });
253
+ }
254
+ function ListIcon(props) {
255
+ return /* @__PURE__ */ jsx(Icon, { ...props, children: /* @__PURE__ */ jsx("path", { d: "M4 6h16M4 12h11M4 18h7", stroke: "currentColor", strokeLinecap: "round", strokeWidth: "1.8" }) });
256
+ }
257
+
258
+ // src/documentation/runtime/code-block.tsx
259
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
260
+ var CodeBlockContext = createContext({
261
+ assistantEnabled: false,
262
+ onAsk: () => {
263
+ }
264
+ });
265
+ var CodeBlockControlsProvider = CodeBlockContext.Provider;
266
+ function CodeBlockActions({ readCode }) {
267
+ const { assistantEnabled, onAsk } = useContext(CodeBlockContext);
268
+ const [copied, setCopied] = useState(false);
269
+ const copy = async () => {
270
+ await writeClipboardText(readCode());
271
+ setCopied(true);
272
+ window.setTimeout(() => setCopied(false), 1400);
273
+ };
274
+ return /* @__PURE__ */ jsxs2(Fragment, { children: [
275
+ /* @__PURE__ */ jsx2("button", { "aria-label": copied ? "Code copied" : "Copy code", onClick: () => {
276
+ void copy();
277
+ }, type: "button", children: copied ? /* @__PURE__ */ jsx2(CheckIcon, {}) : /* @__PURE__ */ jsx2(CopyIcon, {}) }),
278
+ assistantEnabled && /* @__PURE__ */ jsx2(
279
+ "button",
280
+ {
281
+ "aria-label": "Ask the documentation assistant about this code",
282
+ onClick: () => onAsk(readCode().trim().slice(0, 1200)),
283
+ type: "button",
284
+ children: /* @__PURE__ */ jsx2(SparkIcon, {})
285
+ }
286
+ )
287
+ ] });
288
+ }
289
+ function codeBlockLanguage(className) {
290
+ return /(?:^|\s)(?:language|lang)-([a-z0-9+#-]{1,20})/i.exec(className)?.[1] ?? "";
291
+ }
292
+ function childCodeClassName(children) {
293
+ const code = children;
294
+ return typeof code?.props?.className === "string" ? code.props.className : "";
295
+ }
296
+ function childCodeTitle(children) {
297
+ const code = children;
298
+ const title = code?.props?.["data-code-title"];
299
+ return typeof title === "string" && title ? title : void 0;
300
+ }
301
+ var CodeGroupContext = createContext(false);
302
+ var CodeGroupProvider = CodeGroupContext.Provider;
303
+ function CodeBlock({ children, ...props }) {
304
+ const preRef = useRef(null);
305
+ const inCodeGroup = useContext(CodeGroupContext);
306
+ const language = codeBlockLanguage(childCodeClassName(children));
307
+ const preTitle = props["data-code-title"];
308
+ const title = inCodeGroup ? void 0 : childCodeTitle(children) ?? (typeof preTitle === "string" && preTitle ? preTitle : void 0);
309
+ const readCode = () => preRef.current?.querySelector("code")?.textContent ?? preRef.current?.textContent ?? "";
310
+ return /* @__PURE__ */ jsxs2("div", { className: "documentation-code-block", children: [
311
+ title ? /* @__PURE__ */ jsx2("div", { className: "documentation-code-title", children: title }) : null,
312
+ /* @__PURE__ */ jsx2("pre", { ...props, ref: preRef, children }),
313
+ /* @__PURE__ */ jsxs2("div", { className: "documentation-code-actions", children: [
314
+ language ? /* @__PURE__ */ jsx2("span", { className: "documentation-code-language", children: language }) : null,
315
+ /* @__PURE__ */ jsx2(CodeBlockActions, { readCode })
316
+ ] })
317
+ ] });
318
+ }
319
+ var TAB_SELECTION_KEY = "deepspace-documentation-tab-selection";
320
+ var TAB_CHANGE_EVENT = "deepspace-documentation-tab-change";
321
+ function TabGroup({
322
+ className,
323
+ panels,
324
+ title
325
+ }) {
326
+ const groupId = useId();
327
+ const [active, setActive] = useState(0);
328
+ const controlsRef = useRef(null);
329
+ useEffect(() => {
330
+ const stored = safeStorageGet(TAB_SELECTION_KEY);
331
+ const index = stored ? panels.findIndex((panel) => panel.label === stored) : -1;
332
+ if (index >= 0) setActive(index);
333
+ }, [panels]);
334
+ useEffect(() => {
335
+ const sync = (event) => {
336
+ const detail = event.detail;
337
+ if (!detail?.label || detail.source === groupId) return;
338
+ const index = panels.findIndex((panel) => panel.label === detail.label);
339
+ if (index >= 0) setActive(index);
340
+ };
341
+ document.addEventListener(TAB_CHANGE_EVENT, sync);
342
+ return () => document.removeEventListener(TAB_CHANGE_EVENT, sync);
343
+ }, [groupId, panels]);
344
+ const select = (index, broadcast) => {
345
+ setActive(index);
346
+ const label = panels[index]?.label;
347
+ if (!broadcast || !label) return;
348
+ safeStorageSet(TAB_SELECTION_KEY, label);
349
+ document.dispatchEvent(new CustomEvent(TAB_CHANGE_EVENT, { detail: { label, source: groupId } }));
350
+ };
351
+ const onKeyDown = (event, index) => {
352
+ if (!["ArrowLeft", "ArrowRight", "Home", "End"].includes(event.key)) return;
353
+ event.preventDefault();
354
+ const next = event.key === "Home" ? 0 : event.key === "End" ? panels.length - 1 : (index + (event.key === "ArrowRight" ? 1 : -1) + panels.length) % panels.length;
355
+ select(next, true);
356
+ controlsRef.current?.querySelectorAll("button")[next]?.focus();
357
+ };
358
+ return /* @__PURE__ */ jsxs2("div", { className, "data-tab-group": true, children: [
359
+ title,
360
+ /* @__PURE__ */ jsx2("div", { className: "documentation-tab-buttons", ref: controlsRef, role: "tablist", children: panels.map((panel, index) => /* @__PURE__ */ jsx2(
361
+ "button",
362
+ {
363
+ "aria-selected": index === active,
364
+ id: `documentation-tab-${groupId}-${index}`,
365
+ onClick: () => select(index, true),
366
+ onKeyDown: (event) => onKeyDown(event, index),
367
+ role: "tab",
368
+ tabIndex: index === active ? 0 : -1,
369
+ type: "button",
370
+ children: panel.label
371
+ },
372
+ panel.label
373
+ )) }),
374
+ panels.map((panel, index) => /* @__PURE__ */ jsx2(
375
+ "section",
376
+ {
377
+ "aria-labelledby": `documentation-tab-${groupId}-${index}`,
378
+ className: "documentation-tab",
379
+ "data-tab": true,
380
+ "data-tab-title": panel.label,
381
+ hidden: index !== active,
382
+ role: "tabpanel",
383
+ children: panel.content
384
+ },
385
+ panel.label
386
+ ))
387
+ ] });
388
+ }
389
+
390
+ // src/documentation/runtime/page-actions.tsx
391
+ import { useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
392
+ import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
393
+ function PageActions({
394
+ data,
395
+ onAssistantOpen
396
+ }) {
397
+ const [copied, setCopied] = useState2(null);
398
+ const detailsRef = useRef2(null);
399
+ const actions = data.config.contextual.actions.filter((action) => {
400
+ if (data.page.route === "/404") return false;
401
+ if (action === "assistant" && data.config.assistant.access === "disabled") return false;
402
+ if ((action === "mcp" || action === "add-mcp" || action === "cursor" || action === "vscode") && data.config.mcp.access !== "public") return false;
403
+ return true;
404
+ });
405
+ const primary = actions[0];
406
+ useEffect2(() => {
407
+ const closeOnPointerDown = (event) => {
408
+ const details = detailsRef.current;
409
+ if (details && !details.contains(event.target)) details.open = false;
410
+ };
411
+ const closeOnEscape = (event) => {
412
+ if (event.key === "Escape" && detailsRef.current?.open) {
413
+ detailsRef.current.open = false;
414
+ detailsRef.current.querySelector("summary")?.focus();
415
+ }
416
+ };
417
+ document.addEventListener("pointerdown", closeOnPointerDown);
418
+ document.addEventListener("keydown", closeOnEscape);
419
+ return () => {
420
+ document.removeEventListener("pointerdown", closeOnPointerDown);
421
+ document.removeEventListener("keydown", closeOnEscape);
422
+ };
423
+ }, []);
424
+ if (!primary) return null;
425
+ const markCopied = (action) => {
426
+ setCopied(action);
427
+ window.setTimeout(() => setCopied((current) => current === action ? null : current), 1600);
428
+ };
429
+ const copyPage = async () => {
430
+ const response = await fetch(data.page.markdownUrl);
431
+ if (!response.ok) return;
432
+ await writeClipboardText(await response.text());
433
+ markCopied("copy");
434
+ closeMenu();
435
+ };
436
+ const copyMcp = async () => {
437
+ await writeClipboardText(new URL(documentationPublicPath(data.basePath, "/mcp"), window.location.origin).toString());
438
+ markCopied("mcp");
439
+ closeMenu();
440
+ };
441
+ const copyMcpInstall = async () => {
442
+ const mcpUrl = new URL(documentationPublicPath(data.basePath, "/mcp"), window.location.origin).toString();
443
+ await writeClipboardText(`npx add-mcp ${mcpUrl}`);
444
+ markCopied("add-mcp");
445
+ closeMenu();
446
+ };
447
+ const closeMenu = () => {
448
+ const details = detailsRef.current;
449
+ if (!details?.open) return;
450
+ details.open = false;
451
+ details.querySelector("summary")?.focus();
452
+ };
453
+ const openAiContext = (provider) => {
454
+ const markdownUrl = new URL(data.page.markdownUrl, window.location.origin).toString();
455
+ const prompt = `Use ${markdownUrl} as context. Explain ${data.page.title} and answer my questions about this documentation page.`;
456
+ const destination = new URL(provider === "chatgpt" ? "https://chatgpt.com/" : "https://claude.ai/new");
457
+ destination.searchParams.set("q", prompt);
458
+ window.open(destination.toString(), "_blank", "noopener,noreferrer");
459
+ closeMenu();
460
+ };
461
+ const openMcpEditor = (editor) => {
462
+ const mcpUrl = new URL(documentationPublicPath(data.basePath, "/mcp"), window.location.origin).toString();
463
+ const name = data.config.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "") || "documentation";
464
+ if (editor === "cursor") {
465
+ const config = window.btoa(JSON.stringify({ url: mcpUrl }));
466
+ window.location.href = `cursor://anysphere.cursor-deeplink/mcp/install?name=${encodeURIComponent(name)}&config=${encodeURIComponent(config)}`;
467
+ } else {
468
+ const config = encodeURIComponent(JSON.stringify({ name, type: "http", url: mcpUrl }));
469
+ window.location.href = `vscode:mcp/install?${config}`;
470
+ }
471
+ closeMenu();
472
+ };
473
+ const content = (icon, label, description, menuItem, external = false) => menuItem ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
474
+ /* @__PURE__ */ jsx3("span", { className: "documentation-contextual-action-icon", children: icon }),
475
+ /* @__PURE__ */ jsxs3("span", { className: "documentation-contextual-action-copy", children: [
476
+ /* @__PURE__ */ jsx3("strong", { children: label }),
477
+ /* @__PURE__ */ jsx3("small", { children: description })
478
+ ] }),
479
+ external && /* @__PURE__ */ jsx3(ExternalIcon, { className: "documentation-contextual-action-external" })
480
+ ] }) : /* @__PURE__ */ jsxs3(Fragment2, { children: [
481
+ icon,
482
+ label
483
+ ] });
484
+ const renderAction = (action, menuItem = false) => {
485
+ const role = menuItem ? "menuitem" : void 0;
486
+ if (action === "view") {
487
+ return /* @__PURE__ */ jsx3("a", { href: data.page.markdownUrl, onClick: closeMenu, role, children: content(/* @__PURE__ */ jsx3(FileIcon, {}), "View as Markdown", "Open the raw Markdown page", menuItem) }, action);
488
+ }
489
+ if (action === "assistant") {
490
+ return /* @__PURE__ */ jsx3("button", { role, type: "button", onClick: () => {
491
+ closeMenu();
492
+ onAssistantOpen(`Explain the key ideas on ${data.page.title}.`);
493
+ }, children: content(/* @__PURE__ */ jsx3(OrbitMark, {}), "Ask about this page", "Use the DeepSpace agent with page context", menuItem) }, action);
494
+ }
495
+ if (action === "chatgpt" || action === "claude") {
496
+ const label2 = action === "chatgpt" ? "Open in ChatGPT" : "Open in Claude";
497
+ return /* @__PURE__ */ jsx3("button", { role, type: "button", onClick: () => openAiContext(action), children: content(/* @__PURE__ */ jsx3(MessageIcon, {}), label2, "Ask questions about this page", menuItem, true) }, action);
498
+ }
499
+ if (action === "mcp") {
500
+ const label2 = copied === action ? "MCP URL copied" : "Copy MCP server URL";
501
+ return /* @__PURE__ */ jsx3("button", { role, type: "button", onClick: copyMcp, children: content(copied === action ? /* @__PURE__ */ jsx3(CheckIcon, {}) : /* @__PURE__ */ jsx3(CopyIcon, {}), label2, "Copy the same-release documentation endpoint", menuItem) }, action);
502
+ }
503
+ if (action === "add-mcp") {
504
+ const label2 = copied === action ? "Install command copied" : "Copy MCP install command";
505
+ return /* @__PURE__ */ jsx3("button", { role, type: "button", onClick: copyMcpInstall, children: content(copied === action ? /* @__PURE__ */ jsx3(CheckIcon, {}) : /* @__PURE__ */ jsx3(TerminalIcon, {}), label2, "Copy npx command to install MCP server", menuItem) }, action);
506
+ }
507
+ if (action === "cursor" || action === "vscode") {
508
+ const label2 = action === "cursor" ? "Connect to Cursor" : "Connect to VS Code";
509
+ return /* @__PURE__ */ jsx3("button", { role, type: "button", onClick: () => openMcpEditor(action), children: content(/* @__PURE__ */ jsx3(EditorIcon, {}), label2, `Install MCP server in ${action === "cursor" ? "Cursor" : "VS Code"}`, menuItem, true) }, action);
510
+ }
511
+ const label = copied === action ? "Page copied" : "Copy page";
512
+ return /* @__PURE__ */ jsx3("button", { role, type: "button", onClick: copyPage, children: content(copied === action ? /* @__PURE__ */ jsx3(CheckIcon, {}) : /* @__PURE__ */ jsx3(CopyIcon, {}), label, "Copy page as Markdown for LLMs", menuItem) }, action);
513
+ };
514
+ return /* @__PURE__ */ jsxs3("div", { className: "documentation-contextual-actions", "aria-label": "Page actions", children: [
515
+ /* @__PURE__ */ jsx3("div", { className: "documentation-contextual-primary", "aria-live": "polite", children: renderAction(primary) }),
516
+ actions.length > 0 && /* @__PURE__ */ jsxs3("details", { ref: detailsRef, children: [
517
+ /* @__PURE__ */ jsx3("summary", { "aria-label": "More page actions", children: /* @__PURE__ */ jsx3(ChevronDownIcon, {}) }),
518
+ /* @__PURE__ */ jsx3("div", { "aria-label": "More page actions", className: "documentation-contextual-menu", role: "menu", children: actions.map((action) => renderAction(action, true)) })
519
+ ] })
520
+ ] });
521
+ }
522
+
523
+ // src/documentation/runtime/article.tsx
524
+ import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
525
+ var HTML_PROSE_SCOPE = '.documentation-prose[data-prose="html"]';
526
+ function Article({
527
+ children,
528
+ data,
529
+ launcher,
530
+ onAssistantOpen
531
+ }) {
532
+ const articleRef = useRef3(null);
533
+ const codeBlocks = useCodeBlockMounts(articleRef, data.page.route);
534
+ useArticleEnhancements(articleRef, data);
535
+ const controls = useMemo(() => ({
536
+ assistantEnabled: data.config.assistant.access !== "disabled",
537
+ onAsk: (code) => onAssistantOpen(`Explain this ${data.page.title} example and the important details:
538
+
539
+ ${code}`)
540
+ }), [data.config.assistant.access, data.page.title, onAssistantOpen]);
541
+ return /* @__PURE__ */ jsxs4("article", { className: "documentation-article", ref: articleRef, children: [
542
+ /* @__PURE__ */ jsx4(Breadcrumbs, { items: data.breadcrumbs }),
543
+ /* @__PURE__ */ jsxs4("header", { className: "documentation-article-header", children: [
544
+ /* @__PURE__ */ jsxs4("div", { className: "documentation-article-meta", children: [
545
+ data.config.theme.eyebrowStyle !== "none" && /* @__PURE__ */ jsx4("div", { className: "documentation-eyebrow", children: data.page.kind === "openapi" ? "API reference" : data.config.theme.eyebrowStyle === "breadcrumbs" ? data.breadcrumbs.join(" / ") : data.breadcrumbs[0] ?? "Documentation" }),
546
+ /* @__PURE__ */ jsx4(PageActions, { data, onAssistantOpen })
547
+ ] }),
548
+ /* @__PURE__ */ jsx4("h1", { children: data.page.title }),
549
+ data.page.description && /* @__PURE__ */ jsx4("p", { className: "documentation-lede", children: data.page.description })
550
+ ] }),
551
+ /* @__PURE__ */ jsx4(OutlineDisclosure, { headings: outlineHeadings(data.page.headings) }),
552
+ /* @__PURE__ */ jsxs4(CodeBlockControlsProvider, { value: controls, children: [
553
+ children ?? /* @__PURE__ */ jsx4(
554
+ "div",
555
+ {
556
+ className: "documentation-prose",
557
+ "data-prose": "html",
558
+ dangerouslySetInnerHTML: { __html: data.page.html }
559
+ }
560
+ ),
561
+ codeBlocks.map((mount) => createPortal(
562
+ /* @__PURE__ */ jsx4(CodeBlockActions, { readCode: () => readPreText(mount.pre) }),
563
+ mount.host,
564
+ mount.key
565
+ ))
566
+ ] }),
567
+ data.page.openapi?.playground && /* @__PURE__ */ jsx4(ApiPlayground, { operation: data.page.openapi }),
568
+ launcher && /* @__PURE__ */ jsx4("div", { className: "documentation-launcher-dock", children: launcher }),
569
+ /* @__PURE__ */ jsxs4("nav", { className: "documentation-pagination", "aria-label": "Page navigation", children: [
570
+ data.previous ? /* @__PURE__ */ jsxs4("a", { rel: "prev", href: documentationPublicPath(data.basePath, data.previous.route), children: [
571
+ /* @__PURE__ */ jsx4("small", { children: "Previous" }),
572
+ /* @__PURE__ */ jsxs4("span", { children: [
573
+ /* @__PURE__ */ jsx4(ChevronRightIcon, { className: "is-back" }),
574
+ data.previous.title
575
+ ] })
576
+ ] }) : /* @__PURE__ */ jsx4("span", {}),
577
+ data.next ? /* @__PURE__ */ jsxs4("a", { rel: "next", href: documentationPublicPath(data.basePath, data.next.route), children: [
578
+ /* @__PURE__ */ jsx4("small", { children: "Next" }),
579
+ /* @__PURE__ */ jsxs4("span", { children: [
580
+ data.next.title,
581
+ /* @__PURE__ */ jsx4(ChevronRightIcon, {})
582
+ ] })
583
+ ] }) : /* @__PURE__ */ jsx4("span", {})
584
+ ] })
585
+ ] });
586
+ }
587
+ function Breadcrumbs({ items }) {
588
+ return /* @__PURE__ */ jsx4("nav", { className: "documentation-breadcrumbs", "aria-label": "Breadcrumb", children: items.map((item, index) => /* @__PURE__ */ jsxs4("span", { children: [
589
+ index > 0 && /* @__PURE__ */ jsx4(ChevronRightIcon, {}),
590
+ item
591
+ ] }, `${item}:${items.slice(0, index + 1).join("/")}`)) });
592
+ }
593
+ function outlineHeadings(headings) {
594
+ return headings.filter((heading) => heading.depth === 2 || heading.depth === 3);
595
+ }
596
+ function OutlineLinks({
597
+ active,
598
+ headings
599
+ }) {
600
+ return /* @__PURE__ */ jsx4(Fragment3, { children: headings.map((heading) => /* @__PURE__ */ jsx4(
601
+ "a",
602
+ {
603
+ "aria-current": active === heading.id ? "location" : void 0,
604
+ className: `${heading.depth === 3 ? "depth-3 " : ""}${active === heading.id ? "is-active" : ""}`,
605
+ href: `#${heading.id}`,
606
+ children: heading.text
607
+ },
608
+ heading.id
609
+ )) });
610
+ }
611
+ function OutlineDisclosure({ headings }) {
612
+ if (headings.length === 0) return null;
613
+ return /* @__PURE__ */ jsxs4("details", { className: "documentation-outline-disclosure", children: [
614
+ /* @__PURE__ */ jsxs4("summary", { children: [
615
+ /* @__PURE__ */ jsx4(ListIcon, {}),
616
+ "On this page"
617
+ ] }),
618
+ /* @__PURE__ */ jsx4("nav", { className: "documentation-outline", "aria-label": "On this page", children: /* @__PURE__ */ jsx4(OutlineLinks, { active: "", headings }) })
619
+ ] });
620
+ }
621
+ function ContextRail({
622
+ data
623
+ }) {
624
+ const headings = useMemo(() => outlineHeadings(data.page.headings), [data.page.headings]);
625
+ const [active, setActive] = useState3(headings[0]?.id ?? "");
626
+ useEffect3(() => setActive(headings[0]?.id ?? ""), [headings]);
627
+ useEffect3(() => {
628
+ let frame = null;
629
+ const measure = () => {
630
+ frame = null;
631
+ const root = document.documentElement;
632
+ const clearance = (Number.parseFloat(getComputedStyle(root).scrollPaddingTop) || 96) + 4;
633
+ const scrollable = root.scrollHeight > window.innerHeight + 4;
634
+ const atBottom = scrollable && window.innerHeight + window.scrollY >= root.scrollHeight - 2;
635
+ let next = headings[0]?.id ?? "";
636
+ for (const heading of headings) {
637
+ const element = document.getElementById(heading.id);
638
+ if (!element) continue;
639
+ if (element.getBoundingClientRect().top <= clearance) next = heading.id;
640
+ else break;
641
+ }
642
+ if (atBottom) {
643
+ const hashId = decodeURIComponent(window.location.hash.slice(1));
644
+ if (hashId && headings.some((heading) => heading.id === hashId)) {
645
+ const top = document.getElementById(hashId)?.getBoundingClientRect().top;
646
+ if (top !== void 0 && top > clearance && top < window.innerHeight) next = hashId;
647
+ }
648
+ }
649
+ setActive(next);
650
+ };
651
+ const schedule = () => {
652
+ if (frame === null) frame = window.requestAnimationFrame(measure);
653
+ };
654
+ const onOutlineClick = (event) => {
655
+ if (event.target instanceof Element && event.target.closest(".documentation-outline a")) {
656
+ window.setTimeout(schedule, 0);
657
+ }
658
+ };
659
+ measure();
660
+ window.addEventListener("scroll", schedule, { passive: true });
661
+ window.addEventListener("resize", schedule, { passive: true });
662
+ window.addEventListener("popstate", schedule);
663
+ window.addEventListener("hashchange", schedule);
664
+ document.addEventListener("click", onOutlineClick);
665
+ return () => {
666
+ window.removeEventListener("scroll", schedule);
667
+ window.removeEventListener("resize", schedule);
668
+ window.removeEventListener("popstate", schedule);
669
+ window.removeEventListener("hashchange", schedule);
670
+ document.removeEventListener("click", onOutlineClick);
671
+ if (frame !== null) window.cancelAnimationFrame(frame);
672
+ };
673
+ }, [headings]);
674
+ return /* @__PURE__ */ jsx4("aside", { className: "documentation-context-rail", "aria-label": "Page context", children: headings.length > 0 && /* @__PURE__ */ jsxs4("nav", { className: "documentation-outline", "aria-label": "On this page", children: [
675
+ /* @__PURE__ */ jsx4("h2", { children: "On this page" }),
676
+ /* @__PURE__ */ jsx4(OutlineLinks, { active, headings })
677
+ ] }) });
678
+ }
679
+ function ApiPlayground({ operation }) {
680
+ const [body, setBody] = useState3("");
681
+ const [token, setToken] = useState3("");
682
+ const [responseText, setResponseText] = useState3("No request sent.");
683
+ const [sending, setSending] = useState3(false);
684
+ if (!operation.baseUrl) return null;
685
+ const url = `${operation.baseUrl.replace(/\/$/, "")}${operation.path}`;
686
+ const send = async () => {
687
+ setSending(true);
688
+ setResponseText("Sending request\u2026");
689
+ try {
690
+ const headers = {};
691
+ if (token) headers.Authorization = `Bearer ${token}`;
692
+ if (body.trim()) headers["Content-Type"] = "application/json";
693
+ const response = await fetch(url, {
694
+ method: operation.method,
695
+ headers,
696
+ body: ["GET", "HEAD"].includes(operation.method) ? void 0 : body.trim() || void 0
697
+ });
698
+ const text = await response.text();
699
+ setResponseText(`${response.status} ${response.statusText}
700
+
701
+ ${text}`);
702
+ } catch (error) {
703
+ setResponseText(errorMessage(error));
704
+ } finally {
705
+ setSending(false);
706
+ }
707
+ };
708
+ return /* @__PURE__ */ jsxs4("section", { className: "documentation-playground", "data-playground": true, children: [
709
+ /* @__PURE__ */ jsxs4("div", { className: "documentation-playground-title", children: [
710
+ /* @__PURE__ */ jsxs4("div", { children: [
711
+ /* @__PURE__ */ jsx4("span", { className: `method method-${operation.method.toLowerCase()}`, children: operation.method }),
712
+ /* @__PURE__ */ jsx4("strong", { children: "Try this operation" })
713
+ ] }),
714
+ /* @__PURE__ */ jsx4("code", { children: url })
715
+ ] }),
716
+ /* @__PURE__ */ jsx4("p", { children: "Requests run in your browser. Tokens stay in memory and never enter search or assistant context." }),
717
+ /* @__PURE__ */ jsxs4("label", { children: [
718
+ /* @__PURE__ */ jsx4("span", { children: "Bearer token" }),
719
+ /* @__PURE__ */ jsx4("input", { type: "password", autoCapitalize: "none", autoComplete: "off", autoCorrect: "off", spellCheck: false, value: token, onChange: (event) => setToken(event.target.value), placeholder: "Optional" })
720
+ ] }),
721
+ /* @__PURE__ */ jsxs4("label", { children: [
722
+ /* @__PURE__ */ jsx4("span", { children: "JSON body" }),
723
+ /* @__PURE__ */ jsx4("textarea", { rows: 7, autoCapitalize: "none", autoComplete: "off", autoCorrect: "off", spellCheck: false, value: body, onChange: (event) => setBody(event.target.value), placeholder: "{}" })
724
+ ] }),
725
+ /* @__PURE__ */ jsx4("button", { type: "button", disabled: sending, onClick: send, children: sending ? "Sending\u2026" : `Send ${operation.method} request` }),
726
+ /* @__PURE__ */ jsx4("pre", { "aria-live": "polite", children: responseText })
727
+ ] });
728
+ }
729
+ function useCodeBlockMounts(articleRef, route) {
730
+ const [mounts, setMounts] = useState3([]);
731
+ useEffect3(() => {
732
+ const article = articleRef.current;
733
+ if (!article) return;
734
+ const created = [];
735
+ const next = [];
736
+ article.querySelectorAll(HTML_PROSE_SCOPE + " pre").forEach((pre, index) => {
737
+ const block = document.createElement("div");
738
+ block.className = "documentation-code-block";
739
+ pre.replaceWith(block);
740
+ block.appendChild(pre);
741
+ const title = pre.getAttribute("data-code-title");
742
+ if (title && !block.closest("[data-tab-group]")) {
743
+ const bar = document.createElement("div");
744
+ bar.className = "documentation-code-title";
745
+ bar.textContent = title;
746
+ block.insertBefore(bar, pre);
747
+ }
748
+ const host = document.createElement("div");
749
+ host.className = "documentation-code-actions";
750
+ const language = codeBlockLanguage(pre.querySelector("code")?.className ?? "");
751
+ if (language) {
752
+ const label = document.createElement("span");
753
+ label.className = "documentation-code-language";
754
+ label.textContent = language;
755
+ host.appendChild(label);
756
+ }
757
+ block.appendChild(host);
758
+ created.push({ block, pre });
759
+ next.push({ key: `${route}:${index}`, host, pre });
760
+ });
761
+ setMounts(next);
762
+ return () => {
763
+ setMounts([]);
764
+ for (const { block, pre } of created) block.replaceWith(pre);
765
+ };
766
+ }, [articleRef, route]);
767
+ return mounts;
768
+ }
769
+ function readPreText(pre) {
770
+ return pre.querySelector("code")?.textContent ?? pre.textContent ?? "";
771
+ }
772
+ function useArticleEnhancements(articleRef, data) {
773
+ useEffect3(() => {
774
+ const article = articleRef.current;
775
+ if (!article) return;
776
+ const cleanups = [];
777
+ article.querySelectorAll(`${HTML_PROSE_SCOPE} [data-tab-group]`).forEach((group, groupIndex) => {
778
+ if (group.querySelector(":scope > .documentation-tab-buttons")) return;
779
+ const tabs = Array.from(group.querySelectorAll(":scope > [data-tab]"));
780
+ if (tabs.length === 0) return;
781
+ const controls = document.createElement("div");
782
+ controls.className = "documentation-tab-buttons";
783
+ controls.setAttribute("role", "tablist");
784
+ const labels = tabs.map((tab, tabIndex) => tab.dataset.tabTitle ?? `Tab ${tabIndex + 1}`);
785
+ const activate = (activeIndex, broadcast = false) => {
786
+ tabs.forEach((tab, index) => {
787
+ tab.hidden = index !== activeIndex;
788
+ tab.setAttribute("role", "tabpanel");
789
+ });
790
+ Array.from(controls.querySelectorAll("button")).forEach((button, index) => {
791
+ button.setAttribute("aria-selected", index === activeIndex ? "true" : "false");
792
+ button.setAttribute("tabindex", index === activeIndex ? "0" : "-1");
793
+ });
794
+ if (broadcast) {
795
+ const label = labels[activeIndex];
796
+ if (label) {
797
+ safeStorageSet("deepspace-documentation-tab-selection", label);
798
+ document.dispatchEvent(new CustomEvent("deepspace-documentation-tab-change", {
799
+ detail: { label, source: group }
800
+ }));
801
+ }
802
+ }
803
+ };
804
+ tabs.forEach((tab, tabIndex) => {
805
+ const button = document.createElement("button");
806
+ button.type = "button";
807
+ button.id = `documentation-tab-${groupIndex}-${tabIndex}`;
808
+ button.setAttribute("role", "tab");
809
+ button.textContent = labels[tabIndex];
810
+ tab.setAttribute("aria-labelledby", button.id);
811
+ const click = () => activate(tabIndex, true);
812
+ const keydown = (event) => {
813
+ if (!["ArrowLeft", "ArrowRight", "Home", "End"].includes(event.key)) return;
814
+ event.preventDefault();
815
+ const next = event.key === "Home" ? 0 : event.key === "End" ? tabs.length - 1 : (tabIndex + (event.key === "ArrowRight" ? 1 : -1) + tabs.length) % tabs.length;
816
+ activate(next);
817
+ controls.querySelectorAll("button")[next]?.focus();
818
+ };
819
+ button.addEventListener("click", click);
820
+ button.addEventListener("keydown", keydown);
821
+ controls.appendChild(button);
822
+ cleanups.push(() => {
823
+ button.removeEventListener("click", click);
824
+ button.removeEventListener("keydown", keydown);
825
+ });
826
+ });
827
+ const title = group.querySelector(":scope > .documentation-code-group-title");
828
+ if (title) group.insertBefore(controls, title.nextSibling);
829
+ if (!title) group.insertBefore(controls, group.firstChild);
830
+ const sync = (event) => {
831
+ const detail = event.detail;
832
+ if (!detail?.label || detail.source === group) return;
833
+ const matchingIndex = labels.findIndex((label) => label === detail.label);
834
+ if (matchingIndex >= 0) activate(matchingIndex);
835
+ };
836
+ document.addEventListener("deepspace-documentation-tab-change", sync);
837
+ const stored = safeStorageGet("deepspace-documentation-tab-selection");
838
+ const initial = stored ? labels.findIndex((label) => label === stored) : -1;
839
+ activate(initial >= 0 ? initial : 0);
840
+ cleanups.push(() => {
841
+ document.removeEventListener("deepspace-documentation-tab-change", sync);
842
+ controls.remove();
843
+ });
844
+ });
845
+ return () => cleanups.reverse().forEach((cleanup) => cleanup());
846
+ }, [articleRef, data.page.route]);
847
+ }
848
+
849
+ // src/documentation/runtime/assistant.tsx
850
+ import {
851
+ useEffect as useEffect5,
852
+ useRef as useRef4,
853
+ useState as useState4
854
+ } from "react";
855
+
856
+ // src/client/ai-stream.ts
857
+ function parseSseLine(line) {
858
+ if (!line) return null;
859
+ if (line.startsWith(":")) return null;
860
+ if (!line.startsWith("data:")) return null;
861
+ const raw = line.slice(5).replace(/^ /, "").replace(/\r$/, "");
862
+ if (raw === "[DONE]") return null;
863
+ try {
864
+ const parsed = JSON.parse(raw);
865
+ if (!parsed || typeof parsed !== "object") return null;
866
+ return parsed;
867
+ } catch {
868
+ return null;
869
+ }
870
+ }
871
+
872
+ // src/documentation/text.ts
873
+ function documentationSubject(name) {
874
+ const normalized = name.trim();
875
+ if (!normalized || /^documentation$/i.test(normalized)) return "documentation";
876
+ return /(?:\bdocs?|documentation)$/i.test(normalized) ? normalized : `${normalized} documentation`;
877
+ }
878
+
879
+ // src/documentation/runtime/assistant-transcript.ts
880
+ function completedDocumentationAssistantTranscript(messages, maxMessages = 12) {
881
+ const pairs = [];
882
+ for (let index = 0; index < messages.length - 1; index += 1) {
883
+ const user = messages[index];
884
+ if (user?.role !== "user" || user.state !== void 0 || !user.text.trim()) continue;
885
+ const assistant = messages[index + 1];
886
+ if (assistant?.role !== "assistant" || assistant.state !== void 0 || !assistant.text.trim()) {
887
+ continue;
888
+ }
889
+ pairs.push([
890
+ { role: "user", content: user.text },
891
+ { role: "assistant", content: assistant.text }
892
+ ]);
893
+ index += 1;
894
+ }
895
+ const pairLimit = Math.max(0, Math.floor(maxMessages / 2));
896
+ if (pairLimit === 0) return [];
897
+ return pairs.slice(-pairLimit).flat();
898
+ }
899
+
900
+ // src/documentation/runtime/dialog.ts
901
+ import { useEffect as useEffect4 } from "react";
902
+ function useDialogFocus(open, ref, onClose, trap = true, restoreId) {
903
+ useEffect4(() => {
904
+ if (!open) return;
905
+ const previous = document.activeElement;
906
+ const element = ref.current;
907
+ const frame = window.requestAnimationFrame(() => {
908
+ const target = element?.querySelector("[data-autofocus]") ?? element?.querySelector("button,a,input,textarea");
909
+ target?.focus();
910
+ });
911
+ const keydown = (event) => {
912
+ if (event.key === "Escape") {
913
+ event.preventDefault();
914
+ onClose();
915
+ return;
916
+ }
917
+ if (!trap || event.key !== "Tab" || !element) return;
918
+ const focusable = Array.from(element.querySelectorAll(
919
+ 'a[href],button:not([disabled]),input:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"])'
920
+ ));
921
+ if (focusable.length === 0) return;
922
+ const first = focusable[0];
923
+ const last = focusable[focusable.length - 1];
924
+ if (event.shiftKey && document.activeElement === first) {
925
+ event.preventDefault();
926
+ last?.focus();
927
+ } else if (!event.shiftKey && document.activeElement === last) {
928
+ event.preventDefault();
929
+ first?.focus();
930
+ }
931
+ };
932
+ document.addEventListener("keydown", keydown);
933
+ return () => {
934
+ window.cancelAnimationFrame(frame);
935
+ document.removeEventListener("keydown", keydown);
936
+ const restoreTarget = restoreId ? document.getElementById(restoreId) : previous?.isConnected && previous !== document.body && !element?.contains(previous) ? previous : null;
937
+ restoreTarget?.focus();
938
+ };
939
+ }, [onClose, open, ref, restoreId, trap]);
940
+ }
941
+
942
+ // src/documentation/runtime/assistant.tsx
943
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
944
+ function AssistantLauncher({
945
+ input,
946
+ name,
947
+ onInputChange,
948
+ onSubmit
949
+ }) {
950
+ const subject = documentationSubject(name);
951
+ const submit = (event) => {
952
+ event.preventDefault();
953
+ const question = input.trim();
954
+ if (!question) return;
955
+ onSubmit(question);
956
+ };
957
+ return /* @__PURE__ */ jsxs5(
958
+ "form",
959
+ {
960
+ className: "documentation-assistant-launcher",
961
+ id: "deepspace-documentation-assistant-launcher",
962
+ onSubmit: submit,
963
+ children: [
964
+ /* @__PURE__ */ jsx5(
965
+ "input",
966
+ {
967
+ "aria-label": `Ask the ${subject} agent`,
968
+ autoCapitalize: "sentences",
969
+ autoComplete: "off",
970
+ autoCorrect: "off",
971
+ enterKeyHint: "send",
972
+ id: "deepspace-documentation-assistant-launcher-input",
973
+ maxLength: 4e3,
974
+ onChange: (event) => onInputChange(event.target.value),
975
+ placeholder: "Ask anything\u2026",
976
+ spellCheck: false,
977
+ value: input
978
+ }
979
+ ),
980
+ /* @__PURE__ */ jsxs5("span", { "aria-hidden": "true", className: "documentation-launcher-hint", children: [
981
+ /* @__PURE__ */ jsx5("kbd", { children: "\u2318" }),
982
+ /* @__PURE__ */ jsx5("kbd", { children: "I" })
983
+ ] }),
984
+ /* @__PURE__ */ jsx5("button", { className: "documentation-launcher-send", type: "submit", disabled: !input.trim(), "aria-label": "Send question to documentation agent", children: /* @__PURE__ */ jsx5(SendIcon, {}) })
985
+ ]
986
+ }
987
+ );
988
+ }
989
+ function DocumentationAssistant({
990
+ access,
991
+ basePath,
992
+ input,
993
+ name,
994
+ onClose,
995
+ onInputChange,
996
+ open,
997
+ route,
998
+ seed,
999
+ suggestions
1000
+ }) {
1001
+ const panelRef = useRef4(null);
1002
+ const formRef = useRef4(null);
1003
+ const messagesRef = useRef4(null);
1004
+ const composerRef = useRef4(null);
1005
+ const abortRef = useRef4(null);
1006
+ const queuedQuestionRef = useRef4(null);
1007
+ const handledSeedRef = useRef4(0);
1008
+ const [messages, setMessages] = useState4([
1009
+ { id: "welcome", role: "assistant", text: `I can search the ${documentationSubject(name)} and cite the exact pages I use.` }
1010
+ ]);
1011
+ const [sending, setSending] = useState4(false);
1012
+ useDialogFocus(open, panelRef, onClose, false, "deepspace-documentation-assistant-launcher-input");
1013
+ useEffect5(() => {
1014
+ if (!open || seed.id === 0 || handledSeedRef.current === seed.id) return;
1015
+ handledSeedRef.current = seed.id;
1016
+ if (!seed.submit || !seed.value?.trim()) return;
1017
+ queuedQuestionRef.current = seed.value;
1018
+ const frame = window.requestAnimationFrame(() => formRef.current?.requestSubmit());
1019
+ return () => window.cancelAnimationFrame(frame);
1020
+ }, [open, seed]);
1021
+ useEffect5(() => {
1022
+ const element = messagesRef.current;
1023
+ if (element) element.scrollTop = element.scrollHeight;
1024
+ }, [messages]);
1025
+ useEffect5(() => {
1026
+ const element = composerRef.current;
1027
+ if (!element) return;
1028
+ element.style.height = "auto";
1029
+ element.style.height = `${Math.min(element.scrollHeight, 160)}px`;
1030
+ element.style.overflowY = element.scrollHeight > 160 ? "auto" : "hidden";
1031
+ }, [input, open]);
1032
+ useEffect5(() => () => abortRef.current?.abort(), []);
1033
+ if (!open) return null;
1034
+ const submit = async (event) => {
1035
+ event?.preventDefault();
1036
+ const question = (queuedQuestionRef.current ?? input).trim();
1037
+ queuedQuestionRef.current = null;
1038
+ if (!question || sending) return;
1039
+ const transcript = completedDocumentationAssistantTranscript(messages);
1040
+ onInputChange("");
1041
+ setSending(true);
1042
+ const userId = `user-${Date.now()}`;
1043
+ const answerId = `assistant-${Date.now()}`;
1044
+ setMessages((current) => [...current, { id: userId, role: "user", text: question }, { id: answerId, role: "assistant", text: "", state: "streaming", status: "Searching this documentation\u2026" }]);
1045
+ const controller = new AbortController();
1046
+ abortRef.current = controller;
1047
+ let timedOut = false;
1048
+ const slowTimer = window.setTimeout(() => {
1049
+ setMessages((current) => current.map((message) => message.id === answerId ? { ...message, status: "Still working through the documentation\u2026" } : message));
1050
+ }, 12e3);
1051
+ const timeoutTimer = window.setTimeout(() => {
1052
+ timedOut = true;
1053
+ controller.abort();
1054
+ }, 12e4);
1055
+ try {
1056
+ const headers = {
1057
+ "Content-Type": "application/json"
1058
+ };
1059
+ if (access === "authenticated") {
1060
+ const tokenResponse = await fetch("/api/auth/token", { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" } });
1061
+ const tokenData = tokenResponse.ok ? await tokenResponse.json().catch(() => null) : null;
1062
+ if (!tokenData?.token) throw new Error("Sign in to use the documentation assistant.");
1063
+ headers.Authorization = `Bearer ${tokenData.token}`;
1064
+ }
1065
+ const response = await fetch(documentationPublicPath(basePath, "/api/ai"), {
1066
+ method: "POST",
1067
+ headers,
1068
+ body: JSON.stringify({ question, route, messages: transcript }),
1069
+ signal: controller.signal
1070
+ });
1071
+ if (!response.ok || !response.body) {
1072
+ const body = await response.json().catch(() => null);
1073
+ throw new Error(body?.error ?? `Assistant unavailable (${response.status})`);
1074
+ }
1075
+ const reader = response.body.getReader();
1076
+ const decoder = new TextDecoder();
1077
+ let buffer = "";
1078
+ let answer = "";
1079
+ const consumeLine = (line) => {
1080
+ const data = parseSseLine(line);
1081
+ if (!data) return;
1082
+ if (data.type === "text-delta" && typeof data.delta === "string") {
1083
+ answer += data.delta;
1084
+ setMessages((current) => current.map((message) => message.id === answerId ? { ...message, text: answer, state: "streaming", status: void 0 } : message));
1085
+ } else if (data.type === "tool-input-available") {
1086
+ const status = data.toolName === "documentation_read" ? "Reading the relevant source pages\u2026" : "Searching this documentation\u2026";
1087
+ setMessages((current) => current.map((message) => message.id === answerId ? { ...message, status } : message));
1088
+ } else if (data.type === "tool-output-available") {
1089
+ setMessages((current) => current.map((message) => message.id === answerId ? { ...message, status: "Reviewing the source material\u2026" } : message));
1090
+ } else if (data.type === "error") {
1091
+ throw new Error(typeof data.errorText === "string" ? data.errorText : "The assistant stream failed.");
1092
+ }
1093
+ };
1094
+ while (true) {
1095
+ const chunk = await reader.read();
1096
+ if (chunk.done) break;
1097
+ buffer += decoder.decode(chunk.value, { stream: true });
1098
+ const lines = buffer.split(/\r?\n/);
1099
+ buffer = lines.pop() ?? "";
1100
+ for (const line of lines) consumeLine(line);
1101
+ }
1102
+ buffer += decoder.decode();
1103
+ consumeLine(buffer);
1104
+ setMessages((current) => current.map((message) => message.id === answerId ? { ...message, text: answer || "No answer was returned.", state: void 0 } : message));
1105
+ } catch (error) {
1106
+ const message = timedOut ? "The documentation search took too long. Try a narrower question." : controller.signal.aborted ? "Response stopped." : errorMessage(error);
1107
+ setMessages((current) => current.map((item) => item.id === answerId ? { ...item, text: message, state: "error" } : item));
1108
+ } finally {
1109
+ window.clearTimeout(slowTimer);
1110
+ window.clearTimeout(timeoutTimer);
1111
+ abortRef.current = null;
1112
+ setSending(false);
1113
+ }
1114
+ };
1115
+ const defaults = suggestions ?? ["How do I get started?", "Show me a complete example", "What should I understand first?"];
1116
+ return /* @__PURE__ */ jsxs5(
1117
+ "aside",
1118
+ {
1119
+ "aria-label": "Documentation assistant",
1120
+ className: "documentation-assistant",
1121
+ id: "deepspace-documentation-assistant",
1122
+ ref: panelRef,
1123
+ role: "complementary",
1124
+ children: [
1125
+ /* @__PURE__ */ jsxs5("header", { children: [
1126
+ /* @__PURE__ */ jsx5("div", { children: /* @__PURE__ */ jsxs5("span", { children: [
1127
+ /* @__PURE__ */ jsxs5("strong", { id: "deepspace-documentation-assistant-title", children: [
1128
+ "Ask ",
1129
+ name
1130
+ ] }),
1131
+ /* @__PURE__ */ jsx5("small", { children: "Answers from this documentation build" })
1132
+ ] }) }),
1133
+ /* @__PURE__ */ jsx5("button", { type: "button", onClick: onClose, "aria-label": "Close documentation assistant", children: /* @__PURE__ */ jsx5(CloseIcon, {}) })
1134
+ ] }),
1135
+ /* @__PURE__ */ jsxs5("div", { className: "documentation-assistant-messages", ref: messagesRef, "aria-live": "polite", children: [
1136
+ messages.map((message) => /* @__PURE__ */ jsx5("div", { className: `documentation-assistant-message is-${message.role}${message.state ? ` is-${message.state}` : ""}`, children: /* @__PURE__ */ jsxs5("div", { children: [
1137
+ message.role === "assistant" ? /* @__PURE__ */ jsx5(AssistantMarkdown, { value: message.text }) : message.text,
1138
+ message.state === "streaming" && !message.text && /* @__PURE__ */ jsxs5("span", { className: "documentation-assistant-activity", role: "status", children: [
1139
+ /* @__PURE__ */ jsx5("span", { "aria-hidden": "true" }),
1140
+ message.status ?? "Working\u2026"
1141
+ ] })
1142
+ ] }) }, message.id)),
1143
+ messages.length === 1 && /* @__PURE__ */ jsxs5("div", { className: "documentation-assistant-suggestions", children: [
1144
+ /* @__PURE__ */ jsx5("p", { children: "Try asking" }),
1145
+ defaults.map((suggestion) => /* @__PURE__ */ jsx5("button", { type: "button", onClick: () => onInputChange(suggestion), children: suggestion }, suggestion))
1146
+ ] })
1147
+ ] }),
1148
+ /* @__PURE__ */ jsxs5("form", { className: "documentation-assistant-composer", onSubmit: submit, ref: formRef, children: [
1149
+ /* @__PURE__ */ jsx5(
1150
+ "textarea",
1151
+ {
1152
+ "aria-label": "Ask a documentation question",
1153
+ autoCapitalize: "sentences",
1154
+ autoComplete: "off",
1155
+ autoCorrect: "off",
1156
+ "data-autofocus": true,
1157
+ enterKeyHint: "send",
1158
+ maxLength: 4e3,
1159
+ onChange: (event) => onInputChange(event.target.value),
1160
+ onKeyDown: (event) => {
1161
+ if (event.key === "Enter" && !event.shiftKey) {
1162
+ event.preventDefault();
1163
+ void submit();
1164
+ }
1165
+ },
1166
+ placeholder: "Ask a question about this documentation",
1167
+ ref: composerRef,
1168
+ rows: 1,
1169
+ spellCheck: false,
1170
+ value: input
1171
+ }
1172
+ ),
1173
+ sending ? /* @__PURE__ */ jsx5("button", { type: "button", onClick: () => abortRef.current?.abort(), "aria-label": "Stop response", children: /* @__PURE__ */ jsx5(StopIcon, {}) }) : /* @__PURE__ */ jsx5("button", { type: "submit", disabled: !input.trim(), "aria-label": "Send question", children: /* @__PURE__ */ jsx5(SendIcon, {}) })
1174
+ ] }),
1175
+ /* @__PURE__ */ jsx5("footer", { children: "Responses cite this deployment and may still contain mistakes." })
1176
+ ]
1177
+ }
1178
+ );
1179
+ }
1180
+ function AssistantMarkdown({ value }) {
1181
+ const blocks = [];
1182
+ const fence = /```([^\n`]*)\n([\s\S]*?)(?:```|$)/g;
1183
+ let cursor = 0;
1184
+ for (const match of value.matchAll(fence)) {
1185
+ const index = match.index ?? 0;
1186
+ if (index > cursor) blocks.push(...renderAssistantText(value.slice(cursor, index), `text-${cursor}`));
1187
+ blocks.push(
1188
+ /* @__PURE__ */ jsx5(
1189
+ AssistantCodeBlock,
1190
+ {
1191
+ code: (match[2] ?? "").replace(/\n$/, ""),
1192
+ language: (match[1] ?? "").trim().split(/\s+/, 1)[0] ?? ""
1193
+ },
1194
+ `code-${index}`
1195
+ )
1196
+ );
1197
+ cursor = index + match[0].length;
1198
+ }
1199
+ if (cursor < value.length) blocks.push(...renderAssistantText(value.slice(cursor), `text-${cursor}`));
1200
+ return /* @__PURE__ */ jsx5("div", { className: "documentation-assistant-markdown", children: blocks });
1201
+ }
1202
+ function AssistantCodeBlock({ code, language }) {
1203
+ const [copied, setCopied] = useState4(false);
1204
+ const copy = async () => {
1205
+ await writeClipboardText(code);
1206
+ setCopied(true);
1207
+ window.setTimeout(() => setCopied(false), 1400);
1208
+ };
1209
+ return /* @__PURE__ */ jsxs5("figure", { className: "documentation-assistant-code", children: [
1210
+ /* @__PURE__ */ jsxs5("figcaption", { children: [
1211
+ /* @__PURE__ */ jsx5("span", { children: language || "Code" }),
1212
+ /* @__PURE__ */ jsx5("button", { type: "button", onClick: copy, children: copied ? "Copied" : "Copy" })
1213
+ ] }),
1214
+ /* @__PURE__ */ jsx5("pre", { children: /* @__PURE__ */ jsx5("code", { children: code }) })
1215
+ ] });
1216
+ }
1217
+ function renderAssistantText(value, keyPrefix) {
1218
+ const lines = value.split("\n");
1219
+ const blocks = [];
1220
+ let index = 0;
1221
+ while (index < lines.length) {
1222
+ const line = lines[index]?.trim() ?? "";
1223
+ if (!line) {
1224
+ index += 1;
1225
+ continue;
1226
+ }
1227
+ const heading = line.match(/^(#{1,4})\s+(.+)$/);
1228
+ if (heading) {
1229
+ blocks.push(/* @__PURE__ */ jsx5("strong", { className: "documentation-assistant-heading", children: renderInlineMarkdown(heading[2]) }, `${keyPrefix}-heading-${index}`));
1230
+ index += 1;
1231
+ continue;
1232
+ }
1233
+ const unordered = /^[-*]\s+/.test(line);
1234
+ const ordered = /^\d+[.)]\s+/.test(line);
1235
+ if (unordered || ordered) {
1236
+ const items = [];
1237
+ while (index < lines.length) {
1238
+ const candidate = lines[index]?.trim() ?? "";
1239
+ const match = unordered ? candidate.match(/^[-*]\s+(.+)$/) : candidate.match(/^\d+[.)]\s+(.+)$/);
1240
+ if (!match) break;
1241
+ items.push(/* @__PURE__ */ jsx5("li", { children: renderInlineMarkdown(match[1]) }, `${keyPrefix}-item-${index}`));
1242
+ index += 1;
1243
+ }
1244
+ blocks.push(unordered ? /* @__PURE__ */ jsx5("ul", { children: items }, `${keyPrefix}-list-${index}`) : /* @__PURE__ */ jsx5("ol", { children: items }, `${keyPrefix}-list-${index}`));
1245
+ continue;
1246
+ }
1247
+ const paragraph = [line];
1248
+ index += 1;
1249
+ while (index < lines.length) {
1250
+ const candidate = lines[index]?.trim() ?? "";
1251
+ if (!candidate || /^(?:#{1,4}\s+|[-*]\s+|\d+[.)]\s+)/.test(candidate)) break;
1252
+ paragraph.push(candidate);
1253
+ index += 1;
1254
+ }
1255
+ blocks.push(/* @__PURE__ */ jsx5("p", { children: paragraph.map((part, partIndex) => /* @__PURE__ */ jsxs5("span", { children: [
1256
+ renderInlineMarkdown(part),
1257
+ partIndex < paragraph.length - 1 && " "
1258
+ ] }, `${partIndex}:${part.slice(0, 18)}`)) }, `${keyPrefix}-paragraph-${index}`));
1259
+ }
1260
+ return blocks;
1261
+ }
1262
+ function renderInlineMarkdown(value) {
1263
+ const pattern = /(\[[^\]]+\]\((?:https?:\/\/|\/)[^)]+\)|`[^`]+`|\*\*[^*]+\*\*)/g;
1264
+ return value.split(pattern).filter(Boolean).map((part, index) => {
1265
+ const link = part.match(/^\[([^\]]+)\]\(((?:https?:\/\/|\/)[^)]+)\)$/);
1266
+ if (link) return /* @__PURE__ */ jsx5("a", { href: link[2], ...externalProps(link[2]), children: link[1] }, `${index}:${part}`);
1267
+ if (part.startsWith("`") && part.endsWith("`")) return /* @__PURE__ */ jsx5("code", { children: part.slice(1, -1) }, `${index}:${part}`);
1268
+ if (part.startsWith("**") && part.endsWith("**")) return /* @__PURE__ */ jsx5("strong", { children: part.slice(2, -2) }, `${index}:${part}`);
1269
+ return /* @__PURE__ */ jsx5("span", { children: part }, `${index}:${part}`);
1270
+ });
1271
+ }
1272
+
1273
+ // src/documentation/runtime/navigation.tsx
1274
+ import { useEffect as useEffect6, useRef as useRef5, useState as useState5 } from "react";
1275
+
1276
+ // package.json
1277
+ var version = "0.35.0";
1278
+
1279
+ // src/documentation/runtime/navigation.tsx
1280
+ import { Fragment as Fragment4, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1281
+ function DocumentationHeader({
1282
+ data,
1283
+ onMenuOpen,
1284
+ onSearchOpen
1285
+ }) {
1286
+ return /* @__PURE__ */ jsxs6("header", { className: "documentation-header", children: [
1287
+ /* @__PURE__ */ jsx6(
1288
+ "a",
1289
+ {
1290
+ className: "documentation-brand",
1291
+ href: data.config.theme.logoHref ?? documentationPublicPath(data.basePath, "/"),
1292
+ "aria-label": `${documentationSubject(data.config.name)} home`,
1293
+ ...externalProps(data.config.theme.logoHref ?? documentationPublicPath(data.basePath, "/")),
1294
+ children: /* @__PURE__ */ jsx6(BrandLogo, { data })
1295
+ }
1296
+ ),
1297
+ /* @__PURE__ */ jsx6("div", { className: "documentation-header-center", children: /* @__PURE__ */ jsxs6("button", { className: "documentation-search-trigger", type: "button", onClick: onSearchOpen, "aria-label": "Search documentation", children: [
1298
+ /* @__PURE__ */ jsx6(SearchIcon, {}),
1299
+ /* @__PURE__ */ jsx6("span", { children: "Search documentation" }),
1300
+ /* @__PURE__ */ jsx6("kbd", { children: "\u2318K" })
1301
+ ] }) }),
1302
+ /* @__PURE__ */ jsx6("nav", { className: "documentation-top-links", "aria-label": "Product navigation", children: data.config.links.map((link) => /* @__PURE__ */ jsxs6("a", { href: link.href, ...externalProps(link.href), children: [
1303
+ link.label,
1304
+ isExternal(link.href) && /* @__PURE__ */ jsx6(ExternalIcon, {})
1305
+ ] }, `${link.label}:${link.href}`)) }),
1306
+ /* @__PURE__ */ jsxs6("div", { className: "documentation-mobile-actions", children: [
1307
+ /* @__PURE__ */ jsx6("button", { className: "documentation-icon-button", type: "button", onClick: onSearchOpen, "aria-label": "Search documentation", children: /* @__PURE__ */ jsx6(SearchIcon, {}) }),
1308
+ /* @__PURE__ */ jsx6("button", { className: "documentation-icon-button", type: "button", onClick: onMenuOpen, "aria-label": "Open documentation navigation", children: /* @__PURE__ */ jsx6(MenuIcon, {}) })
1309
+ ] })
1310
+ ] });
1311
+ }
1312
+ function BrandLogo({ data }) {
1313
+ const { logo, logoDark } = data.config.theme;
1314
+ if (logo) {
1315
+ return /* @__PURE__ */ jsxs6(Fragment4, { children: [
1316
+ /* @__PURE__ */ jsx6("img", { className: "documentation-brand-logo documentation-brand-logo-light", src: logo, alt: "" }),
1317
+ /* @__PURE__ */ jsx6("img", { className: "documentation-brand-logo documentation-brand-logo-dark", src: logoDark ?? logo, alt: "" }),
1318
+ /* @__PURE__ */ jsx6("span", { className: "documentation-brand-product", children: "Documentation" })
1319
+ ] });
1320
+ }
1321
+ return /* @__PURE__ */ jsxs6(Fragment4, { children: [
1322
+ /* @__PURE__ */ jsx6("span", { className: "documentation-brand-mark", children: /* @__PURE__ */ jsx6(OrbitMark, {}) }),
1323
+ /* @__PURE__ */ jsx6("span", { className: "documentation-brand-name", children: data.config.name })
1324
+ ] });
1325
+ }
1326
+ function DesktopSidebar({ data }) {
1327
+ return /* @__PURE__ */ jsxs6("aside", { className: "documentation-sidebar", "aria-label": "Documentation navigation", children: [
1328
+ /* @__PURE__ */ jsx6("nav", { className: "documentation-page-tree", children: /* @__PURE__ */ jsx6(NavigationTree, { activeRoute: data.page.route, basePath: data.basePath, nodes: data.navigation }) }),
1329
+ /* @__PURE__ */ jsxs6("div", { className: "documentation-sidebar-footer", children: [
1330
+ !data.config.theme.strictMode && /* @__PURE__ */ jsx6(ThemeControls, { defaultMode: data.config.theme.defaultMode ?? "system" }),
1331
+ data.config.footer.length > 0 && /* @__PURE__ */ jsx6("nav", { "aria-label": "Documentation footer", children: data.config.footer.map((link) => /* @__PURE__ */ jsx6("a", { href: link.href, ...externalProps(link.href), children: link.label }, `${link.label}:${link.href}`)) }),
1332
+ /* @__PURE__ */ jsx6("span", { children: `Built with deepspace ${version}` })
1333
+ ] })
1334
+ ] });
1335
+ }
1336
+ function NavigationTree({
1337
+ activeRoute,
1338
+ basePath,
1339
+ depth = 0,
1340
+ nodes
1341
+ }) {
1342
+ return /* @__PURE__ */ jsx6(Fragment4, { children: nodes.map((node) => {
1343
+ if (node.kind === "group") {
1344
+ const active2 = containsRoute(node, activeRoute);
1345
+ if (depth === 0) {
1346
+ return /* @__PURE__ */ jsxs6("section", { className: "documentation-nav-section", children: [
1347
+ /* @__PURE__ */ jsx6("h2", { children: node.label }),
1348
+ /* @__PURE__ */ jsx6(NavigationTree, { activeRoute, basePath, depth: depth + 1, nodes: node.items })
1349
+ ] }, `group:${node.label}`);
1350
+ }
1351
+ return /* @__PURE__ */ jsxs6("details", { className: "documentation-nav-folder", open: active2, children: [
1352
+ /* @__PURE__ */ jsxs6("summary", { children: [
1353
+ /* @__PURE__ */ jsx6("span", { children: node.label }),
1354
+ /* @__PURE__ */ jsx6(ChevronDownIcon, {})
1355
+ ] }),
1356
+ /* @__PURE__ */ jsx6("div", { children: /* @__PURE__ */ jsx6(NavigationTree, { activeRoute, basePath, depth: depth + 1, nodes: node.items }) })
1357
+ ] }, `group:${node.label}`);
1358
+ }
1359
+ const href = node.kind === "page" ? documentationPublicPath(basePath, node.route) : node.href;
1360
+ const active = node.kind === "page" && node.route === activeRoute;
1361
+ return /* @__PURE__ */ jsxs6(
1362
+ "a",
1363
+ {
1364
+ "aria-current": active ? "page" : void 0,
1365
+ className: active ? "documentation-nav-link is-active" : "documentation-nav-link",
1366
+ href,
1367
+ ...externalProps(href),
1368
+ children: [
1369
+ /* @__PURE__ */ jsx6("span", { children: node.label }),
1370
+ isExternal(href) && /* @__PURE__ */ jsx6(ExternalIcon, {})
1371
+ ]
1372
+ },
1373
+ `${node.kind}:${href}`
1374
+ );
1375
+ }) });
1376
+ }
1377
+ function MobileNavigation({
1378
+ data,
1379
+ onClose,
1380
+ open
1381
+ }) {
1382
+ const dialogRef = useRef5(null);
1383
+ useDialogFocus(open, dialogRef, onClose);
1384
+ if (!open) return null;
1385
+ return /* @__PURE__ */ jsx6("div", { className: "documentation-modal-layer", role: "presentation", onMouseDown: (event) => {
1386
+ if (event.currentTarget === event.target) onClose();
1387
+ }, children: /* @__PURE__ */ jsxs6("div", { className: "documentation-mobile-nav", role: "dialog", "aria-modal": "true", "aria-label": "Documentation navigation", ref: dialogRef, children: [
1388
+ /* @__PURE__ */ jsxs6("header", { children: [
1389
+ /* @__PURE__ */ jsx6(
1390
+ "a",
1391
+ {
1392
+ "aria-label": `${documentationSubject(data.config.name)} home`,
1393
+ className: "documentation-brand",
1394
+ href: data.config.theme.logoHref ?? documentationPublicPath(data.basePath, "/"),
1395
+ ...externalProps(data.config.theme.logoHref ?? documentationPublicPath(data.basePath, "/")),
1396
+ children: /* @__PURE__ */ jsx6(BrandLogo, { data })
1397
+ }
1398
+ ),
1399
+ /* @__PURE__ */ jsx6("button", { className: "documentation-icon-button", type: "button", onClick: onClose, "aria-label": "Close documentation navigation", "data-autofocus": true, children: /* @__PURE__ */ jsx6(CloseIcon, {}) })
1400
+ ] }),
1401
+ /* @__PURE__ */ jsx6("nav", { className: "documentation-page-tree", children: /* @__PURE__ */ jsx6(NavigationTree, { activeRoute: data.page.route, basePath: data.basePath, nodes: data.navigation }) }),
1402
+ /* @__PURE__ */ jsxs6("footer", { children: [
1403
+ !data.config.theme.strictMode && /* @__PURE__ */ jsx6(ThemeControls, { defaultMode: data.config.theme.defaultMode ?? "system" }),
1404
+ data.config.footer.map((link) => /* @__PURE__ */ jsx6("a", { href: link.href, ...externalProps(link.href), children: link.label }, `${link.label}:${link.href}`))
1405
+ ] })
1406
+ ] }) });
1407
+ }
1408
+ function ThemeControls({ defaultMode }) {
1409
+ const [mode, setMode] = useState5(defaultMode);
1410
+ useEffect6(() => {
1411
+ const saved = safeStorageGet("deepspace-documentation-theme");
1412
+ if (saved === "light" || saved === "dark" || saved === "system") setMode(saved);
1413
+ }, []);
1414
+ useEffect6(() => applyThemeMode(mode), [mode]);
1415
+ const choose = (next) => {
1416
+ setMode(next);
1417
+ safeStorageSet("deepspace-documentation-theme", next);
1418
+ };
1419
+ return /* @__PURE__ */ jsxs6("div", { className: "documentation-theme-controls", role: "group", "aria-label": "Theme preference", children: [
1420
+ /* @__PURE__ */ jsx6("button", { "aria-pressed": mode === "system", "aria-label": "Use system theme", onClick: () => choose("system"), type: "button", children: /* @__PURE__ */ jsx6(SystemIcon, {}) }),
1421
+ /* @__PURE__ */ jsx6("button", { "aria-pressed": mode === "light", "aria-label": "Use light theme", onClick: () => choose("light"), type: "button", children: /* @__PURE__ */ jsx6(SunIcon, {}) }),
1422
+ /* @__PURE__ */ jsx6("button", { "aria-pressed": mode === "dark", "aria-label": "Use dark theme", onClick: () => choose("dark"), type: "button", children: /* @__PURE__ */ jsx6(MoonIcon, {}) })
1423
+ ] });
1424
+ }
1425
+ function containsRoute(node, route) {
1426
+ return node.items.some((item) => item.kind === "page" ? item.route === route : item.kind === "group" && containsRoute(item, route));
1427
+ }
1428
+ function applyThemeMode(mode) {
1429
+ const media = window.matchMedia("(prefers-color-scheme: dark)");
1430
+ const apply = () => {
1431
+ document.documentElement.dataset.themeMode = mode;
1432
+ document.documentElement.dataset.theme = mode === "system" ? media.matches ? "dark" : "light" : mode;
1433
+ };
1434
+ apply();
1435
+ media.addEventListener?.("change", apply);
1436
+ return () => media.removeEventListener?.("change", apply);
1437
+ }
1438
+
1439
+ // src/documentation/runtime/search.tsx
1440
+ import {
1441
+ useEffect as useEffect7,
1442
+ useMemo as useMemo2,
1443
+ useRef as useRef6,
1444
+ useState as useState6
1445
+ } from "react";
1446
+ import { Fragment as Fragment5, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1447
+ function SearchCommand({
1448
+ assistantEnabled,
1449
+ basePath,
1450
+ name,
1451
+ onAssistantOpen,
1452
+ onClose,
1453
+ onNavigate,
1454
+ open
1455
+ }) {
1456
+ const dialogRef = useRef6(null);
1457
+ const resultsRef = useRef6(null);
1458
+ const [index, setIndex] = useState6(null);
1459
+ const [loading, setLoading] = useState6(false);
1460
+ const [query, setQuery] = useState6("");
1461
+ const [activeIndex, setActiveIndex] = useState6(0);
1462
+ useDialogFocus(open, dialogRef, onClose);
1463
+ useEffect7(() => {
1464
+ if (!open || index || loading) return;
1465
+ setLoading(true);
1466
+ fetch(documentationPublicPath(basePath, "/search.json")).then((response) => response.ok ? response.json() : []).then(setIndex).catch(() => setIndex([])).finally(() => setLoading(false));
1467
+ }, [basePath, index, loading, open]);
1468
+ useEffect7(() => {
1469
+ if (!open) {
1470
+ setQuery("");
1471
+ setActiveIndex(0);
1472
+ }
1473
+ }, [open]);
1474
+ useEffect7(() => {
1475
+ resultsRef.current?.querySelector(`#documentation-search-option-${activeIndex}`)?.scrollIntoView({ block: "nearest" });
1476
+ }, [activeIndex, query]);
1477
+ const preparedIndex = useMemo2(() => prepareSearchIndex(index ?? []), [index]);
1478
+ const results = useMemo2(() => rankSearch(preparedIndex, query), [preparedIndex, query]);
1479
+ if (!open) return null;
1480
+ const selectableCount = results.length + (assistantEnabled && query.trim() ? 1 : 0);
1481
+ const handleKeyDown = (event) => {
1482
+ if (event.key === "ArrowDown" && selectableCount > 0) {
1483
+ event.preventDefault();
1484
+ setActiveIndex((current) => (current + 1) % selectableCount);
1485
+ } else if (event.key === "ArrowUp" && selectableCount > 0) {
1486
+ event.preventDefault();
1487
+ setActiveIndex((current) => (current - 1 + selectableCount) % selectableCount);
1488
+ } else if (event.key === "Enter") {
1489
+ event.preventDefault();
1490
+ const result = activeIndex < results.length ? results[activeIndex] : void 0;
1491
+ if (result) {
1492
+ onClose();
1493
+ const href = documentationPublicPath(basePath, result.href);
1494
+ if (onNavigate) onNavigate(href);
1495
+ else window.location.assign(href);
1496
+ } else if (assistantEnabled && query.trim()) {
1497
+ onAssistantOpen(query.trim());
1498
+ }
1499
+ }
1500
+ };
1501
+ return /* @__PURE__ */ jsx7("div", { className: "documentation-modal-layer documentation-search-layer", role: "presentation", onMouseDown: (event) => {
1502
+ if (event.currentTarget === event.target) onClose();
1503
+ }, children: /* @__PURE__ */ jsxs7("div", { className: "documentation-search-dialog", role: "dialog", "aria-modal": "true", "aria-label": "Search documentation", ref: dialogRef, children: [
1504
+ /* @__PURE__ */ jsxs7("label", { className: "documentation-search-input", children: [
1505
+ /* @__PURE__ */ jsx7(SearchIcon, {}),
1506
+ /* @__PURE__ */ jsx7(
1507
+ "input",
1508
+ {
1509
+ "aria-activedescendant": selectableCount > 0 ? `documentation-search-option-${activeIndex}` : void 0,
1510
+ "aria-controls": "documentation-search-results",
1511
+ "aria-expanded": "true",
1512
+ "aria-label": `Search ${documentationSubject(name)}`,
1513
+ autoCapitalize: "none",
1514
+ autoComplete: "off",
1515
+ autoCorrect: "off",
1516
+ "data-autofocus": true,
1517
+ enterKeyHint: "go",
1518
+ onChange: (event) => {
1519
+ setQuery(event.target.value);
1520
+ setActiveIndex(0);
1521
+ },
1522
+ onKeyDown: handleKeyDown,
1523
+ placeholder: `Search ${name}`,
1524
+ role: "combobox",
1525
+ spellCheck: false,
1526
+ value: query
1527
+ }
1528
+ ),
1529
+ /* @__PURE__ */ jsx7("kbd", { children: "Esc" })
1530
+ ] }),
1531
+ /* @__PURE__ */ jsxs7("div", { className: "documentation-search-results", id: "documentation-search-results", ref: resultsRef, role: "listbox", "aria-label": "Search results", children: [
1532
+ !query.trim() && /* @__PURE__ */ jsxs7("div", { className: "documentation-search-empty", children: [
1533
+ /* @__PURE__ */ jsx7(OrbitMark, {}),
1534
+ /* @__PURE__ */ jsx7("strong", { children: "Find an answer in these docs" }),
1535
+ /* @__PURE__ */ jsx7("span", { children: "Search pages, headings, APIs, and examples." })
1536
+ ] }),
1537
+ query.trim() && loading && /* @__PURE__ */ jsx7("div", { className: "documentation-search-state", children: "Searching this documentation\u2026" }),
1538
+ query.trim() && !loading && results.length === 0 && /* @__PURE__ */ jsxs7("div", { className: "documentation-search-state", children: [
1539
+ "No documentation matched \u201C",
1540
+ query.trim(),
1541
+ "\u201D."
1542
+ ] }),
1543
+ results.map((result, resultIndex) => /* @__PURE__ */ jsxs7(
1544
+ "a",
1545
+ {
1546
+ "aria-selected": activeIndex === resultIndex,
1547
+ className: activeIndex === resultIndex ? "documentation-search-result is-active" : "documentation-search-result",
1548
+ href: documentationPublicPath(basePath, result.href),
1549
+ id: `documentation-search-option-${resultIndex}`,
1550
+ onClick: onClose,
1551
+ onMouseEnter: () => setActiveIndex(resultIndex),
1552
+ role: "option",
1553
+ children: [
1554
+ /* @__PURE__ */ jsx7("span", { className: "documentation-search-kind", children: result.kind === "section" ? /* @__PURE__ */ jsx7(HashIcon, {}) : /* @__PURE__ */ jsx7(FileIcon, {}) }),
1555
+ /* @__PURE__ */ jsxs7("span", { className: "documentation-search-copy", children: [
1556
+ /* @__PURE__ */ jsx7("strong", { children: highlightMatch(result.title, query) }),
1557
+ result.kind === "section" ? /* @__PURE__ */ jsxs7("span", { className: "documentation-search-crumbs", children: [
1558
+ result.context,
1559
+ /* @__PURE__ */ jsx7(ChevronRightIcon, {}),
1560
+ result.title
1561
+ ] }) : /* @__PURE__ */ jsx7("small", { children: result.context })
1562
+ ] }),
1563
+ /* @__PURE__ */ jsx7(ChevronRightIcon, {})
1564
+ ]
1565
+ },
1566
+ result.href
1567
+ )),
1568
+ assistantEnabled && query.trim() && /* @__PURE__ */ jsxs7(
1569
+ "button",
1570
+ {
1571
+ "aria-selected": activeIndex === results.length,
1572
+ className: activeIndex === results.length ? "documentation-search-assistant is-active" : "documentation-search-assistant",
1573
+ id: `documentation-search-option-${results.length}`,
1574
+ onClick: () => onAssistantOpen(query.trim()),
1575
+ onMouseEnter: () => setActiveIndex(results.length),
1576
+ role: "option",
1577
+ type: "button",
1578
+ children: [
1579
+ /* @__PURE__ */ jsx7(SparkIcon, {}),
1580
+ /* @__PURE__ */ jsxs7("span", { children: [
1581
+ /* @__PURE__ */ jsx7("strong", { children: "Ask DeepSpace" }),
1582
+ /* @__PURE__ */ jsx7("small", { children: "Answer from the current documentation with citations" })
1583
+ ] }),
1584
+ /* @__PURE__ */ jsx7(ChevronRightIcon, {})
1585
+ ]
1586
+ }
1587
+ )
1588
+ ] }),
1589
+ /* @__PURE__ */ jsxs7("footer", { children: [
1590
+ /* @__PURE__ */ jsxs7("span", { children: [
1591
+ /* @__PURE__ */ jsx7("kbd", { children: "\u2191" }),
1592
+ /* @__PURE__ */ jsx7("kbd", { children: "\u2193" }),
1593
+ " select"
1594
+ ] }),
1595
+ /* @__PURE__ */ jsxs7("span", { children: [
1596
+ /* @__PURE__ */ jsx7("kbd", { children: "\u21B5" }),
1597
+ " open"
1598
+ ] }),
1599
+ /* @__PURE__ */ jsxs7("span", { children: [
1600
+ /* @__PURE__ */ jsx7("kbd", { children: "esc" }),
1601
+ " close"
1602
+ ] })
1603
+ ] })
1604
+ ] }) });
1605
+ }
1606
+ function prepareSearchIndex(index) {
1607
+ return index.map((entry) => ({
1608
+ entry,
1609
+ title: normalizeSearch(entry.title),
1610
+ headings: entry.headings.map((heading) => ({ heading, normalized: normalizeSearch(heading.text) })),
1611
+ headingText: normalizeSearch(entry.headings.map((heading) => heading.text).join(" ")),
1612
+ haystack: normalizeSearch(`${entry.description ?? ""} ${entry.text}`)
1613
+ }));
1614
+ }
1615
+ function rankSearch(index, query) {
1616
+ const normalized = normalizeSearch(query);
1617
+ if (!normalized) return [];
1618
+ const tokens = normalized.split(" ").filter(Boolean);
1619
+ const scored = [];
1620
+ for (const { entry, title, headings, headingText, haystack } of index) {
1621
+ let score = title === normalized ? 180 : title.startsWith(normalized) ? 110 : title.includes(normalized) ? 75 : 0;
1622
+ if (headingText.includes(normalized)) score += 55;
1623
+ for (const token of tokens) {
1624
+ if (title.includes(token)) score += 24;
1625
+ else if (headingText.includes(token)) score += 14;
1626
+ else if (haystack.includes(token)) score += 5;
1627
+ else score -= 18;
1628
+ }
1629
+ if (score > 0) {
1630
+ scored.push({
1631
+ route: entry.route,
1632
+ score,
1633
+ result: {
1634
+ href: entry.route,
1635
+ kind: "page",
1636
+ title: entry.title,
1637
+ context: entry.description ?? entry.text.slice(0, 150)
1638
+ }
1639
+ });
1640
+ }
1641
+ for (const { heading, normalized: headingNormalized } of headings) {
1642
+ let sectionScore = headingNormalized === normalized ? 120 : headingNormalized.startsWith(normalized) ? 80 : headingNormalized.includes(normalized) ? 60 : 0;
1643
+ if (sectionScore === 0) {
1644
+ for (const token of tokens) if (headingNormalized.includes(token)) sectionScore += 18;
1645
+ }
1646
+ if (sectionScore <= 0) continue;
1647
+ scored.push({
1648
+ route: `${entry.route}#${heading.id}`,
1649
+ score: sectionScore,
1650
+ result: {
1651
+ href: `${entry.route}#${heading.id}`,
1652
+ kind: "section",
1653
+ title: heading.text,
1654
+ context: entry.title
1655
+ }
1656
+ });
1657
+ }
1658
+ }
1659
+ return scored.sort((left, right) => right.score - left.score || left.route.localeCompare(right.route)).slice(0, 10).map((item) => item.result);
1660
+ }
1661
+ function highlightMatch(value, query) {
1662
+ const normalizedQuery = query.trim();
1663
+ if (!normalizedQuery) return value;
1664
+ const index = value.toLowerCase().indexOf(normalizedQuery.toLowerCase());
1665
+ if (index < 0) return value;
1666
+ return /* @__PURE__ */ jsxs7(Fragment5, { children: [
1667
+ value.slice(0, index),
1668
+ /* @__PURE__ */ jsx7("mark", { children: value.slice(index, index + normalizedQuery.length) }),
1669
+ value.slice(index + normalizedQuery.length)
1670
+ ] });
1671
+ }
1672
+ function normalizeSearch(value) {
1673
+ return value.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
1674
+ }
1675
+
1676
+ // src/documentation/runtime/app.tsx
1677
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1678
+ function DocumentationApp({
1679
+ data,
1680
+ children,
1681
+ navigating = false,
1682
+ onNavigate
1683
+ }) {
1684
+ const [mobileNavOpen, setMobileNavOpen] = useState7(false);
1685
+ const [searchOpen, setSearchOpen] = useState7(false);
1686
+ const [assistantOpen, setAssistantOpen] = useState7(false);
1687
+ const [assistantDraft, setAssistantDraft] = useState7("");
1688
+ const [assistantSeed, setAssistantSeed] = useState7({ id: 0, submit: false });
1689
+ const assistantAccess = data.config.assistant.access === "disabled" ? null : data.config.assistant.access;
1690
+ const assistantEnabled = assistantAccess !== null;
1691
+ const appClassName = [
1692
+ "documentation-app",
1693
+ assistantEnabled ? "has-assistant" : "",
1694
+ assistantOpen ? "is-assistant-open" : "",
1695
+ navigating ? "is-navigating" : ""
1696
+ ].filter(Boolean).join(" ");
1697
+ const openAssistant = useCallback((seed, submit = false) => {
1698
+ if (seed !== void 0) setAssistantDraft(seed);
1699
+ setAssistantSeed((current) => ({ id: current.id + 1, submit, value: seed }));
1700
+ setSearchOpen(false);
1701
+ setAssistantOpen(true);
1702
+ }, []);
1703
+ useEffect8(() => {
1704
+ const handleKeydown = (event) => {
1705
+ if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "k") {
1706
+ event.preventDefault();
1707
+ setSearchOpen(true);
1708
+ }
1709
+ if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "i") {
1710
+ event.preventDefault();
1711
+ if (data.config.assistant.access !== "disabled") openAssistant();
1712
+ }
1713
+ };
1714
+ document.addEventListener("keydown", handleKeydown);
1715
+ return () => document.removeEventListener("keydown", handleKeydown);
1716
+ }, [data.config.assistant.access, openAssistant]);
1717
+ useEffect8(() => {
1718
+ setMobileNavOpen(false);
1719
+ setSearchOpen(false);
1720
+ }, [data.page.route]);
1721
+ return /* @__PURE__ */ jsxs8(
1722
+ "div",
1723
+ {
1724
+ "aria-busy": navigating || void 0,
1725
+ className: appClassName,
1726
+ children: [
1727
+ /* @__PURE__ */ jsx8("a", { className: "documentation-skip-link", href: "#documentation-content", children: "Skip to main content" }),
1728
+ /* @__PURE__ */ jsx8(
1729
+ DocumentationHeader,
1730
+ {
1731
+ data,
1732
+ onMenuOpen: () => setMobileNavOpen(true),
1733
+ onSearchOpen: () => setSearchOpen(true)
1734
+ }
1735
+ ),
1736
+ /* @__PURE__ */ jsxs8("div", { className: "documentation-frame", children: [
1737
+ /* @__PURE__ */ jsx8(DesktopSidebar, { data }),
1738
+ /* @__PURE__ */ jsx8("main", { className: "documentation-main", id: "documentation-content", children: /* @__PURE__ */ jsxs8("div", { className: "documentation-reader-grid", children: [
1739
+ /* @__PURE__ */ jsx8("div", { className: "documentation-reader-column", children: /* @__PURE__ */ jsx8(
1740
+ Article,
1741
+ {
1742
+ data,
1743
+ launcher: assistantAccess && !assistantOpen ? /* @__PURE__ */ jsx8(
1744
+ AssistantLauncher,
1745
+ {
1746
+ input: assistantDraft,
1747
+ name: data.config.name,
1748
+ onInputChange: setAssistantDraft,
1749
+ onSubmit: (question) => openAssistant(question, true)
1750
+ }
1751
+ ) : void 0,
1752
+ onAssistantOpen: openAssistant,
1753
+ children
1754
+ }
1755
+ ) }),
1756
+ /* @__PURE__ */ jsx8(ContextRail, { data })
1757
+ ] }) })
1758
+ ] }),
1759
+ /* @__PURE__ */ jsx8(
1760
+ MobileNavigation,
1761
+ {
1762
+ data,
1763
+ onClose: () => setMobileNavOpen(false),
1764
+ open: mobileNavOpen
1765
+ }
1766
+ ),
1767
+ /* @__PURE__ */ jsx8(
1768
+ SearchCommand,
1769
+ {
1770
+ assistantEnabled,
1771
+ basePath: data.basePath,
1772
+ name: data.config.name,
1773
+ onAssistantOpen: openAssistant,
1774
+ onClose: () => setSearchOpen(false),
1775
+ onNavigate,
1776
+ open: searchOpen
1777
+ }
1778
+ ),
1779
+ assistantAccess && /* @__PURE__ */ jsx8(
1780
+ DocumentationAssistant,
1781
+ {
1782
+ access: assistantAccess,
1783
+ basePath: data.basePath,
1784
+ input: assistantDraft,
1785
+ name: data.config.name,
1786
+ onClose: () => setAssistantOpen(false),
1787
+ onInputChange: setAssistantDraft,
1788
+ open: assistantOpen,
1789
+ route: data.page.route,
1790
+ seed: assistantSeed,
1791
+ suggestions: data.config.assistant.suggestions
1792
+ }
1793
+ )
1794
+ ]
1795
+ }
1796
+ );
1797
+ }
1798
+
1799
+ // src/documentation/runtime/mdx-components.tsx
1800
+ import { Children, createElement, isValidElement } from "react";
1801
+ import { Fragment as Fragment6, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
1802
+ function Callout({ children, title, kind }) {
1803
+ const label = title ?? kind.charAt(0).toUpperCase() + kind.slice(1);
1804
+ return /* @__PURE__ */ jsxs9("aside", { className: `documentation-callout documentation-callout-${kind}`, children: [
1805
+ /* @__PURE__ */ jsx9("strong", { children: label }),
1806
+ children
1807
+ ] });
1808
+ }
1809
+ function Note(props) {
1810
+ return /* @__PURE__ */ jsx9(Callout, { ...props, kind: "note" });
1811
+ }
1812
+ function Tip(props) {
1813
+ return /* @__PURE__ */ jsx9(Callout, { ...props, kind: "tip" });
1814
+ }
1815
+ function Warning(props) {
1816
+ return /* @__PURE__ */ jsx9(Callout, { ...props, kind: "warning" });
1817
+ }
1818
+ function Info(props) {
1819
+ return /* @__PURE__ */ jsx9(Callout, { ...props, kind: "info" });
1820
+ }
1821
+ function Card({ children, href, label, title }) {
1822
+ const content = /* @__PURE__ */ jsxs9(Fragment6, { children: [
1823
+ title ?? label ? /* @__PURE__ */ jsx9("strong", { children: title ?? label }) : null,
1824
+ children
1825
+ ] });
1826
+ return href ? /* @__PURE__ */ jsxs9("a", { className: "documentation-card", href, children: [
1827
+ content,
1828
+ /* @__PURE__ */ jsx9("span", { "aria-hidden": "true", children: "\u2192" })
1829
+ ] }) : /* @__PURE__ */ jsx9("div", { className: "documentation-card", children: content });
1830
+ }
1831
+ function Accordion({ children, label, title }) {
1832
+ return /* @__PURE__ */ jsxs9("details", { className: "documentation-accordion", children: [
1833
+ /* @__PURE__ */ jsx9("summary", { children: title ?? label ?? "Details" }),
1834
+ children
1835
+ ] });
1836
+ }
1837
+ function AccordionGroup({ children }) {
1838
+ return /* @__PURE__ */ jsx9("div", { className: "documentation-accordion-group", children });
1839
+ }
1840
+ function Step({ children, label, title }) {
1841
+ return /* @__PURE__ */ jsxs9("section", { className: "documentation-step", children: [
1842
+ title ?? label ? /* @__PURE__ */ jsx9("h3", { children: title ?? label }) : null,
1843
+ children
1844
+ ] });
1845
+ }
1846
+ function Steps({ children }) {
1847
+ return /* @__PURE__ */ jsx9("div", { className: "documentation-steps", children });
1848
+ }
1849
+ function Tab({ children, label, title }) {
1850
+ return /* @__PURE__ */ jsx9("section", { className: "documentation-tab", "data-tab": true, "data-tab-title": title ?? label ?? "Tab", children });
1851
+ }
1852
+ function Tabs({ children }) {
1853
+ return /* @__PURE__ */ jsx9(TabGroup, { className: "documentation-tabs", panels: tabPanels(children) });
1854
+ }
1855
+ function CodeGroup({ children, label, title }) {
1856
+ const panels = Children.toArray(children).map((child, index) => ({
1857
+ label: codeBlockTitle(child) ?? `Example ${index + 1}`,
1858
+ content: child
1859
+ }));
1860
+ return /* @__PURE__ */ jsx9(CodeGroupProvider, { value: true, children: /* @__PURE__ */ jsx9(
1861
+ TabGroup,
1862
+ {
1863
+ className: "documentation-code-group",
1864
+ panels,
1865
+ title: title ?? label ? /* @__PURE__ */ jsx9("div", { className: "documentation-code-group-title", children: title ?? label }) : void 0
1866
+ }
1867
+ ) });
1868
+ }
1869
+ function tabPanels(children) {
1870
+ return Children.toArray(children).map((child, index) => {
1871
+ const props = isValidElement(child) ? child.props : void 0;
1872
+ return {
1873
+ label: props?.title ?? props?.label ?? `Tab ${index + 1}`,
1874
+ content: props && "children" in props ? props.children : child
1875
+ };
1876
+ });
1877
+ }
1878
+ function codeBlockTitle(node) {
1879
+ if (!isValidElement(node)) return void 0;
1880
+ const code = node.props.children;
1881
+ if (!isValidElement(code)) return void 0;
1882
+ const title = code.props["data-code-title"];
1883
+ return typeof title === "string" && title ? title : void 0;
1884
+ }
1885
+ function Heading({ level, ...props }) {
1886
+ const id = props.id;
1887
+ return createElement(
1888
+ `h${level}`,
1889
+ props,
1890
+ props.children,
1891
+ id ? /* @__PURE__ */ jsx9("a", { "aria-label": "Link to this section", className: "documentation-heading-anchor", href: `#${id}`, children: "#" }) : null
1892
+ );
1893
+ }
1894
+ var documentationMdxComponents = {
1895
+ /** React renders the code-block wrapper so nothing has to move a `pre` that
1896
+ * the reconciler already owns. */
1897
+ pre: CodeBlock,
1898
+ Accordion,
1899
+ AccordionGroup,
1900
+ Card,
1901
+ CodeGroup,
1902
+ Info,
1903
+ Note,
1904
+ Step,
1905
+ Steps,
1906
+ Tab,
1907
+ Tabs,
1908
+ Tip,
1909
+ Warning,
1910
+ h1: (props) => /* @__PURE__ */ jsx9(Heading, { ...props, level: 1 }),
1911
+ h2: (props) => /* @__PURE__ */ jsx9(Heading, { ...props, level: 2 }),
1912
+ h3: (props) => /* @__PURE__ */ jsx9(Heading, { ...props, level: 3 }),
1913
+ h4: (props) => /* @__PURE__ */ jsx9(Heading, { ...props, level: 4 }),
1914
+ h5: (props) => /* @__PURE__ */ jsx9(Heading, { ...props, level: 5 }),
1915
+ h6: (props) => /* @__PURE__ */ jsx9(Heading, { ...props, level: 6 })
1916
+ };
1917
+
1918
+ // src/documentation/runtime/public.tsx
1919
+ import { jsx as jsx10 } from "react/jsx-runtime";
1920
+ var DefaultDocumentation = DocumentationApp;
1921
+ function DocumentationContent({
1922
+ data,
1923
+ Page
1924
+ }) {
1925
+ if (!Page) {
1926
+ return /* @__PURE__ */ jsx10(
1927
+ "div",
1928
+ {
1929
+ className: "documentation-prose",
1930
+ "data-prose": "html",
1931
+ dangerouslySetInnerHTML: { __html: data.page.html }
1932
+ }
1933
+ );
1934
+ }
1935
+ return /* @__PURE__ */ jsx10("div", { className: "documentation-prose", "data-prose": "react", children: /* @__PURE__ */ jsx10(Page, { components: documentationMdxComponents }) });
1936
+ }
1937
+
1938
+ export {
1939
+ documentationPublicPath,
1940
+ documentationLogicalPath,
1941
+ DocumentationApp,
1942
+ Note,
1943
+ Tip,
1944
+ Warning,
1945
+ Info,
1946
+ Card,
1947
+ Accordion,
1948
+ AccordionGroup,
1949
+ Step,
1950
+ Steps,
1951
+ Tab,
1952
+ Tabs,
1953
+ CodeGroup,
1954
+ DefaultDocumentation,
1955
+ DocumentationContent
1956
+ };
1957
+ //# sourceMappingURL=chunk-ESYXTIV2.js.map