@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,112 +1,123 @@
1
1
  <script lang="ts" module>
2
- import type { PenColor } from "../pen/types.js";
2
+ import type { PenColor } from "../pen/types.js";
3
3
 
4
- export interface StyleLabels {
5
- panel: string;
6
- fill: string;
7
- stroke: string;
8
- /** The stroke row's name when the selection has no outline, only ink, like text. */
9
- colour: string;
10
- none: string;
11
- width: string;
12
- /** Thin, regular, thick. */
13
- widths: readonly [string, string, string];
14
- font: string;
15
- regular: string;
16
- bold: string;
17
- colors: Record<PenColor, string>;
18
- }
4
+ export interface StyleLabels {
5
+ panel: string;
6
+ fill: string;
7
+ stroke: string;
8
+ /** The stroke row's name when the selection has no outline, only ink, like text. */
9
+ colour: string;
10
+ none: string;
11
+ width: string;
12
+ /** Thin, regular, thick. */
13
+ widths: readonly [string, string, string];
14
+ font: string;
15
+ regular: string;
16
+ bold: string;
17
+ colors: Record<PenColor, string>;
18
+ }
19
19
  </script>
20
20
 
21
21
  <script lang="ts">
22
- // =========================
23
- // IMPORTS
24
- // =========================
25
- import { Panel, useStore } from "@xyflow/svelte";
26
- import type { CanvasOp } from "../ops/ops.js";
27
- import {
28
- FONT_SIZES,
29
- styleOps,
30
- summarizeStyle,
31
- type FontWeight,
32
- type StyleNode,
33
- type StyleValues,
34
- type WidthLevel,
35
- } from "../editor/style.js";
36
- import { PEN_COLORS } from "../pen/types.js";
37
- import "../pen/pen.css";
38
- import { placeBeside, type Box } from "./place-panel.js";
39
- import "./canvas-ui.css";
22
+ // =========================
23
+ // IMPORTS
24
+ // =========================
25
+ import { Panel, useStore } from "@xyflow/svelte";
26
+ import type { CanvasOp } from "../ops/ops.js";
27
+ import {
28
+ FONT_SIZES,
29
+ styleOps,
30
+ summarizeStyle,
31
+ type FontWeight,
32
+ type StyleNode,
33
+ type StyleValues,
34
+ type WidthLevel,
35
+ } from "../editor/style.js";
36
+ import { PEN_COLORS } from "../pen/types.js";
37
+ import "../pen/pen.css";
38
+ import { placeBeside, type Box } from "./place-panel.js";
39
+ import "./canvas-ui.css";
40
40
 
41
- // =========================
42
- // TYPES
43
- // =========================
44
- interface Props {
45
- /** The selected cards. The panel hides when none of them takes a style. */
46
- nodes: readonly StyleNode[];
47
- /** The node type of a frame or group, whose fill and outline the panel sets. */
48
- frameType?: string;
49
- labels: StyleLabels;
50
- /** One call per change, so each is one undo step. */
51
- commit: (ops: CanvasOp[]) => void;
52
- }
41
+ // =========================
42
+ // TYPES
43
+ // =========================
44
+ interface Props {
45
+ /** The selected cards. The panel hides when none of them takes a style. */
46
+ nodes: readonly StyleNode[];
47
+ /** The node type of a frame or group, whose fill and outline the panel sets. */
48
+ frameType?: string;
49
+ labels: StyleLabels;
50
+ /** One call per change, so each is one undo step. */
51
+ commit: (ops: CanvasOp[]) => void;
52
+ }
53
53
 
54
- // =========================
55
- // PROPS
56
- // =========================
57
- let { nodes, frameType = "group", labels, commit }: Props = $props();
54
+ // =========================
55
+ // PROPS
56
+ // =========================
57
+ let { nodes, frameType = "group", labels, commit }: Props = $props();
58
58
 
