@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,318 +1,323 @@
1
+ // Granular canvas mutations: pure functions over a board's nodes and edges. These are the
2
+ // ops every canvas has. A plugin adds its own kinds through `OpDefinition` (see plugin.ts).
1
3
  import { z } from "zod";
4
+ // =========================
5
+ // SCHEMAS
6
+ // =========================
2
7
  const positionSchema = z.object({
3
- x: z.number(),
4
- y: z.number()
8
+ x: z.number(),
9
+ y: z.number()
5
10
  });
6
11
  export const addNodeOp = z.object({
7
- kind: z.literal("addNode"),
8
- node: z.object({
9
- id: z.string().min(1).optional(),
10
- type: z.string().min(1),
11
- position: positionSchema.optional(),
12
- data: z.record(z.string(), z.unknown()),
13
- style: z.record(z.string(), z.unknown()).optional(),
14
- width: z.number().optional(),
15
- height: z.number().optional(),
16
- parentId: z.string().min(1).optional()
17
- })
12
+ kind: z.literal("addNode"),
13
+ node: z.object({
14
+ id: z.string().min(1).optional(),
15
+ type: z.string().min(1),
16
+ position: positionSchema.optional(),
17
+ data: z.record(z.string(), z.unknown()),
18
+ // A container is created at its real size in one op, with no second
19
+ // setParent op per child.
20
+ style: z.record(z.string(), z.unknown()).optional(),
21
+ width: z.number().optional(),
22
+ height: z.number().optional(),
23
+ parentId: z.string().min(1).optional()
24
+ })
18
25
  });
19
26
  export const removeNodeOp = z.object({
20
- kind: z.literal("removeNode"),
21
- nodeId: z.string().min(1)
27
+ kind: z.literal("removeNode"),
28
+ nodeId: z.string().min(1)
22
29
  });
23
30
  export const patchNodeOp = z.object({
24
- kind: z.literal("patchNode"),
25
- nodeId: z.string().min(1),
26
- data: z.record(z.string(), z.unknown()).optional(),
27
- style: z.record(z.string(), z.unknown()).optional(),
28
- width: z.number().optional(),
29
- height: z.number().optional()
31
+ kind: z.literal("patchNode"),
32
+ nodeId: z.string().min(1),
33
+ data: z.record(z.string(), z.unknown()).optional(),
34
+ // Merged into the node's style object. A CSS-string style from an old save is replaced
35
+ // wholesale (there is nothing sane to merge into).
36
+ style: z.record(z.string(), z.unknown()).optional(),
37
+ width: z.number().optional(),
38
+ height: z.number().optional()
30
39
  });
31
40
  export const moveNodeOp = z.object({
32
- kind: z.literal("moveNode"),
33
- nodeId: z.string().min(1),
34
- position: positionSchema
41
+ kind: z.literal("moveNode"),
42
+ nodeId: z.string().min(1),
43
+ position: positionSchema
35
44
  });
36
45
  export const setParentOp = z.object({
37
- kind: z.literal("setParent"),
38
- nodeId: z.string().min(1),
39
- parentId: z.string().min(1).nullable(),
40
- extent: z.literal("parent").nullable().optional()
46
+ kind: z.literal("setParent"),
47
+ nodeId: z.string().min(1),
48
+ // null detaches the node from its parent; "parent" extent confines it to the parent bounds.
49
+ parentId: z.string().min(1).nullable(),
50
+ extent: z.literal("parent").nullable().optional()
41
51
  });
42
52
  export const addEdgeOp = z.object({
43
- kind: z.literal("addEdge"),
44
- edge: z.object({
45
- id: z.string().min(1).optional(),
46
- source: z.string().min(1),
47
- target: z.string().min(1),
48
- sourceHandle: z.string().optional(),
49
- targetHandle: z.string().optional(),
50
- type: z.string().optional(),
51
- label: z.string().optional(),
52
- animated: z.boolean().optional(),
53
- style: z.record(z.string(), z.unknown()).optional(),
54
- data: z.record(z.string(), z.unknown()).optional()
55
- })
53
+ kind: z.literal("addEdge"),
54
+ edge: z.object({
55
+ id: z.string().min(1).optional(),
56
+ source: z.string().min(1),
57
+ target: z.string().min(1),
58
+ sourceHandle: z.string().optional(),
59
+ targetHandle: z.string().optional(),
60
+ type: z.string().optional(),
61
+ label: z.string().optional(),
62
+ animated: z.boolean().optional(),
63
+ style: z.record(z.string(), z.unknown()).optional(),
64
+ data: z.record(z.string(), z.unknown()).optional()
65
+ })
56
66
  });
