@specglass/theme-default 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.
- package/dist/__tests__/code-tabs.test.d.ts +2 -0
- package/dist/__tests__/code-tabs.test.d.ts.map +1 -0
- package/dist/__tests__/code-tabs.test.js +219 -0
- package/dist/__tests__/code-tabs.test.js.map +1 -0
- package/dist/__tests__/copy-button.test.d.ts +2 -0
- package/dist/__tests__/copy-button.test.d.ts.map +1 -0
- package/dist/__tests__/copy-button.test.js +116 -0
- package/dist/__tests__/copy-button.test.js.map +1 -0
- package/dist/__tests__/search-palette.test.d.ts +2 -0
- package/dist/__tests__/search-palette.test.d.ts.map +1 -0
- package/dist/__tests__/search-palette.test.js +71 -0
- package/dist/__tests__/search-palette.test.js.map +1 -0
- package/dist/__tests__/shiki.test.d.ts +2 -0
- package/dist/__tests__/shiki.test.d.ts.map +1 -0
- package/dist/__tests__/shiki.test.js +37 -0
- package/dist/__tests__/shiki.test.js.map +1 -0
- package/dist/__tests__/theme-css.test.d.ts +2 -0
- package/dist/__tests__/theme-css.test.d.ts.map +1 -0
- package/dist/__tests__/theme-css.test.js +124 -0
- package/dist/__tests__/theme-css.test.js.map +1 -0
- package/dist/__tests__/theme-helpers.test.d.ts +2 -0
- package/dist/__tests__/theme-helpers.test.d.ts.map +1 -0
- package/dist/__tests__/theme-helpers.test.js +81 -0
- package/dist/__tests__/theme-helpers.test.js.map +1 -0
- package/dist/index.d.ts +63 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/islands/CodeTabs.d.ts +21 -0
- package/dist/islands/CodeTabs.d.ts.map +1 -0
- package/dist/islands/CodeTabs.js +125 -0
- package/dist/islands/CodeTabs.js.map +1 -0
- package/dist/islands/CopyButton.d.ts +16 -0
- package/dist/islands/CopyButton.d.ts.map +1 -0
- package/dist/islands/CopyButton.js +54 -0
- package/dist/islands/CopyButton.js.map +1 -0
- package/dist/islands/SearchPalette.d.ts +2 -0
- package/dist/islands/SearchPalette.d.ts.map +1 -0
- package/dist/islands/SearchPalette.js +109 -0
- package/dist/islands/SearchPalette.js.map +1 -0
- package/dist/islands/SearchResults.d.ts +2 -0
- package/dist/islands/SearchResults.d.ts.map +1 -0
- package/dist/islands/SearchResults.js +130 -0
- package/dist/islands/SearchResults.js.map +1 -0
- package/dist/islands/ThemeToggle.d.ts +12 -0
- package/dist/islands/ThemeToggle.d.ts.map +1 -0
- package/dist/islands/ThemeToggle.js +43 -0
- package/dist/islands/ThemeToggle.js.map +1 -0
- package/dist/layouts/DocPage.test.d.ts +2 -0
- package/dist/layouts/DocPage.test.d.ts.map +1 -0
- package/dist/layouts/DocPage.test.js +165 -0
- package/dist/layouts/DocPage.test.js.map +1 -0
- package/dist/lib/utils.d.ts +10 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +13 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/scripts/code-block-enhancer.d.ts +16 -0
- package/dist/scripts/code-block-enhancer.d.ts.map +1 -0
- package/dist/scripts/code-block-enhancer.js +55 -0
- package/dist/scripts/code-block-enhancer.js.map +1 -0
- package/dist/ui/command.d.ts +87 -0
- package/dist/ui/command.d.ts.map +1 -0
- package/dist/ui/command.js +28 -0
- package/dist/ui/command.js.map +1 -0
- package/dist/ui/dialog.d.ts +20 -0
- package/dist/ui/dialog.d.ts.map +1 -0
- package/dist/ui/dialog.js +22 -0
- package/dist/ui/dialog.js.map +1 -0
- package/dist/utils/parse-highlight-range.d.ts +12 -0
- package/dist/utils/parse-highlight-range.d.ts.map +1 -0
- package/dist/utils/parse-highlight-range.js +40 -0
- package/dist/utils/parse-highlight-range.js.map +1 -0
- package/dist/utils/parse-highlight-range.test.d.ts +2 -0
- package/dist/utils/parse-highlight-range.test.d.ts.map +1 -0
- package/dist/utils/parse-highlight-range.test.js +32 -0
- package/dist/utils/parse-highlight-range.test.js.map +1 -0
- package/dist/utils/schema-renderer.d.ts +38 -0
- package/dist/utils/schema-renderer.d.ts.map +1 -0
- package/dist/utils/schema-renderer.js +115 -0
- package/dist/utils/schema-renderer.js.map +1 -0
- package/dist/utils/schema-renderer.test.d.ts +2 -0
- package/dist/utils/schema-renderer.test.d.ts.map +1 -0
- package/dist/utils/schema-renderer.test.js +219 -0
- package/dist/utils/schema-renderer.test.js.map +1 -0
- package/dist/utils/shiki.d.ts +20 -0
- package/dist/utils/shiki.d.ts.map +1 -0
- package/dist/utils/shiki.js +84 -0
- package/dist/utils/shiki.js.map +1 -0
- package/dist/utils/sidebar-helpers.d.ts +10 -0
- package/dist/utils/sidebar-helpers.d.ts.map +1 -0
- package/dist/utils/sidebar-helpers.js +14 -0
- package/dist/utils/sidebar-helpers.js.map +1 -0
- package/dist/utils/theme-css.d.ts +21 -0
- package/dist/utils/theme-css.d.ts.map +1 -0
- package/dist/utils/theme-css.js +77 -0
- package/dist/utils/theme-css.js.map +1 -0
- package/dist/utils/theme-helpers.d.ts +28 -0
- package/dist/utils/theme-helpers.d.ts.map +1 -0
- package/dist/utils/theme-helpers.js +55 -0
- package/dist/utils/theme-helpers.js.map +1 -0
- package/dist/utils/toc-helpers.d.ts +12 -0
- package/dist/utils/toc-helpers.d.ts.map +1 -0
- package/dist/utils/toc-helpers.js +9 -0
- package/dist/utils/toc-helpers.js.map +1 -0
- package/package.json +68 -0
- package/src/components/ApiAuth.astro +116 -0
- package/src/components/ApiEndpoint.astro +75 -0
- package/src/components/ApiNavigation.astro +110 -0
- package/src/components/ApiParameters.astro +204 -0
- package/src/components/ApiResponse.astro +144 -0
- package/src/components/Callout.astro +54 -0
- package/src/components/Card.astro +46 -0
- package/src/components/CodeBlock.astro +142 -0
- package/src/components/CodeBlockGroup.astro +196 -0
- package/src/components/CodeTabs.astro +53 -0
- package/src/components/Footer.astro +41 -0
- package/src/components/Header.astro +80 -0
- package/src/components/Sidebar.astro +117 -0
- package/src/components/TabItem.astro +24 -0
- package/src/components/TableOfContents.astro +111 -0
- package/src/components/Tabs.astro +185 -0
- package/src/islands/CodeTabs.tsx +212 -0
- package/src/islands/CopyButton.tsx +101 -0
- package/src/islands/SearchPalette.tsx +307 -0
- package/src/islands/SearchResults.tsx +301 -0
- package/src/islands/ThemeToggle.tsx +107 -0
- package/src/layouts/ApiReferencePage.astro +239 -0
- package/src/layouts/DocPage.astro +199 -0
- package/src/layouts/DocPage.test.ts +183 -0
- package/src/layouts/LandingPage.astro +143 -0
- package/src/lib/utils.ts +13 -0
- package/src/styles/global.css +241 -0
- package/src/utils/parse-highlight-range.test.ts +40 -0
- package/src/utils/parse-highlight-range.ts +41 -0
- package/src/utils/schema-renderer.test.ts +269 -0
- package/src/utils/schema-renderer.ts +152 -0
- package/src/utils/shiki.ts +99 -0
- package/src/utils/sidebar-helpers.ts +24 -0
- package/src/utils/theme-css.ts +101 -0
- package/src/utils/theme-helpers.ts +59 -0
- package/src/utils/toc-helpers.ts +11 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useCallback, useEffect, useRef } from "react";
|
|
3
|
+
const DEBOUNCE_MS = 150;
|
|
4
|
+
const MAX_RESULTS = 8;
|
|
5
|
+
export function SearchResults() {
|
|
6
|
+
const [query, setQuery] = useState("");
|
|
7
|
+
const [results, setResults] = useState([]);
|
|
8
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
9
|
+
const [isUnavailable, setIsUnavailable] = useState(false);
|
|
10
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
11
|
+
const pagefindRef = useRef(null);
|
|
12
|
+
const debounceRef = useRef(null);
|
|
13
|
+
const inputRef = useRef(null);
|
|
14
|
+
const resultsRef = useRef(null);
|
|
15
|
+
const [selectedIndex, setSelectedIndex] = useState(-1);
|
|
16
|
+
// Load Pagefind JS lazily on first focus
|
|
17
|
+
const loadPagefind = useCallback(async () => {
|
|
18
|
+
if (pagefindRef.current)
|
|
19
|
+
return;
|
|
20
|
+
try {
|
|
21
|
+
// Pagefind generates this file at build time in the output directory.
|
|
22
|
+
// Dynamic concatenation prevents Vite from statically analyzing the path
|
|
23
|
+
// (the index only exists after `astro build`, not during dev).
|
|
24
|
+
const pagefindPath = `/pagefind/pagefind.js`;
|
|
25
|
+
const pf = await import(/* @vite-ignore */ pagefindPath);
|
|
26
|
+
await pf.init();
|
|
27
|
+
pagefindRef.current = pf;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// During dev server, the index doesn't exist
|
|
31
|
+
setIsUnavailable(true);
|
|
32
|
+
}
|
|
33
|
+
}, []);
|
|
34
|
+
// Perform search with debouncing
|
|
35
|
+
const performSearch = useCallback(async (searchQuery) => {
|
|
36
|
+
if (!searchQuery.trim()) {
|
|
37
|
+
setResults([]);
|
|
38
|
+
setSelectedIndex(-1);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (!pagefindRef.current) {
|
|
42
|
+
await loadPagefind();
|
|
43
|
+
if (!pagefindRef.current)
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
setIsLoading(true);
|
|
47
|
+
try {
|
|
48
|
+
const response = await pagefindRef.current.search(searchQuery);
|
|
49
|
+
// Load full data for top results
|
|
50
|
+
const loaded = await Promise.all(response.results.slice(0, MAX_RESULTS).map((r) => r.data()));
|
|
51
|
+
setResults(loaded);
|
|
52
|
+
setSelectedIndex(-1);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
setResults([]);
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
setIsLoading(false);
|
|
59
|
+
}
|
|
60
|
+
}, [loadPagefind]);
|
|
61
|
+
// Handle input changes with debounce
|
|
62
|
+
const handleInputChange = useCallback((value) => {
|
|
63
|
+
setQuery(value);
|
|
64
|
+
if (debounceRef.current) {
|
|
65
|
+
clearTimeout(debounceRef.current);
|
|
66
|
+
}
|
|
67
|
+
debounceRef.current = setTimeout(() => {
|
|
68
|
+
performSearch(value);
|
|
69
|
+
}, DEBOUNCE_MS);
|
|
70
|
+
}, [performSearch]);
|
|
71
|
+
// Keyboard navigation
|
|
72
|
+
const handleKeyDown = useCallback((e) => {
|
|
73
|
+
if (e.key === "ArrowDown") {
|
|
74
|
+
e.preventDefault();
|
|
75
|
+
setSelectedIndex((prev) => Math.min(prev + 1, results.length - 1));
|
|
76
|
+
}
|
|
77
|
+
else if (e.key === "ArrowUp") {
|
|
78
|
+
e.preventDefault();
|
|
79
|
+
setSelectedIndex((prev) => Math.max(prev - 1, -1));
|
|
80
|
+
}
|
|
81
|
+
else if (e.key === "Enter" && selectedIndex >= 0) {
|
|
82
|
+
e.preventDefault();
|
|
83
|
+
const result = results[selectedIndex];
|
|
84
|
+
if (result) {
|
|
85
|
+
window.location.href = result.url;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else if (e.key === "Escape") {
|
|
89
|
+
setQuery("");
|
|
90
|
+
setResults([]);
|
|
91
|
+
setSelectedIndex(-1);
|
|
92
|
+
inputRef.current?.blur();
|
|
93
|
+
}
|
|
94
|
+
}, [results, selectedIndex]);
|
|
95
|
+
// Click outside to close
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
const handleClickOutside = (e) => {
|
|
98
|
+
if (resultsRef.current &&
|
|
99
|
+
!resultsRef.current.contains(e.target) &&
|
|
100
|
+
inputRef.current &&
|
|
101
|
+
!inputRef.current.contains(e.target)) {
|
|
102
|
+
setIsFocused(false);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
106
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
107
|
+
}, []);
|
|
108
|
+
// Cleanup debounce on unmount
|
|
109
|
+
useEffect(() => {
|
|
110
|
+
return () => {
|
|
111
|
+
if (debounceRef.current) {
|
|
112
|
+
clearTimeout(debounceRef.current);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
}, []);
|
|
116
|
+
const showResults = isFocused && query.trim().length > 0;
|
|
117
|
+
return (_jsxs("div", { className: "relative", "data-pagefind-ignore": "", children: [_jsxs("div", { className: "relative", children: [_jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", className: "absolute left-3 top-1/2 -translate-y-1/2 text-text-muted pointer-events-none", children: [_jsx("circle", { cx: "11", cy: "11", r: "8" }), _jsx("path", { d: "m21 21-4.3-4.3" })] }), _jsx("input", { ref: inputRef, type: "search", value: query, onChange: (e) => handleInputChange(e.target.value), onFocus: () => {
|
|
118
|
+
setIsFocused(true);
|
|
119
|
+
loadPagefind();
|
|
120
|
+
}, onKeyDown: handleKeyDown, placeholder: "Search docs\u2026", "aria-label": "Search documentation", "aria-expanded": showResults, "aria-controls": "search-results", "aria-autocomplete": "list", "aria-activedescendant": selectedIndex >= 0
|
|
121
|
+
? `search-result-${results[selectedIndex]?.id}`
|
|
122
|
+
: undefined, role: "combobox", className: "w-full pl-9 pr-3 py-1.5 text-sm rounded-md border border-border bg-surface text-text placeholder-text-muted focus:outline-none focus:ring-2 focus:ring-primary transition-colors" })] }), showResults && (_jsxs("div", { ref: resultsRef, id: "search-results", role: "listbox", "aria-label": "Search results", className: "absolute top-full left-0 right-0 mt-1 max-h-80 overflow-y-auto rounded-lg border border-border bg-surface shadow-lg z-50", style: { minWidth: "320px" }, children: [isUnavailable && (_jsx("div", { className: "px-4 py-3 text-sm text-text-muted", children: "Search available after build" })), isLoading && !isUnavailable && (_jsx("div", { className: "px-4 py-3 text-sm text-text-muted", children: "Searching\u2026" })), !isLoading && !isUnavailable && results.length === 0 && (_jsx("div", { className: "px-4 py-3 text-sm text-text-muted", children: "No results found" })), !isUnavailable &&
|
|
123
|
+
results.map((result, index) => (_jsxs("a", { id: `search-result-${result.id}`, href: result.url, role: "option", "aria-selected": index === selectedIndex, className: `block px-4 py-3 text-sm no-underline border-b border-border last:border-b-0 transition-colors ${index === selectedIndex
|
|
124
|
+
? "bg-hover-bg"
|
|
125
|
+
: "hover:bg-hover-bg"}`, children: [_jsx("div", { className: "font-medium text-text mb-0.5", children: result.meta?.title ?? "Untitled" }), result.sub_results?.[0]?.title &&
|
|
126
|
+
result.sub_results[0].title !== result.meta?.title && (_jsx("div", { className: "text-xs text-text-muted mb-1", children: result.sub_results[0].title })), _jsx("div", { className: "text-xs text-text-muted line-clamp-2 [&_mark]:bg-primary/20 [&_mark]:text-text [&_mark]:rounded-sm [&_mark]:px-0.5", dangerouslySetInnerHTML: {
|
|
127
|
+
__html: result.sub_results?.[0]?.excerpt ?? result.excerpt ?? "",
|
|
128
|
+
} })] }, result.id)))] }))] }));
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=SearchResults.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SearchResults.js","sourceRoot":"","sources":["../../src/islands/SearchResults.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ThemeToggle — React island for switching between dark and light mode.
|
|
3
|
+
*
|
|
4
|
+
* Renders as a button with sun/moon icons that crossfade on toggle.
|
|
5
|
+
* Reads initial state from the DOM (set by the FOUC-prevention inline script),
|
|
6
|
+
* toggles .dark class on <html>, and persists preference to localStorage.
|
|
7
|
+
*
|
|
8
|
+
* Uses inline SVGs — no icon library dependency to keep bundle minimal.
|
|
9
|
+
* Imports shared utilities from theme-helpers.ts for consistency.
|
|
10
|
+
*/
|
|
11
|
+
export declare function ThemeToggle(): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
//# sourceMappingURL=ThemeToggle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ThemeToggle.d.ts","sourceRoot":"","sources":["../../src/islands/ThemeToggle.tsx"],"names":[],"mappings":"AAOA;;;;;;;;;GASG;AAEH,wBAAgB,WAAW,4CAwF1B"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useCallback, useEffect } from "react";
|
|
3
|
+
import { THEME_STORAGE_KEY, applyTheme, } from "../utils/theme-helpers";
|
|
4
|
+
/**
|
|
5
|
+
* ThemeToggle — React island for switching between dark and light mode.
|
|
6
|
+
*
|
|
7
|
+
* Renders as a button with sun/moon icons that crossfade on toggle.
|
|
8
|
+
* Reads initial state from the DOM (set by the FOUC-prevention inline script),
|
|
9
|
+
* toggles .dark class on <html>, and persists preference to localStorage.
|
|
10
|
+
*
|
|
11
|
+
* Uses inline SVGs — no icon library dependency to keep bundle minimal.
|
|
12
|
+
* Imports shared utilities from theme-helpers.ts for consistency.
|
|
13
|
+
*/
|
|
14
|
+
export function ThemeToggle() {
|
|
15
|
+
const [isDark, setIsDark] = useState(() => {
|
|
16
|
+
// Read initial state from DOM — FOUC script already set .dark class
|
|
17
|
+
if (typeof document !== "undefined") {
|
|
18
|
+
return document.documentElement.classList.contains("dark");
|
|
19
|
+
}
|
|
20
|
+
return true; // SSR fallback: default dark
|
|
21
|
+
});
|
|
22
|
+
// Sync with external changes (e.g., other tabs via StorageEvent)
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
const handleStorage = (e) => {
|
|
25
|
+
if (e.key === THEME_STORAGE_KEY) {
|
|
26
|
+
const newTheme = e.newValue === "light" ? "light" : "dark";
|
|
27
|
+
setIsDark(newTheme === "dark");
|
|
28
|
+
applyTheme(newTheme);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
window.addEventListener("storage", handleStorage);
|
|
32
|
+
return () => window.removeEventListener("storage", handleStorage);
|
|
33
|
+
}, []);
|
|
34
|
+
const toggleTheme = useCallback(() => {
|
|
35
|
+
// Read from DOM (source of truth) to avoid stale closure issues
|
|
36
|
+
const currentlyDark = document.documentElement.classList.contains("dark");
|
|
37
|
+
const newIsDark = !currentlyDark;
|
|
38
|
+
setIsDark(newIsDark);
|
|
39
|
+
applyTheme(newIsDark ? "dark" : "light");
|
|
40
|
+
}, []);
|
|
41
|
+
return (_jsx("button", { type: "button", onClick: toggleTheme, "aria-label": isDark ? "Switch to light mode" : "Switch to dark mode", className: "inline-flex items-center justify-center rounded-md p-2 text-text-muted hover:text-(--color-text) hover:bg-hover-bg focus:outline-none focus-visible:ring-2 focus-visible:ring-primary transition-colors", children: _jsxs("span", { className: "relative inline-flex items-center justify-center w-5 h-5", children: [_jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", className: `absolute inset-0 transition-transform duration-300 ${isDark ? "rotate-0 scale-100" : "-rotate-90 scale-0"}`, children: [_jsx("circle", { cx: "12", cy: "12", r: "4" }), _jsx("path", { d: "M12 2v2" }), _jsx("path", { d: "M12 20v2" }), _jsx("path", { d: "m4.93 4.93 1.41 1.41" }), _jsx("path", { d: "m17.66 17.66 1.41 1.41" }), _jsx("path", { d: "M2 12h2" }), _jsx("path", { d: "M20 12h2" }), _jsx("path", { d: "m6.34 17.66-1.41 1.41" }), _jsx("path", { d: "m19.07 4.93-1.41 1.41" })] }), _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", className: `absolute inset-0 transition-transform duration-300 ${!isDark ? "rotate-0 scale-100" : "rotate-90 scale-0"}`, children: _jsx("path", { d: "M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" }) })] }) }));
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=ThemeToggle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ThemeToggle.js","sourceRoot":"","sources":["../../src/islands/ThemeToggle.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DocPage.test.d.ts","sourceRoot":"","sources":["../../src/layouts/DocPage.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for DocPage layout supporting utilities and contracts.
|
|
3
|
+
*
|
|
4
|
+
* Note: .astro components cannot be unit tested directly in Vitest.
|
|
5
|
+
* These tests verify the shared utils that the layout components import,
|
|
6
|
+
* and validate data contracts. Build verification confirms rendering.
|
|
7
|
+
*/
|
|
8
|
+
import { describe, it, expect } from "vitest";
|
|
9
|
+
import { filterVisibleItems, isActiveOrAncestor, } from "../utils/sidebar-helpers.js";
|
|
10
|
+
import { filterTocHeadings } from "../utils/toc-helpers.js";
|
|
11
|
+
// ─── Tests ─────────────────────────────────────────────────────────
|
|
12
|
+
describe("Sidebar filtering", () => {
|
|
13
|
+
it("excludes hidden items", () => {
|
|
14
|
+
const items = [
|
|
15
|
+
{ title: "Visible", slug: "visible", type: "page" },
|
|
16
|
+
{ title: "Hidden", slug: "hidden", type: "page", hidden: true },
|
|
17
|
+
{ title: "Also Visible", slug: "also-visible", type: "page" },
|
|
18
|
+
];
|
|
19
|
+
const result = filterVisibleItems(items);
|
|
20
|
+
expect(result).toHaveLength(2);
|
|
21
|
+
expect(result.map((i) => i.title)).toEqual(["Visible", "Also Visible"]);
|
|
22
|
+
});
|
|
23
|
+
it("includes all items when none are hidden", () => {
|
|
24
|
+
const items = [
|
|
25
|
+
{ title: "A", slug: "a", type: "page" },
|
|
26
|
+
{ title: "B", slug: "b", type: "page" },
|
|
27
|
+
];
|
|
28
|
+
expect(filterVisibleItems(items)).toHaveLength(2);
|
|
29
|
+
});
|
|
30
|
+
it("returns empty array for all-hidden items", () => {
|
|
31
|
+
const items = [
|
|
32
|
+
{ title: "A", slug: "a", type: "page", hidden: true },
|
|
33
|
+
];
|
|
34
|
+
expect(filterVisibleItems(items)).toHaveLength(0);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
describe("Sidebar active detection", () => {
|
|
38
|
+
it("detects direct page match", () => {
|
|
39
|
+
const item = { title: "Guide", slug: "guide", type: "page" };
|
|
40
|
+
expect(isActiveOrAncestor(item, "guide")).toBe(true);
|
|
41
|
+
expect(isActiveOrAncestor(item, "other")).toBe(false);
|
|
42
|
+
});
|
|
43
|
+
it("detects ancestor section when child is active", () => {
|
|
44
|
+
const section = {
|
|
45
|
+
title: "API",
|
|
46
|
+
slug: "api",
|
|
47
|
+
type: "section",
|
|
48
|
+
children: [
|
|
49
|
+
{ title: "Auth", slug: "api/auth", type: "page" },
|
|
50
|
+
{ title: "Users", slug: "api/users", type: "page" },
|
|
51
|
+
],
|
|
52
|
+
};
|
|
53
|
+
expect(isActiveOrAncestor(section, "api/auth")).toBe(true);
|
|
54
|
+
expect(isActiveOrAncestor(section, "api/users")).toBe(true);
|
|
55
|
+
expect(isActiveOrAncestor(section, "other")).toBe(false);
|
|
56
|
+
});
|
|
57
|
+
it("handles deeply nested sections", () => {
|
|
58
|
+
const root = {
|
|
59
|
+
title: "Docs",
|
|
60
|
+
slug: "docs",
|
|
61
|
+
type: "section",
|
|
62
|
+
children: [
|
|
63
|
+
{
|
|
64
|
+
title: "Advanced",
|
|
65
|
+
slug: "docs/advanced",
|
|
66
|
+
type: "section",
|
|
67
|
+
children: [
|
|
68
|
+
{
|
|
69
|
+
title: "Plugins",
|
|
70
|
+
slug: "docs/advanced/plugins",
|
|
71
|
+
type: "page",
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
};
|
|
77
|
+
expect(isActiveOrAncestor(root, "docs/advanced/plugins")).toBe(true);
|
|
78
|
+
expect(isActiveOrAncestor(root, "other")).toBe(false);
|
|
79
|
+
});
|
|
80
|
+
it("ignores external links", () => {
|
|
81
|
+
const item = {
|
|
82
|
+
title: "External",
|
|
83
|
+
slug: "",
|
|
84
|
+
type: "external-link",
|
|
85
|
+
href: "https://example.com",
|
|
86
|
+
};
|
|
87
|
+
expect(isActiveOrAncestor(item, "")).toBe(false);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
describe("Table of contents heading filtering", () => {
|
|
91
|
+
it("includes h2 and h3 headings only", () => {
|
|
92
|
+
const headings = [
|
|
93
|
+
{ depth: 1, slug: "title", text: "Title" },
|
|
94
|
+
{ depth: 2, slug: "intro", text: "Introduction" },
|
|
95
|
+
{ depth: 3, slug: "detail", text: "Detail" },
|
|
96
|
+
{ depth: 4, slug: "deep", text: "Deep" },
|
|
97
|
+
];
|
|
98
|
+
const result = filterTocHeadings(headings);
|
|
99
|
+
expect(result).toHaveLength(2);
|
|
100
|
+
expect(result.map((h) => h.text)).toEqual(["Introduction", "Detail"]);
|
|
101
|
+
});
|
|
102
|
+
it("returns empty array when no h2/h3 headings exist", () => {
|
|
103
|
+
const headings = [
|
|
104
|
+
{ depth: 1, slug: "title", text: "Title" },
|
|
105
|
+
{ depth: 4, slug: "deep", text: "Deep" },
|
|
106
|
+
];
|
|
107
|
+
expect(filterTocHeadings(headings)).toHaveLength(0);
|
|
108
|
+
});
|
|
109
|
+
it("handles empty headings array", () => {
|
|
110
|
+
expect(filterTocHeadings([])).toHaveLength(0);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
describe("NavigationTree contract", () => {
|
|
114
|
+
it("matches expected shape for layout props", () => {
|
|
115
|
+
const tree = {
|
|
116
|
+
items: [
|
|
117
|
+
{ title: "Getting Started", slug: "getting-started", type: "page" },
|
|
118
|
+
{
|
|
119
|
+
title: "API Reference",
|
|
120
|
+
slug: "api",
|
|
121
|
+
type: "section",
|
|
122
|
+
collapsed: true,
|
|
123
|
+
children: [
|
|
124
|
+
{ title: "Auth", slug: "api/auth", type: "page" },
|
|
125
|
+
{
|
|
126
|
+
title: "GitHub",
|
|
127
|
+
slug: "",
|
|
128
|
+
type: "external-link",
|
|
129
|
+
href: "https://github.com",
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
},
|
|
133
|
+
],
|
|
134
|
+
};
|
|
135
|
+
expect(tree.items).toHaveLength(2);
|
|
136
|
+
expect(tree.items[0].type).toBe("page");
|
|
137
|
+
expect(tree.items[1].type).toBe("section");
|
|
138
|
+
expect(tree.items[1].collapsed).toBe(true);
|
|
139
|
+
expect(tree.items[1].children).toHaveLength(2);
|
|
140
|
+
expect(tree.items[1].children[1].type).toBe("external-link");
|
|
141
|
+
expect(tree.items[1].children[1].href).toBe("https://github.com");
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
describe("DocPage props contract", () => {
|
|
145
|
+
it("validates expected frontmatter shape", () => {
|
|
146
|
+
const frontmatter = {
|
|
147
|
+
title: "Getting Started",
|
|
148
|
+
description: "A guide to getting started",
|
|
149
|
+
};
|
|
150
|
+
expect(frontmatter.title).toBeDefined();
|
|
151
|
+
expect(typeof frontmatter.title).toBe("string");
|
|
152
|
+
});
|
|
153
|
+
it("headings array has correct shape", () => {
|
|
154
|
+
const headings = [
|
|
155
|
+
{ depth: 2, slug: "installation", text: "Installation" },
|
|
156
|
+
{ depth: 3, slug: "npm", text: "Using npm" },
|
|
157
|
+
];
|
|
158
|
+
headings.forEach((h) => {
|
|
159
|
+
expect(typeof h.depth).toBe("number");
|
|
160
|
+
expect(typeof h.slug).toBe("string");
|
|
161
|
+
expect(typeof h.text).toBe("string");
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
//# sourceMappingURL=DocPage.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DocPage.test.js","sourceRoot":"","sources":["../../src/layouts/DocPage.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ClassValue } from "clsx";
|
|
2
|
+
/**
|
|
3
|
+
* Merge Tailwind CSS classes with clsx and tailwind-merge.
|
|
4
|
+
* This is the standard shadcn/ui utility function.
|
|
5
|
+
*
|
|
6
|
+
* Note: Scaffolding for future shadcn/ui component integration.
|
|
7
|
+
* Will be used by UI primitives in upcoming stories (e.g., search, dialogs).
|
|
8
|
+
*/
|
|
9
|
+
export declare function cn(...inputs: ClassValue[]): string;
|
|
10
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,UAAU,EAAE,MAAM,MAAM,CAAC;AAG7C;;;;;;GAMG;AACH,wBAAgB,EAAE,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,CAElD"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { clsx } from "clsx";
|
|
2
|
+
import { twMerge } from "tailwind-merge";
|
|
3
|
+
/**
|
|
4
|
+
* Merge Tailwind CSS classes with clsx and tailwind-merge.
|
|
5
|
+
* This is the standard shadcn/ui utility function.
|
|
6
|
+
*
|
|
7
|
+
* Note: Scaffolding for future shadcn/ui component integration.
|
|
8
|
+
* Will be used by UI primitives in upcoming stories (e.g., search, dialogs).
|
|
9
|
+
*/
|
|
10
|
+
export function cn(...inputs) {
|
|
11
|
+
return twMerge(clsx(inputs));
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAmB,MAAM,MAAM,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEzC;;;;;;GAMG;AACH,MAAM,UAAU,EAAE,CAAC,GAAG,MAAoB;IACxC,OAAO,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;AAC/B,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side enhancer for fenced code blocks.
|
|
3
|
+
*
|
|
4
|
+
* The rehype plugin wraps fenced code <pre> elements in:
|
|
5
|
+
* <div class="code-block code-block--fenced" data-code="...">
|
|
6
|
+
* <div class="code-block-body">
|
|
7
|
+
* <pre>...</pre>
|
|
8
|
+
* </div>
|
|
9
|
+
* </div>
|
|
10
|
+
*
|
|
11
|
+
* This script adds a lightweight copy button to each fenced code block
|
|
12
|
+
* WITHOUT requiring React hydration — keeping fenced blocks zero-JS
|
|
13
|
+
* until the user actually clicks copy.
|
|
14
|
+
*/
|
|
15
|
+
declare function initCopyButtons(): void;
|
|
16
|
+
//# sourceMappingURL=code-block-enhancer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-block-enhancer.d.ts","sourceRoot":"","sources":["../../src/scripts/code-block-enhancer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,iBAAS,eAAe,SAqCvB"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Client-side enhancer for fenced code blocks.
|
|
4
|
+
*
|
|
5
|
+
* The rehype plugin wraps fenced code <pre> elements in:
|
|
6
|
+
* <div class="code-block code-block--fenced" data-code="...">
|
|
7
|
+
* <div class="code-block-body">
|
|
8
|
+
* <pre>...</pre>
|
|
9
|
+
* </div>
|
|
10
|
+
* </div>
|
|
11
|
+
*
|
|
12
|
+
* This script adds a lightweight copy button to each fenced code block
|
|
13
|
+
* WITHOUT requiring React hydration — keeping fenced blocks zero-JS
|
|
14
|
+
* until the user actually clicks copy.
|
|
15
|
+
*/
|
|
16
|
+
function initCopyButtons() {
|
|
17
|
+
document
|
|
18
|
+
.querySelectorAll(".code-block--fenced")
|
|
19
|
+
.forEach((block) => {
|
|
20
|
+
// Skip if already enhanced
|
|
21
|
+
if (block.querySelector(".sg-copy-btn-native"))
|
|
22
|
+
return;
|
|
23
|
+
const code = block.getAttribute("data-code") ?? "";
|
|
24
|
+
const btn = document.createElement("button");
|
|
25
|
+
btn.type = "button";
|
|
26
|
+
btn.className = "sg-copy-btn-native sg-copy-btn";
|
|
27
|
+
btn.setAttribute("aria-label", "Copy code");
|
|
28
|
+
btn.title = "Copy code";
|
|
29
|
+
btn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>`;
|
|
30
|
+
btn.addEventListener("click", () => {
|
|
31
|
+
navigator.clipboard.writeText(code).then(() => {
|
|
32
|
+
btn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="color:#22c55e"><polyline points="20 6 9 17 4 12"/></svg>`;
|
|
33
|
+
btn.setAttribute("aria-label", "Copied!");
|
|
34
|
+
btn.title = "Copied!";
|
|
35
|
+
setTimeout(() => {
|
|
36
|
+
btn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>`;
|
|
37
|
+
btn.setAttribute("aria-label", "Copy code");
|
|
38
|
+
btn.title = "Copy code";
|
|
39
|
+
}, 2000);
|
|
40
|
+
}).catch(() => {
|
|
41
|
+
console.warn("[CopyButton] Clipboard API unavailable");
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
// Insert into the code-block-body
|
|
45
|
+
const body = block.querySelector(".code-block-body");
|
|
46
|
+
if (body) {
|
|
47
|
+
body.appendChild(btn);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
// Run on initial load
|
|
52
|
+
document.addEventListener("DOMContentLoaded", initCopyButtons);
|
|
53
|
+
// Re-run after Astro ViewTransitions page swaps
|
|
54
|
+
document.addEventListener("astro:after-swap", initCopyButtons);
|
|
55
|
+
//# sourceMappingURL=code-block-enhancer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-block-enhancer.js","sourceRoot":"","sources":["../../src/scripts/code-block-enhancer.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;GAaG;AAEH,SAAS,eAAe;IACtB,QAAQ;SACL,gBAAgB,CAAiB,qBAAqB,CAAC;SACvD,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;QACjB,2BAA2B;QAC3B,IAAI,KAAK,CAAC,aAAa,CAAC,qBAAqB,CAAC;YAAE,OAAO;QAEvD,MAAM,IAAI,GAAG,KAAK,CAAC,YAAY,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;QAEnD,MAAM,GAAG,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAC7C,GAAG,CAAC,IAAI,GAAG,QAAQ,CAAC;QACpB,GAAG,CAAC,SAAS,GAAG,gCAAgC,CAAC;QACjD,GAAG,CAAC,YAAY,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;QAC5C,GAAG,CAAC,KAAK,GAAG,WAAW,CAAC;QACxB,GAAG,CAAC,SAAS,GAAG,wTAAwT,CAAC;QAEzU,GAAG,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE;YACjC,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE;gBAC5C,GAAG,CAAC,SAAS,GAAG,sPAAsP,CAAC;gBACvQ,GAAG,CAAC,YAAY,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;gBAC1C,GAAG,CAAC,KAAK,GAAG,SAAS,CAAC;gBACtB,UAAU,CAAC,GAAG,EAAE;oBACd,GAAG,CAAC,SAAS,GAAG,wTAAwT,CAAC;oBACzU,GAAG,CAAC,YAAY,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;oBAC5C,GAAG,CAAC,KAAK,GAAG,WAAW,CAAC;gBAC1B,CAAC,EAAE,IAAI,CAAC,CAAC;YACX,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;gBACZ,OAAO,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC;YACzD,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,kCAAkC;QAClC,MAAM,IAAI,GAAG,KAAK,CAAC,aAAa,CAAiB,kBAAkB,CAAC,CAAC;QACrE,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QACxB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC;AAED,sBAAsB;AACtB,QAAQ,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,eAAe,CAAC,CAAC;AAE/D,gDAAgD;AAChD,QAAQ,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,eAAe,CAAC,CAAC"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { type DialogProps } from "@radix-ui/react-dialog";
|
|
3
|
+
declare const Command: React.ForwardRefExoticComponent<Omit<{
|
|
4
|
+
children?: React.ReactNode;
|
|
5
|
+
} & Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
|
|
6
|
+
ref?: React.Ref<HTMLDivElement>;
|
|
7
|
+
} & {
|
|
8
|
+
asChild?: boolean;
|
|
9
|
+
}, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild"> & {
|
|
10
|
+
label?: string;
|
|
11
|
+
shouldFilter?: boolean;
|
|
12
|
+
filter?: (value: string, search: string, keywords?: string[]) => number;
|
|
13
|
+
defaultValue?: string;
|
|
14
|
+
value?: string;
|
|
15
|
+
onValueChange?: (value: string) => void;
|
|
16
|
+
loop?: boolean;
|
|
17
|
+
disablePointerSelection?: boolean;
|
|
18
|
+
vimBindings?: boolean;
|
|
19
|
+
} & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
20
|
+
interface CommandDialogProps extends DialogProps {
|
|
21
|
+
/** Pass false to disable cmdk's built-in filtering (e.g. when using external search like Pagefind) */
|
|
22
|
+
shouldFilter?: boolean;
|
|
23
|
+
/** Accessible dialog title (screen-reader only). Defaults to "Search" */
|
|
24
|
+
dialogTitle?: string;
|
|
25
|
+
}
|
|
26
|
+
declare const CommandDialog: ({ children, shouldFilter, dialogTitle, ...props }: CommandDialogProps) => import("react/jsx-runtime").JSX.Element;
|
|
27
|
+
declare const CommandInput: React.ForwardRefExoticComponent<Omit<Omit<Pick<Pick<React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "key" | keyof React.InputHTMLAttributes<HTMLInputElement>> & {
|
|
28
|
+
ref?: React.Ref<HTMLInputElement>;
|
|
29
|
+
} & {
|
|
30
|
+
asChild?: boolean;
|
|
31
|
+
}, "key" | "asChild" | keyof React.InputHTMLAttributes<HTMLInputElement>>, "type" | "onChange" | "value"> & {
|
|
32
|
+
value?: string;
|
|
33
|
+
onValueChange?: (search: string) => void;
|
|
34
|
+
} & React.RefAttributes<HTMLInputElement>, "ref"> & React.RefAttributes<HTMLInputElement>>;
|
|
35
|
+
declare const CommandList: React.ForwardRefExoticComponent<Omit<{
|
|
36
|
+
children?: React.ReactNode;
|
|
37
|
+
} & Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
|
|
38
|
+
ref?: React.Ref<HTMLDivElement>;
|
|
39
|
+
} & {
|
|
40
|
+
asChild?: boolean;
|
|
41
|
+
}, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild"> & {
|
|
42
|
+
label?: string;
|
|
43
|
+
} & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
44
|
+
declare const CommandEmpty: React.ForwardRefExoticComponent<Omit<{
|
|
45
|
+
children?: React.ReactNode;
|
|
46
|
+
} & Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
|
|
47
|
+
ref?: React.Ref<HTMLDivElement>;
|
|
48
|
+
} & {
|
|
49
|
+
asChild?: boolean;
|
|
50
|
+
}, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild"> & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
51
|
+
declare const CommandGroup: React.ForwardRefExoticComponent<Omit<{
|
|
52
|
+
children?: React.ReactNode;
|
|
53
|
+
} & Omit<Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
|
|
54
|
+
ref?: React.Ref<HTMLDivElement>;
|
|
55
|
+
} & {
|
|
56
|
+
asChild?: boolean;
|
|
57
|
+
}, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild">, "heading" | "value"> & {
|
|
58
|
+
heading?: React.ReactNode;
|
|
59
|
+
value?: string;
|
|
60
|
+
forceMount?: boolean;
|
|
61
|
+
} & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
62
|
+
declare const CommandSeparator: React.ForwardRefExoticComponent<Omit<Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
|
|
63
|
+
ref?: React.Ref<HTMLDivElement>;
|
|
64
|
+
} & {
|
|
65
|
+
asChild?: boolean;
|
|
66
|
+
}, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild"> & {
|
|
67
|
+
alwaysRender?: boolean;
|
|
68
|
+
} & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
69
|
+
declare const CommandItem: React.ForwardRefExoticComponent<Omit<{
|
|
70
|
+
children?: React.ReactNode;
|
|
71
|
+
} & Omit<Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
|
|
72
|
+
ref?: React.Ref<HTMLDivElement>;
|
|
73
|
+
} & {
|
|
74
|
+
asChild?: boolean;
|
|
75
|
+
}, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild">, "onSelect" | "disabled" | "value"> & {
|
|
76
|
+
disabled?: boolean;
|
|
77
|
+
onSelect?: (value: string) => void;
|
|
78
|
+
value?: string;
|
|
79
|
+
keywords?: string[];
|
|
80
|
+
forceMount?: boolean;
|
|
81
|
+
} & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
82
|
+
declare const CommandShortcut: {
|
|
83
|
+
({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>): import("react/jsx-runtime").JSX.Element;
|
|
84
|
+
displayName: string;
|
|
85
|
+
};
|
|
86
|
+
export { Command, CommandDialog, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandShortcut, CommandSeparator, };
|
|
87
|
+
//# sourceMappingURL=command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../src/ui/command.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAM1D,QAAA,MAAM,OAAO;;;;;;;;;;;;;;;;sFAYX,CAAC;AAGH,UAAU,kBAAmB,SAAQ,WAAW;IAC9C,sGAAsG;IACtG,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,yEAAyE;IACzE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,QAAA,MAAM,aAAa,GAAI,mDAKpB,kBAAkB,4CAqBpB,CAAC;AAEF,QAAA,MAAM,YAAY;;;;;;;0FA8BhB,CAAC;AAGH,QAAA,MAAM,WAAW;;;;;;;;sFASf,CAAC;AAGH,QAAA,MAAM,YAAY;;;;;;uJAShB,CAAC;AAGH,QAAA,MAAM,YAAY;;;;;;;;;;sFAYhB,CAAC;AAGH,QAAA,MAAM,gBAAgB;;;;;;sFASpB,CAAC;AAGH,QAAA,MAAM,WAAW;;;;;;;;;;;;sFAYf,CAAC;AAGH,QAAA,MAAM,eAAe;8BAGlB,KAAK,CAAC,cAAc,CAAC,eAAe,CAAC;;CAUvC,CAAC;AAGF,OAAO,EACL,OAAO,EACP,aAAa,EACb,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,eAAe,EACf,gBAAgB,GACjB,CAAC"}
|