svelte-docsmith 0.2.0 → 0.3.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.
Files changed (124) hide show
  1. package/README.md +57 -25
  2. package/dist/clipboard.svelte.d.ts +1 -1
  3. package/dist/clipboard.svelte.js +15 -8
  4. package/dist/config.d.ts +36 -6
  5. package/dist/config.js +33 -7
  6. package/dist/index.d.ts +4 -2
  7. package/dist/index.js +4 -0
  8. package/dist/markdown-layout.svelte +14 -1
  9. package/dist/markdown-layout.svelte.d.ts +2 -0
  10. package/dist/preprocess.d.ts +4 -5
  11. package/dist/preprocess.js +4 -5
  12. package/dist/search/context.svelte.d.ts +7 -0
  13. package/dist/search/context.svelte.js +20 -0
  14. package/dist/search/create-search.d.ts +14 -0
  15. package/dist/search/create-search.js +56 -0
  16. package/dist/search/snippet.d.ts +7 -0
  17. package/dist/search/snippet.js +43 -0
  18. package/dist/search.d.ts +10 -0
  19. package/dist/search.js +4 -0
  20. package/dist/theme.css +25 -0
  21. package/dist/types.d.ts +0 -6
  22. package/dist/ui/badge.svelte +10 -1
  23. package/dist/ui/badge.svelte.d.ts +2 -0
  24. package/dist/ui/callout.svelte +9 -9
  25. package/dist/ui/callout.svelte.d.ts +3 -3
  26. package/dist/ui/layouts/docs-header.svelte +5 -11
  27. package/dist/ui/layouts/docs-header.svelte.d.ts +0 -6
  28. package/dist/ui/layouts/docs-mobile-header.svelte +40 -8
  29. package/dist/ui/layouts/docs-mobile-header.svelte.d.ts +5 -4
  30. package/dist/ui/layouts/docs-shell.svelte +49 -5
  31. package/dist/ui/layouts/docs-shell.svelte.d.ts +16 -1
  32. package/dist/ui/layouts/error-page.svelte +82 -0
  33. package/dist/ui/layouts/error-page.svelte.d.ts +25 -0
  34. package/dist/ui/layouts/seo-head.svelte +65 -0
  35. package/dist/ui/layouts/seo-head.svelte.d.ts +11 -0
  36. package/dist/ui/markdown/h2.svelte +7 -3
  37. package/dist/ui/markdown/h3.svelte +7 -3
  38. package/dist/ui/markdown/pre.svelte +1 -1
  39. package/dist/ui/prop.svelte +71 -36
  40. package/dist/ui/prop.svelte.d.ts +1 -1
  41. package/dist/ui/props-table.svelte +10 -62
  42. package/dist/ui/search-trigger.svelte +31 -0
  43. package/dist/ui/search-trigger.svelte.d.ts +6 -0
  44. package/dist/ui/search.svelte +167 -0
  45. package/dist/ui/search.svelte.d.ts +13 -0
  46. package/dist/ui/shadcn/accordion/accordion.svelte.d.ts +1 -1
  47. package/dist/ui/shadcn/command/command-dialog.svelte +42 -0
  48. package/dist/ui/shadcn/command/command-dialog.svelte.d.ts +14 -0
  49. package/dist/ui/shadcn/command/command-empty.svelte +17 -0
  50. package/dist/ui/shadcn/command/command-empty.svelte.d.ts +4 -0
  51. package/dist/ui/shadcn/command/command-group.svelte +33 -0
  52. package/dist/ui/shadcn/command/command-group.svelte.d.ts +7 -0
  53. package/dist/ui/shadcn/command/command-input.svelte +36 -0
  54. package/dist/ui/shadcn/command/command-input.svelte.d.ts +4 -0
  55. package/dist/ui/shadcn/command/command-item.svelte +27 -0
  56. package/dist/ui/shadcn/command/command-item.svelte.d.ts +4 -0
  57. package/dist/ui/shadcn/command/command-link-item.svelte +20 -0
  58. package/dist/ui/shadcn/command/command-link-item.svelte.d.ts +4 -0
  59. package/dist/ui/shadcn/command/command-list.svelte +20 -0
  60. package/dist/ui/shadcn/command/command-list.svelte.d.ts +4 -0
  61. package/dist/ui/shadcn/command/command-loading.svelte +7 -0
  62. package/dist/ui/shadcn/command/command-loading.svelte.d.ts +4 -0
  63. package/dist/ui/shadcn/command/command-separator.svelte +17 -0
  64. package/dist/ui/shadcn/command/command-separator.svelte.d.ts +4 -0
  65. package/dist/ui/shadcn/command/command-shortcut.svelte +23 -0
  66. package/dist/ui/shadcn/command/command-shortcut.svelte.d.ts +5 -0
  67. package/dist/ui/shadcn/command/command.svelte +28 -0
  68. package/dist/ui/shadcn/command/command.svelte.d.ts +8 -0
  69. package/dist/ui/shadcn/command/index.d.ts +12 -0
  70. package/dist/ui/shadcn/command/index.js +14 -0
  71. package/dist/ui/shadcn/dialog/dialog-close.svelte +11 -0
  72. package/dist/ui/shadcn/dialog/dialog-close.svelte.d.ts +4 -0
  73. package/dist/ui/shadcn/dialog/dialog-content.svelte +48 -0
  74. package/dist/ui/shadcn/dialog/dialog-content.svelte.d.ts +13 -0
  75. package/dist/ui/shadcn/dialog/dialog-description.svelte +20 -0
  76. package/dist/ui/shadcn/dialog/dialog-description.svelte.d.ts +4 -0
  77. package/dist/ui/shadcn/dialog/dialog-footer.svelte +32 -0
  78. package/dist/ui/shadcn/dialog/dialog-footer.svelte.d.ts +8 -0
  79. package/dist/ui/shadcn/dialog/dialog-header.svelte +20 -0
  80. package/dist/ui/shadcn/dialog/dialog-header.svelte.d.ts +5 -0
  81. package/dist/ui/shadcn/dialog/dialog-overlay.svelte +20 -0
  82. package/dist/ui/shadcn/dialog/dialog-overlay.svelte.d.ts +4 -0
  83. package/dist/ui/shadcn/dialog/dialog-portal.svelte +7 -0
  84. package/dist/ui/shadcn/dialog/dialog-portal.svelte.d.ts +3 -0
  85. package/dist/ui/shadcn/dialog/dialog-title.svelte +17 -0
  86. package/dist/ui/shadcn/dialog/dialog-title.svelte.d.ts +4 -0
  87. package/dist/ui/shadcn/dialog/dialog-trigger.svelte +11 -0
  88. package/dist/ui/shadcn/dialog/dialog-trigger.svelte.d.ts +4 -0
  89. package/dist/ui/shadcn/dialog/dialog.svelte +7 -0
  90. package/dist/ui/shadcn/dialog/dialog.svelte.d.ts +3 -0
  91. package/dist/ui/shadcn/dialog/index.d.ts +11 -0
  92. package/dist/ui/shadcn/dialog/index.js +13 -0
  93. package/dist/ui/shadcn/input/index.d.ts +2 -0
  94. package/dist/ui/shadcn/input/index.js +4 -0
  95. package/dist/ui/shadcn/input/input.svelte +48 -0
  96. package/dist/ui/shadcn/input/input.svelte.d.ts +13 -0
  97. package/dist/ui/shadcn/input-group/index.d.ts +7 -0
  98. package/dist/ui/shadcn/input-group/index.js +9 -0
  99. package/dist/ui/shadcn/input-group/input-group-addon.svelte +53 -0
  100. package/dist/ui/shadcn/input-group/input-group-addon.svelte.d.ts +32 -0
  101. package/dist/ui/shadcn/input-group/input-group-button.svelte +49 -0
  102. package/dist/ui/shadcn/input-group/input-group-button.svelte.d.ts +32 -0
  103. package/dist/ui/shadcn/input-group/input-group-input.svelte +23 -0
  104. package/dist/ui/shadcn/input-group/input-group-input.svelte.d.ts +11 -0
  105. package/dist/ui/shadcn/input-group/input-group-text.svelte +22 -0
  106. package/dist/ui/shadcn/input-group/input-group-text.svelte.d.ts +5 -0
  107. package/dist/ui/shadcn/input-group/input-group-textarea.svelte +23 -0
  108. package/dist/ui/shadcn/input-group/input-group-textarea.svelte.d.ts +3 -0
  109. package/dist/ui/shadcn/input-group/input-group.svelte +24 -0
  110. package/dist/ui/shadcn/input-group/input-group.svelte.d.ts +5 -0
  111. package/dist/ui/shadcn/tabs/tabs.svelte.d.ts +1 -1
  112. package/dist/ui/shadcn/textarea/index.d.ts +2 -0
  113. package/dist/ui/shadcn/textarea/index.js +4 -0
  114. package/dist/ui/shadcn/textarea/textarea.svelte +23 -0
  115. package/dist/ui/shadcn/textarea/textarea.svelte.d.ts +5 -0
  116. package/dist/ui/tab-item.svelte +35 -9
  117. package/dist/ui/tab-item.svelte.d.ts +9 -4
  118. package/dist/ui/tabs-phase.svelte +19 -0
  119. package/dist/ui/tabs-phase.svelte.d.ts +9 -0
  120. package/dist/ui/tabs.svelte +47 -24
  121. package/dist/ui/tabs.svelte.d.ts +16 -6
  122. package/dist/vite.d.ts +8 -6
  123. package/dist/vite.js +140 -47
  124. package/package.json +8 -2
