svelte-docsmith 0.2.0 → 0.4.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 +66 -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 +15 -7
  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 +79 -29
  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
@@ -5,9 +5,13 @@
5
5
  const { children, ...attrs }: HeadingProps = $props();
6
6
  </script>
7
7
 
8
- <h3 {...attrs} class="flex gap-2 items-center group">
8
+ <h3 {...attrs} class="group flex items-center gap-2">
9
9
  {@render children()}
10
- <a href="#{attrs.id}" class="opacity-0 group-hover:opacity-100 transition-opacity">
11
- <Link class="size-5 text-primary" />
10
+ <a
11
+ href="#{attrs.id}"
12
+ aria-label="Link to this section"
13
+ class="opacity-0 transition-opacity group-hover:opacity-100 focus-visible:opacity-100"
14
+ >
15
+ <Link class="text-primary size-5" />
12
16
  </a>
13
17
  </h3>
@@ -20,24 +20,6 @@
20
20
  <pre class="not-prose shiki max-h-[32rem] flex shrink" use:clipboard.readText>
21
21
  {@render children()}
22
22
  </pre>
23
-
24
- <!-- Scroll indicators with increased fade intensity -->
25
- <div
26
- class="absolute bottom-0 left-0 right-0 h-4 bg-gradient-to-t from-muted via-muted/70 to-transparent pointer-events-none"
27
- aria-hidden="true"
28
- ></div>
29
- <div
30
- class="absolute top-0 left-0 right-0 h-4 bg-gradient-to-b from-muted via-muted/70 to-transparent pointer-events-none"
31
- aria-hidden="true"
32
- ></div>
33
- <div
34
- class="absolute top-0 bottom-0 right-0 w-4 bg-gradient-to-l from-muted via-muted/70 to-transparent pointer-events-none"
35
- aria-hidden="true"
36
- ></div>
37
- <div
38
- class="absolute top-0 bottom-0 left-0 w-4 bg-gradient-to-r from-muted via-muted/70 to-transparent pointer-events-none"
39
- aria-hidden="true"
40
- ></div>
41
23
  </ScrollArea>
42
24
  </div>
43
25
 
@@ -46,7 +28,7 @@
46
28
  * Plain CSS (not @apply): these style Shiki's output and ship inside the
47
29
  * published package, where the consumer's Tailwind theme context isn't
48
30
  * available to resolve @apply (Tailwind v4 compiles scoped styles in
49
- * isolation). See PLAN.md §2.5.
31
+ * isolation).
50
32
  */
51
33
  :global(pre code) {
52
34
  display: block;
@@ -62,27 +44,95 @@
62
44
  padding-inline: 1rem;
63
45
  }
64
46
 
47
+ /* Two tiers of annotation. EMPHASIS (highlight, word) tints with the theme's
48
+ --primary, so it reads as the brand pointing at a line and recolors with
49
+ the active theme. SEMANTIC (diff, error, warning) uses fixed OKLCH hues so
50
+ green/red/amber stay legible on any theme. All are translucent washes over
51
+ the code background via color-mix. */
52
+
53
+ /* --- Line highlight (// [!code highlight] and {1,3} meta) --- */
65
54
  :global(pre span.line.highlighted) {
66
- background-color: rgb(254 240 138 / 0.5);
55
+ background: color-mix(in oklch, var(--primary) 12%, transparent);
67
56
  }
68
-
69
57
  :global(.dark pre span.line.highlighted) {
70
- background-color: rgb(250 204 21 / 0.2);
58
+ background: color-mix(in oklch, var(--primary) 20%, transparent);
71
59
  }
72
60
 
61
+ /* --- Diff (// [!code ++] / [!code --]) with +/- gutter glyphs --- */
62
+ :global(pre span.line.diff) {
63
+ position: relative;
64
+ }
73
65
  :global(pre span.line.diff.add) {
74
- background-color: rgb(134 239 172 / 0.5);
66
+ background: color-mix(in oklch, oklch(0.7 0.16 150) 15%, transparent);
75
67
  }
