@farming-labs/theme 0.0.2-beta.8 → 0.0.2

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.
@@ -0,0 +1,654 @@
1
+ "use client";
2
+
3
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
4
+ import { createPortal } from "react-dom";
5
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
+
7
+ //#region src/docs-command-search.tsx
8
+ function cn(...classes) {
9
+ return classes.filter(Boolean).join(" ");
10
+ }
11
+ function stripHtml(html) {
12
+ if (typeof document !== "undefined") {
13
+ const el = document.createElement("div");
14
+ el.innerHTML = html;
15
+ return el.textContent || el.innerText || "";
16
+ }
17
+ return html.replace(/<[^>]+>/g, "");
18
+ }
19
+ function fuzzyScore(query, text) {
20
+ const q = query.trim().toLowerCase();
21
+ const t = text.toLowerCase();
22
+ if (!q) return {
23
+ score: 0,
24
+ indices: []
25
+ };
26
+ let score = 0;
27
+ const indices = [];
28
+ const idx = t.indexOf(q);
29
+ if (idx >= 0) {
30
+ score += 100 + Math.max(0, 20 - idx);
31
+ for (let i = 0; i < q.length; i++) indices.push(idx + i);
32
+ } else {
33
+ let tPos = 0;
34
+ let chain = 0;
35
+ for (let i = 0; i < q.length; i++) {
36
+ const found = t.indexOf(q[i], tPos);
37
+ if (found === -1) score -= 5;
38
+ else {
39
+ indices.push(found);
40
+ if (found === tPos) chain += 2;
41
+ else chain = 0;
42
+ score += 2 + chain;
43
+ tPos = found + 1;
44
+ if (found === 0 || /\s|-|_|\/|\./.test(text[found - 1])) score += 3;
45
+ }
46
+ }
47
+ }
48
+ return {
49
+ score,
50
+ indices: Array.from(new Set(indices)).sort((a, b) => a - b)
51
+ };
52
+ }
53
+ function HighlightedLabel({ label, indices }) {
54
+ if (!indices.length) return /* @__PURE__ */ jsx(Fragment, { children: label });
55
+ const out = [];
56
+ for (let pos = 0; pos < label.length; pos++) if (indices.includes(pos)) {
57
+ let run = label[pos];
58
+ let p = pos + 1;
59
+ while (indices.includes(p) && p < label.length) {
60
+ run += label[p];
61
+ p++;
62
+ }
63
+ out.push(/* @__PURE__ */ jsx("mark", {
64
+ className: "omni-highlight",
65
+ children: run
66
+ }, `m-${pos}`));
67
+ pos = p - 1;
68
+ } else out.push(/* @__PURE__ */ jsx("span", { children: label[pos] }, `t-${pos}`));
69
+ return /* @__PURE__ */ jsx(Fragment, { children: out });
70
+ }
71
+ function SearchIcon() {
72
+ return /* @__PURE__ */ jsxs("svg", {
73
+ width: "16",
74
+ height: "16",
75
+ viewBox: "0 0 24 24",
76
+ fill: "none",
77
+ stroke: "currentColor",
78
+ strokeWidth: "2",
79
+ strokeLinecap: "round",
80
+ strokeLinejoin: "round",
81
+ children: [/* @__PURE__ */ jsx("circle", {
82
+ cx: "11",
83
+ cy: "11",
84
+ r: "8"
85
+ }), /* @__PURE__ */ jsx("path", { d: "m21 21-4.3-4.3" })]
86
+ });
87
+ }
88
+ function FileIcon() {
89
+ return /* @__PURE__ */ jsxs("svg", {
90
+ width: "16",
91
+ height: "16",
92
+ viewBox: "0 0 24 24",
93
+ fill: "none",
94
+ stroke: "currentColor",
95
+ strokeWidth: "2",
96
+ strokeLinecap: "round",
97
+ strokeLinejoin: "round",
98
+ children: [/* @__PURE__ */ jsx("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }), /* @__PURE__ */ jsx("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })]
99
+ });
100
+ }
101
+ function HashIcon() {
102
+ return /* @__PURE__ */ jsxs("svg", {
103
+ width: "16",
104
+ height: "16",
105
+ viewBox: "0 0 24 24",
106
+ fill: "none",
107
+ stroke: "currentColor",
108
+ strokeWidth: "2",
109
+ strokeLinecap: "round",
110
+ strokeLinejoin: "round",
111
+ children: [
112
+ /* @__PURE__ */ jsx("line", {
113
+ x1: "4",
114
+ x2: "20",
115
+ y1: "9",
116
+ y2: "9"
117
+ }),
118
+ /* @__PURE__ */ jsx("line", {
119
+ x1: "4",
120
+ x2: "20",
121
+ y1: "15",
122
+ y2: "15"
123
+ }),
124
+ /* @__PURE__ */ jsx("line", {
125
+ x1: "10",
126
+ x2: "8",
127
+ y1: "3",
128
+ y2: "21"
129
+ }),
130
+ /* @__PURE__ */ jsx("line", {
131
+ x1: "16",
132
+ x2: "14",
133
+ y1: "3",
134
+ y2: "21"
135
+ })
136
+ ]
137
+ });
138
+ }
139
+ function TypeIcon() {
140
+ return /* @__PURE__ */ jsxs("svg", {
141
+ width: "16",
142
+ height: "16",
143
+ viewBox: "0 0 24 24",
144
+ fill: "none",
145
+ stroke: "currentColor",
146
+ strokeWidth: "2",
147
+ strokeLinecap: "round",
148
+ strokeLinejoin: "round",
149
+ children: [
150
+ /* @__PURE__ */ jsx("polyline", { points: "4 7 4 4 20 4 20 7" }),
151
+ /* @__PURE__ */ jsx("line", {
152
+ x1: "9",
153
+ x2: "15",
154
+ y1: "20",
155
+ y2: "20"
156
+ }),
157
+ /* @__PURE__ */ jsx("line", {
158
+ x1: "12",
159
+ x2: "12",
160
+ y1: "4",
161
+ y2: "20"
162
+ })
163
+ ]
164
+ });
165
+ }
166
+ function CloseIcon() {
167
+ return /* @__PURE__ */ jsxs("svg", {
168
+ width: "16",
169
+ height: "16",
170
+ viewBox: "0 0 24 24",
171
+ fill: "none",
172
+ stroke: "currentColor",
173
+ strokeWidth: "2",
174
+ strokeLinecap: "round",
175
+ strokeLinejoin: "round",
176
+ children: [/* @__PURE__ */ jsx("path", { d: "M18 6 6 18" }), /* @__PURE__ */ jsx("path", { d: "m6 6 12 12" })]
177
+ });
178
+ }
179
+ function EnterIcon() {
180
+ return /* @__PURE__ */ jsxs("svg", {
181
+ width: "12",
182
+ height: "12",
183
+ viewBox: "0 0 24 24",
184
+ fill: "none",
185
+ stroke: "currentColor",
186
+ strokeWidth: "2",
187
+ strokeLinecap: "round",
188
+ strokeLinejoin: "round",
189
+ children: [/* @__PURE__ */ jsx("polyline", { points: "9 10 4 15 9 20" }), /* @__PURE__ */ jsx("path", { d: "M20 4v7a4 4 0 0 1-4 4H4" })]
190
+ });
191
+ }
192
+ function ArrowUpIcon() {
193
+ return /* @__PURE__ */ jsx("svg", {
194
+ width: "12",
195
+ height: "12",
196
+ viewBox: "0 0 24 24",
197
+ fill: "none",
198
+ stroke: "currentColor",
199
+ strokeWidth: "2",
200
+ strokeLinecap: "round",
201
+ strokeLinejoin: "round",
202
+ children: /* @__PURE__ */ jsx("path", { d: "m18 15-6-6-6 6" })
203
+ });
204
+ }
205
+ function ArrowDownIcon() {
206
+ return /* @__PURE__ */ jsx("svg", {
207
+ width: "12",
208
+ height: "12",
209
+ viewBox: "0 0 24 24",
210
+ fill: "none",
211
+ stroke: "currentColor",
212
+ strokeWidth: "2",
213
+ strokeLinecap: "round",
214
+ strokeLinejoin: "round",
215
+ children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" })
216
+ });
217
+ }
218
+ function ExternalLinkIcon() {
219
+ return /* @__PURE__ */ jsxs("svg", {
220
+ width: "14",
221
+ height: "14",
222
+ viewBox: "0 0 24 24",
223
+ fill: "none",
224
+ stroke: "currentColor",
225
+ strokeWidth: "2",
226
+ strokeLinecap: "round",
227
+ strokeLinejoin: "round",
228
+ children: [
229
+ /* @__PURE__ */ jsx("path", { d: "M15 3h6v6" }),
230
+ /* @__PURE__ */ jsx("path", { d: "M10 14 21 3" }),
231
+ /* @__PURE__ */ jsx("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" })
232
+ ]
233
+ });
234
+ }
235
+ function ChevronRightIcon() {
236
+ return /* @__PURE__ */ jsx("svg", {
237
+ width: "14",
238
+ height: "14",
239
+ viewBox: "0 0 24 24",
240
+ fill: "none",
241
+ stroke: "currentColor",
242
+ strokeWidth: "2",
243
+ strokeLinecap: "round",
244
+ strokeLinejoin: "round",
245
+ children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" })
246
+ });
247
+ }
248
+ function LoaderIcon() {
249
+ return /* @__PURE__ */ jsx("svg", {
250
+ width: "12",
251
+ height: "12",
252
+ viewBox: "0 0 24 24",
253
+ fill: "none",
254
+ stroke: "currentColor",
255
+ strokeWidth: "2",
256
+ strokeLinecap: "round",
257
+ strokeLinejoin: "round",
258
+ className: "omni-spin",
259
+ children: /* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" })
260
+ });
261
+ }
262
+ function HistoryIcon() {
263
+ return /* @__PURE__ */ jsxs("svg", {
264
+ width: "16",
265
+ height: "16",
266
+ viewBox: "0 0 24 24",
267
+ fill: "none",
268
+ stroke: "currentColor",
269
+ strokeWidth: "2",
270
+ strokeLinecap: "round",
271
+ strokeLinejoin: "round",
272
+ children: [
273
+ /* @__PURE__ */ jsx("path", { d: "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" }),
274
+ /* @__PURE__ */ jsx("path", { d: "M3 3v5h5" }),
275
+ /* @__PURE__ */ jsx("path", { d: "M12 7v5l4 2" })
276
+ ]
277
+ });
278
+ }
279
+ function iconForType(type) {
280
+ switch (type) {
281
+ case "heading": return /* @__PURE__ */ jsx(HashIcon, {});
282
+ case "text": return /* @__PURE__ */ jsx(TypeIcon, {});
283
+ default: return /* @__PURE__ */ jsx(FileIcon, {});
284
+ }
285
+ }
286
+ function labelForType(type) {
287
+ switch (type) {
288
+ case "page": return "Page";
289
+ case "heading": return "Section";
290
+ case "text": return "Content";
291
+ default: return "Result";
292
+ }
293
+ }
294
+ /**
295
+ * Built-in docs search command palette.
296
+ * Intercepts Cmd+K and sidebar search button to provide an advanced
297
+ * fuzzy-search experience. Styled entirely via omni-* CSS classes
298
+ * so each theme provides its own visual variant.
299
+ */
300
+ function DocsCommandSearch() {
301
+ const [open, setOpen] = useState(false);
302
+ const [query, setQuery] = useState("");
303
+ const [debouncedQuery, setDebouncedQuery] = useState("");
304
+ const [results, setResults] = useState([]);
305
+ const [loading, setLoading] = useState(false);
306
+ const [activeIndex, setActiveIndex] = useState(0);
307
+ const [recents, setRecents] = useState([]);
308
+ const [mounted, setMounted] = useState(false);
309
+ const inputRef = useRef(null);
310
+ const listRef = useRef(null);
311
+ useEffect(() => {
312
+ setMounted(true);
313
+ try {
314
+ const raw = localStorage.getItem("fd:omni:recents");
315
+ if (raw) setRecents(JSON.parse(raw));
316
+ } catch {}
317
+ }, []);
318
+ useEffect(() => {
319
+ const id = setTimeout(() => setDebouncedQuery(query), 150);
320
+ return () => clearTimeout(id);
321
+ }, [query]);
322
+ useEffect(() => {
323
+ function handler(e) {
324
+ if ((e.metaKey || e.ctrlKey) && e.key === "k") {
325
+ e.preventDefault();
326
+ e.stopPropagation();
327
+ e.stopImmediatePropagation();
328
+ setOpen(true);
329
+ }
330
+ }
331
+ document.addEventListener("keydown", handler, true);
332
+ return () => document.removeEventListener("keydown", handler, true);
333
+ }, []);
334
+ useEffect(() => {
335
+ function handler(e) {
336
+ const button = e.target.closest("button");
337
+ if (!button) return;
338
+ const text = button.textContent || "";
339
+ const ariaLabel = (button.getAttribute("aria-label") || "").toLowerCase();
340
+ if (text.includes("Search") && (text.includes("⌘") || text.includes("K")) || ariaLabel.includes("search") || text === "Open Search") {
341
+ e.preventDefault();
342
+ e.stopPropagation();
343
+ e.stopImmediatePropagation();
344
+ setOpen(true);
345
+ }
346
+ }
347
+ document.addEventListener("click", handler, true);
348
+ return () => document.removeEventListener("click", handler, true);
349
+ }, []);
350
+ useEffect(() => {
351
+ if (!debouncedQuery.trim()) {
352
+ setResults([]);
353
+ setActiveIndex(0);
354
+ return;
355
+ }
356
+ let cancelled = false;
357
+ setLoading(true);
358
+ (async () => {
359
+ try {
360
+ const res = await fetch(`/api/docs?query=${encodeURIComponent(debouncedQuery)}`);
361
+ if (!res.ok || cancelled) return;
362
+ const items = (await res.json()).map((r) => {
363
+ const label = stripHtml(r.content);
364
+ const { score, indices } = fuzzyScore(debouncedQuery, label);
365
+ return {
366
+ id: r.id,
367
+ label,
368
+ subtitle: labelForType(r.type),
369
+ url: r.url,
370
+ icon: iconForType(r.type),
371
+ score,
372
+ indices
373
+ };
374
+ });
375
+ items.sort((a, b) => b.score - a.score || a.label.localeCompare(b.label));
376
+ if (!cancelled) {
377
+ setResults(items);
378
+ setActiveIndex(0);
379
+ }
380
+ } catch {}
381
+ if (!cancelled) setLoading(false);
382
+ })();
383
+ return () => {
384
+ cancelled = true;
385
+ };
386
+ }, [debouncedQuery]);
387
+ useEffect(() => {
388
+ if (open) setTimeout(() => inputRef.current?.focus(), 10);
389
+ else {
390
+ setQuery("");
391
+ setResults([]);
392
+ setActiveIndex(0);
393
+ }
394
+ }, [open]);
395
+ useEffect(() => {
396
+ if (!open) return;
397
+ function handler(e) {
398
+ if (e.key === "Escape") setOpen(false);
399
+ }
400
+ document.addEventListener("keydown", handler);
401
+ return () => document.removeEventListener("keydown", handler);
402
+ }, [open]);
403
+ const saveRecent = useCallback((item) => {
404
+ try {
405
+ const entry = {
406
+ id: item.id,
407
+ label: item.label,
408
+ url: item.url
409
+ };
410
+ const next = [entry, ...recents.filter((r) => r.id !== entry.id)].slice(0, 8);
411
+ setRecents(next);
412
+ localStorage.setItem("fd:omni:recents", JSON.stringify(next));
413
+ } catch {}
414
+ }, [recents]);
415
+ const execute = useCallback((item) => {
416
+ saveRecent(item);
417
+ setOpen(false);
418
+ if (item.url.startsWith("http")) window.open(item.url, "_blank", "noopener");
419
+ else window.location.href = item.url;
420
+ }, [saveRecent]);
421
+ const displayItems = useMemo(() => {
422
+ if (results.length > 0) return results;
423
+ return [];
424
+ }, [results]);
425
+ const recentItems = useMemo(() => {
426
+ if (query.trim() || results.length > 0) return [];
427
+ return recents.map((r) => ({
428
+ id: r.id,
429
+ label: r.label,
430
+ subtitle: "Recently viewed",
431
+ url: r.url,
432
+ icon: /* @__PURE__ */ jsx(FileIcon, {}),
433
+ score: 0,
434
+ indices: []
435
+ }));
436
+ }, [
437
+ query,
438
+ results,
439
+ recents
440
+ ]);
441
+ function moveActive(delta) {
442
+ const total = displayItems.length + recentItems.length;
443
+ if (!total) return;
444
+ let next = activeIndex + delta;
445
+ if (next < 0) next = total - 1;
446
+ if (next >= total) next = 0;
447
+ setActiveIndex(next);
448
+ const allItems = [...recentItems, ...displayItems];
449
+ (listRef.current?.querySelector(`[data-id="${allItems[next]?.id}"]`))?.scrollIntoView({ block: "nearest" });
450
+ }
451
+ function handleKeyDown(e) {
452
+ if (e.key === "ArrowDown") {
453
+ e.preventDefault();
454
+ moveActive(1);
455
+ } else if (e.key === "ArrowUp") {
456
+ e.preventDefault();
457
+ moveActive(-1);
458
+ } else if (e.key === "Enter") {
459
+ e.preventDefault();
460
+ const allItems = [...recentItems, ...displayItems];
461
+ if (allItems[activeIndex]) execute(allItems[activeIndex]);
462
+ }
463
+ }
464
+ if (!mounted || !open) return null;
465
+ const allItems = [...recentItems, ...displayItems];
466
+ return createPortal(/* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
467
+ className: "omni-overlay",
468
+ onClick: () => setOpen(false)
469
+ }), /* @__PURE__ */ jsxs("div", {
470
+ className: "omni-content",
471
+ role: "dialog",
472
+ "aria-label": "Search documentation",
473
+ children: [
474
+ /* @__PURE__ */ jsx("div", {
475
+ className: "omni-header",
476
+ children: /* @__PURE__ */ jsxs("div", {
477
+ className: "omni-search-row",
478
+ children: [
479
+ /* @__PURE__ */ jsx("span", {
480
+ className: "omni-search-icon",
481
+ children: /* @__PURE__ */ jsx(SearchIcon, {})
482
+ }),
483
+ /* @__PURE__ */ jsx("input", {
484
+ ref: inputRef,
485
+ type: "text",
486
+ role: "combobox",
487
+ "aria-expanded": "true",
488
+ placeholder: "Search documentation…",
489
+ value: query,
490
+ onChange: (e) => setQuery(e.target.value),
491
+ onKeyDown: handleKeyDown,
492
+ className: "omni-search-input"
493
+ }),
494
+ /* @__PURE__ */ jsx("kbd", {
495
+ className: "omni-kbd",
496
+ children: "⌘K"
497
+ }),
498
+ /* @__PURE__ */ jsx("button", {
499
+ "aria-label": "Close",
500
+ className: "omni-close-btn",
501
+ onClick: () => setOpen(false),
502
+ children: /* @__PURE__ */ jsx(CloseIcon, {})
503
+ })
504
+ ]
505
+ })
506
+ }),
507
+ /* @__PURE__ */ jsxs("div", {
508
+ className: "omni-body",
509
+ ref: listRef,
510
+ role: "listbox",
511
+ "aria-label": "Search results",
512
+ children: [
513
+ loading && /* @__PURE__ */ jsxs("div", {
514
+ className: "omni-loading",
515
+ children: [/* @__PURE__ */ jsx(LoaderIcon, {}), " Searching…"]
516
+ }),
517
+ recentItems.length > 0 && /* @__PURE__ */ jsxs("div", {
518
+ className: "omni-group",
519
+ children: [/* @__PURE__ */ jsx("div", {
520
+ className: "omni-group-label",
521
+ children: "Recent"
522
+ }), /* @__PURE__ */ jsx("div", {
523
+ className: "omni-group-items",
524
+ children: recentItems.map((item, i) => /* @__PURE__ */ jsxs("button", {
525
+ "data-id": item.id,
526
+ role: "option",
527
+ "aria-selected": i === activeIndex,
528
+ onMouseEnter: () => setActiveIndex(i),
529
+ onClick: () => execute(item),
530
+ className: cn("omni-item", i === activeIndex && "omni-item-active"),
531
+ children: [
532
+ /* @__PURE__ */ jsx("div", {
533
+ className: "omni-item-icon",
534
+ children: item.icon
535
+ }),
536
+ /* @__PURE__ */ jsxs("div", {
537
+ className: "omni-item-text",
538
+ children: [/* @__PURE__ */ jsx("div", {
539
+ className: "omni-item-label",
540
+ children: item.label
541
+ }), /* @__PURE__ */ jsx("div", {
542
+ className: "omni-item-subtitle",
543
+ children: item.subtitle
544
+ })]
545
+ }),
546
+ /* @__PURE__ */ jsx("a", {
547
+ href: item.url,
548
+ target: "_blank",
549
+ rel: "noopener noreferrer",
550
+ className: "omni-item-ext",
551
+ title: "Open in new tab",
552
+ onClick: (e) => {
553
+ e.stopPropagation();
554
+ },
555
+ children: /* @__PURE__ */ jsx(ExternalLinkIcon, {})
556
+ }),
557
+ /* @__PURE__ */ jsx(ChevronRightIcon, {})
558
+ ]
559
+ }, item.id))
560
+ })]
561
+ }),
562
+ displayItems.length > 0 && /* @__PURE__ */ jsxs("div", {
563
+ className: "omni-group",
564
+ children: [/* @__PURE__ */ jsx("div", {
565
+ className: "omni-group-label",
566
+ children: "Documentation"
567
+ }), /* @__PURE__ */ jsx("div", {
568
+ className: "omni-group-items",
569
+ children: displayItems.map((item, i) => {
570
+ const idx = recentItems.length + i;
571
+ return /* @__PURE__ */ jsxs("button", {
572
+ "data-id": item.id,
573
+ role: "option",
574
+ "aria-selected": idx === activeIndex,
575
+ onMouseEnter: () => setActiveIndex(idx),
576
+ onClick: () => execute(item),
577
+ className: cn("omni-item", idx === activeIndex && "omni-item-active"),
578
+ children: [
579
+ /* @__PURE__ */ jsx("div", {
580
+ className: "omni-item-icon",
581
+ children: item.icon
582
+ }),
583
+ /* @__PURE__ */ jsxs("div", {
584
+ className: "omni-item-text",
585
+ children: [/* @__PURE__ */ jsx("div", {
586
+ className: "omni-item-label",
587
+ children: /* @__PURE__ */ jsx(HighlightedLabel, {
588
+ label: item.label,
589
+ indices: item.indices
590
+ })
591
+ }), /* @__PURE__ */ jsx("div", {
592
+ className: "omni-item-subtitle",
593
+ children: item.subtitle
594
+ })]
595
+ }),
596
+ /* @__PURE__ */ jsx("a", {
597
+ href: item.url,
598
+ target: "_blank",
599
+ rel: "noopener noreferrer",
600
+ className: "omni-item-ext",
601
+ title: "Open in new tab",
602
+ onClick: (e) => {
603
+ e.stopPropagation();
604
+ },
605
+ children: /* @__PURE__ */ jsx(ExternalLinkIcon, {})
606
+ }),
607
+ /* @__PURE__ */ jsx(ChevronRightIcon, {})
608
+ ]
609
+ }, item.id);
610
+ })
611
+ })]
612
+ }),
613
+ allItems.length === 0 && !loading && /* @__PURE__ */ jsxs("div", {
614
+ className: "omni-empty",
615
+ children: [/* @__PURE__ */ jsx("div", {
616
+ className: "omni-empty-icon",
617
+ children: /* @__PURE__ */ jsx(HistoryIcon, {})
618
+ }), debouncedQuery ? "No results found. Try a different query." : "Type to search the docs, or browse recent items."]
619
+ })
620
+ ]
621
+ }),
622
+ /* @__PURE__ */ jsx("div", {
623
+ className: "omni-footer",
624
+ children: /* @__PURE__ */ jsx("div", {
625
+ className: "omni-footer-inner",
626
+ children: /* @__PURE__ */ jsxs("div", {
627
+ className: "omni-footer-hints",
628
+ children: [
629
+ /* @__PURE__ */ jsxs("span", {
630
+ className: "omni-footer-hint",
631
+ children: [/* @__PURE__ */ jsx(EnterIcon, {}), " to select"]
632
+ }),
633
+ /* @__PURE__ */ jsxs("span", {
634
+ className: "omni-footer-hint",
635
+ children: [
636
+ /* @__PURE__ */ jsx(ArrowUpIcon, {}),
637
+ /* @__PURE__ */ jsx(ArrowDownIcon, {}),
638
+ " to navigate"
639
+ ]
640
+ }),
641
+ /* @__PURE__ */ jsxs("span", {
642
+ className: "omni-footer-hint omni-footer-hint-desktop",
643
+ children: [/* @__PURE__ */ jsx(CloseIcon, {}), " to close"]
644
+ })
645
+ ]
646
+ })
647
+ })
648
+ })
649
+ ]
650
+ })] }), document.body);
651
+ }
652
+
653
+ //#endregion
654
+ export { DocsCommandSearch };
@@ -14,20 +14,29 @@ import { DocsConfig } from "@farming-labs/docs";
14
14
  * export const metadata = createDocsMetadata(docsConfig);
