@heyramzi/ui 2.11.1 → 2.11.2

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 (266) hide show
  1. package/dist/agents/helpers.js +38 -38
  2. package/dist/agents/page-state.js +49 -50
  3. package/dist/agents/vendor/color.js +146 -127
  4. package/dist/agents/vendor/draw.js +617 -508
  5. package/dist/agents/vendor/engine.js +698 -599
  6. package/dist/agents/vendor/mesh.js +653 -683
  7. package/dist/agents/vendor/plastic.js +3913 -3112
  8. package/dist/agents/vendor/presets.js +163 -164
  9. package/dist/agents/vendor/shapes.js +21 -20
  10. package/dist/agents/vendor/ticker.js +41 -42
  11. package/dist/agents/vendor/types.js +1 -0
  12. package/dist/agents/vendor/wear.js +1421 -1448
  13. package/dist/ai/confirmation/confirmation-context.svelte.js +6 -6
  14. package/dist/ai/confirmation/index.js +3 -1
  15. package/dist/ai/index.js +33 -0
  16. package/dist/ai/prompt-input/index.js +3 -1
  17. package/dist/ai/reasoning/index.js +3 -1
  18. package/dist/ai/reasoning/reasoning-context.svelte.js +6 -6
  19. package/dist/ai/suggestion/index.js +3 -1
  20. package/dist/ai/typing-dots/index.js +3 -1
  21. package/dist/animations/text-swap.js +24 -24
  22. package/dist/brand/index.js +54 -48
  23. package/dist/canvas/card/resize.js +5 -3
  24. package/dist/canvas/edges/edge-path.js +255 -242
  25. package/dist/canvas/editor/context-menu.js +39 -36
  26. package/dist/canvas/editor/dispatch.svelte.js +154 -151
  27. package/dist/canvas/editor/editor-context.js +7 -2
  28. package/dist/canvas/editor/editor-mode.js +1 -0
  29. package/dist/canvas/editor/frame.js +73 -76
  30. package/dist/canvas/editor/hand-context.js +5 -2
  31. package/dist/canvas/editor/history-core.js +163 -191
  32. package/dist/canvas/editor/keymap.js +95 -169
  33. package/dist/canvas/editor/layout-actions.js +143 -144
  34. package/dist/canvas/editor/lock.js +52 -51
  35. package/dist/canvas/editor/rebase.js +217 -240
  36. package/dist/canvas/editor/style.js +132 -137
  37. package/dist/canvas/editor/text-field-context.js +7 -2
  38. package/dist/canvas/editor/z-order.js +65 -70
  39. package/dist/canvas/headless/alignment.js +22 -16
  40. package/dist/canvas/headless/break-apart-find.js +286 -279
  41. package/dist/canvas/headless/break-apart-pieces.js +62 -58
  42. package/dist/canvas/headless/break-apart-plan.js +299 -325
  43. package/dist/canvas/headless/break-apart-style.js +75 -70
  44. package/dist/canvas/headless/break-apart.js +188 -181
  45. package/dist/canvas/headless/canvas-cleanup.js +78 -58
  46. package/dist/canvas/headless/edge-anchors.js +115 -116
  47. package/dist/canvas/headless/edge-memo.js +24 -22
  48. package/dist/canvas/headless/edge-serialization.js +17 -27
  49. package/dist/canvas/headless/figure-geometry.js +232 -264
  50. package/dist/canvas/headless/fit-viewport.js +84 -67
  51. package/dist/canvas/headless/grid.js +88 -86
  52. package/dist/canvas/headless/icon-node.js +44 -36
  53. package/dist/canvas/headless/index.js +1 -0
  54. package/dist/canvas/headless/linkify.js +17 -19
  55. package/dist/canvas/headless/pen-marks.js +101 -95
  56. package/dist/canvas/headless/sketch.js +231 -209
  57. package/dist/canvas/headless/snap-guides.js +99 -83
  58. package/dist/canvas/headless/svg-figure.js +133 -117
  59. package/dist/canvas/headless/table-layout.js +146 -107
  60. package/dist/canvas/index.js +2 -0
  61. package/dist/canvas/lod/lod-mode.svelte.js +20 -18
  62. package/dist/canvas/lod/render-all.svelte.js +36 -39
  63. package/dist/canvas/ops/arrange.js +78 -71
  64. package/dist/canvas/ops/ops.js +260 -255
  65. package/dist/canvas/pen/colors.js +37 -35
  66. package/dist/canvas/pen/eraser.js +28 -27
  67. package/dist/canvas/pen/geometry.js +120 -130
  68. package/dist/canvas/pen/index.js +1 -0
  69. package/dist/canvas/pen/ops.js +11 -11
  70. package/dist/canvas/pen/pen-session.js +150 -159
  71. package/dist/canvas/pen/plugin.js +107 -104
  72. package/dist/canvas/pen/shapes.js +73 -68
  73. package/dist/canvas/pen/stroke-path.js +32 -30
  74. package/dist/canvas/pen/types.js +21 -21
  75. package/dist/canvas/pen/ui.js +10 -8
  76. package/dist/canvas/plugin.js +46 -46
  77. package/dist/canvas/screen/glyphs.js +12 -11
  78. package/dist/canvas/screen/labels.js +22 -22
  79. package/dist/canvas/screen/parts.js +98 -111
  80. package/dist/canvas/screen/plugin.js +47 -56
  81. package/dist/canvas/screen/types.js +30 -35
  82. package/dist/canvas/screen/ui.js +7 -7
  83. package/dist/canvas/test-utils.js +14 -14
  84. package/dist/canvas/types.js +1 -0
  85. package/dist/canvas/ui/forward-wheel.js +24 -25
  86. package/dist/canvas/ui/icons.js +64 -131
  87. package/dist/canvas/ui/place-panel.js +58 -49
  88. package/dist/canvas/ui/shortcut-tip.js +14 -12
  89. package/dist/canvas/ui/tools.js +300 -385
  90. package/dist/charts/dashboard/chart.js +4 -0
  91. package/dist/charts/dashboard/data-table.js +11 -0
  92. package/dist/charts/dashboard/windows.js +8 -3
  93. package/dist/charts/edge-fade.js +10 -5
  94. package/dist/charts/hover-band.js +16 -11
  95. package/dist/charts/index.js +0 -1
  96. package/dist/charts/motion.js +11 -7
  97. package/dist/charts/primitives/chart-utils.js +12 -12
  98. package/dist/components/copy/clipboard.js +32 -32
  99. package/dist/components/copy/copied.svelte.js +31 -23
  100. package/dist/components/data-table/data-table.js +39 -39
  101. package/dist/components/docs/index.js +4 -0
  102. package/dist/components/docs/scroll-spy.svelte.js +73 -68
  103. package/dist/components/list-page/filter-section.js +4 -1
  104. package/dist/components/list-page/index.js +2 -0
  105. package/dist/components/list-page/list-table.js +7 -2
  106. package/dist/components/list-page/queue-rail.js +14 -0
  107. package/dist/components/list-page/section-host.js +4 -2
  108. package/dist/components/list-page/step.js +4 -3
  109. package/dist/components/list-page/types.js +1 -0
  110. package/dist/components/picker/trigger.js +48 -30
  111. package/dist/components/picker/types.js +1 -0
  112. package/dist/components/segmented/collapse.js +38 -27
  113. package/dist/components/segmented/index.js +8 -0
  114. package/dist/components/segmented/segmented.js +67 -42
  115. package/dist/components/star-rating-input/index.js +3 -1
  116. package/dist/components/table-view/types.js +1 -0
  117. package/dist/components/tags-input/tags.js +81 -69
  118. package/dist/components/theme/index.js +4 -0
  119. package/dist/components/toolbar/index.js +4 -0
  120. package/dist/components/toolbar/toolbar-trigger.js +14 -1
  121. package/dist/editor/anchors.js +17 -13
  122. package/dist/editor/block-id.js +52 -51
  123. package/dist/editor/block-ops.js +108 -80
  124. package/dist/editor/doc-blocks.js +155 -166
  125. package/dist/editor/extensions.js +139 -180
  126. package/dist/editor/index.js +4 -0
  127. package/dist/editor/markdown-test-helpers.js +4 -2
  128. package/dist/editor/markdown.js +420 -408
  129. package/dist/editor/serializer.js +204 -200
  130. package/dist/editor/slash-items.js +89 -101
  131. package/dist/editor/youtube-url.js +25 -20
  132. package/dist/effects/animated-beam/path-calculator.svelte.js +43 -38
  133. package/dist/effects/animated-beam/types.js +1 -0
  134. package/dist/effects/border-beam/css.js +92 -78
  135. package/dist/effects/border-beam/presets.js +1705 -1720
  136. package/dist/effects/border-beam/register-properties.js +43 -33
  137. package/dist/effects/border-beam/types.js +1 -0
  138. package/dist/effects/image-comparison/compare.js +3 -2
  139. package/dist/effects/image-reveal/types.js +1 -0
  140. package/dist/effects/magnetic/magnetic.js +58 -52
  141. package/dist/effects/reveal/observe.js +50 -40
  142. package/dist/effects/reveal/types.js +1 -0
  143. package/dist/effects/shader-gradient/color.js +36 -29
  144. package/dist/effects/shader-gradient/renderer.js +384 -295
  145. package/dist/effects/shader-gradient/shaders.js +31 -0
  146. package/dist/effects/shader-gradient/types.js +1 -0
  147. package/dist/icons/apple-symbol-exports.js +99 -124
  148. package/dist/icons/sf-symbols.js +357 -461
  149. package/dist/icons/trades.js +43 -28
  150. package/dist/inbox/index.js +1 -0
  151. package/dist/inbox/logic.js +126 -122
  152. package/dist/inbox/types.js +43 -43
  153. package/dist/lib/arrive.js +19 -12
  154. package/dist/lib/icon-types.js +1 -0
  155. package/dist/lib/initials.js +16 -10
  156. package/dist/lib/layout.js +5 -0
  157. package/dist/lib/panel.js +13 -0
  158. package/dist/lib/result.js +22 -22
  159. package/dist/lib/sliding-pill.js +59 -44
  160. package/dist/lib/theme-transition.js +43 -43
  161. package/dist/lib/tone.js +31 -24
  162. package/dist/lib/utils.js +3 -1
  163. package/dist/onboarding/define-tours.js +24 -23
  164. package/dist/onboarding/onboarding.svelte.js +16 -12
  165. package/dist/onboarding/pick.js +10 -6
  166. package/dist/onboarding/stores.js +40 -41
  167. package/dist/onboarding/wait.js +24 -23
  168. package/dist/panel/side-panel-types.js +1 -0
  169. package/dist/record/demo-data.js +459 -486
  170. package/dist/record/fields.js +129 -131
  171. package/dist/record/format.js +70 -82
  172. package/dist/record/gutter.js +1 -0
  173. package/dist/record/kinds.js +95 -105
  174. package/dist/record/logic.js +238 -219
  175. package/dist/record/types.js +49 -48
  176. package/dist/shadcn/accordion/index.js +3 -1
  177. package/dist/shadcn/alert/index.js +3 -1
  178. package/dist/shadcn/alert-dialog/index.js +3 -1
  179. package/dist/shadcn/attachment/index.js +3 -1
  180. package/dist/shadcn/avatar/index.js +3 -1
  181. package/dist/shadcn/breadcrumb/index.js +3 -1
  182. package/dist/shadcn/bubble/index.js +3 -1
  183. package/dist/shadcn/button/index.js +3 -1
  184. package/dist/shadcn/calendar/index.js +3 -1
  185. package/dist/shadcn/card/index.js +3 -1
  186. package/dist/shadcn/checkbox/index.js +3 -1
  187. package/dist/shadcn/code-block/index.js +3 -1
  188. package/dist/shadcn/collapsible/index.js +3 -1
  189. package/dist/shadcn/command/index.js +4 -1
  190. package/dist/shadcn/context-menu/index.js +3 -1
  191. package/dist/shadcn/dialog/index.js +3 -1
  192. package/dist/shadcn/dropdown-menu/menu-item.js +40 -20
  193. package/dist/shadcn/empty/index.js +3 -1
  194. package/dist/shadcn/field/index.js +3 -1
  195. package/dist/shadcn/hooks/highlighter.js +77 -75
  196. package/dist/shadcn/hooks/is-mobile.svelte.js +3 -3
  197. package/dist/shadcn/hooks/use-highlighted-code.svelte.js +62 -62
  198. package/dist/shadcn/input/index.js +3 -1
  199. package/dist/shadcn/kbd/index.js +3 -1
  200. package/dist/shadcn/label/index.js +3 -1
  201. package/dist/shadcn/marker/index.js +3 -1
  202. package/dist/shadcn/message/index.js +3 -1
  203. package/dist/shadcn/message-scroller/index.js +3 -1
  204. package/dist/shadcn/popover/index.js +3 -1
  205. package/dist/shadcn/progress/index.js +3 -1
  206. package/dist/shadcn/progress/tone.js +8 -7
  207. package/dist/shadcn/radio-group/index.js +3 -1
  208. package/dist/shadcn/scroll-area/index.js +3 -1
  209. package/dist/shadcn/select/index.js +3 -1
  210. package/dist/shadcn/separator/index.js +3 -1
  211. package/dist/shadcn/sheet/index.js +3 -1
  212. package/dist/shadcn/sidebar/context.svelte.js +42 -28
  213. package/dist/shadcn/sidebar/index.js +3 -1
  214. package/dist/shadcn/skeleton/index.js +3 -1
  215. package/dist/shadcn/slider/index.js +3 -1
  216. package/dist/shadcn/switch/index.js +3 -1
  217. package/dist/shadcn/table/index.js +3 -1
  218. package/dist/shadcn/tabs/index.js +3 -1
  219. package/dist/shadcn/textarea/index.js +3 -1
  220. package/dist/shadcn/toggle/index.js +3 -1
  221. package/dist/shadcn/toggle-group/index.js +3 -1
  222. package/dist/shadcn/tooltip/context.js +7 -2
  223. package/dist/shadcn/tooltip/index.js +3 -1
  224. package/dist/shadcn/trace/index.js +3 -1
  225. package/dist/slides/bridges.js +65 -59
  226. package/dist/slides/deck-controller.svelte.js +210 -207
  227. package/dist/slides/export/capture.js +66 -71
  228. package/dist/slides/export/ffmpeg.js +76 -72
  229. package/dist/slides/export/index.js +3 -0
  230. package/dist/slides/export/launch.js +9 -6
  231. package/dist/slides/export/pdf.js +60 -54
  232. package/dist/slides/export/types.js +1 -0
  233. package/dist/slides/labels.js +24 -24
  234. package/dist/slides/layout.js +39 -41
  235. package/dist/slides/model.js +9 -7
  236. package/dist/slides/navigation.js +78 -73
  237. package/dist/slides/types.js +2 -2
  238. package/dist/status/define-statuses.js +18 -14
  239. package/dist/status/status-types.js +1 -0
  240. package/dist/test-setup.js +24 -20
  241. package/dist/thread/logic.js +73 -77
  242. package/dist/thread/types.js +21 -21
  243. package/dist/work/board-order.js +94 -90
  244. package/dist/work/dates.js +16 -9
  245. package/dist/work/detail/detail-types.js +18 -18
  246. package/dist/work/docs/edited.js +32 -29
  247. package/dist/work/docs/index.js +4 -0
  248. package/dist/work/docs/labels.js +34 -34
  249. package/dist/work/docs/menu.js +113 -106
  250. package/dist/work/docs/outline.js +39 -29
  251. package/dist/work/gantt/logic.js +211 -235
  252. package/dist/work/inbox/inbox-logic.js +71 -75
  253. package/dist/work/inbox/inbox-statuses.js +75 -72
  254. package/dist/work/inbox/inbox-types.js +1 -0
  255. package/dist/work/kanban-drag.js +27 -22
  256. package/dist/work/kanban-types.js +1 -0
  257. package/dist/work/presets/sales.js +36 -32
  258. package/dist/work/reorder.js +9 -8
  259. package/dist/work/task-types.js +1 -0
  260. package/dist/work/time/duration.js +51 -46
  261. package/dist/work/time/time-types.js +23 -23
  262. package/dist/work/use-kanban-dnd.svelte.js +126 -125
  263. package/package.json +3 -1
  264. package/src/components/list-page/master-detail.svelte +3 -1
  265. package/src/components/list-page/rail-row.svelte +4 -4
  266. package/src/editor/markdown.ts +30 -35