76
-
77
68
  :global(pre span.line.diff.remove) {
78
- background-color: rgb(252 165 165 / 0.5);
69
+ background: color-mix(in oklch, oklch(0.62 0.2 25) 14%, transparent);
79
70
  }
80
-
81
71
  :global(.dark pre span.line.diff.add) {
82
- background-color: rgb(22 163 74 / 0.2);
72
+ background: color-mix(in oklch, oklch(0.7 0.16 150) 22%, transparent);
83
73
  }
84
-
85
74
  :global(.dark pre span.line.diff.remove) {
86
- background-color: rgb(220 38 38 / 0.2);
75
+ background: color-mix(in oklch, oklch(0.62 0.2 25) 22%, transparent);
76
+ }
77
+ :global(pre span.line.diff::before) {
78
+ position: absolute;
79
+ left: 0.45rem;
80
+ font-weight: 600;
81
+ }
82
+ :global(pre span.line.diff.add::before) {
83
+ content: '+';
84
+ color: oklch(0.6 0.15 150);
85
+ }
86
+ :global(pre span.line.diff.remove::before) {
87
+ content: '−';
88
+ color: oklch(0.58 0.19 25);
89
+ }
90
+ :global(.dark pre span.line.diff.add::before) {
91
+ color: oklch(0.78 0.17 150);
92
+ }
93
+ :global(.dark pre span.line.diff.remove::before) {
94
+ color: oklch(0.72 0.18 25);
95
+ }
96
+
97
+ /* --- Error / warning severity (// [!code error] / [!code warning]).
98
+ No gutter glyph, so they stay distinct from the +/- diff lines. --- */
99
+ :global(pre span.line.highlighted.error) {
100
+ background: color-mix(in oklch, oklch(0.62 0.2 25) 18%, transparent);
101
+ }
102
+ :global(pre span.line.highlighted.warning) {
103
+ background: color-mix(in oklch, oklch(0.8 0.13 85) 22%, transparent);
104
+ }
105
+ :global(.dark pre span.line.highlighted.error) {
106
+ background: color-mix(in oklch, oklch(0.62 0.2 25) 26%, transparent);
107
+ }
108
+ :global(.dark pre span.line.highlighted.warning) {
109
+ background: color-mix(in oklch, oklch(0.8 0.13 85) 20%, transparent);
110
+ }
111
+
112
+ /* --- Focus (// [!code focus]): dim the other lines and restore on hover.
113
+ A clean opacity fade reads calmer than a muddy blur. `:has` recreates
114
+ Shiki's stripped container flag from the line classes alone. --- */
115
+ :global(pre code:has(span.line.focused) span.line:not(.focused)) {
116
+ opacity: 0.42;
117
+ transition: opacity 0.25s ease;
118
+ }
119
+ :global(pre:hover code:has(span.line.focused) span.line:not(.focused)) {
120
+ opacity: 1;
121
+ }
122
+ @media (prefers-reduced-motion: reduce) {
123
+ :global(pre code:has(span.line.focused) span.line:not(.focused)) {
124
+ transition: none;
125
+ }
126
+ }
127
+
128
+ /* --- Word highlight (// [!code word:name]): a primary-tinted inline pill --- */
129
+ :global(pre .highlighted-word) {
130
+ border-radius: 0.3rem;
131
+ padding: 0.1rem 0.3rem;
132
+ background: color-mix(in oklch, var(--primary) 16%, transparent);
133
+ box-shadow: 0 0 0 1px color-mix(in oklch, var(--primary) 35%, transparent);
134
+ }
135
+ :global(.dark pre .highlighted-word) {
136
+ background: color-mix(in oklch, var(--primary) 24%, transparent);
87
137
  }
88
138
  </style>
@@ -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;