blodemd 0.0.5 → 0.0.6

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 (184) hide show
  1. package/dev-server/app/[[...slug]]/page.tsx +139 -0
  2. package/dev-server/app/blodemd-dev/invalidate/route.ts +12 -0
  3. package/dev-server/app/blodemd-dev/static/[...path]/route.ts +32 -0
  4. package/dev-server/app/blodemd-dev/version/route.ts +14 -0
  5. package/dev-server/app/blodemd-internal/proxy/route.ts +86 -0
  6. package/dev-server/app/error.tsx +24 -0
  7. package/dev-server/app/globals.css +4 -0
  8. package/dev-server/app/layout.tsx +38 -0
  9. package/dev-server/app/not-found.tsx +18 -0
  10. package/dev-server/app/search/route.ts +17 -0
  11. package/dev-server/components/dev-reload-script.tsx +86 -0
  12. package/dev-server/components/providers.tsx +15 -0
  13. package/dev-server/lib/dev-state.ts +8 -0
  14. package/dev-server/lib/local-content-source.ts +103 -0
  15. package/dev-server/lib/local-runtime.tsx +558 -0
  16. package/dev-server/next.config.js +46 -0
  17. package/dev-server/package.json +57 -0
  18. package/dev-server/postcss.config.mjs +7 -0
  19. package/dev-server/public/glide-variable.woff2 +0 -0
  20. package/dev-server/tsconfig.json +49 -0
  21. package/dist/cli.mjs +108 -39
  22. package/dist/cli.mjs.map +1 -1
  23. package/docs/app/globals.css +455 -0
  24. package/docs/components/api/api-playground.tsx +295 -0
  25. package/docs/components/api/api-reference.tsx +121 -0
  26. package/docs/components/content/collection-index.tsx +114 -0
  27. package/docs/components/docs/contextual-menu.tsx +406 -0
  28. package/docs/components/docs/copy-page-menu.tsx +255 -0
  29. package/docs/components/docs/doc-header.tsx +192 -0
  30. package/docs/components/docs/doc-shell.tsx +289 -0
  31. package/docs/components/docs/doc-sidebar.tsx +206 -0
  32. package/docs/components/docs/doc-toc.tsx +45 -0
  33. package/docs/components/docs/mobile-nav.tsx +207 -0
  34. package/docs/components/mdx/accordion.tsx +83 -0
  35. package/docs/components/mdx/badge.tsx +79 -0
  36. package/docs/components/mdx/callout.tsx +88 -0
  37. package/docs/components/mdx/card.tsx +104 -0
  38. package/docs/components/mdx/code-block.tsx +75 -0
  39. package/docs/components/mdx/code-group.tsx +94 -0
  40. package/docs/components/mdx/color.tsx +87 -0
  41. package/docs/components/mdx/columns.tsx +25 -0
  42. package/docs/components/mdx/expandable.tsx +45 -0
  43. package/docs/components/mdx/field-layout.tsx +77 -0
  44. package/docs/components/mdx/frame.tsx +23 -0
  45. package/docs/components/mdx/get-text-content.ts +18 -0
  46. package/docs/components/mdx/icon.tsx +56 -0
  47. package/docs/components/mdx/index.tsx +96 -0
  48. package/docs/components/mdx/installer.tsx +20 -0
  49. package/docs/components/mdx/panel.tsx +11 -0
  50. package/docs/components/mdx/param-field.tsx +56 -0
  51. package/docs/components/mdx/preview.tsx +36 -0
  52. package/docs/components/mdx/prompt.tsx +63 -0
  53. package/docs/components/mdx/request-example.tsx +27 -0
  54. package/docs/components/mdx/response-field.tsx +42 -0
  55. package/docs/components/mdx/steps.tsx +92 -0
  56. package/docs/components/mdx/tabs.tsx +88 -0
  57. package/docs/components/mdx/tile.tsx +43 -0
  58. package/docs/components/mdx/tooltip.tsx +71 -0
  59. package/docs/components/mdx/tree.tsx +120 -0
  60. package/docs/components/mdx/type-table.tsx +71 -0
  61. package/docs/components/mdx/update.tsx +44 -0
  62. package/docs/components/mdx/video.tsx +12 -0
  63. package/docs/components/mdx/view.tsx +66 -0
  64. package/docs/components/providers.tsx +15 -0
  65. package/docs/components/ui/breadcrumb.tsx +92 -0
  66. package/docs/components/ui/button.tsx +90 -0
  67. package/docs/components/ui/card.tsx +92 -0
  68. package/docs/components/ui/command.tsx +139 -0
  69. package/docs/components/ui/dialog.tsx +97 -0
  70. package/docs/components/ui/field.tsx +237 -0
  71. package/docs/components/ui/input.tsx +105 -0
  72. package/docs/components/ui/label.tsx +22 -0
  73. package/docs/components/ui/popover.tsx +72 -0
  74. package/docs/components/ui/search.tsx +380 -0
  75. package/docs/components/ui/separator.tsx +26 -0
  76. package/docs/components/ui/sheet.tsx +104 -0
  77. package/docs/components/ui/sidebar.tsx +433 -0
  78. package/docs/components/ui/theme-toggle.tsx +62 -0
  79. package/docs/components/ui/tooltip.tsx +53 -0
  80. package/docs/lib/contextual-options.ts +193 -0
  81. package/docs/lib/docs-collection.ts +22 -0
  82. package/docs/lib/mdx.ts +90 -0
  83. package/docs/lib/navigation.ts +288 -0
  84. package/docs/lib/openapi.ts +158 -0
  85. package/docs/lib/routes.ts +10 -0
  86. package/docs/lib/server-cache.ts +83 -0
  87. package/docs/lib/shiki.ts +35 -0
  88. package/docs/lib/theme.ts +29 -0
  89. package/docs/lib/toc.ts +2 -0
  90. package/docs/lib/utils.ts +5 -0
  91. package/package.json +33 -4
  92. package/packages/@repo/common/dist/index.d.ts +9 -0
  93. package/packages/@repo/common/dist/index.d.ts.map +1 -0
  94. package/packages/@repo/common/dist/index.js +42 -0
  95. package/packages/@repo/common/package.json +34 -0
  96. package/packages/@repo/common/src/common.unit.test.ts +55 -0
  97. package/packages/@repo/common/src/index.ts +51 -0
  98. package/packages/@repo/contracts/dist/api-key.d.ts +30 -0
  99. package/packages/@repo/contracts/dist/api-key.d.ts.map +1 -0
  100. package/packages/@repo/contracts/dist/api-key.js +20 -0
  101. package/packages/@repo/contracts/dist/dates.d.ts +4 -0
  102. package/packages/@repo/contracts/dist/dates.d.ts.map +1 -0
  103. package/packages/@repo/contracts/dist/dates.js +2 -0
  104. package/packages/@repo/contracts/dist/deployment.d.ts +71 -0
  105. package/packages/@repo/contracts/dist/deployment.d.ts.map +1 -0
  106. package/packages/@repo/contracts/dist/deployment.js +46 -0
  107. package/packages/@repo/contracts/dist/domain.d.ts +94 -0
  108. package/packages/@repo/contracts/dist/domain.d.ts.map +1 -0
  109. package/packages/@repo/contracts/dist/domain.js +36 -0
  110. package/packages/@repo/contracts/dist/ids.d.ts +14 -0
  111. package/packages/@repo/contracts/dist/ids.d.ts.map +1 -0
  112. package/packages/@repo/contracts/dist/ids.js +10 -0
  113. package/packages/@repo/contracts/dist/index.d.ts +10 -0
  114. package/packages/@repo/contracts/dist/index.d.ts.map +1 -0
  115. package/packages/@repo/contracts/dist/index.js +11 -0
  116. package/packages/@repo/contracts/dist/pagination.d.ts +23 -0
  117. package/packages/@repo/contracts/dist/pagination.d.ts.map +1 -0
  118. package/packages/@repo/contracts/dist/pagination.js +15 -0
  119. package/packages/@repo/contracts/dist/project.d.ts +25 -0
  120. package/packages/@repo/contracts/dist/project.d.ts.map +1 -0
  121. package/packages/@repo/contracts/dist/project.js +23 -0
  122. package/packages/@repo/contracts/dist/tenant.d.ts +99 -0
  123. package/packages/@repo/contracts/dist/tenant.d.ts.map +1 -0
  124. package/packages/@repo/contracts/dist/tenant.js +36 -0
  125. package/packages/@repo/contracts/dist/user.d.ts +9 -0
  126. package/packages/@repo/contracts/dist/user.d.ts.map +1 -0
  127. package/packages/@repo/contracts/dist/user.js +9 -0
  128. package/packages/@repo/contracts/package.json +37 -0
  129. package/packages/@repo/contracts/src/api-key.ts +27 -0
  130. package/packages/@repo/contracts/src/dates.ts +4 -0
  131. package/packages/@repo/contracts/src/deployment.ts +73 -0
  132. package/packages/@repo/contracts/src/domain.ts +51 -0
  133. package/packages/@repo/contracts/src/ids.ts +22 -0
  134. package/packages/@repo/contracts/src/index.ts +11 -0
  135. package/packages/@repo/contracts/src/pagination.ts +21 -0
  136. package/packages/@repo/contracts/src/project.ts +30 -0
  137. package/packages/@repo/contracts/src/tenant.ts +54 -0
  138. package/packages/@repo/contracts/src/user.ts +12 -0
  139. package/packages/@repo/models/dist/docs-config.d.ts +985 -0
  140. package/packages/@repo/models/dist/docs-config.d.ts.map +1 -0
  141. package/packages/@repo/models/dist/docs-config.js +548 -0
  142. package/packages/@repo/models/dist/index.d.ts +3 -0
  143. package/packages/@repo/models/dist/index.d.ts.map +1 -0
  144. package/packages/@repo/models/dist/index.js +3 -0
  145. package/packages/@repo/models/dist/tenant.d.ts +25 -0
  146. package/packages/@repo/models/dist/tenant.d.ts.map +1 -0
  147. package/packages/@repo/models/dist/tenant.js +1 -0
  148. package/packages/@repo/models/package.json +37 -0
  149. package/packages/@repo/models/src/docs-config.ts +648 -0
  150. package/packages/@repo/models/src/index.ts +3 -0
  151. package/packages/@repo/models/src/tenant.ts +29 -0
  152. package/packages/@repo/prebuild/dist/index.d.ts +2 -0
  153. package/packages/@repo/prebuild/dist/index.d.ts.map +1 -0
  154. package/packages/@repo/prebuild/dist/index.js +2 -0
  155. package/packages/@repo/prebuild/dist/openapi.d.ts +43 -0
  156. package/packages/@repo/prebuild/dist/openapi.d.ts.map +1 -0
  157. package/packages/@repo/prebuild/dist/openapi.js +58 -0
  158. package/packages/@repo/prebuild/package.json +39 -0
  159. package/packages/@repo/prebuild/src/index.ts +2 -0
  160. package/packages/@repo/prebuild/src/openapi.ts +116 -0
  161. package/packages/@repo/previewing/dist/blob-source.d.ts +16 -0
  162. package/packages/@repo/previewing/dist/blob-source.d.ts.map +1 -0
  163. package/packages/@repo/previewing/dist/blob-source.js +110 -0
  164. package/packages/@repo/previewing/dist/content-source.d.ts +12 -0
  165. package/packages/@repo/previewing/dist/content-source.d.ts.map +1 -0
  166. package/packages/@repo/previewing/dist/content-source.js +1 -0
  167. package/packages/@repo/previewing/dist/fs-source.d.ts +11 -0
  168. package/packages/@repo/previewing/dist/fs-source.d.ts.map +1 -0
  169. package/packages/@repo/previewing/dist/fs-source.js +79 -0
  170. package/packages/@repo/previewing/dist/index.d.ts +120 -0
  171. package/packages/@repo/previewing/dist/index.d.ts.map +1 -0
  172. package/packages/@repo/previewing/dist/index.js +984 -0
  173. package/packages/@repo/previewing/package.json +41 -0
  174. package/packages/@repo/previewing/src/blob-source.ts +167 -0
  175. package/packages/@repo/previewing/src/content-source.ts +12 -0
  176. package/packages/@repo/previewing/src/fs-source.ts +111 -0
  177. package/packages/@repo/previewing/src/index.ts +1490 -0
  178. package/packages/@repo/previewing/src/index.unit.test.ts +290 -0
  179. package/packages/@repo/validation/dist/index.d.ts +12 -0
  180. package/packages/@repo/validation/dist/index.d.ts.map +1 -0
  181. package/packages/@repo/validation/dist/index.js +30 -0
  182. package/packages/@repo/validation/package.json +37 -0
  183. package/packages/@repo/validation/src/index.ts +59 -0
  184. package/packages/@repo/validation/src/mintlify-docs-schema.json +5016 -0