@@ -1,82 +1,99 @@
1
+ /** What a node occupies when the saved data does not say. */
1
2
  const FALLBACK_NODE = {
2
- width: 240,
3
- height: 80
3
+ width: 240,
4
+ height: 80
4
5
  };
5
6
  function num(value) {
6
- return typeof value === "number" && Number.isFinite(value) ? value : null;
7
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
7
8
  }
8
9
  function cssDim(css, prop) {
9
- const found = css.match(new RegExp(`(?:^|[;\\s])${prop}:\\s*(\\d+(?:\\.\\d+)?)px`))?.[1];
10
- return found ? Number(found) : null;
10
+ const found = css.match(new RegExp(`(?:^|[;\\s])${prop}:\\s*(\\d+(?:\\.\\d+)?)px`))?.[1];
11
+ return found ? Number(found) : null;
11
12
  }
13
+ /** A stored node often has a width and no height (the DOM measures it), so each side is read alone. */
12
14
  function styleDim(style, prop) {
13
- if (typeof style === "string") return cssDim(style, prop);
14
- if (!style || typeof style !== "object") return null;
15
- return num(style[prop]);
15
+ if (typeof style === "string") return cssDim(style, prop);
16
+ if (!style || typeof style !== "object") return null;
17
+ return num(style[prop]);
16
18
  }
