@heyramzi/ui 2.11.0 → 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 (267) 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/components/tags-input/tags-input.svelte +2 -0
  267. package/src/editor/markdown.ts +30 -35
@@ -1,80 +1,85 @@
1
1
  import { PEN_DEFAULTS, SHAPE_NODE, TEXT_NODE } from "./types.js";
2
+ /** A shape smaller than this on both sides was a click, so it gets the default size. */
2
3
  const MIN_DRAG = 6;
3
4
  const DEFAULT_SHAPE = {
4
- width: 160,
5
- height: 100
5
+ width: 160,
6
+ height: 100
6
7
  };
8
+ /** The box a drag from `from` to `to` covers. Shift keeps it square. */
7
9
  export function dragBox(from, to, square = false) {
8
- let dx = to.x - from.x;
9
- let dy = to.y - from.y;
10
- if (square) {
11
- const side = Math.max(Math.abs(dx), Math.abs(dy));
12
- dx = Math.sign(dx || 1) * side;
13
- dy = Math.sign(dy || 1) * side;
14
- }
15
- return {
16
- x: Math.min(from.x, from.x + dx),
17
- y: Math.min(from.y, from.y + dy),
18
- width: Math.abs(dx),
19
- height: Math.abs(dy)
20
- };
10
+ let dx = to.x - from.x;
11
+ let dy = to.y - from.y;
12
+ if (square) {
13
+ const side = Math.max(Math.abs(dx), Math.abs(dy));
14
+ dx = Math.sign(dx || 1) * side;
15
+ dy = Math.sign(dy || 1) * side;
16
+ }
17
+ return {
18
+ x: Math.min(from.x, from.x + dx),
19
+ y: Math.min(from.y, from.y + dy),
20
+ width: Math.abs(dx),
21
+ height: Math.abs(dy)
22
+ };
21
23
  }
22
24
  export function buildShapeNode(shape, box, options = {}) {
23
- const click = box.width < MIN_DRAG && box.height < MIN_DRAG;
24
- const width = Math.round(click ? DEFAULT_SHAPE.width : Math.max(box.width, MIN_DRAG));
25
- const height = Math.round(click ? DEFAULT_SHAPE.height : Math.max(box.height, MIN_DRAG));
26
- const x = click ? box.x - width / 2 : box.x;
27
- const y = click ? box.y - height / 2 : box.y;
28
- return {
29
- id: options.id,
30
- type: SHAPE_NODE,
31
- position: {
32
- x: Math.round(x),
33
- y: Math.round(y)
34
- },
35
- data: {
36
- shape,
37
- color: options.color ?? PEN_DEFAULTS.color,
38
- strokeWidth: PEN_DEFAULTS.strokeWidth,
39
- filled: options.filled ?? false,
40
- label: options.label ?? ""
41
- },
42
- width,
43
- height
44
- };
25
+ const click = box.width < MIN_DRAG && box.height < MIN_DRAG;
26
+ const width = Math.round(click ? DEFAULT_SHAPE.width : Math.max(box.width, MIN_DRAG));
27
+ const height = Math.round(click ? DEFAULT_SHAPE.height : Math.max(box.height, MIN_DRAG));
28
+ // A click centres the default shape on the cursor.
29
+ const x = click ? box.x - width / 2 : box.x;
30
+ const y = click ? box.y - height / 2 : box.y;
31
+ return {
32
+ id: options.id,
33
+ type: SHAPE_NODE,
34
+ position: {
35
+ x: Math.round(x),
36
+ y: Math.round(y)
37
+ },
38
+ data: {
39
+ shape,
40
+ color: options.color ?? PEN_DEFAULTS.color,
41
+ strokeWidth: PEN_DEFAULTS.strokeWidth,
42
+ filled: options.filled ?? false,
43
+ label: options.label ?? ""
44
+ },
45
+ width,
46
+ height
47
+ };
45
48
  }
