@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
package/dist/canvas/ops/ops.js
CHANGED
|
@@ -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
|
-
|
|
4
|
-
|
|
8
|
+
x: z.number(),
|
|
9
|
+
y: z.number()
|
|
5
10
|
});
|
|
6
11
|
export const addNodeOp = z.object({
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
21
|
-
|
|
27
|
+
kind: z.literal("removeNode"),
|
|
28
|
+
nodeId: z.string().min(1)
|
|
22
29
|
});
|
|
23
30
|
export const patchNodeOp = z.object({
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
41
|
+
kind: z.literal("moveNode"),
|
|
42
|
+
nodeId: z.string().min(1),
|
|
43
|
+
position: positionSchema
|
|
35
44
|
});
|
|
36
45
|
export const setParentOp = z.object({
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
59
|
-
|
|
68
|
+
kind: z.literal("removeEdge"),
|
|
69
|
+
edgeId: z.string().min(1)
|
|
60
70
|
});
|
|
61
71
|
export const patchEdgeOp = z.object({
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
109
|
+
return crypto.randomUUID();
|
|
100
110
|
}
|
|
101
111
|
export function getNodeId(node) {
|
|
102
|
-
|
|
112
|
+
return typeof node.id === "string" ? node.id : undefined;
|
|
103
113
|
}
|
|
104
114
|
export function getNodeData(node) {
|
|
105
|
-
|
|
115
|
+
return typeof node.data === "object" && node.data !== null ? node.data : {};
|
|
106
116
|
}
|
|
107
117
|
export function hasNode(nodes, id) {
|
|
108
|
-
|
|
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
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
122
|
-
|
|
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
|
-
|
|
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
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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
|
-
|
|
144
|
-
|
|
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
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
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
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
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
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
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
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
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
|
-
|
|
37
|
-
|
|
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
|
-
|
|
42
|
+
return Object.keys(PEN_HEX).map((name) => `--pen-${name}: ${PEN_HEX[name][mode]};`).join(" ");
|
|
41
43
|
}
|