@heyramzi/ui 6.1.0 → 8.0.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 (332) hide show
  1. package/COMPONENTS.md +32 -40
  2. package/README.md +10 -4
  3. package/package.json +31 -29
  4. package/scripts/sync-shadcn.mjs +29 -16
  5. package/scripts/sync-transitions.mjs +11 -11
  6. package/src/ai/ai-button/AiButton.svelte +86 -0
  7. package/src/ai/ai-button/index.ts +4 -0
  8. package/src/ai/confirmation/Confirmation.svelte +8 -8
  9. package/src/ai/confirmation/confirmation-context.svelte.ts +2 -2
  10. package/src/ai/generation-progress/GenerationProgress.svelte +354 -0
  11. package/src/ai/generation-progress/index.ts +7 -0
  12. package/src/ai/index.ts +16 -24
  13. package/src/ai/prompt-input/PromptInput.svelte +19 -1
  14. package/src/ai/prompt-input/PromptInputSubmit.svelte +9 -3
  15. package/src/ai/reasoning/Reasoning.svelte +11 -8
  16. package/src/ambient.d.ts +4 -0
  17. package/src/animations/README.md +17 -17
  18. package/src/animations/index.css +3 -14
  19. package/src/animations/upstream-coverage.test.ts +1 -1
  20. package/src/components/copy/clipboard.test.ts +206 -0
  21. package/src/components/copy/clipboard.ts +49 -0
  22. package/src/components/copy/copied.svelte.ts +39 -0
  23. package/src/components/copy/copy-button.svelte +175 -0
  24. package/src/components/copy/copy-button.test.ts +110 -0
  25. package/src/components/copy/index.ts +3 -0
  26. package/src/components/data-table/data-table.test.ts +134 -0
  27. package/src/components/data-table/data-table.ts +77 -0
  28. package/src/components/data-table/index.ts +20 -0
  29. package/src/components/data-table/infinite-scroll.svelte +69 -0
  30. package/src/components/data-table/pagination.svelte +62 -0
  31. package/src/components/data-table/row-arrow.svelte +28 -0
  32. package/src/components/data-table/row-context-menu.svelte +106 -0
  33. package/src/components/data-table/row-context-menu.test.ts +18 -0
  34. package/src/components/data-table/sortable-head.svelte +56 -0
  35. package/src/components/data-table/table-skeleton.svelte +83 -0
  36. package/src/components/hold-to-confirm/hold-to-confirm.svelte +232 -0
  37. package/src/components/hold-to-confirm/index.ts +1 -0
  38. package/src/components/icon/icon.svelte +23 -0
  39. package/src/components/icon/index.ts +1 -0
  40. package/src/components/language-flag/index.ts +1 -0
  41. package/src/components/language-flag/language-flag.svelte +60 -0
  42. package/src/components/list-page/detail-empty.svelte +50 -0
  43. package/src/components/list-page/detail-header.svelte +107 -0
  44. package/src/components/list-page/detail-panel.svelte +90 -0
  45. package/src/components/list-page/detail-row.svelte +36 -0
  46. package/src/components/list-page/detail-stack.svelte +33 -0
  47. package/src/components/list-page/empty-state.svelte +91 -0
  48. package/src/components/list-page/filter-item.svelte +49 -0
  49. package/src/components/list-page/filter-popover.svelte +88 -0
  50. package/src/components/list-page/filter-section.ts +12 -0
  51. package/src/components/list-page/grouped-list.svelte +116 -0
  52. package/src/components/list-page/index.ts +28 -0
  53. package/src/components/list-page/list-page-shell.svelte +195 -0
  54. package/src/components/list-page/list-table.ts +19 -0
  55. package/src/components/list-page/master-detail.svelte +206 -0
  56. package/src/components/list-page/master-detail.test.ts +37 -0
  57. package/src/components/list-page/notice-bar.svelte +68 -0
  58. package/src/components/list-page/prev-next.svelte +104 -0
  59. package/src/components/list-page/queue-rail.ts +25 -0
  60. package/src/components/list-page/rail-body.svelte +29 -0
  61. package/src/components/list-page/rail-empty.svelte +51 -0
  62. package/src/components/list-page/rail-head.svelte +49 -0
  63. package/src/components/list-page/rail-row.svelte +163 -0
  64. package/src/components/list-page/section-host.ts +24 -0
  65. package/src/components/list-page/section-label.svelte +39 -0
  66. package/src/components/list-page/step.test.ts +21 -0
  67. package/src/components/list-page/step.ts +10 -0
  68. package/src/components/list-page/toolbar-search.svelte +63 -0
  69. package/src/components/list-page/toolbar-trigger.ts +23 -0
  70. package/src/components/list-page/types.ts +1 -0
  71. package/src/components/locale-switch/index.ts +1 -0
  72. package/src/components/locale-switch/locale-switch.svelte +59 -0
  73. package/src/components/mark/index.ts +1 -0
  74. package/src/components/mark/mark.svelte +70 -0
  75. package/src/components/open-in/index.ts +6 -0
  76. package/src/components/open-in/open-in.svelte +106 -0
  77. package/src/components/picker/index.ts +4 -0
  78. package/src/components/picker/mark-picker.svelte +84 -0
  79. package/src/components/picker/picker.svelte +143 -0
  80. package/src/components/picker/trigger.ts +42 -0
  81. package/src/components/picker/types.ts +26 -0
  82. package/src/components/segmented/animated-tabs.svelte +175 -0
  83. package/src/components/segmented/collapse.test.ts +49 -0
  84. package/src/components/segmented/collapse.ts +39 -0
  85. package/src/components/segmented/index.ts +41 -0
  86. package/src/components/segmented/section-runs.svelte +79 -0
  87. package/src/components/segmented/segment.svelte +106 -0
  88. package/src/components/segmented/segmented-filter.svelte +125 -0
  89. package/src/components/segmented/segmented-nav.svelte +82 -0
  90. package/src/components/segmented/segmented-overflow.svelte +85 -0
  91. package/src/components/segmented/segmented-track.svelte +181 -0
  92. package/src/components/segmented/segmented.test.ts +20 -0
  93. package/src/components/segmented/segmented.ts +80 -0
  94. package/src/components/star-rating-input/index.ts +9 -0
  95. package/src/components/star-rating-input/star-rating-input.svelte +256 -0
  96. package/src/components/svg/index.ts +1 -0
  97. package/src/components/svg/svg.svelte +9 -0
  98. package/src/effects/border-beam/BorderBeam.svelte +1 -1
  99. package/src/effects/image-comparison/ImageComparison.css +42 -8
  100. package/src/effects/image-comparison/ImageComparison.svelte +138 -21
  101. package/src/effects/image-comparison/compare.test.ts +17 -0
  102. package/src/effects/image-comparison/compare.ts +5 -0
  103. package/src/effects/interactive-hover-button/InteractiveHoverButton.svelte +338 -0
  104. package/src/effects/magic-card/MagicCard.css +47 -0
  105. package/src/effects/magic-card/MagicCard.svelte +57 -0
  106. package/src/effects/marquee/Marquee.css +27 -1
  107. package/src/effects/marquee/Marquee.svelte +67 -3
  108. package/src/effects/reveal/index.ts +2 -1
  109. package/src/effects/reveal/observe.test.ts +60 -0
  110. package/src/effects/reveal/observe.ts +33 -1
  111. package/src/effects/reveal/types.ts +7 -0
  112. package/src/effects/theme-toggle/ThemeToggle.css +199 -6
  113. package/src/effects/theme-toggle/ThemeToggle.svelte +51 -26
  114. package/src/gallery.test.ts +52 -0
  115. package/src/index.ts +5 -1
  116. package/src/lib/panel.ts +7 -1
  117. package/src/lib/sliding-pill.ts +68 -0
  118. package/src/lib/theme-transition.ts +41 -13
  119. package/src/lib/tone.ts +48 -0
  120. package/src/shadcn/accordion/accordion-trigger.svelte +15 -6
  121. package/src/shadcn/alert/alert.svelte +3 -1
  122. package/src/shadcn/badge/badge.svelte +8 -7
  123. package/src/shadcn/breadcrumb/breadcrumb-ellipsis.svelte +2 -2
  124. package/src/shadcn/breadcrumb/breadcrumb-separator.svelte +2 -2
  125. package/src/shadcn/calendar/calendar-month-select.svelte +2 -2
  126. package/src/shadcn/calendar/calendar-next-button.svelte +2 -2
  127. package/src/shadcn/calendar/calendar-prev-button.svelte +2 -2
  128. package/src/shadcn/calendar/calendar-year-select.svelte +2 -2
  129. package/src/shadcn/calendar/calendar.svelte +6 -0
  130. package/src/shadcn/code-block/code-block-actions.svelte +23 -0
  131. package/src/shadcn/code-block/code-block-content.svelte +55 -0
  132. package/src/shadcn/code-block/code-block-copy-button.svelte +10 -0
  133. package/src/shadcn/code-block/code-block-footer.svelte +20 -0
  134. package/src/shadcn/code-block/code-block-header.svelte +23 -0
  135. package/src/shadcn/code-block/code-block-line.svelte +71 -0
  136. package/src/shadcn/code-block/code-block-title.svelte +23 -0
  137. package/src/shadcn/code-block/code-block.svelte +47 -0
  138. package/src/shadcn/code-block/index.ts +42 -0
  139. package/src/shadcn/command/index.ts +3 -0
  140. package/src/shadcn/field/index.ts +3 -0
  141. package/src/shadcn/field/simple-field.svelte +71 -0
  142. package/src/shadcn/field/simple-field.test.ts +47 -0
  143. package/src/shadcn/hooks/highlighter.ts +132 -0
  144. package/src/shadcn/hooks/use-highlighted-code.svelte.ts +98 -0
  145. package/src/shadcn/kbd/index.ts +10 -0
  146. package/src/shadcn/kbd/kbd-group.svelte +20 -0
  147. package/src/shadcn/kbd/kbd.svelte +23 -0
  148. package/src/shadcn/message-scroller/index.ts +28 -0
  149. package/src/shadcn/message-scroller/message-scroller-button.svelte +52 -0
  150. package/src/shadcn/message-scroller/message-scroller-content.svelte +17 -0
  151. package/src/shadcn/message-scroller/message-scroller-item.svelte +22 -0
  152. package/src/shadcn/message-scroller/message-scroller-provider.svelte +7 -0
  153. package/src/shadcn/message-scroller/message-scroller-viewport.svelte +20 -0
  154. package/src/shadcn/message-scroller/message-scroller.svelte +20 -0
  155. package/src/shadcn/radio-group/radio-group-item.svelte +3 -2
  156. package/src/shadcn/sheet/sheet-content.svelte +1 -0
  157. package/src/shadcn/sheet/sheet.css +48 -0
  158. package/src/shadcn/sonner/sonner.svelte +7 -10
  159. package/src/shadcn/tabs/tabs-list.svelte +13 -2
  160. package/src/shadcn/tabs/tabs-trigger.svelte +1 -1
  161. package/src/shadcn/toggle-group/toggle-group.svelte +10 -3
  162. package/src/shadcn/trace/index.ts +29 -0
  163. package/src/shadcn/trace/trace-collapsible-content.svelte +29 -0
  164. package/src/shadcn/trace/trace-collapsible-trigger.svelte +29 -0
  165. package/src/shadcn/trace/trace-collapsible.svelte +20 -0
  166. package/src/shadcn/trace/trace-description.svelte +20 -0
  167. package/src/shadcn/trace/trace-icon.svelte +24 -0
  168. package/src/shadcn/trace/trace-line.svelte +31 -0
  169. package/src/shadcn/trace/trace-title.svelte +20 -0
  170. package/src/shadcn/trace/trace.svelte +23 -0
  171. package/src/ai/composer/Composer.css +0 -108
  172. package/src/ai/composer/Composer.svelte +0 -111
  173. package/src/ai/composer/index.ts +0 -10
  174. package/src/ai/conversation/Conversation.svelte +0 -28
  175. package/src/ai/conversation/ConversationContent.svelte +0 -35
  176. package/src/ai/conversation/ConversationEmptyState.svelte +0 -46
  177. package/src/ai/conversation/ConversationScrollButton.svelte +0 -51
  178. package/src/ai/conversation/index.ts +0 -33
  179. package/src/ai/conversation/stick-to-bottom-context.svelte.ts +0 -131
  180. package/src/animations/accordion.css +0 -58
  181. package/src/animations/icon-swap.css +0 -43
  182. package/src/animations/notification-badge.css +0 -81
  183. package/src/animations/panel.css +0 -86
  184. package/src/animations/reasoning-stream.css +0 -49
  185. package/src/animations/skeleton-reveal.css +0 -97
  186. package/src/animations/streaming-text.css +0 -31
  187. package/src/animations/tabs-sliding.css +0 -83
  188. package/src/animations/texts-reveal.css +0 -70
  189. package/src/animations/tooltip.css +0 -72
  190. package/src/animations/view-transition-reveal.ts +0 -74
  191. package/src/charts/animated-value.css +0 -7
  192. package/src/charts/animated-value.svelte +0 -110
  193. package/src/charts/area-chart.css +0 -29
  194. package/src/charts/area-chart.svelte +0 -195
  195. package/src/charts/bar-chart.css +0 -119
  196. package/src/charts/bar-chart.svelte +0 -269
  197. package/src/charts/charts.css +0 -201
  198. package/src/charts/crosshair.svelte +0 -35
  199. package/src/charts/donut-chart.css +0 -56
  200. package/src/charts/donut-chart.svelte +0 -158
  201. package/src/charts/edge-fade.svelte +0 -43
  202. package/src/charts/edge-fade.ts +0 -16
  203. package/src/charts/figure.css +0 -100
  204. package/src/charts/figure.svelte +0 -122
  205. package/src/charts/gauge-chart.css +0 -94
  206. package/src/charts/gauge-chart.svelte +0 -303
  207. package/src/charts/hover-band.test.ts +0 -30
  208. package/src/charts/hover-band.ts +0 -21
  209. package/src/charts/hover-focus.svelte +0 -85
  210. package/src/charts/index.ts +0 -30
  211. package/src/charts/legend.css +0 -131
  212. package/src/charts/legend.svelte +0 -112
  213. package/src/charts/line-chart.css +0 -26
  214. package/src/charts/line-chart.svelte +0 -256
  215. package/src/charts/motion.ts +0 -19
  216. package/src/charts/number-chart.css +0 -168
  217. package/src/charts/number-chart.svelte +0 -177
  218. package/src/charts/primitives/chart-container.svelte +0 -57
  219. package/src/charts/primitives/chart-style.svelte +0 -36
  220. package/src/charts/primitives/chart-tooltip.svelte +0 -161
  221. package/src/charts/primitives/chart-utils.ts +0 -54
  222. package/src/charts/primitives/index.ts +0 -11
  223. package/src/charts/reveal.svelte +0 -61
  224. package/src/charts/ring-chart.css +0 -96
  225. package/src/charts/ring-chart.svelte +0 -158
  226. package/src/charts/sparkline.css +0 -45
  227. package/src/charts/sparkline.svelte +0 -140
  228. package/src/charts/stat-card.css +0 -103
  229. package/src/charts/stat-card.svelte +0 -267
  230. package/src/charts/time-series-chart.css +0 -28
  231. package/src/charts/time-series-chart.svelte +0 -179
  232. package/src/charts/trend-pill.css +0 -38
  233. package/src/charts/trend-pill.svelte +0 -48
  234. package/src/lib/hooks/is-mobile.svelte.ts +0 -11
  235. package/src/lib/utils.ts +0 -45
  236. package/src/showcase/AiSection.svelte +0 -157
  237. package/src/showcase/AnimationsSection.svelte +0 -1658
  238. package/src/showcase/ChartsSection.svelte +0 -458
  239. package/src/showcase/EffectsSection.svelte +0 -152
  240. package/src/showcase/ShadcnSection.svelte +0 -27
  241. package/src/showcase/ShowcaseImport.svelte +0 -126
  242. package/src/showcase/ShowcaseList.svelte +0 -132
  243. package/src/showcase/ShowcaseNav.svelte +0 -247
  244. package/src/showcase/ShowcaseSection.svelte +0 -112
  245. package/src/showcase/ShowcaseShell.svelte +0 -55
  246. package/src/showcase/ShowcaseStage.svelte +0 -342
  247. package/src/showcase/ShowcaseVariant.svelte +0 -109
  248. package/src/showcase/assets/image-reveal/art-1.jpg +0 -0
  249. package/src/showcase/assets/image-reveal/art-2.jpg +0 -0
  250. package/src/showcase/assets/image-reveal/art-3.jpg +0 -0
  251. package/src/showcase/check.svg +0 -1
  252. package/src/showcase/chevron-arms.svg +0 -2
  253. package/src/showcase/chevron-down.svg +0 -1
  254. package/src/showcase/copy.svg +0 -2
  255. package/src/showcase/demos/ai/confirmation.svelte +0 -59
  256. package/src/showcase/demos/ai/conversation.svelte +0 -59
  257. package/src/showcase/demos/ai/loading-state.svelte +0 -19
  258. package/src/showcase/demos/ai/prompt-input.svelte +0 -57
  259. package/src/showcase/demos/ai/reasoning.svelte +0 -19
  260. package/src/showcase/demos/ai/thinking-dots.svelte +0 -15
  261. package/src/showcase/demos/ai/voice-waveform.svelte +0 -24
  262. package/src/showcase/demos/effects/animated-beam.svelte +0 -50
  263. package/src/showcase/demos/effects/animated-group.svelte +0 -47
  264. package/src/showcase/demos/effects/border-beam-default.svelte +0 -20
  265. package/src/showcase/demos/effects/border-beam-line.svelte +0 -20
  266. package/src/showcase/demos/effects/border-beam-mono.svelte +0 -20
  267. package/src/showcase/demos/effects/border-beam-sunset.svelte +0 -20
  268. package/src/showcase/demos/effects/image-comparison.svelte +0 -25
  269. package/src/showcase/demos/effects/image-reveal.svelte +0 -49
  270. package/src/showcase/demos/effects/in-view.svelte +0 -41
  271. package/src/showcase/demos/effects/integrations-hub.svelte +0 -82
  272. package/src/showcase/demos/effects/magnetic.svelte +0 -31
  273. package/src/showcase/demos/effects/marquee.svelte +0 -33
  274. package/src/showcase/demos/effects/progressive-blur.svelte +0 -47
  275. package/src/showcase/demos/effects/spotlight.svelte +0 -40
  276. package/src/showcase/demos/effects/text-effect.svelte +0 -55
  277. package/src/showcase/demos/shadcn/accordion.svelte +0 -45
  278. package/src/showcase/demos/shadcn/alert-dialog.svelte +0 -23
  279. package/src/showcase/demos/shadcn/alert.svelte +0 -30
  280. package/src/showcase/demos/shadcn/attachment.svelte +0 -71
  281. package/src/showcase/demos/shadcn/avatar.svelte +0 -30
  282. package/src/showcase/demos/shadcn/badge.svelte +0 -26
  283. package/src/showcase/demos/shadcn/breadcrumb.svelte +0 -34
  284. package/src/showcase/demos/shadcn/bubble.svelte +0 -36
  285. package/src/showcase/demos/shadcn/button.svelte +0 -11
  286. package/src/showcase/demos/shadcn/calendar.svelte +0 -8
  287. package/src/showcase/demos/shadcn/card.svelte +0 -39
  288. package/src/showcase/demos/shadcn/checkbox.svelte +0 -39
  289. package/src/showcase/demos/shadcn/collapsible.svelte +0 -20
  290. package/src/showcase/demos/shadcn/command.svelte +0 -48
  291. package/src/showcase/demos/shadcn/context-menu.svelte +0 -54
  292. package/src/showcase/demos/shadcn/demo-harness.svelte +0 -12
  293. package/src/showcase/demos/shadcn/demos.test.ts +0 -20
  294. package/src/showcase/demos/shadcn/dialog.svelte +0 -38
  295. package/src/showcase/demos/shadcn/dropdown-menu.svelte +0 -59
  296. package/src/showcase/demos/shadcn/empty.svelte +0 -29
  297. package/src/showcase/demos/shadcn/field.svelte +0 -115
  298. package/src/showcase/demos/shadcn/hover-card.svelte +0 -32
  299. package/src/showcase/demos/shadcn/index.ts +0 -358
  300. package/src/showcase/demos/shadcn/input-otp.svelte +0 -19
  301. package/src/showcase/demos/shadcn/input.svelte +0 -5
  302. package/src/showcase/demos/shadcn/label.svelte +0 -11
  303. package/src/showcase/demos/shadcn/marker.svelte +0 -30
  304. package/src/showcase/demos/shadcn/message.svelte +0 -75
  305. package/src/showcase/demos/shadcn/popover.svelte +0 -36
  306. package/src/showcase/demos/shadcn/progress.svelte +0 -13
  307. package/src/showcase/demos/shadcn/radio-group.svelte +0 -19
  308. package/src/showcase/demos/shadcn/scroll-area.svelte +0 -18
  309. package/src/showcase/demos/shadcn/select.svelte +0 -29
  310. package/src/showcase/demos/shadcn/separator.svelte +0 -12
  311. package/src/showcase/demos/shadcn/sheet.svelte +0 -48
  312. package/src/showcase/demos/shadcn/skeleton.svelte +0 -11
  313. package/src/showcase/demos/shadcn/slider.svelte +0 -7
  314. package/src/showcase/demos/shadcn/sonner.svelte +0 -18
  315. package/src/showcase/demos/shadcn/spinner.svelte +0 -11
  316. package/src/showcase/demos/shadcn/switch.svelte +0 -9
  317. package/src/showcase/demos/shadcn/table.svelte +0 -76
  318. package/src/showcase/demos/shadcn/tabs.svelte +0 -62
  319. package/src/showcase/demos/shadcn/textarea.svelte +0 -5
  320. package/src/showcase/demos/shadcn/toggle-group.svelte +0 -18
  321. package/src/showcase/demos/shadcn/toggle.svelte +0 -14
  322. package/src/showcase/demos/shadcn/tooltip.svelte +0 -13
  323. package/src/showcase/heart.svg +0 -1
  324. package/src/showcase/index.ts +0 -38
  325. package/src/showcase/kit.test.ts +0 -72
  326. package/src/showcase/kit.ts +0 -17
  327. package/src/showcase/plus.svg +0 -1
  328. package/src/showcase/showcase-shell.css +0 -91
  329. package/src/showcase/stacked-section.css +0 -15
  330. package/src/showcase/success-check.svg +0 -1
  331. package/src/showcase/types.ts +0 -29
  332. package/src/svg-raw.d.ts +0 -7