46
49
  export function buildTextNode(at, options = {}) {
47
- return {
48
- id: options.id,
49
- type: TEXT_NODE,
50
- position: {
51
- x: Math.round(at.x),
52
- y: Math.round(at.y)
53
- },
54
- data: {
55
- text: options.text ?? "",
56
- color: options.color ?? PEN_DEFAULTS.color,
57
- fontSize: options.fontSize ?? PEN_DEFAULTS.fontSize
58
- },
59
- width: 120,
60
- height: Math.round((options.fontSize ?? PEN_DEFAULTS.fontSize) * 1.4)
61
- };
50
+ return {
51
+ id: options.id,
52
+ type: TEXT_NODE,
53
+ position: {
54
+ x: Math.round(at.x),
55
+ y: Math.round(at.y)
56
+ },
57
+ data: {
58
+ text: options.text ?? "",
59
+ color: options.color ?? PEN_DEFAULTS.color,
60
+ fontSize: options.fontSize ?? PEN_DEFAULTS.fontSize
61
+ },
62
+ // Text sizes itself to the words once it renders, so this is only the empty box.
63
+ width: 120,
64
+ height: Math.round((options.fontSize ?? PEN_DEFAULTS.fontSize) * 1.4)
65
+ };
62
66
  }
67
+ /** The outline of a shape inside a width by height box, as an SVG path. */
63
68
  export function shapeOutline(shape, width, height, inset) {
64
- const left = inset;
65
- const top = inset;
66
- const right = Math.max(width - inset, left);
67
- const bottom = Math.max(height - inset, top);
68
- if (shape === "diamond") {
69
- const midX = (left + right) / 2;
70
- const midY = (top + bottom) / 2;
71
- return `M${midX},${top} L${right},${midY} L${midX},${bottom} L${left},${midY} Z`;
72
- }
73
- if (shape === "ellipse") {
74
- const rx = (right - left) / 2;
75
- const ry = (bottom - top) / 2;
76
- const cy = (top + bottom) / 2;
77
- return `M${left},${cy} a${rx},${ry} 0 1,0 ${rx * 2},0 a${rx},${ry} 0 1,0 ${-rx * 2},0 Z`;
78
- }
79
- return `M${left},${top} H${right} V${bottom} H${left} Z`;
69
+ const left = inset;
70
+ const top = inset;
71
+ const right = Math.max(width - inset, left);
72
+ const bottom = Math.max(height - inset, top);
73
+ if (shape === "diamond") {
74
+ const midX = (left + right) / 2;
75
+ const midY = (top + bottom) / 2;
76
+ return `M${midX},${top} L${right},${midY} L${midX},${bottom} L${left},${midY} Z`;
77
+ }
78
+ if (shape === "ellipse") {
79
+ const rx = (right - left) / 2;
80
+ const ry = (bottom - top) / 2;
81
+ const cy = (top + bottom) / 2;
82
+ return `M${left},${cy} a${rx},${ry} 0 1,0 ${rx * 2},0 a${rx},${ry} 0 1,0 ${-rx * 2},0 Z`;
83
+ }
84
+ return `M${left},${top} H${right} V${bottom} H${left} Z`;
80
85
  }
@@ -1,37 +1,39 @@
1
+ // A stroke's points become one SVG path through perfect-freehand. The editor,
2
+ // the headless export and the tests all call this, so they draw the same shape.
1
3
  import { getStroke } from "perfect-freehand";
2
4
  function average(a, b) {
3
- return (a + b) / 2;
5
+ return (a + b) / 2;
4
6
  }