17
19
  function readSize(node) {
18
- return {
19
- width: num(node.width) ?? styleDim(node.style, "width") ?? FALLBACK_NODE.width,
20
- height: num(node.height) ?? styleDim(node.style, "height") ?? FALLBACK_NODE.height
21
- };
20
+ return {
21
+ width: num(node.width) ?? styleDim(node.style, "width") ?? FALLBACK_NODE.width,
22
+ height: num(node.height) ?? styleDim(node.style, "height") ?? FALLBACK_NODE.height
23
+ };
22
24
  }
25
+ /**
26
+ * The box every top-level node sits in. Children are parent-relative, so
27
+ * counting them would push the bounds out and shrink the drawing.
28
+ */
23
29
  export function diagramBounds(nodes) {
24
- const boxes = nodes.filter((node)=>!node.parentId).map((node)=>({
25
- ...readSize(node),
26
- x: node.position.x,
27
- y: node.position.y
28
- }));
29
- if (boxes.length === 0) return null;
30
- const minX = Math.min(...boxes.map((box)=>box.x));
31
- const minY = Math.min(...boxes.map((box)=>box.y));
32
- const maxX = Math.max(...boxes.map((box)=>box.x + box.width));
33
- const maxY = Math.max(...boxes.map((box)=>box.y + box.height));
34
- return {
35
- x: minX,
36
- y: minY,
37
- width: maxX - minX,
38
- height: maxY - minY
39
- };
30
+ const boxes = nodes.filter((node) => !node.parentId).map((node) => ({
31
+ ...readSize(node),
32
+ x: node.position.x,
33
+ y: node.position.y
34
+ }));
35
+ if (boxes.length === 0) return null;
36
+ const minX = Math.min(...boxes.map((box) => box.x));
37
+ const minY = Math.min(...boxes.map((box) => box.y));
38
+ const maxX = Math.max(...boxes.map((box) => box.x + box.width));
39
+ const maxY = Math.max(...boxes.map((box) => box.y + box.height));
40
+ return {
41
+ x: minX,
42
+ y: minY,
43
+ width: maxX - minX,
44
+ height: maxY - minY
45
+ };
40
46
  }