57
67
  export const removeEdgeOp = z.object({
58
- kind: z.literal("removeEdge"),
59
- edgeId: z.string().min(1)
68
+ kind: z.literal("removeEdge"),
69
+ edgeId: z.string().min(1)
60
70
  });
61
71
  export const patchEdgeOp = z.object({
62
- kind: z.literal("patchEdge"),
63
- edgeId: z.string().min(1),
64
- patch: z.object({
65
- label: z.string().optional(),
66
- animated: z.boolean().optional(),
67
- type: z.string().optional(),
68
- style: z.record(z.string(), z.unknown()).optional(),
69
- data: z.record(z.string(), z.unknown()).optional(),
70
- sourceHandle: z.string().optional(),
71
- targetHandle: z.string().optional(),
72
- source: z.string().min(1).optional(),
73
- target: z.string().min(1).optional()
74
- }).refine((v)=>Object.keys(v).length > 0, {
75
- message: "patch must include at least one field"
76
- })
72
+ kind: z.literal("patchEdge"),
73
+ edgeId: z.string().min(1),
74
+ patch: z.object({
75
+ label: z.string().optional(),
76
+ animated: z.boolean().optional(),
77
+ type: z.string().optional(),
78
+ style: z.record(z.string(), z.unknown()).optional(),
79
+ /** Replaces the edge's data whole: send every field it should keep. */
80
+ data: z.record(z.string(), z.unknown()).optional(),
81
+ sourceHandle: z.string().optional(),
82
+ targetHandle: z.string().optional(),
83
+ /** Re-point the edge. The new endpoint must be a node on the board. */
84
+ source: z.string().min(1).optional(),
85
+ target: z.string().min(1).optional()
86
+ }).refine((v) => Object.keys(v).length > 0, { message: "patch must include at least one field" })
77
87
  });
88
+ /** Every schema the canvas ships, for an app that builds its own discriminated union. */
78
89
  export const CORE_OP_SCHEMAS = [
79
- addNodeOp,
80
- removeNodeOp,
81
- patchNodeOp,
82
- moveNodeOp,
83
- setParentOp,
84
- addEdgeOp,
85
- removeEdgeOp,
86
- patchEdgeOp
90
+ addNodeOp,
91
+ removeNodeOp,
92
+ patchNodeOp,
93
+ moveNodeOp,
94
+ setParentOp,
95
+ addEdgeOp,
96
+ removeEdgeOp,
97
+ patchEdgeOp
87
98
  ];
88
- export const canvasOpSchema = z.discriminatedUnion("kind", [
89
- ...CORE_OP_SCHEMAS
90
- ]);
99
+ export const canvasOpSchema = z.discriminatedUnion("kind", [...CORE_OP_SCHEMAS]);
91
100
  export class CanvasOpError extends Error {
92
- opIndex;
93
- constructor(message, opIndex){
94
- super(message), this.opIndex = opIndex;
95
- this.name = "CanvasOpError";
96
- }
101
+ opIndex;
102
+ constructor(message, opIndex) {
103
+ super(message);
104
+ this.opIndex = opIndex;
105
+ this.name = "CanvasOpError";
106
+ }
97
107
  }
98
108
  export function randomId() {
99
- return crypto.randomUUID();
109
+ return crypto.randomUUID();
100
110
  }
101
111
  export function getNodeId(node) {
102
- return typeof node.id === "string" ? node.id : undefined;
112
+ return typeof node.id === "string" ? node.id : undefined;
103
113
  }
104
114
  export function getNodeData(node) {
105
- return typeof node.data === "object" && node.data !== null ? node.data : {};
115
+ return typeof node.data === "object" && node.data !== null ? node.data : {};
106
116
  }
107
117
  export function hasNode(nodes, id) {
108
- return nodes.some((n)=>getNodeId(n) === id);
118
+ return nodes.some((n) => getNodeId(n) === id);
109
119
  }
