@heyramzi/ui 2.1.0 → 2.2.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 (312) hide show
  1. package/package.json +7 -8
  2. package/scripts/sync-shadcn.mjs +3 -4
  3. package/src/agents/agent-avatar.svelte +604 -575
  4. package/src/agents/agent-cast.svelte +179 -183
  5. package/src/agents/demo.svelte +125 -113
  6. package/src/agents/helpers.ts +23 -17
  7. package/src/agents/index.ts +4 -4
  8. package/src/agents/page-state.ts +6 -6
  9. package/src/agents/types.ts +6 -6
  10. package/src/ai/ai-button/AiButton.svelte +4 -12
  11. package/src/ai/generation-progress/GenerationProgress.svelte +10 -3
  12. package/src/ai/prompt-input/PromptInputTextarea.svelte +1 -2
  13. package/src/ai/response/Response.css +2 -3
  14. package/src/animations/avatar-group-hover.css +2 -16
  15. package/src/animations/card-tilt.css +3 -6
  16. package/src/animations/digit-pop-in.css +2 -15
  17. package/src/animations/dropdown.css +4 -17
  18. package/src/animations/input-clear-dissolve.css +5 -18
  19. package/src/animations/input-shake.css +9 -41
  20. package/src/animations/learn-more-hover.css +17 -9
  21. package/src/animations/like-button.css +2 -4
  22. package/src/animations/page-side-by-side.css +2 -10
  23. package/src/animations/plus-menu-morph.css +2 -5
  24. package/src/animations/shimmer-text.css +5 -18
  25. package/src/animations/spinning-counter.css +2 -6
  26. package/src/animations/success-check.css +2 -15
  27. package/src/animations/thinking-states.css +2 -7
  28. package/src/brand/brand.test.ts +21 -5
  29. package/src/brand/index.ts +4 -10
  30. package/src/canvas/card/card-frame.svelte +43 -39
  31. package/src/canvas/card/resize-handles.svelte +89 -80
  32. package/src/canvas/chrome/chrome-zoom.svelte +101 -100
  33. package/src/canvas/chrome/lock-badge.svelte +8 -8
  34. package/src/canvas/chrome/side-handles.svelte +24 -19
  35. package/src/canvas/chrome/size-chip.svelte +30 -30
  36. package/src/canvas/chrome/snap-guides.svelte +20 -20
  37. package/src/canvas/components/icons/ToolIcon.svelte +8 -8
  38. package/src/canvas/components/shared/svg/PreviewSvg.svelte +19 -19
  39. package/src/canvas/components/shared/svg/ShapeOutline.svelte +71 -48
  40. package/src/canvas/components/shared/svg/StrokeNode.svelte +21 -27
  41. package/src/canvas/components/shared/svg/TextRule.svelte +25 -17
  42. package/src/canvas/edges/bend-handle.svelte +46 -46
  43. package/src/canvas/edges/edge-gradient.svelte +10 -10
  44. package/src/canvas/edges/gradient-edge.svelte +45 -29
  45. package/src/canvas/editor/lock.test.ts +6 -1
  46. package/src/canvas/editor/style.ts +7 -1
  47. package/src/canvas/headless/svg-figure.test.ts +5 -1
  48. package/src/canvas/lod/lod-switch.svelte +111 -106
  49. package/src/canvas/pen/PenField.svelte +87 -81
  50. package/src/canvas/pen/PenOverlay.svelte +127 -127
  51. package/src/canvas/pen/ShapeNode.svelte +31 -32
  52. package/src/canvas/pen/TextNode.svelte +54 -53
  53. package/src/canvas/pen/host-field.test.svelte +7 -7
  54. package/src/canvas/pen/shapes.ts +7 -1
  55. package/src/canvas/screen/GlyphIcon.svelte +22 -17
  56. package/src/canvas/screen/PartView.svelte +138 -72
  57. package/src/canvas/screen/ScreenBody.svelte +81 -60
  58. package/src/canvas/screen/ScreenCard.svelte +20 -20
  59. package/src/canvas/ui/CanvasToolbar.svelte +173 -85
  60. package/src/canvas/ui/FrameLayer.svelte +59 -59
  61. package/src/canvas/ui/PenLayer.svelte +39 -30
  62. package/src/canvas/ui/StylePanel.svelte +119 -102
  63. package/src/canvas/ui/canvas-grid.svelte +137 -129
  64. package/src/charts/animated-value.svelte +1 -1
  65. package/src/charts/area-chart.css +2 -5
  66. package/src/charts/area-chart.svelte +1 -1
  67. package/src/charts/bar-chart.css +2 -3
  68. package/src/charts/bar-chart.svelte +1 -1
  69. package/src/charts/charts.css +5 -17
  70. package/src/charts/crosshair.svelte +1 -1
  71. package/src/charts/dashboard/dashboard-empty.svelte +25 -25
  72. package/src/charts/dashboard/dashboard-section.svelte +24 -24
  73. package/src/charts/dashboard/data-panel.svelte +50 -50
  74. package/src/charts/dashboard/data-sort-head.svelte +56 -56
  75. package/src/charts/dashboard/data-table.svelte +138 -138
  76. package/src/charts/dashboard/panel-grid.svelte +30 -30
  77. package/src/charts/dashboard/period-filter.svelte +25 -25
  78. package/src/charts/dashboard/rating-chip.svelte +19 -19
  79. package/src/charts/dashboard/stat-band.svelte +32 -32
  80. package/src/charts/dashboard/stat-tile.svelte +106 -99
  81. package/src/charts/donut-chart.svelte +1 -1
  82. package/src/charts/edge-fade.svelte +1 -1
  83. package/src/charts/figure.svelte +1 -1
  84. package/src/charts/gauge-chart.css +4 -7
  85. package/src/charts/gauge-chart.svelte +1 -1
  86. package/src/charts/hover-focus.svelte +1 -1
  87. package/src/charts/legend.css +1 -3
  88. package/src/charts/legend.svelte +1 -1
  89. package/src/charts/line-chart.svelte +1 -1
  90. package/src/charts/number-chart.css +6 -9
  91. package/src/charts/number-chart.svelte +1 -1
  92. package/src/charts/reference-line.svelte +19 -19
  93. package/src/charts/reveal.svelte +1 -1
  94. package/src/charts/ring-chart.css +2 -4
  95. package/src/charts/ring-chart.svelte +1 -1
  96. package/src/charts/sparkline.svelte +5 -2
  97. package/src/charts/stat-card.svelte +1 -1
  98. package/src/charts/time-series-chart.css +2 -5
  99. package/src/charts/trend-pill.css +2 -4
  100. package/src/charts/trend-pill.svelte +1 -1
  101. package/src/components/copy/copy-button.svelte +93 -98
  102. package/src/components/copy/copy-button.test.ts +10 -2
  103. package/src/components/data-table/infinite-scroll.svelte +42 -42
  104. package/src/components/data-table/pagination.svelte +34 -22
  105. package/src/components/data-table/row-arrow.svelte +19 -16
  106. package/src/components/data-table/row-context-menu.svelte +52 -52
  107. package/src/components/data-table/sortable-head.svelte +25 -25
  108. package/src/components/data-table/table-skeleton.svelte +41 -36
  109. package/src/components/docs/doc-page.svelte +18 -9
  110. package/src/components/docs/on-this-page.svelte +16 -3
  111. package/src/components/docs/scroll-spy.svelte.ts +2 -3
  112. package/src/components/field-row/field-row.svelte +10 -8
  113. package/src/components/hold-to-confirm/hold-to-confirm.svelte +5 -1
  114. package/src/components/icon/icon.svelte +14 -14
  115. package/src/components/language-flag/language-flag.svelte +3 -1
  116. package/src/components/list-page/detail-empty.svelte +34 -34
  117. package/src/components/list-page/detail-header.svelte +55 -47
  118. package/src/components/list-page/detail-panel.svelte +51 -59
  119. package/src/components/list-page/detail-row.svelte +19 -19
  120. package/src/components/list-page/detail-stack.svelte +21 -21
  121. package/src/components/list-page/empty-state.svelte +30 -30
  122. package/src/components/list-page/filter-item.svelte +20 -20
  123. package/src/components/list-page/filter-popover.svelte +60 -55
  124. package/src/components/list-page/grouped-list.svelte +4 -2
  125. package/src/components/list-page/index.ts +5 -1
  126. package/src/components/list-page/list-page-shell.svelte +2 -5
  127. package/src/components/list-page/master-detail.svelte +87 -88
  128. package/src/components/list-page/notice-bar.svelte +38 -38
  129. package/src/components/list-page/prev-next.svelte +63 -63
  130. package/src/components/list-page/rail-body.svelte +18 -18
  131. package/src/components/list-page/rail-empty.svelte +23 -30
  132. package/src/components/list-page/rail-head.svelte +24 -24
  133. package/src/components/list-page/rail-row.svelte +77 -73
  134. package/src/components/list-page/section-host.ts +2 -3
  135. package/src/components/locale-switch/locale-switch.svelte +41 -41
  136. package/src/components/mark/mark.svelte +34 -38
  137. package/src/components/open-in/index.ts +1 -5
  138. package/src/components/open-in/open-in.svelte +40 -40
  139. package/src/components/picker/mark-picker.svelte +49 -49
  140. package/src/components/picker/picker.svelte +5 -3
  141. package/src/components/section-header/section-header.test.ts +7 -1
  142. package/src/components/section-header/section-label.svelte +20 -20
  143. package/src/components/segmented/animated-tabs.svelte +31 -2
  144. package/src/components/segmented/collapse.test.ts +2 -1
  145. package/src/components/segmented/index.ts +2 -0
  146. package/src/components/segmented/segment.svelte +52 -49
  147. package/src/components/segmented/segmented-filter.svelte +79 -80
  148. package/src/components/segmented/segmented-nav.svelte +34 -40
  149. package/src/components/segmented/segmented-overflow.svelte +53 -50
  150. package/src/components/segmented/segmented-track.svelte +62 -46
  151. package/src/components/segmented/segmented.ts +2 -3
  152. package/src/components/star-rating-input/star-rating-input.svelte +103 -103
  153. package/src/components/svg/svg.svelte +4 -4
  154. package/src/components/table-view/table-view.svelte +17 -2
  155. package/src/components/table-view/table-view.test.ts +7 -1
  156. package/src/components/table-view/types.ts +11 -11
  157. package/src/components/tags-input/index.ts +13 -0
  158. package/src/components/tags-input/tags-input.svelte +305 -0
  159. package/src/components/tags-input/tags.test.ts +64 -0
  160. package/src/components/tags-input/tags.ts +142 -0
  161. package/src/components/theme/index.ts +2 -4
  162. package/src/components/theme/theme-provider.svelte +1 -3
  163. package/src/components/toolbar/toolbar-search.svelte +25 -25
  164. package/src/components/toolbar/toolbar.svelte +11 -7
  165. package/src/components/toolbar/toolbar.test.ts +10 -1
  166. package/src/design/index.css +21 -78
  167. package/src/design/polish.css +26 -35
  168. package/src/editor/editor-toolbar.svelte +104 -99
  169. package/src/editor/editor.svelte +296 -266
  170. package/src/editor/image-toolbar.svelte +20 -22
  171. package/src/editor/index.ts +1 -4
  172. package/src/editor/inline-editor.svelte +280 -238
  173. package/src/editor/markdown.ts +15 -15
  174. package/src/editor/renderer.svelte +71 -71
  175. package/src/editor/slash-menu.svelte +24 -27
  176. package/src/effects/animated-beam/diagrams/BeamSvg.svelte +1 -1
  177. package/src/effects/image-comparison/ImageComparison.svelte +11 -1
  178. package/src/effects/interactive-hover-button/InteractiveHoverButton.svelte +59 -46
  179. package/src/effects/magic-card/MagicCard.css +14 -7
  180. package/src/effects/marquee/Marquee.svelte +3 -5
  181. package/src/effects/reveal/observe.ts +0 -3
  182. package/src/effects/spotlight/Spotlight.svelte +1 -1
  183. package/src/effects/theme-toggle/ThemeToggle.css +25 -22
  184. package/src/effects/theme-toggle/ThemeToggle.svelte +6 -7
  185. package/src/gallery.test.ts +14 -4
  186. package/src/inbox/README.md +1 -3
  187. package/src/inbox/inbox.test.ts +181 -138
  188. package/src/inbox/index.ts +20 -20
  189. package/src/inbox/logic.ts +114 -111
  190. package/src/inbox/thread-inbox.svelte +227 -151
  191. package/src/inbox/types.ts +130 -108
  192. package/src/lib/theme-transition.ts +2 -4
  193. package/src/lib/tone.ts +2 -4
  194. package/src/lib/utils.ts +2 -2
  195. package/src/modules.test.ts +79 -69
  196. package/src/onboarding/define-tours.test.ts +55 -40
  197. package/src/onboarding/define-tours.ts +23 -23
  198. package/src/onboarding/demo.svelte +24 -5
  199. package/src/onboarding/index.ts +1 -8
  200. package/src/onboarding/onboarding-provider.svelte +19 -7
  201. package/src/onboarding/onboarding.svelte.ts +16 -16
  202. package/src/onboarding/pick.test.ts +61 -52
  203. package/src/onboarding/pick.ts +16 -13
  204. package/src/onboarding/stores.test.ts +80 -80
  205. package/src/onboarding/stores.ts +41 -39
  206. package/src/onboarding/tour-card.svelte +30 -5
  207. package/src/onboarding/tour-runner.svelte +12 -3
  208. package/src/onboarding/types.ts +20 -12
  209. package/src/onboarding/wait.ts +22 -22
  210. package/src/panel/side-panel.svelte +94 -93
  211. package/src/record/demo-data.ts +291 -225
  212. package/src/record/field-section.svelte +38 -34
  213. package/src/record/fields.ts +148 -96
  214. package/src/record/format.ts +63 -56
  215. package/src/record/index.ts +49 -35
  216. package/src/record/info-list.svelte +145 -114
  217. package/src/record/kinds.ts +31 -31
  218. package/src/record/logic.ts +216 -214
  219. package/src/record/next-up.svelte +45 -34
  220. package/src/record/project-list.svelte +56 -50
  221. package/src/record/record-demo.svelte +130 -110
  222. package/src/record/record-header.svelte +37 -28
  223. package/src/record/record-shell.svelte +54 -54
  224. package/src/record/record-stats.svelte +25 -25
  225. package/src/record/record-timeline.svelte +134 -114
  226. package/src/record/record.test.ts +430 -309
  227. package/src/record/steps-rail.svelte +225 -216
  228. package/src/record/types.ts +234 -215
  229. package/src/record/where-we-are.svelte +62 -58
  230. package/src/shadcn/button/button.svelte +1 -1
  231. package/src/shadcn/card/card-title.svelte +1 -5
  232. package/src/shadcn/hooks/use-highlighted-code.svelte.ts +2 -13
  233. package/src/shadcn/sheet/sheet-content.svelte +2 -4
  234. package/src/shadcn/table/table-cell.svelte +2 -3
  235. package/src/shadcn/utilities.css +3 -8
  236. package/src/slides/Controls.svelte +150 -124
  237. package/src/slides/Deck.svelte +243 -238
  238. package/src/slides/Fragment.svelte +64 -57
  239. package/src/slides/Overview.svelte +106 -101
  240. package/src/slides/Presenter.svelte +300 -266
  241. package/src/slides/Slide.svelte +185 -170
  242. package/src/slides/SlideList.svelte +16 -12
  243. package/src/slides/ambient.d.ts +1 -1
  244. package/src/slides/bridges.ts +42 -23
  245. package/src/slides/deck-controller.svelte.ts +47 -35
  246. package/src/slides/demo-slides.svelte +330 -306
  247. package/src/slides/demo.svelte +164 -160
  248. package/src/slides/export/capture.ts +20 -16
  249. package/src/slides/export/ffmpeg.ts +43 -38
  250. package/src/slides/export/index.ts +5 -5
  251. package/src/slides/export/launch.ts +3 -1
  252. package/src/slides/export/pdf.ts +30 -27
  253. package/src/slides/export/types.ts +10 -8
  254. package/src/slides/index.ts +11 -11
  255. package/src/slides/labels.ts +22 -19
  256. package/src/slides/layout.ts +9 -2
  257. package/src/slides/model.ts +4 -4
  258. package/src/slides/navigation.ts +43 -31
  259. package/src/slides/theme.css +5 -7
  260. package/src/slides/types.ts +9 -10
  261. package/src/status/define-statuses.ts +17 -24
  262. package/src/status/index.ts +5 -5
  263. package/src/status/status-badge.svelte +3 -1
  264. package/src/status/status-dot.svelte +1 -3
  265. package/src/status/status-icon.svelte +1 -1
  266. package/src/status/status-types.ts +21 -21
  267. package/src/tasks/board-order.ts +2 -4
  268. package/src/tasks/demo.svelte +189 -31
  269. package/src/tasks/detail/detail-demo.svelte +73 -13
  270. package/src/tasks/detail/detail-types.ts +45 -45
  271. package/src/tasks/detail/task-detail.svelte +40 -10
  272. package/src/tasks/detail/task-property.svelte +8 -2
  273. package/src/tasks/detail/task-thread.svelte +53 -12
  274. package/src/tasks/gantt/gantt-bar.svelte +17 -6
  275. package/src/tasks/gantt/gantt-grid.svelte +40 -9
  276. package/src/tasks/gantt/gantt-group-row.svelte +8 -2
  277. package/src/tasks/gantt/gantt-header.svelte +9 -3
  278. package/src/tasks/gantt/gantt-task-row.svelte +26 -6
  279. package/src/tasks/gantt/gantt.css +2 -1
  280. package/src/tasks/gantt/gantt.test.ts +3 -1
  281. package/src/tasks/gantt/logic.test.ts +110 -20
  282. package/src/tasks/gantt/logic.ts +42 -6
  283. package/src/tasks/inbox/inbox-card.svelte +13 -5
  284. package/src/tasks/inbox/inbox-draft.svelte +1 -1
  285. package/src/tasks/inbox/inbox-logic.ts +6 -40
  286. package/src/tasks/inbox/inbox-statuses.ts +23 -23
  287. package/src/tasks/inbox/inbox-trace.svelte +9 -3
  288. package/src/tasks/inbox/inbox-types.ts +54 -58
  289. package/src/tasks/inbox/index.ts +22 -21
  290. package/src/tasks/index.ts +49 -52
  291. package/src/tasks/kanban-board.svelte +16 -12
  292. package/src/tasks/kanban-types.ts +2 -3
  293. package/src/tasks/presets/sales.ts +6 -6
  294. package/src/tasks/task-gantt.svelte +28 -8
  295. package/src/tasks/task-list.svelte +41 -17
  296. package/src/tasks/time/duration.ts +40 -40
  297. package/src/tasks/time/time-chip.svelte +9 -2
  298. package/src/tasks/time/time-dot.svelte +1 -1
  299. package/src/tasks/time/time-ring.svelte +11 -3
  300. package/src/tasks/time/time-tracker.svelte +57 -15
  301. package/src/tasks/time/time-types.ts +57 -57
  302. package/src/tasks/use-kanban-dnd.svelte.ts +2 -3
  303. package/src/thread/index.ts +7 -7
  304. package/src/thread/logic.ts +36 -33
  305. package/src/thread/thread.svelte +82 -16
  306. package/src/thread/thread.test.ts +58 -47
  307. package/src/thread/types.ts +68 -68
  308. package/src/tokens/craft.css +17 -53
  309. package/src/tokens/index.css +3 -13
  310. package/src/tokens/palette.css +13 -11
  311. package/src/tokens/primitives.css +10 -23
  312. package/src/tasks/inbox/inbox.svelte +0 -203
