@hypoth-ui/docs-renderer-next 0.1.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.
package/dist/index.js ADDED
@@ -0,0 +1,751 @@
1
+ // src/index.ts
2
+ export * from "@hypoth-ui/docs-core";
3
+
4
+ // components/search/search-input.tsx
5
+ import { useCallback, useEffect, useRef, useState } from "react";
6
+ import { useRouter } from "next/navigation";
7
+ function SearchInput({
8
+ placeholder = "Search documentation...",
9
+ className = "",
10
+ enabled = true
11
+ }) {
12
+ const [query, setQuery] = useState("");
13
+ const [results, setResults] = useState([]);
14
+ const [isOpen, setIsOpen] = useState(false);
15
+ const [isLoading, setIsLoading] = useState(false);
16
+ const [selectedIndex, setSelectedIndex] = useState(-1);
17
+ const inputRef = useRef(null);
18
+ const containerRef = useRef(null);
19
+ const router = useRouter();
20
+ useEffect(() => {
21
+ if (!query.trim()) {
22
+ setResults([]);
23
+ return;
24
+ }
25
+ const abortController = new AbortController();
26
+ const timeoutId = setTimeout(async () => {
27
+ setIsLoading(true);
28
+ try {
29
+ const response = await fetch(`/api/search?q=${encodeURIComponent(query)}&limit=10`, {
30
+ signal: abortController.signal
31
+ });
32
+ if (response.ok) {
33
+ const data = await response.json();
34
+ setResults(data.results);
35
+ }
36
+ } catch (error) {
37
+ if (error.name !== "AbortError") {
38
+ console.error("Search error:", error);
39
+ }
40
+ } finally {
41
+ setIsLoading(false);
42
+ }
43
+ }, 200);
44
+ return () => {
45
+ clearTimeout(timeoutId);
46
+ abortController.abort();
47
+ };
48
+ }, [query]);
49
+ useEffect(() => {
50
+ const handleKeyDown2 = (e) => {
51
+ if (e.key === "/" && !isInputFocused()) {
52
+ e.preventDefault();
53
+ inputRef.current?.focus();
54
+ }
55
+ };
56
+ function isInputFocused() {
57
+ const active = document.activeElement;
58
+ return active instanceof HTMLInputElement || active instanceof HTMLTextAreaElement || active?.getAttribute("contenteditable") === "true";
59
+ }
60
+ document.addEventListener("keydown", handleKeyDown2);
61
+ return () => document.removeEventListener("keydown", handleKeyDown2);
62
+ }, []);
63
+ useEffect(() => {
64
+ const handleClickOutside = (e) => {
65
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
66
+ setIsOpen(false);
67
+ }
68
+ };
69
+ document.addEventListener("mousedown", handleClickOutside);
70
+ return () => document.removeEventListener("mousedown", handleClickOutside);
71
+ }, []);
72
+ const handleKeyDown = useCallback(
73
+ (e) => {
74
+ switch (e.key) {
75
+ case "Escape":
76
+ setIsOpen(false);
77
+ setQuery("");
78
+ inputRef.current?.blur();
79
+ break;
80
+ case "ArrowDown":
81
+ e.preventDefault();
82
+ setSelectedIndex((prev) => Math.min(prev + 1, results.length - 1));
83
+ break;
84
+ case "ArrowUp":
85
+ e.preventDefault();
86
+ setSelectedIndex((prev) => Math.max(prev - 1, -1));
87
+ break;
88
+ case "Enter":
89
+ if (selectedIndex >= 0 && results[selectedIndex]) {
90
+ e.preventDefault();
91
+ navigateToResult(results[selectedIndex]);
92
+ }
93
+ break;
94
+ }
95
+ },
96
+ [results, selectedIndex]
97
+ );
98
+ const navigateToResult = useCallback(
99
+ (result) => {
100
+ setIsOpen(false);
101
+ setQuery("");
102
+ router.push(result.url);
103
+ },
104
+ [router]
105
+ );
106
+ const componentResults = results.filter((r) => r.type === "component");
107
+ const guideResults = results.filter((r) => r.type === "guide");
108
+ if (!enabled) {
109
+ return null;
110
+ }
111
+ return /* @__PURE__ */ React.createElement("div", { ref: containerRef, className: `search-input ${className}` }, /* @__PURE__ */ React.createElement("div", { className: "search-input__wrapper" }, /* @__PURE__ */ React.createElement("span", { className: "search-input__icon", "aria-hidden": "true" }, isLoading ? /* @__PURE__ */ React.createElement(
112
+ "svg",
113
+ {
114
+ width: "16",
115
+ height: "16",
116
+ viewBox: "0 0 16 16",
117
+ fill: "none",
118
+ className: "search-input__spinner",
119
+ "aria-hidden": "true"
120
+ },
121
+ /* @__PURE__ */ React.createElement(
122
+ "circle",
123
+ {
124
+ cx: "8",
125
+ cy: "8",
126
+ r: "6",
127
+ stroke: "currentColor",
128
+ strokeWidth: "2",
129
+ strokeDasharray: "32",
130
+ strokeDashoffset: "8"
131
+ }
132
+ )
133
+ ) : /* @__PURE__ */ React.createElement(
134
+ "svg",
135
+ {
136
+ width: "16",
137
+ height: "16",
138
+ viewBox: "0 0 16 16",
139
+ fill: "none",
140
+ stroke: "currentColor",
141
+ strokeWidth: "2",
142
+ "aria-hidden": "true"
143
+ },
144
+ /* @__PURE__ */ React.createElement("circle", { cx: "6.5", cy: "6.5", r: "5.5" }),
145
+ /* @__PURE__ */ React.createElement("path", { d: "M10.5 10.5L15 15" })
146
+ )), /* @__PURE__ */ React.createElement(
147
+ "input",
148
+ {
149
+ ref: inputRef,
150
+ type: "search",
151
+ className: "search-input__field",
152
+ placeholder,
153
+ value: query,
154
+ onChange: (e) => {
155
+ setQuery(e.target.value);
156
+ setSelectedIndex(-1);
157
+ },
158
+ onFocus: () => setIsOpen(true),
159
+ onKeyDown: handleKeyDown,
160
+ "aria-label": "Search documentation",
161
+ "aria-expanded": isOpen && results.length > 0,
162
+ "aria-controls": "search-results",
163
+ "aria-activedescendant": selectedIndex >= 0 ? `search-result-${selectedIndex}` : void 0,
164
+ role: "combobox",
165
+ autoComplete: "off"
166
+ }
167
+ ), /* @__PURE__ */ React.createElement("span", { className: "search-input__shortcut", "aria-hidden": "true" }, "/")), isOpen && query.length > 0 && /* @__PURE__ */ React.createElement(
168
+ "div",
169
+ {
170
+ id: "search-results",
171
+ className: "search-input__dropdown",
172
+ role: "listbox",
173
+ "aria-label": "Search results",
174
+ tabIndex: -1
175
+ },
176
+ results.length === 0 && !isLoading && /* @__PURE__ */ React.createElement("div", { className: "search-input__empty" }, "No results found for \u201C", query, "\u201D"),
177
+ componentResults.length > 0 && /* @__PURE__ */ React.createElement("div", { className: "search-input__group" }, /* @__PURE__ */ React.createElement("div", { className: "search-input__group-label" }, "Components"), componentResults.map((result, idx) => {
178
+ const globalIndex = idx;
179
+ return /* @__PURE__ */ React.createElement(
180
+ "button",
181
+ {
182
+ type: "button",
183
+ key: result.id,
184
+ id: `search-result-${globalIndex}`,
185
+ className: `search-input__result ${selectedIndex === globalIndex ? "search-input__result--selected" : ""}`,
186
+ onClick: () => navigateToResult(result),
187
+ onMouseEnter: () => setSelectedIndex(globalIndex),
188
+ role: "option",
189
+ "aria-selected": selectedIndex === globalIndex
190
+ },
191
+ /* @__PURE__ */ React.createElement("span", { className: "search-input__result-icon" }, /* @__PURE__ */ React.createElement("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "currentColor", "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("rect", { x: "1", y: "1", width: "5", height: "5", rx: "1" }), /* @__PURE__ */ React.createElement("rect", { x: "8", y: "1", width: "5", height: "5", rx: "1" }), /* @__PURE__ */ React.createElement("rect", { x: "1", y: "8", width: "5", height: "5", rx: "1" }), /* @__PURE__ */ React.createElement("rect", { x: "8", y: "8", width: "5", height: "5", rx: "1" }))),
192
+ /* @__PURE__ */ React.createElement("span", { className: "search-input__result-content" }, /* @__PURE__ */ React.createElement("span", { className: "search-input__result-title" }, result.title), result.description && /* @__PURE__ */ React.createElement("span", { className: "search-input__result-description" }, result.description)),
193
+ result.status && /* @__PURE__ */ React.createElement("span", { className: `search-input__result-status search-input__result-status--${result.status}` }, result.status)
194
+ );
195
+ })),
196
+ guideResults.length > 0 && /* @__PURE__ */ React.createElement("div", { className: "search-input__group" }, /* @__PURE__ */ React.createElement("div", { className: "search-input__group-label" }, "Guides"), guideResults.map((result, idx) => {
197
+ const globalIndex = componentResults.length + idx;
198
+ return /* @__PURE__ */ React.createElement(
199
+ "button",
200
+ {
201
+ type: "button",
202
+ key: result.id,
203
+ id: `search-result-${globalIndex}`,
204
+ className: `search-input__result ${selectedIndex === globalIndex ? "search-input__result--selected" : ""}`,
205
+ onClick: () => navigateToResult(result),
206
+ onMouseEnter: () => setSelectedIndex(globalIndex),
207
+ role: "option",
208
+ "aria-selected": selectedIndex === globalIndex
209
+ },
210
+ /* @__PURE__ */ React.createElement("span", { className: "search-input__result-icon" }, /* @__PURE__ */ React.createElement("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "currentColor", "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("path", { d: "M2 2h10v10H2V2zm1 1v8h8V3H3z" }), /* @__PURE__ */ React.createElement("path", { d: "M4 5h6M4 7h6M4 9h4", stroke: "currentColor", strokeWidth: "1" }))),
211
+ /* @__PURE__ */ React.createElement("span", { className: "search-input__result-content" }, /* @__PURE__ */ React.createElement("span", { className: "search-input__result-title" }, result.title), result.description && /* @__PURE__ */ React.createElement("span", { className: "search-input__result-description" }, result.description))
212
+ );
213
+ }))
214
+ ), /* @__PURE__ */ React.createElement("style", { jsx: true }, `
215
+ .search-input {
216
+ position: relative;
217
+ }
218
+
219
+ .search-input__wrapper {
220
+ display: flex;
221
+ align-items: center;
222
+ gap: 0.5rem;
223
+ padding: 0.5rem 0.75rem;
224
+ background: var(--ds-color-background-subtle, #f5f5f5);
225
+ border: 1px solid var(--ds-color-border-default, #e5e5e5);
226
+ border-radius: 6px;
227
+ transition: border-color 0.15s, box-shadow 0.15s;
228
+ }
229
+
230
+ .search-input__wrapper:focus-within {
231
+ border-color: var(--ds-brand-primary, #0066cc);
232
+ box-shadow: 0 0 0 3px rgba(0, 102, 204, 0.1);
233
+ }
234
+
235
+ .search-input__icon {
236
+ color: var(--ds-color-foreground-muted, #666);
237
+ flex-shrink: 0;
238
+ }
239
+
240
+ .search-input__spinner {
241
+ animation: spin 1s linear infinite;
242
+ }
243
+
244
+ @keyframes spin {
245
+ from { transform: rotate(0deg); }
246
+ to { transform: rotate(360deg); }
247
+ }
248
+
249
+ .search-input__field {
250
+ flex: 1;
251
+ border: none;
252
+ background: transparent;
253
+ font-size: 0.875rem;
254
+ color: var(--ds-color-foreground-default, #1a1a1a);
255
+ outline: none;
256
+ min-width: 150px;
257
+ }
258
+
259
+ .search-input__field::placeholder {
260
+ color: var(--ds-color-foreground-muted, #666);
261
+ }
262
+
263
+ .search-input__shortcut {
264
+ padding: 0.125rem 0.375rem;
265
+ font-size: 0.75rem;
266
+ font-family: monospace;
267
+ background: var(--ds-color-background-surface, #fff);
268
+ border: 1px solid var(--ds-color-border-default, #e5e5e5);
269
+ border-radius: 4px;
270
+ color: var(--ds-color-foreground-muted, #666);
271
+ }
272
+
273
+ .search-input__dropdown {
274
+ position: absolute;
275
+ top: 100%;
276
+ left: 0;
277
+ right: 0;
278
+ margin-top: 0.5rem;
279
+ background: var(--ds-color-background-surface, #fff);
280
+ border: 1px solid var(--ds-color-border-default, #e5e5e5);
281
+ border-radius: 8px;
282
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
283
+ z-index: 1000;
284
+ max-height: 400px;
285
+ overflow-y: auto;
286
+ }
287
+
288
+ .search-input__empty {
289
+ padding: 1rem;
290
+ color: var(--ds-color-foreground-muted, #666);
291
+ font-size: 0.875rem;
292
+ text-align: center;
293
+ }
294
+
295
+ .search-input__group {
296
+ padding: 0.5rem 0;
297
+ }
298
+
299
+ .search-input__group:not(:last-child) {
300
+ border-bottom: 1px solid var(--ds-color-border-default, #e5e5e5);
301
+ }
302
+
303
+ .search-input__group-label {
304
+ padding: 0.25rem 0.75rem;
305
+ font-size: 0.75rem;
306
+ font-weight: 600;
307
+ text-transform: uppercase;
308
+ letter-spacing: 0.05em;
309
+ color: var(--ds-color-foreground-muted, #666);
310
+ }
311
+
312
+ .search-input__result {
313
+ display: flex;
314
+ align-items: flex-start;
315
+ gap: 0.75rem;
316
+ width: 100%;
317
+ padding: 0.5rem 0.75rem;
318
+ background: transparent;
319
+ border: none;
320
+ cursor: pointer;
321
+ text-align: left;
322
+ }
323
+
324
+ .search-input__result:hover,
325
+ .search-input__result--selected {
326
+ background: var(--ds-color-background-subtle, #f5f5f5);
327
+ }
328
+
329
+ .search-input__result-icon {
330
+ flex-shrink: 0;
331
+ width: 20px;
332
+ height: 20px;
333
+ display: flex;
334
+ align-items: center;
335
+ justify-content: center;
336
+ color: var(--ds-color-foreground-muted, #666);
337
+ }
338
+
339
+ .search-input__result-content {
340
+ flex: 1;
341
+ min-width: 0;
342
+ }
343
+
344
+ .search-input__result-title {
345
+ display: block;
346
+ font-size: 0.875rem;
347
+ font-weight: 500;
348
+ color: var(--ds-color-foreground-default, #1a1a1a);
349
+ }
350
+
351
+ .search-input__result-description {
352
+ display: block;
353
+ font-size: 0.75rem;
354
+ color: var(--ds-color-foreground-muted, #666);
355
+ white-space: nowrap;
356
+ overflow: hidden;
357
+ text-overflow: ellipsis;
358
+ }
359
+
360
+ .search-input__result-status {
361
+ flex-shrink: 0;
362
+ padding: 0.125rem 0.375rem;
363
+ font-size: 0.625rem;
364
+ font-weight: 500;
365
+ text-transform: uppercase;
366
+ border-radius: 4px;
367
+ }
368
+
369
+ .search-input__result-status--stable {
370
+ background: var(--ds-color-success-subtle, #dcfce7);
371
+ color: var(--ds-color-success, #16a34a);
372
+ }
373
+
374
+ .search-input__result-status--beta {
375
+ background: var(--ds-color-warning-subtle, #fef3c7);
376
+ color: var(--ds-color-warning, #d97706);
377
+ }
378
+
379
+ .search-input__result-status--experimental {
380
+ background: var(--ds-color-info-subtle, #dbeafe);
381
+ color: var(--ds-color-info, #2563eb);
382
+ }
383
+
384
+ .search-input__result-status--deprecated {
385
+ background: var(--ds-color-error-subtle, #fee2e2);
386
+ color: var(--ds-color-error, #dc2626);
387
+ }
388
+ `));
389
+ }
390
+
391
+ // components/live-example.tsx
392
+ import { useState as useState2 } from "react";
393
+ function LiveExample({
394
+ children,
395
+ code,
396
+ language = "tsx",
397
+ title,
398
+ description,
399
+ defaultShowCode = false,
400
+ variants,
401
+ className = ""
402
+ }) {
403
+ const [showCode, setShowCode] = useState2(defaultShowCode);
404
+ const [copied, setCopied] = useState2(false);
405
+ const [activeVariant, setActiveVariant] = useState2(0);
406
+ const currentCode = variants ? variants[activeVariant]?.code || code : code;
407
+ const currentPreview = variants ? variants[activeVariant]?.preview || children : children;
408
+ const handleCopy = async () => {
409
+ try {
410
+ await navigator.clipboard.writeText(currentCode);
411
+ setCopied(true);
412
+ setTimeout(() => setCopied(false), 2e3);
413
+ } catch (error) {
414
+ console.error("Failed to copy:", error);
415
+ }
416
+ };
417
+ return /* @__PURE__ */ React.createElement("div", { className: `live-example ${className}` }, title && /* @__PURE__ */ React.createElement("div", { className: "live-example__header" }, /* @__PURE__ */ React.createElement("h3", { className: "live-example__title" }, title), description && /* @__PURE__ */ React.createElement("p", { className: "live-example__description" }, description)), variants && variants.length > 1 && /* @__PURE__ */ React.createElement("div", { className: "live-example__variants", role: "tablist" }, variants.map((variant, index) => /* @__PURE__ */ React.createElement(
418
+ "button",
419
+ {
420
+ key: variant.name,
421
+ type: "button",
422
+ className: `live-example__variant-tab ${activeVariant === index ? "live-example__variant-tab--active" : ""}`,
423
+ onClick: () => setActiveVariant(index),
424
+ role: "tab",
425
+ "aria-selected": activeVariant === index
426
+ },
427
+ variant.name
428
+ ))), /* @__PURE__ */ React.createElement("div", { className: "live-example__preview" }, /* @__PURE__ */ React.createElement("div", { className: "live-example__preview-content" }, currentPreview)), /* @__PURE__ */ React.createElement("div", { className: "live-example__controls" }, /* @__PURE__ */ React.createElement(
429
+ "button",
430
+ {
431
+ type: "button",
432
+ className: `live-example__toggle ${showCode ? "live-example__toggle--active" : ""}`,
433
+ onClick: () => setShowCode(!showCode),
434
+ "aria-expanded": showCode,
435
+ "aria-controls": "live-example-code"
436
+ },
437
+ /* @__PURE__ */ React.createElement(
438
+ "svg",
439
+ {
440
+ width: "16",
441
+ height: "16",
442
+ viewBox: "0 0 16 16",
443
+ fill: "none",
444
+ stroke: "currentColor",
445
+ strokeWidth: "1.5",
446
+ "aria-hidden": "true"
447
+ },
448
+ /* @__PURE__ */ React.createElement("path", { d: "M10.5 4.5L14 8l-3.5 3.5M5.5 4.5L2 8l3.5 3.5" })
449
+ ),
450
+ /* @__PURE__ */ React.createElement("span", null, showCode ? "Hide code" : "Show code")
451
+ ), showCode && /* @__PURE__ */ React.createElement("button", { type: "button", className: "live-example__copy", onClick: handleCopy, "aria-label": "Copy code" }, copied ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
452
+ "svg",
453
+ {
454
+ width: "16",
455
+ height: "16",
456
+ viewBox: "0 0 16 16",
457
+ fill: "none",
458
+ stroke: "currentColor",
459
+ strokeWidth: "2",
460
+ "aria-hidden": "true"
461
+ },
462
+ /* @__PURE__ */ React.createElement("path", { d: "M3.5 8l3 3 6-6" })
463
+ ), /* @__PURE__ */ React.createElement("span", null, "Copied!")) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
464
+ "svg",
465
+ {
466
+ width: "16",
467
+ height: "16",
468
+ viewBox: "0 0 16 16",
469
+ fill: "none",
470
+ stroke: "currentColor",
471
+ strokeWidth: "1.5",
472
+ "aria-hidden": "true"
473
+ },
474
+ /* @__PURE__ */ React.createElement("rect", { x: "5", y: "5", width: "9", height: "9", rx: "1" }),
475
+ /* @__PURE__ */ React.createElement("path", { d: "M11 5V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h2" })
476
+ ), /* @__PURE__ */ React.createElement("span", null, "Copy")))), showCode && /* @__PURE__ */ React.createElement("div", { id: "live-example-code", className: "live-example__code" }, /* @__PURE__ */ React.createElement("pre", { className: `language-${language}` }, /* @__PURE__ */ React.createElement("code", null, currentCode))), /* @__PURE__ */ React.createElement("style", { jsx: true }, `
477
+ .live-example {
478
+ border: 1px solid var(--ds-color-border-default, #e5e5e5);
479
+ border-radius: 8px;
480
+ overflow: hidden;
481
+ margin: 1.5rem 0;
482
+ }
483
+
484
+ .live-example__header {
485
+ padding: 1rem;
486
+ border-bottom: 1px solid var(--ds-color-border-default, #e5e5e5);
487
+ background: var(--ds-color-background-subtle, #f9f9f9);
488
+ }
489
+
490
+ .live-example__title {
491
+ font-size: 0.875rem;
492
+ font-weight: 600;
493
+ margin: 0;
494
+ color: var(--ds-color-foreground-default, #1a1a1a);
495
+ }
496
+
497
+ .live-example__description {
498
+ font-size: 0.75rem;
499
+ margin: 0.25rem 0 0;
500
+ color: var(--ds-color-foreground-muted, #666);
501
+ }
502
+
503
+ .live-example__variants {
504
+ display: flex;
505
+ gap: 0;
506
+ border-bottom: 1px solid var(--ds-color-border-default, #e5e5e5);
507
+ background: var(--ds-color-background-subtle, #f9f9f9);
508
+ }
509
+
510
+ .live-example__variant-tab {
511
+ padding: 0.5rem 1rem;
512
+ font-size: 0.75rem;
513
+ font-weight: 500;
514
+ color: var(--ds-color-foreground-muted, #666);
515
+ background: transparent;
516
+ border: none;
517
+ border-bottom: 2px solid transparent;
518
+ cursor: pointer;
519
+ transition: color 0.15s, border-color 0.15s;
520
+ }
521
+
522
+ .live-example__variant-tab:hover {
523
+ color: var(--ds-color-foreground-default, #1a1a1a);
524
+ }
525
+
526
+ .live-example__variant-tab--active {
527
+ color: var(--ds-brand-primary, #0066cc);
528
+ border-bottom-color: var(--ds-brand-primary, #0066cc);
529
+ }
530
+
531
+ .live-example__preview {
532
+ padding: 1.5rem;
533
+ background: var(--ds-color-background-surface, #fff);
534
+ min-height: 80px;
535
+ display: flex;
536
+ align-items: center;
537
+ justify-content: center;
538
+ }
539
+
540
+ .live-example__preview-content {
541
+ width: 100%;
542
+ }
543
+
544
+ .live-example__controls {
545
+ display: flex;
546
+ align-items: center;
547
+ gap: 0.5rem;
548
+ padding: 0.5rem 1rem;
549
+ border-top: 1px solid var(--ds-color-border-default, #e5e5e5);
550
+ background: var(--ds-color-background-subtle, #f9f9f9);
551
+ }
552
+
553
+ .live-example__toggle,
554
+ .live-example__copy {
555
+ display: inline-flex;
556
+ align-items: center;
557
+ gap: 0.375rem;
558
+ padding: 0.375rem 0.75rem;
559
+ font-size: 0.75rem;
560
+ font-weight: 500;
561
+ color: var(--ds-color-foreground-muted, #666);
562
+ background: var(--ds-color-background-surface, #fff);
563
+ border: 1px solid var(--ds-color-border-default, #e5e5e5);
564
+ border-radius: 4px;
565
+ cursor: pointer;
566
+ transition: color 0.15s, border-color 0.15s;
567
+ }
568
+
569
+ .live-example__toggle:hover,
570
+ .live-example__copy:hover {
571
+ color: var(--ds-color-foreground-default, #1a1a1a);
572
+ border-color: var(--ds-color-border-strong, #ccc);
573
+ }
574
+
575
+ .live-example__toggle--active {
576
+ color: var(--ds-brand-primary, #0066cc);
577
+ border-color: var(--ds-brand-primary, #0066cc);
578
+ background: rgba(0, 102, 204, 0.05);
579
+ }
580
+
581
+ .live-example__copy {
582
+ margin-left: auto;
583
+ }
584
+
585
+ .live-example__code {
586
+ border-top: 1px solid var(--ds-color-border-default, #e5e5e5);
587
+ background: var(--ds-color-background-code, #1e1e1e);
588
+ overflow-x: auto;
589
+ }
590
+
591
+ .live-example__code pre {
592
+ margin: 0;
593
+ padding: 1rem;
594
+ font-size: 0.8125rem;
595
+ line-height: 1.6;
596
+ font-family: "SF Mono", Menlo, Monaco, "Courier New", monospace;
597
+ }
598
+
599
+ .live-example__code code {
600
+ color: var(--ds-color-code-text, #d4d4d4);
601
+ white-space: pre;
602
+ }
603
+
604
+ /* Dark mode adjustments */
605
+ :global([data-theme="dark"]) .live-example__preview {
606
+ background: var(--ds-color-background-surface-dark, #1a1a1a);
607
+ }
608
+ `));
609
+ }
610
+ function CodeBlock({
611
+ code,
612
+ language = "tsx",
613
+ filename,
614
+ showLineNumbers = false,
615
+ highlightLines = [],
616
+ className = ""
617
+ }) {
618
+ const [copied, setCopied] = useState2(false);
619
+ const handleCopy = async () => {
620
+ try {
621
+ await navigator.clipboard.writeText(code);
622
+ setCopied(true);
623
+ setTimeout(() => setCopied(false), 2e3);
624
+ } catch (error) {
625
+ console.error("Failed to copy:", error);
626
+ }
627
+ };
628
+ const lines = code.split("\n");
629
+ return /* @__PURE__ */ React.createElement("div", { className: `code-block ${className}` }, filename && /* @__PURE__ */ React.createElement("div", { className: "code-block__header" }, /* @__PURE__ */ React.createElement("span", { className: "code-block__filename" }, filename), /* @__PURE__ */ React.createElement("button", { type: "button", className: "code-block__copy", onClick: handleCopy, "aria-label": "Copy code" }, copied ? /* @__PURE__ */ React.createElement(
630
+ "svg",
631
+ {
632
+ width: "14",
633
+ height: "14",
634
+ viewBox: "0 0 16 16",
635
+ fill: "none",
636
+ stroke: "currentColor",
637
+ strokeWidth: "2",
638
+ "aria-hidden": "true"
639
+ },
640
+ /* @__PURE__ */ React.createElement("path", { d: "M3.5 8l3 3 6-6" })
641
+ ) : /* @__PURE__ */ React.createElement(
642
+ "svg",
643
+ {
644
+ width: "14",
645
+ height: "14",
646
+ viewBox: "0 0 16 16",
647
+ fill: "none",
648
+ stroke: "currentColor",
649
+ strokeWidth: "1.5",
650
+ "aria-hidden": "true"
651
+ },
652
+ /* @__PURE__ */ React.createElement("rect", { x: "5", y: "5", width: "9", height: "9", rx: "1" }),
653
+ /* @__PURE__ */ React.createElement("path", { d: "M11 5V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h2" })
654
+ ))), /* @__PURE__ */ React.createElement("pre", { className: `language-${language}` }, /* @__PURE__ */ React.createElement("code", null, showLineNumbers ? lines.map((line, i) => /* @__PURE__ */ React.createElement(
655
+ "span",
656
+ {
657
+ key: i,
658
+ className: `code-block__line ${highlightLines.includes(i + 1) ? "code-block__line--highlighted" : ""}`
659
+ },
660
+ /* @__PURE__ */ React.createElement("span", { className: "code-block__line-number" }, i + 1),
661
+ /* @__PURE__ */ React.createElement("span", { className: "code-block__line-content" }, line),
662
+ "\n"
663
+ )) : code)), /* @__PURE__ */ React.createElement("style", { jsx: true }, `
664
+ .code-block {
665
+ border-radius: 8px;
666
+ overflow: hidden;
667
+ margin: 1rem 0;
668
+ background: var(--ds-color-background-code, #1e1e1e);
669
+ }
670
+
671
+ .code-block__header {
672
+ display: flex;
673
+ align-items: center;
674
+ justify-content: space-between;
675
+ padding: 0.5rem 1rem;
676
+ background: rgba(255, 255, 255, 0.05);
677
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
678
+ }
679
+
680
+ .code-block__filename {
681
+ font-size: 0.75rem;
682
+ font-family: "SF Mono", Menlo, Monaco, "Courier New", monospace;
683
+ color: var(--ds-color-code-text, #d4d4d4);
684
+ opacity: 0.7;
685
+ }
686
+
687
+ .code-block__copy {
688
+ padding: 0.25rem;
689
+ background: transparent;
690
+ border: none;
691
+ color: var(--ds-color-code-text, #d4d4d4);
692
+ opacity: 0.5;
693
+ cursor: pointer;
694
+ transition: opacity 0.15s;
695
+ }
696
+
697
+ .code-block__copy:hover {
698
+ opacity: 1;
699
+ }
700
+
701
+ .code-block pre {
702
+ margin: 0;
703
+ padding: 1rem;
704
+ font-size: 0.8125rem;
705
+ line-height: 1.6;
706
+ font-family: "SF Mono", Menlo, Monaco, "Courier New", monospace;
707
+ overflow-x: auto;
708
+ }
709
+
710
+ .code-block code {
711
+ color: var(--ds-color-code-text, #d4d4d4);
712
+ }
713
+
714
+ .code-block__line {
715
+ display: flex;
716
+ }
717
+
718
+ .code-block__line--highlighted {
719
+ background: rgba(255, 255, 255, 0.1);
720
+ margin: 0 -1rem;
721
+ padding: 0 1rem;
722
+ }
723
+
724
+ .code-block__line-number {
725
+ display: inline-block;
726
+ width: 2.5rem;
727
+ flex-shrink: 0;
728
+ text-align: right;
729
+ padding-right: 1rem;
730
+ color: var(--ds-color-code-text, #d4d4d4);
731
+ opacity: 0.3;
732
+ user-select: none;
733
+ }
734
+
735
+ .code-block__line-content {
736
+ flex: 1;
737
+ white-space: pre;
738
+ }
739
+ `));
740
+ }
741
+
742
+ // src/index.ts
743
+ var COMPONENTS_PATH = "./components";
744
+ var STYLES_PATH = "./styles";
745
+ export {
746
+ COMPONENTS_PATH,
747
+ CodeBlock,
748
+ LiveExample,
749
+ STYLES_PATH,
750
+ SearchInput
751
+ };