7
+ /** Quadratic smoothing through the outline's midpoints, the way perfect-freehand's README does it. */
5
8
  export function outlineToPath(outline) {
6
- if (outline.length < 4) return "";
7
- const first = outline[0];
8
- const second = outline[1];
9
- const third = outline[2];
10
- let path = `M${first[0].toFixed(2)},${first[1].toFixed(2)} ` + `Q${second[0].toFixed(2)},${second[1].toFixed(2)} ` + `${average(second[0], third[0]).toFixed(2)},${average(second[1], third[1]).toFixed(2)} T`;
11
- for(let i = 2; i < outline.length - 1; i++){
12
- const a = outline[i];
13
- const b = outline[i + 1];
14
- path += `${average(a[0], b[0]).toFixed(2)},${average(a[1], b[1]).toFixed(2)} `;
15
- }
16
- return `${path}Z`;
9
+ if (outline.length < 4) return "";
10
+ const first = outline[0];
11
+ const second = outline[1];
12
+ const third = outline[2];
13
+ let path = `M${first[0].toFixed(2)},${first[1].toFixed(2)} ` + `Q${second[0].toFixed(2)},${second[1].toFixed(2)} ` + `${average(second[0], third[0]).toFixed(2)},${average(second[1], third[1]).toFixed(2)} T`;
14
+ for (let i = 2; i < outline.length - 1; i++) {
15
+ const a = outline[i];
16
+ const b = outline[i + 1];
17
+ path += `${average(a[0], b[0]).toFixed(2)},${average(a[1], b[1]).toFixed(2)} `;
18
+ }
19
+ return `${path}Z`;
17
20
  }
21
+ /** `last` is false while the pen is still down, so the live tip isn't tapered to a point. */
18
22
  export function strokePath(points, shape, last = true) {
19
- if (points.length === 0) return "";
20
- const highlighter = shape.tool === "highlighter";
21
- const outline = getStroke(points.map((point)=>point[2] === undefined ? [
22
- point[0],
23
- point[1]
24
- ] : [
25
- point[0],
26
- point[1],
27
- point[2]
28
- ]), {
29
- size: shape.size,
30
- thinning: highlighter ? 0 : 0.55,
31
- smoothing: 0.6,
32
- streamline: highlighter ? 0.7 : 0.5,
33
- simulatePressure: shape.simulatePressure && !highlighter,
34
- last
35
- });
36
- return outlineToPath(outline);
23
+ if (points.length === 0) return "";
24
+ const highlighter = shape.tool === "highlighter";
25
+ const outline = getStroke(points.map((point) => point[2] === undefined ? [point[0], point[1]] : [
26
+ point[0],
27
+ point[1],
28
+ point[2]
29
+ ]), {
30
+ size: shape.size,
31
+ // Flat ink for the highlighter: no pressure taper, a straight sweep.
32
+ thinning: highlighter ? 0 : .55,
33
+ smoothing: .6,
34
+ streamline: highlighter ? .7 : .5,
35
+ simulatePressure: shape.simulatePressure && !highlighter,
36
+ last
37
+ });
38
+ return outlineToPath(outline);
37
39
  }
@@ -1,30 +1,30 @@
1
+ // The pen layer's node shapes. A stroke, a text and a shape are ordinary xyflow
2
+ // nodes, so undo, copy and save come from the node diffing the editor already does.
1
3
  export const STROKE_NODE = "stroke";
2
4
  export const TEXT_NODE = "text";
3
5
  export const SHAPE_NODE = "shape";
6
+ /** Colours are token names, never hex, so a board drawn in light reads in dark. */
4
7
  export const PEN_COLORS = [
5
- "ink",
6
- "red",
7
- "blue",
8
- "green",
9
- "amber",
10
- "violet",
11
- "indigo",
12
- "terra"
8
+ "ink",
9
+ "red",
10
+ "blue",
11
+ "green",
12
+ "amber",
13
+ "violet",
14
+ "indigo",
15
+ "terra"
13
16
  ];
14
17
  export const SHAPE_KINDS = [
15
- "rectangle",
16
- "ellipse",
17
- "diamond"
18
+ "rectangle",
19
+ "ellipse",
20
+ "diamond"
18
21
  ];
19
22
  export const PEN_DEFAULTS = {
20
- pen: {
21
- size: 4
22
- },
23
- highlighter: {
24
- size: 20
25
- },
26
- strokeWidth: 3,
27
- fontSize: 20,
28
- color: "ink"
23
+ pen: { size: 4 },
24
+ highlighter: { size: 20 },
25
+ strokeWidth: 3,
26
+ fontSize: 20,
27
+ color: "ink"
29
28
  };