@@ -1,29 +1,29 @@
1
1
  <script lang="ts" module>
2
- /** A target the data is judged against: a deal, a budget, a limit. */
3
- export type ChartReference = {
4
- value: number;
5
- /** Already formatted, figure included: "Deal €61,500". */
6
- label: string;
7
- /** A status token when crossing it means something, e.g. `var(--destructive)`. */
8
- color?: string;
9
- };
2
+ /** A target the data is judged against: a deal, a budget, a limit. */
3
+ export type ChartReference = {
4
+ value: number;
5
+ /** Already formatted, figure included: "Deal €61,500". */
6
+ label: string;
7
+ /** A status token when crossing it means something, e.g. `var(--destructive)`. */
8
+ color?: string;
9
+ };
10
10
  </script>
11
11
 
12
12
  <script lang="ts">
13
- /*
14
- * A dashed rule across the plot at a fixed value, labelled at its start.
15
- * Drawn inside a layerchart `<Svg>`; the chart widens its domain to keep it in view.
16
- */
17
- import "./reference-line.css";
18
- import { AnnotationLine } from "layerchart";
13
+ /*
14
+ * A dashed rule across the plot at a fixed value, labelled at its start.
15
+ * Drawn inside a layerchart `<Svg>`; the chart widens its domain to keep it in view.
16
+ */
17
+ import "./reference-line.css";
18
+ import { AnnotationLine } from "layerchart";
19
19
 
