@heyramzi/ui 2.1.0 → 2.3.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 (317) hide show
  1. package/COMPONENTS.md +1 -1
  2. package/package.json +7 -8
  3. package/scripts/sync-shadcn.mjs +3 -4
  4. package/src/agents/agent-avatar.svelte +604 -575
  5. package/src/agents/agent-cast.svelte +179 -183
  6. package/src/agents/demo.svelte +125 -113
  7. package/src/agents/helpers.ts +23 -17
  8. package/src/agents/index.ts +4 -4
  9. package/src/agents/page-state.ts +6 -6
  10. package/src/agents/types.ts +6 -6
  11. package/src/ai/ai-button/AiButton.svelte +4 -12
  12. package/src/ai/generation-progress/GenerationProgress.svelte +10 -3
  13. package/src/ai/prompt-input/PromptInputTextarea.svelte +1 -2
  14. package/src/ai/response/Response.css +2 -3
  15. package/src/animations/avatar-group-hover.css +2 -16
  16. package/src/animations/card-tilt.css +3 -6
  17. package/src/animations/digit-pop-in.css +2 -15
  18. package/src/animations/dropdown.css +4 -17
  19. package/src/animations/input-clear-dissolve.css +5 -18
  20. package/src/animations/input-shake.css +9 -41
  21. package/src/animations/learn-more-hover.css +17 -9
  22. package/src/animations/like-button.css +2 -4
  23. package/src/animations/page-side-by-side.css +2 -10
  24. package/src/animations/plus-menu-morph.css +2 -5
  25. package/src/animations/shimmer-text.css +5 -18
  26. package/src/animations/spinning-counter.css +2 -6
  27. package/src/animations/success-check.css +2 -15
  28. package/src/animations/thinking-states.css +2 -7
  29. package/src/brand/brand.test.ts +21 -5
  30. package/src/brand/index.ts +4 -10
  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/canvas-cleanup.test.ts +1 -1
  49. package/src/canvas/headless/svg-figure.test.ts +5 -1
  50. package/src/canvas/lod/lod-switch.svelte +111 -106
  51. package/src/canvas/ops/ops.ts +1 -1
  52. package/src/canvas/pen/PenField.svelte +87 -81
  53. package/src/canvas/pen/PenOverlay.svelte +127 -127
  54. package/src/canvas/pen/ShapeNode.svelte +31 -32
  55. package/src/canvas/pen/TextNode.svelte +54 -53
  56. package/src/canvas/pen/host-field.test.svelte +7 -7
  57. package/src/canvas/pen/shapes.ts +7 -1
  58. package/src/canvas/screen/GlyphIcon.svelte +22 -17
  59. package/src/canvas/screen/PartView.svelte +138 -72
  60. package/src/canvas/screen/ScreenBody.svelte +81 -60
  61. package/src/canvas/screen/ScreenCard.svelte +20 -20
  62. package/src/canvas/ui/CanvasToolbar.svelte +173 -85
  63. package/src/canvas/ui/FrameLayer.svelte +59 -59
  64. package/src/canvas/ui/PenLayer.svelte +39 -30
  65. package/src/canvas/ui/StylePanel.svelte +119 -102
  66. package/src/canvas/ui/canvas-grid.svelte +137 -129
  67. package/src/charts/animated-value.svelte +1 -1
  68. package/src/charts/area-chart.css +2 -5
  69. package/src/charts/area-chart.svelte +1 -1
  70. package/src/charts/bar-chart.css +2 -3
  71. package/src/charts/bar-chart.svelte +1 -1
  72. package/src/charts/charts.css +5 -17
  73. package/src/charts/crosshair.svelte +1 -1
  74. package/src/charts/dashboard/dashboard-empty.svelte +25 -25
  75. package/src/charts/dashboard/dashboard-section.svelte +24 -24
  76. package/src/charts/dashboard/data-panel.svelte +50 -50
  77. package/src/charts/dashboard/data-sort-head.svelte +56 -56
  78. package/src/charts/dashboard/data-table.svelte +138 -138
  79. package/src/charts/dashboard/panel-grid.svelte +30 -30
  80. package/src/charts/dashboard/period-filter.svelte +25 -25
  81. package/src/charts/dashboard/rating-chip.svelte +19 -19
  82. package/src/charts/dashboard/stat-band.svelte +32 -32
  83. package/src/charts/dashboard/stat-tile.svelte +106 -99
  84. package/src/charts/donut-chart.svelte +1 -1
  85. package/src/charts/edge-fade.svelte +1 -1
  86. package/src/charts/figure.svelte +1 -1
  87. package/src/charts/gauge-chart.css +4 -7
  88. package/src/charts/gauge-chart.svelte +1 -1
  89. package/src/charts/hover-focus.svelte +1 -1
  90. package/src/charts/legend.css +1 -3
  91. package/src/charts/legend.svelte +1 -1
  92. package/src/charts/line-chart.svelte +1 -1
  93. package/src/charts/number-chart.css +6 -9
  94. package/src/charts/number-chart.svelte +1 -1
  95. package/src/charts/reference-line.svelte +19 -19
  96. package/src/charts/reveal.svelte +1 -1
  97. package/src/charts/ring-chart.css +2 -4
  98. package/src/charts/ring-chart.svelte +1 -1
  99. package/src/charts/sparkline.svelte +5 -2
  100. package/src/charts/stat-card.svelte +1 -1
  101. package/src/charts/time-series-chart.css +2 -5
  102. package/src/charts/trend-pill.css +2 -4
  103. package/src/charts/trend-pill.svelte +1 -1
  104. package/src/components/copy/copy-button.svelte +93 -98
  105. package/src/components/copy/copy-button.test.ts +10 -2
  106. package/src/components/data-table/infinite-scroll.svelte +42 -42
  107. package/src/components/data-table/pagination.svelte +34 -22
  108. package/src/components/data-table/row-arrow.svelte +19 -16
  109. package/src/components/data-table/row-context-menu.svelte +52 -52
  110. package/src/components/data-table/sortable-head.svelte +25 -25
  111. package/src/components/data-table/table-skeleton.svelte +41 -36
  112. package/src/components/docs/doc-page.svelte +18 -9
  113. package/src/components/docs/on-this-page.svelte +16 -3
  114. package/src/components/docs/scroll-spy.svelte.ts +2 -3
  115. package/src/components/field-row/field-row.svelte +10 -8
  116. package/src/components/hold-to-confirm/hold-to-confirm.svelte +5 -1
  117. package/src/components/icon/icon.svelte +14 -14
  118. package/src/components/language-flag/language-flag.svelte +3 -1
  119. package/src/components/list-page/detail-empty.svelte +34 -34
  120. package/src/components/list-page/detail-header.svelte +55 -47
  121. package/src/components/list-page/detail-panel.svelte +51 -59
  122. package/src/components/list-page/detail-row.svelte +19 -19
  123. package/src/components/list-page/detail-stack.svelte +21 -21
  124. package/src/components/list-page/empty-state.svelte +30 -30
  125. package/src/components/list-page/filter-item.svelte +20 -20
  126. package/src/components/list-page/filter-popover.svelte +60 -55
  127. package/src/components/list-page/grouped-list.svelte +4 -2
  128. package/src/components/list-page/index.ts +5 -1
  129. package/src/components/list-page/list-page-shell.svelte +2 -5
  130. package/src/components/list-page/master-detail.svelte +87 -88
  131. package/src/components/list-page/notice-bar.svelte +38 -38
  132. package/src/components/list-page/prev-next.svelte +63 -63
  133. package/src/components/list-page/rail-body.svelte +18 -18
  134. package/src/components/list-page/rail-empty.svelte +23 -30
  135. package/src/components/list-page/rail-head.svelte +24 -24
  136. package/src/components/list-page/rail-row.svelte +77 -73
  137. package/src/components/list-page/section-host.ts +2 -3
  138. package/src/components/locale-switch/locale-switch.svelte +41 -41
  139. package/src/components/mark/mark.svelte +34 -38
  140. package/src/components/open-in/index.ts +1 -5
  141. package/src/components/open-in/open-in.svelte +40 -40
  142. package/src/components/picker/mark-picker.svelte +49 -49
  143. package/src/components/picker/picker.svelte +5 -3
  144. package/src/components/section-header/section-header.test.ts +7 -1
  145. package/src/components/section-header/section-label.svelte +20 -20
  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/index.ts +1 -1
  158. package/src/components/table-view/table-view.svelte +352 -13
  159. package/src/components/table-view/table-view.test.ts +108 -1
  160. package/src/components/table-view/types.ts +22 -11
  161. package/src/components/tags-input/index.ts +13 -0
  162. package/src/components/tags-input/tags-input.svelte +305 -0
  163. package/src/components/tags-input/tags.test.ts +64 -0
  164. package/src/components/tags-input/tags.ts +142 -0
  165. package/src/components/theme/index.ts +2 -4
  166. package/src/components/theme/theme-provider.svelte +1 -3
  167. package/src/components/toolbar/toolbar-search.svelte +25 -25
  168. package/src/components/toolbar/toolbar.svelte +11 -7
  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/icons/trades.ts +1 -1
  191. package/src/inbox/README.md +1 -3
  192. package/src/inbox/inbox.test.ts +181 -138
  193. package/src/inbox/index.ts +20 -20
  194. package/src/inbox/logic.ts +114 -111
  195. package/src/inbox/thread-inbox.svelte +227 -151
  196. package/src/inbox/types.ts +130 -108
  197. package/src/lib/theme-transition.ts +2 -4
  198. package/src/lib/tone.ts +2 -4
  199. package/src/lib/utils.ts +2 -2
  200. package/src/modules.test.ts +79 -69
  201. package/src/onboarding/define-tours.test.ts +55 -40
  202. package/src/onboarding/define-tours.ts +23 -23
  203. package/src/onboarding/demo.svelte +24 -5
  204. package/src/onboarding/index.ts +1 -8
  205. package/src/onboarding/onboarding-provider.svelte +19 -7
  206. package/src/onboarding/onboarding.svelte.ts +16 -16
  207. package/src/onboarding/pick.test.ts +61 -52
  208. package/src/onboarding/pick.ts +16 -13
  209. package/src/onboarding/stores.test.ts +80 -80
  210. package/src/onboarding/stores.ts +41 -39
  211. package/src/onboarding/tour-card.svelte +30 -5
  212. package/src/onboarding/tour-runner.svelte +12 -3
  213. package/src/onboarding/types.ts +20 -12
  214. package/src/onboarding/wait.ts +22 -22
  215. package/src/panel/side-panel.svelte +94 -93
  216. package/src/record/demo-data.ts +291 -225
  217. package/src/record/field-section.svelte +38 -34
  218. package/src/record/fields.ts +148 -96
  219. package/src/record/format.ts +63 -56
  220. package/src/record/index.ts +49 -35
  221. package/src/record/info-list.svelte +145 -114
  222. package/src/record/kinds.ts +31 -31
  223. package/src/record/logic.ts +216 -214
  224. package/src/record/next-up.svelte +45 -34
  225. package/src/record/project-list.svelte +56 -50
  226. package/src/record/record-demo.svelte +130 -110
  227. package/src/record/record-header.svelte +37 -28
  228. package/src/record/record-shell.svelte +54 -54
  229. package/src/record/record-stats.svelte +25 -25
  230. package/src/record/record-timeline.svelte +134 -114
  231. package/src/record/record.test.ts +430 -309
  232. package/src/record/steps-rail.svelte +225 -216
  233. package/src/record/types.ts +234 -215
  234. package/src/record/where-we-are.svelte +62 -58
  235. package/src/shadcn/button/button.svelte +1 -1
  236. package/src/shadcn/card/card-title.svelte +1 -5
  237. package/src/shadcn/hooks/use-highlighted-code.svelte.ts +2 -13
  238. package/src/shadcn/sheet/sheet-content.svelte +2 -4
  239. package/src/shadcn/table/table-cell.svelte +2 -3
  240. package/src/shadcn/utilities.css +3 -8
  241. package/src/slides/Controls.svelte +150 -124
  242. package/src/slides/Deck.svelte +243 -238
  243. package/src/slides/Fragment.svelte +64 -57
  244. package/src/slides/Overview.svelte +106 -101
  245. package/src/slides/Presenter.svelte +300 -266
  246. package/src/slides/Slide.svelte +185 -170
  247. package/src/slides/SlideList.svelte +16 -12
  248. package/src/slides/ambient.d.ts +1 -1
  249. package/src/slides/bridges.ts +42 -23
  250. package/src/slides/deck-controller.svelte.ts +47 -35
  251. package/src/slides/demo-slides.svelte +330 -306
  252. package/src/slides/demo.svelte +164 -160
  253. package/src/slides/export/capture.ts +20 -16
  254. package/src/slides/export/ffmpeg.ts +43 -38
  255. package/src/slides/export/index.ts +5 -5
  256. package/src/slides/export/launch.ts +3 -1
  257. package/src/slides/export/pdf.ts +30 -27
  258. package/src/slides/export/types.ts +10 -8
  259. package/src/slides/index.ts +11 -11
  260. package/src/slides/labels.ts +22 -19
  261. package/src/slides/layout.ts +9 -2
  262. package/src/slides/model.ts +4 -4
  263. package/src/slides/navigation.ts +43 -31
  264. package/src/slides/theme.css +5 -7
  265. package/src/slides/types.ts +9 -10
  266. package/src/status/define-statuses.ts +17 -24
  267. package/src/status/index.ts +5 -5
  268. package/src/status/status-badge.svelte +3 -1
  269. package/src/status/status-dot.svelte +1 -3
  270. package/src/status/status-icon.svelte +1 -1
  271. package/src/status/status-types.ts +21 -21
  272. package/src/tasks/board-order.ts +2 -4
  273. package/src/tasks/demo.svelte +189 -31
  274. package/src/tasks/detail/detail-demo.svelte +73 -13
  275. package/src/tasks/detail/detail-types.ts +45 -45
  276. package/src/tasks/detail/task-detail.svelte +40 -10
  277. package/src/tasks/detail/task-property.svelte +8 -2
  278. package/src/tasks/detail/task-thread.svelte +53 -12
  279. package/src/tasks/gantt/gantt-bar.svelte +17 -6
  280. package/src/tasks/gantt/gantt-grid.svelte +40 -9
  281. package/src/tasks/gantt/gantt-group-row.svelte +8 -2
  282. package/src/tasks/gantt/gantt-header.svelte +9 -3
  283. package/src/tasks/gantt/gantt-task-row.svelte +26 -6
  284. package/src/tasks/gantt/gantt.css +2 -1
  285. package/src/tasks/gantt/gantt.test.ts +3 -1
  286. package/src/tasks/gantt/logic.test.ts +110 -20
  287. package/src/tasks/gantt/logic.ts +42 -6
  288. package/src/tasks/inbox/inbox-card.svelte +13 -5
  289. package/src/tasks/inbox/inbox-draft.svelte +1 -1
  290. package/src/tasks/inbox/inbox-logic.ts +6 -40
  291. package/src/tasks/inbox/inbox-statuses.ts +23 -23
  292. package/src/tasks/inbox/inbox-trace.svelte +9 -3
  293. package/src/tasks/inbox/inbox-types.ts +54 -58
  294. package/src/tasks/inbox/index.ts +22 -21
  295. package/src/tasks/index.ts +49 -52
  296. package/src/tasks/kanban-board.svelte +16 -12
  297. package/src/tasks/kanban-types.ts +2 -3
  298. package/src/tasks/presets/sales.ts +6 -6
  299. package/src/tasks/task-gantt.svelte +28 -8
  300. package/src/tasks/task-list.svelte +41 -17
  301. package/src/tasks/time/duration.ts +40 -40
  302. package/src/tasks/time/time-chip.svelte +9 -2
  303. package/src/tasks/time/time-dot.svelte +1 -1
  304. package/src/tasks/time/time-ring.svelte +11 -3
  305. package/src/tasks/time/time-tracker.svelte +57 -15
  306. package/src/tasks/time/time-types.ts +57 -57
  307. package/src/tasks/use-kanban-dnd.svelte.ts +2 -3
  308. package/src/thread/index.ts +7 -7
  309. package/src/thread/logic.ts +36 -33
  310. package/src/thread/thread.svelte +82 -16
  311. package/src/thread/thread.test.ts +58 -47
  312. package/src/thread/types.ts +68 -68
  313. package/src/tokens/craft.css +17 -53
  314. package/src/tokens/index.css +3 -13
  315. package/src/tokens/palette.css +13 -11
  316. package/src/tokens/primitives.css +10 -23
  317. package/src/tasks/inbox/inbox.svelte +0 -203
@@ -1,145 +1,145 @@
1
1
  <script lang="ts">
2
- // =========================
3
- // IMPORTS
4
- // =========================
5
- import "./pen.css";
6
- import PreviewSvg from "../components/shared/svg/PreviewSvg.svelte";
7
- import type { ErasableNode } from "./eraser.js";
8
- import type { PenOp } from "./ops.js";
9
- import { createPenSession, type PenPreview } from "./pen-session.js";
10
- import type { PenColor, PenTool } from "./types.js";
2
+ // =========================
3
+ // IMPORTS
4
+ // =========================
5
+ import "./pen.css";
6
+ import PreviewSvg from "../components/shared/svg/PreviewSvg.svelte";
7
+ import type { ErasableNode } from "./eraser.js";
8
+ import type { PenOp } from "./ops.js";
9
+ import { createPenSession, type PenPreview } from "./pen-session.js";
10
+ import type { PenColor, PenTool } from "./types.js";
11
11
 
12
- // =========================
13
- // TYPES
14
- // =========================
15
- interface Props {
16
- tool: PenTool;
17
- color?: PenColor;
18
- /** Brush width in flow units. Unset takes the tool's default. */
19
- size?: number;
20
- /** The xyflow viewport, so screen pixels map to flow units. */
21
- viewport: { x: number; y: number; zoom: number };
22
- nodes: readonly ErasableNode[];
23
- commit: (ops: PenOp[]) => void;
24
- startGesture: () => () => void;
25
- onTextPlaced?: (nodeId: string) => void;
26
- }
12
+ // =========================
13
+ // TYPES
14
+ // =========================
15
+ interface Props {
16
+ tool: PenTool;
17
+ color?: PenColor;
18
+ /** Brush width in flow units. Unset takes the tool's default. */
19
+ size?: number;
20
+ /** The xyflow viewport, so screen pixels map to flow units. */
21
+ viewport: { x: number; y: number; zoom: number };
22
+ nodes: readonly ErasableNode[];
23
+ commit: (ops: PenOp[]) => void;
24
+ startGesture: () => () => void;
25
+ onTextPlaced?: (nodeId: string) => void;
26
+ }
27
27
 
28
- // =========================
29
- // PROPS
30
- // =========================
31
- let {
32
- tool,
33
- color = "ink",
34
- size,
35
- viewport,
36
- nodes,
37
- commit,
38
- startGesture,
39
- onTextPlaced,
40
- }: Props = $props();
28
+ // =========================
29
+ // PROPS
30
+ // =========================
31
+ let {
32
+ tool,
33
+ color = "ink",
34
+ size,
35
+ viewport,
36
+ nodes,
37
+ commit,
38
+ startGesture,
39
+ onTextPlaced,
40
+ }: Props = $props();
41
41
 
42
- // =========================
43
- // STATE
44
- // =========================
45
- let layer = $state<HTMLDivElement | null>(null);
46
- let preview = $state<PenPreview | null>(null);
47
- const session = createPenSession(
48
- {
49
- getNodes: () => nodes,
50
- commit: (ops) => commit(ops),
51
- startGesture: () => startGesture(),
52
- onTextPlaced: (id) => onTextPlaced?.(id),
53
- onPreview: (next) => {
54
- preview = next;
42
+ // =========================
43
+ // STATE
44
+ // =========================
45
+ let layer = $state<HTMLDivElement | null>(null);
46
+ let preview = $state<PenPreview | null>(null);
47
+ const session = createPenSession(
48
+ {
49
+ getNodes: () => nodes,
50
+ commit: (ops) => commit(ops),
51
+ startGesture: () => startGesture(),
52
+ onTextPlaced: (id) => onTextPlaced?.(id),
53
+ onPreview: (next) => {
54
+ preview = next;
55
+ },
55
56
  },
56
- },
57
- () => ({ tool, color, size, zoom: viewport.zoom }),
58
- );
57
+ () => ({ tool, color, size, zoom: viewport.zoom }),
58
+ );
59
59
 
60
- // =========================
61
- // EFFECTS
62
- // =========================
63
- // Switching tool mid-stroke unmounts the overlay, and an open gesture would block undo for good.
64
- $effect(() => () => session.cancel());
60
+ // =========================
61
+ // EFFECTS
62
+ // =========================
63
+ // Switching tool mid-stroke unmounts the overlay, and an open gesture would block undo for good.
64
+ $effect(() => () => session.cancel());
65
65
 
66
- // =========================
67
- // FUNCTIONS
68
- // =========================
69
- function toFlow(event: { clientX: number; clientY: number }) {
70
- const rect = layer?.getBoundingClientRect();
71
- const left = rect?.left ?? 0;
72
- const top = rect?.top ?? 0;
73
- return {
74
- x: (event.clientX - left - viewport.x) / viewport.zoom,
75
- y: (event.clientY - top - viewport.y) / viewport.zoom,
76
- };
77
- }
66
+ // =========================
67
+ // FUNCTIONS
68
+ // =========================
69
+ function toFlow(event: { clientX: number; clientY: number }) {
70
+ const rect = layer?.getBoundingClientRect();
71
+ const left = rect?.left ?? 0;
72
+ const top = rect?.top ?? 0;
73
+ return {
74
+ x: (event.clientX - left - viewport.x) / viewport.zoom,
75
+ y: (event.clientY - top - viewport.y) / viewport.zoom,
76
+ };
77
+ }
78
78
 
79
- function pressureOf(event: PointerEvent): number | undefined {
80
- return event.pointerType === "pen" ? event.pressure : undefined;
81
- }
79
+ function pressureOf(event: PointerEvent): number | undefined {
80
+ return event.pointerType === "pen" ? event.pressure : undefined;
81
+ }
82
82
 
83
- function onpointerdown(event: PointerEvent) {
84
- // A second finger or a right click mustn't start a stroke.
85
- if (event.button !== 0 || !event.isPrimary) return;
86
- layer?.setPointerCapture(event.pointerId);
87
- session.down(toFlow(event), pressureOf(event));
88
- }
83
+ function onpointerdown(event: PointerEvent) {
84
+ // A second finger or a right click mustn't start a stroke.
85
+ if (event.button !== 0 || !event.isPrimary) return;
86
+ layer?.setPointerCapture(event.pointerId);
87
+ session.down(toFlow(event), pressureOf(event));
88
+ }
89
89
 
90
- function onpointermove(event: PointerEvent) {
91
- if (!session.active) return;
92
- // Coalesced events keep a fast pen stroke smooth between animation frames.
93
- const batch = event.getCoalescedEvents?.() ?? [];
94
- for (const sample of batch.length > 0 ? batch : [event]) {
95
- session.move(toFlow(sample), pressureOf(sample));
90
+ function onpointermove(event: PointerEvent) {
91
+ if (!session.active) return;
92
+ // Coalesced events keep a fast pen stroke smooth between animation frames.
93
+ const batch = event.getCoalescedEvents?.() ?? [];
94
+ for (const sample of batch.length > 0 ? batch : [event]) {
95
+ session.move(toFlow(sample), pressureOf(sample));
96
+ }
96
97
  }
97
- }
98
98
 
99
- function onpointerup(event: PointerEvent) {
100
- if (!session.active) return;
101
- layer?.releasePointerCapture(event.pointerId);
102
- session.up(toFlow(event), { square: event.shiftKey });
103
- }
99
+ function onpointerup(event: PointerEvent) {
100
+ if (!session.active) return;
101
+ layer?.releasePointerCapture(event.pointerId);
102
+ session.up(toFlow(event), { square: event.shiftKey });
103
+ }
104
104
 
105
- function onkeydown(event: KeyboardEvent) {
106
- if (event.key === "Escape") session.cancel();
107
- }
105
+ function onkeydown(event: KeyboardEvent) {
106
+ if (event.key === "Escape") session.cancel();
107
+ }
108
108
 
109
- // The overlay sits above xyflow's zoom pane, so a wheel or a trackpad pinch would
110
- // never reach it. Hand those to the pane so you can zoom and pan mid-drawing.
111
- function forwardWheel(node: HTMLElement) {
112
- const handler = (event: WheelEvent) => {
113
- const zoomPane = node.closest(".svelte-flow")?.querySelector(".svelte-flow__zoom");
114
- if (!zoomPane) return;
115
- event.preventDefault();
116
- zoomPane.dispatchEvent(
117
- new WheelEvent("wheel", {
118
- deltaX: event.deltaX,
119
- deltaY: event.deltaY,
120
- deltaZ: event.deltaZ,
121
- deltaMode: event.deltaMode,
122
- clientX: event.clientX,
123
- clientY: event.clientY,
124
- screenX: event.screenX,
125
- screenY: event.screenY,
126
- ctrlKey: event.ctrlKey,
127
- shiftKey: event.shiftKey,
128
- altKey: event.altKey,
129
- metaKey: event.metaKey,
130
- bubbles: true,
131
- cancelable: true,
132
- }),
133
- );
134
- };
135
- node.addEventListener("wheel", handler, { passive: false });
136
- return () => node.removeEventListener("wheel", handler);
137
- }
109
+ // The overlay sits above xyflow's zoom pane, so a wheel or a trackpad pinch would
110
+ // never reach it. Hand those to the pane so you can zoom and pan mid-drawing.
111
+ function forwardWheel(node: HTMLElement) {
112
+ const handler = (event: WheelEvent) => {
113
+ const zoomPane = node.closest(".svelte-flow")?.querySelector(".svelte-flow__zoom");
114
+ if (!zoomPane) return;
115
+ event.preventDefault();
116
+ zoomPane.dispatchEvent(
117
+ new WheelEvent("wheel", {
118
+ deltaX: event.deltaX,
119
+ deltaY: event.deltaY,
120
+ deltaZ: event.deltaZ,
121
+ deltaMode: event.deltaMode,
122
+ clientX: event.clientX,
123
+ clientY: event.clientY,
124
+ screenX: event.screenX,
125
+ screenY: event.screenY,
126
+ ctrlKey: event.ctrlKey,
127
+ shiftKey: event.shiftKey,
128
+ altKey: event.altKey,
129
+ metaKey: event.metaKey,
130
+ bubbles: true,
131
+ cancelable: true,
132
+ }),
133
+ );
134
+ };
135
+ node.addEventListener("wheel", handler, { passive: false });
136
+ return () => node.removeEventListener("wheel", handler);
137
+ }
138
138
 
139
- function cursorFor(current: PenTool): string {
140
- if (current === "text") return "text";
141
- return "crosshair";
142
- }
139
+ function cursorFor(current: PenTool): string {
140
+ if (current === "text") return "text";
141
+ return "crosshair";
142
+ }
143
143
  </script>
144
144
 
145
145
  <!-- =========================== -->
@@ -1,38 +1,36 @@
1
1
  <script lang="ts">
2
- // =========================
3
- // IMPORTS
4
- // =========================
5
- import { NodeResizer, type NodeProps } from "@xyflow/svelte";
6
- import ShapeOutline from "../components/shared/svg/ShapeOutline.svelte";
7
- import PenField from "./PenField.svelte";
8
- import { getDiagramEditorContext } from "../editor/editor-context.js";
9
- import { useHandDrawn } from "../editor/hand-context.js";
10
- import { isLocked } from "../editor/lock.js";
11
- import "./pen.css";
12
- import { penColorVar } from "./colors.js";
13
- import { shapeOutline } from "./shapes.js";
14
- import type { ShapeData } from "./types.js";
2
+ // =========================
3
+ // IMPORTS
4
+ // =========================
5
+ import { NodeResizer, type NodeProps } from "@xyflow/svelte";
6
+ import ShapeOutline from "../components/shared/svg/ShapeOutline.svelte";
7
+ import PenField from "./PenField.svelte";
8
+ import { getDiagramEditorContext } from "../editor/editor-context.js";
9
+ import { useHandDrawn } from "../editor/hand-context.js";
10
+ import { isLocked } from "../editor/lock.js";
11
+ import "./pen.css";
12
+ import { penColorVar } from "./colors.js";
13
+ import { shapeOutline } from "./shapes.js";
14
+ import type { ShapeData } from "./types.js";
15
15
 
16
- // =========================
17
- // PROPS
18
- // =========================
19
- let { id, data, selected, width, height }: NodeProps = $props();
16
+ // =========================
17
+ // PROPS
18
+ // =========================
19
+ let { id, data, selected, width, height }: NodeProps = $props();
20
20
 
21
- // =========================
22
- // STATE
23
- // =========================
24
- const editor = getDiagramEditorContext();
25
- const handDrawn = useHandDrawn();
26
- let endResize: (() => void) | null = null;
21
+ // =========================
22
+ // STATE
23
+ // =========================
24
+ const editor = getDiagramEditorContext();
25
+ const handDrawn = useHandDrawn();
26
+ let endResize: (() => void) | null = null;
27
27
 
28
- // =========================
29
- // DERIVED
30
- // =========================
31
- let shape = $derived(data as ShapeData);
32
- let outline = $derived(
33
- shapeOutline(shape.shape, width ?? 0, height ?? 0, shape.strokeWidth / 2),
34
- );
35
- let ink = $derived(penColorVar(shape.color));
28
+ // =========================
29
+ // DERIVED
30
+ // =========================
31
+ let shape = $derived(data as ShapeData);
32
+ let outline = $derived(shapeOutline(shape.shape, width ?? 0, height ?? 0, shape.strokeWidth / 2));
33
+ let ink = $derived(penColorVar(shape.color));
36
34
  </script>
37
35
 
38
36
  <!-- =========================== -->
@@ -58,7 +56,8 @@ let ink = $derived(penColorVar(shape.color));
58
56
  readonly={!editor || isLocked({ data })}
59
57
  ariaLabel="Shape label"
60
58
  color={ink}
61
- onCommit={(next, live) => editor?.patchNodeData(id, { label: next }, live ? { coalesce: true } : undefined)}
59
+ onCommit={(next, live) =>
60
+ editor?.patchNodeData(id, { label: next }, live ? { coalesce: true } : undefined)}
62
61
  onExit={() => editor?.endTextEdit()}
63
62
  />
64
63
  {/if}
@@ -1,61 +1,61 @@
1
1
  <script lang="ts">
2
- // =========================
3
- // IMPORTS
4
- // =========================
5
- import { Handle, Position, type NodeProps } from "@xyflow/svelte";
6
- import { onMount } from "svelte";
7
- import TextRule from "../components/shared/svg/TextRule.svelte";
8
- import PenField from "./PenField.svelte";
9
- import { getDiagramEditorContext } from "../editor/editor-context.js";
10
- import { useHandDrawn } from "../editor/hand-context.js";
11
- import { isLocked } from "../editor/lock.js";
12
- import { roughUnderline, seedFrom, sketchColors } from "../headless/sketch.js";
13
- import "./pen.css";
14
- import { penColorVar } from "./colors.js";
15
- import type { TextData } from "./types.js";
2
+ // =========================
3
+ // IMPORTS
4
+ // =========================
5
+ import { Handle, Position, type NodeProps } from "@xyflow/svelte";
6
+ import { onMount } from "svelte";
7
+ import TextRule from "../components/shared/svg/TextRule.svelte";
8
+ import PenField from "./PenField.svelte";
9
+ import { getDiagramEditorContext } from "../editor/editor-context.js";
10
+ import { useHandDrawn } from "../editor/hand-context.js";
11
+ import { isLocked } from "../editor/lock.js";
12
+ import { roughUnderline, seedFrom, sketchColors } from "../headless/sketch.js";
13
+ import "./pen.css";
14
+ import { penColorVar } from "./colors.js";
15
+ import type { TextData } from "./types.js";
16
16
 
17
- // =========================
18
- // PROPS
19
- // =========================
20
- let { id, data, selected }: NodeProps = $props();
17
+ // =========================
18
+ // PROPS
19
+ // =========================
20
+ let { id, data, selected }: NodeProps = $props();
21
21
 
22
- // =========================
23
- // STATE
24
- // =========================
25
- const editor = getDiagramEditorContext();
26
- const handDrawn = useHandDrawn();
27
- let field = $state<ReturnType<typeof PenField>>();
28
- let boxWidth = $state(0);
22
+ // =========================
23
+ // STATE
24
+ // =========================
25
+ const editor = getDiagramEditorContext();
26
+ const handDrawn = useHandDrawn();
27
+ let field = $state<ReturnType<typeof PenField>>();
28
+ let boxWidth = $state(0);
29
29
 
30
- // =========================
31
- // DERIVED
32
- // =========================
33
- let text = $derived(data as TextData);
34
- let ink = $derived(penColorVar(text.color));
35
- let rule = $derived(
36
- text.underline && boxWidth > 2
37
- ? roughUnderline(
38
- boxWidth,
39
- { seed: seedFrom(id) + 7, colors: { ...sketchColors("ink", false), stroke: ink } },
40
- { formal: !handDrawn() },
41
- )
42
- : [],
43
- );
30
+ // =========================
31
+ // DERIVED
32
+ // =========================
33
+ let text = $derived(data as TextData);
34
+ let ink = $derived(penColorVar(text.color));
35
+ let rule = $derived(
36
+ text.underline && boxWidth > 2
37
+ ? roughUnderline(
38
+ boxWidth,
39
+ { seed: seedFrom(id) + 7, colors: { ...sketchColors("ink", false), stroke: ink } },
40
+ { formal: !handDrawn() },
41
+ )
42
+ : [],
43
+ );
44
44
 
45
- // =========================
46
- // EFFECTS
47
- // =========================
48
- // The editor asks a freshly dropped text to open, so the first thing typed lands in it.
49
- onMount(() => editor?.registerStartEdit(id, () => void field?.edit()));
45
+ // =========================
46
+ // EFFECTS
47
+ // =========================
48
+ // The editor asks a freshly dropped text to open, so the first thing typed lands in it.
49
+ onMount(() => editor?.registerStartEdit(id, () => void field?.edit()));
50
50
 
51
- // =========================
52
- // FUNCTIONS
53
- // =========================
54
- function onExit() {
55
- editor?.endTextEdit();
56
- // A click that typed nothing leaves no empty node behind.
57
- if (editor && text.text.trim() === "") editor.removeNode(id);
58
- }
51
+ // =========================
52
+ // FUNCTIONS
53
+ // =========================
54
+ function onExit() {
55
+ editor?.endTextEdit();
56
+ // A click that typed nothing leaves no empty node behind.
57
+ if (editor && text.text.trim() === "") editor.removeNode(id);
58
+ }
59
59
  </script>
60
60
 
61
61
  <!-- =========================== -->
@@ -81,7 +81,8 @@ function onExit() {
81
81
  fontWeight={text.fontWeight === "bold" ? 700 : undefined}
82
82
  center={text.align === "center"}
83
83
  column={text.fixedWidth}
84
- onCommit={(next, live) => editor?.patchNodeData(id, { text: next }, live ? { coalesce: true } : undefined)}
84
+ onCommit={(next, live) =>
85
+ editor?.patchNodeData(id, { text: next }, live ? { coalesce: true } : undefined)}
85
86
  {onExit}
86
87
  />
87
88
  {#if rule.length > 0}
@@ -1,13 +1,13 @@
1
1
  <script lang="ts">
2
- // A host's text field for the PenField test: shows its value, records that it was opened.
3
- import type { TextFieldProps } from "../editor/text-field-context.js";
2
+ // A host's text field for the PenField test: shows its value, records that it was opened.
3
+ import type { TextFieldProps } from "../editor/text-field-context.js";
4
4
 
5
- let { value, readonly = false, class: klass = "" }: TextFieldProps = $props();
6
- let opened = $state(0);
5
+ let { value, readonly = false, class: klass = "" }: TextFieldProps = $props();
6
+ let opened = $state(0);
7
7
 
8
- export function edit() {
9
- opened++;
10
- }
8
+ export function edit() {
9
+ opened++;
10
+ }
11
11
  </script>
12
12
 
13
13
  <div data-host-field class={klass} data-readonly={readonly} data-opened={opened}>{value}</div>
@@ -1,7 +1,13 @@
1
1
  // Shape and text nodes from a drag or a click. Pure, so the CLI can reuse it.
2
2
 
3
3
  import type { Box, Pt } from "./geometry.js";
4
- import type { PenColor, PenNodeInput, ShapeData, ShapeKind, TextData } from "./types.js";
4
+ import type {
5
+ PenColor,
6
+ PenNodeInput,
7
+ ShapeData,
8
+ ShapeKind,
9
+ TextData,
10
+ } from "./types.js";
5
11
  import { PEN_DEFAULTS, SHAPE_NODE, TEXT_NODE } from "./types.js";
6
12
 
7
13
  /** A shape smaller than this on both sides was a click, so it gets the default size. */
@@ -1,25 +1,30 @@
1
1
  <script lang="ts">
2
- // =========================
3
- // IMPORTS
4
- // =========================
5
- import { GLYPHS, type GlyphName } from "./glyphs.js";
2
+ // =========================
3
+ // IMPORTS
4
+ // =========================
5
+ import { GLYPHS, type GlyphName } from "./glyphs.js";
6
6
 
7
- // =========================
8
- // TYPES
9
- // =========================
10
- interface Props {
11
- name: GlyphName;
12
- size?: number;
13
- class?: string;
14
- }
7
+ // =========================
8
+ // TYPES
9
+ // =========================
10
+ interface Props {
11
+ name: GlyphName;
12
+ size?: number;
13
+ class?: string;
14
+ }
15
15
 
16
- // =========================
17
- // PROPS
18
- // =========================
19
- let { name, size = 12, class: klass = "" }: Props = $props();
16
+ // =========================
17
+ // PROPS
18
+ // =========================
19
+ let { name, size = 12, class: klass = "" }: Props = $props();
20
20
  </script>
21
21
 
22
22
  <!-- =========================== -->
23
23
  <!-- MARKUP -->
24
24
  <!-- =========================== -->
25
- <span class="cv-glyph {klass}" style:--glyph={`url("${GLYPHS[name]}")`} style:--size="{size}px" aria-hidden="true"></span>
25
+ <span
26
+ class="cv-glyph {klass}"
27
+ style:--glyph={`url("${GLYPHS[name]}")`}
28
+ style:--size="{size}px"
29
+ aria-hidden="true"
30
+ ></span>