@unterberg/nivel 0.1.11 → 0.1.14

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 (39) hide show
  1. package/README.md +5 -13
  2. package/dist/SearchModal-YJZFUB53.js +335 -0
  3. package/dist/SearchModal-YJZFUB53.js.map +1 -0
  4. package/dist/chunk-ESAHWFDZ.js +196 -0
  5. package/dist/chunk-ESAHWFDZ.js.map +1 -0
  6. package/dist/{chunk-GT62XN7K.js → chunk-NGX2C26M.js} +12 -22
  7. package/dist/chunk-NGX2C26M.js.map +1 -0
  8. package/dist/{chunk-HQHLPFGA.js → chunk-PKXDOKJY.js} +5 -3
  9. package/dist/{chunk-HQHLPFGA.js.map → chunk-PKXDOKJY.js.map} +1 -1
  10. package/dist/chunk-PYYPYIBD.js +49 -0
  11. package/dist/chunk-PYYPYIBD.js.map +1 -0
  12. package/dist/{chunk-NDJ5LYLK.js → chunk-QWIYQPCW.js} +6 -45
  13. package/dist/chunk-QWIYQPCW.js.map +1 -0
  14. package/dist/{chunk-6Q5XESPG.js → chunk-R6O4NLHC.js} +3 -3
  15. package/dist/{chunk-6TXPHBIC.js → chunk-SH5XWPXW.js} +353 -757
  16. package/dist/chunk-SH5XWPXW.js.map +1 -0
  17. package/dist/cli.js +3 -2
  18. package/dist/cli.js.map +1 -1
  19. package/dist/client.d.ts +1 -1
  20. package/dist/client.js +8 -5
  21. package/dist/config.d.ts +1 -1
  22. package/dist/index.d.ts +1 -1
  23. package/dist/index.js +3 -2
  24. package/dist/mdx/code-blocks.d.ts +1 -1
  25. package/dist/mdx.js +3 -2
  26. package/dist/mdx.js.map +1 -1
  27. package/dist/runtime/client.d.ts +1 -1
  28. package/dist/runtime/client.js +8 -5
  29. package/dist/runtime/node.d.ts +1 -1
  30. package/dist/runtime/node.js +4 -3
  31. package/dist/{types-BGAec0JI.d.ts → types-IiJ1jLWc.d.ts} +1 -0
  32. package/dist/vike.d.ts +1 -1
  33. package/dist/vike.js +249 -4
  34. package/dist/vike.js.map +1 -1
  35. package/package.json +6 -7
  36. package/dist/chunk-6TXPHBIC.js.map +0 -1
  37. package/dist/chunk-GT62XN7K.js.map +0 -1
  38. package/dist/chunk-NDJ5LYLK.js.map +0 -1
  39. /package/dist/{chunk-6Q5XESPG.js.map → chunk-R6O4NLHC.js.map} +0 -0
package/README.md CHANGED
@@ -22,6 +22,8 @@ pnpm add -D vite typescript @types/react @types/react-dom
22
22
 
23
23
  `vike` and `vite` are peer dependencies. The package exposes a local `nivel` binary after install.
24
24
 
25
+ Set `siteUrl` in `pages/+docs.ts` to enable automatic `sitemap.xml` and `robots.txt` generation. V1 includes canonical docs routes from the docs graph plus normal filesystem-routed consumer pages. Consumer routes remapped only through custom `+route.ts` files are not included automatically.
26
+
25
27
  ## Quick Start
26
28
 
27
29
  Scaffold a consumer:
@@ -108,6 +110,7 @@ const docsConfig = defineDocsConfig({
108
110
  graph: docsGraph,
109
111
  siteTitle: 'My Docs',
110
112
  siteDescription: 'Documentation site powered by @unterberg/nivel.',
113
+ siteUrl: 'https://docs.example.com',
111
114
  basePath: '/docs',
112
115
  })
113
116
 
@@ -117,25 +120,14 @@ export default docsConfig
117
120
  `pages/+config.ts`
118
121
 
119
122
  ```ts
120
- import nivel from '@unterberg/nivel/vike'
123
+ import { createNivelVikeConfig } from '@unterberg/nivel/vike'
121
124
  import type { Config } from 'vike/types'
122
- import vikeReact from 'vike-react/config'
123
125
  import docsConfig from './+docs'
124
126
 
125
127
  export { config }
126
128
 
127
- const themePreference = docsConfig.theme?.defaultPreference ?? 'light'
128
- const dataTheme =
129
- themePreference === 'dark'
130
- ? (docsConfig.theme?.dark ?? 'consumer-dark')
131
- : (docsConfig.theme?.light ?? 'consumer-light')
132
-
133
129
  const config: Config = {
134
- ...nivel,
135
- extends: [vikeReact],
136
- title: docsConfig.siteTitle,
137
- description: docsConfig.siteDescription ?? `${docsConfig.siteTitle} documentation`,
138
- htmlAttributes: { 'data-theme': dataTheme },
130
+ ...createNivelVikeConfig(docsConfig),
139
131
  prerender: true,
140
132
  }
141
133
  ```