20
- interface Props {
21
- reference: ChartReference;
22
- };
20
+ interface Props {
21
+ reference: ChartReference;
22
+ }
23
23
 
24
- let { reference }: Props = $props();
24
+ let { reference }: Props = $props();
25
25
 
26
- const stroke = $derived(reference.color ?? "var(--foreground)");
26
+ const stroke = $derived(reference.color ?? "var(--foreground)");
27
27
  </script>
28
28
 
29
29
  <g class="hr-reference-line" style:--hr-reference-color={stroke}>
@@ -15,7 +15,7 @@
15
15
  children: Snippet;
16
16
  /** Headroom around the plot so a stroke at the series max is not cut. */
17
17
  bleed?: number;
18
- };
18
+ }
19
19
 
20
20
  // =========================
21
21
  // PROPS
@@ -1,7 +1,5 @@
1
- /* Styles for RingChart. Plain stylesheet (see area-chart.css for why).
2
- Selectors are namespaced under `.hr-ring` (the generic ones below would
3
- otherwise collide with GaugeChart's identical names once they are no longer
4
- Svelte-scoped). */
1
+ /* RingChart styles, namespaced under `.hr-ring` because unscoped, the generic names
2
+ below collide with GaugeChart's (area-chart.css says why it's plain CSS). */
5
3
 