59
- // =========================
60
- // STATE
61
- // =========================
62
- const store = useStore();
63
- let barWidth = $state(0);
64
- let barHeight = $state(0);
59
+ // =========================
60
+ // STATE
61
+ // =========================
62
+ const store = useStore();
63
+ let barWidth = $state(0);
64
+ let barHeight = $state(0);
65
65
 
66
- // =========================
67
- // DERIVED
68
- // =========================
69
- const WEIGHTS: readonly FontWeight[] = ["regular", "bold"];
70
- const LEVELS: readonly WidthLevel[] = [0, 1, 2];
66
+ // =========================
67
+ // DERIVED
68
+ // =========================
69
+ const WEIGHTS: readonly FontWeight[] = ["regular", "bold"];
70
+ const LEVELS: readonly WidthLevel[] = [0, 1, 2];
71
71
 
72
- let summary = $derived(summarizeStyle(nodes, frameType));
73
- let strokeLabel = $derived(summary?.controls.has("fill") || summary?.controls.has("width") ? labels.stroke : labels.colour);
72
+ let summary = $derived(summarizeStyle(nodes, frameType));
73
+ let strokeLabel = $derived(
74
+ summary?.controls.has("fill") || summary?.controls.has("width") ? labels.stroke : labels.colour,
75
+ );
74
76
 
75
- // The selection in screen pixels, read from the live viewport so the panel follows a pan, a zoom and a drag.
76
- let selection = $derived.by<Box | null>(() => {
77
- const { x, y, zoom } = store.viewport;
78
- let box: Box | null = null;
79
- for (const node of nodes) {
80
- const internal = store.nodeLookup.get(node.id);
81
- if (!internal) continue;
82
- const at = internal.internals.positionAbsolute;
83
- const width = internal.measured.width ?? internal.width ?? 0;
84
- const height = internal.measured.height ?? internal.height ?? 0;
85
- const left = at.x * zoom + x;
86
- const top = at.y * zoom + y;
87
- const right = left + width * zoom;
88
- const bottom = top + height * zoom;
89
- box = box
90
- ? { left: Math.min(box.left, left), top: Math.min(box.top, top), right: Math.max(box.right, right), bottom: Math.max(box.bottom, bottom) }
91
- : { left, top, right, bottom };
92
- }
93
- return box;
94
- });
77
+ // The selection in screen pixels, read from the live viewport so the panel follows a pan, a zoom and a drag.
78
+ let selection = $derived.by<Box | null>(() => {
79
+ const { x, y, zoom } = store.viewport;
80
+ let box: Box | null = null;
81
+ for (const node of nodes) {
82
+ const internal = store.nodeLookup.get(node.id);
83
+ if (!internal) continue;
84
+ const at = internal.internals.positionAbsolute;
85
+ const width = internal.measured.width ?? internal.width ?? 0;
86
+ const height = internal.measured.height ?? internal.height ?? 0;
87
+ const left = at.x * zoom + x;
88
+ const top = at.y * zoom + y;
89
+ const right = left + width * zoom;
90
+ const bottom = top + height * zoom;
91
+ box = box
92
+ ? {
93
+ left: Math.min(box.left, left),
94
+ top: Math.min(box.top, top),
95
+ right: Math.max(box.right, right),
96
+ bottom: Math.max(box.bottom, bottom),
97
+ }
98
+ : { left, top, right, bottom };
99
+ }
100
+ return box;
101
+ });
95
102
 
96
- // Beside the selection on the side with room, never over its grips. Null leaves the panel docked at the right edge.
97
- let placed = $derived(
98
- selection && barWidth > 0
99
- ? placeBeside(selection, { width: barWidth, height: barHeight }, { width: store.width, height: store.height })
100
- : null,
101
- );
103
+ // Beside the selection on the side with room, never over its grips. Null leaves the panel docked at the right edge.
104
+ let placed = $derived(
105
+ selection && barWidth > 0
106
+ ? placeBeside(
107
+ selection,
108
+ { width: barWidth, height: barHeight },
109
+ { width: store.width, height: store.height },
110
+ )
111
+ : null,
112
+ );
102
113
 
