@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.
- package/dist/agents/helpers.js +38 -38
- package/dist/agents/page-state.js +49 -50
- package/dist/agents/vendor/color.js +146 -127
- package/dist/agents/vendor/draw.js +617 -508
- package/dist/agents/vendor/engine.js +698 -599
- package/dist/agents/vendor/mesh.js +653 -683
- package/dist/agents/vendor/plastic.js +3913 -3112
- package/dist/agents/vendor/presets.js +163 -164
- package/dist/agents/vendor/shapes.js +21 -20
- package/dist/agents/vendor/ticker.js +41 -42
- package/dist/agents/vendor/types.js +1 -0
- package/dist/agents/vendor/wear.js +1421 -1448
- package/dist/ai/confirmation/confirmation-context.svelte.js +6 -6
- package/dist/ai/confirmation/index.js +3 -1
- package/dist/ai/index.js +33 -0
- package/dist/ai/prompt-input/index.js +3 -1
- package/dist/ai/reasoning/index.js +3 -1
- package/dist/ai/reasoning/reasoning-context.svelte.js +6 -6
- package/dist/ai/suggestion/index.js +3 -1
- package/dist/ai/typing-dots/index.js +3 -1
- package/dist/animations/text-swap.js +24 -24
- package/dist/brand/index.js +54 -48
- package/dist/canvas/card/resize.js +5 -3
- package/dist/canvas/edges/edge-path.js +255 -242
- package/dist/canvas/editor/context-menu.js +39 -36
- package/dist/canvas/editor/dispatch.svelte.js +154 -151
- package/dist/canvas/editor/editor-context.js +7 -2
- package/dist/canvas/editor/editor-mode.js +1 -0
- package/dist/canvas/editor/frame.js +73 -76
- package/dist/canvas/editor/hand-context.js +5 -2
- package/dist/canvas/editor/history-core.js +163 -191
- package/dist/canvas/editor/keymap.js +95 -169
- package/dist/canvas/editor/layout-actions.js +143 -144
- package/dist/canvas/editor/lock.js +52 -51
- package/dist/canvas/editor/rebase.js +217 -240
- package/dist/canvas/editor/style.js +132 -137
- package/dist/canvas/editor/text-field-context.js +7 -2
- package/dist/canvas/editor/z-order.js +65 -70
- package/dist/canvas/headless/alignment.js +22 -16
- package/dist/canvas/headless/break-apart-find.js +286 -279
- package/dist/canvas/headless/break-apart-pieces.js +62 -58
- package/dist/canvas/headless/break-apart-plan.js +299 -325
- package/dist/canvas/headless/break-apart-style.js +75 -70
- package/dist/canvas/headless/break-apart.js +188 -181
- package/dist/canvas/headless/canvas-cleanup.js +78 -58
- package/dist/canvas/headless/edge-anchors.js +115 -116
- package/dist/canvas/headless/edge-memo.js +24 -22
- package/dist/canvas/headless/edge-serialization.js +17 -27
- package/dist/canvas/headless/figure-geometry.js +232 -264
- package/dist/canvas/headless/fit-viewport.js +84 -67
- package/dist/canvas/headless/grid.js +88 -86
- package/dist/canvas/headless/icon-node.js +44 -36
- package/dist/canvas/headless/index.js +1 -0
- package/dist/canvas/headless/linkify.js +17 -19
- package/dist/canvas/headless/pen-marks.js +101 -95
- package/dist/canvas/headless/sketch.js +231 -209
- package/dist/canvas/headless/snap-guides.js +99 -83
- package/dist/canvas/headless/svg-figure.js +133 -117
- package/dist/canvas/headless/table-layout.js +146 -107
- package/dist/canvas/index.js +2 -0
- package/dist/canvas/lod/lod-mode.svelte.js +20 -18
- package/dist/canvas/lod/render-all.svelte.js +36 -39
- package/dist/canvas/ops/arrange.js +78 -71
- package/dist/canvas/ops/ops.js +260 -255
- package/dist/canvas/pen/colors.js +37 -35
- package/dist/canvas/pen/eraser.js +28 -27
- package/dist/canvas/pen/geometry.js +120 -130
- package/dist/canvas/pen/index.js +1 -0
- package/dist/canvas/pen/ops.js +11 -11
- package/dist/canvas/pen/pen-session.js +150 -159
- package/dist/canvas/pen/plugin.js +107 -104
- package/dist/canvas/pen/shapes.js +73 -68
- package/dist/canvas/pen/stroke-path.js +32 -30
- package/dist/canvas/pen/types.js +21 -21
- package/dist/canvas/pen/ui.js +10 -8
- package/dist/canvas/plugin.js +46 -46
- package/dist/canvas/screen/glyphs.js +12 -11
- package/dist/canvas/screen/labels.js +22 -22
- package/dist/canvas/screen/parts.js +98 -111
- package/dist/canvas/screen/plugin.js +47 -56
- package/dist/canvas/screen/types.js +30 -35
- package/dist/canvas/screen/ui.js +7 -7
- package/dist/canvas/test-utils.js +14 -14
- package/dist/canvas/types.js +1 -0
- package/dist/canvas/ui/forward-wheel.js +24 -25
- package/dist/canvas/ui/icons.js +64 -131
- package/dist/canvas/ui/place-panel.js +58 -49
- package/dist/canvas/ui/shortcut-tip.js +14 -12
- package/dist/canvas/ui/tools.js +300 -385
- package/dist/charts/dashboard/chart.js +4 -0
- package/dist/charts/dashboard/data-table.js +11 -0
- package/dist/charts/dashboard/windows.js +8 -3
- package/dist/charts/edge-fade.js +10 -5
- package/dist/charts/hover-band.js +16 -11
- package/dist/charts/index.js +0 -1
- package/dist/charts/motion.js +11 -7
- package/dist/charts/primitives/chart-utils.js +12 -12
- package/dist/components/copy/clipboard.js +32 -32
- package/dist/components/copy/copied.svelte.js +31 -23
- package/dist/components/data-table/data-table.js +39 -39
- package/dist/components/docs/index.js +4 -0
- package/dist/components/docs/scroll-spy.svelte.js +73 -68
- package/dist/components/list-page/filter-section.js +4 -1
- package/dist/components/list-page/index.js +2 -0
- package/dist/components/list-page/list-table.js +7 -2
- package/dist/components/list-page/queue-rail.js +14 -0
- package/dist/components/list-page/section-host.js +4 -2
- package/dist/components/list-page/step.js +4 -3
- package/dist/components/list-page/types.js +1 -0
- package/dist/components/picker/trigger.js +48 -30
- package/dist/components/picker/types.js +1 -0
- package/dist/components/segmented/collapse.js +38 -27
- package/dist/components/segmented/index.js +8 -0
- package/dist/components/segmented/segmented.js +67 -42
- package/dist/components/star-rating-input/index.js +3 -1
- package/dist/components/table-view/types.js +1 -0
- package/dist/components/tags-input/tags.js +81 -69
- package/dist/components/theme/index.js +4 -0
- package/dist/components/toolbar/index.js +4 -0
- package/dist/components/toolbar/toolbar-trigger.js +14 -1
- package/dist/editor/anchors.js +17 -13
- package/dist/editor/block-id.js +52 -51
- package/dist/editor/block-ops.js +108 -80
- package/dist/editor/doc-blocks.js +155 -166
- package/dist/editor/extensions.js +139 -180
- package/dist/editor/index.js +4 -0
- package/dist/editor/markdown-test-helpers.js +4 -2
- package/dist/editor/markdown.js +420 -408
- package/dist/editor/serializer.js +204 -200
- package/dist/editor/slash-items.js +89 -101
- package/dist/editor/youtube-url.js +25 -20
- package/dist/effects/animated-beam/path-calculator.svelte.js +43 -38
- package/dist/effects/animated-beam/types.js +1 -0
- package/dist/effects/border-beam/css.js +92 -78
- package/dist/effects/border-beam/presets.js +1705 -1720
- package/dist/effects/border-beam/register-properties.js +43 -33
- package/dist/effects/border-beam/types.js +1 -0
- package/dist/effects/image-comparison/compare.js +3 -2
- package/dist/effects/image-reveal/types.js +1 -0
- package/dist/effects/magnetic/magnetic.js +58 -52
- package/dist/effects/reveal/observe.js +50 -40
- package/dist/effects/reveal/types.js +1 -0
- package/dist/effects/shader-gradient/color.js +36 -29
- package/dist/effects/shader-gradient/renderer.js +384 -295
- package/dist/effects/shader-gradient/shaders.js +31 -0
- package/dist/effects/shader-gradient/types.js +1 -0
- package/dist/icons/apple-symbol-exports.js +99 -124
- package/dist/icons/sf-symbols.js +357 -461
- package/dist/icons/trades.js +43 -28
- package/dist/inbox/index.js +1 -0
- package/dist/inbox/logic.js +126 -122
- package/dist/inbox/types.js +43 -43
- package/dist/lib/arrive.js +19 -12
- package/dist/lib/icon-types.js +1 -0
- package/dist/lib/initials.js +16 -10
- package/dist/lib/layout.js +5 -0
- package/dist/lib/panel.js +13 -0
- package/dist/lib/result.js +22 -22
- package/dist/lib/sliding-pill.js +59 -44
- package/dist/lib/theme-transition.js +43 -43
- package/dist/lib/tone.js +31 -24
- package/dist/lib/utils.js +3 -1
- package/dist/onboarding/define-tours.js +24 -23
- package/dist/onboarding/onboarding.svelte.js +16 -12
- package/dist/onboarding/pick.js +10 -6
- package/dist/onboarding/stores.js +40 -41
- package/dist/onboarding/wait.js +24 -23
- package/dist/panel/side-panel-types.js +1 -0
- package/dist/record/demo-data.js +459 -486
- package/dist/record/fields.js +129 -131
- package/dist/record/format.js +70 -82
- package/dist/record/gutter.js +1 -0
- package/dist/record/kinds.js +95 -105
- package/dist/record/logic.js +238 -219
- package/dist/record/types.js +49 -48
- package/dist/shadcn/accordion/index.js +3 -1
- package/dist/shadcn/alert/index.js +3 -1
- package/dist/shadcn/alert-dialog/index.js +3 -1
- package/dist/shadcn/attachment/index.js +3 -1
- package/dist/shadcn/avatar/index.js +3 -1
- package/dist/shadcn/breadcrumb/index.js +3 -1
- package/dist/shadcn/bubble/index.js +3 -1
- package/dist/shadcn/button/index.js +3 -1
- package/dist/shadcn/calendar/index.js +3 -1
- package/dist/shadcn/card/index.js +3 -1
- package/dist/shadcn/checkbox/index.js +3 -1
- package/dist/shadcn/code-block/index.js +3 -1
- package/dist/shadcn/collapsible/index.js +3 -1
- package/dist/shadcn/command/index.js +4 -1
- package/dist/shadcn/context-menu/index.js +3 -1
- package/dist/shadcn/dialog/index.js +3 -1
- package/dist/shadcn/dropdown-menu/menu-item.js +40 -20
- package/dist/shadcn/empty/index.js +3 -1
- package/dist/shadcn/field/index.js +3 -1
- package/dist/shadcn/hooks/highlighter.js +77 -75
- package/dist/shadcn/hooks/is-mobile.svelte.js +3 -3
- package/dist/shadcn/hooks/use-highlighted-code.svelte.js +62 -62
- package/dist/shadcn/input/index.js +3 -1
- package/dist/shadcn/kbd/index.js +3 -1
- package/dist/shadcn/label/index.js +3 -1
- package/dist/shadcn/marker/index.js +3 -1
- package/dist/shadcn/message/index.js +3 -1
- package/dist/shadcn/message-scroller/index.js +3 -1
- package/dist/shadcn/popover/index.js +3 -1
- package/dist/shadcn/progress/index.js +3 -1
- package/dist/shadcn/progress/tone.js +8 -7
- package/dist/shadcn/radio-group/index.js +3 -1
- package/dist/shadcn/scroll-area/index.js +3 -1
- package/dist/shadcn/select/index.js +3 -1
- package/dist/shadcn/separator/index.js +3 -1
- package/dist/shadcn/sheet/index.js +3 -1
- package/dist/shadcn/sidebar/context.svelte.js +42 -28
- package/dist/shadcn/sidebar/index.js +3 -1
- package/dist/shadcn/skeleton/index.js +3 -1
- package/dist/shadcn/slider/index.js +3 -1
- package/dist/shadcn/switch/index.js +3 -1
- package/dist/shadcn/table/index.js +3 -1
- package/dist/shadcn/tabs/index.js +3 -1
- package/dist/shadcn/textarea/index.js +3 -1
- package/dist/shadcn/toggle/index.js +3 -1
- package/dist/shadcn/toggle-group/index.js +3 -1
- package/dist/shadcn/tooltip/context.js +7 -2
- package/dist/shadcn/tooltip/index.js +3 -1
- package/dist/shadcn/trace/index.js +3 -1
- package/dist/slides/bridges.js +65 -59
- package/dist/slides/deck-controller.svelte.js +210 -207
- package/dist/slides/export/capture.js +66 -71
- package/dist/slides/export/ffmpeg.js +76 -72
- package/dist/slides/export/index.js +3 -0
- package/dist/slides/export/launch.js +9 -6
- package/dist/slides/export/pdf.js +60 -54
- package/dist/slides/export/types.js +1 -0
- package/dist/slides/labels.js +24 -24
- package/dist/slides/layout.js +39 -41
- package/dist/slides/model.js +9 -7
- package/dist/slides/navigation.js +78 -73
- package/dist/slides/types.js +2 -2
- package/dist/status/define-statuses.js +18 -14
- package/dist/status/status-types.js +1 -0
- package/dist/test-setup.js +24 -20
- package/dist/thread/logic.js +73 -77
- package/dist/thread/types.js +21 -21
- package/dist/work/board-order.js +94 -90
- package/dist/work/dates.js +16 -9
- package/dist/work/detail/detail-types.js +18 -18
- package/dist/work/docs/edited.js +32 -29
- package/dist/work/docs/index.js +4 -0
- package/dist/work/docs/labels.js +34 -34
- package/dist/work/docs/menu.js +113 -106
- package/dist/work/docs/outline.js +39 -29
- package/dist/work/gantt/logic.js +211 -235
- package/dist/work/inbox/inbox-logic.js +71 -75
- package/dist/work/inbox/inbox-statuses.js +75 -72
- package/dist/work/inbox/inbox-types.js +1 -0
- package/dist/work/kanban-drag.js +27 -22
- package/dist/work/kanban-types.js +1 -0
- package/dist/work/presets/sales.js +36 -32
- package/dist/work/reorder.js +9 -8
- package/dist/work/task-types.js +1 -0
- package/dist/work/time/duration.js +51 -46
- package/dist/work/time/time-types.js +23 -23
- package/dist/work/use-kanban-dnd.svelte.js +126 -125
- package/package.json +3 -1
- package/src/components/list-page/master-detail.svelte +3 -1
- package/src/components/list-page/rail-row.svelte +4 -4
- 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
|
-
|
|
6
|
-
const
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
-
|
|
9
|
+
setContext(KEY, context);
|
|
5
10
|
}
|
|
6
11
|
export function getDiagramEditorContext() {
|
|
7
|
-
|
|
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
|
-
|
|
4
|
-
|
|
4
|
+
width: 480,
|
|
5
|
+
height: 300
|
|
5
6
|
};
|
|
6
7
|
const MIN_FRAME = {
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
7
|
+
setContext(KEY, read);
|
|
5
8
|
}
|
|
6
9
|
export function useHandDrawn() {
|
|
7
|
-
|
|
10
|
+
return getContext(KEY) ?? (() => false);
|
|
8
11
|
}
|