6
4
  .hr-ring {
7
5
  position: relative;
@@ -45,7 +45,7 @@
45
45
  /** Replaces the center readout, for a number that isn't the ring's own value. */
46
46
  children?: Snippet;
47
47
  class?: string;
48
- };
48
+ }
49
49
 
50
50
  // =========================
51
51
  // PROPS
@@ -32,7 +32,7 @@
32
32
  bare?: boolean;
33
33
  formatValue?: (v: number) => string;
34
34
  class?: string;
35
- };
35
+ }
36
36
 
37
37
  // =========================
38
38
  // PROPS
@@ -96,7 +96,10 @@
96
96
  onpointerleave={() => (active = null)}
97
97
  >
98
98
  {#if data.length > 0}
99
- <div class="hr-sparkline-chart" style:height={typeof height === "number" ? `${height}px` : height}>
99
+ <div
100
+ class="hr-sparkline-chart"
101
+ style:height={typeof height === "number" ? `${height}px` : height}
102
+ >
100
103
  <Chart {data} x={xKey} y={yKey} {padding} tooltipContext={{ mode: "bisect-x" }}>
101
104
  <Svg>
102
105
  <LinearGradient id={uid} vertical>
@@ -62,7 +62,7 @@
62
62
  /** Leading icon next to the label. */
63
63
  icon?: Snippet;
64
64
  class?: string;
65
- };
65
+ }
66
66
 