30
- export const HIGHLIGHTER_OPACITY = 0.38;
29
+ /** Highlighter ink is drawn at this opacity so what's under it still shows. */
30
+ export const HIGHLIGHTER_OPACITY = .38;
@@ -1,3 +1,5 @@
1
+ // The browser registers `penUiPlugin`, and a server registers `penPlugin`.
2
+ // Never both, because a kind has one owner.
1
3
  import { definePlugin } from "../plugin.js";
2
4
  import PenOverlay from "../../../src/canvas/pen/PenOverlay.svelte";
3
5
  import { penPlugin } from "./plugin.js";
@@ -7,14 +9,14 @@ import TextNode from "../../../src/canvas/pen/TextNode.svelte";
7
9
  import { SHAPE_NODE, STROKE_NODE, TEXT_NODE } from "./types.js";
8
10
  export { PenOverlay, ShapeNode, StrokeNode, TextNode };
9
11
  const components = {
10
- [STROKE_NODE]: StrokeNode,
11
- [TEXT_NODE]: TextNode,
12
- [SHAPE_NODE]: ShapeNode
12
+ [STROKE_NODE]: StrokeNode,
13
+ [TEXT_NODE]: TextNode,
14
+ [SHAPE_NODE]: ShapeNode
13
15
  };
14
16
  export const penUiPlugin = definePlugin({
15
- ...penPlugin,
16
- nodes: (penPlugin.nodes ?? []).map((kind)=>({
17
- ...kind,
18
- component: components[kind.type]
19
- }))
17
+ ...penPlugin,
18
+ nodes: (penPlugin.nodes ?? []).map((kind) => ({
19
+ ...kind,
20
+ component: components[kind.type]
21
+ }))
20
22
  });
@@ -1,51 +1,51 @@
1
1
  import { z } from "zod";
2
+ /** Identity, so a plugin file gets its types checked where it is written. */
2
3
  export function definePlugin(plugin) {
3
- return plugin;
4
+ return plugin;
4
5
  }
6
+ /** Collects plugins into one lookup. A node type or op kind two plugins both claim is an error. */
5
7
  export function createRegistry(plugins) {
6
- const nodes = new Map();
7
- const ops = new Map();
8
- const layouts = [];
9
- const owner = new Map();
10
- const claim = (key, pluginId)=>{
11
- const first = owner.get(key);
12
- if (first) throw new Error(`"${key}" is claimed by both "${first}" and "${pluginId}"`);
13
- owner.set(key, pluginId);
14
- };
15
- for (const plugin of plugins){
16
- for (const kind of plugin.nodes ?? []){
17
- claim(`node:${kind.type}`, plugin.id);
18
- nodes.set(kind.type, kind);
19
- }
20
- for (const op of plugin.ops ?? []){
21
- claim(`op:${op.kind}`, plugin.id);
22
- ops.set(op.kind, op);
23
- }
24
- layouts.push(...plugin.layouts ?? []);
25
- }
26
- const typesWhere = (sizing)=>new Set([
27
- ...nodes.values()
28
- ].filter((k)=>k.sizing === sizing).map((k)=>k.type));
29
- return {
30
- plugins,
31
- nodes,
32
- ops,
33
- layouts,
34
- nodeTypes () {
35
- const out = {};
36
- for (const kind of nodes.values())if (kind.component) out[kind.type] = kind.component;
37
- return out;
38
- },
39
- contentSizedTypes: ()=>typesWhere("content-sized"),
40
- layoutOwnedTypes: ()=>typesWhere("layout-owned"),
41
- parseOp (input) {
42
- const kind = input?.kind;
43
- const def = typeof kind === "string" ? ops.get(kind) : undefined;
44
- if (!def) throw new Error(`Unknown op kind: ${String(kind)}`);
45
- return def.schema.parse(input);
46
- },
47
- pickLayout (data, options = {}) {
48
- return layouts.find((pass)=>pass.matches(data, options)) ?? null;
49
- }
50
- };
8
+ const nodes = new Map();
9
+ const ops = new Map();
10
+ const layouts = [];
11
+ const owner = new Map();
12
+ const claim = (key, pluginId) => {
13
+ const first = owner.get(key);
14
+ if (first) throw new Error(`"${key}" is claimed by both "${first}" and "${pluginId}"`);
15
+ owner.set(key, pluginId);
16
+ };
17
+ for (const plugin of plugins) {
18
+ for (const kind of plugin.nodes ?? []) {
19
+ claim(`node:${kind.type}`, plugin.id);
20
+ nodes.set(kind.type, kind);
21
+ }
22
+ for (const op of plugin.ops ?? []) {
23
+ claim(`op:${op.kind}`, plugin.id);
24
+ ops.set(op.kind, op);
25
+ }
26
+ layouts.push(...plugin.layouts ?? []);
27
+ }
28
+ const typesWhere = (sizing) => new Set([...nodes.values()].filter((k) => k.sizing === sizing).map((k) => k.type));
29
+ return {
30
+ plugins,
31
+ nodes,
32
+ ops,
33
+ layouts,
34
+ nodeTypes() {
35
+ const out = {};
36
+ for (const kind of nodes.values()) if (kind.component) out[kind.type] = kind.component;
37
+ return out;
38
+ },
39
+ contentSizedTypes: () => typesWhere("content-sized"),
40
+ layoutOwnedTypes: () => typesWhere("layout-owned"),
41
+ parseOp(input) {
42
+ const kind = input?.kind;
43
+ const def = typeof kind === "string" ? ops.get(kind) : undefined;
44
+ if (!def) throw new Error(`Unknown op kind: ${String(kind)}`);
45
+ return def.schema.parse(input);
46
+ },
47
+ pickLayout(data, options = {}) {
48
+ return layouts.find((pass) => pass.matches(data, options)) ?? null;
49
+ }
50
+ };
51
51
  }