@@ -0,0 +1,47 @@
1
+ import { createRawSnippet, flushSync, mount, unmount } from "svelte";
2
+ import { describe, expect, it } from "vite-plus/test";
3
+ import SimpleField from "./simple-field.svelte";
4
+
5
+ const input = createRawSnippet(() => ({ render: () => `<input id="email" />` }));
6
+
7
+ function render(props: { error?: string; description?: string }) {
8
+ const target = document.createElement("div");
9
+ document.body.appendChild(target);
10
+ const app = mount(SimpleField, { target, props: { label: "Email", for: "email", children: input, ...props } });
11
+ flushSync();
12
+ return {
13
+ target,
14
+ done: () => {
15
+ void unmount(app);
16
+ target.remove();
17
+ },
18
+ };
19
+ }
20
+
21
+ describe("SimpleField", () => {
22
+ it("links the error to its input and marks it invalid", () => {
23
+ const { target, done } = render({ error: "Enter a valid email" });
24
+ const control = target.querySelector("input")!;
25
+ const message = target.querySelector("[data-slot=field-error]")!;
26
+ expect(message.textContent).toContain("Enter a valid email");
27
+ expect(message.getAttribute("role")).toBe("alert");
28
+ expect(control.getAttribute("aria-describedby")).toBe(message.id);
29
+ expect(control.getAttribute("aria-invalid")).toBe("true");
30
+ done();
31
+ });
32
+
33
+ it("links the description when there is no error", () => {
34
+ const { target, done } = render({ description: "We never share it" });
35
+ const control = target.querySelector("input")!;
36
+ const message = target.querySelector("[data-slot=field-description]")!;
37
+ expect(control.getAttribute("aria-describedby")).toBe(message.id);
38
+ expect(control.hasAttribute("aria-invalid")).toBe(false);
39
+ done();
40
+ });
41
+
42
+ it("leaves the input alone when there is nothing to say", () => {
43
+ const { target, done } = render({});
44
+ expect(target.querySelector("input")!.hasAttribute("aria-describedby")).toBe(false);
45
+ done();
46
+ });
47
+ });
@@ -0,0 +1,132 @@
1
+ import {
2
+ createHighlighterCore,
3
+ type GrammarState,
4
+ type HighlighterCore,
5
+ type ThemedToken,
6
+ } from "shiki/core";
7
+ import { createJavaScriptRegexEngine } from "shiki/engine/javascript";
8
+
9
+ // Add the languages you need. Each one loads the first time it is used.
10
+ const languages = {
11
+ bash: () => import("shiki/langs/shellscript.mjs"),
12
+ css: () => import("shiki/langs/css.mjs"),
13
+ diff: () => import("shiki/langs/diff.mjs"),
14
+ html: () => import("shiki/langs/html.mjs"),
15
+ javascript: () => import("shiki/langs/javascript.mjs"),
16
+ json: () => import("shiki/langs/json.mjs"),
17
+ jsx: () => import("shiki/langs/jsx.mjs"),
18
+ markdown: () => import("shiki/langs/markdown.mjs"),
19
+ python: () => import("shiki/langs/python.mjs"),
20
+ sql: () => import("shiki/langs/sql.mjs"),
21
+ svelte: () => import("shiki/langs/svelte.mjs"),
22
+ tsx: () => import("shiki/langs/tsx.mjs"),
23
+ typescript: () => import("shiki/langs/typescript.mjs"),
24
+ yaml: () => import("shiki/langs/yaml.mjs"),
25
+ };
26
+
27
+ const aliases: Record<string, keyof typeof languages> = {
28
+ js: "javascript",
29
+ md: "markdown",
30
+ py: "python",
31
+ sh: "bash",
32
+ shell: "bash",
33
+ shellscript: "bash",
34
+ ts: "typescript",
35
+ yml: "yaml",
36
+ zsh: "bash",
37
+ };
38
+
39
+ let highlighter: Promise<HighlighterCore> | undefined;
40
+
41
+ function getHighlighter() {
42
+ highlighter ??= createHighlighterCore({
43
+ themes: [import("shiki/themes/github-light.mjs"), import("shiki/themes/github-dark.mjs")],
44
+ langs: [],
45
+ engine: createJavaScriptRegexEngine({ forgiving: true }),
46
+ });
47
+
48
+ return highlighter;
49
+ }
50
+
51
+ function getLanguage(language: string) {
52
+ const name = language.toLowerCase();
53
+
54
+ if (name === "ansi") {
55
+ return name;
56
+ }
57
+
58
+ if (name in languages) {
59
+ return name as keyof typeof languages;
60
+ }
61
+
62
+ return aliases[name] ?? null;
63
+ }
64
+
65
+ // One highlighted token. Render it as a `<span>` with this style and class.
66
+ export type HighlightedToken = {
67
+ content: string;
68
+ style?: string;
69
+ class?: string;
70
+ };
71
+
72
+ // A line of plain text, or the highlighted tokens of a line.
73
+ export type HighlightedLine = string | HighlightedToken[];
74
+
75
+ function toHighlightedLine(tokens: ThemedToken[]): HighlightedToken[] {
76
+ return tokens.map((token) => ({
77
+ content: token.content,
78
+ style: Object.entries(token.htmlStyle ?? {})
79
+ .map(([property, value]) => `${property}:${value}`)
80
+ .join(";"),
81
+ class: "text-(--shiki-light) dark:text-(--shiki-dark)",
82
+ }));
83
+ }
84
+
85
+ export type Highlighter = NonNullable<Awaited<ReturnType<typeof loadHighlighter>>>;
86
+
87
+ // Loads a language and returns a highlighter for it, or null for a language
88
+ // that is not listed.
89
+ export async function loadHighlighter(language: string) {
90
+ const lang = getLanguage(language);
91
+
92
+ if (!lang) {
93
+ return null;
94
+ }
95
+
96
+ const shiki = await getHighlighter();
97
+
98
+ if (lang !== "ansi" && !shiki.getLoadedLanguages().includes(lang)) {
99
+ await shiki.loadLanguage(languages[lang]);
100
+ }
101
+
102
+ return {
103
+ // Returns the tokens of each line, and the grammar state after the last
104
+ // line. Pass that state back to continue where the code left off.
105
+ highlight(code: string, state?: GrammarState) {
106
+ const tokenize = (text: string) =>
107
+ shiki.codeToTokens(text, {
108
+ lang,
109
+ themes: { light: "github-light", dark: "github-dark" },
110
+ defaultColor: false,
111
+ grammarState: state,
112
+ }).tokens;
113
+
114
+ // ANSI has no grammar state, so each line is tokenized on its own and
115
+ // gets the same colors whether it arrives in one piece or streams.
116
+ const tokens =
117
+ lang === "ansi" ? code.split("\n").flatMap((line) => tokenize(line)) : tokenize(code);
118
+
119
+ return {
120
+ lines: tokens.map((line) => toHighlightedLine(line)),
121
+ state: shiki.getLastGrammarState(tokens),
122
+ };
123
+ },
124
+ };
125
+ }
126
+
127
+ // Returns the tokens of each line, or null for a language that is not listed.
128
+ export async function highlightCode(code: string, language: string) {
129
+ const highlighter = await loadHighlighter(language);
130
+
131
+ return highlighter ? highlighter.highlight(code).lines : null;
132
+ }
@@ -0,0 +1,98 @@
1
+ import {
2
+ loadHighlighter,
3
+ type Highlighter,
4
+ type HighlightedLine,
5
+ } from "./highlighter.js";
6
+ import type { GrammarState } from "shiki/core";
7
+
8
+ /**
9
+ * Highlights `code` in `language`. `current` holds one entry per line: a plain
10
+ * string until the highlighter loads, then the line's tokens. Pass each one to
11
+ * the `code` prop of a `CodeBlock.Line`.
12
+ *
13
+ * ```svelte
14
+ * <script lang="ts">
15
+ * const lines = useHighlightedCode(() => code, () => "json");
16
+ * </script>
17
+ *
18
+ * {#each lines.current as line, index (index)}
19
+ * <CodeBlock.Line code={line} />
20
+ * {/each}
21
+ * ```
22
+ */
23
+ export function useHighlightedCode(value: () => string, language: () => string) {
24
+ let loaded = $state.raw<{
25
+ language: string;
26
+ highlighter: Highlighter | null;
27
+ } | null>(null);
28
+
29
+ $effect(() => {
30
+ const lang = language();
31
+ let cancelled = false;
32
+
33
+ loadHighlighter(lang)
34
+ .then((next) => {
35
+ if (!cancelled) {
36
+ loaded = { language: lang, highlighter: next };
37
+ }
38
+ })
39
+ .catch(() => {});
40
+
41
+ return () => {
42
+ cancelled = true;
43
+ };
44
+ });
45
+
46
+ // The complete lines, highlighted. While the code streams, only the new
47
+ // lines are tokenized, starting from the grammar state of the last one.
48
+ let done: {
49
+ highlighter: Highlighter | null;
50
+ text: string;
51
+ lines: HighlightedLine[];
52
+ state?: GrammarState;
53
+ } = { highlighter: null, text: "", lines: [] };
54
+
55
+ const lines = $derived.by((): HighlightedLine[] => {
56
+ const raw = value();
57
+ // A trailing newline would render as an extra empty line.
58
+ const code = raw.endsWith("\n") ? raw.slice(0, -1) : raw;
59
+ const lang = language();
60
+ const highlighter = loaded?.language === lang ? loaded.highlighter : null;
61
+
62
+ if (!highlighter) {
63
+ return code.split("\n");
64
+ }
65
+
66
+ let next = done;
67
+
68
+ if (next.highlighter !== highlighter || !code.startsWith(next.text)) {
69
+ next = { highlighter, text: "", lines: [] };
70
+ }
71
+
72
+ const end = code.lastIndexOf("\n") + 1;
73
+
74
+ if (end > next.text.length) {
75
+ const result = highlighter.highlight(code.slice(next.text.length, end - 1), next.state);
76
+
77
+ next = {
78
+ highlighter,
79
+ text: code.slice(0, end),
80
+ lines: [...next.lines, ...result.lines],
81
+ state: result.state,
82
+ };
83
+ }
84
+
85
+ done = next;
86
+
87
+ // The last line is still being written, so it is tokenized every time.
88
+ const tail = code.slice(end);
89
+
90
+ return [...next.lines, highlighter.highlight(tail, next.state).lines[0]];
91
+ });
92
+
93
+ return {
94
+ get current() {
95
+ return lines;
96
+ },
97
+ };
98
+ }
@@ -0,0 +1,10 @@
1
+ import Root from "./kbd.svelte";
2
+ import Group from "./kbd-group.svelte";
3
+
4
+ export {
5
+ Root,
6
+ Group,
7
+ //
8
+ Root as Kbd,
9
+ Group as KbdGroup,
10
+ };
@@ -0,0 +1,20 @@
1
+ <script lang="ts">
2
+ import { cn, type WithElementRef } from "@heyramzi/utils";
3
+ import type { HTMLAttributes } from "svelte/elements";
4
+
5
+ let {
6
+ ref = $bindable(null),
7
+ class: className,
8
+ children,
9
+ ...restProps
10
+ }: WithElementRef<HTMLAttributes<HTMLElement>> = $props();
11
+ </script>
12
+
13
+ <kbd
14
+ bind:this={ref}
15
+ data-slot="kbd-group"
16
+ class={cn("inline-flex items-center gap-1", className)}
17
+ {...restProps}
18
+ >
19
+ {@render children?.()}
20
+ </kbd>
@@ -0,0 +1,23 @@
1
+ <script lang="ts">
2
+ import { cn, type WithElementRef } from "@heyramzi/utils";
3
+ import type { HTMLAttributes } from "svelte/elements";
4
+
5
+ let {
6
+ ref = $bindable(null),
7
+ class: className,
8
+ children,
9
+ ...restProps
10
+ }: WithElementRef<HTMLAttributes<HTMLElement>> = $props();
11
+ </script>
12
+
13
+ <kbd
14
+ bind:this={ref}
15
+ data-slot="kbd"
16
+ class={cn(
17
+ "bg-muted text-muted-foreground pointer-events-none inline-flex h-5 w-fit min-w-5 items-center justify-center gap-1 rounded-sm px-1 font-sans text-xs font-medium tabular-nums select-none [&_svg:not([class*='size-'])]:size-3",
18
+ className,
19
+ )}
20
+ {...restProps}
21
+ >
22
+ {@render children?.()}
23
+ </kbd>
@@ -0,0 +1,28 @@
1
+ import Button from "./message-scroller-button.svelte";
2
+ import Content from "./message-scroller-content.svelte";
3
+ import Item from "./message-scroller-item.svelte";
4
+ import Provider from "./message-scroller-provider.svelte";
5
+ import Viewport from "./message-scroller-viewport.svelte";
6
+ import Root from "./message-scroller.svelte";
7
+
8
+ export {
9
+ useMessageScroller,
10
+ useMessageScrollerScrollable,
11
+ useMessageScrollerVisibility,
12
+ } from "@shadcn-svelte/primitives/message-scroller";
13
+
14
+ export {
15
+ Provider,
16
+ Root,
17
+ Viewport,
18
+ Content,
19
+ Item,
20
+ Button,
21
+ //
22
+ Provider as MessageScrollerProvider,
23
+ Root as MessageScroller,
24
+ Viewport as MessageScrollerViewport,
25
+ Content as MessageScrollerContent,
26
+ Item as MessageScrollerItem,
27
+ Button as MessageScrollerButton,
28
+ };
@@ -0,0 +1,52 @@
1
+ <script lang="ts">
2
+ import { MessageScroller as MessageScrollerPrimitive } from "@shadcn-svelte/primitives/message-scroller";
3
+ import { ArrowDown } from "phosphor-svelte";
4
+ import { Button } from "../button/index.js";
5
+ import type { ButtonSize, ButtonVariant } from "../button/index.js";
6
+ import { cn } from "@heyramzi/utils";
7
+
8
+ let {
9
+ ref = $bindable(null),
10
+ direction = "end",
11
+ class: className,
12
+ children,
13
+ child: childProp,
14
+ variant = "outline",
15
+ size = "sm",
16
+ ...restProps
17
+ }: MessageScrollerPrimitive.ButtonProps & {
18
+ variant?: ButtonVariant;
19
+ size?: ButtonSize;
20
+ } = $props();
21
+ </script>
22
+
23
+ <MessageScrollerPrimitive.Button
24
+ bind:ref
25
+ data-slot="message-scroller-button"
26
+ data-direction={direction}
27
+ data-variant={variant}
28
+ data-size={size}
29
+ {direction}
30
+ class={cn(
31
+ "absolute inset-s-1/2 -translate-x-1/2 border-border bg-background text-foreground transition-[translate,scale,opacity] duration-200 hover:bg-muted hover:text-foreground data-[active=false]:pointer-events-none data-[active=false]:scale-95 data-[active=false]:opacity-0 data-[active=false]:duration-400 data-[active=false]:ease-[cubic-bezier(0.7,0,0.84,0)] data-[active=true]:translate-y-0 data-[active=true]:scale-100 data-[active=true]:opacity-100 data-[active=true]:ease-[cubic-bezier(0.23,1,0.32,1)] data-[direction=end]:bottom-4 data-[direction=end]:data-[active=false]:translate-y-full data-[direction=start]:top-4 data-[direction=start]:data-[active=false]:-translate-y-full rtl:translate-x-1/2 data-[direction=start]:[&_svg]:rotate-180",
32
+ className
33
+ )}
34
+ {...restProps}
35
+ >
36
+ {#snippet child(state)}
37
+ {#if childProp}
38
+ {@render childProp(state)}
39
+ {:else}
40
+ <Button {variant} {size} icon={!children} {...state.props}>
41
+ {#if children}
42
+ {@render children()}
43
+ {:else}
44
+ <ArrowDown />
45
+ <span class="sr-only">
46
+ {direction === "end" ? "Scroll to end" : "Scroll to start"}
47
+ </span>
48
+ {/if}
49
+ </Button>
50
+ {/if}
51
+ {/snippet}
52
+ </MessageScrollerPrimitive.Button>
@@ -0,0 +1,17 @@
1
+ <script lang="ts">
2
+ import { MessageScroller as MessageScrollerPrimitive } from "@shadcn-svelte/primitives/message-scroller";
3
+ import { cn } from "@heyramzi/utils";
4
+
5
+ let {
6
+ ref = $bindable(null),
7
+ class: className,
8
+ ...restProps
9
+ }: MessageScrollerPrimitive.ContentProps = $props();
10
+ </script>
11
+
12
+ <MessageScrollerPrimitive.Content
13
+ bind:ref
14
+ data-slot="message-scroller-content"
15
+ class={cn("gap-8 flex h-max min-h-full flex-col", className)}
16
+ {...restProps}
17
+ />
@@ -0,0 +1,22 @@
1
+ <script lang="ts">
2
+ import { MessageScroller as MessageScrollerPrimitive } from "@shadcn-svelte/primitives/message-scroller";
3
+ import { cn } from "@heyramzi/utils";
4
+
5
+ let {
6
+ ref = $bindable(null),
7
+ class: className,
8
+ scrollAnchor = false,
9
+ ...restProps
10
+ }: MessageScrollerPrimitive.ItemProps = $props();
11
+ </script>
12
+
13
+ <MessageScrollerPrimitive.Item
14
+ bind:ref
15
+ data-slot="message-scroller-item"
16
+ {scrollAnchor}
17
+ class={cn(
18
+ "min-w-0 shrink-0 [contain-intrinsic-size:auto_10rem] [content-visibility:auto]",
19
+ className
20
+ )}
21
+ {...restProps}
22
+ />
@@ -0,0 +1,7 @@
1
+ <script lang="ts">
2
+ import { MessageScroller as MessageScrollerPrimitive } from "@shadcn-svelte/primitives/message-scroller";
3
+
4
+ let { ...restProps }: MessageScrollerPrimitive.ProviderProps = $props();
5
+ </script>
6
+
7
+ <MessageScrollerPrimitive.Provider {...restProps} />
@@ -0,0 +1,20 @@
1
+ <script lang="ts">
2
+ import { MessageScroller as MessageScrollerPrimitive } from "@shadcn-svelte/primitives/message-scroller";
3
+ import { cn } from "@heyramzi/utils";
4
+
5
+ let {
6
+ class: className,
7
+ ref = $bindable(null),
8
+ ...restProps
9
+ }: MessageScrollerPrimitive.ViewportProps = $props();
10
+ </script>
11
+
12
+ <MessageScrollerPrimitive.Viewport
13
+ bind:ref
14
+ data-slot="message-scroller-viewport"
15
+ class={cn(
16
+ "size-full min-h-0 min-w-0 scroll-fade-b scrollbar-thin scrollbar-gutter-stable overflow-y-auto overscroll-contain contain-content data-autoscrolling:scrollbar-thumb-transparent data-autoscrolling:scrollbar-track-transparent data-pending-scroll:invisible",
17
+ className
18
+ )}
19
+ {...restProps}
20
+ />
@@ -0,0 +1,20 @@
1
+ <script lang="ts">
2
+ import { MessageScroller as MessageScrollerPrimitive } from "@shadcn-svelte/primitives/message-scroller";
3
+ import { cn } from "@heyramzi/utils";
4
+
5
+ let {
6
+ class: className,
7
+ ref = $bindable(null),
8
+ ...restProps
9
+ }: MessageScrollerPrimitive.RootProps = $props();
10
+ </script>
11
+
12
+ <MessageScrollerPrimitive.Root
13
+ bind:ref
14
+ data-slot="message-scroller"
15
+ class={cn(
16
+ "group/message-scroller relative flex size-full min-h-0 flex-col overflow-hidden",
17
+ className
18
+ )}
19
+ {...restProps}
20
+ />
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { RadioGroup as RadioGroupPrimitive } from "bits-ui";
3
- import CircleIcon from "@lucide/svelte/icons/circle";
3
+ import { Circle } from "phosphor-svelte";
4
4
  import { cn, type WithoutChildrenOrChild } from "@heyramzi/utils";
5
5
 
6
6
  let {
@@ -22,7 +22,8 @@
22
22
  {#snippet children({ checked })}
23
23
  <div data-slot="radio-group-indicator" class="relative flex items-center justify-center">
24
24
  {#if checked}
25
- <CircleIcon
25
+ <Circle
26
+ weight="fill"
26
27
  class="fill-primary absolute start-1/2 top-1/2 size-2 -translate-x-1/2 -translate-y-1/2"
27
28
  />
28
29
  {/if}
@@ -4,6 +4,7 @@
4
4
  </script>
5
5
 
6
6
  <script lang="ts">
7
+ import "./sheet.css";
7
8
  import { Dialog as SheetPrimitive } from "bits-ui";
8
9
  import type { Snippet } from "svelte";
9
10
  import SheetPortal from "./sheet-portal.svelte";
@@ -0,0 +1,48 @@
1
+ /* The sheet slide: transitions.dev panel reveal, owned by the sheet. */
2
+
3
+ :root {
4
+ --sheet-open-dur: 300ms;
5
+ --sheet-close-dur: 250ms;
6
+ --sheet-translate: 100%;
7
+ --sheet-ease: cubic-bezier(0.22, 1, 0.36, 1);
8
+ }
9
+
10
+ .t-sheet {
11
+ opacity: 0;
12
+ pointer-events: none;
13
+ transition:
14
+ transform var(--sheet-close-dur) var(--sheet-ease),
15
+ opacity var(--sheet-close-dur) var(--sheet-ease);
16
+ will-change: transform, opacity;
17
+ }
18
+
19
+ .t-sheet[data-side="right"] {
20
+ transform: translateX(var(--sheet-translate));
21
+ }
22
+
23
+ .t-sheet[data-side="left"] {
24
+ transform: translateX(calc(-1 * var(--sheet-translate)));
25
+ }
26
+
27
+ .t-sheet[data-side="top"] {
28
+ transform: translateY(calc(-1 * var(--sheet-translate)));
29
+ }
30
+
31
+ .t-sheet[data-side="bottom"] {
32
+ transform: translateY(var(--sheet-translate));
33
+ }
34
+
35
+ .t-sheet[data-state="open"] {
36
+ transform: translate(0, 0);
37
+ opacity: 1;
38
+ pointer-events: auto;
39
+ transition:
40
+ transform var(--sheet-open-dur) var(--sheet-ease),
41
+ opacity var(--sheet-open-dur) var(--sheet-ease);
42
+ }
43
+
44
+ @media (prefers-reduced-motion: reduce) {
45
+ .t-sheet {
46
+ transition: none !important;
47
+ }
48
+ }
@@ -1,10 +1,7 @@
1
1
  <script lang="ts">
2
- import CircleCheckIcon from "@lucide/svelte/icons/circle-check";
3
- import InfoIcon from "@lucide/svelte/icons/info";
4
- import Loader2Icon from "@lucide/svelte/icons/loader-2";
5
- import OctagonXIcon from "@lucide/svelte/icons/octagon-x";
6
- import TriangleAlertIcon from "@lucide/svelte/icons/triangle-alert";
2
+ import { CheckCircle, Info, Warning, XCircle } from "phosphor-svelte";
7
3
 
4
+ import { Spinner } from "../spinner/index.js";
8
5
  import { Toaster as Sonner, type ToasterProps as SonnerProps } from "svelte-sonner";
9
6
  import { mode } from "mode-watcher";
10
7
 
@@ -17,18 +14,18 @@
17
14
  style="--normal-bg: var(--color-popover); --normal-text: var(--color-popover-foreground); --normal-border: var(--color-border);"
18
15
  {...restProps}
19
16
  >{#snippet loadingIcon()}
20
- <Loader2Icon class="size-4 animate-spin" />
17
+ <Spinner />
21
18
  {/snippet}
22
19
  {#snippet successIcon()}
23
- <CircleCheckIcon class="size-4" />
20
+ <CheckCircle class="size-4" />
24
21
  {/snippet}
25
22
  {#snippet errorIcon()}
26
- <OctagonXIcon class="size-4" />
23
+ <XCircle class="size-4" />
27
24
  {/snippet}
28
25
  {#snippet infoIcon()}
29
- <InfoIcon class="size-4" />
26
+ <Info class="size-4" />
30
27
  {/snippet}
31
28
  {#snippet warningIcon()}
32
- <TriangleAlertIcon class="size-4" />
29
+ <Warning class="size-4" />
33
30
  {/snippet}
34
31
  </Sonner>
@@ -1,11 +1,13 @@
1
1
  <script lang="ts">
2
2
  import { Tabs as TabsPrimitive } from "bits-ui";
3
3
  import { cn } from "@heyramzi/utils";
4
+ import { slidingPill } from "../../lib/sliding-pill.js";
4
5
 
5
6
  let {
6
7
  ref = $bindable(null),
7
8
  class: className,
8
9
  size = "default",
10
+ children,
9
11
  ...restProps
10
12
  }: TabsPrimitive.ListProps & {
11
13
  /** Same steps as Button: sm 28, default 32, lg 36. */
@@ -18,8 +20,17 @@
18
20
  data-slot="tabs-list"
19
21
  data-size={size}
20
22
  class={cn(
21
- "bg-muted text-muted-foreground group/tabs-list inline-flex data-[size=sm]:h-7 data-[size=default]:h-8 data-[size=lg]:h-9 w-fit items-center justify-center rounded-lg p-[3px]",
23
+ "bg-muted text-muted-foreground group/tabs-list relative inline-flex data-[size=sm]:h-7 data-[size=default]:h-8 data-[size=lg]:h-9 w-fit items-center justify-center rounded-lg p-[3px]",
22
24
  className,
23
25
  )}
24
26
  {...restProps}
25
- />
27
+ >
28
+ <!-- Zero width until the attachment measures, so a server-rendered list
29
+ shows the trigger's own active background and never an empty pill. -->
30
+ <span
31
+ aria-hidden="true"
32
+ class="bg-background dark:border-input dark:bg-input/30 pointer-events-none absolute inset-y-[3px] left-0 z-0 w-0 rounded-md border border-transparent opacity-0 shadow-sm transition-[transform,width,opacity] duration-[var(--duration-fast,250ms)] ease-[var(--ease-smooth-out,cubic-bezier(0.22,1,0.36,1))] will-change-[transform,width] motion-reduce:transition-opacity"
33
+ {@attach slidingPill}
34
+ ></span>
35
+ {@render children?.()}
36
+ </TabsPrimitive.List>
@@ -13,7 +13,7 @@
13
13
  bind:ref
14
14
  data-slot="tabs-trigger"
15
15
  class={cn(
16
- "data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground relative inline-flex h-full flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 text-sm group-data-[size=sm]/tabs-list:text-[0.8rem] font-medium whitespace-nowrap after:absolute after:top-1/2 after:left-0 after:h-[max(100%,2rem)] after:w-full after:-translate-y-1/2 any-pointer-coarse:after:h-[max(100%,2.75rem)] transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
16
+ "data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground group-data-[pill=on]/tabs-list:data-[state=active]:border-transparent group-data-[pill=on]/tabs-list:data-[state=active]:bg-transparent group-data-[pill=on]/tabs-list:data-[state=active]:shadow-none dark:group-data-[pill=on]/tabs-list:data-[state=active]:bg-transparent relative z-[1] inline-flex h-full flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 text-sm group-data-[size=sm]/tabs-list:text-[0.8rem] font-medium whitespace-nowrap after:absolute after:top-1/2 after:left-0 after:h-[max(100%,2rem)] after:w-full after:-translate-y-1/2 any-pointer-coarse:after:h-[max(100%,2.75rem)] transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
17
17
  className,
18
18
  )}
19
19
  {...restProps}