@heyramzi/ui 2.0.1 → 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 (341) hide show
  1. package/package.json +48 -24
  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/README.md +9 -12
  31. package/src/canvas/card/card-frame.svelte +43 -39
  32. package/src/canvas/card/resize-handles.svelte +89 -80
  33. package/src/canvas/chrome/chrome-zoom.svelte +101 -100
  34. package/src/canvas/chrome/lock-badge.svelte +8 -8
  35. package/src/canvas/chrome/side-handles.svelte +24 -19
  36. package/src/canvas/chrome/size-chip.svelte +30 -30
  37. package/src/canvas/chrome/snap-guides.svelte +20 -20
  38. package/src/canvas/components/icons/ToolIcon.svelte +8 -8
  39. package/src/canvas/components/shared/svg/PreviewSvg.svelte +19 -19
  40. package/src/canvas/components/shared/svg/ShapeOutline.svelte +71 -48
  41. package/src/canvas/components/shared/svg/StrokeNode.svelte +21 -27
  42. package/src/canvas/components/shared/svg/TextRule.svelte +25 -17
  43. package/src/canvas/edges/bend-handle.svelte +46 -46
  44. package/src/canvas/edges/edge-gradient.svelte +10 -10
  45. package/src/canvas/edges/gradient-edge.svelte +45 -29
  46. package/src/canvas/editor/lock.test.ts +6 -1
  47. package/src/canvas/editor/style.ts +7 -1
  48. package/src/canvas/headless/svg-figure.test.ts +5 -1
  49. package/src/canvas/lod/lod-switch.svelte +111 -106
  50. package/src/canvas/pen/PenField.svelte +87 -81
  51. package/src/canvas/pen/PenOverlay.svelte +127 -127
  52. package/src/canvas/pen/ShapeNode.svelte +31 -32
  53. package/src/canvas/pen/TextNode.svelte +54 -53
  54. package/src/canvas/pen/host-field.test.svelte +7 -7
  55. package/src/canvas/pen/shapes.ts +7 -1
  56. package/src/canvas/screen/GlyphIcon.svelte +22 -17
  57. package/src/canvas/screen/PartView.svelte +138 -72
  58. package/src/canvas/screen/ScreenBody.svelte +81 -60
  59. package/src/canvas/screen/ScreenCard.svelte +20 -20
  60. package/src/canvas/ui/CanvasToolbar.svelte +173 -85
  61. package/src/canvas/ui/FrameLayer.svelte +59 -59
  62. package/src/canvas/ui/PenLayer.svelte +39 -30
  63. package/src/canvas/ui/StylePanel.svelte +119 -102
  64. package/src/canvas/ui/canvas-grid.svelte +137 -129
  65. package/src/charts/animated-value.svelte +1 -1
  66. package/src/charts/area-chart.css +2 -5
  67. package/src/charts/area-chart.svelte +1 -1
  68. package/src/charts/bar-chart.css +2 -3
  69. package/src/charts/bar-chart.svelte +1 -1
  70. package/src/charts/charts.css +5 -17
  71. package/src/charts/crosshair.svelte +1 -1
  72. package/src/charts/dashboard/dashboard-empty.svelte +25 -25
  73. package/src/charts/dashboard/dashboard-section.svelte +24 -24
  74. package/src/charts/dashboard/data-panel.svelte +50 -50
  75. package/src/charts/dashboard/data-sort-head.svelte +56 -56
  76. package/src/charts/dashboard/data-table.svelte +138 -138
  77. package/src/charts/dashboard/panel-grid.svelte +30 -30
  78. package/src/charts/dashboard/period-filter.svelte +25 -25
  79. package/src/charts/dashboard/rating-chip.svelte +19 -19
  80. package/src/charts/dashboard/stat-band.svelte +32 -32
  81. package/src/charts/dashboard/stat-tile.svelte +106 -99
  82. package/src/charts/donut-chart.svelte +1 -1
  83. package/src/charts/edge-fade.svelte +1 -1
  84. package/src/charts/figure.svelte +1 -1
  85. package/src/charts/gauge-chart.css +4 -7
  86. package/src/charts/gauge-chart.svelte +1 -1
  87. package/src/charts/hover-focus.svelte +1 -1
  88. package/src/charts/legend.css +1 -3
  89. package/src/charts/legend.svelte +1 -1
  90. package/src/charts/line-chart.svelte +1 -1
  91. package/src/charts/number-chart.css +6 -9
  92. package/src/charts/number-chart.svelte +1 -1
  93. package/src/charts/reference-line.svelte +19 -19
  94. package/src/charts/reveal.svelte +1 -1
  95. package/src/charts/ring-chart.css +2 -4
  96. package/src/charts/ring-chart.svelte +1 -1
  97. package/src/charts/sparkline.svelte +5 -2
  98. package/src/charts/stat-card.svelte +1 -1
  99. package/src/charts/time-series-chart.css +2 -5
  100. package/src/charts/trend-pill.css +2 -4
  101. package/src/charts/trend-pill.svelte +1 -1
  102. package/src/components/copy/copy-button.svelte +93 -98
  103. package/src/components/copy/copy-button.test.ts +10 -2
  104. package/src/components/data-table/infinite-scroll.svelte +42 -42
  105. package/src/components/data-table/pagination.svelte +34 -22
  106. package/src/components/data-table/row-arrow.svelte +19 -16
  107. package/src/components/data-table/row-context-menu.svelte +52 -52
  108. package/src/components/data-table/sortable-head.svelte +25 -25
  109. package/src/components/data-table/table-skeleton.svelte +41 -36
  110. package/src/components/docs/doc-page.svelte +18 -9
  111. package/src/components/docs/on-this-page.svelte +16 -3
  112. package/src/components/docs/scroll-spy.svelte.ts +2 -3
  113. package/src/components/field-row/field-row.svelte +10 -8
  114. package/src/components/hold-to-confirm/hold-to-confirm.svelte +5 -1
  115. package/src/components/icon/icon.svelte +14 -14
  116. package/src/components/language-flag/language-flag.svelte +3 -1
  117. package/src/components/list-page/detail-empty.svelte +34 -34
  118. package/src/components/list-page/detail-header.svelte +55 -47
  119. package/src/components/list-page/detail-panel.svelte +51 -59
  120. package/src/components/list-page/detail-row.svelte +19 -19
  121. package/src/components/list-page/detail-stack.svelte +21 -21
  122. package/src/components/list-page/empty-state.svelte +30 -30
  123. package/src/components/list-page/filter-item.svelte +20 -20
  124. package/src/components/list-page/filter-popover.svelte +60 -55
  125. package/src/components/list-page/grouped-list.svelte +4 -2
  126. package/src/components/list-page/index.ts +8 -4
  127. package/src/components/list-page/list-page-shell.svelte +2 -5
  128. package/src/components/list-page/master-detail.svelte +87 -88
  129. package/src/components/list-page/notice-bar.svelte +38 -38
  130. package/src/components/list-page/prev-next.svelte +63 -63
  131. package/src/components/list-page/rail-body.svelte +18 -18
  132. package/src/components/list-page/rail-empty.svelte +23 -30
  133. package/src/components/list-page/rail-head.svelte +24 -24
  134. package/src/components/list-page/rail-row.svelte +77 -73
  135. package/src/components/list-page/section-host.ts +2 -3
  136. package/src/components/locale-switch/locale-switch.svelte +41 -41
  137. package/src/components/mark/mark.svelte +34 -38
  138. package/src/components/open-in/index.ts +1 -5
  139. package/src/components/open-in/open-in.svelte +40 -40
  140. package/src/components/picker/mark-picker.svelte +49 -49
  141. package/src/components/picker/picker.svelte +5 -3
  142. package/src/components/section-header/index.ts +1 -0
  143. package/src/components/section-header/section-header.svelte +1 -1
  144. package/src/components/section-header/section-header.test.ts +7 -1
  145. package/src/components/section-header/section-label.svelte +39 -0
  146. package/src/components/segmented/animated-tabs.svelte +31 -2
  147. package/src/components/segmented/collapse.test.ts +2 -1
  148. package/src/components/segmented/index.ts +2 -0
  149. package/src/components/segmented/segment.svelte +52 -49
  150. package/src/components/segmented/segmented-filter.svelte +79 -80
  151. package/src/components/segmented/segmented-nav.svelte +34 -40
  152. package/src/components/segmented/segmented-overflow.svelte +53 -50
  153. package/src/components/segmented/segmented-track.svelte +62 -46
  154. package/src/components/segmented/segmented.ts +2 -3
  155. package/src/components/star-rating-input/star-rating-input.svelte +103 -103
  156. package/src/components/svg/svg.svelte +4 -4
  157. package/src/components/table-view/table-view.svelte +17 -2
  158. package/src/components/table-view/table-view.test.ts +7 -1
  159. package/src/components/table-view/types.ts +11 -11
  160. package/src/components/tags-input/index.ts +13 -0
  161. package/src/components/tags-input/tags-input.svelte +305 -0
  162. package/src/components/tags-input/tags.test.ts +64 -0
  163. package/src/components/tags-input/tags.ts +142 -0
  164. package/src/components/theme/index.ts +2 -4
  165. package/src/components/theme/theme-provider.svelte +1 -3
  166. package/src/components/toolbar/index.ts +2 -0
  167. package/src/components/{list-page → toolbar}/toolbar-search.svelte +25 -25
  168. package/src/components/toolbar/toolbar.svelte +13 -9
  169. package/src/components/toolbar/toolbar.test.ts +10 -1
  170. package/src/design/index.css +21 -78
  171. package/src/design/polish.css +26 -35
  172. package/src/editor/editor-toolbar.svelte +104 -99
  173. package/src/editor/editor.svelte +296 -266
  174. package/src/editor/image-toolbar.svelte +20 -22
  175. package/src/editor/index.ts +1 -4
  176. package/src/editor/inline-editor.svelte +280 -238
  177. package/src/editor/markdown.ts +15 -15
  178. package/src/editor/renderer.svelte +71 -71
  179. package/src/editor/slash-menu.svelte +24 -27
  180. package/src/effects/animated-beam/diagrams/BeamSvg.svelte +1 -1
  181. package/src/effects/image-comparison/ImageComparison.svelte +11 -1
  182. package/src/effects/interactive-hover-button/InteractiveHoverButton.svelte +59 -46
  183. package/src/effects/magic-card/MagicCard.css +14 -7
  184. package/src/effects/marquee/Marquee.svelte +3 -5
  185. package/src/effects/reveal/observe.ts +0 -3
  186. package/src/effects/spotlight/Spotlight.svelte +1 -1
  187. package/src/effects/theme-toggle/ThemeToggle.css +25 -22
  188. package/src/effects/theme-toggle/ThemeToggle.svelte +6 -7
  189. package/src/gallery.test.ts +14 -4
  190. package/src/inbox/README.md +27 -0
  191. package/src/inbox/inbox.test.ts +214 -0
  192. package/src/inbox/index.ts +31 -0
  193. package/src/inbox/logic.ts +169 -0
  194. package/src/inbox/thread-inbox.svelte +439 -0
  195. package/src/inbox/types.ts +161 -0
  196. package/src/lib/theme-transition.ts +2 -4
  197. package/src/lib/tone.ts +2 -4
  198. package/src/lib/utils.ts +2 -2
  199. package/src/modules.test.ts +105 -0
  200. package/src/onboarding/define-tours.test.ts +55 -40
  201. package/src/onboarding/define-tours.ts +23 -23
  202. package/src/onboarding/demo.svelte +24 -5
  203. package/src/onboarding/index.ts +1 -8
  204. package/src/onboarding/onboarding-provider.svelte +19 -7
  205. package/src/onboarding/onboarding.svelte.ts +16 -16
  206. package/src/onboarding/pick.test.ts +61 -52
  207. package/src/onboarding/pick.ts +16 -13
  208. package/src/onboarding/stores.test.ts +80 -80
  209. package/src/onboarding/stores.ts +41 -39
  210. package/src/onboarding/tour-card.svelte +30 -5
  211. package/src/onboarding/tour-runner.svelte +12 -3
  212. package/src/onboarding/types.ts +20 -12
  213. package/src/onboarding/wait.ts +22 -22
  214. package/src/panel/README.md +5 -0
  215. package/src/panel/index.ts +2 -0
  216. package/src/panel/side-panel.svelte +194 -0
  217. package/src/record/README.md +3 -0
  218. package/src/record/demo-data.ts +352 -0
  219. package/src/record/field-section.svelte +57 -0
  220. package/src/record/fields.ts +194 -0
  221. package/src/record/format.ts +94 -0
  222. package/src/{tasks/record → record}/index.ts +49 -35
  223. package/src/{tasks/record → record}/info-list.svelte +145 -114
  224. package/src/record/kinds.ts +42 -0
  225. package/src/record/logic.ts +326 -0
  226. package/src/record/next-up.svelte +81 -0
  227. package/src/{tasks/record → record}/project-list.svelte +56 -50
  228. package/src/{tasks/record → record}/record-demo.svelte +130 -110
  229. package/src/{tasks/record → record}/record-header.svelte +37 -28
  230. package/src/{tasks/record → record}/record-shell.svelte +54 -54
  231. package/src/record/record-stats.svelte +48 -0
  232. package/src/record/record-timeline.svelte +251 -0
  233. package/src/record/record.test.ts +471 -0
  234. package/src/{tasks/record → record}/steps-rail.svelte +225 -216
  235. package/src/record/types.ts +299 -0
  236. package/src/record/where-we-are.svelte +125 -0
  237. package/src/shadcn/button/button.svelte +1 -1
  238. package/src/shadcn/card/card-title.svelte +1 -5
  239. package/src/shadcn/hooks/use-highlighted-code.svelte.ts +2 -13
  240. package/src/shadcn/sheet/sheet-content.svelte +2 -4
  241. package/src/shadcn/table/table-cell.svelte +2 -3
  242. package/src/shadcn/utilities.css +3 -8
  243. package/src/slides/Controls.svelte +150 -124
  244. package/src/slides/Deck.svelte +243 -238
  245. package/src/slides/Fragment.svelte +64 -57
  246. package/src/slides/Overview.svelte +106 -101
  247. package/src/slides/Presenter.svelte +300 -266
  248. package/src/slides/Slide.svelte +185 -170
  249. package/src/slides/SlideList.svelte +16 -12
  250. package/src/slides/ambient.d.ts +1 -1
  251. package/src/slides/bridges.ts +42 -23
  252. package/src/slides/deck-controller.svelte.ts +47 -35
  253. package/src/slides/demo-slides.svelte +330 -306
  254. package/src/slides/demo.svelte +164 -160
  255. package/src/slides/export/capture.ts +20 -16
  256. package/src/slides/export/ffmpeg.ts +43 -38
  257. package/src/slides/export/index.ts +5 -5
  258. package/src/slides/export/launch.ts +3 -1
  259. package/src/slides/export/pdf.ts +30 -27
  260. package/src/slides/export/types.ts +10 -8
  261. package/src/slides/index.ts +11 -11
  262. package/src/slides/labels.ts +22 -19
  263. package/src/slides/layout.ts +9 -2
  264. package/src/slides/model.ts +4 -4
  265. package/src/slides/navigation.ts +43 -31
  266. package/src/slides/theme.css +5 -7
  267. package/src/slides/types.ts +9 -10
  268. package/src/status/README.md +18 -0
  269. package/src/status/define-statuses.ts +25 -0
  270. package/src/status/index.ts +11 -0
  271. package/src/{tasks → status}/status-badge.svelte +3 -1
  272. package/src/{tasks → status}/status-dot.svelte +1 -3
  273. package/src/{tasks → status}/status-icon.svelte +1 -1
  274. package/src/status/status-types.ts +40 -0
  275. package/src/tasks/README.md +1 -1
  276. package/src/tasks/board-order.ts +2 -4
  277. package/src/tasks/demo.svelte +192 -34
  278. package/src/tasks/detail/detail-demo.svelte +75 -15
  279. package/src/tasks/detail/detail-types.ts +45 -45
  280. package/src/tasks/detail/task-detail.svelte +40 -10
  281. package/src/tasks/detail/task-property.svelte +8 -2
  282. package/src/tasks/detail/task-thread.svelte +53 -12
  283. package/src/tasks/gantt/gantt-bar.svelte +17 -6
  284. package/src/tasks/gantt/gantt-grid.svelte +40 -9
  285. package/src/tasks/gantt/gantt-group-row.svelte +8 -2
  286. package/src/tasks/gantt/gantt-header.svelte +9 -3
  287. package/src/tasks/gantt/gantt-task-row.svelte +26 -6
  288. package/src/tasks/gantt/gantt.css +2 -1
  289. package/src/tasks/gantt/gantt.test.ts +4 -2
  290. package/src/tasks/gantt/logic.test.ts +110 -20
  291. package/src/tasks/gantt/logic.ts +42 -6
  292. package/src/tasks/inbox/inbox-card.svelte +15 -7
  293. package/src/tasks/inbox/inbox-draft.svelte +1 -1
  294. package/src/tasks/inbox/inbox-logic.ts +6 -40
  295. package/src/tasks/inbox/inbox-statuses.ts +24 -24
  296. package/src/tasks/inbox/inbox-trace.svelte +9 -3
  297. package/src/tasks/inbox/inbox-types.ts +54 -58
  298. package/src/tasks/inbox/index.ts +22 -21
  299. package/src/tasks/index.ts +52 -53
  300. package/src/tasks/kanban-board.svelte +16 -12
  301. package/src/tasks/kanban-types.ts +2 -3
  302. package/src/tasks/presets/sales.ts +7 -7
  303. package/src/tasks/task-gantt.svelte +28 -8
  304. package/src/tasks/task-list.svelte +41 -17
  305. package/src/tasks/time/duration.ts +40 -40
  306. package/src/tasks/time/time-chip.svelte +9 -2
  307. package/src/tasks/time/time-dot.svelte +1 -1
  308. package/src/tasks/time/time-ring.svelte +11 -3
  309. package/src/tasks/time/time-tracker.svelte +57 -15
  310. package/src/tasks/time/time-types.ts +57 -57
  311. package/src/tasks/use-kanban-dnd.svelte.ts +2 -3
  312. package/src/thread/README.md +31 -0
  313. package/src/thread/index.ts +12 -0
  314. package/src/thread/logic.ts +61 -0
  315. package/src/thread/thread.svelte +432 -0
  316. package/src/thread/thread.test.ts +73 -0
  317. package/src/thread/types.ts +93 -0
  318. package/src/tokens/craft.css +17 -53
  319. package/src/tokens/index.css +3 -13
  320. package/src/tokens/palette.css +13 -11
  321. package/src/tokens/primitives.css +10 -23
  322. package/src/components/list-page/section-label.svelte +0 -39
  323. package/src/tasks/define-statuses.ts +0 -32
  324. package/src/tasks/inbox/inbox.svelte +0 -203
  325. package/src/tasks/record/demo-data.ts +0 -286
  326. package/src/tasks/record/field-section.svelte +0 -53
  327. package/src/tasks/record/fields.ts +0 -142
  328. package/src/tasks/record/format.ts +0 -87
  329. package/src/tasks/record/kinds.ts +0 -42
  330. package/src/tasks/record/logic.ts +0 -324
  331. package/src/tasks/record/next-up.svelte +0 -70
  332. package/src/tasks/record/record-stats.svelte +0 -48
  333. package/src/tasks/record/record-timeline.svelte +0 -231
  334. package/src/tasks/record/record.test.ts +0 -350
  335. package/src/tasks/record/types.ts +0 -280
  336. package/src/tasks/record/where-we-are.svelte +0 -121
  337. package/src/tasks/side-panel.svelte +0 -193
  338. package/src/tasks/status-types.ts +0 -40
  339. /package/src/components/{list-page → toolbar}/toolbar-trigger.ts +0 -0
  340. /package/src/{tasks → panel}/side-panel-types.ts +0 -0
  341. /package/src/{tasks/record → record}/gutter.ts +0 -0