67
67
  // =========================
68
68
  // PROPS
@@ -1,8 +1,5 @@
1
- /* Styles for TimeSeriesChart. Shipped as a plain stylesheet (imported by the
2
- component) instead of a Svelte `<style>` block: a `<style>` block becomes a
3
- `?type=style&lang.css` virtual module that Tailwind v4's Vite plugin mis-parses
4
- when this package is consumed as source from node_modules (tailwindlabs/
5
- tailwindcss#20000). Selectors are namespaced under `.hr-ts-chart`. */
1
+ /* A plain stylesheet, not a `<style>` block, because Tailwind v4's Vite plugin mis-parses that
2
+ virtual module when this package is consumed from node_modules (tailwindcss#20000). */
6
3
 
7
4
  .hr-ts-chart {
8
5
  width: 100%;
@@ -11,10 +11,8 @@
11
11
  border-radius: 999px;
12
12
  border: 1px solid color-mix(in srgb, var(--stat-trend, currentColor) 22%, transparent);
13
13
  background: color-mix(in srgb, var(--stat-trend, currentColor) 10%, transparent);
14
- /* WHY the mix: a trend colour is picked to read as a mark, and at 12px on its
15
- own 10% wash the raw hue measured 3.5:1. Mixing toward --foreground darkens
16
- it on a light page and lightens it on a dark one, so one rule serves both
17
- themes and the package needs to know nothing about which is on. */
14
+ /* The raw hue measured 3.5:1 on its own wash, so it mixes toward --foreground,
15
+ which fixes the contrast in both themes with one rule. */
18
16
  color: color-mix(
19
17
  in oklab,
20
18
  var(--stat-trend, var(--muted-foreground, oklch(0.5 0.01 286))) 72%,
@@ -18,7 +18,7 @@
18
18
  /** Format the delta text. */
19
19
  format?: (v: number) => string;
20
20
  class?: string;
21
- };
21
+ }
22
22
 
23
23
  // =========================
24
24
  // PROPS
@@ -1,101 +1,98 @@
1
1
  <script lang="ts">
2
- /**
3
- * The one copy control. No `text` prop draws the icon button; `text` draws the
4
- * chip. `agent` marks a payload written for a model.
5
- */
6
-
7
- // =========================
8
- // IMPORTS
9
- // =========================
10
- import { Check, Copy, StarFour } from "phosphor-svelte";
11
- import { cn } from "../../lib/utils.js";
12
- import { buttonVariants } from "../../shadcn/button/index.js";
13
- import type { HTMLButtonAttributes } from "svelte/elements";
14
- import { copyText } from "./clipboard.js";
15
-
16
- // =========================
17
- // TYPES
18
- // =========================
19
- interface Props extends Omit<HTMLButtonAttributes, "value" | "onclick" | "children"> {
20
- /**
21
- * What lands on the clipboard. A function runs on the click, so a long list
22
- * doesn't build every prompt up front. It may return a promise.
23
- */
24
- value: string | (() => string | Promise<string>);
25
- /** Names the thing being copied, for a screen reader and the tooltip. */
26
- label?: string;
27
- /** Announced when the write lands; see `announce`. */
28
- copiedLabel?: string;
29
- /** The chip's visible text. Its presence is what makes this a chip. */
30
- text?: string;
31
- /** Marks the payload as an instruction for an AI rather than a raw string. */
32
- agent?: boolean;
33
- /**
34
- * `sm` matches `OpenIn`, for the icon row inside a calendar chip. `md` is
35
- * the 28px icon `Button`, for a toolbar that sits beside other icon buttons.
36
- */
37
- size?: "default" | "md" | "sm";
38
- /**
39
- * The icon button's `Button` variant: ghost unless it sits beside an outline
40
- * one. `inline` is for the chip: it takes the surrounding text's font and
41
- * colour, and the icon trails it.
42
- */
43
- variant?: "ghost" | "outline" | "inline";
44
2
  /**
45
- * How a screen reader hears the copy. `region` keeps the name fixed and
46
- * announces `copiedLabel` in a live region. `name` swaps the button's own name
47
- * to `copiedLabel` (write it in full) and has no region, for a page that
48
- * renders hundreds of these.
3
+ * The one copy control. No `text` prop draws the icon button; `text` draws the
4
+ * chip. `agent` marks a payload written for a model.
49
5
  */
50
- announce?: "region" | "name";
51
- /** Runs after a write that landed, for a toast the call site owns. */
52
- onCopied?: () => void;
53
- class?: string;
54
- }
55
6
 