103
- // =========================
104
- // FUNCTIONS
105
- // =========================
106
- function change(next: Partial<StyleValues>) {
107
- const ops = styleOps(nodes, next, frameType);
108
- if (ops.length > 0) commit(ops);
109
- }
114
+ // =========================
115
+ // FUNCTIONS
116
+ // =========================
117
+ function change(next: Partial<StyleValues>) {
118
+ const ops = styleOps(nodes, next, frameType);
119
+ if (ops.length > 0) commit(ops);
120
+ }
110
121
  </script>
111
122
 
112
123
  <!-- =========================== -->
@@ -116,13 +127,19 @@ function change(next: Partial<StyleValues>) {
116
127
  <Panel
117
128
  position="center-right"
118
129
  class="cv-dock cv-style-dock {placed ? 'is-placed' : ''}"
119
- style={placed ? `--cv-style-x:${Math.round(placed.x)}px;--cv-style-y:${Math.round(placed.y)}px` : undefined}
130
+ style={placed
131
+ ? `--cv-style-x:${Math.round(placed.x)}px;--cv-style-y:${Math.round(placed.y)}px`
132
+ : undefined}
120
133
  data-side={placed?.side}
121
134
  >
122
135
  <div
123
136
  bind:offsetWidth={barWidth}
124
137
  bind:offsetHeight={barHeight}
125
- class="cv-bar cv-side cv-style t-enter" role="toolbar" aria-label={labels.panel} data-testid="style-panel">
138
+ class="cv-bar cv-side cv-style t-enter"
139
+ role="toolbar"
140
+ aria-label={labels.panel}
141
+ data-testid="style-panel"
142
+ >
126
143
  {#if summary.controls.has("fill")}
127
144
  <div class="cv-section" role="group" aria-label={labels.fill} data-testid="style-fill">
128
145
  <span class="cv-label" aria-hidden="true">{labels.fill}</span>
@@ -199,8 +216,8 @@ function change(next: Partial<StyleValues>) {
199
216
  class="cv-btn cv-figure"
200
217
  aria-pressed={summary.values.fontSize === size}
201
218
  aria-label="{labels.font}: {size}"
202
- onclick={() => change({ fontSize: size })}
203
- >{size}</button>
219
+ onclick={() => change({ fontSize: size })}>{size}</button
220
+ >
204
221
  {/each}
205
222
  {#each WEIGHTS as weight (weight)}
206
223
  <button
@@ -210,8 +227,8 @@ function change(next: Partial<StyleValues>) {
210
227
  aria-pressed={summary.values.fontWeight === weight}
211
228
  aria-label={weight === "bold" ? labels.bold : labels.regular}
212
229
  data-tip={weight === "bold" ? labels.bold : labels.regular}
213
- onclick={() => change({ fontWeight: weight })}
214
- >Aa</button>
230
+ onclick={() => change({ fontWeight: weight })}>Aa</button
231
+ >
215
232
  {/each}
216
233
  </div>
217
234
  </div>
@@ -1,148 +1,156 @@
1
1
  <script lang="ts">
2
- // =========================
3
- // IMPORTS
4
- // =========================
5
- import { useStore } from "@xyflow/svelte";
2
+ // =========================
3
+ // IMPORTS
4
+ // =========================
5
+ import { useStore } from "@xyflow/svelte";
6
6
 
7
- // =========================
8
- // TYPES
9
- // =========================
10
- interface Props {
11
- /** Flow units between dots at the finest level, the same as the snap grid. */
12
- gap?: number;
13
- /** Dot diameter in flow units at the finest level, so it grows and shrinks with zoom. */
14
- size?: number;
15
- /** Any CSS colour; the theme's pattern colour when unset. */
16
- color?: string;
17
- }
7
+ // =========================
8
+ // TYPES
9
+ // =========================
10
+ interface Props {
11
+ /** Flow units between dots at the finest level, the same as the snap grid. */
12
+ gap?: number;
13
+ /** Dot diameter in flow units at the finest level, so it grows and shrinks with zoom. */
14
+ size?: number;
15
+ /** Any CSS colour; the theme's pattern colour when unset. */
16
+ color?: string;
17
+ }
18
18
 
19
- // =========================
20
- // CONSTANTS
21
- // =========================
22
- // Five levels, each twice as wide. A level fades out between these screen spacings,
23
- // so a zoomed-out board keeps an even texture instead of a grey haze.
24
- const LEVELS = 5;
25
- const FADE_START = 6;
26
- const FADE_END = 12;
27
- // Screen-pixel bounds: a dot never vanishes zoomed out or turns into a blob zoomed in.
28
- const MIN_RADIUS = 0.5;
29
- const MAX_RADIUS = 2;
19
+ // =========================
20
+ // CONSTANTS
21
+ // =========================
22
+ // Five levels, each twice as wide. A level fades out between these screen spacings,
23
+ // so a zoomed-out board keeps an even texture instead of a grey haze.
24
+ const LEVELS = 5;
25
+ const FADE_START = 6;
26
+ const FADE_END = 12;
27
+ // Screen-pixel bounds: a dot never vanishes zoomed out or turns into a blob zoomed in.
28
+ const MIN_RADIUS = 0.5;
29
+ const MAX_RADIUS = 2;
30
30
 
31
- // =========================
32
- // PROPS
33
- // =========================
34
- let { gap = 20, size = 2, color = "var(--xy-background-pattern-color, var(--xy-background-pattern-color-default))" }: Props = $props();
31
+ // =========================
32
+ // PROPS
33
+ // =========================
34
+ let {
35
+ gap = 20,
36
+ size = 2,
37
+ color = "var(--xy-background-pattern-color, var(--xy-background-pattern-color-default))",
38
+ }: Props = $props();
35
39
 
36
- // =========================
37
- // STATE
38
- // =========================
39
- const store = useStore();
40
- let canvas = $state<HTMLCanvasElement | null>(null);
41
- let width = $state(0);
42
- let height = $state(0);
43
- let ratio = $state(1);
44
- let theme = $state(0);
40
+ // =========================
41
+ // STATE
42
+ // =========================
43
+ const store = useStore();
44
+ let canvas = $state<HTMLCanvasElement | null>(null);
45
+ let width = $state(0);
46
+ let height = $state(0);
47
+ let ratio = $state(1);
48
+ let theme = $state(0);
45
49
 
46
- // =========================
47
- // FUNCTIONS
48
- // =========================
49
- function smoothstep(from: number, to: number, value: number) {
50
- const t = Math.min(1, Math.max(0, (value - from) / (to - from)));
51
- return t * t * (3 - 2 * t);
52
- }
50
+ // =========================
51
+ // FUNCTIONS
52
+ // =========================
53
+ function smoothstep(from: number, to: number, value: number) {
54
+ const t = Math.min(1, Math.max(0, (value - from) / (to - from)));
55
+ return t * t * (3 - 2 * t);
56
+ }
53
57
 
54
- // Pan redraws every frame, so a tile is built once per size and colour.
55
- // eslint-disable-next-line svelte/prefer-svelte-reactivity -- render cache written inside the draw effect, never rendered
56
- const tiles = new Map<string, HTMLCanvasElement>();
58
+ // Pan redraws every frame, so a tile is built once per size and colour.
59
+ // eslint-disable-next-line svelte/prefer-svelte-reactivity -- render cache written inside the draw effect, never rendered
60
+ const tiles = new Map<string, HTMLCanvasElement>();
57
61
 
58
- function dotTile(step: number, radius: number, fill: string) {
59
- const key = `${step}:${radius.toFixed(2)}:${fill}`;
60
- const hit = tiles.get(key);
61
- if (hit) return hit;
62
- if (tiles.size > 64) tiles.clear();
63
- const tile = document.createElement("canvas");
64
- tiles.set(key, tile);
65
- tile.width = step;
66
- tile.height = step;
67
- const context = tile.getContext("2d");
68
- if (!context) return tile;
69
- context.fillStyle = fill;
70
- context.beginPath();
71
- context.arc(step / 2, step / 2, radius, 0, Math.PI * 2);
72
- context.fill();
73
- return tile;
74
- }
62
+ function dotTile(step: number, radius: number, fill: string) {
63
+ const key = `${step}:${radius.toFixed(2)}:${fill}`;
64
+ const hit = tiles.get(key);
65
+ if (hit) return hit;
66
+ if (tiles.size > 64) tiles.clear();
67
+ const tile = document.createElement("canvas");
68
+ tiles.set(key, tile);
69
+ tile.width = step;
70
+ tile.height = step;
71
+ const context = tile.getContext("2d");
72
+ if (!context) return tile;
73
+ context.fillStyle = fill;
74
+ context.beginPath();
75
+ context.arc(step / 2, step / 2, radius, 0, Math.PI * 2);
76
+ context.fill();
77
+ return tile;
78
+ }
75
79
 
76
- // fallow-ignore-next-line complexity
77
- function draw(element: HTMLCanvasElement, view: { x: number; y: number; zoom: number }, fill: string) {
78
- const context = element.getContext("2d");
79
- if (!context) return;
80
- context.setTransform(1, 0, 0, 1, 0, 0);
81
- context.clearRect(0, 0, element.width, element.height);
80
+ // fallow-ignore-next-line complexity
81
+ function draw(
82
+ element: HTMLCanvasElement,
83
+ view: { x: number; y: number; zoom: number },
84
+ fill: string,
85
+ ) {
86
+ const context = element.getContext("2d");
87
+ if (!context) return;
88
+ context.setTransform(1, 0, 0, 1, 0, 0);
89
+ context.clearRect(0, 0, element.width, element.height);
82
90
 
83
- for (let level = 0; level < LEVELS; level++) {
84
- const spacing = gap * 2 ** level * view.zoom;
85
- // A coarser level draws bigger dots, so zooming out reads as moving away.
86
- const screenRadius = (size / 2) * 2 ** level * view.zoom;
87
- const radius = Math.min(MAX_RADIUS, Math.max(MIN_RADIUS, screenRadius)) * ratio;
88
- const last = level === LEVELS - 1;
89
- const alpha = last ? 1 : smoothstep(FADE_START, FADE_END, spacing);
90
- if (alpha <= 0.01) continue;
91
- const step = spacing * ratio;
92
- const tileSize = Math.max(2, Math.round(step));
93
- const scale = step / tileSize;
94
- const pattern = context.createPattern(dotTile(tileSize, radius / scale, fill), "repeat");
95
- if (!pattern) return;
96
- pattern.setTransform(
97
- new DOMMatrix([scale, 0, 0, scale, view.x * ratio - step / 2, view.y * ratio - step / 2]),
98
- );
99
- context.globalAlpha = alpha;
100
- context.fillStyle = pattern;
101
- context.fillRect(0, 0, element.width, element.height);
102
- if (alpha >= 0.999) break;
91
+ for (let level = 0; level < LEVELS; level++) {
92
+ const spacing = gap * 2 ** level * view.zoom;
93
+ // A coarser level draws bigger dots, so zooming out reads as moving away.
94
+ const screenRadius = (size / 2) * 2 ** level * view.zoom;
95
+ const radius = Math.min(MAX_RADIUS, Math.max(MIN_RADIUS, screenRadius)) * ratio;
96
+ const last = level === LEVELS - 1;
97
+ const alpha = last ? 1 : smoothstep(FADE_START, FADE_END, spacing);
98
+ if (alpha <= 0.01) continue;
99
+ const step = spacing * ratio;
100
+ const tileSize = Math.max(2, Math.round(step));
101
+ const scale = step / tileSize;
102
+ const pattern = context.createPattern(dotTile(tileSize, radius / scale, fill), "repeat");
103
+ if (!pattern) return;
104
+ pattern.setTransform(
105
+ new DOMMatrix([scale, 0, 0, scale, view.x * ratio - step / 2, view.y * ratio - step / 2]),
106
+ );
107
+ context.globalAlpha = alpha;
108
+ context.fillStyle = pattern;
109
+ context.fillRect(0, 0, element.width, element.height);
110
+ if (alpha >= 0.999) break;
111
+ }
112
+ context.globalAlpha = 1;
103
113
  }
104
- context.globalAlpha = 1;
105
- }
106
114
 
107
- // =========================
108
- // EFFECTS
109
- // =========================
110
- $effect(() => {
111
- if (!canvas) return;
112
- const element = canvas;
113
- const observer = new ResizeObserver(() => {
114
- width = element.clientWidth;
115
- height = element.clientHeight;
116
- ratio = window.devicePixelRatio || 1;
115
+ // =========================
116
+ // EFFECTS
117
+ // =========================
118
+ $effect(() => {
119
+ if (!canvas) return;
120
+ const element = canvas;
121
+ const observer = new ResizeObserver(() => {
122
+ width = element.clientWidth;
123
+ height = element.clientHeight;
124
+ ratio = window.devicePixelRatio || 1;
125
+ });
126
+ observer.observe(element);
127
+ return () => observer.disconnect();
117
128
  });
118
- observer.observe(element);
119
- return () => observer.disconnect();
120
- });
121
129
 
122
- // The dot colour is a theme token, so a theme switch redraws.
123
- $effect(() => {
124
- const root = document.documentElement;
125
- const observer = new MutationObserver(() => (theme += 1));
126
- observer.observe(root, { attributes: true, attributeFilter: ["class"] });
127
- return () => observer.disconnect();
128
- });
130
+ // The dot colour is a theme token, so a theme switch redraws.
131
+ $effect(() => {
132
+ const root = document.documentElement;
133
+ const observer = new MutationObserver(() => (theme += 1));
134
+ observer.observe(root, { attributes: true, attributeFilter: ["class"] });
135
+ return () => observer.disconnect();
136
+ });
129
137
 
130
- // Read once per theme: a computed style read mid-pan restyles the whole page every frame.
131
- let fill = $derived.by(() => {
132
- void theme;
133
- void color;
134
- return canvas ? getComputedStyle(canvas).color : "";
135
- });
138
+ // Read once per theme: a computed style read mid-pan restyles the whole page every frame.
139
+ let fill = $derived.by(() => {
140
+ void theme;
141
+ void color;
142
+ return canvas ? getComputedStyle(canvas).color : "";
143
+ });
136
144
 
137
- // fallow-ignore-next-line complexity
138
- $effect(() => {
139
- if (!canvas || !width || !height) return;
140
- const pixelWidth = Math.round(width * ratio);
141
- const pixelHeight = Math.round(height * ratio);
142
- if (canvas.width !== pixelWidth) canvas.width = pixelWidth;
143
- if (canvas.height !== pixelHeight) canvas.height = pixelHeight;
144
- draw(canvas, store.viewport, fill);
145
- });
145
+ // fallow-ignore-next-line complexity
146
+ $effect(() => {
147
+ if (!canvas || !width || !height) return;
148
+ const pixelWidth = Math.round(width * ratio);
149
+ const pixelHeight = Math.round(height * ratio);
150
+ if (canvas.width !== pixelWidth) canvas.width = pixelWidth;
151
+ if (canvas.height !== pixelHeight) canvas.height = pixelHeight;
152
+ draw(canvas, store.viewport, fill);
153
+ });
146
154
  </script>
147
155
 
148
156
  <!-- =========================== -->
@@ -28,7 +28,7 @@
28
28
  suffix?: string;
29
29
  class?: string;
30
30
  style?: string;
31
- };
31
+ }
32
32
 
33
33
  // =========================
34
34
  // PROPS
@@ -1,8 +1,5 @@
1
- /* Styles for AreaChart. 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-area-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-area-chart {
8
5
  width: 100%;
@@ -46,7 +46,7 @@
46
46
  /** A target drawn across the plot, like the deal a cost runs up to. */
47
47
  reference?: ChartReference;
48
48
  class?: string;
49
- };
49
+ }
50
50
 
51
51
  // =========================
52
52
  // PROPS
@@ -24,9 +24,8 @@
24
24
  width: 100%;
25
25
  }
26
26
 
27
- /* WHY: height="fill" grows the chart to its parent's height (e.g. a card that
28
- stretches to match a taller sibling) instead of the fixed body height. The
29
- parent must be a flex column that gives this a determinate height. */
27
+ /* height="fill" grows the chart to its parent's height, so the parent must be a
28
+ flex column with a determinate height. */
30
29
  .hr-bar-chart.hr-bar-fill {
31
30
  flex: 1 1 auto;
32
31
  min-height: 0;
@@ -44,7 +44,7 @@
44
44
  /** Show the value/label figure above the chart. */
45
45
  showFigure?: boolean;
46
46
  class?: string;
47
- };
47
+ }
48
48
 
49
49
  // =========================
50
50
  // PROPS
@@ -1,16 +1,6 @@
1
- /* Global theming for layerchart-rendered chrome shared by every chart in this
2
- package (tooltip surface, header, rows). Ships with `@heyramzi/ui/charts`, so
3
- any consumer that defines the shadcn token set gets a legible tooltip in both
4
- themes for free.
5
-
6
- WHY: layerchart's built-in `default` tooltip paints itself with
7
- `light-dark(var(--color-surface-100, white), var(--color-surface-300, black))`
8
- and a `backdrop-filter` blur. Apps that don't define `--color-surface-*` (and
9
- don't set `color-scheme`) fall back to a translucent white card, which is
10
- unreadable on a dark page. These rules repaint it with `--popover` /
11
- `--border` / `--muted-foreground`, which already flip per theme. The selectors
12
- stay unlayered so they beat layerchart's `@layer component` defaults
13
- regardless of specificity. */
1
+ /* Layerchart's tooltip falls back to translucent white on a dark page without
2
+ `--color-surface-*`, so these unlayered rules repaint it with `--popover`,
3
+ beating its `@layer component` defaults. */
14
4
 
15
5
  .lc-tooltip-container[data-variant="default"] {
16
6
  color: var(--popover-foreground, var(--foreground));
@@ -109,10 +99,8 @@
109
99
  --chart-card-shadow: 0 1px 2px 0 oklch(0 0 0 / 0.04), 0 1px 3px 0 oklch(0 0 0 / 0.06);
110
100
  }
111
101
 
112
- /* Shared layerchart chrome: axis labels, grid rules, and hover highlights were
113
- duplicated identically across every chart's scoped <style>. Centralize them
114
- here, scoped to the chart roots, so the look is defined once. `:where()` keeps
115
- specificity at 0 so a component can still override locally if it must. */
102
+ /* Axis labels, grid rules and hover highlights for every chart, defined once.
103
+ `:where()` keeps specificity at 0 so a component can still override them. */
116
104
  :where(
117
105
  .hr-area-chart,
118
106
  .hr-bar-chart,
@@ -10,7 +10,7 @@
10
10
  interface Props {
11
11
  /** Gradient id. Paint the highlight line with `stroke: url(#id)`. */
12
12
  id: string;
13
- };
13
+ }
14
14
 
15
15
  // =========================
16
16
  // PROPS