@@ -1,120 +1,121 @@
1
1
  <script lang="ts">
2
- // =========================
3
- // IMPORTS
4
- // =========================
5
- import { useConnection, useStore } from "@xyflow/svelte";
6
- import { untrack } from "svelte";
2
+ // =========================
3
+ // IMPORTS
4
+ // =========================
5
+ import { useConnection, useStore } from "@xyflow/svelte";
6
+ import { untrack } from "svelte";
7
7
 
8
- // A root `--canvas-unzoom` restyled every card per zoom frame. It lives only on nodes showing chrome: selected, hovered, or all while a link is dragged.
8
+ // A root `--canvas-unzoom` restyled every card per zoom frame. It lives only on nodes showing chrome: selected, hovered, or all while a link is dragged.
9
9
 
10
- // =========================
11
- // CONSTANTS
12
- // =========================
13
- const MIN_ZOOM = 0.05;
14
- const NODE = ".svelte-flow__node";
10
+ // =========================
11
+ // CONSTANTS
12
+ // =========================
13
+ const MIN_ZOOM = 0.05;
14
+ const NODE = ".svelte-flow__node";
15
15
 
16
- // =========================
17
- // STATE
18
- // =========================
19
- const store = useStore();
20
- const connection = useConnection();
16
+ // =========================
17
+ // STATE
18
+ // =========================
19
+ const store = useStore();
20
+ const connection = useConnection();
21
21
 
22
- // Plain collections: they change on selection and hover, never per frame, and nothing reads them reactively.
23
- const selected = new Set<HTMLElement>();
24
- let hovered: HTMLElement | null = null;
22
+ // Plain collections: they change on selection and hover, never per frame, and nothing reads them reactively.
23
+ const selected = new Set<HTMLElement>();
24
+ let hovered: HTMLElement | null = null;
25
25
 
26
- // =========================
27
- // DERIVED
28
- // =========================
29
- let unzoom = $derived(String(1 / Math.max(store.viewport.zoom, MIN_ZOOM)));
30
- let linking = $derived(connection.current.inProgress);
26
+ // =========================
27
+ // DERIVED
28
+ // =========================
29
+ let unzoom = $derived(String(1 / Math.max(store.viewport.zoom, MIN_ZOOM)));
30
+ let linking = $derived(connection.current.inProgress);
31
31
 
32
- // =========================
33
- // FUNCTIONS
34
- // =========================
35
- const paint = (node: HTMLElement | null, value = unzoom) => node?.style.setProperty("--canvas-unzoom", value);
32
+ // =========================
33
+ // FUNCTIONS
34
+ // =========================
35
+ const paint = (node: HTMLElement | null, value = unzoom) =>
36
+ node?.style.setProperty("--canvas-unzoom", value);
36
37
 
37
- function paintShowing(value: string) {
38
- for (const node of selected) {
39
- if (node.isConnected) paint(node, value);
40
- else selected.delete(node);
41
- }
42
- paint(hovered, value);
43
- }
44
-
45
- // Chrome that belongs to one card (grips, quick-add) hides while several are selected: see chrome.css.
46
- function flagMulti() {
47
- store.domNode?.toggleAttribute("data-cv-multi", selected.size > 1);
48
- }
49
-
50
- function track(node: HTMLElement) {
51
- if (node.classList.contains("selected")) {
52
- if (!selected.has(node)) {
53
- selected.add(node);
54
- paint(node);
38
+ function paintShowing(value: string) {
39
+ for (const node of selected) {
40
+ if (node.isConnected) paint(node, value);
41
+ else selected.delete(node);
55
42
  }
56
- } else selected.delete(node);
57
- flagMulti();
58
- }
43
+ paint(hovered, value);
44
+ }
59
45
 
60
- // =========================
61
- // EFFECTS
62
- // =========================
63
- $effect(() => {
64
- paintShowing(unzoom);
65
- });
46
+ // Chrome that belongs to one card (grips, quick-add) hides while several are selected: see chrome.css.
47
+ function flagMulti() {
48
+ store.domNode?.toggleAttribute("data-cv-multi", selected.size > 1);
49
+ }
66
50
 
67
- $effect(() => {
68
- const root = store.domNode;
69
- if (!root) return;
70
- if (linking) {
71
- root.dataset.cvLinking = "";
72
- const value = unzoom;
73
- for (const node of root.querySelectorAll<HTMLElement>(NODE)) paint(node, value);
74
- return () => delete root.dataset.cvLinking;
51
+ function track(node: HTMLElement) {
52
+ if (node.classList.contains("selected")) {
53
+ if (!selected.has(node)) {
54
+ selected.add(node);
55
+ paint(node);
56
+ }
57
+ } else selected.delete(node);
58
+ flagMulti();
75
59
  }
76
- });
77
60
 
78
- function watch(root: HTMLElement) {
79
- const over = (event: PointerEvent) => {
80
- const node = (event.target as Element | null)?.closest<HTMLElement>(NODE) ?? null;
81
- if (node === hovered) return;
82
- hovered = node;
83
- paint(node);
84
- };
85
- root.addEventListener("pointerover", over, { passive: true });
61
+ // =========================
62
+ // EFFECTS
63
+ // =========================
64
+ $effect(() => {
65
+ paintShowing(unzoom);
66
+ });
86
67
 
87
- // A node picks up or loses the `selected` class when the selection changes, and arrives selected when pasted or created.
88
- for (const node of root.querySelectorAll<HTMLElement>(`${NODE}.selected`)) track(node);
89
- const classes = new MutationObserver((records) => {
90
- for (const record of records) {
91
- const node = record.target as HTMLElement;
92
- if (node.matches(NODE)) track(node);
68
+ $effect(() => {
69
+ const root = store.domNode;
70
+ if (!root) return;
71
+ if (linking) {
72
+ root.dataset.cvLinking = "";
73
+ const value = unzoom;
74
+ for (const node of root.querySelectorAll<HTMLElement>(NODE)) paint(node, value);
75
+ return () => delete root.dataset.cvLinking;
93
76
  }
94
77
  });
95
- classes.observe(root, { attributes: true, attributeFilter: ["class"], subtree: true });
96
- const arrivals = new MutationObserver((records) => {
97
- for (const record of records) {
98
- for (const added of record.addedNodes) {
99
- if (added instanceof HTMLElement && added.matches(NODE)) track(added);
78
+
79
+ function watch(root: HTMLElement) {
80
+ const over = (event: PointerEvent) => {
81
+ const node = (event.target as Element | null)?.closest<HTMLElement>(NODE) ?? null;
82
+ if (node === hovered) return;
83
+ hovered = node;
84
+ paint(node);
85
+ };
86
+ root.addEventListener("pointerover", over, { passive: true });
87
+
88
+ // A node picks up or loses the `selected` class when the selection changes, and arrives selected when pasted or created.
89
+ for (const node of root.querySelectorAll<HTMLElement>(`${NODE}.selected`)) track(node);
90
+ const classes = new MutationObserver((records) => {
91
+ for (const record of records) {
92
+ const node = record.target as HTMLElement;
93
+ if (node.matches(NODE)) track(node);
100
94
  }
101
- }
102
- });
103
- const layer = root.querySelector(".svelte-flow__nodes");
104
- if (layer) arrivals.observe(layer, { childList: true });
95
+ });
96
+ classes.observe(root, { attributes: true, attributeFilter: ["class"], subtree: true });
97
+ const arrivals = new MutationObserver((records) => {
98
+ for (const record of records) {
99
+ for (const added of record.addedNodes) {
100
+ if (added instanceof HTMLElement && added.matches(NODE)) track(added);
101
+ }
102
+ }
103
+ });
104
+ const layer = root.querySelector(".svelte-flow__nodes");
105
+ if (layer) arrivals.observe(layer, { childList: true });
105
106
 
106
- return () => {
107
- root.removeEventListener("pointerover", over);
108
- classes.disconnect();
109
- arrivals.disconnect();
110
- selected.clear();
111
- root.removeAttribute("data-cv-multi");
112
- };
113
- }
107
+ return () => {
108
+ root.removeEventListener("pointerover", over);
109
+ classes.disconnect();
110
+ arrivals.disconnect();
111
+ selected.clear();
112
+ root.removeAttribute("data-cv-multi");
113
+ };
114
+ }
114
115
 
115
- $effect(() => {
116
- const root = store.domNode;
117
- if (!root) return;
118
- return untrack(() => watch(root));
119
- });
116
+ $effect(() => {
117
+ const root = store.domNode;
118
+ if (!root) return;
119
+ return untrack(() => watch(root));
120
+ });
120
121
  </script>
@@ -1,13 +1,13 @@
1
1
  <script lang="ts">
2
- // =========================
3
- // IMPORTS
4
- // =========================
5
- import type { Snippet } from "svelte";
2
+ // =========================
3
+ // IMPORTS
4
+ // =========================
5
+ import type { Snippet } from "svelte";
6
6
 
7
- // =========================
8
- // PROPS
9
- // =========================
10
- let { label, children }: { label: string; children: Snippet } = $props();
7
+ // =========================
8
+ // PROPS
9
+ // =========================
10
+ let { label, children }: { label: string; children: Snippet } = $props();
11
11
  </script>
12
12
 
13
13
  <!-- =========================== -->
@@ -1,24 +1,24 @@
1
1
  <script lang="ts">
2
- // =========================
3
- // IMPORTS
4
- // =========================
5
- import { Handle, Position } from "@xyflow/svelte";
2
+ // =========================
3
+ // IMPORTS
4
+ // =========================
5
+ import { Handle, Position } from "@xyflow/svelte";
6
6
 
7
- // =========================
8
- // CONSTANTS
9
- // =========================
10
- // Each side has an invisible source over the visible target. An edge saved without handle ids takes the first, so top and bottom stay first.
11
- const SIDES = [
12
- { id: "top", position: Position.Top },
13
- { id: "bottom", position: Position.Bottom },
14
- { id: "left", position: Position.Left },
15
- { id: "right", position: Position.Right },
16
- ] as const;
7
+ // =========================
8
+ // CONSTANTS
9
+ // =========================
10
+ // Each side has an invisible source over the visible target. An edge saved without handle ids takes the first, so top and bottom stay first.
11
+ const SIDES = [
12
+ { id: "top", position: Position.Top },
13
+ { id: "bottom", position: Position.Bottom },
14
+ { id: "left", position: Position.Left },
15
+ { id: "right", position: Position.Right },
16
+ ] as const;
17
17
 
18
- // =========================
19
- // PROPS
20
- // =========================
21
- let { class: className = "" }: { class?: string } = $props();
18
+ // =========================
19
+ // PROPS
20
+ // =========================
21
+ let { class: className = "" }: { class?: string } = $props();
22
22
  </script>
23
23
 
24
24
  <!-- =========================== -->
@@ -26,5 +26,10 @@ let { class: className = "" }: { class?: string } = $props();
26
26
  <!-- =========================== -->
27
27
  {#each SIDES as side (side.id)}
28
28
  <Handle type="target" position={side.position} id={side.id} class="cv-handle {className}" />
29
- <Handle type="source" position={side.position} id={side.id} class="cv-handle !opacity-0 {className}" />
29
+ <Handle
30
+ type="source"
31
+ position={side.position}
32
+ id={side.id}
33
+ class="cv-handle !opacity-0 {className}"
34
+ />
30
35
  {/each}
@@ -1,32 +1,32 @@
1
1
  <script lang="ts">
2
- // =========================
3
- // IMPORTS
4
- // =========================
5
- import { onMount } from "svelte";
2
+ // =========================
3
+ // IMPORTS
4
+ // =========================
5
+ import { onMount } from "svelte";
6
6
 
7
- // =========================
8
- // STATE
9
- // =========================
10
- let chip = $state<HTMLElement>();
11
- let width = $state(0);
12
- let height = $state(0);
7
+ // =========================
8
+ // STATE
9
+ // =========================
10
+ let chip = $state<HTMLElement>();
11
+ let width = $state(0);
12
+ let height = $state(0);
13
13
 
14
- // =========================
15
- // LIFECYCLE
16
- // =========================
17
- // Layout size, which a zoom transform doesn't change. Observing the card's own box keeps the figure live through a resize drag.
18
- onMount(() => {
19
- const card = chip?.parentElement;
20
- if (!card) return;
21
- const read = () => {
22
- width = Math.round(card.offsetWidth);
23
- height = Math.round(card.offsetHeight);
24
- };
25
- read();
26
- const observer = new ResizeObserver(read);
27
- observer.observe(card);
28
- return () => observer.disconnect();
29
- });
14
+ // =========================
15
+ // LIFECYCLE
16
+ // =========================
17
+ // Layout size, which a zoom transform doesn't change. Observing the card's own box keeps the figure live through a resize drag.
18
+ onMount(() => {
19
+ const card = chip?.parentElement;
20
+ if (!card) return;
21
+ const read = () => {
22
+ width = Math.round(card.offsetWidth);
23
+ height = Math.round(card.offsetHeight);
24
+ };
25
+ read();
26
+ const observer = new ResizeObserver(read);
27
+ observer.observe(card);
28
+ return () => observer.disconnect();
29
+ });
30
30
  </script>
31
31
 
32
32
  <!-- =========================== -->
@@ -38,8 +38,8 @@ onMount(() => {
38
38
  <!-- STYLES -->
39
39
  <!-- =========================== -->
40
40
  <style>
41
- /* A table draws its add-row strip from 12 to 32 card pixels under it. The chip hangs under that, 10 screen pixels clear. */
42
- :global(.svelte-flow__node-table .cv-size-chip) {
43
- top: calc(100% + 32px + 10px * var(--canvas-unzoom, 1));
44
- }
41
+ /* A table draws its add-row strip from 12 to 32 card pixels under it. The chip hangs under that, 10 screen pixels clear. */
42
+ :global(.svelte-flow__node-table .cv-size-chip) {
43
+ top: calc(100% + 32px + 10px * var(--canvas-unzoom, 1));
44
+ }
45
45
  </style>
@@ -1,27 +1,27 @@
1
1
  <script lang="ts">
2
- // =========================
3
- // IMPORTS
4
- // =========================
5
- import type { SnapGuide } from "../headless/snap-guides.js";
2
+ // =========================
3
+ // IMPORTS
4
+ // =========================
5
+ import type { SnapGuide } from "../headless/snap-guides.js";
6
6
 
7
- // =========================
8
- // TYPES
9
- // =========================
10
- interface Props {
11
- guides: SnapGuide[];
12
- /** Canvas zoom, so the line stays one screen pixel at every scale. */
13
- zoom: number;
14
- }
7
+ // =========================
8
+ // TYPES
9
+ // =========================
10
+ interface Props {
11
+ guides: SnapGuide[];
12
+ /** Canvas zoom, so the line stays one screen pixel at every scale. */
13
+ zoom: number;
14
+ }
15
15
 
16
- // =========================
17
- // PROPS
18
- // =========================
19
- let { guides, zoom }: Props = $props();
16
+ // =========================
17
+ // PROPS
18
+ // =========================
19
+ let { guides, zoom }: Props = $props();
20
20
 
21
- // =========================
22
- // DERIVED
23
- // =========================
24
- let thickness = $derived(1 / Math.max(zoom, 0.05));
21
+ // =========================
22
+ // DERIVED
23
+ // =========================
24
+ let thickness = $derived(1 / Math.max(zoom, 0.05));
25
25
  </script>
26
26
 
27
27
  <!-- =========================== -->
@@ -1,13 +1,13 @@
1
1
  <script lang="ts">
2
- // =========================
3
- // IMPORTS
4
- // =========================
5
- import { ICON_PATHS, type IconName } from "../../ui/icons.js";
2
+ // =========================
3
+ // IMPORTS
4
+ // =========================
5
+ import { ICON_PATHS, type IconName } from "../../ui/icons.js";
6
6
 
7
- // =========================
8
- // PROPS
9
- // =========================
10
- let { name, size = 18 }: { name: IconName; size?: number } = $props();
7
+ // =========================
8
+ // PROPS
9
+ // =========================
10
+ let { name, size = 18 }: { name: IconName; size?: number } = $props();
11
11
  </script>
12
12
 
13
13
  <!-- =========================== -->
@@ -1,25 +1,25 @@
1
1
  <script lang="ts">
2
- // =========================
3
- // IMPORTS
4
- // =========================
5
- import { penColorVar } from "../../../pen/colors.js";
6
- import type { PenPreview } from "../../../pen/pen-session.js";
7
- import { shapeOutline } from "../../../pen/shapes.js";
8
- import { HIGHLIGHTER_OPACITY } from "../../../pen/types.js";
2
+ // =========================
3
+ // IMPORTS
4
+ // =========================
5
+ import { penColorVar } from "../../../pen/colors.js";
6
+ import type { PenPreview } from "../../../pen/pen-session.js";
7
+ import { shapeOutline } from "../../../pen/shapes.js";
8
+ import { HIGHLIGHTER_OPACITY } from "../../../pen/types.js";
9
9
 
10
- // =========================
11
- // TYPES
12
- // =========================
13
- interface Props {
14
- preview: PenPreview | null;
15
- /** The xyflow viewport, so the preview lines up with the board. */
16
- viewport: { x: number; y: number; zoom: number };
17
- }
10
+ // =========================
11
+ // TYPES
12
+ // =========================
13
+ interface Props {
14
+ preview: PenPreview | null;
15
+ /** The xyflow viewport, so the preview lines up with the board. */
16
+ viewport: { x: number; y: number; zoom: number };
17
+ }
18
18
 
19
- // =========================
20
- // PROPS
21
- // =========================
22
- let { preview, viewport }: Props = $props();
19
+ // =========================
20
+ // PROPS
21
+ // =========================
22
+ let { preview, viewport }: Props = $props();
23
23
  </script>
24
24
 
25
25
  <!-- =========================== -->
@@ -1,44 +1,58 @@
1
1
  <script lang="ts">
2
- // =========================
3
- // IMPORTS
4
- // =========================
5
- import { roughEllipse, roughPolygon, roughRect, seedFrom, sketchColors } from "../../../headless/sketch.js";
6
- import { penColorVar } from "../../../pen/colors.js";
7
- import type { ShapeData } from "../../../pen/types.js";
2
+ // =========================
3
+ // IMPORTS
4
+ // =========================
5
+ import {
6
+ roughEllipse,
7
+ roughPolygon,
8
+ roughRect,
9
+ seedFrom,
10
+ sketchColors,
11
+ } from "../../../headless/sketch.js";
12
+ import { penColorVar } from "../../../pen/colors.js";
13
+ import type { ShapeData } from "../../../pen/types.js";
8
14
 
9
- // =========================
10
- // TYPES
11
- // =========================
12
- interface Props {
13
- shape: ShapeData;
14
- outline: string;
15
- ink: string;
16
- width?: number;
17
- height?: number;
18
- /** Draw the outline with the hand instead of the exact path. */
19
- hand?: boolean;
20
- /** Seeds the hand, so a shape keeps its wobble between renders. */
21
- seed?: string;
22
- }
15
+ // =========================
16
+ // TYPES
17
+ // =========================
18
+ interface Props {
19
+ shape: ShapeData;
20
+ outline: string;
21
+ ink: string;
22
+ width?: number;
23
+ height?: number;
24
+ /** Draw the outline with the hand instead of the exact path. */
25
+ hand?: boolean;
26
+ /** Seeds the hand, so a shape keeps its wobble between renders. */
27
+ seed?: string;
28
+ }
23
29
 
24
- // =========================
25
- // PROPS
26
- // =========================
27
- let { shape, outline, ink, width, height, hand = false, seed = "" }: Props = $props();
30
+ // =========================
31
+ // PROPS
32
+ // =========================
33
+ let { shape, outline, ink, width, height, hand = false, seed = "" }: Props = $props();
28
34
 
29
- let wash = $derived(shape.fill ? penColorVar(shape.fill) : ink);
35
+ let wash = $derived(shape.fill ? penColorVar(shape.fill) : ink);
30
36
 
31
- let rough = $derived.by(() => {
32
- const w = width ?? 0;
33
- const h = height ?? 0;
34
- if (!hand || w < 4 || h < 4) return [];
35
- const pen = { seed: seedFrom(seed), colors: { ...sketchColors("ink", false), stroke: ink } };
36
- if (shape.shape === "ellipse") return roughEllipse(w, h, pen);
37
- if (shape.shape === "diamond") {
38
- return roughPolygon([[w / 2, 1], [w - 1, h / 2], [w / 2, h - 1], [1, h / 2]], pen);
39
- }
40
- return roughRect(w, h, pen);
41
- });
37
+ let rough = $derived.by(() => {
38
+ const w = width ?? 0;
39
+ const h = height ?? 0;
40
+ if (!hand || w < 4 || h < 4) return [];
41
+ const pen = { seed: seedFrom(seed), colors: { ...sketchColors("ink", false), stroke: ink } };
42
+ if (shape.shape === "ellipse") return roughEllipse(w, h, pen);
43
+ if (shape.shape === "diamond") {
44
+ return roughPolygon(
45
+ [
46
+ [w / 2, 1],
47
+ [w - 1, h / 2],
48
+ [w / 2, h - 1],
49
+ [1, h / 2],
50
+ ],
51
+ pen,
52
+ );
53
+ }
54
+ return roughRect(w, h, pen);
55
+ });
42
56
  </script>
43
57
 
44
58
  <!-- =========================== -->
@@ -50,20 +64,29 @@ let rough = $derived.by(() => {
50
64
  <path d={outline} fill={wash} fill-opacity="0.12" stroke="none" pointer-events="all" />
51
65
  {/if}
52
66
  {#each rough as path, i (i)}
53
- <path class="pen-line" style:--sw={shape.strokeWidth} d={path.d} fill="none" stroke={ink} stroke-width={shape.strokeWidth} stroke-linecap="round" stroke-linejoin="round" />
67
+ <path
68
+ class="pen-line"
69
+ style:--sw={shape.strokeWidth}
70
+ d={path.d}
71
+ fill="none"
72
+ stroke={ink}
73
+ stroke-width={shape.strokeWidth}
74
+ stroke-linecap="round"
75
+ stroke-linejoin="round"
76
+ />
54
77
  {/each}
55
78
  {:else}
56
- <path
57
- class="pen-line"
58
- style:--sw={shape.strokeWidth}
59
- d={outline}
60
- fill={shape.filled ? wash : "none"}
61
- fill-opacity={shape.filled ? 0.12 : 0}
62
- stroke={ink}
63
- stroke-width={shape.strokeWidth}
64
- stroke-linejoin="round"
65
- pointer-events={shape.filled ? "all" : "stroke"}
66
- />
79
+ <path
80
+ class="pen-line"
81
+ style:--sw={shape.strokeWidth}
82
+ d={outline}
83
+ fill={shape.filled ? wash : "none"}
84
+ fill-opacity={shape.filled ? 0.12 : 0}
85
+ stroke={ink}
86
+ stroke-width={shape.strokeWidth}
87
+ stroke-linejoin="round"
88
+ pointer-events={shape.filled ? "all" : "stroke"}
89
+ />
67
90
  {/if}
68
91
  </svg>
69
92