120
+ /** Every node-targeting op shares one find-map-or-throw, so each handler only states its transform. */
110
121
  export function mapNodeOrThrow(nodes, nodeId, opIndex, transform) {
111
- let found = false;
112
- const next = nodes.map((n)=>{
113
- if (getNodeId(n) !== nodeId) return n;
114
- found = true;
115
- return transform(n);
116
- });
117
- if (!found) throw new CanvasOpError(`Unknown nodeId: ${nodeId}`, opIndex);
118
- return next;
122
+ let found = false;
123
+ const next = nodes.map((n) => {
124
+ if (getNodeId(n) !== nodeId) return n;
125
+ found = true;
126
+ return transform(n);
127
+ });
128
+ if (!found) throw new CanvasOpError(`Unknown nodeId: ${nodeId}`, opIndex);
129
+ return next;
119
130
  }
131
+ /** A copy of `list` with `item` inserted at `index`, clamped, or appended. */
120
132
  export function insertAt(list, item, index) {
121
- const next = [
122
- ...list
123
- ];
124
- next.splice(Math.min(index ?? next.length, next.length), 0, item);
125
- return next;
133
+ const next = [...list];
134
+ next.splice(Math.min(index ?? next.length, next.length), 0, item);
135
+ return next;
126
136
  }
127
137
  export function assertIndex(index, length, what, opIndex) {
128
- if (index >= length) throw new CanvasOpError(`${what} index ${index} out of bounds`, opIndex);
138
+ if (index >= length) throw new CanvasOpError(`${what} index ${index} out of bounds`, opIndex);
129
139
  }
140
+ /** Replace some of a node's data fields, throwing if `nodeId` is unknown. */
130
141
  export function patchData(state, nodeId, opIndex, fields) {
131
- state.nodes = mapNodeOrThrow(state.nodes, nodeId, opIndex, (n)=>{
132
- const nodeData = getNodeData(n);
133
- return {
134
- ...n,
135
- data: {
136
- ...nodeData,
137
- ...fields(nodeData)
138
- }
139
- };
140
- });
142
+ state.nodes = mapNodeOrThrow(state.nodes, nodeId, opIndex, (n) => {
143
+ const nodeData = getNodeData(n);
144
+ return {
145
+ ...n,
146
+ data: {
147
+ ...nodeData,
148
+ ...fields(nodeData)
149
+ }
150
+ };
151
+ });
141
152
  }
153
+ // =========================
154
+ // NODE OPS
155
+ // =========================
142
156
  function assertParentExists(nodes, nodeId, parentId, i) {
143
- if (parentId === nodeId) throw new CanvasOpError(`Node cannot be its own parent: ${nodeId}`, i);
144
- if (!hasNode(nodes, parentId)) throw new CanvasOpError(`Unknown parentId: ${parentId}`, i);
157
+ if (parentId === nodeId) throw new CanvasOpError(`Node cannot be its own parent: ${nodeId}`, i);
158
+ if (!hasNode(nodes, parentId)) throw new CanvasOpError(`Unknown parentId: ${parentId}`, i);
145
159
  }
146
160
  function addNode(state, op, i) {
147
- const { node } = op;
148
- const id = node.id ?? randomId();
149
- if (hasNode(state.nodes, id)) throw new CanvasOpError(`Node id already exists: ${id}`, i);
150
- if (node.parentId) assertParentExists(state.nodes, id, node.parentId, i);
151
- state.nodes = [
152
- ...state.nodes,
153
- {
154
- id,
155
- type: node.type,
156
- position: node.position ?? {
157
- x: 0,
158
- y: 0
159
- },
160
- data: node.data,
161
- ...node.style && {
162
- style: node.style
163
- },
164
- ...node.width != null && {
165
- width: node.width
166
- },
167
- ...node.height != null && {
168
- height: node.height
169
- },
170
- ...node.parentId && {
171
- parentId: node.parentId,
172
- extent: "parent"
173
- }
174
- }
175
- ];
161
+ const { node } = op;
162
+ const id = node.id ?? randomId();
163
+ if (hasNode(state.nodes, id)) throw new CanvasOpError(`Node id already exists: ${id}`, i);
164
+ if (node.parentId) assertParentExists(state.nodes, id, node.parentId, i);
165
+ state.nodes = [...state.nodes, {
166
+ id,
167
+ type: node.type,
168
+ position: node.position ?? {
169
+ x: 0,
170
+ y: 0
171
+ },
172
+ data: node.data,
173
+ ...node.style && { style: node.style },
174
+ ...node.width != null && { width: node.width },
175
+ ...node.height != null && { height: node.height },
176
+ // A parented node with no extent escapes its container in xyflow, so
177
+ // parentId implies extent right here at creation.
178
+ ...node.parentId && {
179
+ parentId: node.parentId,
180
+ extent: "parent"
181
+ }
182
+ }];
176
183
  }