56
- // =========================
57
- // PROPS
58
- // =========================
59
- let {
60
- value,
61
- label = "Copy",
62
- copiedLabel = "Copied",
63
- text,
64
- agent = false,
65
- size = "default",
66
- variant = "ghost",
67
- announce = "region",
68
- onCopied,
69
- class: className,
70
- ...restProps
71
- }: Props = $props();
7
+ // =========================
8
+ // IMPORTS
9
+ // =========================
10
+ import { Check, Copy, StarFour } from "phosphor-svelte";
11
+ import { cn } from "../../lib/utils.js";
12
+ import { buttonVariants } from "../../shadcn/button/index.js";
13
+ import type { HTMLButtonAttributes } from "svelte/elements";
14
+ import { copyText } from "./clipboard.js";
15
+
16
+ // =========================
17
+ // TYPES
18
+ // =========================
19
+ interface Props extends Omit<HTMLButtonAttributes, "value" | "onclick" | "children"> {
20
+ /**
21
+ * What lands on the clipboard. A function runs on the click, so a long list
22
+ * doesn't build every prompt up front. It may return a promise.
23
+ */
24
+ value: string | (() => string | Promise<string>);
25
+ /** Names the thing being copied, for a screen reader and the tooltip. */
26
+ label?: string;
27
+ /** Announced when the write lands; see `announce`. */
28
+ copiedLabel?: string;
29
+ /** The chip's visible text. Its presence is what makes this a chip. */
30
+ text?: string;
31
+ /** Marks the payload as an instruction for an AI rather than a raw string. */
32
+ agent?: boolean;
33
+ /**
34
+ * `sm` matches `OpenIn`, for the icon row inside a calendar chip. `md` is
35
+ * the 28px icon `Button`, for a toolbar that sits beside other icon buttons.
36
+ */
37
+ size?: "default" | "md" | "sm";
38
+ /**
39
+ * Ghost unless it sits beside an outline button; `inline` is for the chip,
40
+ * taking the surrounding text's font and colour.
41
+ */
42
+ variant?: "ghost" | "outline" | "inline";
43
+ /**
44
+ * `region` announces `copiedLabel` in a live region; `name` swaps the button's
45
+ * name to it (write it in full), for a page rendering hundreds of these.
46
+ */
47
+ announce?: "region" | "name";
48
+ /** Runs after a write that landed, for a toast the call site owns. */
49
+ onCopied?: () => void;
50
+ class?: string;
51
+ }
52
+
53
+ // =========================
54
+ // PROPS
55
+ // =========================
56
+ let {
57
+ value,
58
+ label = "Copy",
59
+ copiedLabel = "Copied",
60
+ text,
61
+ agent = false,
62
+ size = "default",
63
+ variant = "ghost",
64
+ announce = "region",
65
+ onCopied,
66
+ class: className,
67
+ ...restProps
68
+ }: Props = $props();
72
69
 
73
- // =========================
74
- // STATE
75
- // =========================
76
- let copied = $state(false);
77
- let resetAt: ReturnType<typeof setTimeout> | undefined;
70
+ // =========================
71
+ // STATE
72
+ // =========================
73
+ let copied = $state(false);
74
+ let resetAt: ReturnType<typeof setTimeout> | undefined;
78
75
 
79
- // =========================
80
- // DERIVED
81
- // =========================
82
- const inline = $derived(text !== undefined && variant === "inline");
76
+ // =========================
77
+ // DERIVED
78
+ // =========================
79
+ const inline = $derived(text !== undefined && variant === "inline");
83
80
 
84
- // =========================
85
- // FUNCTIONS
86
- // =========================
87
- async function copy(event: MouseEvent) {
88
- // Rows and cards are clickable; taking the payload out of one must not also
89
- // open the record it belongs to.
90
- event.stopPropagation();
91
- event.preventDefault();
92
- const payload = typeof value === "function" ? await value() : value;
93
- if (!(await copyText(payload))) return;
94
- copied = true;
95
- clearTimeout(resetAt);
96
- resetAt = setTimeout(() => (copied = false), 1400);
97
- onCopied?.();
98
- }
81
+ // =========================
82
+ // FUNCTIONS
83
+ // =========================
84
+ async function copy(event: MouseEvent) {
85
+ // Rows and cards are clickable; taking the payload out of one must not also
86
+ // open the record it belongs to.
87
+ event.stopPropagation();
88
+ event.preventDefault();
89
+ const payload = typeof value === "function" ? await value() : value;
90
+ if (!(await copyText(payload))) return;
91
+ copied = true;
92
+ clearTimeout(resetAt);
93
+ resetAt = setTimeout(() => (copied = false), 1400);
94
+ onCopied?.();
95
+ }
99
96
  </script>
100
97
 
101
98
  <!-- =========================== -->
@@ -113,10 +110,10 @@ async function copy(event: MouseEvent) {
113
110
  ? size === "sm"
114
111
  ? "grid size-5 place-items-center rounded-md hover:bg-accent"
115
112
  : buttonVariants({
116
- variant: variant === "outline" ? "outline" : "ghost",
117
- size: size === "md" ? "sm" : "default",
118
- icon: true,
119
- })
113
+ variant: variant === "outline" ? "outline" : "ghost",
114
+ size: size === "md" ? "sm" : "default",
115
+ icon: true,
116
+ })
120
117
  : inline
121
118
  ? "inline-flex cursor-copy items-baseline gap-1.5 transition-colors hover:text-foreground"
122
119
  : "flex items-center gap-1 rounded px-1.5 py-0.5 font-mono text-xs tabular-nums text-muted-foreground/70 transition-colors hover:bg-muted hover:text-foreground",
@@ -154,9 +151,7 @@ async function copy(event: MouseEvent) {
154
151
  <Copy
155
152
  class={cn(
156
153
  "col-start-1 row-start-1 size-full transition-[opacity,color] duration-[var(--duration-fast)] ease-[var(--ease-smooth-out)]",
157
- inline
158
- ? "text-current"
159
- : "text-muted-foreground group-hover/copy:text-foreground",
154
+ inline ? "text-current" : "text-muted-foreground group-hover/copy:text-foreground",
160
155
  copied ? "opacity-0" : "opacity-100",
161
156
  )}
162
157
  aria-hidden="true"
@@ -51,14 +51,22 @@ describe("CopyButton", () => {
51
51
 
52
52
  it("names what it copies, so the button is not just a value", () => {
53
53
  stubClipboard();
54
- const { button, done } = render({ value: "abc123", text: "abc123", label: "Copy API key abc123" });
54
+ const { button, done } = render({
55
+ value: "abc123",
56
+ text: "abc123",
57
+ label: "Copy API key abc123",
58
+ });
55
59
  expect(button.getAttribute("aria-label")).toBe("Copy API key abc123");
56
60
  done();
57
61
  });
58
62
 
59
63
  it("announces in a live region by default and keeps its name", async () => {
60
64
  stubClipboard();
61
- const { button, target, done } = render({ value: "x", label: "Copy id", copiedLabel: "Copied" });
65
+ const { button, target, done } = render({
66
+ value: "x",
67
+ label: "Copy id",
68
+ copiedLabel: "Copied",
69
+ });
62
70
  await click(button);
63
71
  expect(target.querySelector("[aria-live]")?.textContent).toBe("Copied");
64
72
  expect(button.getAttribute("aria-label")).toBe("Copy id");
@@ -1,52 +1,52 @@
1
1
  <script lang="ts" generics="T">
2
- // ========================= IMPORTS
3
- import type { Snippet } from "svelte";
4
- import { Spinner } from "../../shadcn/spinner/index.js";
2
+ // ========================= IMPORTS
3
+ import type { Snippet } from "svelte";
4
+ import { Spinner } from "../../shadcn/spinner/index.js";
5
5
 
6
- // ========================= TYPES
7
- interface Props {
8
- items: T[];
9
- isDone: boolean;
10
- isLoading: boolean;
11
- children: Snippet<[{ items: T[] }]>;
12
- /** Skeleton snippet rendered while loading more */
13
- skeleton?: Snippet;
14
- onLoadMore: () => void;
15
- threshold?: number;
16
- loadingClass?: string;
17
- };
6
+ // ========================= TYPES
7
+ interface Props {
8
+ items: T[];
9
+ isDone: boolean;
10
+ isLoading: boolean;
11
+ children: Snippet<[{ items: T[] }]>;
12
+ /** Skeleton snippet rendered while loading more */
13
+ skeleton?: Snippet;
14
+ onLoadMore: () => void;
15
+ threshold?: number;
16
+ loadingClass?: string;
17
+ }
18
18
 
19
- // ========================= PROPS
20
- let {
21
- items,
22
- isDone,
23
- isLoading,
24
- children,
25
- skeleton,
26
- onLoadMore,
27
- threshold = 200,
28
- loadingClass,
29
- }: Props = $props();
19
+ // ========================= PROPS
20
+ let {
21
+ items,
22
+ isDone,
23
+ isLoading,
24
+ children,
25
+ skeleton,
26
+ onLoadMore,
27
+ threshold = 200,
28
+ loadingClass,
29
+ }: Props = $props();
30
30
 
31
- // ========================= STATE
32
- let sentinelEl: HTMLDivElement | undefined = $state();
31
+ // ========================= STATE
32
+ let sentinelEl: HTMLDivElement | undefined = $state();
33
33
 
34
- // ========================= EFFECTS
35
- $effect(() => {
36
- if (!sentinelEl || isDone) return;
34
+ // ========================= EFFECTS
35
+ $effect(() => {
36
+ if (!sentinelEl || isDone) return;
37
37
 
38
- const observer = new IntersectionObserver(
39
- (entries) => {
40
- if (entries[0]?.isIntersecting && !isLoading && !isDone) {
41
- onLoadMore();
42
- }
43
- },
44
- { rootMargin: `${threshold}px` },
45
- );
38
+ const observer = new IntersectionObserver(
39
+ (entries) => {
40
+ if (entries[0]?.isIntersecting && !isLoading && !isDone) {
41
+ onLoadMore();
42
+ }
43
+ },
44
+ { rootMargin: `${threshold}px` },
45
+ );
46
46
 
47
- observer.observe(sentinelEl);
48
- return () => observer.disconnect();
49
- });
47
+ observer.observe(sentinelEl);
48
+ return () => observer.disconnect();
49
+ });
50
50
  </script>
51
51
 
52
52
  <!-- =========================== -->
@@ -1,27 +1,27 @@
1
1
  <script lang="ts">
2
- import { CaretLeft, CaretRight } from "phosphor-svelte";
3
- import { Button } from "../../shadcn/button/index.js";
4
- import { totalPages as calcTotalPages } from "./data-table.js";
2
+ import { CaretLeft, CaretRight } from "phosphor-svelte";
3
+ import { Button } from "../../shadcn/button/index.js";
4
+ import { totalPages as calcTotalPages } from "./data-table.js";
5
5
 
6
- // ========================= TYPES
7
- interface Props {
8
- page: number;
9
- pageSize: number;
10
- total: number;
11
- onPageChange?: (page: number) => void;
12
- /** If provided, renders <a> tags instead of buttons (for server-side pagination) */
13
- buildHref?: (page: number) => string;
14
- };
6
+ // ========================= TYPES
7
+ interface Props {
8
+ page: number;
9
+ pageSize: number;
10
+ total: number;
11
+ onPageChange?: (page: number) => void;
12
+ /** If provided, renders <a> tags instead of buttons (for server-side pagination) */
13
+ buildHref?: (page: number) => string;
14
+ }
15
15
 
16
- // ========================= PROPS
17
- let { page, pageSize, total, onPageChange, buildHref }: Props = $props();
16
+ // ========================= PROPS
17
+ let { page, pageSize, total, onPageChange, buildHref }: Props = $props();
18
18
 
19
- // ========================= DERIVED
20
- let pages = $derived(calcTotalPages(total, pageSize));
21
- let hasPrev = $derived(page > 1);
22
- let hasNext = $derived(page < pages);
23
- let start = $derived((page - 1) * pageSize + 1);
24
- let end = $derived(Math.min(page * pageSize, total));
19
+ // ========================= DERIVED
20
+ let pages = $derived(calcTotalPages(total, pageSize));
21
+ let hasPrev = $derived(page > 1);
22
+ let hasNext = $derived(page < pages);
23
+ let start = $derived((page - 1) * pageSize + 1);
24
+ let end = $derived(Math.min(page * pageSize, total));
25
25
  </script>
26
26
 
27
27
  {#if pages > 1}
@@ -49,11 +49,23 @@ let end = $derived(Math.min(page * pageSize, total));
49
49
  </Button>
50
50
  {/if}
51
51
  {:else}
52
- <Button variant="outline" size="sm" icon disabled={!hasPrev} onclick={() => onPageChange?.(page - 1)}>
52
+ <Button
53
+ variant="outline"
54
+ size="sm"
55
+ icon
56
+ disabled={!hasPrev}
57
+ onclick={() => onPageChange?.(page - 1)}
58
+ >
53
59
  <CaretLeft class="size-4" />
54
60
  </Button>
55
61
  <span class="mx-2 tabular-nums">{page} / {pages}</span>
56
- <Button variant="outline" size="sm" icon disabled={!hasNext} onclick={() => onPageChange?.(page + 1)}>
62
+ <Button
63
+ variant="outline"
64
+ size="sm"
65
+ icon
66
+ disabled={!hasNext}
67
+ onclick={() => onPageChange?.(page + 1)}
68
+ >
57
69
  <CaretRight class="size-4" />
58
70
  </Button>
59
71
  {/if}
@@ -1,28 +1,31 @@
1
1
  <script lang="ts">
2
- // =========================
3
- // IMPORTS
4
- // =========================
5
- import { CaretRight } from "phosphor-svelte";
6
- import { cn } from "../../lib/utils.js";
2
+ // =========================
3
+ // IMPORTS
4
+ // =========================
5
+ import { CaretRight } from "phosphor-svelte";
6
+ import { cn } from "../../lib/utils.js";
7
7
 
8
- // =========================
9
- // TYPES
10
- // =========================
11
- interface Props {
12
- class?: string;
13
- };
8
+ // =========================
9
+ // TYPES
10
+ // =========================
11
+ interface Props {
12
+ class?: string;
13
+ }
14
14
 
15
- // =========================
16
- // PROPS
17
- // =========================
18
- let { class: className }: Props = $props();
15
+ // =========================
16
+ // PROPS
17
+ // =========================
18
+ let { class: className }: Props = $props();
19
19
  </script>
20
20
 
21
21
  <!-- =========================== -->
22
22
  <!-- MARKUP -->
23
23
  <!-- =========================== -->
24
24
  <CaretRight
25
- class={cn("size-3.5 text-muted-foreground opacity-0 transition-opacity group-hover/row:opacity-100", className)}
25
+ class={cn(
26
+ "size-3.5 text-muted-foreground opacity-0 transition-opacity group-hover/row:opacity-100",
27
+ className,
28
+ )}
26
29
  weight="bold"
27
30
  aria-hidden="true"
28
31
  />