15
15
  * ```
16
16
  */
17
- declare function createDocsMetadata(config: DocsConfig): {
18
- twitter?: {
19
- card: "summary" | "summary_large_image";
20
- } | undefined;
21
- description?: string | undefined;
22
- title: {
23
- template: string;
24
- default: string;
25
- };
26
- };
17
+ declare function createDocsMetadata(config: DocsConfig): Record<string, unknown>;
18
+ /**
19
+ * Generate page-level metadata with dynamic OG images.
20
+ *
21
+ * Usage in a docs page or [[...slug]] route:
22
+ * ```ts
23
+ * export function generateMetadata({ params }) {
24
+ * const page = getPage(params.slug);
25
+ * return createPageMetadata(docsConfig, {
26
+ * title: page.data.title,
27
+ * description: page.data.description,
28
+ * });
29
+ * }
30
+ * ```
31
+ */
32
+ declare function createPageMetadata(config: DocsConfig, page: {
33
+ title: string;
34
+ description?: string;
35
+ }): Record<string, unknown>;
27
36
  declare function createDocsLayout(config: DocsConfig): ({
28
37
  children
29
38
  }: {
30
39
  children: ReactNode;
31
40
  }) => react_jsx_runtime0.JSX.Element;
32
41
  //#endregion
33
- export { createDocsLayout, createDocsMetadata };
42
+ export { createDocsLayout, createDocsMetadata, createPageMetadata };