177
184
  function removeNode(state, op, i) {
178
- if (!hasNode(state.nodes, op.nodeId)) {
179
- throw new CanvasOpError(`Unknown nodeId: ${op.nodeId}`, i);
180
- }
181
- state.nodes = state.nodes.filter((n)=>getNodeId(n) !== op.nodeId);
182
- state.edges = state.edges.filter((e)=>e.source !== op.nodeId && e.target !== op.nodeId);
185
+ if (!hasNode(state.nodes, op.nodeId)) {
186
+ throw new CanvasOpError(`Unknown nodeId: ${op.nodeId}`, i);
187
+ }
188
+ state.nodes = state.nodes.filter((n) => getNodeId(n) !== op.nodeId);
189
+ state.edges = state.edges.filter((e) => e.source !== op.nodeId && e.target !== op.nodeId);
183
190
  }
184
191
  function patchNodeRecord(n, op) {
185
- const next = {
186
- ...n
187
- };
188
- if (op.data) next.data = {
189
- ...getNodeData(n),
190
- ...op.data
191
- };
192
- if (op.style) {
193
- const existing = typeof n.style === "object" && n.style !== null ? n.style : {};
194
- next.style = {
195
- ...existing,
196
- ...op.style
197
- };
198
- }
199
- if (op.width != null) next.width = op.width;
200
- if (op.height != null) next.height = op.height;
201
- return next;
192
+ const next = { ...n };
193
+ if (op.data) next.data = {
194
+ ...getNodeData(n),
195
+ ...op.data
196
+ };
197
+ if (op.style) {
198
+ const existing = typeof n.style === "object" && n.style !== null ? n.style : {};
199
+ next.style = {
200
+ ...existing,
201
+ ...op.style
202
+ };
203
+ }
204
+ if (op.width != null) next.width = op.width;
205
+ if (op.height != null) next.height = op.height;
206
+ return next;
202
207
  }
203
208
  function patchNode(state, op, i) {
204
- if (!op.data && !op.style && op.width == null && op.height == null) {
205
- throw new CanvasOpError("patchNode requires data, style, width or height", i);
206
- }
207
- state.nodes = mapNodeOrThrow(state.nodes, op.nodeId, i, (n)=>patchNodeRecord(n, op));
209
+ if (!op.data && !op.style && op.width == null && op.height == null) {
210
+ throw new CanvasOpError("patchNode requires data, style, width or height", i);
211
+ }
212
+ state.nodes = mapNodeOrThrow(state.nodes, op.nodeId, i, (n) => patchNodeRecord(n, op));
208
213
  }
209
214
  function moveNode(state, op, i) {
210
- state.nodes = mapNodeOrThrow(state.nodes, op.nodeId, i, (n)=>({
211
- ...n,
212
- position: {
213
- x: op.position.x,
214
- y: op.position.y
215
- }
216
- }));
215
+ state.nodes = mapNodeOrThrow(state.nodes, op.nodeId, i, (n) => ({
216
+ ...n,
217
+ position: {
218
+ x: op.position.x,
219
+ y: op.position.y
220
+ }
221
+ }));
217
222
  }
218
223
  function setParent(state, op, i) {
219
- const { parentId } = op;
220
- if (parentId !== null) assertParentExists(state.nodes, op.nodeId, parentId, i);
221
- state.nodes = mapNodeOrThrow(state.nodes, op.nodeId, i, (n)=>{
222
- const { parentId: _p, extent: _e, ...rest } = n;
223
- if (parentId === null) return rest;
224
- const extent = op.extent === null ? undefined : op.extent ?? "parent";
225
- return extent ? {
226
- ...rest,
227
- parentId,
228
- extent
229
- } : {
230
- ...rest,
231
- parentId
232
- };
233
- });
224
+ const { parentId } = op;
225
+ if (parentId !== null) assertParentExists(state.nodes, op.nodeId, parentId, i);
226
+ state.nodes = mapNodeOrThrow(state.nodes, op.nodeId, i, (n) => {
227
+ const { parentId: _p, extent: _e, ...rest } = n;
228
+ if (parentId === null) return rest;
229
+ const extent = op.extent === null ? undefined : op.extent ?? "parent";
230
+ return extent ? {
231
+ ...rest,
232
+ parentId,
233
+ extent
234
+ } : {
235
+ ...rest,
236
+ parentId
237
+ };
238
+ });
234
239
  }