@@ -0,0 +1,380 @@
1
+ "use client";
2
+
3
+ import { SearchIcon } from "blode-icons-react";
4
+ import { useRouter } from "next/navigation";
5
+ import {
6
+ startTransition,
7
+ useCallback,
8
+ useDeferredValue,
9
+ useEffect,
10
+ useMemo,
11
+ useRef,
12
+ useState,
13
+ } from "react";
14
+ import type {
15
+ ChangeEvent,
16
+ KeyboardEvent as ReactKeyboardEvent,
17
+ MouseEvent as ReactMouseEvent,
18
+ } from "react";
19
+
20
+ import { toDocHref } from "@/lib/routes";
21
+
22
+ export interface SearchItem {
23
+ href?: string;
24
+ title: string;
25
+ path: string;
26
+ }
27
+
28
+ interface SearchResponse {
29
+ items: SearchItem[];
30
+ }
31
+
32
+ const MAX_RESULTS = 12;
33
+
34
+ const isEditableTarget = (target: EventTarget | null) =>
35
+ (target instanceof HTMLElement && target.isContentEditable) ||
36
+ target instanceof HTMLInputElement ||
37
+ target instanceof HTMLTextAreaElement ||
38
+ target instanceof HTMLSelectElement;
39
+
40
+ const searchMatches = (item: SearchItem, query: string) => {
41
+ if (!query) {
42
+ return true;
43
+ }
44
+
45
+ const haystack =
46
+ `${item.title} ${item.path} ${item.href ?? ""}`.toLowerCase();
47
+ return haystack.includes(query);
48
+ };
49
+
50
+ const getWrappedNextIndex = (current: number, length: number) => {
51
+ if (length === 0 || current >= length - 1) {
52
+ return 0;
53
+ }
54
+
55
+ return current + 1;
56
+ };
57
+
58
+ const getWrappedPrevIndex = (current: number, length: number) => {
59
+ if (length === 0 || current <= 0) {
60
+ return length - 1;
61
+ }
62
+
63
+ return current - 1;
64
+ };
65
+
66
+ export const Search = ({ basePath }: { basePath: string }) => {
67
+ const router = useRouter();
68
+ const inputRef = useRef<HTMLInputElement>(null);
69
+ const requestRef = useRef<Promise<void> | null>(null);
70
+ const loadedRef = useRef(false);
71
+ const [activeIndex, setActiveIndex] = useState(0);
72
+ const [items, setItems] = useState<SearchItem[]>([]);
73
+ const [open, setOpen] = useState(false);
74
+ const [query, setQuery] = useState("");
75
+ const [status, setStatus] = useState<"idle" | "loading" | "ready" | "error">(
76
+ "idle"
77
+ );
78
+
79
+ const deferredQuery = useDeferredValue(query.trim().toLowerCase());
80
+
81
+ const loadSearchItems = useCallback(() => {
82
+ if (loadedRef.current) {
83
+ return Promise.resolve();
84
+ }
85
+ if (requestRef.current) {
86
+ return requestRef.current;
87
+ }
88
+
89
+ setStatus("loading");
90
+ const request = (async () => {
91
+ try {
92
+ const response = await fetch(toDocHref("search", basePath), {
93
+ headers: {
94
+ accept: "application/json",
95
+ },
96
+ });
97
+
98
+ if (!response.ok) {
99
+ throw new Error(`Failed to load search index: ${response.status}`);
100
+ }
101
+
102
+ const payload = (await response.json()) as SearchResponse;
103
+ const nextItems = Array.isArray(payload.items) ? payload.items : [];
104
+ loadedRef.current = true;
105
+ startTransition(() => {
106
+ setItems(nextItems);
107
+ setStatus("ready");
108
+ });
109
+ } catch {
110
+ setStatus("error");
111
+ } finally {
112
+ requestRef.current = null;
113
+ }
114
+ })();
115
+
116
+ requestRef.current = request;
117
+ return request;
118
+ }, [basePath]);
119
+
120
+ const filteredItems = useMemo(
121
+ () =>
122
+ items
123
+ .filter((item) => searchMatches(item, deferredQuery))
124
+ .slice(0, MAX_RESULTS),
125
+ [deferredQuery, items]
126
+ );
127
+
128
+ const closeSearch = useCallback(() => {
129
+ setOpen(false);
130
+ setQuery("");
131
+ setActiveIndex(0);
132
+ }, []);
133
+
134
+ const runSelection = useCallback(
135
+ (item: SearchItem) => {
136
+ closeSearch();
137
+ if (item.href) {
138
+ window.open(item.href, "_blank", "noopener,noreferrer");
139
+ return;
140
+ }
141
+ router.push(toDocHref(item.path, basePath));
142
+ },
143
+ [basePath, closeSearch, router]
144
+ );
145
+
146
+ const openSearch = useCallback(async () => {
147
+ setOpen(true);
148
+ await loadSearchItems();
149
+ }, [loadSearchItems]);
150
+
151
+ const warmSearch = useCallback(async () => {
152
+ try {
153
+ await loadSearchItems();
154
+ } catch {
155
+ // Ignore warm-up failures and let the explicit open path show the error state.
156
+ }
157
+ }, [loadSearchItems]);
158
+
159
+ const handleQueryChange = useCallback(
160
+ (event: ChangeEvent<HTMLInputElement>) => {
161
+ setQuery(event.target.value);
162
+ },
163
+ []
164
+ );
165
+
166
+ const handleResultClick = useCallback(
167
+ (event: ReactMouseEvent<HTMLButtonElement>) => {
168
+ const index = Number(event.currentTarget.dataset.index);
169
+ const item = filteredItems[index];
170
+ if (!item) {
171
+ return;
172
+ }
173
+
174
+ runSelection(item);
175
+ },
176
+ [filteredItems, runSelection]
177
+ );
178
+
179
+ const handleResultMouseEnter = useCallback(
180
+ (event: ReactMouseEvent<HTMLButtonElement>) => {
181
+ const index = Number(event.currentTarget.dataset.index);
182
+ if (Number.isNaN(index)) {
183
+ return;
184
+ }
185
+
186
+ setActiveIndex(index);
187
+ },
188
+ []
189
+ );
190
+
191
+ useEffect(() => {
192
+ if (!open) {
193
+ return;
194
+ }
195
+
196
+ inputRef.current?.focus();
197
+ const previousOverflow = document.body.style.overflow;
198
+ document.body.style.overflow = "hidden";
199
+
200
+ return () => {
201
+ document.body.style.overflow = previousOverflow;
202
+ };
203
+ }, [open]);
204
+
205
+ useEffect(() => {
206
+ setActiveIndex(0);
207
+ }, [deferredQuery, open]);
208
+
209
+ useEffect(() => {
210
+ const handleKeydown = async (event: KeyboardEvent) => {
211
+ if (
212
+ (event.key === "k" && (event.metaKey || event.ctrlKey)) ||
213
+ event.key === "/"
214
+ ) {
215
+ if (isEditableTarget(event.target)) {
216
+ return;
217
+ }
218
+
219
+ event.preventDefault();
220
+ if (open) {
221
+ closeSearch();
222
+ return;
223
+ }
224
+ await openSearch();
225
+ return;
226
+ }
227
+
228
+ if (!open || event.key !== "Escape") {
229
+ return;
230
+ }
231
+
232
+ event.preventDefault();
233
+ closeSearch();
234
+ };
235
+
236
+ document.addEventListener("keydown", handleKeydown);
237
+ return () => document.removeEventListener("keydown", handleKeydown);
238
+ }, [closeSearch, open, openSearch]);
239
+
240
+ const handleDialogKeyDown = useCallback(
241
+ (event: ReactKeyboardEvent<HTMLDivElement>) => {
242
+ if (event.key === "ArrowDown") {
243
+ event.preventDefault();
244
+ setActiveIndex((current) =>
245
+ getWrappedNextIndex(current, filteredItems.length)
246
+ );
247
+ return;
248
+ }
249
+
250
+ if (event.key === "ArrowUp") {
251
+ event.preventDefault();
252
+ setActiveIndex((current) =>
253
+ getWrappedPrevIndex(current, filteredItems.length)
254
+ );
255
+ return;
256
+ }
257
+
258
+ if (event.key === "Enter") {
259
+ const activeItem = filteredItems[activeIndex];
260
+ if (!activeItem) {
261
+ return;
262
+ }
263
+
264
+ event.preventDefault();
265
+ runSelection(activeItem);
266
+ }
267
+ },
268
+ [activeIndex, filteredItems, runSelection]
269
+ );
270
+
271
+ return (
272
+ <>
273
+ <button
274
+ aria-label="Search documentation"
275
+ className="inline-flex size-8 items-center justify-center rounded-md hover:bg-accent hover:text-accent-foreground md:hidden"
276
+ onClick={openSearch}
277
+ onFocus={warmSearch}
278
+ onMouseEnter={warmSearch}
279
+ type="button"
280
+ >
281
+ <SearchIcon className="size-4.5" />
282
+ </button>
283
+ <button
284
+ className="relative hidden h-8 w-full items-center justify-start rounded-lg border border-border bg-muted/50 pl-3 text-sm font-normal text-foreground shadow-none transition-colors hover:bg-muted/80 md:flex md:w-48 lg:w-56 xl:w-64 dark:bg-card"
285
+ onClick={openSearch}
286
+ onFocus={warmSearch}
287
+ onMouseEnter={warmSearch}
288
+ type="button"
289
+ >
290
+ <span className="hidden lg:inline-flex">Search documentation...</span>
291
+ <span className="inline-flex lg:hidden">Search...</span>
292
+ <span className="ml-auto hidden pr-3 text-[11px] text-muted-foreground sm:inline-flex">
293
+ Cmd K
294
+ </span>
295
+ </button>
296
+ {open ? (
297
+ <div className="fixed inset-0 z-50">
298
+ <button
299
+ aria-label="Close search"
300
+ className="absolute inset-0 bg-background/80 backdrop-blur-sm"
301
+ onClick={closeSearch}
302
+ type="button"
303
+ />
304
+ <div
305
+ aria-modal="true"
306
+ className="relative mx-auto mt-[10vh] flex w-[calc(100%-2rem)] max-w-2xl flex-col overflow-hidden rounded-2xl border border-border bg-background shadow-2xl"
307
+ onKeyDown={handleDialogKeyDown}
308
+ role="dialog"
309
+ >
310
+ <div className="flex items-center gap-3 border-b border-border px-4 py-3">
311
+ <SearchIcon className="size-4 text-muted-foreground" />
312
+ <input
313
+ aria-label="Search documentation"
314
+ className="w-full bg-transparent text-sm text-foreground outline-none placeholder:text-muted-foreground"
315
+ onChange={handleQueryChange}
316
+ placeholder="Search docs..."
317
+ ref={inputRef}
318
+ type="text"
319
+ value={query}
320
+ />
321
+ <button
322
+ className="rounded-md border border-border px-2 py-1 text-[11px] text-muted-foreground hover:bg-accent hover:text-foreground"
323
+ onClick={closeSearch}
324
+ type="button"
325
+ >
326
+ Esc
327
+ </button>
328
+ </div>
329
+ <div className="max-h-[min(70vh,32rem)] overflow-y-auto p-2">
330
+ {status === "loading" ? (
331
+ <div className="px-3 py-10 text-center text-sm text-muted-foreground">
332
+ Loading search index...
333
+ </div>
334
+ ) : null}
335
+ {status === "error" ? (
336
+ <div className="px-3 py-10 text-center text-sm text-muted-foreground">
337
+ Search is temporarily unavailable.
338
+ </div>
339
+ ) : null}
340
+ {status === "ready" && filteredItems.length === 0 ? (
341
+ <div className="px-3 py-10 text-center text-sm text-muted-foreground">
342
+ No results found.
343
+ </div>
344
+ ) : null}
345
+ {status === "ready" && filteredItems.length > 0 ? (
346
+ <div className="grid gap-1">
347
+ {filteredItems.map((item, index) => {
348
+ const isActive = index === activeIndex;
349
+
350
+ return (
351
+ <button
352
+ className={`grid gap-1 rounded-xl px-3 py-2 text-left transition-colors ${
353
+ isActive
354
+ ? "bg-accent text-foreground"
355
+ : "text-muted-foreground hover:bg-accent/70 hover:text-foreground"
356
+ }`}
357
+ data-index={index}
358
+ key={`${item.path}-${item.href ?? "internal"}`}
359
+ onClick={handleResultClick}
360
+ onMouseEnter={handleResultMouseEnter}
361
+ type="button"
362
+ >
363
+ <span className="text-sm font-medium text-foreground">
364
+ {item.title}
365
+ </span>
366
+ <span className="text-xs">
367
+ {item.href ?? toDocHref(item.path, basePath)}
368
+ </span>
369
+ </button>
370
+ );
371
+ })}
372
+ </div>
373
+ ) : null}
374
+ </div>
375
+ </div>
376
+ </div>
377
+ ) : null}
378
+ </>
379
+ );
380
+ };
@@ -0,0 +1,26 @@
1
+ "use client";
2
+
3
+ import { Separator as SeparatorPrimitive } from "radix-ui";
4
+ import * as React from "react";
5
+
6
+ import { cn } from "@/lib/utils";
7
+
8
+ const Separator = ({
9
+ className,
10
+ orientation = "horizontal",
11
+ decorative = true,
12
+ ...props
13
+ }: React.ComponentProps<typeof SeparatorPrimitive.Root>) => (
14
+ <SeparatorPrimitive.Root
15
+ data-slot="separator"
16
+ decorative={decorative}
17
+ orientation={orientation}
18
+ className={cn(
19
+ "shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
20
+ className
21
+ )}
22
+ {...props}
23
+ />
24
+ );
25
+
26
+ export { Separator };
@@ -0,0 +1,104 @@
1
+ "use client";
2
+
3
+ import { XIcon } from "blode-icons-react";
4
+ import { Dialog as SheetPrimitive } from "radix-ui";
5
+ import * as React from "react";
6
+
7
+ import { cn } from "@/lib/utils";
8
+
9
+ const Sheet = ({
10
+ ...props
11
+ }: React.ComponentProps<typeof SheetPrimitive.Root>) => (
12
+ <SheetPrimitive.Root data-slot="sheet" {...props} />
13
+ );
14
+
15
+ const SheetPortal = ({
16
+ ...props
17
+ }: React.ComponentProps<typeof SheetPrimitive.Portal>) => (
18
+ <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />
19
+ );
20
+
21
+ const SheetOverlay = ({
22
+ className,
23
+ ...props
24
+ }: React.ComponentProps<typeof SheetPrimitive.Overlay>) => (
25
+ <SheetPrimitive.Overlay
26
+ data-slot="sheet-overlay"
27
+ className={cn(
28
+ "fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",
29
+ className
30
+ )}
31
+ {...props}
32
+ />
33
+ );
34
+
35
+ const SheetContent = ({
36
+ className,
37
+ children,
38
+ side = "right",
39
+ showCloseButton = true,
40
+ ...props
41
+ }: React.ComponentProps<typeof SheetPrimitive.Content> & {
42
+ side?: "top" | "right" | "bottom" | "left";
43
+ showCloseButton?: boolean;
44
+ }) => (
45
+ <SheetPortal>
46
+ <SheetOverlay />
47
+ <SheetPrimitive.Content
48
+ data-slot="sheet-content"
49
+ className={cn(
50
+ "fixed z-50 flex flex-col gap-4 bg-background shadow-lg transition ease-in-out data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:animate-in data-[state=open]:duration-500",
51
+ side === "right" &&
52
+ "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
53
+ side === "left" &&
54
+ "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
55
+ side === "top" &&
56
+ "inset-x-0 top-0 h-auto border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
57
+ side === "bottom" &&
58
+ "inset-x-0 bottom-0 h-auto border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
59
+ className
60
+ )}
61
+ {...props}
62
+ >
63
+ {children}
64
+ {showCloseButton && (
65
+ <SheetPrimitive.Close className="absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none data-[state=open]:bg-secondary">
66
+ <XIcon className="size-4" />
67
+ <span className="sr-only">Close</span>
68
+ </SheetPrimitive.Close>
69
+ )}
70
+ </SheetPrimitive.Content>
71
+ </SheetPortal>
72
+ );
73
+
74
+ const SheetHeader = ({ className, ...props }: React.ComponentProps<"div">) => (
75
+ <div
76
+ data-slot="sheet-header"
77
+ className={cn("flex flex-col gap-1.5 p-4", className)}
78
+ {...props}
79
+ />
80
+ );
81
+
82
+ const SheetTitle = ({
83
+ className,
84
+ ...props
85
+ }: React.ComponentProps<typeof SheetPrimitive.Title>) => (
86
+ <SheetPrimitive.Title
87
+ data-slot="sheet-title"
88
+ className={cn("font-semibold text-foreground", className)}
89
+ {...props}
90
+ />
91
+ );
92
+
93
+ const SheetDescription = ({
94
+ className,
95
+ ...props
96
+ }: React.ComponentProps<typeof SheetPrimitive.Description>) => (
97
+ <SheetPrimitive.Description
98
+ data-slot="sheet-description"
99
+ className={cn("text-sm text-muted-foreground", className)}
100
+ {...props}
101
+ />
102
+ );
103
+
104
+ export { Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription };