@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,90 +1,95 @@
1
1
  <script lang="ts">
2
- // =========================
3
- // IMPORTS
4
- // =========================
5
- import { useTextField, type TextFieldHandle } from "../editor/text-field-context.js";
6
- import { useHandDrawn } from "../editor/hand-context.js";
7
- import "./pen.css";
2
+ // =========================
3
+ // IMPORTS
4
+ // =========================
5
+ import { useTextField, type TextFieldHandle } from "../editor/text-field-context.js";
6
+ import { useHandDrawn } from "../editor/hand-context.js";
7
+ import "./pen.css";
8
8
 
9
- // The words of a pen text or a shape label: the host's field when it gave one (formatted text,
10
- // its own bar and undo), else a plain textarea.
9
+ // The words of a pen text or a shape label: the host's field when it gave one (formatted text,
10
+ // its own bar and undo), else a plain textarea.
11
11
 
12
- // =========================
13
- // TYPES
14
- // =========================
15
- interface Props {
16
- value: string;
17
- /** Which pen element it sits in. Sets the size and the box it keeps. */
18
- variant: "text" | "label";
19
- onCommit: (next: string, live: boolean) => void;
20
- ariaLabel: string;
21
- /** Typing needs the canvas editor; without one the words are shown, not changed. */
22
- readonly?: boolean;
23
- selected?: boolean;
24
- color?: string;
25
- fontSize?: number;
26
- fontWeight?: number;
27
- center?: boolean;
28
- column?: boolean;
29
- /** Called when the field closes. */
30
- onExit?: () => void;
31
- }
12
+ // =========================
13
+ // TYPES
14
+ // =========================
15
+ interface Props {
16
+ value: string;
17
+ /** Which pen element it sits in. Sets the size and the box it keeps. */
18
+ variant: "text" | "label";
19
+ onCommit: (next: string, live: boolean) => void;
20
+ ariaLabel: string;
21
+ /** Typing needs the canvas editor; without one the words are shown, not changed. */
22
+ readonly?: boolean;
23
+ selected?: boolean;
24
+ color?: string;
25
+ fontSize?: number;
26
+ fontWeight?: number;
27
+ center?: boolean;
28
+ column?: boolean;
29
+ /** Called when the field closes. */
30
+ onExit?: () => void;
31
+ }
32
32
 
33
- // =========================
34
- // PROPS
35
- // =========================
36
- let {
37
- value,
38
- variant,
39
- onCommit,
40
- ariaLabel,
41
- readonly = false,
42
- selected = false,
43
- color,
44
- fontSize,
45
- fontWeight,
46
- center = false,
47
- column = false,
48
- onExit,
49
- }: Props = $props();
33
+ // =========================
34
+ // PROPS
35
+ // =========================
36
+ let {
37
+ value,
38
+ variant,
39
+ onCommit,
40
+ ariaLabel,
41
+ readonly = false,
42
+ selected = false,
43
+ color,
44
+ fontSize,
45
+ fontWeight,
46
+ center = false,
47
+ column = false,
48
+ onExit,
49
+ }: Props = $props();
50
50
 
51
- // =========================
52
- // STATE
53
- // =========================
54
- const Field = useTextField();
55
- const handDrawn = useHandDrawn();
56
- let textarea = $state<HTMLTextAreaElement>();
57
- let field = $state<TextFieldHandle>();
58
- let editing = $state(false);
51
+ // =========================
52
+ // STATE
53
+ // =========================
54
+ const Field = useTextField();
55
+ const handDrawn = useHandDrawn();
56
+ let textarea = $state<HTMLTextAreaElement>();
57
+ let field = $state<TextFieldHandle>();
58
+ let editing = $state(false);
59
59
 
60
- // =========================
61
- // DERIVED
62
- // =========================
63
- let style = $derived(
64
- [color && `color: ${color}`, fontSize && `font-size: ${fontSize}px`, fontWeight && `font-weight: ${fontWeight}`, center && "text-align: center"]
65
- .filter(Boolean)
66
- .join("; "),
67
- );
60
+ // =========================
61
+ // DERIVED
62
+ // =========================
63
+ let style = $derived(
64
+ [
65
+ color && `color: ${color}`,
66
+ fontSize && `font-size: ${fontSize}px`,
67
+ fontWeight && `font-weight: ${fontWeight}`,
68
+ center && "text-align: center",
69
+ ]
70
+ .filter(Boolean)
71
+ .join("; "),
72
+ );
68
73
 
69
- // =========================
70
- // FUNCTIONS
71
- // =========================
72
- /** Open the field for typing. A host field opens where `point` landed, else at the end. */
73
- export function edit(point?: { x: number; y: number }) {
74
- if (readonly) return;
75
- if (field) return field.edit(point);
76
- textarea?.focus();
77
- }
74
+ // =========================
75
+ // FUNCTIONS
76
+ // =========================
77
+ /** Open the field for typing. A host field opens where `point` landed, else at the end. */
78
+ export function edit(point?: { x: number; y: number }) {
79
+ if (readonly) return;
80
+ if (field) return field.edit(point);
81
+ textarea?.focus();
82
+ }
78
83
 
79
- function close() {
80
- editing = false;
81
- onExit?.();
82
- }
84
+ function close() {
85
+ editing = false;
86
+ onExit?.();
87
+ }
83
88
 
84
- // A text you aren't typing in drags like any card, so a first press must not focus it.
85
- function press(event: MouseEvent) {
86
- if (variant === "text" && !editing) event.preventDefault();
87
- }
89
+ // A text you aren't typing in drags like any card, so a first press must not focus it.
90
+ function press(event: MouseEvent) {
91
+ if (variant === "text" && !editing) event.preventDefault();
92
+ }
88
93
  </script>
89
94
 
90
95
  <!-- =========================== -->
@@ -98,7 +103,9 @@ function press(event: MouseEvent) {
98
103
  {ariaLabel}
99
104
  {readonly}
100
105
  {style}
101
- class="pen-field is-{variant} is-rich {handDrawn() ? 'hand' : ''} {selected ? 'selected' : ''} {column ? 'column' : ''}"
106
+ class="pen-field is-{variant} is-rich {handDrawn() ? 'hand' : ''} {selected
107
+ ? 'selected'
108
+ : ''} {column ? 'column' : ''}"
102
109
  clickToEdit={variant === "label"}
103
110
  onExit={close}
104
111
  />
@@ -123,6 +130,5 @@ function press(event: MouseEvent) {
123
130
  // Typing must never reach the canvas shortcuts, where Delete would remove the node.
124
131
  event.stopPropagation();
125
132
  if (event.key === "Escape") event.currentTarget.blur();
126
- }}
127
- ></textarea>
133
+ }}></textarea>
128
134
  {/if}
@@ -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}