240
+ // =========================
241
+ // EDGE OPS
242
+ // =========================
235
243
  function addEdge(state, op, i) {
236
- const id = op.edge.id ?? randomId();
237
- if (state.edges.some((e)=>e.id === id)) {
238
- throw new CanvasOpError(`Edge id already exists: ${id}`, i);
239
- }
240
- if (!hasNode(state.nodes, op.edge.source)) {
241
- throw new CanvasOpError(`Edge source not found: ${op.edge.source}`, i);
242
- }
243
- if (!hasNode(state.nodes, op.edge.target)) {
244
- throw new CanvasOpError(`Edge target not found: ${op.edge.target}`, i);
245
- }
246
- state.edges = [
247
- ...state.edges,
248
- {
249
- id,
250
- ...op.edge
251
- }
252
- ];
244
+ const id = op.edge.id ?? randomId();
245
+ if (state.edges.some((e) => e.id === id)) {
246
+ throw new CanvasOpError(`Edge id already exists: ${id}`, i);
247
+ }
248
+ if (!hasNode(state.nodes, op.edge.source)) {
249
+ throw new CanvasOpError(`Edge source not found: ${op.edge.source}`, i);
250
+ }
251
+ if (!hasNode(state.nodes, op.edge.target)) {
252
+ throw new CanvasOpError(`Edge target not found: ${op.edge.target}`, i);
253
+ }
254
+ state.edges = [...state.edges, {
255
+ id,
256
+ ...op.edge
257
+ }];
253
258
  }
254
259
  function removeEdge(state, op, i) {
255
- if (!state.edges.some((e)=>e.id === op.edgeId)) {
256
- throw new CanvasOpError(`Unknown edgeId: ${op.edgeId}`, i);
257
- }
258
- state.edges = state.edges.filter((e)=>e.id !== op.edgeId);
260
+ if (!state.edges.some((e) => e.id === op.edgeId)) {
261
+ throw new CanvasOpError(`Unknown edgeId: ${op.edgeId}`, i);
262
+ }
263
+ state.edges = state.edges.filter((e) => e.id !== op.edgeId);
259
264
  }
260
265
  function patchEdge(state, op, i) {
261
- const { source, target } = op.patch;
262
- if (source !== undefined && !hasNode(state.nodes, source)) {
263
- throw new CanvasOpError(`Edge source not found: ${source}`, i);
264
- }
265
- if (target !== undefined && !hasNode(state.nodes, target)) {
266
- throw new CanvasOpError(`Edge target not found: ${target}`, i);
267
- }
268
- let found = false;
269
- state.edges = state.edges.map((e)=>{
270
- if (e.id !== op.edgeId) return e;
271
- found = true;
272
- return {
273
- ...e,
274
- ...op.patch
275
- };
276
- });
277
- if (!found) throw new CanvasOpError(`Unknown edgeId: ${op.edgeId}`, i);
266
+ const { source, target } = op.patch;
267
+ if (source !== undefined && !hasNode(state.nodes, source)) {
268
+ throw new CanvasOpError(`Edge source not found: ${source}`, i);
269
+ }
270
+ if (target !== undefined && !hasNode(state.nodes, target)) {
271
+ throw new CanvasOpError(`Edge target not found: ${target}`, i);
272
+ }
273
+ let found = false;
274
+ state.edges = state.edges.map((e) => {
275
+ if (e.id !== op.edgeId) return e;
276
+ found = true;
277
+ return {
278
+ ...e,
279
+ ...op.patch
280
+ };
281
+ });
282
+ if (!found) throw new CanvasOpError(`Unknown edgeId: ${op.edgeId}`, i);
278
283
  }
284
+ // =========================
285
+ // APPLY
286
+ // =========================
279
287
  const CORE_APPLY = {
280
- addNode,
281
- removeNode,
282
- patchNode,
283
- moveNode,
284
- setParent,
285
- addEdge,
286
- removeEdge,
287
- patchEdge
288
+ addNode,
289
+ removeNode,
290
+ patchNode,
291
+ moveNode,
292
+ setParent,
293
+ addEdge,
294
+ removeEdge,
295
+ patchEdge
288
296
  };
