@cssints/apg-specs 0.0.0-stage → 0.5.1
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/LICENSE +21 -0
- package/README.md +65 -2
- package/dist/apg/accordion.bridge.d.mts +35 -0
- package/dist/apg/accordion.bridge.mjs +326 -0
- package/dist/apg/accordion.model.json +8397 -0
- package/dist/apg/alert.bridge.d.mts +22 -0
- package/dist/apg/alert.bridge.mjs +86 -0
- package/dist/apg/alert.model.json +871 -0
- package/dist/apg/alertdialog.bridge.d.mts +21 -0
- package/dist/apg/alertdialog.bridge.mjs +221 -0
- package/dist/apg/alertdialog.model.json +5232 -0
- package/dist/apg/breadcrumb.bridge.d.mts +26 -0
- package/dist/apg/breadcrumb.bridge.mjs +120 -0
- package/dist/apg/breadcrumb.model.json +574 -0
- package/dist/apg/button.bridge.d.mts +35 -0
- package/dist/apg/button.bridge.mjs +208 -0
- package/dist/apg/button.model.json +5814 -0
- package/dist/apg/carousel.bridge.d.mts +44 -0
- package/dist/apg/carousel.bridge.mjs +262 -0
- package/dist/apg/carousel.model.json +8963 -0
- package/dist/apg/checkbox-group.bridge.d.mts +25 -0
- package/dist/apg/checkbox-group.bridge.mjs +276 -0
- package/dist/apg/checkbox-group.model.json +3863 -0
- package/dist/apg/checkbox.bridge.d.mts +34 -0
- package/dist/apg/checkbox.bridge.mjs +253 -0
- package/dist/apg/checkbox.model.json +3158 -0
- package/dist/apg/combobox-autocomplete.bridge.d.mts +14 -0
- package/dist/apg/combobox-autocomplete.bridge.mjs +326 -0
- package/dist/apg/combobox-autocomplete.model.json +11586 -0
- package/dist/apg/combobox-dialog.bridge.d.mts +17 -0
- package/dist/apg/combobox-dialog.bridge.mjs +250 -0
- package/dist/apg/combobox-dialog.model.json +8981 -0
- package/dist/apg/combobox-grid.bridge.d.mts +17 -0
- package/dist/apg/combobox-grid.bridge.mjs +227 -0
- package/dist/apg/combobox-grid.model.json +8554 -0
- package/dist/apg/combobox-select-only.bridge.d.mts +24 -0
- package/dist/apg/combobox-select-only.bridge.mjs +304 -0
- package/dist/apg/combobox-select-only.model.json +7846 -0
- package/dist/apg/combobox-tree.bridge.d.mts +17 -0
- package/dist/apg/combobox-tree.bridge.mjs +211 -0
- package/dist/apg/combobox-tree.model.json +9345 -0
- package/dist/apg/dialog-modal.bridge.d.mts +35 -0
- package/dist/apg/dialog-modal.bridge.mjs +186 -0
- package/dist/apg/dialog-modal.model.json +7580 -0
- package/dist/apg/disclosure.bridge.d.mts +25 -0
- package/dist/apg/disclosure.bridge.mjs +202 -0
- package/dist/apg/disclosure.model.json +4703 -0
- package/dist/apg/feed.bridge.d.mts +31 -0
- package/dist/apg/feed.bridge.mjs +246 -0
- package/dist/apg/feed.model.json +4523 -0
- package/dist/apg/grid-selection.bridge.d.mts +13 -0
- package/dist/apg/grid-selection.bridge.mjs +160 -0
- package/dist/apg/grid-selection.model.json +6974 -0
- package/dist/apg/grid-sort.bridge.d.mts +12 -0
- package/dist/apg/grid-sort.bridge.mjs +138 -0
- package/dist/apg/grid-sort.model.json +3799 -0
- package/dist/apg/grid.bridge.d.mts +54 -0
- package/dist/apg/grid.bridge.mjs +441 -0
- package/dist/apg/grid.model.json +3802 -0
- package/dist/apg/link.bridge.d.mts +24 -0
- package/dist/apg/link.bridge.mjs +126 -0
- package/dist/apg/link.model.json +694 -0
- package/dist/apg/listbox-multi.bridge.d.mts +10 -0
- package/dist/apg/listbox-multi.bridge.mjs +261 -0
- package/dist/apg/listbox-multi.model.json +10893 -0
- package/dist/apg/listbox-rearrangeable.bridge.d.mts +20 -0
- package/dist/apg/listbox-rearrangeable.bridge.mjs +237 -0
- package/dist/apg/listbox-rearrangeable.model.json +17592 -0
- package/dist/apg/listbox-shared.d.mts +58 -0
- package/dist/apg/listbox-shared.mjs +171 -0
- package/dist/apg/listbox-single.bridge.d.mts +10 -0
- package/dist/apg/listbox-single.bridge.mjs +196 -0
- package/dist/apg/listbox-single.model.json +8024 -0
- package/dist/apg/listbox-typeahead.bridge.d.mts +10 -0
- package/dist/apg/listbox-typeahead.bridge.mjs +153 -0
- package/dist/apg/listbox-typeahead.model.json +5615 -0
- package/dist/apg/menu-button.bridge.d.mts +29 -0
- package/dist/apg/menu-button.bridge.mjs +259 -0
- package/dist/apg/menu-button.model.json +5706 -0
- package/dist/apg/menu-checkable.bridge.d.mts +25 -0
- package/dist/apg/menu-checkable.bridge.mjs +226 -0
- package/dist/apg/menu-checkable.model.json +4277 -0
- package/dist/apg/menu-menubar.bridge.d.mts +27 -0
- package/dist/apg/menu-menubar.bridge.mjs +273 -0
- package/dist/apg/menu-menubar.model.json +9540 -0
- package/dist/apg/meter.bridge.d.mts +28 -0
- package/dist/apg/meter.bridge.mjs +159 -0
- package/dist/apg/meter.model.json +1185 -0
- package/dist/apg/player.bridge.d.mts +24 -0
- package/dist/apg/player.bridge.mjs +342 -0
- package/dist/apg/player.model.json +11013 -0
- package/dist/apg/radio.bridge.d.mts +35 -0
- package/dist/apg/radio.bridge.mjs +371 -0
- package/dist/apg/radio.model.json +5011 -0
- package/dist/apg/slider-multithumb.bridge.d.mts +34 -0
- package/dist/apg/slider-multithumb.bridge.mjs +365 -0
- package/dist/apg/slider-multithumb.model.json +5774 -0
- package/dist/apg/slider.bridge.d.mts +54 -0
- package/dist/apg/slider.bridge.mjs +359 -0
- package/dist/apg/slider.model.json +4881 -0
- package/dist/apg/spinbutton.bridge.d.mts +41 -0
- package/dist/apg/spinbutton.bridge.mjs +327 -0
- package/dist/apg/spinbutton.model.json +7571 -0
- package/dist/apg/switch.bridge.d.mts +30 -0
- package/dist/apg/switch.bridge.mjs +243 -0
- package/dist/apg/switch.model.json +3159 -0
- package/dist/apg/tabs.bridge.d.mts +41 -0
- package/dist/apg/tabs.bridge.mjs +294 -0
- package/dist/apg/tabs.model.json +7659 -0
- package/dist/apg/timeline.bridge.d.mts +36 -0
- package/dist/apg/timeline.bridge.mjs +265 -0
- package/dist/apg/timeline.model.json +6189 -0
- package/dist/apg/toolbar.bridge.d.mts +25 -0
- package/dist/apg/toolbar.bridge.mjs +307 -0
- package/dist/apg/toolbar.model.json +6616 -0
- package/dist/apg/tooltip.bridge.d.mts +36 -0
- package/dist/apg/tooltip.bridge.mjs +208 -0
- package/dist/apg/tooltip.model.json +8295 -0
- package/dist/apg/treegrid.bridge.d.mts +28 -0
- package/dist/apg/treegrid.bridge.mjs +311 -0
- package/dist/apg/treegrid.model.json +17358 -0
- package/dist/apg/treeview.bridge.d.mts +26 -0
- package/dist/apg/treeview.bridge.mjs +396 -0
- package/dist/apg/treeview.model.json +13745 -0
- package/dist/apg/windowsplitter.bridge.d.mts +36 -0
- package/dist/apg/windowsplitter.bridge.mjs +242 -0
- package/dist/apg/windowsplitter.model.json +6253 -0
- package/dist/canvas/camera.model.json +6568 -0
- package/dist/canvas/canvas.bridge.d.mts +190 -0
- package/dist/canvas/canvas.bridge.mjs +729 -0
- package/dist/canvas/pointer.model.json +17095 -0
- package/dist/scenario.d.mts +233 -0
- package/dist/scenario.mjs +313 -0
- package/package.json +57 -4
- package/scenarios/apg/README.md +221 -0
- package/scenarios/apg/accordion.base-ui.json +1005 -0
- package/scenarios/apg/accordion.react-aria.json +929 -0
- package/scenarios/apg/alert.base-ui.json +81 -0
- package/scenarios/apg/alert.react-aria.json +98 -0
- package/scenarios/apg/alertdialog.base-ui.json +331 -0
- package/scenarios/apg/alertdialog.react-aria.json +225 -0
- package/scenarios/apg/breadcrumb.react-aria.json +312 -0
- package/scenarios/apg/button.ariakit.json +136 -0
- package/scenarios/apg/button.base-ui.json +747 -0
- package/scenarios/apg/button.react-aria.json +443 -0
- package/scenarios/apg/carousel.apg.json +909 -0
- package/scenarios/apg/checkbox.ariakit.json +299 -0
- package/scenarios/apg/checkbox.base-ui.json +1126 -0
- package/scenarios/apg/checkbox.react-aria.json +518 -0
- package/scenarios/apg/combobox.ariakit.json +945 -0
- package/scenarios/apg/combobox.base-ui.json +2444 -0
- package/scenarios/apg/combobox.react-aria.json +845 -0
- package/scenarios/apg/dialog-modal.ariakit.json +1102 -0
- package/scenarios/apg/dialog-modal.base-ui.json +775 -0
- package/scenarios/apg/dialog-modal.react-aria.json +638 -0
- package/scenarios/apg/disclosure.apg.json +349 -0
- package/scenarios/apg/disclosure.ariakit.json +163 -0
- package/scenarios/apg/disclosure.base-ui.json +451 -0
- package/scenarios/apg/disclosure.react-aria.json +643 -0
- package/scenarios/apg/feed.apg.json +442 -0
- package/scenarios/apg/grid.ariakit.json +197 -0
- package/scenarios/apg/grid.base-ui.json +720 -0
- package/scenarios/apg/grid.react-aria.json +969 -0
- package/scenarios/apg/landmarks.ariakit.json +93 -0
- package/scenarios/apg/landmarks.react-aria.json +330 -0
- package/scenarios/apg/link.ariakit.json +83 -0
- package/scenarios/apg/link.base-ui.json +88 -0
- package/scenarios/apg/link.react-aria.json +186 -0
- package/scenarios/apg/listbox.ariakit.json +78 -0
- package/scenarios/apg/listbox.base-ui.json +163 -0
- package/scenarios/apg/listbox.react-aria.json +1118 -0
- package/scenarios/apg/menu-button.ariakit.json +924 -0
- package/scenarios/apg/menu-button.base-ui.json +1155 -0
- package/scenarios/apg/menu-button.react-aria.json +1080 -0
- package/scenarios/apg/menubar.ariakit.json +758 -0
- package/scenarios/apg/menubar.base-ui.json +673 -0
- package/scenarios/apg/meter.base-ui.json +243 -0
- package/scenarios/apg/meter.react-aria.json +201 -0
- package/scenarios/apg/radio.ariakit.json +459 -0
- package/scenarios/apg/radio.base-ui.json +581 -0
- package/scenarios/apg/radio.react-aria.json +576 -0
- package/scenarios/apg/slider-multithumb.base-ui.json +316 -0
- package/scenarios/apg/slider-multithumb.react-aria.json +363 -0
- package/scenarios/apg/slider.base-ui.json +544 -0
- package/scenarios/apg/slider.react-aria.json +456 -0
- package/scenarios/apg/spinbutton.base-ui.json +1688 -0
- package/scenarios/apg/spinbutton.react-aria.json +426 -0
- package/scenarios/apg/switch.base-ui.json +456 -0
- package/scenarios/apg/switch.react-aria.json +303 -0
- package/scenarios/apg/table.ariakit.json +65 -0
- package/scenarios/apg/table.react-aria.json +221 -0
- package/scenarios/apg/tabs.ariakit.json +329 -0
- package/scenarios/apg/tabs.base-ui.json +875 -0
- package/scenarios/apg/tabs.react-aria.json +644 -0
- package/scenarios/apg/toolbar.ariakit.json +595 -0
- package/scenarios/apg/toolbar.base-ui.json +1189 -0
- package/scenarios/apg/toolbar.react-aria.json +383 -0
- package/scenarios/apg/tooltip.ariakit.json +441 -0
- package/scenarios/apg/tooltip.base-ui.json +411 -0
- package/scenarios/apg/tooltip.react-aria.json +454 -0
- package/scenarios/apg/treegrid.apg.json +2558 -0
- package/scenarios/apg/treegrid.react-aria.json +5094 -0
- package/scenarios/apg/treeview.apg.json +928 -0
- package/scenarios/apg/windowsplitter.apg.json +557 -0
- package/scenarios/canvas/README.md +69 -0
- package/scenarios/canvas/camera.contract.json +237 -0
- package/scenarios/canvas/camera.excalidraw.json +57 -0
- package/scenarios/canvas/marquee.excalidraw.json +122 -0
- package/scenarios/canvas/pan.excalidraw.json +46 -0
- package/scenarios/canvas/pointer.contract.json +528 -0
- package/scenarios/parts.json +291 -0
- package/shared/aria.qnt +186 -0
- package/shared/details-group.qnt +50 -0
- package/shared/focusgroup.qnt +152 -0
- package/shared/invokers.qnt +81 -0
- package/shared/popover.qnt +71 -0
- package/specs/apg/STATIC.md +103 -0
- package/specs/apg/accordion.qnt +578 -0
- package/specs/apg/alert.qnt +131 -0
- package/specs/apg/alertdialog.qnt +455 -0
- package/specs/apg/breadcrumb.qnt +88 -0
- package/specs/apg/button.qnt +445 -0
- package/specs/apg/carousel.qnt +732 -0
- package/specs/apg/checkbox-group.qnt +388 -0
- package/specs/apg/checkbox.qnt +394 -0
- package/specs/apg/combobox-autocomplete.qnt +764 -0
- package/specs/apg/combobox-dialog.qnt +627 -0
- package/specs/apg/combobox-grid.qnt +632 -0
- package/specs/apg/combobox-select-only.qnt +626 -0
- package/specs/apg/combobox-tree.qnt +633 -0
- package/specs/apg/dialog-modal.qnt +499 -0
- package/specs/apg/disclosure.qnt +309 -0
- package/specs/apg/feed.qnt +485 -0
- package/specs/apg/grid-selection.qnt +512 -0
- package/specs/apg/grid-sort.qnt +330 -0
- package/specs/apg/grid.qnt +360 -0
- package/specs/apg/link.qnt +119 -0
- package/specs/apg/listbox-multi.qnt +799 -0
- package/specs/apg/listbox-rearrangeable.qnt +801 -0
- package/specs/apg/listbox-single.qnt +511 -0
- package/specs/apg/listbox-typeahead.qnt +396 -0
- package/specs/apg/menu-button.qnt +363 -0
- package/specs/apg/menu-checkable.qnt +304 -0
- package/specs/apg/menu-menubar.qnt +531 -0
- package/specs/apg/meter.qnt +129 -0
- package/specs/apg/player.qnt +1081 -0
- package/specs/apg/radio.qnt +394 -0
- package/specs/apg/slider-multithumb.qnt +441 -0
- package/specs/apg/slider.qnt +396 -0
- package/specs/apg/spinbutton.qnt +494 -0
- package/specs/apg/switch.qnt +389 -0
- package/specs/apg/tabs.qnt +412 -0
- package/specs/apg/timeline.qnt +612 -0
- package/specs/apg/toolbar.qnt +364 -0
- package/specs/apg/tooltip.qnt +553 -0
- package/specs/apg/treegrid.qnt +983 -0
- package/specs/apg/treeview.qnt +1004 -0
- package/specs/apg/windowsplitter.qnt +487 -0
- package/specs/canvas/camera.qnt +407 -0
- package/specs/canvas/pointer.qnt +841 -0
|
@@ -0,0 +1,729 @@
|
|
|
1
|
+
import { Unsupported } from "../scenario.mjs";
|
|
2
|
+
//#region src/canvas/canvas.bridge.ts
|
|
3
|
+
const specEvent = (tag, params = {}) => ({
|
|
4
|
+
_tag: tag,
|
|
5
|
+
...params
|
|
6
|
+
});
|
|
7
|
+
/**
|
|
8
|
+
* Canvas vocabulary <-> `specs/canvas/camera.qnt` and `specs/canvas/pointer.qnt`.
|
|
9
|
+
*
|
|
10
|
+
* Two machines, so two bridges (`cameraBridge`, `pointerBridge`). Both parse a scenario step into
|
|
11
|
+
* a typed, library-neutral OP (`parseCameraOp`, `parsePointerOp`); the spec side turns the op into
|
|
12
|
+
* spec events, the DOM driver (packages/headless/test) turns the same op into real input. Anything
|
|
13
|
+
* outside the model's pools throws `Unsupported`, which skips the scenario on every target.
|
|
14
|
+
*
|
|
15
|
+
* Why steps are pool-shaped: the specs pick every selector from a small fixed pool (one nondet per
|
|
16
|
+
* action), so a step may only name a pool member - a palette position `p0..p11`, a pan delta from
|
|
17
|
+
* {-9e6, -400, 0, 5, 9e6}, a zoom factor from {1/2, 3/4, 5/4, 3/2, 2, 16, 1/16}, a named anchor.
|
|
18
|
+
*
|
|
19
|
+
* Observation targets (probe / `expect.attrs`):
|
|
20
|
+
* `layer` `--canvas-x` `--canvas-y` `--canvas-zoom` the rendered camera custom properties
|
|
21
|
+
* `node<i>` `aria-selected` `--node-x` `--node-y` `content-visibility`
|
|
22
|
+
* `live` `text` the live region
|
|
23
|
+
* `state` `mode owner selection cursor editing guides culled announcements` (pointer)
|
|
24
|
+
* `camera viewport tweening reducedMotion` (camera)
|
|
25
|
+
* `state` reads the primitive's own snapshot (`Canvas.peek()`), JSON-formatted by `formatValue`.
|
|
26
|
+
*
|
|
27
|
+
* Not an oracle: `culled` is an independent TS port of the spec's `culled` def (defs are not
|
|
28
|
+
* callable from the reference model), so it checks the primitive against the port, not the spec.
|
|
29
|
+
*/
|
|
30
|
+
/** pointer.qnt's three nodes, bottom of the z-order first. */
|
|
31
|
+
const NODES = [
|
|
32
|
+
{
|
|
33
|
+
id: "0",
|
|
34
|
+
x: 100,
|
|
35
|
+
y: 100,
|
|
36
|
+
width: 200,
|
|
37
|
+
height: 100,
|
|
38
|
+
label: "Node A"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
id: "1",
|
|
42
|
+
x: 250,
|
|
43
|
+
y: 150,
|
|
44
|
+
width: 200,
|
|
45
|
+
height: 150,
|
|
46
|
+
label: "Node B"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
id: "2",
|
|
50
|
+
x: 600,
|
|
51
|
+
y: 400,
|
|
52
|
+
width: 100,
|
|
53
|
+
height: 100,
|
|
54
|
+
label: "Node C"
|
|
55
|
+
}
|
|
56
|
+
];
|
|
57
|
+
/** pointer.qnt's VW / VH: the viewport its culling predicate uses. */
|
|
58
|
+
const POINTER_VIEWPORT = {
|
|
59
|
+
width: 1024,
|
|
60
|
+
height: 768
|
|
61
|
+
};
|
|
62
|
+
/** pointer.qnt's screen palette (px): `p<k>` is `PALETTE[k]`. */
|
|
63
|
+
const PALETTE = [
|
|
64
|
+
[150, 130],
|
|
65
|
+
[152, 131],
|
|
66
|
+
[154, 130],
|
|
67
|
+
[155, 130],
|
|
68
|
+
[280, 170],
|
|
69
|
+
[400, 250],
|
|
70
|
+
[650, 450],
|
|
71
|
+
[60, 60],
|
|
72
|
+
[320, 220],
|
|
73
|
+
[500, 350],
|
|
74
|
+
[800, 600],
|
|
75
|
+
[600, 60]
|
|
76
|
+
];
|
|
77
|
+
/** pointer.qnt's `extX(k)` / `extY(k)`: the positions a host move can set (the last is far past the world bound). */
|
|
78
|
+
const HOST_POSITIONS = [
|
|
79
|
+
[0, 0],
|
|
80
|
+
[320, 200],
|
|
81
|
+
[-300, -300],
|
|
82
|
+
[5e6, -5e6]
|
|
83
|
+
];
|
|
84
|
+
/** pointer.qnt's `zoomPreset(level)`, as zoom values. */
|
|
85
|
+
const ZOOM_LEVELS = [
|
|
86
|
+
1 / 16,
|
|
87
|
+
1 / 2,
|
|
88
|
+
1,
|
|
89
|
+
2,
|
|
90
|
+
8
|
|
91
|
+
];
|
|
92
|
+
const VIEWPORTS = [
|
|
93
|
+
[320, 240],
|
|
94
|
+
[800, 600],
|
|
95
|
+
[1024, 768],
|
|
96
|
+
[1920, 1080],
|
|
97
|
+
[4096, 2160]
|
|
98
|
+
];
|
|
99
|
+
const PAN_DELTAS = [
|
|
100
|
+
-9e6,
|
|
101
|
+
-400,
|
|
102
|
+
0,
|
|
103
|
+
5,
|
|
104
|
+
9e6
|
|
105
|
+
];
|
|
106
|
+
const ZOOM_FACTORS = [
|
|
107
|
+
1 / 2,
|
|
108
|
+
3 / 4,
|
|
109
|
+
5 / 4,
|
|
110
|
+
3 / 2,
|
|
111
|
+
2,
|
|
112
|
+
16,
|
|
113
|
+
1 / 16
|
|
114
|
+
];
|
|
115
|
+
const ANCHORS = [
|
|
116
|
+
"top-left",
|
|
117
|
+
"centre",
|
|
118
|
+
"bottom-right",
|
|
119
|
+
"quarter"
|
|
120
|
+
];
|
|
121
|
+
const CENTRE_POINTS = [
|
|
122
|
+
[0, 0],
|
|
123
|
+
[1e3, 500],
|
|
124
|
+
[-25e4, 4e5],
|
|
125
|
+
[999e3, -999e3]
|
|
126
|
+
];
|
|
127
|
+
const FIXED_ONE = 4096;
|
|
128
|
+
const TONE = 256;
|
|
129
|
+
/** camera.qnt's `anchorX` / `anchorY`: where an anchor name sits in a viewport of this size. */
|
|
130
|
+
const anchorPoint = (anchor, width, height) => {
|
|
131
|
+
switch (anchor) {
|
|
132
|
+
case "top-left": return [0, 0];
|
|
133
|
+
case "centre": return [Math.floor(width / 2), Math.floor(height / 2)];
|
|
134
|
+
case "bottom-right": return [width, height];
|
|
135
|
+
case "quarter": return [Math.floor(width / 4), Math.floor(3 * height / 4)];
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
const MIN_ZOOM_GOAL = 0;
|
|
139
|
+
const MAX_ZOOM_GOAL = 1;
|
|
140
|
+
const UNIT_ZOOM_GOAL = 2;
|
|
141
|
+
const ANCHOR_COUNT = 4;
|
|
142
|
+
const PAN_POOL = 5;
|
|
143
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
144
|
+
const indexIn = (pool, value, what) => {
|
|
145
|
+
const index = pool.indexOf(value);
|
|
146
|
+
if (index < 0) throw new Unsupported(`${what} ${JSON.stringify(value)} is not in the spec pool`);
|
|
147
|
+
return index;
|
|
148
|
+
};
|
|
149
|
+
const anchorOf = (value) => {
|
|
150
|
+
const index = ANCHORS.findIndex((name) => name === value);
|
|
151
|
+
if (index < 0) throw new Unsupported(`anchor ${JSON.stringify(value)}`);
|
|
152
|
+
return index;
|
|
153
|
+
};
|
|
154
|
+
const anchorName = (index) => ANCHORS[index] ?? "top-left";
|
|
155
|
+
const zoomGoalKinds = /* @__PURE__ */ new Map([
|
|
156
|
+
["min", MIN_ZOOM_GOAL],
|
|
157
|
+
["max", MAX_ZOOM_GOAL],
|
|
158
|
+
["100%", UNIT_ZOOM_GOAL]
|
|
159
|
+
]);
|
|
160
|
+
const ZOOM_GOAL_VALUES = [
|
|
161
|
+
256,
|
|
162
|
+
32768,
|
|
163
|
+
FIXED_ONE
|
|
164
|
+
];
|
|
165
|
+
/** A camera step -> an op; `Unsupported` for anything the camera machine does not model. */
|
|
166
|
+
const parseCameraOp = (step) => {
|
|
167
|
+
if (step.event !== "prop") throw new Unsupported(`event ${step.event}`);
|
|
168
|
+
const { value } = step;
|
|
169
|
+
switch (step.prop) {
|
|
170
|
+
case "resize":
|
|
171
|
+
if (typeof value !== "number" || value < 0 || value >= VIEWPORTS.length) throw new Unsupported(`viewport ${JSON.stringify(value)}`);
|
|
172
|
+
return {
|
|
173
|
+
op: "resize",
|
|
174
|
+
viewport: value
|
|
175
|
+
};
|
|
176
|
+
case "motion": return {
|
|
177
|
+
op: "motion",
|
|
178
|
+
reduced: value === true
|
|
179
|
+
};
|
|
180
|
+
case "pan": {
|
|
181
|
+
if (!Array.isArray(value) || value.length !== 2) throw new Unsupported("pan needs [dx, dy]");
|
|
182
|
+
const [dx, dy] = value;
|
|
183
|
+
if (typeof dx !== "number" || typeof dy !== "number") throw new Unsupported("pan needs [dx, dy] numbers");
|
|
184
|
+
indexIn(PAN_DELTAS, dx, "pan dx");
|
|
185
|
+
indexIn(PAN_DELTAS, dy, "pan dy");
|
|
186
|
+
return {
|
|
187
|
+
op: "pan",
|
|
188
|
+
dx,
|
|
189
|
+
dy
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
case "zoom":
|
|
193
|
+
if (!isRecord(value) || typeof value["factor"] !== "number") throw new Unsupported("zoom needs { factor, anchor }");
|
|
194
|
+
indexIn(ZOOM_FACTORS, value["factor"], "zoom factor");
|
|
195
|
+
return {
|
|
196
|
+
op: "zoom",
|
|
197
|
+
factor: Math.round(value["factor"] * FIXED_ONE),
|
|
198
|
+
anchor: anchorName(anchorOf(value["anchor"]))
|
|
199
|
+
};
|
|
200
|
+
case "zoomLimit":
|
|
201
|
+
if (!isRecord(value) || value["limit"] !== "min" && value["limit"] !== "max") throw new Unsupported("zoomLimit needs { limit: min | max, anchor }");
|
|
202
|
+
return {
|
|
203
|
+
op: "zoomLimit",
|
|
204
|
+
limit: value["limit"],
|
|
205
|
+
anchor: anchorName(anchorOf(value["anchor"]))
|
|
206
|
+
};
|
|
207
|
+
case "tween": {
|
|
208
|
+
if (!isRecord(value)) throw new Unsupported("tween needs a goal");
|
|
209
|
+
const centre = value["centre"];
|
|
210
|
+
if (Array.isArray(centre)) {
|
|
211
|
+
const [x, y] = centre;
|
|
212
|
+
if (typeof x !== "number" || typeof y !== "number") throw new Unsupported("centre needs [x, y] numbers");
|
|
213
|
+
if (CENTRE_POINTS.findIndex((point) => point[0] === x && point[1] === y) < 0) throw new Unsupported(`centre [${x}, ${y}] is not in the spec pool`);
|
|
214
|
+
return {
|
|
215
|
+
op: "tween",
|
|
216
|
+
goal: {
|
|
217
|
+
kind: "centre",
|
|
218
|
+
x,
|
|
219
|
+
y
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
const kind = zoomGoalKinds.get(String(value["zoom"]));
|
|
224
|
+
if (kind === void 0) throw new Unsupported(`tween goal ${JSON.stringify(value)}`);
|
|
225
|
+
return {
|
|
226
|
+
op: "tween",
|
|
227
|
+
goal: {
|
|
228
|
+
kind: "zoom",
|
|
229
|
+
zoom: ZOOM_GOAL_VALUES[kind] ?? FIXED_ONE,
|
|
230
|
+
anchor: anchorName(anchorOf(value["anchor"]))
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
case "tick": return { op: "tick" };
|
|
235
|
+
case "cancelTween": return { op: "cancelTween" };
|
|
236
|
+
default: throw new Unsupported(`prop ${step.prop ?? ""}`);
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
const cameraEvents = (op) => {
|
|
240
|
+
switch (op.op) {
|
|
241
|
+
case "resize": return [specEvent("resize", { vp: BigInt(op.viewport) })];
|
|
242
|
+
case "motion": return [specEvent("set_motion", { reduced: op.reduced })];
|
|
243
|
+
case "pan": {
|
|
244
|
+
const ix = indexIn(PAN_DELTAS, op.dx, "pan dx");
|
|
245
|
+
const iy = indexIn(PAN_DELTAS, op.dy, "pan dy");
|
|
246
|
+
return [specEvent("pan_by", { delta: BigInt(iy * PAN_POOL + ix) })];
|
|
247
|
+
}
|
|
248
|
+
case "zoom": {
|
|
249
|
+
const factor = indexIn(ZOOM_FACTORS.map((f) => Math.round(f * FIXED_ONE)), op.factor, "zoom factor");
|
|
250
|
+
const anchor = ANCHORS.indexOf(op.anchor);
|
|
251
|
+
return [specEvent("zoom_at", { gesture: BigInt(anchor + ANCHOR_COUNT * factor) })];
|
|
252
|
+
}
|
|
253
|
+
case "zoomLimit": {
|
|
254
|
+
const anchor = ANCHORS.indexOf(op.anchor);
|
|
255
|
+
return [specEvent("zoom_limit", { limit: BigInt(anchor + ANCHOR_COUNT * (op.limit === "max" ? 1 : 0)) })];
|
|
256
|
+
}
|
|
257
|
+
case "tween": {
|
|
258
|
+
if (op.goal.kind === "centre") {
|
|
259
|
+
const { x, y } = op.goal;
|
|
260
|
+
const index = CENTRE_POINTS.findIndex((point) => point[0] === x && point[1] === y);
|
|
261
|
+
return [specEvent("request_tween", { goal: BigInt(12 + index) })];
|
|
262
|
+
}
|
|
263
|
+
const { zoom } = op.goal;
|
|
264
|
+
const kind = ZOOM_GOAL_VALUES.findIndex((value) => value === zoom);
|
|
265
|
+
return [specEvent("request_tween", { goal: BigInt(kind * ANCHOR_COUNT + ANCHORS.indexOf(op.goal.anchor)) })];
|
|
266
|
+
}
|
|
267
|
+
case "tick": return [specEvent("tween_tick")];
|
|
268
|
+
case "cancelTween": return [specEvent("cancel_tween")];
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
const NODE_TARGET = /^node(\d+)$/;
|
|
272
|
+
const pointerOf = (value) => {
|
|
273
|
+
if (value === void 0 || value === 1) return 1;
|
|
274
|
+
if (value === 2) return 2;
|
|
275
|
+
throw new Unsupported(`pointer id ${JSON.stringify(value)}`);
|
|
276
|
+
};
|
|
277
|
+
const paletteOf = (target) => {
|
|
278
|
+
const match = /^p(\d+)$/.exec(target ?? "");
|
|
279
|
+
const index = match === null ? -1 : Number(match[1]);
|
|
280
|
+
if (index < 0 || index >= PALETTE.length) throw new Unsupported(`palette position ${target ?? ""}`);
|
|
281
|
+
return index;
|
|
282
|
+
};
|
|
283
|
+
const parsePlace = ({ target, value }) => {
|
|
284
|
+
const match = NODE_TARGET.exec(target ?? "");
|
|
285
|
+
const node = match === null ? -1 : Number(match[1]);
|
|
286
|
+
if (node < 0 || node >= NODES.length) throw new Unsupported(`place needs a node target, got ${target ?? ""}`);
|
|
287
|
+
const position = HOST_POSITIONS.findIndex((point) => Array.isArray(value) && point[0] === value[0] && point[1] === value[1]);
|
|
288
|
+
if (position < 0) throw new Unsupported(`host position ${JSON.stringify(value)} is not in the spec pool`);
|
|
289
|
+
return {
|
|
290
|
+
op: "place",
|
|
291
|
+
node,
|
|
292
|
+
position
|
|
293
|
+
};
|
|
294
|
+
};
|
|
295
|
+
/**
|
|
296
|
+
* A pointer step -> an op. Pointer input is `{ event: pointerdown | pointermove | pointerup |
|
|
297
|
+
* pointercancel, target: "p<k>", value: <pointer id, default 1> }`; modifiers and parameters are
|
|
298
|
+
* `prop` steps (`shift`, `space`, `snap`, `marquee`, `zoom`, `edit`).
|
|
299
|
+
*/
|
|
300
|
+
const parsePointerOp = (step) => {
|
|
301
|
+
const { value } = step;
|
|
302
|
+
switch (step.event) {
|
|
303
|
+
case "pointerdown": return {
|
|
304
|
+
op: "down",
|
|
305
|
+
pointer: pointerOf(value),
|
|
306
|
+
at: paletteOf(step.target)
|
|
307
|
+
};
|
|
308
|
+
case "pointermove": return {
|
|
309
|
+
op: "move",
|
|
310
|
+
pointer: pointerOf(value),
|
|
311
|
+
at: paletteOf(step.target)
|
|
312
|
+
};
|
|
313
|
+
case "pointerup": return {
|
|
314
|
+
op: "up",
|
|
315
|
+
pointer: pointerOf(value)
|
|
316
|
+
};
|
|
317
|
+
case "pointercancel": return {
|
|
318
|
+
op: "cancel",
|
|
319
|
+
pointer: pointerOf(value)
|
|
320
|
+
};
|
|
321
|
+
case "prop": switch (step.prop) {
|
|
322
|
+
case "shift":
|
|
323
|
+
case "space":
|
|
324
|
+
case "snap": return {
|
|
325
|
+
op: step.prop,
|
|
326
|
+
on: value === true
|
|
327
|
+
};
|
|
328
|
+
case "marquee":
|
|
329
|
+
if (value === "contain" || value === "overlap") return {
|
|
330
|
+
op: "marquee",
|
|
331
|
+
mode: value
|
|
332
|
+
};
|
|
333
|
+
throw new Unsupported(`marquee ${JSON.stringify(value)}`);
|
|
334
|
+
case "zoom": {
|
|
335
|
+
const level = ZOOM_LEVELS.findIndex((zoom) => zoom === value);
|
|
336
|
+
if (level < 0) throw new Unsupported(`zoom level ${JSON.stringify(value)}`);
|
|
337
|
+
return {
|
|
338
|
+
op: "zoom",
|
|
339
|
+
level
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
case "edit": return { op: "edit" };
|
|
343
|
+
case "place": return parsePlace(step);
|
|
344
|
+
default: throw new Unsupported(`prop ${step.prop ?? ""}`);
|
|
345
|
+
}
|
|
346
|
+
default: throw new Unsupported(`event ${step.event}`);
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
const POINTER_POOL = PALETTE.length;
|
|
350
|
+
const pointerEvents = (op) => {
|
|
351
|
+
switch (op.op) {
|
|
352
|
+
case "down": return [specEvent("down", { touch: BigInt((op.pointer - 1) * POINTER_POOL + op.at) })];
|
|
353
|
+
case "move": return [specEvent("move", { touch: BigInt((op.pointer - 1) * POINTER_POOL + op.at) })];
|
|
354
|
+
case "up": return [specEvent("up", { lift: BigInt(op.pointer) })];
|
|
355
|
+
case "cancel": return [specEvent("cancel_pointer", { lost: BigInt(op.pointer) })];
|
|
356
|
+
case "shift": return [specEvent("set_shift", { held: op.on })];
|
|
357
|
+
case "space": return [specEvent("set_space", { held: op.on })];
|
|
358
|
+
case "snap": return [specEvent("set_snap", { snapping: op.on })];
|
|
359
|
+
case "marquee": return [specEvent("set_marquee_mode", { choice: op.mode })];
|
|
360
|
+
case "zoom": return [specEvent("zoom_to", { level: BigInt(op.level) })];
|
|
361
|
+
case "edit": return [specEvent("toggle_edit")];
|
|
362
|
+
case "place": return [specEvent("host_move", { place: BigInt(op.position * NODES.length + op.node) })];
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
/** Scenario `config` -> what both targets are mounted with. */
|
|
366
|
+
const cameraConfig = (config) => {
|
|
367
|
+
let reducedMotion = false;
|
|
368
|
+
for (const [key, value] of Object.entries(config)) if (key === "reducedMotion" && typeof value === "boolean") reducedMotion = value;
|
|
369
|
+
else throw new Unsupported(`config ${key}=${JSON.stringify(value)}`);
|
|
370
|
+
return { reducedMotion };
|
|
371
|
+
};
|
|
372
|
+
const pointerConfig = (config) => {
|
|
373
|
+
let marquee = "contain";
|
|
374
|
+
let snap = false;
|
|
375
|
+
for (const [key, value] of Object.entries(config)) if (key === "marquee" && (value === "contain" || value === "overlap")) marquee = value;
|
|
376
|
+
else if (key === "snap" && typeof value === "boolean") snap = value;
|
|
377
|
+
else throw new Unsupported(`config ${key}=${JSON.stringify(value)}`);
|
|
378
|
+
return {
|
|
379
|
+
marquee,
|
|
380
|
+
snap
|
|
381
|
+
};
|
|
382
|
+
};
|
|
383
|
+
/** How a `state` value is read as an attribute: a string as is, `null` = absent, the rest JSON. */
|
|
384
|
+
const formatValue = (value) => {
|
|
385
|
+
if (value === null || value === void 0) return null;
|
|
386
|
+
return typeof value === "string" ? value : JSON.stringify(value);
|
|
387
|
+
};
|
|
388
|
+
const num = (value) => Number(value);
|
|
389
|
+
const world = (fixed) => String(fixed / TONE);
|
|
390
|
+
const layerAttr = (tx, ty, z, name) => {
|
|
391
|
+
switch (name) {
|
|
392
|
+
case "--canvas-x": return world(tx);
|
|
393
|
+
case "--canvas-y": return world(ty);
|
|
394
|
+
case "--canvas-zoom": return String(z / FIXED_ONE);
|
|
395
|
+
default: throw new Unsupported(`attribute ${name} on layer`);
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
const stateAttr = (value, name) => {
|
|
399
|
+
if (!Object.hasOwn(value, name)) throw new Unsupported(`state ${name}`);
|
|
400
|
+
return formatValue(value[name]);
|
|
401
|
+
};
|
|
402
|
+
const cameraValue = (state) => ({
|
|
403
|
+
camera: {
|
|
404
|
+
tx: num(state["tx"]),
|
|
405
|
+
ty: num(state["ty"]),
|
|
406
|
+
z: num(state["z"])
|
|
407
|
+
},
|
|
408
|
+
viewport: {
|
|
409
|
+
width: num(state["vw"]),
|
|
410
|
+
height: num(state["vh"])
|
|
411
|
+
},
|
|
412
|
+
tweening: state["active"] === true,
|
|
413
|
+
reducedMotion: state["rm"] === true
|
|
414
|
+
});
|
|
415
|
+
const cameraBridge = {
|
|
416
|
+
setup: (config) => cameraConfig(config).reducedMotion ? [specEvent("set_motion", { reduced: true })] : [],
|
|
417
|
+
event: (step) => cameraEvents(parseCameraOp(step)),
|
|
418
|
+
value: cameraValue,
|
|
419
|
+
focus: () => null,
|
|
420
|
+
attr: (state, target, name) => {
|
|
421
|
+
if (target === "layer") return layerAttr(num(state["tx"]), num(state["ty"]), num(state["z"]), name);
|
|
422
|
+
if (target === "state") return stateAttr(cameraValue(state), name);
|
|
423
|
+
throw new Unsupported(`attribute ${name} on ${target}`);
|
|
424
|
+
},
|
|
425
|
+
visible: () => [],
|
|
426
|
+
events: () => []
|
|
427
|
+
};
|
|
428
|
+
const bitSet = (mask, bit) => Math.floor(mask / 2 ** bit) % 2 === 1;
|
|
429
|
+
const maskIds = (mask) => NODES.flatMap((node, bit) => bitSet(mask, bit) ? [node.id] : []);
|
|
430
|
+
const nodePosition = (state, index) => {
|
|
431
|
+
const dragging = state["mode"] === "dragging" && bitSet(num(state["sel"]), index);
|
|
432
|
+
const dx = dragging ? num(state["ddx"]) : 0;
|
|
433
|
+
const dy = dragging ? num(state["ddy"]) : 0;
|
|
434
|
+
return [num(state[`n${index}x`]) + dx, num(state[`n${index}y`]) + dy];
|
|
435
|
+
};
|
|
436
|
+
const exempt = (state, index) => bitSet(num(state["sel"]), index) || num(state["cursor"]) === index || num(state["editing"]) === index;
|
|
437
|
+
const screenToWorldFp = (s, t, z) => {
|
|
438
|
+
const tz = FIXED_ONE * TONE;
|
|
439
|
+
return Math.floor((2 * s * tz + z) / (2 * z)) - t;
|
|
440
|
+
};
|
|
441
|
+
/** Independent port of pointer.qnt's `inView` / `culled`. */
|
|
442
|
+
const culledNodes = (state) => {
|
|
443
|
+
const tx = num(state["tx"]);
|
|
444
|
+
const ty = num(state["ty"]);
|
|
445
|
+
const z = num(state["z"]);
|
|
446
|
+
const left = screenToWorldFp(0, tx, z);
|
|
447
|
+
const right = screenToWorldFp(POINTER_VIEWPORT.width, tx, z);
|
|
448
|
+
const top = screenToWorldFp(0, ty, z);
|
|
449
|
+
const bottom = screenToWorldFp(POINTER_VIEWPORT.height, ty, z);
|
|
450
|
+
return NODES.flatMap((node, index) => {
|
|
451
|
+
const [x, y] = nodePosition(state, index);
|
|
452
|
+
return x * TONE < right && (x + node.width) * TONE > left && y * TONE < bottom && (y + node.height) * TONE > top || exempt(state, index) ? [] : [node.id];
|
|
453
|
+
});
|
|
454
|
+
};
|
|
455
|
+
const idOf = (index) => NODES[index]?.id ?? null;
|
|
456
|
+
const pointerValue = (state) => ({
|
|
457
|
+
mode: String(state["mode"]),
|
|
458
|
+
owner: num(state["owner"]) === 0 ? null : num(state["owner"]),
|
|
459
|
+
selection: maskIds(num(state["sel"])),
|
|
460
|
+
cursor: idOf(num(state["cursor"])),
|
|
461
|
+
editing: idOf(num(state["editing"])),
|
|
462
|
+
guides: {
|
|
463
|
+
x: bitSet(num(state["guide"]), 0),
|
|
464
|
+
y: bitSet(num(state["guide"]), 1)
|
|
465
|
+
},
|
|
466
|
+
culled: culledNodes(state),
|
|
467
|
+
announcements: num(state["annCount"])
|
|
468
|
+
});
|
|
469
|
+
/** The text the primitive's default messages give a settled selection. */
|
|
470
|
+
const selectionText = (ids) => {
|
|
471
|
+
const labels = ids.map((id) => NODES.find((node) => node.id === id)?.label ?? id);
|
|
472
|
+
if (labels.length === 0) return "Selection cleared";
|
|
473
|
+
return labels.length === 1 ? `${labels[0] ?? ""} selected` : `${labels.length} nodes selected: ${labels.join(", ")}`;
|
|
474
|
+
};
|
|
475
|
+
const pointerBridge = {
|
|
476
|
+
setup: (config) => {
|
|
477
|
+
const { marquee, snap } = pointerConfig(config);
|
|
478
|
+
return [...marquee === "contain" ? [] : [specEvent("set_marquee_mode", { choice: marquee })], ...snap ? [specEvent("set_snap", { snapping: true })] : []];
|
|
479
|
+
},
|
|
480
|
+
event: (step) => pointerEvents(parsePointerOp(step)),
|
|
481
|
+
value: pointerValue,
|
|
482
|
+
focus: (state) => {
|
|
483
|
+
const cursor = num(state["cursor"]);
|
|
484
|
+
return cursor < 0 ? null : `node${cursor}`;
|
|
485
|
+
},
|
|
486
|
+
attr: (state, target, name) => {
|
|
487
|
+
if (target === "layer") return layerAttr(num(state["tx"]), num(state["ty"]), num(state["z"]), name);
|
|
488
|
+
if (target === "state") return stateAttr(pointerValue(state), name);
|
|
489
|
+
if (target === "live" && name === "text") return num(state["annCount"]) === 0 ? "" : selectionText(maskIds(num(state["annSel"])));
|
|
490
|
+
const match = NODE_TARGET.exec(target);
|
|
491
|
+
const index = match === null ? -1 : Number(match[1]);
|
|
492
|
+
if (index >= 0 && index < NODES.length) {
|
|
493
|
+
const [x, y] = nodePosition(state, index);
|
|
494
|
+
switch (name) {
|
|
495
|
+
case "aria-selected": return String(bitSet(num(state["sel"]), index));
|
|
496
|
+
case "--node-x": return String(x);
|
|
497
|
+
case "--node-y": return String(y);
|
|
498
|
+
case "content-visibility": return exempt(state, index) ? "visible" : "auto";
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
throw new Unsupported(`attribute ${name} on ${target}`);
|
|
502
|
+
},
|
|
503
|
+
visible: () => [],
|
|
504
|
+
events: (before, after) => {
|
|
505
|
+
const out = [];
|
|
506
|
+
if (num(before["annCount"]) !== num(after["annCount"])) out.push(["onSelectionChange", maskIds(num(after["annSel"]))]);
|
|
507
|
+
const moves = NODES.flatMap((node, index) => {
|
|
508
|
+
const [bx, by] = [num(before[`n${index}x`]), num(before[`n${index}y`])];
|
|
509
|
+
const [ax, ay] = [num(after[`n${index}x`]), num(after[`n${index}y`])];
|
|
510
|
+
return bx === ax && by === ay ? [] : [{
|
|
511
|
+
id: node.id,
|
|
512
|
+
x: ax,
|
|
513
|
+
y: ay
|
|
514
|
+
}];
|
|
515
|
+
});
|
|
516
|
+
if (moves.length > 0) out.push(["onNodesMove", moves]);
|
|
517
|
+
return out;
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
const paletteIndexes = PALETTE.map((_, index) => index);
|
|
521
|
+
const press = (event, at, pointer) => ({
|
|
522
|
+
event,
|
|
523
|
+
target: `p${at}`,
|
|
524
|
+
value: pointer
|
|
525
|
+
});
|
|
526
|
+
/** The user-level steps a pointer differential walk draws from. */
|
|
527
|
+
const pointerSteps = [
|
|
528
|
+
...paletteIndexes.map((at) => press("pointerdown", at, 1)),
|
|
529
|
+
...paletteIndexes.flatMap((at) => [press("pointermove", at, 1), press("pointermove", at, 1)]),
|
|
530
|
+
press("pointerdown", 4, 2),
|
|
531
|
+
press("pointerdown", 7, 2),
|
|
532
|
+
press("pointermove", 9, 2),
|
|
533
|
+
press("pointermove", 5, 2),
|
|
534
|
+
{
|
|
535
|
+
event: "pointerup",
|
|
536
|
+
value: 1
|
|
537
|
+
},
|
|
538
|
+
{
|
|
539
|
+
event: "pointerup",
|
|
540
|
+
value: 1
|
|
541
|
+
},
|
|
542
|
+
{
|
|
543
|
+
event: "pointerup",
|
|
544
|
+
value: 2
|
|
545
|
+
},
|
|
546
|
+
{
|
|
547
|
+
event: "pointercancel",
|
|
548
|
+
value: 1
|
|
549
|
+
},
|
|
550
|
+
{
|
|
551
|
+
event: "pointercancel",
|
|
552
|
+
value: 2
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
event: "prop",
|
|
556
|
+
prop: "shift",
|
|
557
|
+
value: true
|
|
558
|
+
},
|
|
559
|
+
{
|
|
560
|
+
event: "prop",
|
|
561
|
+
prop: "shift",
|
|
562
|
+
value: false
|
|
563
|
+
},
|
|
564
|
+
{
|
|
565
|
+
event: "prop",
|
|
566
|
+
prop: "space",
|
|
567
|
+
value: true
|
|
568
|
+
},
|
|
569
|
+
{
|
|
570
|
+
event: "prop",
|
|
571
|
+
prop: "space",
|
|
572
|
+
value: false
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
event: "prop",
|
|
576
|
+
prop: "snap",
|
|
577
|
+
value: true
|
|
578
|
+
},
|
|
579
|
+
{
|
|
580
|
+
event: "prop",
|
|
581
|
+
prop: "snap",
|
|
582
|
+
value: false
|
|
583
|
+
},
|
|
584
|
+
{
|
|
585
|
+
event: "prop",
|
|
586
|
+
prop: "marquee",
|
|
587
|
+
value: "contain"
|
|
588
|
+
},
|
|
589
|
+
{
|
|
590
|
+
event: "prop",
|
|
591
|
+
prop: "marquee",
|
|
592
|
+
value: "overlap"
|
|
593
|
+
},
|
|
594
|
+
...ZOOM_LEVELS.map((value) => ({
|
|
595
|
+
event: "prop",
|
|
596
|
+
prop: "zoom",
|
|
597
|
+
value
|
|
598
|
+
})),
|
|
599
|
+
{
|
|
600
|
+
event: "prop",
|
|
601
|
+
prop: "edit"
|
|
602
|
+
},
|
|
603
|
+
...NODES.flatMap((_, node) => HOST_POSITIONS.map(([x, y]) => ({
|
|
604
|
+
event: "prop",
|
|
605
|
+
prop: "place",
|
|
606
|
+
target: `node${node}`,
|
|
607
|
+
value: [x, y]
|
|
608
|
+
})))
|
|
609
|
+
];
|
|
610
|
+
const pointerProbe = {
|
|
611
|
+
attrs: [
|
|
612
|
+
["layer", "--canvas-x"],
|
|
613
|
+
["layer", "--canvas-y"],
|
|
614
|
+
["layer", "--canvas-zoom"],
|
|
615
|
+
["state", "mode"],
|
|
616
|
+
["state", "owner"],
|
|
617
|
+
["state", "selection"],
|
|
618
|
+
["state", "cursor"],
|
|
619
|
+
["state", "editing"],
|
|
620
|
+
["state", "guides"],
|
|
621
|
+
["state", "culled"],
|
|
622
|
+
["state", "announcements"],
|
|
623
|
+
["live", "text"],
|
|
624
|
+
...NODES.flatMap((_, index) => [
|
|
625
|
+
[`node${index}`, "aria-selected"],
|
|
626
|
+
[`node${index}`, "--node-x"],
|
|
627
|
+
[`node${index}`, "--node-y"],
|
|
628
|
+
[`node${index}`, "content-visibility"]
|
|
629
|
+
])
|
|
630
|
+
],
|
|
631
|
+
events: ["onSelectionChange", "onNodesMove"]
|
|
632
|
+
};
|
|
633
|
+
const anchorSteps = ANCHORS.map((anchor) => anchor);
|
|
634
|
+
/** The user-level steps a camera differential walk draws from. */
|
|
635
|
+
const cameraSteps = [
|
|
636
|
+
...VIEWPORTS.map((_, value) => ({
|
|
637
|
+
event: "prop",
|
|
638
|
+
prop: "resize",
|
|
639
|
+
value
|
|
640
|
+
})),
|
|
641
|
+
{
|
|
642
|
+
event: "prop",
|
|
643
|
+
prop: "motion",
|
|
644
|
+
value: true
|
|
645
|
+
},
|
|
646
|
+
{
|
|
647
|
+
event: "prop",
|
|
648
|
+
prop: "motion",
|
|
649
|
+
value: false
|
|
650
|
+
},
|
|
651
|
+
...PAN_DELTAS.flatMap((dx) => [
|
|
652
|
+
-400,
|
|
653
|
+
5,
|
|
654
|
+
9e6
|
|
655
|
+
].map((dy) => ({
|
|
656
|
+
event: "prop",
|
|
657
|
+
prop: "pan",
|
|
658
|
+
value: [dx, dy]
|
|
659
|
+
}))),
|
|
660
|
+
...ZOOM_FACTORS.flatMap((factor) => anchorSteps.map((anchor) => ({
|
|
661
|
+
event: "prop",
|
|
662
|
+
prop: "zoom",
|
|
663
|
+
value: {
|
|
664
|
+
factor,
|
|
665
|
+
anchor
|
|
666
|
+
}
|
|
667
|
+
}))),
|
|
668
|
+
...anchorSteps.flatMap((anchor) => [
|
|
669
|
+
{
|
|
670
|
+
event: "prop",
|
|
671
|
+
prop: "zoomLimit",
|
|
672
|
+
value: {
|
|
673
|
+
limit: "min",
|
|
674
|
+
anchor
|
|
675
|
+
}
|
|
676
|
+
},
|
|
677
|
+
{
|
|
678
|
+
event: "prop",
|
|
679
|
+
prop: "zoomLimit",
|
|
680
|
+
value: {
|
|
681
|
+
limit: "max",
|
|
682
|
+
anchor
|
|
683
|
+
}
|
|
684
|
+
},
|
|
685
|
+
{
|
|
686
|
+
event: "prop",
|
|
687
|
+
prop: "tween",
|
|
688
|
+
value: {
|
|
689
|
+
zoom: "100%",
|
|
690
|
+
anchor
|
|
691
|
+
}
|
|
692
|
+
},
|
|
693
|
+
{
|
|
694
|
+
event: "prop",
|
|
695
|
+
prop: "tween",
|
|
696
|
+
value: {
|
|
697
|
+
zoom: "max",
|
|
698
|
+
anchor
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
]),
|
|
702
|
+
...CENTRE_POINTS.map(([x, y]) => ({
|
|
703
|
+
event: "prop",
|
|
704
|
+
prop: "tween",
|
|
705
|
+
value: { centre: [x, y] }
|
|
706
|
+
})),
|
|
707
|
+
...Array.from({ length: 12 }, () => ({
|
|
708
|
+
event: "prop",
|
|
709
|
+
prop: "tick"
|
|
710
|
+
})),
|
|
711
|
+
{
|
|
712
|
+
event: "prop",
|
|
713
|
+
prop: "cancelTween"
|
|
714
|
+
}
|
|
715
|
+
];
|
|
716
|
+
const cameraProbe = {
|
|
717
|
+
attrs: [
|
|
718
|
+
["layer", "--canvas-x"],
|
|
719
|
+
["layer", "--canvas-y"],
|
|
720
|
+
["layer", "--canvas-zoom"],
|
|
721
|
+
["state", "camera"],
|
|
722
|
+
["state", "viewport"],
|
|
723
|
+
["state", "tweening"],
|
|
724
|
+
["state", "reducedMotion"]
|
|
725
|
+
],
|
|
726
|
+
events: []
|
|
727
|
+
};
|
|
728
|
+
//#endregion
|
|
729
|
+
export { ANCHORS, CENTRE_POINTS, HOST_POSITIONS, NODES, PALETTE, PAN_DELTAS, POINTER_VIEWPORT, VIEWPORTS, ZOOM_FACTORS, ZOOM_LEVELS, anchorPoint, cameraBridge, cameraConfig, cameraProbe, cameraSteps, cameraValue, formatValue, parseCameraOp, parsePointerOp, pointerBridge, pointerConfig, pointerProbe, pointerSteps, pointerValue, selectionText };
|