@@ -1,3 +1,4 @@
1
+ // The card's small drawings, as files a CSS mask paints in the current colour.
1
2
  import header from "../../../src/canvas/screen/glyphs/header.svg";
2
3
  import button from "../../../src/canvas/screen/glyphs/button.svg";
3
4
  import input from "../../../src/canvas/screen/glyphs/input.svg";
@@ -10,15 +11,15 @@ import close from "../../../src/canvas/screen/glyphs/close.svg";
10
11
  import plus from "../../../src/canvas/screen/glyphs/plus.svg";
11
12
  import hills from "../../../src/canvas/screen/glyphs/hills.svg";
12
13
  export const GLYPHS = {
13
- header,
14
- button,
15
- input,
16
- image,
17
- list,
18
- modal,
19
- up,
20
- down,
21
- close,
22
- plus,
23
- hills
14
+ header,
15
+ button,
16
+ input,
17
+ image,
18
+ list,
19
+ modal,
20
+ up,
21
+ down,
22
+ close,
23
+ plus,
24
+ hills
24
25
  };
@@ -1,24 +1,24 @@
1
1
  export const DEFAULT_SCREEN_LABELS = {
2
- name: "Screen",
3
- mode: "Mode",
4
- modes: {
5
- wireframe: "Wireframe",
6
- mockup: "Mockup"
7
- },
8
- kinds: {
9
- header: "Header",
10
- button: "Button",
11
- input: "Input",
12
- image: "Image",
13
- list: "List",
14
- modal: "Modal"
15
- },
16
- addPart: "Add part",
17
- moveUp: "Move up",
18
- moveDown: "Move down",
19
- remove: "Remove",
20
- primary: "Primary",
21
- secondary: "Secondary",
22
- addItem: "Add item",
23
- removeItem: "Remove item"
2
+ name: "Screen",
3
+ mode: "Mode",
4
+ modes: {
5
+ wireframe: "Wireframe",
6
+ mockup: "Mockup"
7
+ },
8
+ kinds: {
9
+ header: "Header",
10
+ button: "Button",
11
+ input: "Input",
12
+ image: "Image",
13
+ list: "List",
14
+ modal: "Modal"
15
+ },
16
+ addPart: "Add part",
17
+ moveUp: "Move up",
18
+ moveDown: "Move down",
19
+ remove: "Remove",
20
+ primary: "Primary",
21
+ secondary: "Secondary",
22
+ addItem: "Add item",
23
+ removeItem: "Remove item"
24
24
  };