297
+ /**
298
+ * Apply `ops` in order and return the new board. `definitions` are the op kinds a plugin
299
+ * adds. Unknown kinds throw, so a typo never passes as a no-op.
300
+ */
289
301
  export function applyOps(data, ops, definitions = []) {
290
- const state = {
291
- nodes: [
292
- ...data.nodes
293
- ],
294
- edges: [
295
- ...data.edges
296
- ]
297
- };
298
- const byKind = new Map(definitions.map((def)=>[
299
- def.kind,
300
- def
301
- ]));
302
- ops.forEach((op, i)=>{
303
- const core = CORE_APPLY[op.kind];
304
- if (core) return core(state, op, i);
305
- const def = byKind.get(op.kind);
306
- if (!def) throw new CanvasOpError(`Unknown op kind: ${op.kind}`, i);
307
- def.apply(state, op, i);
308
- });
309
- const { nodes, edges } = state;
310
- return data.viewport ? {
311
- nodes,
312
- edges,
313
- viewport: data.viewport
314
- } : {
315
- nodes,
316
- edges
317
- };
302
+ const state = {
303
+ nodes: [...data.nodes],
304
+ edges: [...data.edges]
305
+ };
306
+ const byKind = new Map(definitions.map((def) => [def.kind, def]));
307
+ ops.forEach((op, i) => {
308
+ const core = CORE_APPLY[op.kind];
309
+ if (core) return core(state, op, i);
310
+ const def = byKind.get(op.kind);
311
+ if (!def) throw new CanvasOpError(`Unknown op kind: ${op.kind}`, i);
312
+ def.apply(state, op, i);
313
+ });
314
+ const { nodes, edges } = state;
315
+ return data.viewport ? {
316
+ nodes,
317
+ edges,
318
+ viewport: data.viewport
319
+ } : {
320
+ nodes,
321
+ edges
322
+ };
318
323
  }
@@ -1,41 +1,43 @@
1
1
  export const PEN_HEX = {
2
- ink: {
3
- light: "#1e1e1e",
4
- dark: "#e9ecef"
5
- },
6
- red: {
7
- light: "#e03131",
8
- dark: "#ff8787"
9
- },
10
- blue: {
11
- light: "#1971c2",
12
- dark: "#74c0fc"
13
- },
14
- green: {
15
- light: "#2f9e44",
16
- dark: "#69db7c"
17
- },
18
- amber: {
19
- light: "#e8a33d",
20
- dark: "#ffd43b"
21
- },
22
- violet: {
23
- light: "#6741d9",
24
- dark: "#b197fc"
25
- },
26
- indigo: {
27
- light: "#414fd2",
28
- dark: "#8b96ff"
29
- },
30
- terra: {
31
- light: "#f0503d",
32
- dark: "#ff8a78"
33
- }
2
+ ink: {
3
+ light: "#1e1e1e",
4
+ dark: "#e9ecef"
5
+ },
6
+ red: {
7
+ light: "#e03131",
8
+ dark: "#ff8787"
9
+ },
10
+ blue: {
11
+ light: "#1971c2",
12
+ dark: "#74c0fc"
13
+ },
14
+ green: {
15
+ light: "#2f9e44",
16
+ dark: "#69db7c"
17
+ },
18
+ amber: {
19
+ light: "#e8a33d",
20
+ dark: "#ffd43b"
21
+ },
22
+ violet: {
23
+ light: "#6741d9",
24
+ dark: "#b197fc"
25
+ },
26
+ // The brand pair the email lesson figures are drawn in (headless/sketch.ts PALETTE).
27
+ indigo: {
28
+ light: "#414fd2",
29
+ dark: "#8b96ff"
30
+ },
31
+ terra: {
32
+ light: "#f0503d",
33
+ dark: "#ff8a78"
34
+ }
34
35
  };
35
36
  export function penColorVar(color) {
36
- const known = color in PEN_HEX ? color : "ink";
37
- return `var(--pen-${known}, ${PEN_HEX[known].light})`;
37
+ const known = color in PEN_HEX ? color : "ink";
38
+ return `var(--pen-${known}, ${PEN_HEX[known].light})`;
38
39
  }
40
+ /** The `:root` block pen.css carries, built from the table so the two can't drift. */
39
41
  export function penCssTokens(mode) {
40
- return Object.keys(PEN_HEX).map((name)=>`--pen-${name}: ${PEN_HEX[name][mode]};`).join(" ");
42
+ return Object.keys(PEN_HEX).map((name) => `--pen-${name}: ${PEN_HEX[name][mode]};`).join(" ");
41
43
  }