@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
@@ -2,156 +2,159 @@ import { applyOps } from "../ops/ops.js";
2
2
  import { tryCatch } from "../../lib/result.js";
3
3
  import { applyHistoryEntry, diffFlowState, mergeHistoryEntries } from "./history-core.js";
4
4
  const MAX_ENTRIES = 200;
5
- const COALESCE_WINDOW_MS = 1000;
6
- const STUCK_TRANSACTION_MS = 10_000;
5
+ /** Keystrokes under this far apart share one undo step. */
6
+ const COALESCE_WINDOW_MS = 1e3;
7
+ // A drag whose stop never fires (pointer lost, node unmounted) leaves its transaction open,
8
+ // and drag frames never reach the dispatcher, so this counts from the last begin or commit.
9
+ const STUCK_TRANSACTION_MS = 1e4;
10
+ /** `Op` is the union of op shapes the caller dispatches: the canvas's own plus its plugins'. */
7
11
  export function createCanvasDispatcher(io) {
8
- const now = io.now ?? Date.now;
9
- let undoStack = $state.raw([]);
10
- let redoStack = $state.raw([]);
11
- let depth = 0;
12
- let baseline = null;
13
- let lastActivityAt = 0;
14
- let session = null;
15
- function snapshot() {
16
- const state = io.getState();
17
- return {
18
- nodes: [
19
- ...state.nodes
20
- ],
21
- edges: [
22
- ...state.edges
23
- ]
24
- };
25
- }
26
- function isStale() {
27
- return depth > 0 && now() - lastActivityAt > STUCK_TRANSACTION_MS;
28
- }
29
- function finish(coalesceKey) {
30
- depth = 0;
31
- const before = baseline;
32
- baseline = null;
33
- const entry = before && diffFlowState(before, io.getState());
34
- if (!entry) return;
35
- const at = now();
36
- const top = undoStack.at(-1);
37
- if (coalesceKey && top && session?.key === coalesceKey && at - session.at < COALESCE_WINDOW_MS) {
38
- undoStack = [
39
- ...undoStack.slice(0, -1),
40
- mergeHistoryEntries(top, entry)
41
- ];
42
- session = {
43
- key: coalesceKey,
44
- at
45
- };
46
- } else {
47
- undoStack = [
48
- ...undoStack.slice(-(MAX_ENTRIES - 1)),
49
- entry
50
- ];
51
- session = coalesceKey ? {
52
- key: coalesceKey,
53
- at
54
- } : null;
55
- }
56
- redoStack = [];
57
- io.onMutated();
58
- }
59
- function recoverStuck() {
60
- if (isStale()) finish();
61
- }
62
- function begin() {
63
- recoverStuck();
64
- lastActivityAt = now();
65
- if (depth === 0) baseline = snapshot();
66
- depth += 1;
67
- }
68
- function commit(coalesceKey) {
69
- lastActivityAt = now();
70
- if (depth > 1) {
71
- depth -= 1;
72
- return;
73
- }
74
- if (depth === 1) finish(coalesceKey);
75
- }
76
- function run(fn, coalesceKey) {
77
- begin();
78
- const res = tryCatch(fn);
79
- commit(coalesceKey);
80
- if (!res.ok) throw res.error;
81
- return res.value;
82
- }
83
- function dispatch(ops, options) {
84
- const asked = Array.isArray(ops) ? ops : [
85
- ops
86
- ];
87
- const list = io.guard ? io.guard(io.getState().nodes, asked) : asked;
88
- if (list.length === 0) return true;
89
- return run(()=>{
90
- const state = io.getState();
91
- const res = tryCatch(()=>applyOps({
92
- nodes: state.nodes,
93
- edges: state.edges
94
- }, list, io.opDefinitions));
95
- if (!res.ok) return false;
96
- io.setState({
97
- nodes: res.value.nodes,
98
- edges: res.value.edges
99
- });
100
- return true;
101
- }, options?.coalesceKey);
102
- }
103
- function undo() {
104
- recoverStuck();
105
- session = null;
106
- const entry = undoStack.at(-1);
107
- if (!entry || depth > 0) return;
108
- undoStack = undoStack.slice(0, -1);
109
- redoStack = [
110
- ...redoStack,
111
- entry
112
- ];
113
- io.setState(applyHistoryEntry(io.getState(), entry, "undo"));
114
- io.onMutated();
115
- }
116
- function redo() {
117
- recoverStuck();
118
- session = null;
119
- const entry = redoStack.at(-1);
120
- if (!entry || depth > 0) return;
121
- redoStack = redoStack.slice(0, -1);
122
- undoStack = [
123
- ...undoStack,
124
- entry
125
- ];
126
- io.setState(applyHistoryEntry(io.getState(), entry, "redo"));
127
- io.onMutated();
128
- }
129
- return {
130
- get canUndo () {
131
- return undoStack.length > 0;
132
- },
133
- get canRedo () {
134
- return redoStack.length > 0;
135
- },
136
- get isOpen () {
137
- return depth > 0 && !isStale();
138
- },
139
- begin,
140
- touch () {
141
- if (depth > 0) lastActivityAt = now();
142
- },
143
- commit: ()=>commit(),
144
- run: (fn)=>run(fn),
145
- dispatch,
146
- endCoalesce () {
147
- session = null;
148
- },
149
- undo,
150
- redo,
151
- clear () {
152
- undoStack = [];
153
- redoStack = [];
154
- session = null;
155
- }
156
- };
12
+ const now = io.now ?? Date.now;
13
+ let undoStack = $state.raw([]);
14
+ let redoStack = $state.raw([]);
15
+ let depth = 0;
16
+ let baseline = null;
17
+ let lastActivityAt = 0;
18
+ // The text session the top undo entry belongs to, while keystrokes keep merging into it.
19
+ let session = null;
20
+ // Cards are replaced, never edited in place, so holding the objects equals a deep copy for a pointer each.
21
+ function snapshot() {
22
+ const state = io.getState();
23
+ return {
24
+ nodes: [...state.nodes],
25
+ edges: [...state.edges]
26
+ };
27
+ }
28
+ function isStale() {
29
+ return depth > 0 && now() - lastActivityAt > STUCK_TRANSACTION_MS;
30
+ }
31
+ /** Record the open transaction's diff as one history entry, merged into the text session it continues. */
32
+ function finish(coalesceKey) {
33
+ depth = 0;
34
+ const before = baseline;
35
+ baseline = null;
36
+ const entry = before && diffFlowState(before, io.getState());
37
+ if (!entry) return;
38
+ const at = now();
39
+ const top = undoStack.at(-1);
40
+ if (coalesceKey && top && session?.key === coalesceKey && at - session.at < COALESCE_WINDOW_MS) {
41
+ undoStack = [...undoStack.slice(0, -1), mergeHistoryEntries(top, entry)];
42
+ session = {
43
+ key: coalesceKey,
44
+ at
45
+ };
46
+ } else {
47
+ undoStack = [...undoStack.slice(-(MAX_ENTRIES - 1)), entry];
48
+ session = coalesceKey ? {
49
+ key: coalesceKey,
50
+ at
51
+ } : null;
52
+ }
53
+ redoStack = [];
54
+ io.onMutated();
55
+ }
56
+ /** Force-close a transaction nobody closed, so undo and the next gesture work again. */
57
+ function recoverStuck() {
58
+ if (isStale()) finish();
59
+ }
60
+ /** Open a transaction. Nested calls join the outermost one. */
61
+ function begin() {
62
+ recoverStuck();
63
+ lastActivityAt = now();
64
+ if (depth === 0) baseline = snapshot();
65
+ depth += 1;
66
+ }
67
+ /** Close a transaction. The outermost close diffs and records the entry. */
68
+ function commit(coalesceKey) {
69
+ lastActivityAt = now();
70
+ if (depth > 1) {
71
+ depth -= 1;
72
+ return;
73
+ }
74
+ // A commit with no begin has nothing to record.
75
+ if (depth === 1) finish(coalesceKey);
76
+ }
77
+ function run(fn, coalesceKey) {
78
+ begin();
79
+ const res = tryCatch(fn);
80
+ commit(coalesceKey);
81
+ if (!res.ok) throw res.error;
82
+ return res.value;
83
+ }
84
+ /**
85
+ * Apply granular ops to the flow state inside a transaction. A `coalesceKey` folds the
86
+ * step into the top undo entry while that key keeps committing under a second apart.
87
+ */
88
+ function dispatch(ops, options) {
89
+ const asked = Array.isArray(ops) ? ops : [ops];
90
+ const list = io.guard ? io.guard(io.getState().nodes, asked) : asked;
91
+ if (list.length === 0) return true;
92
+ return run(() => {
93
+ const state = io.getState();
94
+ const res = tryCatch(() => applyOps({
95
+ // `applyOps` patches plain records so one op log replays over xyflow
96
+ // and persisted shapes. Widening in and narrowing out is that seam.
97
+ nodes: state.nodes,
98
+ edges: state.edges
99
+ }, list, io.opDefinitions));
100
+ if (!res.ok) return false;
101
+ io.setState({
102
+ nodes: res.value.nodes,
103
+ edges: res.value.edges
104
+ });
105
+ return true;
106
+ }, options?.coalesceKey);
107
+ }
108
+ // An open gesture owns the baseline, so a replay under it would fold into its entry.
109
+ function undo() {
110
+ recoverStuck();
111
+ session = null;
112
+ const entry = undoStack.at(-1);
113
+ if (!entry || depth > 0) return;
114
+ undoStack = undoStack.slice(0, -1);
115
+ redoStack = [...redoStack, entry];
116
+ io.setState(applyHistoryEntry(io.getState(), entry, "undo"));
117
+ io.onMutated();
118
+ }
119
+ function redo() {
120
+ recoverStuck();
121
+ session = null;
122
+ const entry = redoStack.at(-1);
123
+ if (!entry || depth > 0) return;
124
+ redoStack = redoStack.slice(0, -1);
125
+ undoStack = [...undoStack, entry];
126
+ io.setState(applyHistoryEntry(io.getState(), entry, "redo"));
127
+ io.onMutated();
128
+ }
129
+ return {
130
+ get canUndo() {
131
+ return undoStack.length > 0;
132
+ },
133
+ get canRedo() {
134
+ return redoStack.length > 0;
135
+ },
136
+ /** True while a gesture holds a transaction open. A stuck one reads false. */
137
+ get isOpen() {
138
+ return depth > 0 && !isStale();
139
+ },
140
+ begin,
141
+ /** A long gesture calls this on every frame, so a slow drag isn't mistaken for a stuck one. */
142
+ touch() {
143
+ if (depth > 0) lastActivityAt = now();
144
+ },
145
+ commit: () => commit(),
146
+ run: (fn) => run(fn),
147
+ dispatch,
148
+ /** End the text session, so the next keystroke starts a new undo step. Call it on blur. */
149
+ endCoalesce() {
150
+ session = null;
151
+ },
152
+ undo,
153
+ redo,
154
+ clear() {
155
+ undoStack = [];
156
+ redoStack = [];
157
+ session = null;
158
+ }
159
+ };
157
160
  }