@@ -0,0 +1,335 @@
1
+ import {
2
+ LayoutComponent,
3
+ useDocsGlobalContext,
4
+ useDocsSearchActions,
5
+ useDocsSearchStore
6
+ } from "./chunk-ESAHWFDZ.js";
7
+ import {
8
+ withSiteBaseUrl
9
+ } from "./chunk-PYYPYIBD.js";
10
+
11
+ // src/runtime/client/components/SearchModal.tsx
12
+ import { ArrowRightFromLine, MessageCircleQuestion, TriangleAlert } from "lucide-react";
13
+ import { useEffect, useRef, useState } from "react";
14
+ import { createPortal } from "react-dom";
15
+ import { usePageContext } from "vike-react/usePageContext";
16
+
17
+ // src/runtime/client/search.ts
18
+ var hierarchyLevels = ["lvl0", "lvl1", "lvl2", "lvl3", "lvl4", "lvl5", "lvl6"];
19
+ var stripHtml = (value) => value.replace(/<[^>]+>/g, "");
20
+ var normalizeString = (value) => {
21
+ if (typeof value === "string") {
22
+ const normalized = stripHtml(value).replace(/\s+/g, " ").trim();
23
+ return normalized || void 0;
24
+ }
25
+ if (typeof value === "number" || typeof value === "boolean") {
26
+ return String(value);
27
+ }
28
+ return void 0;
29
+ };
30
+ var getValueAtPath = (value, path) => {
31
+ if (!path) {
32
+ return void 0;
33
+ }
34
+ const segments = path.split(".").filter(Boolean);
35
+ let currentValue = value;
36
+ for (const segment of segments) {
37
+ if (!currentValue || typeof currentValue !== "object" || Array.isArray(currentValue)) {
38
+ return void 0;
39
+ }
40
+ currentValue = currentValue[segment];
41
+ }
42
+ return currentValue;
43
+ };
44
+ var getMappedString = (value, path) => {
45
+ return normalizeString(getValueAtPath(value, path));
46
+ };
47
+ var buildSearchUrl = (appId, indexName) => {
48
+ return `https://${appId}-dsn.algolia.net/1/indexes/${encodeURIComponent(indexName)}/query`;
49
+ };
50
+ var getDocSearchHierarchyValue = (hierarchy, level) => {
51
+ if (!hierarchy || typeof hierarchy !== "object") {
52
+ return void 0;
53
+ }
54
+ return normalizeString(hierarchy[level]);
55
+ };
56
+ var getDocSearchTitleLevel = (hit) => {
57
+ const levelFromType = typeof hit.type === "string" && /^lvl[0-6]$/.test(hit.type) ? hit.type : null;
58
+ if (levelFromType && getDocSearchHierarchyValue(hit.hierarchy, levelFromType)) {
59
+ return levelFromType;
60
+ }
61
+ return [...hierarchyLevels].reverse().find((level) => level !== "lvl0" && getDocSearchHierarchyValue(hit.hierarchy, level));
62
+ };
63
+ var getDocSearchFallbackResult = (hit) => {
64
+ const docSearchHit = hit;
65
+ const titleLevel = getDocSearchTitleLevel(docSearchHit);
66
+ const title = titleLevel ? getDocSearchHierarchyValue(docSearchHit.hierarchy, titleLevel) : void 0;
67
+ const titleLevelIndex = titleLevel ? hierarchyLevels.indexOf(titleLevel) : -1;
68
+ const sectionTitle = hierarchyLevels.slice(0, Math.max(titleLevelIndex, 0)).reverse().map((level) => getDocSearchHierarchyValue(docSearchHit.hierarchy, level)).find(Boolean) ?? normalizeString(docSearchHit.category);
69
+ return {
70
+ href: normalizeString(docSearchHit.url) ?? normalizeString(docSearchHit.url_without_anchor),
71
+ title: title ?? normalizeString(docSearchHit.category),
72
+ excerpt: normalizeString(docSearchHit._snippetResult?.content?.value) ?? normalizeString(docSearchHit.content) ?? normalizeString(docSearchHit._highlightResult?.content?.value),
73
+ sectionTitle
74
+ };
75
+ };
76
+ var mapHitToSearchResult = (hit, config) => {
77
+ const docSearchFallback = getDocSearchFallbackResult(hit);
78
+ const href = getMappedString(hit, config.fields.href) ?? docSearchFallback.href;
79
+ const title = getMappedString(hit, config.fields.title) ?? docSearchFallback.title;
80
+ if (!href || !title) {
81
+ return null;
82
+ }
83
+ const excerpt = getMappedString(hit, config.fields.excerpt) ?? docSearchFallback.excerpt;
84
+ const sectionTitle = getMappedString(hit, config.fields.sectionTitle) ?? docSearchFallback.sectionTitle;
85
+ return {
86
+ href,
87
+ title,
88
+ excerpt,
89
+ sectionTitle
90
+ };
91
+ };
92
+ var searchAlgoliaIndex = async (options) => {
93
+ const { config, query, signal } = options;
94
+ if (!config) {
95
+ throw new Error("Algolia search is not configured.");
96
+ }
97
+ const response = await fetch(buildSearchUrl(config.appId, config.indexName), {
98
+ method: "POST",
99
+ signal,
100
+ headers: {
101
+ accept: "application/json",
102
+ "content-type": "application/json",
103
+ "x-algolia-api-key": config.apiKey,
104
+ "x-algolia-application-id": config.appId
105
+ },
106
+ body: JSON.stringify({
107
+ query,
108
+ ...config.searchParams
109
+ })
110
+ });
111
+ if (!response.ok) {
112
+ throw new Error(`Algolia search request failed with status ${response.status}.`);
113
+ }
114
+ const data = await response.json();
115
+ return (data.hits ?? []).map((hit) => mapHitToSearchResult(hit, config)).filter((result) => result !== null);
116
+ };
117
+
118
+ // src/runtime/client/components/SearchModal.tsx
119
+ import { jsx, jsxs } from "react/jsx-runtime";
120
+ var MIN_QUERY_LENGTH = 2;
121
+ var QUERY_DEBOUNCE_MS = 150;
122
+ var useDebouncedValue = (value, delayMs) => {
123
+ const [debouncedValue, setDebouncedValue] = useState(value);
124
+ useEffect(() => {
125
+ const timeoutId = window.setTimeout(() => {
126
+ setDebouncedValue(value);
127
+ }, delayMs);
128
+ return () => {
129
+ window.clearTimeout(timeoutId);
130
+ };
131
+ }, [delayMs, value]);
132
+ return debouncedValue;
133
+ };
134
+ var useSearchResults = (options) => {
135
+ const docs = useDocsGlobalContext();
136
+ const { canSearch, isOpen, normalizedQuery } = options;
137
+ const [results, setResults] = useState([]);
138
+ const [isLoading, setIsLoading] = useState(false);
139
+ const [isError, setIsError] = useState(false);
140
+ useEffect(() => {
141
+ if (!isOpen || !canSearch) {
142
+ setResults([]);
143
+ setIsLoading(false);
144
+ setIsError(false);
145
+ return;
146
+ }
147
+ const abortController = new AbortController();
148
+ setIsLoading(true);
149
+ setIsError(false);
150
+ searchAlgoliaIndex({
151
+ config: docs.algolia,
152
+ query: normalizedQuery,
153
+ signal: abortController.signal
154
+ }).then((nextResults) => {
155
+ if (abortController.signal.aborted) {
156
+ return;
157
+ }
158
+ setResults(nextResults);
159
+ }).catch((error) => {
160
+ if (abortController.signal.aborted) {
161
+ return;
162
+ }
163
+ const isAbortError = error instanceof DOMException && error.name === "AbortError";
164
+ if (!isAbortError) {
165
+ setResults([]);
166
+ setIsError(true);
167
+ }
168
+ }).finally(() => {
169
+ if (!abortController.signal.aborted) {
170
+ setIsLoading(false);
171
+ }
172
+ });
173
+ return () => {
174
+ abortController.abort();
175
+ };
176
+ }, [canSearch, docs.algolia, isOpen, normalizedQuery]);
177
+ return {
178
+ results,
179
+ isLoading,
180
+ isError
181
+ };
182
+ };
183
+ var SearchModal = () => {
184
+ const docs = useDocsGlobalContext();
185
+ const { urlPathname } = usePageContext();
186
+ const { close, setQuery } = useDocsSearchActions();
187
+ const isOpen = useDocsSearchStore((state) => state.isOpen);
188
+ const query = useDocsSearchStore((state) => state.query);
189
+ const containerRef = useRef(null);
190
+ const suggestionBoxRef = useRef(null);
191
+ const previousPathnameRef = useRef(urlPathname);
192
+ const debouncedQuery = useDebouncedValue(query, QUERY_DEBOUNCE_MS);
193
+ const normalizedQuery = debouncedQuery.trim();
194
+ const canSearch = Boolean(docs.algolia) && normalizedQuery.length >= MIN_QUERY_LENGTH;
195
+ const { isError, isLoading, results } = useSearchResults({
196
+ canSearch,
197
+ isOpen,
198
+ normalizedQuery
199
+ });
200
+ useEffect(() => {
201
+ if (previousPathnameRef.current !== urlPathname) {
202
+ close();
203
+ previousPathnameRef.current = urlPathname;
204
+ }
205
+ }, [close, urlPathname]);
206
+ useEffect(() => {
207
+ if (!isOpen) {
208
+ return;
209
+ }
210
+ const handlePointerDown = (event) => {
211
+ const target = event.target;
212
+ if (!containerRef.current?.contains(target) && !suggestionBoxRef.current?.contains(target)) {
213
+ close();
214
+ }
215
+ };
216
+ const handleKeyDown = (event) => {
217
+ if (event.key === "Escape") {
218
+ close();
219
+ }
220
+ };
221
+ document.addEventListener("pointerdown", handlePointerDown);
222
+ document.addEventListener("keydown", handleKeyDown);
223
+ return () => {
224
+ document.removeEventListener("pointerdown", handlePointerDown);
225
+ document.removeEventListener("keydown", handleKeyDown);
226
+ };
227
+ }, [close, isOpen]);
228
+ if (!docs.algolia) {
229
+ return null;
230
+ }
231
+ return /* @__PURE__ */ jsx("div", { ref: containerRef, className: "relative", children: /* @__PURE__ */ jsx(
232
+ SearchSuggestionBox,
233
+ {
234
+ contentRef: suggestionBoxRef,
235
+ isError,
236
+ isLoading,
237
+ isOpen,
238
+ onClose: close,
239
+ onQueryChange: setQuery,
240
+ query,
241
+ results
242
+ }
243
+ ) });
244
+ };
245
+ var SearchSuggestionBox = ({
246
+ contentRef,
247
+ isError,
248
+ isLoading,
249
+ isOpen,
250
+ onClose,
251
+ onQueryChange,
252
+ query,
253
+ results
254
+ }) => {
255
+ const inputRef = useRef(null);
256
+ const normalizedQuery = query.trim();
257
+ const canSearch = normalizedQuery.length >= MIN_QUERY_LENGTH;
258
+ useEffect(() => {
259
+ if (!isOpen) {
260
+ return;
261
+ }
262
+ const frameId = window.requestAnimationFrame(() => {
263
+ inputRef.current?.focus();
264
+ inputRef.current?.setSelectionRange(query.length, query.length);
265
+ });
266
+ return () => {
267
+ window.cancelAnimationFrame(frameId);
268
+ };
269
+ }, [isOpen, query.length]);
270
+ if (!isOpen || typeof document === "undefined") {
271
+ return null;
272
+ }
273
+ return createPortal(
274
+ /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-30 h-full w-full bg-base-100/50 backdrop-blur-lg", children: [
275
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 z-1 bg-linear-to-b from-base-100 via-base-100 via-25% to-primary-muted-superlight dark:bg-linear-to-t" }),
276
+ /* @__PURE__ */ jsxs(
277
+ LayoutComponent,
278
+ {
279
+ ref: contentRef,
280
+ $size: "sm",
281
+ className: "mt-5 relative z-2 rounded-box bg-base-100/70 p-6 pt-6 shadow-lg shadow-primary-muted-light",
282
+ children: [
283
+ /* @__PURE__ */ jsx(
284
+ "input",
285
+ {
286
+ placeholder: "Search docs",
287
+ ref: inputRef,
288
+ type: "text",
289
+ className: "input input-primary input-xl w-full",
290
+ value: query,
291
+ onChange: (event) => onQueryChange(event.target.value)
292
+ }
293
+ ),
294
+ /* @__PURE__ */ jsx("div", { className: "flex h-7 items-center px-4 text-xs text-base-muted", children: isLoading ? /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1", children: [
295
+ /* @__PURE__ */ jsx("span", { className: "loading loading-dots loading-xs" }),
296
+ "Searching..."
297
+ ] }) : normalizedQuery ? null : /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1", children: [
298
+ /* @__PURE__ */ jsx(MessageCircleQuestion, { className: "h-3 w-3 shrink-0" }),
299
+ "Type at least ",
300
+ MIN_QUERY_LENGTH,
301
+ " characters."
302
+ ] }) }),
303
+ normalizedQuery ? isError ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-box border border-warning/40 bg-base-100 p-4 text-sm text-base-muted shadow-md shadow-primary-muted-light", children: [
304
+ /* @__PURE__ */ jsx(TriangleAlert, { className: "h-4 w-4 shrink-0 text-warning" }),
305
+ "Search is temporarily unavailable."
306
+ ] }) : !canSearch ? /* @__PURE__ */ jsx("div", { className: "text-sm text-base-muted", children: "Keep typing to search." }) : !isLoading && results.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-sm text-base-muted", children: "No results found." }) : /* @__PURE__ */ jsx("div", { className: "-mx-2 max-h-80 overflow-y-auto p-2", children: /* @__PURE__ */ jsx("ul", { className: "flex flex-col gap-2", children: results.map((result) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
307
+ "a",
308
+ {
309
+ href: withSiteBaseUrl(result.href),
310
+ className: "block rounded-box border border-base-muted-medium bg-base-100 p-4 shadow-md hover:border-primary-muted hover:bg-base-200",
311
+ onClick: onClose,
312
+ children: [
313
+ /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-start gap-2", children: [
314
+ /* @__PURE__ */ jsx("div", { className: "font-bold text-base-content", children: result.title }),
315
+ result.sectionTitle ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 text-sm text-base-muted-medium", children: [
316
+ /* @__PURE__ */ jsx(ArrowRightFromLine, { className: "h-3 w-3" }),
317
+ " ",
318
+ result.sectionTitle
319
+ ] }) : null
320
+ ] }),
321
+ result.excerpt ? /* @__PURE__ */ jsx("p", { className: "text-xs leading-5 text-base-muted", children: result.excerpt }) : null
322
+ ]
323
+ }
324
+ ) }, result.href)) }) }) : null
325
+ ]
326
+ }
327
+ )
328
+ ] }),
329
+ document.body
330
+ );
331
+ };
332
+ export {
333
+ SearchModal
334
+ };
335
+ //# sourceMappingURL=SearchModal-YJZFUB53.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runtime/client/components/SearchModal.tsx","../src/runtime/client/search.ts"],"sourcesContent":["import { ArrowRightFromLine, MessageCircleQuestion, TriangleAlert } from 'lucide-react'\nimport type { RefObject } from 'react'\nimport { useEffect, useRef, useState } from 'react'\nimport { createPortal } from 'react-dom'\nimport { usePageContext } from 'vike-react/usePageContext'\nimport { withSiteBaseUrl } from '../../../shared/assets.js'\nimport { useDocsGlobalContext } from '../docsGlobalContext.js'\nimport { searchAlgoliaIndex } from '../search.js'\nimport { useDocsSearchActions, useDocsSearchStore } from '../store/runtime-store.js'\nimport { LayoutComponent } from './LayoutComponent.js'\n\nconst MIN_QUERY_LENGTH = 2\nconst QUERY_DEBOUNCE_MS = 150\n\nconst useDebouncedValue = (value: string, delayMs: number) => {\n const [debouncedValue, setDebouncedValue] = useState(value)\n\n useEffect(() => {\n const timeoutId = window.setTimeout(() => {\n setDebouncedValue(value)\n }, delayMs)\n\n return () => {\n window.clearTimeout(timeoutId)\n }\n }, [delayMs, value])\n\n return debouncedValue\n}\n\ntype SearchResults = Awaited<ReturnType<typeof searchAlgoliaIndex>>\n\nconst useSearchResults = (options: { canSearch: boolean; isOpen: boolean; normalizedQuery: string }) => {\n const docs = useDocsGlobalContext()\n const { canSearch, isOpen, normalizedQuery } = options\n const [results, setResults] = useState<SearchResults>([])\n const [isLoading, setIsLoading] = useState(false)\n const [isError, setIsError] = useState(false)\n\n useEffect(() => {\n if (!isOpen || !canSearch) {\n setResults([])\n setIsLoading(false)\n setIsError(false)\n return\n }\n\n const abortController = new AbortController()\n\n setIsLoading(true)\n setIsError(false)\n\n searchAlgoliaIndex({\n config: docs.algolia,\n query: normalizedQuery,\n signal: abortController.signal,\n })\n .then((nextResults) => {\n if (abortController.signal.aborted) {\n return\n }\n\n setResults(nextResults)\n })\n .catch((error: unknown) => {\n if (abortController.signal.aborted) {\n return\n }\n\n const isAbortError = error instanceof DOMException && error.name === 'AbortError'\n\n if (!isAbortError) {\n setResults([])\n setIsError(true)\n }\n })\n .finally(() => {\n if (!abortController.signal.aborted) {\n setIsLoading(false)\n }\n })\n\n return () => {\n abortController.abort()\n }\n }, [canSearch, docs.algolia, isOpen, normalizedQuery])\n\n return {\n results,\n isLoading,\n isError,\n }\n}\n\nexport const SearchModal = () => {\n const docs = useDocsGlobalContext()\n const { urlPathname } = usePageContext()\n const { close, setQuery } = useDocsSearchActions()\n const isOpen = useDocsSearchStore((state) => state.isOpen)\n const query = useDocsSearchStore((state) => state.query)\n const containerRef = useRef<HTMLDivElement | null>(null)\n const suggestionBoxRef = useRef<HTMLDivElement | null>(null)\n const previousPathnameRef = useRef(urlPathname)\n const debouncedQuery = useDebouncedValue(query, QUERY_DEBOUNCE_MS)\n const normalizedQuery = debouncedQuery.trim()\n const canSearch = Boolean(docs.algolia) && normalizedQuery.length >= MIN_QUERY_LENGTH\n const { isError, isLoading, results } = useSearchResults({\n canSearch,\n isOpen,\n normalizedQuery,\n })\n\n useEffect(() => {\n if (previousPathnameRef.current !== urlPathname) {\n close()\n previousPathnameRef.current = urlPathname\n }\n }, [close, urlPathname])\n\n useEffect(() => {\n if (!isOpen) {\n return\n }\n\n const handlePointerDown = (event: PointerEvent) => {\n const target = event.target as Node\n\n if (!containerRef.current?.contains(target) && !suggestionBoxRef.current?.contains(target)) {\n close()\n }\n }\n\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n close()\n }\n }\n\n document.addEventListener('pointerdown', handlePointerDown)\n document.addEventListener('keydown', handleKeyDown)\n\n return () => {\n document.removeEventListener('pointerdown', handlePointerDown)\n document.removeEventListener('keydown', handleKeyDown)\n }\n }, [close, isOpen])\n\n if (!docs.algolia) {\n return null\n }\n\n return (\n <div ref={containerRef} className=\"relative\">\n <SearchSuggestionBox\n contentRef={suggestionBoxRef}\n isError={isError}\n isLoading={isLoading}\n isOpen={isOpen}\n onClose={close}\n onQueryChange={setQuery}\n query={query}\n results={results}\n />\n </div>\n )\n}\n\ntype SearchSuggestionBoxProps = {\n contentRef: RefObject<HTMLDivElement | null>\n isError: boolean\n isLoading: boolean\n isOpen: boolean\n onClose: () => void\n onQueryChange: (query: string) => void\n query: string\n results: SearchResults\n}\n\nconst SearchSuggestionBox = ({\n contentRef,\n isError,\n isLoading,\n isOpen,\n onClose,\n onQueryChange,\n query,\n results,\n}: SearchSuggestionBoxProps) => {\n const inputRef = useRef<HTMLInputElement | null>(null)\n const normalizedQuery = query.trim()\n const canSearch = normalizedQuery.length >= MIN_QUERY_LENGTH\n\n useEffect(() => {\n if (!isOpen) {\n return\n }\n\n const frameId = window.requestAnimationFrame(() => {\n inputRef.current?.focus()\n inputRef.current?.setSelectionRange(query.length, query.length)\n })\n\n return () => {\n window.cancelAnimationFrame(frameId)\n }\n }, [isOpen, query.length])\n\n if (!isOpen || typeof document === 'undefined') {\n return null\n }\n\n return createPortal(\n <div className=\"fixed inset-0 z-30 h-full w-full bg-base-100/50 backdrop-blur-lg\">\n <div className=\"absolute inset-0 z-1 bg-linear-to-b from-base-100 via-base-100 via-25% to-primary-muted-superlight dark:bg-linear-to-t\" />\n <LayoutComponent\n ref={contentRef}\n $size=\"sm\"\n className=\"mt-5 relative z-2 rounded-box bg-base-100/70 p-6 pt-6 shadow-lg shadow-primary-muted-light\"\n >\n <input\n placeholder=\"Search docs\"\n ref={inputRef}\n type=\"text\"\n className=\"input input-primary input-xl w-full\"\n value={query}\n onChange={(event) => onQueryChange(event.target.value)}\n />\n <div className=\"flex h-7 items-center px-4 text-xs text-base-muted\">\n {isLoading ? (\n <span className=\"flex items-center gap-1\">\n <span className=\"loading loading-dots loading-xs\" />\n Searching...\n </span>\n ) : normalizedQuery ? null : (\n <span className=\"flex items-center gap-1\">\n <MessageCircleQuestion className=\"h-3 w-3 shrink-0\" />\n Type at least {MIN_QUERY_LENGTH} characters.\n </span>\n )}\n </div>\n {normalizedQuery ? (\n isError ? (\n <div className=\"flex items-center gap-2 rounded-box border border-warning/40 bg-base-100 p-4 text-sm text-base-muted shadow-md shadow-primary-muted-light\">\n <TriangleAlert className=\"h-4 w-4 shrink-0 text-warning\" />\n Search is temporarily unavailable.\n </div>\n ) : !canSearch ? (\n <div className=\"text-sm text-base-muted\">Keep typing to search.</div>\n ) : !isLoading && results.length === 0 ? (\n <div className=\"text-sm text-base-muted\">No results found.</div>\n ) : (\n <div className=\"-mx-2 max-h-80 overflow-y-auto p-2\">\n <ul className=\"flex flex-col gap-2\">\n {results.map((result) => (\n <li key={result.href}>\n <a\n href={withSiteBaseUrl(result.href)}\n className=\"block rounded-box border border-base-muted-medium bg-base-100 p-4 shadow-md hover:border-primary-muted hover:bg-base-200\"\n onClick={onClose}\n >\n <div className=\"mb-2 flex items-center justify-start gap-2\">\n <div className=\"font-bold text-base-content\">{result.title}</div>\n {result.sectionTitle ? (\n <div className=\"flex items-center gap-1 text-sm text-base-muted-medium\">\n <ArrowRightFromLine className=\"h-3 w-3\" /> {result.sectionTitle}\n </div>\n ) : null}\n </div>\n {result.excerpt ? <p className=\"text-xs leading-5 text-base-muted\">{result.excerpt}</p> : null}\n </a>\n </li>\n ))}\n </ul>\n </div>\n )\n ) : null}\n </LayoutComponent>\n </div>,\n document.body,\n )\n}\n","import type { ResolvedDocsAlgoliaConfig } from '../../docs/types.js'\n\ntype DocsSearchResult = {\n href: string\n title: string\n excerpt?: string\n sectionTitle?: string\n}\n\ntype SearchAlgoliaResponse = {\n hits?: unknown[]\n}\n\ntype AlgoliaDocSearchHit = {\n url?: unknown\n url_without_anchor?: unknown\n type?: unknown\n category?: unknown\n content?: unknown\n hierarchy?: Record<string, unknown> | null\n _highlightResult?: {\n content?: {\n value?: unknown\n }\n } | null\n _snippetResult?: {\n content?: {\n value?: unknown\n }\n } | null\n}\n\nconst hierarchyLevels = ['lvl0', 'lvl1', 'lvl2', 'lvl3', 'lvl4', 'lvl5', 'lvl6'] as const\n\nconst stripHtml = (value: string) => value.replace(/<[^>]+>/g, '')\n\nconst normalizeString = (value: unknown) => {\n if (typeof value === 'string') {\n const normalized = stripHtml(value).replace(/\\s+/g, ' ').trim()\n return normalized || undefined\n }\n\n if (typeof value === 'number' || typeof value === 'boolean') {\n return String(value)\n }\n\n return undefined\n}\n\nconst getValueAtPath = (value: unknown, path: string): unknown => {\n if (!path) {\n return undefined\n }\n\n const segments = path.split('.').filter(Boolean)\n let currentValue = value\n\n for (const segment of segments) {\n if (!currentValue || typeof currentValue !== 'object' || Array.isArray(currentValue)) {\n return undefined\n }\n\n currentValue = (currentValue as Record<string, unknown>)[segment]\n }\n\n return currentValue\n}\n\nconst getMappedString = (value: unknown, path: string): string | undefined => {\n return normalizeString(getValueAtPath(value, path))\n}\n\nconst buildSearchUrl = (appId: string, indexName: string) => {\n return `https://${appId}-dsn.algolia.net/1/indexes/${encodeURIComponent(indexName)}/query`\n}\n\nconst getDocSearchHierarchyValue = (\n hierarchy: AlgoliaDocSearchHit['hierarchy'],\n level: (typeof hierarchyLevels)[number],\n) => {\n if (!hierarchy || typeof hierarchy !== 'object') {\n return undefined\n }\n\n return normalizeString(hierarchy[level])\n}\n\nconst getDocSearchTitleLevel = (hit: AlgoliaDocSearchHit) => {\n const levelFromType =\n typeof hit.type === 'string' && /^lvl[0-6]$/.test(hit.type) ? (hit.type as (typeof hierarchyLevels)[number]) : null\n\n if (levelFromType && getDocSearchHierarchyValue(hit.hierarchy, levelFromType)) {\n return levelFromType\n }\n\n return [...hierarchyLevels]\n .reverse()\n .find((level) => level !== 'lvl0' && getDocSearchHierarchyValue(hit.hierarchy, level))\n}\n\nconst getDocSearchFallbackResult = (hit: unknown): Partial<DocsSearchResult> => {\n const docSearchHit = hit as AlgoliaDocSearchHit\n const titleLevel = getDocSearchTitleLevel(docSearchHit)\n const title = titleLevel ? getDocSearchHierarchyValue(docSearchHit.hierarchy, titleLevel) : undefined\n const titleLevelIndex = titleLevel ? hierarchyLevels.indexOf(titleLevel) : -1\n const sectionTitle =\n hierarchyLevels\n .slice(0, Math.max(titleLevelIndex, 0))\n .reverse()\n .map((level) => getDocSearchHierarchyValue(docSearchHit.hierarchy, level))\n .find(Boolean) ?? normalizeString(docSearchHit.category)\n\n return {\n href: normalizeString(docSearchHit.url) ?? normalizeString(docSearchHit.url_without_anchor),\n title: title ?? normalizeString(docSearchHit.category),\n excerpt:\n normalizeString(docSearchHit._snippetResult?.content?.value) ??\n normalizeString(docSearchHit.content) ??\n normalizeString(docSearchHit._highlightResult?.content?.value),\n sectionTitle,\n }\n}\n\nconst mapHitToSearchResult = (hit: unknown, config: ResolvedDocsAlgoliaConfig): DocsSearchResult | null => {\n const docSearchFallback = getDocSearchFallbackResult(hit)\n const href = getMappedString(hit, config.fields.href) ?? docSearchFallback.href\n const title = getMappedString(hit, config.fields.title) ?? docSearchFallback.title\n\n if (!href || !title) {\n return null\n }\n\n const excerpt = getMappedString(hit, config.fields.excerpt) ?? docSearchFallback.excerpt\n const sectionTitle = getMappedString(hit, config.fields.sectionTitle) ?? docSearchFallback.sectionTitle\n\n return {\n href,\n title,\n excerpt,\n sectionTitle,\n }\n}\n\nexport const searchAlgoliaIndex = async (options: {\n config?: ResolvedDocsAlgoliaConfig | null\n query: string\n signal?: AbortSignal\n}) => {\n const { config, query, signal } = options\n\n if (!config) {\n throw new Error('Algolia search is not configured.')\n }\n\n const response = await fetch(buildSearchUrl(config.appId, config.indexName), {\n method: 'POST',\n signal,\n headers: {\n accept: 'application/json',\n 'content-type': 'application/json',\n 'x-algolia-api-key': config.apiKey,\n 'x-algolia-application-id': config.appId,\n },\n body: JSON.stringify({\n query,\n ...config.searchParams,\n }),\n })\n\n if (!response.ok) {\n throw new Error(`Algolia search request failed with status ${response.status}.`)\n }\n\n const data = (await response.json()) as SearchAlgoliaResponse\n\n return (data.hits ?? [])\n .map((hit) => mapHitToSearchResult(hit, config))\n .filter((result): result is DocsSearchResult => result !== null)\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,196 @@
1
+ // src/runtime/client/docsGlobalContext.ts
2
+ import { createContext, createElement, useContext } from "react";
3
+ import { usePageContext } from "vike-react/usePageContext";
4
+ var DocsGlobalContext = createContext(null);
5
+ var getDocsFromGlobalContext = (pageContext) => {
6
+ const docs = pageContext.globalContext?.docs;
7
+ if (!docs) {
8
+ throw new Error("Missing docs global context data.");
9
+ }
10
+ return docs;
11
+ };
12
+ var DocsGlobalContextProvider = ({ children, docs }) => {
13
+ return createElement(DocsGlobalContext.Provider, { value: docs }, children);
14
+ };
15
+ var useDocsGlobalContext = () => {
16
+ const docs = useContext(DocsGlobalContext);
17
+ if (!docs) {
18
+ throw new Error("Missing docs global context provider.");
19
+ }
20
+ return docs;
21
+ };
22
+ var useDocsFromPageGlobalContext = () => {
23
+ return getDocsFromGlobalContext(usePageContext());
24
+ };
25
+
26
+ // src/runtime/client/store/runtime-store.tsx
27
+ import { createContext as createContext2, useContext as useContext2 } from "react";
28
+ import { useStore } from "zustand";
29
+ import { createStore } from "zustand/vanilla";
30
+ import { jsx } from "react/jsx-runtime";
31
+ var defaultDocsSearchState = {
32
+ isOpen: false,
33
+ query: ""
34
+ };
35
+ var defaultDocsSidebarState = {
36
+ openNodes: {}
37
+ };
38
+ var createDocsRuntimeStore = () => {
39
+ return createStore()((set) => {
40
+ const searchActions = {
41
+ open: () => set((state) => {
42
+ if (state.searchState.isOpen) {
43
+ return state;
44
+ }
45
+ return {
46
+ searchState: {
47
+ ...state.searchState,
48
+ isOpen: true
49
+ }
50
+ };
51
+ }),
52
+ close: () => set((state) => {
53
+ if (!state.searchState.isOpen) {
54
+ return state;
55
+ }
56
+ return {
57
+ searchState: {
58
+ ...state.searchState,
59
+ isOpen: false
60
+ }
61
+ };
62
+ }),
63
+ toggle: () => set((state) => ({
64
+ searchState: {
65
+ ...state.searchState,
66
+ isOpen: !state.searchState.isOpen
67
+ }
68
+ })),
69
+ setQuery: (query) => set((state) => {
70
+ if (state.searchState.query === query) {
71
+ return state;
72
+ }
73
+ return {
74
+ searchState: {
75
+ ...state.searchState,
76
+ query
77
+ }
78
+ };
79
+ }),
80
+ clearQuery: () => set((state) => {
81
+ if (state.searchState.query === "") {
82
+ return state;
83
+ }
84
+ return {
85
+ searchState: {
86
+ ...state.searchState,
87
+ query: ""
88
+ }
89
+ };
90
+ })
91
+ };
92
+ const sidebarActions = {
93
+ setNodeOpen: (nodeId, isOpen) => set((state) => {
94
+ if (state.sidebarState.openNodes[nodeId] === isOpen) {
95
+ return state;
96
+ }
97
+ return {
98
+ sidebarState: {
99
+ ...state.sidebarState,
100
+ openNodes: {
101
+ ...state.sidebarState.openNodes,
102
+ [nodeId]: isOpen
103
+ }
104
+ }
105
+ };
106
+ })
107
+ };
108
+ return {
109
+ searchActions,
110
+ searchState: defaultDocsSearchState,
111
+ sidebarActions,
112
+ sidebarState: defaultDocsSidebarState
113
+ };
114
+ });
115
+ };
116
+ var DocsRuntimeStoreContext = createContext2(null);
117
+ var DocsRuntimeStoreProvider = ({ children, store }) => {
118
+ return /* @__PURE__ */ jsx(DocsRuntimeStoreContext.Provider, { value: store, children });
119
+ };
120
+ var useDocsRuntimeStoreApi = () => {
121
+ const store = useContext2(DocsRuntimeStoreContext);
122
+ if (store === null) {
123
+ throw new Error("Missing docs runtime store provider.");
124
+ }
125
+ return store;
126
+ };
127
+ var useDocsRuntimeStore = (selector) => {
128
+ return useStore(useDocsRuntimeStoreApi(), selector);
129
+ };
130
+ var useDocsSearchStore = (selector) => {
131
+ return useDocsRuntimeStore(
132
+ (state) => selector({
133
+ ...state.searchState,
134
+ ...state.searchActions
135
+ })
136
+ );
137
+ };
138
+ var useDocsSearchActions = () => {
139
+ return useDocsRuntimeStore((state) => state.searchActions);
140
+ };
141
+ var useDocsSidebarStore = (selector) => {
142
+ return useDocsRuntimeStore(
143
+ (state) => selector({
144
+ ...state.sidebarState,
145
+ ...state.sidebarActions
146
+ })
147
+ );
148
+ };
149
+ var useDocsSidebarActions = () => {
150
+ return useDocsRuntimeStore((state) => state.sidebarActions);
151
+ };
152
+
153
+ // src/runtime/client/components/LayoutComponent.tsx
154
+ import cm from "@classmatejs/react";
155
+ var LayoutSize = {
156
+ xxs: "xxs",
157
+ xs: "xs",
158
+ sm: "sm",
159
+ md: "md",
160
+ lg: "lg",
161
+ xl: "xl",
162
+ full: "full"
163
+ };
164
+ var layoutComponentSizeMapping = {
165
+ xxs: "max-w-[480px]",
166
+ xs: "max-w-[768px]",
167
+ sm: "max-w-5xl",
168
+ md: "max-w-6xl",
169
+ lg: "max-w-7xl",
170
+ xl: "max-w-[1440px]",
171
+ full: "max-w-full"
172
+ };
173
+ var LayoutComponent = cm.div.variants({
174
+ base: ({ $noGrow }) => `px-4 ${$noGrow ? "" : "mx-auto w-full"} relative`,
175
+ variants: {
176
+ $size: layoutComponentSizeMapping
177
+ },
178
+ defaultVariants: {
179
+ $size: LayoutSize.xl
180
+ }
181
+ });
182
+
183
+ export {
184
+ getDocsFromGlobalContext,
185
+ DocsGlobalContextProvider,
186
+ useDocsGlobalContext,
187
+ useDocsFromPageGlobalContext,
188
+ createDocsRuntimeStore,
189
+ DocsRuntimeStoreProvider,
190
+ useDocsSearchStore,
191
+ useDocsSearchActions,
192
+ useDocsSidebarStore,
193
+ useDocsSidebarActions,
194
+ LayoutComponent
195
+ };
196
+ //# sourceMappingURL=chunk-ESAHWFDZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runtime/client/docsGlobalContext.ts","../src/runtime/client/store/runtime-store.tsx","../src/runtime/client/components/LayoutComponent.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\nimport { createContext, createElement, useContext } from 'react'\nimport { usePageContext } from 'vike-react/usePageContext'\nimport type { DocsGlobalContextData } from '../../docs/types.js'\n\nexport type DocsPageContext = {\n globalContext?: {\n docs?: DocsGlobalContextData\n }\n}\n\nconst DocsGlobalContext = createContext<DocsGlobalContextData | null>(null)\n\nexport const getDocsFromGlobalContext = (pageContext: DocsPageContext) => {\n const docs = pageContext.globalContext?.docs\n\n if (!docs) {\n throw new Error('Missing docs global context data.')\n }\n\n return docs\n}\n\nexport const DocsGlobalContextProvider = ({ children, docs }: { children: ReactNode; docs: DocsGlobalContextData }) => {\n return createElement(DocsGlobalContext.Provider, { value: docs }, children)\n}\n\nexport const useDocsGlobalContext = () => {\n const docs = useContext(DocsGlobalContext)\n\n if (!docs) {\n throw new Error('Missing docs global context provider.')\n }\n\n return docs\n}\n\nexport const useDocsFromPageGlobalContext = () => {\n return getDocsFromGlobalContext(usePageContext() as DocsPageContext)\n}\n","import type { ReactNode } from 'react'\nimport { createContext, useContext } from 'react'\nimport { useStore } from 'zustand'\nimport { createStore } from 'zustand/vanilla'\nimport type {\n DocsSearchActions,\n DocsSearchSlice,\n DocsSearchState,\n DocsSidebarActions,\n DocsSidebarSlice,\n DocsSidebarState,\n} from './types.js'\n\ntype DocsRuntimeStoreState = {\n searchActions: DocsSearchActions\n searchState: DocsSearchState\n sidebarActions: DocsSidebarActions\n sidebarState: DocsSidebarState\n}\n\ntype DocsRuntimeStoreApi = ReturnType<typeof createDocsRuntimeStore>\n\nconst defaultDocsSearchState: DocsSearchState = {\n isOpen: false,\n query: '',\n}\n\nconst defaultDocsSidebarState: DocsSidebarState = {\n openNodes: {},\n}\n\nexport const createDocsRuntimeStore = () => {\n return createStore<DocsRuntimeStoreState>()((set) => {\n const searchActions: DocsSearchActions = {\n open: () =>\n set((state) => {\n if (state.searchState.isOpen) {\n return state\n }\n\n return {\n searchState: {\n ...state.searchState,\n isOpen: true,\n },\n }\n }),\n close: () =>\n set((state) => {\n if (!state.searchState.isOpen) {\n return state\n }\n\n return {\n searchState: {\n ...state.searchState,\n isOpen: false,\n },\n }\n }),\n toggle: () =>\n set((state) => ({\n searchState: {\n ...state.searchState,\n isOpen: !state.searchState.isOpen,\n },\n })),\n setQuery: (query) =>\n set((state) => {\n if (state.searchState.query === query) {\n return state\n }\n\n return {\n searchState: {\n ...state.searchState,\n query,\n },\n }\n }),\n clearQuery: () =>\n set((state) => {\n if (state.searchState.query === '') {\n return state\n }\n\n return {\n searchState: {\n ...state.searchState,\n query: '',\n },\n }\n }),\n }\n\n const sidebarActions: DocsSidebarActions = {\n setNodeOpen: (nodeId, isOpen) =>\n set((state) => {\n if (state.sidebarState.openNodes[nodeId] === isOpen) {\n return state\n }\n\n return {\n sidebarState: {\n ...state.sidebarState,\n openNodes: {\n ...state.sidebarState.openNodes,\n [nodeId]: isOpen,\n },\n },\n }\n }),\n }\n\n return {\n searchActions,\n searchState: defaultDocsSearchState,\n sidebarActions,\n sidebarState: defaultDocsSidebarState,\n }\n })\n}\n\nconst DocsRuntimeStoreContext = createContext<DocsRuntimeStoreApi | null>(null)\n\nexport const DocsRuntimeStoreProvider = ({ children, store }: { children: ReactNode; store: DocsRuntimeStoreApi }) => {\n return <DocsRuntimeStoreContext.Provider value={store}>{children}</DocsRuntimeStoreContext.Provider>\n}\n\nconst useDocsRuntimeStoreApi = () => {\n const store = useContext(DocsRuntimeStoreContext)\n\n if (store === null) {\n throw new Error('Missing docs runtime store provider.')\n }\n\n return store\n}\n\nconst useDocsRuntimeStore = <Selected,>(selector: (state: DocsRuntimeStoreState) => Selected) => {\n return useStore(useDocsRuntimeStoreApi(), selector)\n}\n\nexport const useDocsSearchStore = <Selected,>(selector: (state: DocsSearchSlice) => Selected) => {\n return useDocsRuntimeStore((state) =>\n selector({\n ...state.searchState,\n ...state.searchActions,\n }),\n )\n}\n\nexport const useDocsSearchActions = () => {\n return useDocsRuntimeStore((state) => state.searchActions)\n}\n\nexport const useDocsSidebarStore = <Selected,>(selector: (state: DocsSidebarSlice) => Selected) => {\n return useDocsRuntimeStore((state) =>\n selector({\n ...state.sidebarState,\n ...state.sidebarActions,\n }),\n )\n}\n\nexport const useDocsSidebarActions = () => {\n return useDocsRuntimeStore((state) => state.sidebarActions)\n}\n","import cm from '@classmatejs/react'\n\nconst LayoutSize = {\n xxs: 'xxs',\n xs: 'xs',\n sm: 'sm',\n md: 'md',\n lg: 'lg',\n xl: 'xl',\n full: 'full',\n} as const\ntype LayoutSize = (typeof LayoutSize)[keyof typeof LayoutSize]\n\nconst layoutComponentSizeMapping: { [key in LayoutSize]: string } = {\n xxs: 'max-w-[480px]',\n xs: 'max-w-[768px]',\n sm: 'max-w-5xl',\n md: 'max-w-6xl',\n lg: 'max-w-7xl',\n xl: 'max-w-[1440px]',\n full: 'max-w-full',\n} as const\n\ninterface LayoutComponentProps {\n $size?: LayoutSize\n $noGrow?: boolean\n}\n\nexport const LayoutComponent = cm.div.variants<LayoutComponentProps>({\n base: ({ $noGrow }) => `px-4 ${$noGrow ? '' : 'mx-auto w-full'} relative`,\n variants: {\n $size: layoutComponentSizeMapping,\n },\n defaultVariants: {\n $size: LayoutSize.xl,\n },\n})\n"],"mappings":"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","names":["createContext","useContext"]}