41
- export function fitViewport(nodes, container, padding = 0.04) {
42
- const bounds = diagramBounds(nodes);
43
- if (!bounds || bounds.width <= 0 || bounds.height <= 0) return null;
44
- if (container.width <= 0 || container.height <= 0) return null;
45
- const usableWidth = container.width * (1 - padding * 2);
46
- const usableHeight = container.height * (1 - padding * 2);
47
- const zoom = Math.min(usableWidth / bounds.width, usableHeight / bounds.height);
48
- return {
49
- x: (container.width - bounds.width * zoom) / 2 - bounds.x * zoom,
50
- y: (container.height - bounds.height * zoom) / 2 - bounds.y * zoom,
51
- zoom
52
- };
47
+ /**
48
+ * The viewport that centres `nodes` inside a `container` of the given size.
49
+ * `padding` is a fraction of the container, matching fitView's own meaning.
50
+ */
51
+ export function fitViewport(nodes, container, padding = .04) {
52
+ const bounds = diagramBounds(nodes);
53
+ if (!bounds || bounds.width <= 0 || bounds.height <= 0) return null;
54
+ if (container.width <= 0 || container.height <= 0) return null;
55
+ const usableWidth = container.width * (1 - padding * 2);
56
+ const usableHeight = container.height * (1 - padding * 2);
57
+ const zoom = Math.min(usableWidth / bounds.width, usableHeight / bounds.height);
58
+ return {
59
+ x: (container.width - bounds.width * zoom) / 2 - bounds.x * zoom,
60
+ y: (container.height - bounds.height * zoom) / 2 - bounds.y * zoom,
61
+ zoom
62
+ };
53
63
  }
54
- const MIN_READABLE_ZOOM = 0.3;
55
- export function openingViewport(nodes, container, padding = 0.04, gutter = 0) {
56
- const whole = fitViewport(nodes, container, padding);
57
- if (!whole || whole.zoom >= MIN_READABLE_ZOOM) return whole;
58
- const bounds = diagramBounds(nodes);
59
- if (!bounds) return null;
60
- const inset = container.width * padding;
61
- const left = inset + gutter;
62
- const zoom = Math.min((container.width - left - inset) / bounds.width, 1);
63
- return {
64
- x: left - bounds.x * zoom,
65
- y: inset - bounds.y * zoom,
66
- zoom
67
- };
64
+ /** Below this a whole-board fit is too small to read a card's text. */
65
+ const MIN_READABLE_ZOOM = .3;
66
+ /*
67
+ * Where a board with no view opens: a board that fits reads whole, but a tall
68
+ * wall fits at 8%, so it fits its width and starts at the top.
69
+ */
70
+ export function openingViewport(nodes, container, padding = .04, gutter = 0) {
71
+ const whole = fitViewport(nodes, container, padding);
72
+ if (!whole || whole.zoom >= MIN_READABLE_ZOOM) return whole;
73
+ const bounds = diagramBounds(nodes);
74
+ if (!bounds) return null;
75
+ const inset = container.width * padding;
76
+ const left = inset + gutter;
77
+ const zoom = Math.min((container.width - left - inset) / bounds.width, 1);
78
+ return {
79
+ x: left - bounds.x * zoom,
80
+ y: inset - bounds.y * zoom,
81
+ zoom
82
+ };
68
83
  }
69
- export function viewShowsContent(nodes, viewport, container, inset = {
70
- left: 0
71
- }) {
72
- const bounds = diagramBounds(nodes);
73
- if (!bounds || viewport.zoom <= 0) return true;
74
- const left = (inset.left - viewport.x) / viewport.zoom;
75
- const right = (container.width - viewport.x) / viewport.zoom;
76
- const top = -viewport.y / viewport.zoom;
77
- const bottom = (container.height - viewport.y) / viewport.zoom;
78
- const onScreen = bounds.x < right && bounds.x + bounds.width > left && bounds.y < bottom && bounds.y + bounds.height > top;
79
- if (!onScreen) return false;
80
- const fits = bounds.width * viewport.zoom <= container.width - inset.left;
81
- return !(fits && bounds.x < left - 4 / viewport.zoom);
84
+ /**
85
+ * Whether a saved view still shows the board: a card is on screen, and a fitting board isn't
86
+ * pushed off the left. `inset` is the tool rail's strip.
87
+ */
88
+ export function viewShowsContent(nodes, viewport, container, inset = { left: 0 }) {
89
+ const bounds = diagramBounds(nodes);
90
+ if (!bounds || viewport.zoom <= 0) return true;
91
+ const left = (inset.left - viewport.x) / viewport.zoom;
92
+ const right = (container.width - viewport.x) / viewport.zoom;
93
+ const top = -viewport.y / viewport.zoom;
94
+ const bottom = (container.height - viewport.y) / viewport.zoom;
95
+ const onScreen = bounds.x < right && bounds.x + bounds.width > left && bounds.y < bottom && bounds.y + bounds.height > top;
96
+ if (!onScreen) return false;
97
+ const fits = bounds.width * viewport.zoom <= container.width - inset.left;
98
+ return !(fits && bounds.x < left - 4 / viewport.zoom);
82
99
  }
@@ -1,96 +1,98 @@
1
+ // The magnets: the grid the editor snaps cards to. A write that isn't a drag snaps through here.
1
2
  import { STROKE_NODE } from "../pen/types.js";
3
+ /** Flow units between two dots at the finest level. */
2
4
  export const GRID_SIZE = 20;
3
- export const SNAP_GRID = [
4
- GRID_SIZE,
5
- GRID_SIZE
6
- ];
7
- export const FREEHAND_NODE_TYPES = new Set([
8
- STROKE_NODE
9
- ]);
5
+ /** The value xyflow's `snapGrid` prop takes. */
6
+ export const SNAP_GRID = [GRID_SIZE, GRID_SIZE];
7
+ /** Node types that keep their exact points. A stroke's origin is its bounding box, so moving it shifts the drawing. */
8
+ export const FREEHAND_NODE_TYPES = new Set([STROKE_NODE]);
9
+ /** The nearest dot on one axis, rounded the way xyflow rounds a drag. */
10
10
  export function snapToGrid(value) {
11
- return GRID_SIZE * Math.round(value / GRID_SIZE);
11
+ return GRID_SIZE * Math.round(value / GRID_SIZE);
12
12
  }
13
13
  function pointOf(value) {
14
- if (typeof value !== "object" || value === null) return null;
15
- const { x, y } = value;
16
- return typeof x === "number" && typeof y === "number" && Number.isFinite(x) && Number.isFinite(y) ? {
17
- x,
18
- y
19
- } : null;
14
+ if (typeof value !== "object" || value === null) return null;
15
+ const { x, y } = value;
16
+ return typeof x === "number" && typeof y === "number" && Number.isFinite(x) && Number.isFinite(y) ? {
17
+ x,
18
+ y
19
+ } : null;
20
20
  }
21
21
  function stringOf(value) {
22
- return typeof value === "string" ? value : undefined;
22
+ return typeof value === "string" ? value : undefined;
23
23
  }
24
+ /** Snaps the nodes a write created or moved and leaves the rest. A child snaps on the page, then goes back to relative. */
24
25
  export function snapWrittenNodes(before, after, exempt = FREEHAND_NODE_TYPES) {
25
- const prior = new Map();
26
- for (const node of before){
27
- const id = stringOf(node.id);
28
- if (id) prior.set(id, {
29
- at: pointOf(node.position),
30
- parent: stringOf(node.parentId)
31
- });
32
- }
33
- const byId = new Map();
34
- for (const node of after){
35
- const id = stringOf(node.id);
36
- if (id) byId.set(id, node);
37
- }
38
- const written = (node)=>{
39
- const was = prior.get(String(node.id));
40
- const at = pointOf(node.position);
41
- const type = stringOf(node.type);
42
- if (!at || type !== undefined && exempt.has(type)) return false;
43
- if (!was?.at) return true;
44
- return was.at.x !== at.x || was.at.y !== at.y || was.parent !== stringOf(node.parentId);
45
- };
46
- const settled = new Map();
47
- const visiting = new Set();
48
- const settle = (node)=>{
49
- const id = String(node.id);
50
- const done = settled.get(id);
51
- if (done) return done;
52
- const local = pointOf(node.position) ?? {
53
- x: 0,
54
- y: 0
55
- };
56
- const parentId = stringOf(node.parentId);
57
- const parent = parentId ? byId.get(parentId) : undefined;
58
- let origin = {
59
- x: 0,
60
- y: 0
61
- };
62
- if (parent && !visiting.has(id)) {
63
- visiting.add(id);
64
- origin = settle(parent).page;
65
- visiting.delete(id);
66
- }
67
- const page = {
68
- x: origin.x + local.x,
69
- y: origin.y + local.y
70
- };
71
- const result = written(node) ? {
72
- local: {
73
- x: snapToGrid(page.x) - origin.x,
74
- y: snapToGrid(page.y) - origin.y
75
- },
76
- page: {
77
- x: snapToGrid(page.x),
78
- y: snapToGrid(page.y)
79
- }
80
- } : {
81
- local,
82
- page
83
- };
84
- settled.set(id, result);
85
- return result;
86
- };
87
- return after.map((node)=>{
88
- if (!stringOf(node.id)) return node;
89
- const at = pointOf(node.position);
90
- const next = settle(node).local;
91
- return at && (next.x !== at.x || next.y !== at.y) ? {
92
- ...node,
93
- position: next
94
- } : node;
95
- });
26
+ const prior = new Map();
27
+ for (const node of before) {
28
+ const id = stringOf(node.id);
29
+ if (id) prior.set(id, {
30
+ at: pointOf(node.position),
31
+ parent: stringOf(node.parentId)
32
+ });
33
+ }
34
+ const byId = new Map();
35
+ for (const node of after) {
36
+ const id = stringOf(node.id);
37
+ if (id) byId.set(id, node);
38
+ }
39
+ const written = (node) => {
40
+ const was = prior.get(String(node.id));
41
+ const at = pointOf(node.position);
42
+ const type = stringOf(node.type);
43
+ if (!at || type !== undefined && exempt.has(type)) return false;
44
+ if (!was?.at) return true;
45
+ return was.at.x !== at.x || was.at.y !== at.y || was.parent !== stringOf(node.parentId);
46
+ };
47
+ // Parents settle first, because a child's page position rides on its parent's.
48
+ const settled = new Map();
49
+ const visiting = new Set();
50
+ const settle = (node) => {
51
+ const id = String(node.id);
52
+ const done = settled.get(id);
53
+ if (done) return done;
54
+ const local = pointOf(node.position) ?? {
55
+ x: 0,
56
+ y: 0
57
+ };
58
+ const parentId = stringOf(node.parentId);
59
+ const parent = parentId ? byId.get(parentId) : undefined;
60
+ let origin = {
61
+ x: 0,
62
+ y: 0
63
+ };
64
+ if (parent && !visiting.has(id)) {
65
+ visiting.add(id);
66
+ origin = settle(parent).page;
67
+ visiting.delete(id);
68
+ }
69
+ const page = {
70
+ x: origin.x + local.x,
71
+ y: origin.y + local.y
72
+ };
73
+ const result = written(node) ? {
74
+ local: {
75
+ x: snapToGrid(page.x) - origin.x,
76
+ y: snapToGrid(page.y) - origin.y
77
+ },
78
+ page: {
79
+ x: snapToGrid(page.x),
80
+ y: snapToGrid(page.y)
81
+ }
82
+ } : {
83
+ local,
84
+ page
85
+ };
86
+ settled.set(id, result);
87
+ return result;
88
+ };
89
+ return after.map((node) => {
90
+ if (!stringOf(node.id)) return node;
91
+ const at = pointOf(node.position);
92
+ const next = settle(node).local;
93
+ return at && (next.x !== at.x || next.y !== at.y) ? {
94
+ ...node,
95
+ position: next
96
+ } : node;
97
+ });
96
98
  }
@@ -1,45 +1,53 @@
1
1
  export const ICON_SIZE = 56;
2
2
  export const ICON_W = 120;
3
+ /** Gap between the mark and its label, either side. */
3
4
  export const ICON_LABEL_GAP = 12;
4
5
  export const ICON_LABEL_H = 22;
5
6
  export const ICON_LINE_H = 20;
7
+ /** A right-hand label is set larger: it names an area, not a tool. */
6
8
  export const ICON_SIDE_LABEL_H = 28;
9
+ /** Space between the port line and the first line under it. */
7
10
  export const ICON_PORT_GAP = 6;
11
+ /**
12
+ * A label below centres the mark and hands off at its edges. A right label starts it
13
+ * at x 0 and leaves from the box's right edge.
14
+ */
8
15
  export function iconGeometry(icon, width = ICON_W) {
9
- const size = icon.size ?? ICON_SIZE;
10
- const lines = icon.lines?.length ?? 0;
11
- const mid = size / 2;
12
- if (icon.labelSide === "right") {
13
- const top = Math.max(0, (icon.label ? ICON_SIDE_LABEL_H : 0) - mid);
14
- return {
15
- size,
16
- markX: 0,
17
- markY: top,
18
- height: Math.max(top + size, top + mid + ICON_PORT_GAP + lines * ICON_LINE_H),
19
- left: {
20
- x: 0,
21
- y: top + mid
22
- },
23
- right: {
24
- x: Math.max(width, size),
25
- y: top + mid
26
- }
27
- };
28
- }
29
- const markX = (Math.max(width, size) - size) / 2;
30
- const text = (icon.label ? ICON_LABEL_H : 0) + lines * ICON_LINE_H;
31
- return {
32
- size,
33
- markX,
34
- markY: 0,
35
- height: size + (text ? ICON_LABEL_GAP + text : 0),
36
- left: {
37
- x: markX,
38
- y: mid
39
- },
40
- right: {
41
- x: markX + size,
42
- y: mid
43
- }
44
- };
16
+ const size = icon.size ?? ICON_SIZE;
17
+ const lines = icon.lines?.length ?? 0;
18
+ const mid = size / 2;
19
+ if (icon.labelSide === "right") {
20
+ // The label sits across the port line: name above it, lines below.
21
+ const top = Math.max(0, (icon.label ? ICON_SIDE_LABEL_H : 0) - mid);
22
+ return {
23
+ size,
24
+ markX: 0,
25
+ markY: top,
26
+ height: Math.max(top + size, top + mid + ICON_PORT_GAP + lines * ICON_LINE_H),
27
+ left: {
28
+ x: 0,
29
+ y: top + mid
30
+ },
31
+ right: {
32
+ x: Math.max(width, size),
33
+ y: top + mid
34
+ }
35
+ };
36
+ }
37
+ const markX = (Math.max(width, size) - size) / 2;
38
+ const text = (icon.label ? ICON_LABEL_H : 0) + lines * ICON_LINE_H;
39
+ return {
40
+ size,
41
+ markX,
42
+ markY: 0,
43
+ height: size + (text ? ICON_LABEL_GAP + text : 0),
44
+ left: {
45
+ x: markX,
46
+ y: mid
47
+ },
48
+ right: {
49
+ x: markX + size,
50
+ y: mid
51
+ }
52
+ };
45
53
  }
@@ -1 +1,2 @@
1
+ // What a CLI or a server needs to build a board without a browser.
1
2
  export { compilePenMark, penMarkSchema, placeInFrame } from "./pen-marks.js";
@@ -1,25 +1,23 @@
1
1
  const URL_RE = /https?:\/\/[^\s<>"]+/g;
2
+ /** Sentence punctuation that follows a link without belonging to it. */
2
3
  const TRAILING = /[.,;:!?)\]'”]+$/;
4
+ /** Splits a line so each http(s) link is its own part, minus the punctuation after it. */
3
5
  export function linkify(text) {
4
- const parts = [];
5
- let last = 0;
6
- for (const match of text.matchAll(URL_RE)){
7
- const start = match.index ?? 0;
8
- const href = match[0].replace(TRAILING, "");
9
- if (start > last) parts.push({
10
- text: text.slice(last, start)
11
- });
12
- parts.push({
13
- text: href,
14
- href
15
- });
16
- last = start + href.length;
17
- }
18
- if (last < text.length) parts.push({
19
- text: text.slice(last)
20
- });
21
- return parts;
6
+ const parts = [];
7
+ let last = 0;
8
+ for (const match of text.matchAll(URL_RE)) {
9
+ const start = match.index ?? 0;
10
+ const href = match[0].replace(TRAILING, "");
11
+ if (start > last) parts.push({ text: text.slice(last, start) });
12
+ parts.push({
13
+ text: href,
14
+ href
15
+ });
16
+ last = start + href.length;
17
+ }
18
+ if (last < text.length) parts.push({ text: text.slice(last) });
19
+ return parts;
22
20
  }
23
21
  export function hasLink(text) {
24
- return /https?:\/\//.test(text);
22
+ return /https?:\/\//.test(text);
25
23
  }