@@ -1,8 +1,13 @@
1
+ // Typed Svelte context between the editor and the node and edge components inside it,
2
+ // so every component edit lands on the dispatcher (undoable, autosaved).
3
+ // Components must tolerate a missing context: read-only hosts like a slide's embedded
4
+ // canvas provide none.
1
5
  import { getContext, setContext } from "svelte";
2
6
  const KEY = Symbol("diagram-editor-context");
7
+ /** An app widens the context with its own members: pass its type as `T`. */
3
8
  export function setDiagramEditorContext(context) {
4
- setContext(KEY, context);
9
+ setContext(KEY, context);
5
10
  }
6
11
  export function getDiagramEditorContext() {
7
- return getContext(KEY) ?? null;
12
+ return getContext(KEY) ?? null;
8
13
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,87 +1,84 @@
1
+ /** A drag shorter than this on both sides was a click. */
1
2
  const CLICK_DRAG = 8;
2
3
  const DEFAULT_FRAME = {
3
- width: 480,
4
- height: 300
4
+ width: 480,
5
+ height: 300
5
6
  };
6
7
  const MIN_FRAME = {
7
- width: 80,
8
- height: 60
8
+ width: 80,
9
+ height: 60
9
10
  };
11
+ /** The box a drag from one corner to the other covers, or the default size for a click. */
10
12
  export function frameRect(from, to) {
11
- const width = Math.abs(to.x - from.x);
12
- const height = Math.abs(to.y - from.y);
13
- if (width < CLICK_DRAG && height < CLICK_DRAG) {
14
- return {
15
- x: Math.round(from.x),
16
- y: Math.round(from.y),
17
- ...DEFAULT_FRAME
18
- };
19
- }
20
- return {
21
- x: Math.round(Math.min(from.x, to.x)),
22
- y: Math.round(Math.min(from.y, to.y)),
23
- width: Math.round(Math.max(width, MIN_FRAME.width)),
24
- height: Math.round(Math.max(height, MIN_FRAME.height))
25
- };
13
+ const width = Math.abs(to.x - from.x);
14
+ const height = Math.abs(to.y - from.y);
15
+ if (width < CLICK_DRAG && height < CLICK_DRAG) {
16
+ return {
17
+ x: Math.round(from.x),
18
+ y: Math.round(from.y),
19
+ ...DEFAULT_FRAME
20
+ };
21
+ }
22
+ return {
23
+ x: Math.round(Math.min(from.x, to.x)),
24
+ y: Math.round(Math.min(from.y, to.y)),
25
+ width: Math.round(Math.max(width, MIN_FRAME.width)),
26
+ height: Math.round(Math.max(height, MIN_FRAME.height))
27
+ };
26
28
  }
27
29
  function sizeOf(node) {
28
- const width = node.measured?.width ?? node.width;
29
- const height = node.measured?.height ?? node.height;
30
- return width && height ? {
31
- width,
32
- height
33
- } : null;
30
+ const width = node.measured?.width ?? node.width;
31
+ const height = node.measured?.height ?? node.height;
32
+ return width && height ? {
33
+ width,
34
+ height
35
+ } : null;
34
36
  }
37
+ /** The op list for one frame, and the id it was given. Top-level cards wholly inside the box become its children. */
35
38
  export function frameOps(rect, nodes, { type, title, data = {} }) {
36
- const id = crypto.randomUUID();
37
- const inside = nodes.filter((node)=>{
38
- const size = sizeOf(node);
39
- if (!size || node.parentId || node.type === type) return false;
40
- const { x, y } = node.position;
41
- return x >= rect.x && y >= rect.y && x + size.width <= rect.x + rect.width && y + size.height <= rect.y + rect.height;
42
- });
43
- const ops = [
44
- {
45
- kind: "addNode",
46
- node: {
47
- id,
48
- type,
49
- position: {
50
- x: rect.x,
51
- y: rect.y
52
- },
53
- data: {
54
- ...data,
55
- label: title,
56
- frame: true
57
- },
58
- width: rect.width,
59
- height: rect.height,
60
- style: {
61
- width: rect.width,
62
- height: rect.height
63
- }
64
- }
65
- },
66
- ...inside.flatMap((node)=>[
67
- {
68
- kind: "setParent",
69
- nodeId: node.id,
70
- parentId: id,
71
- extent: "parent"
72
- },
73
- {
74
- kind: "moveNode",
75
- nodeId: node.id,
76
- position: {
77
- x: node.position.x - rect.x,
78
- y: node.position.y - rect.y
79
- }
80
- }
81
- ])
82
- ];
83
- return {
84
- id,
85
- ops
86
- };
39
+ const id = crypto.randomUUID();
40
+ const inside = nodes.filter((node) => {
41
+ const size = sizeOf(node);
42
+ if (!size || node.parentId || node.type === type) return false;
43
+ const { x, y } = node.position;
44
+ return x >= rect.x && y >= rect.y && x + size.width <= rect.x + rect.width && y + size.height <= rect.y + rect.height;
45
+ });
46
+ const ops = [{
47
+ kind: "addNode",
48
+ node: {
49
+ id,
50
+ type,
51
+ position: {
52
+ x: rect.x,
53
+ y: rect.y
54
+ },
55
+ data: {
56
+ ...data,
57
+ label: title,
58
+ frame: true
59
+ },
60
+ width: rect.width,
61
+ height: rect.height,
62
+ style: {
63
+ width: rect.width,
64
+ height: rect.height
65
+ }
66
+ }
67
+ }, ...inside.flatMap((node) => [{
68
+ kind: "setParent",
69
+ nodeId: node.id,
70
+ parentId: id,
71
+ extent: "parent"
72
+ }, {
73
+ kind: "moveNode",
74
+ nodeId: node.id,
75
+ position: {
76
+ x: node.position.x - rect.x,
77
+ y: node.position.y - rect.y
78
+ }
79
+ }])];
80
+ return {
81
+ id,
82
+ ops
83
+ };
87
84
  }
@@ -1,8 +1,11 @@
1
+ // Whether the host page is drawn by hand. The host sets it once and the pen's
2
+ // text and shapes read it. With no host, they keep the app's look.
1
3
  import { getContext, setContext } from "svelte";
2
4
  const KEY = Symbol("canvas-hand-drawn");
5
+ /** Call during component init. `read` is a getter, so a toggle reaches mounted nodes. */
3
6
  export function setHandDrawn(read) {
4
- setContext(KEY, read);
7
+ setContext(KEY, read);
5
8
  }
6
9
  export function useHandDrawn() {
7
- return getContext(KEY) ?? (()=>false);
10
+ return getContext(KEY) ?? (() => false);
8
11
  }