@@ -14,7 +14,7 @@
14
14
  type?: string;
15
15
  /** Mark the property as required. */
16
16
  required?: boolean;
17
- /** Default value, shown in its own column. */
17
+ /** Default value, shown alongside the type. */
18
18
  default?: string;
19
19
  /** Description. */
20
20
  children?: Snippet;
@@ -22,7 +22,8 @@
22
22
 
23
23
  // Split a union into per-value chips so long ones wrap as a tidy pill group
24
24
  // instead of ugly broken text. Skip splitting when the type is a generic
25
- // (`Array<A | B>`) where a top-level `|` split would be wrong.
25
+ // (`Array<A | B>`) or a signature (`() => …`) where a top-level `|` split
26
+ // would be wrong — those render as one chip that wraps on its own.
26
27
  const typeParts = $derived.by(() => {
27
28
  if (!type) return [];
28
29
  if (/[<(]/.test(type)) return [type];
@@ -31,46 +32,64 @@
31
32
  .map((s) => s.trim())
32
33
  .filter(Boolean);
33
34
  });
35
+ const solo = $derived(typeParts.length === 1);
34
36
  </script>
35
37
 
36
- <tr>
37
- <td class="prop-name">
38
- <code>{name}</code>
38
+ <div class="prop-row">
39
+ <div class="prop-head">
40
+ <code class="prop-name">{name}</code>
39
41
  {#if required}<span class="prop-required">required</span>{/if}
40
- </td>
41
- <td class="prop-type">
42
- {#each typeParts as part (part)}
43
- <code class="type-chip">{part}</code>
44
- {/each}
45
- </td>
46
- <td class="prop-default">
42
+
43
+ {#if type}
44
+ <span class="prop-types">
45
+ {#each typeParts as part (part)}
46
+ <code class="type-chip" class:type-chip-solo={solo}>{part}</code>
47
+ {/each}
48
+ </span>
49
+ {/if}
50
+
47
51
  {#if defaultValue !== undefined}
48
- <code>{defaultValue}</code>
49
- {:else}
50
- <span class="prop-none" aria-hidden="true">—</span>
52
+ <span class="prop-default">
53
+ <span class="prop-default-label">default</span>
54
+ <code>{defaultValue}</code>
55
+ </span>
51
56
  {/if}
52
- </td>
53
- <td class="prop-desc">
54
- {#if children}{@render children()}{/if}
55
- </td>
56
- </tr>
57
+ </div>
58
+
59
+ {#if children}
60
+ <div class="prop-desc">{@render children()}</div>
61
+ {/if}
62
+ </div>
57
63
 
58
64
  <style>
65
+ .prop-row {
66
+ padding: 0.95rem 1rem;
67
+ border-top: 1px solid var(--border);
68
+ }
69
+ .prop-row:first-child {
70
+ border-top: none;
71
+ }
72
+
73
+ /* Name + required + type + default flow on one wrapping line. */
74
+ .prop-head {
75
+ display: flex;
76
+ flex-wrap: wrap;
77
+ align-items: center;
78
+ gap: 0.4rem 0.6rem;
79
+ min-width: 0;
80
+ }
81
+
59
82
  /* Name is the anchor you scan for: mono, ink, medium weight. */
60
83
  .prop-name {
61
- white-space: nowrap;
62
- }
63
- .prop-name code {
64
84
  font-family: var(--font-mono, ui-monospace, monospace);
65
85
  font-size: 0.8125rem;
66
86
  font-weight: 600;
67
87
  color: var(--foreground);
68
88
  }
89
+
69
90
  /* Solid brand pill — the sanctioned primary-foreground-on-primary pairing,
70
91
  so it stays legible in both themes (terracotta-on-tint fails AA in light). */
71
92
  .prop-required {
72
- display: inline-block;
73
- margin-left: 0.45rem;
74
93
  padding: 0.05rem 0.4rem;
75
94
  border-radius: 9999px;
76
95
  background: var(--primary);
@@ -78,17 +97,18 @@
78
97
  font-size: 0.625rem;
79
98
  font-weight: 600;
80
99
  letter-spacing: 0.02em;
81
- vertical-align: 0.1em;
100
+ text-transform: uppercase;
82
101
  }
83
102
 
84
103
  /* Type values as chips: a union wraps as a neat pill group, each token
85
- unbroken. Single types render as one chip. */
86
- .prop-type {
87
- max-width: 26rem;
104
+ unbroken. A lone signature/generic wraps within itself on narrow screens. */
105
+ .prop-types {
106
+ display: inline-flex;
107
+ flex-wrap: wrap;
108
+ gap: 0.3rem;
109
+ min-width: 0;
88
110
  }
89
111
  .type-chip {
90
- display: inline-block;
91
- margin: 0.1rem 0.3rem 0.1rem 0;
92
112
  padding: 0.05rem 0.4rem;
93
113
  border-radius: 0.4rem;
94
114
  border: 1px solid var(--border);
@@ -98,22 +118,36 @@
98
118
  color: var(--foreground);
99
119
  white-space: nowrap;
100
120
  }
121
+ .type-chip-solo {
122
+ white-space: normal;
123
+ overflow-wrap: anywhere;
124
+ max-width: 100%;
125
+ }
101
126
 
127
+ /* Default sits at the end of the line, pushed right when there's room. */
102
128
  .prop-default {
129
+ display: inline-flex;
130
+ align-items: baseline;
131
+ gap: 0.35rem;
132
+ margin-left: auto;
103
133
  white-space: nowrap;
104
134
  }
135
+ .prop-default-label {
136
+ font-size: 0.7rem;
137
+ color: var(--muted-foreground);
138
+ }
105
139
  .prop-default code {
106
140
  font-family: var(--font-mono, ui-monospace, monospace);
107
- font-size: 0.8125rem;
141
+ font-size: 0.78rem;
108
142
  color: var(--foreground);
109
143
  }
110
- .prop-none {
111
- color: var(--muted-foreground);
112
- }
113
144
 
114
145
  .prop-desc {
115
- color: var(--foreground);
146
+ margin-top: 0.5rem;
147
+ color: var(--muted-foreground);
148
+ font-size: 0.875rem;
116
149
  line-height: 1.55;
150
+ max-width: 68ch;
117
151
  }
118
152
  .prop-desc :global(code) {
119
153
  font-family: var(--font-mono, ui-monospace, monospace);
@@ -121,6 +155,7 @@
121
155
  padding: 0.05em 0.3em;
122
156
  border-radius: 0.3rem;
123
157
  background: color-mix(in oklch, var(--muted) 60%, transparent);
158
+ color: var(--foreground);
124
159
  }
125
160
  .prop-desc :global(a) {
126
161
  color: var(--primary);
@@ -6,7 +6,7 @@ type $$ComponentProps = {
6
6
  type?: string;
7
7
  /** Mark the property as required. */
8
8
  required?: boolean;
9
- /** Default value, shown in its own column. */
9
+ /** Default value, shown alongside the type. */
10
10
  default?: string;
11
11
  /** Description. */
12
12
  children?: Snippet;
@@ -18,6 +18,9 @@
18
18
  <Prop name="variant" type="'a' | 'b'" default="'a'">Visual intent.</Prop>
19
19
  <Prop name="href" type="string" required>Turns it into a link.</Prop>
20
20
  </PropsTable>
21
+
22
+ Rows stack (name/type/default on one line, description below) so long type
23
+ signatures never force a horizontal scroll or crush the description.
21
24
  -->
22
25
  <div class="props-table not-prose">
23
26
  {#if title}
@@ -26,20 +29,8 @@
26
29
  <span class="props-table-caption-name">{title}</span>
27
30
  </div>
28
31
  {/if}
29
- <div class="props-table-scroll">
30
- <table>
31
- <thead>
32
- <tr>
33
- <th class="col-prop">Prop</th>
34
- <th class="col-type">Type</th>
35
- <th class="col-default">Default</th>
36
- <th class="col-desc">Description</th>
37
- </tr>
38
- </thead>
39
- <tbody>
40
- {@render children()}
41
- </tbody>
42
- </table>
32
+ <div class="props-list">
33
+ {@render children()}
43
34
  </div>
44
35
  </div>
45
36
 
@@ -49,12 +40,11 @@
49
40
  border: 1px solid var(--border);
50
41
  border-radius: var(--radius);
51
42
  background: var(--card);
52
- /* Clip the scroll region to the rounded corners. */
53
43
  overflow: hidden;
54
44
  }
55
45
 
56
- /* Caption = which component. Mono + ink weight, distinct from the quiet
57
- column-header row below it so the two never read as one grey slab. */
46
+ /* Caption = which component. Mono + ink weight, distinct from the rows below
47
+ it so the header never reads as one grey slab. */
58
48
  .props-table-caption {
59
49
  display: flex;
60
50
  align-items: center;
@@ -78,50 +68,8 @@
78
68
  color: var(--foreground);
79
69
  }
80
70
 
81
- .props-table-scroll {
82
- overflow-x: auto;
83
- }
84
- .props-table table {
85
- width: 100%;
86
- border-collapse: collapse;
87
- font-size: 0.875rem;
88
- }
89
-
90
- /* Column headers: quiet labels, no fill — the caption owns the emphasis. */
91
- .props-table :global(thead th) {
92
- text-align: left;
93
- font-size: 0.75rem;
94
- font-weight: 500;
95
- color: var(--muted-foreground);
96
- padding: 0.55rem 1rem;
97
- border-bottom: 1px solid var(--border);
98
- white-space: nowrap;
99
- }
100
- /* Prop / Default shrink to their content; Type flexes; Description holds width. */
101
- .props-table :global(.col-prop),
102
- .props-table :global(.col-default) {
103
- width: 1%;
104
- }
105
- .props-table :global(.col-desc) {
106
- width: 40%;
107
- }
108
-
109
- .props-table :global(tbody td) {
110
- padding: 0.7rem 1rem;
111
- vertical-align: baseline;
112
- border-top: 1px solid var(--border);
113
- transition: background-color 120ms ease;
114
- }
115
- .props-table :global(tbody tr:first-child td) {
116
- border-top: none;
117
- }
118
- .props-table :global(tbody tr:hover td) {
119
- background: color-mix(in oklch, var(--muted) 45%, transparent);
120
- }
121
-
122
- @media (prefers-reduced-motion: reduce) {
123
- .props-table :global(tbody td) {
124
- transition: none;
125
- }
71
+ /* Each child <Prop> is a `.prop-row` with its own border-top. */
72
+ .props-list {
73
+ min-width: 0;
126
74
  }
127
75
  </style>
@@ -0,0 +1,31 @@
1
+ <script lang="ts">
2
+ import SearchIcon from '@lucide/svelte/icons/search';
3
+ import { useSearch } from '../search/context.svelte.js';
4
+
5
+ const { class: className = '' }: { class?: string } = $props();
6
+
7
+ const search = useSearch();
8
+
9
+ // Resolve the modifier label client-side so SSR stays platform-neutral.
10
+ let isMac = $state(false);
11
+ $effect(() => {
12
+ isMac = navigator.platform.toLowerCase().includes('mac');
13
+ });
14
+ </script>
15
+
16
+ {#if search}
17
+ <button
18
+ type="button"
19
+ onclick={() => (search.open = true)}
20
+ class="text-muted-foreground hover:text-foreground border-border/50 bg-muted/40 hover:bg-muted focus-visible:ring-ring inline-flex h-8 items-center gap-2 rounded-md border px-2.5 text-sm transition-colors focus-visible:ring-2 focus-visible:outline-none {className}"
21
+ >
22
+ <SearchIcon class="size-4 shrink-0" />
23
+ <span class="hidden sm:inline">Search</span>
24
+ <kbd
25
+ class="border-border/60 bg-background text-muted-foreground pointer-events-none ml-2 hidden h-5 items-center gap-0.5 rounded border px-1.5 font-mono text-[10px] font-medium sm:inline-flex"
26
+ >
27
+ {isMac ? '⌘' : 'Ctrl'}K
28
+ </kbd>
29
+ <span class="sr-only">Search documentation</span>
30
+ </button>
31
+ {/if}
@@ -0,0 +1,6 @@
1
+ type $$ComponentProps = {
2
+ class?: string;
3
+ };
4
+ declare const SearchTrigger: import("svelte").Component<$$ComponentProps, {}, "">;
5
+ type SearchTrigger = ReturnType<typeof SearchTrigger>;
6
+ export default SearchTrigger;
@@ -0,0 +1,167 @@
1
+ <script lang="ts">
2
+ import { goto } from '$app/navigation';
3
+ import * as Command from './shadcn/command/index.js';
4
+ import { useSearch } from '../search/context.svelte.js';
5
+ import type { SearchDoc } from '../config.js';
6
+ import type { SearchEngine } from '../search/create-search.js';
7
+ import CornerDownLeft from '@lucide/svelte/icons/corner-down-left';
8
+ import FileText from '@lucide/svelte/icons/file-text';
9
+ import LoaderCircle from '@lucide/svelte/icons/loader-circle';
10
+ import SearchIcon from '@lucide/svelte/icons/search';
11
+
12
+ const {
13
+ load,
14
+ placeholder = 'Search documentation...'
15
+ }: {
16
+ /**
17
+ * Lazily provide the generated search records. Wired by the consumer so the
18
+ * index is code-split and only fetched when the palette first opens, e.g.
19
+ * `() => import('svelte-docsmith/search').then((m) => m.docs)`.
20
+ */
21
+ load: () => Promise<SearchDoc[]>;
22
+ placeholder?: string;
23
+ } = $props();
24
+
25
+ const search = useSearch();
26
+
27
+ let query = $state('');
28
+ let engine = $state<SearchEngine | null>(null);
29
+ let status = $state<'idle' | 'loading' | 'error'>('idle');
30
+
31
+ const trimmed = $derived(query.trim());
32
+ const results = $derived(engine && trimmed ? engine.search(query) : []);
33
+
34
+ // Build the index the first time the palette opens; keep it for later opens.
35
+ async function ensureEngine() {
36
+ if (engine || status === 'loading') return;
37
+ status = 'loading';
38
+ try {
39
+ const [{ createSearchEngine }, docs] = await Promise.all([
40
+ import('../search/create-search.js'),
41
+ load()
42
+ ]);
43
+ engine = createSearchEngine(docs);
44
+ status = 'idle';
45
+ } catch (error) {
46
+ console.error('[svelte-docsmith] failed to load the search index', error);
47
+ status = 'error';
48
+ }
49
+ }
50
+
51
+ $effect(() => {
52
+ if (search?.open) ensureEngine();
53
+ });
54
+
55
+ function onWindowKeydown(event: KeyboardEvent) {
56
+ if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'k') {
57
+ event.preventDefault();
58
+ if (search) search.open = !search.open;
59
+ }
60
+ }
61
+
62
+ function select(path: string) {
63
+ if (search) search.open = false;
64
+ query = '';
65
+ goto(path);
66
+ }
67
+ </script>
68
+
69
+ <svelte:window onkeydown={onWindowKeydown} />
70
+
71
+ {#if search}
72
+ <Command.Dialog
73
+ bind:open={search.open}
74
+ shouldFilter={false}
75
+ title="Search"
76
+ description="Search the documentation"
77
+ class="ring-border/70 shadow-2xl ring-1 sm:max-w-xl"
78
+ >
79
+ <Command.Input bind:value={query} {placeholder} />
80
+
81
+ <div class="bg-border/50 -mx-1 mt-1 h-px"></div>
82
+
83
+ <Command.List class="scrollbar-slim max-h-[min(24rem,60vh)] px-2 py-2">
84
+ {#if status === 'loading'}
85
+ <div class="text-muted-foreground flex items-center justify-center gap-2 py-10 text-sm">
86
+ <LoaderCircle class="size-4 animate-spin" />
87
+ Loading search…
88
+ </div>
89
+ {:else if status === 'error'}
90
+ <div class="text-muted-foreground px-4 py-10 text-center text-sm">
91
+ Search is unavailable right now.
92
+ </div>
93
+ {:else if !trimmed}
94
+ <div
95
+ class="text-muted-foreground flex flex-col items-center gap-3 px-4 py-10 text-center text-sm"
96
+ >
97
+ <span
98
+ class="bg-muted/60 text-muted-foreground flex size-11 items-center justify-center rounded-full"
99
+ >
100
+ <SearchIcon class="size-5" />
101
+ </span>
102
+ <span>Search across every page of the documentation.</span>
103
+ </div>
104
+ {:else if results.length === 0}
105
+ <div class="text-muted-foreground px-4 py-10 text-center text-sm">
106
+ No results for <span class="text-foreground font-medium">“{trimmed}”</span>.
107
+ </div>
108
+ {:else}
109
+ {#each results as result (result.path)}
110
+ <Command.LinkItem
111
+ href={result.path}
112
+ onSelect={() => select(result.path)}
113
+ class="group/result mb-0.5 flex items-center gap-3 rounded-lg px-2.5 py-2 aria-selected:bg-accent aria-selected:text-accent-foreground"
114
+ >
115
+ <span
116
+ class="border-border/60 bg-muted/40 text-muted-foreground group-aria-selected/result:border-accent-foreground/20 group-aria-selected/result:text-accent-foreground flex size-8 shrink-0 items-center justify-center rounded-md border"
117
+ >
118
+ <FileText class="size-4" />
119
+ </span>
120
+
121
+ <span class="flex min-w-0 flex-1 flex-col gap-0.5">
122
+ <span class="flex items-center gap-2">
123
+ <span class="truncate font-medium">{result.title}</span>
124
+ {#if result.section}
125
+ <span
126
+ class="border-border/60 text-muted-foreground group-aria-selected/result:border-accent-foreground/20 group-aria-selected/result:text-accent-foreground/80 ml-auto shrink-0 rounded-full border px-2 py-0.5 text-[10px] font-medium tracking-wide uppercase"
127
+ >
128
+ {result.section}
129
+ </span>
130
+ {/if}
131
+ </span>
132
+ {#if result.snippet}
133
+ <span
134
+ class="text-muted-foreground group-aria-selected/result:text-accent-foreground/80 line-clamp-1 text-xs"
135
+ >
136
+ {result.snippet}
137
+ </span>
138
+ {/if}
139
+ </span>
140
+
141
+ <CornerDownLeft
142
+ class="text-muted-foreground group-aria-selected/result:text-accent-foreground size-4 shrink-0 opacity-0 group-aria-selected/result:opacity-100"
143
+ />
144
+ </Command.LinkItem>
145
+ {/each}
146
+ {/if}
147
+ </Command.List>
148
+
149
+ <div
150
+ class="border-border/50 text-muted-foreground -mx-1 flex items-center gap-4 border-t px-4 py-2 text-[11px]"
151
+ >
152
+ <span class="flex items-center gap-1.5">
153
+ <kbd class="border-border/60 bg-muted/40 rounded border px-1.5 py-0.5 font-sans">↑</kbd>
154
+ <kbd class="border-border/60 bg-muted/40 rounded border px-1.5 py-0.5 font-sans">↓</kbd>
155
+ to navigate
156
+ </span>
157
+ <span class="flex items-center gap-1.5">
158
+ <kbd class="border-border/60 bg-muted/40 rounded border px-1.5 py-0.5 font-sans">↵</kbd>
159
+ to open
160
+ </span>
161
+ <span class="ml-auto flex items-center gap-1.5">
162
+ <kbd class="border-border/60 bg-muted/40 rounded border px-1.5 py-0.5 font-sans">esc</kbd>
163
+ to close
164
+ </span>
165
+ </div>
166
+ </Command.Dialog>
167
+ {/if}
@@ -0,0 +1,13 @@
1
+ import type { SearchDoc } from '../config.js';
2
+ type $$ComponentProps = {
3
+ /**
4
+ * Lazily provide the generated search records. Wired by the consumer so the
5
+ * index is code-split and only fetched when the palette first opens, e.g.
6
+ * `() => import('svelte-docsmith/search').then((m) => m.docs)`.
7
+ */
8
+ load: () => Promise<SearchDoc[]>;
9
+ placeholder?: string;
10
+ };
11
+ declare const Search: import("svelte").Component<$$ComponentProps, {}, "">;
12
+ type Search = ReturnType<typeof Search>;
13
+ export default Search;
@@ -1,4 +1,4 @@
1
1
  import { Accordion as AccordionPrimitive } from 'bits-ui';
2
- declare const Accordion: import("svelte").Component<AccordionPrimitive.RootProps, {}, "ref" | "value">;
2
+ declare const Accordion: import("svelte").Component<AccordionPrimitive.RootProps, {}, "value" | "ref">;
3
3
  type Accordion = ReturnType<typeof Accordion>;
4
4
  export default Accordion;
@@ -0,0 +1,42 @@
1
+ <script lang="ts">
2
+ import type { Command as CommandPrimitive, Dialog as DialogPrimitive } from 'bits-ui';
3
+ import type { Snippet } from 'svelte';
4
+ import Command from './command.svelte';
5
+ import * as Dialog from '../dialog/index.js';
6
+ import { cn, type WithoutChildrenOrChild } from '../../../shadcn.js';
7
+
8
+ let {
9
+ open = $bindable(false),
10
+ ref = $bindable(null),
11
+ value = $bindable(''),
12
+ title = 'Command Palette',
13
+ description = 'Search for a command to run...',
14
+ showCloseButton = false,
15
+ portalProps,
16
+ children,
17
+ class: className,
18
+ ...restProps
19
+ }: WithoutChildrenOrChild<DialogPrimitive.RootProps> &
20
+ WithoutChildrenOrChild<CommandPrimitive.RootProps> & {
21
+ portalProps?: DialogPrimitive.PortalProps;
22
+ children: Snippet;
23
+ title?: string;
24
+ description?: string;
25
+ showCloseButton?: boolean;
26
+ class?: string;
27
+ } = $props();
28
+ </script>
29
+
30
+ <Dialog.Root bind:open {...restProps}>
31
+ <Dialog.Header class="sr-only">
32
+ <Dialog.Title>{title}</Dialog.Title>
33
+ <Dialog.Description>{description}</Dialog.Description>
34
+ </Dialog.Header>
35
+ <Dialog.Content
36
+ class={cn('rounded-xl! top-1/3 translate-y-0 overflow-hidden p-0', className)}
37
+ {showCloseButton}
38
+ {portalProps}
39
+ >
40
+ <Command {...restProps} bind:value bind:ref {children} />
41
+ </Dialog.Content>
42
+ </Dialog.Root>
@@ -0,0 +1,14 @@
1
+ import type { Command as CommandPrimitive, Dialog as DialogPrimitive } from 'bits-ui';
2
+ import type { Snippet } from 'svelte';
3
+ import { type WithoutChildrenOrChild } from '../../../shadcn.js';
4
+ type $$ComponentProps = WithoutChildrenOrChild<DialogPrimitive.RootProps> & WithoutChildrenOrChild<CommandPrimitive.RootProps> & {
5
+ portalProps?: DialogPrimitive.PortalProps;
6
+ children: Snippet;
7
+ title?: string;
8
+ description?: string;
9
+ showCloseButton?: boolean;
10
+ class?: string;
11
+ };
12
+ declare const CommandDialog: import("svelte").Component<$$ComponentProps, {}, "value" | "ref" | "open">;
13
+ type CommandDialog = ReturnType<typeof CommandDialog>;
14
+ export default CommandDialog;
@@ -0,0 +1,17 @@
1
+ <script lang="ts">
2
+ import { Command as CommandPrimitive } from 'bits-ui';
3
+ import { cn } from '../../../shadcn.js';
4
+
5
+ let {
6
+ ref = $bindable(null),
7
+ class: className,
8
+ ...restProps
9
+ }: CommandPrimitive.EmptyProps = $props();
10
+ </script>
11
+
12
+ <CommandPrimitive.Empty
13
+ bind:ref
14
+ data-slot="command-empty"
15
+ class={cn('py-6 text-center text-sm', className)}
16
+ {...restProps}
17
+ />
@@ -0,0 +1,4 @@
1
+ import { Command as CommandPrimitive } from 'bits-ui';
2
+ declare const CommandEmpty: import("svelte").Component<CommandPrimitive.EmptyProps, {}, "ref">;
3
+ type CommandEmpty = ReturnType<typeof CommandEmpty>;
4
+ export default CommandEmpty;
@@ -0,0 +1,33 @@
1
+ <script lang="ts">
2
+ import { Command as CommandPrimitive, useId } from 'bits-ui';
3
+ import { cn } from '../../../shadcn.js';
4
+
5
+ let {
6
+ ref = $bindable(null),
7
+ class: className,
8
+ children,
9
+ heading,
10
+ value,
11
+ ...restProps
12
+ }: CommandPrimitive.GroupProps & {
13
+ heading?: string;
14
+ } = $props();
15
+ </script>
16
+
17
+ <CommandPrimitive.Group
18
+ bind:ref
19
+ data-slot="command-group"
20
+ class={cn(
21
+ 'text-foreground **:[[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-1 **:[[cmdk-group-heading]]:px-2 **:[[cmdk-group-heading]]:py-1.5 **:[[cmdk-group-heading]]:text-xs **:[[cmdk-group-heading]]:font-medium',
22
+ className
23
+ )}
24
+ value={value ?? heading ?? `----${useId()}`}
25
+ {...restProps}
26
+ >
27
+ {#if heading}
28
+ <CommandPrimitive.GroupHeading class="text-muted-foreground px-2 py-1.5 text-xs font-medium">
29
+ {heading}
30
+ </CommandPrimitive.GroupHeading>
31
+ {/if}
32
+ <CommandPrimitive.GroupItems {children} />
33
+ </CommandPrimitive.Group>
@@ -0,0 +1,7 @@
1
+ import { Command as CommandPrimitive } from 'bits-ui';
2
+ type $$ComponentProps = CommandPrimitive.GroupProps & {
3
+ heading?: string;
4
+ };
5
+ declare const CommandGroup: import("svelte").Component<$$ComponentProps, {}, "ref">;
6
+ type CommandGroup = ReturnType<typeof CommandGroup>;
7
+ export default CommandGroup;
@@ -0,0 +1,36 @@
1
+ <script lang="ts">
2
+ import { Command as CommandPrimitive } from 'bits-ui';
3
+ import { cn } from '../../../shadcn.js';
4
+ import * as InputGroup from '../input-group/index.js';
5
+ import SearchIcon from '@lucide/svelte/icons/search';
6
+
7
+ let {
8
+ ref = $bindable(null),
9
+ class: className,
10
+ value = $bindable(''),
11
+ ...restProps
12
+ }: CommandPrimitive.InputProps = $props();
13
+ </script>
14
+
15
+ <div data-slot="command-input-wrapper" class="p-1 pb-0">
16
+ <InputGroup.Root
17
+ class="bg-input/30 border-input/30 h-8! rounded-lg! shadow-none! *:data-[slot=input-group-addon]:pl-2!"
18
+ >
19
+ <CommandPrimitive.Input
20
+ {value}
21
+ data-slot="command-input"
22
+ class={cn(
23
+ 'w-full text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50',
24
+ className
25
+ )}
26
+ {...restProps}
27
+ >
28
+ {#snippet child({ props })}
29
+ <InputGroup.Input {...props} bind:value bind:ref />
30
+ {/snippet}
31
+ </CommandPrimitive.Input>
32
+ <InputGroup.Addon>
33
+ <SearchIcon class="size-4 shrink-0 opacity-50" />
34
+ </InputGroup.Addon>
35
+ </InputGroup.Root>
36
+ </div>