@weasel-js/labkit 0.7.2 → 1.0.0
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 +11 -3
- package/dist/_dts/{DrawCommand-CEvS5AdV.d.ts → DrawCommand-BwRCZYuo.d.ts} +106 -52
- package/dist/_dts/{useExperimentState-eSvpwV_P.d.ts → useExperimentState-CJn2hHzd.d.ts} +6 -4
- package/dist/{chunk-SSMHPMDV.js → chunk-53XSBIUK.js} +12 -11
- package/dist/chunk-53XSBIUK.js.map +1 -0
- package/dist/{chunk-BG6NLMOL.js → chunk-FP5LYDVX.js} +1229 -1227
- package/dist/chunk-FP5LYDVX.js.map +1 -0
- package/dist/{chunk-ERZQAU4N.js → chunk-HNXVLKBG.js} +3 -3
- package/dist/{chunk-ERZQAU4N.js.map → chunk-HNXVLKBG.js.map} +1 -1
- package/dist/{chunk-5XDFILHZ.js → chunk-XVBXYKFJ.js} +1675 -670
- package/dist/chunk-XVBXYKFJ.js.map +1 -0
- package/dist/fonts/inter-latin.woff2 +0 -0
- package/dist/fonts/oswald-latin-variable.woff2 +0 -0
- package/dist/index.d.ts +37 -13
- package/dist/index.js +776 -27
- package/dist/index.js.map +1 -1
- package/dist/passthrough/weasel-canvas.d.ts +1 -1
- package/dist/passthrough/weasel-canvas.js +1 -1
- package/dist/passthrough/weasel-ui.d.ts +332 -189
- package/dist/passthrough/weasel-ui.js +2 -2
- package/dist/state/index.d.ts +2 -2
- package/dist/state/index.js +2 -2
- package/dist/styles.css +229 -288
- package/dist/ui/layers/index.js +3 -3
- package/package.json +5 -6
- package/src/canvas/CanvasStack.less +1 -1
- package/src/controls/ControlPanel.less +12 -12
- package/src/dragdrop/DragGhost.less +2 -2
- package/src/dragdrop/Palette.less +6 -6
- package/src/index.ts +2 -0
- package/src/lab/Lab.test.tsx +20 -15
- package/src/lab/Lab.tsx +33 -32
- package/src/lab/LabContext.ts +3 -3
- package/src/lab/LabShell.less +14 -14
- package/src/lab/LabShell.test.tsx +7 -20
- package/src/lab/LabShell.tsx +22 -13
- package/src/lab/WorkspaceGrid.less +1 -1
- package/src/lab/WorkspaceGrid.stories.tsx +4 -4
- package/src/lab/index.ts +1 -1
- package/src/lab/useSystemMode.ts +30 -0
- package/src/layers/LayerList.less +9 -9
- package/src/primitives/FpsMeter.less +5 -5
- package/src/primitives/ScaleIndicator.less +7 -7
- package/src/primitives/Sidebar.less +12 -12
- package/src/primitives/StatusBar.less +10 -10
- package/src/primitives/Toolbar.less +16 -16
- package/src/state/store.test.ts +17 -9
- package/src/state/store.ts +13 -10
- package/src/state/types.ts +5 -2
- package/src/theme/Interstellar.stories.less +20 -20
- package/src/theme/Interstellar.stories.tsx +44 -59
- package/src/theme/base.less +29 -28
- package/src/theme/interstellar.test.ts +30 -0
- package/src/theme/interstellar.tokens.json +57 -0
- package/src/theme/interstellar.ts +9 -0
- package/src/ui/layers/LayerStack.less +21 -21
- package/src/ui/properties/CurveField.less +5 -5
- package/src/ui/properties/EffectCard.tsx +2 -2
- package/src/ui/properties/PropertyGroup.less +6 -6
- package/src/ui/properties/PropertyPanel.less +70 -70
- package/src/ui/properties/SpeechBalloonPanels.stories.tsx +1 -1
- package/src/ui/properties/storyLayouts.tsx +1 -1
- package/src/workspace/Workspace.less +14 -14
- package/src/workspace/Workspace.stories.tsx +2 -2
- package/src/workspace/Workspace.test.tsx +2 -2
- package/dist/chunk-5XDFILHZ.js.map +0 -1
- package/dist/chunk-BG6NLMOL.js.map +0 -1
- package/dist/chunk-SSMHPMDV.js.map +0 -1
- package/dist/theme-interstellar.css +0 -25
- package/dist/theme-light.css +0 -13
- package/src/fonts/oswald-latin-variable.woff2 +0 -0
- package/src/theme/interstellar.less +0 -34
- package/src/theme/light.less +0 -14
- package/src/theme/tokens.less +0 -85
|
@@ -16,11 +16,12 @@ var GESTURE_DESCRIPTORS = [
|
|
|
16
16
|
{ name: "pointerDown", hasTarget: true },
|
|
17
17
|
{ name: "dblTap", hasTarget: true },
|
|
18
18
|
{ name: "drag", hasTarget: true },
|
|
19
|
-
{ name: "wheel", hasTarget:
|
|
19
|
+
{ name: "wheel", hasTarget: true, arg: { name: "direction", values: ["up", "down", "*"], default: "*" } },
|
|
20
20
|
{ name: "keyDown", hasTarget: false, arg: { name: "key", values: "free" } },
|
|
21
21
|
{ name: "keyUp", hasTarget: false, arg: { name: "key", values: "free" } },
|
|
22
22
|
{ name: "keyHeld", hasTarget: false, arg: { name: "key", values: "free" } },
|
|
23
23
|
{ name: "contextMenu", hasTarget: true },
|
|
24
|
+
{ name: "longPress", hasTarget: true },
|
|
24
25
|
{ name: "multiTouchTap", hasTarget: false, arg: { name: "fingers", values: ["2", "3", "4"] } }
|
|
25
26
|
];
|
|
26
27
|
var BY_NAME = new Map(
|
|
@@ -318,6 +319,8 @@ function actionClause(parsed, required) {
|
|
|
318
319
|
return `the user taps with ${parsed.arg ?? "multiple"} fingers`;
|
|
319
320
|
case "contextMenu":
|
|
320
321
|
return `the user ${modPrefix}opens the context menu${target}`;
|
|
322
|
+
case "longPress":
|
|
323
|
+
return `the user ${modPrefix}presses and holds${target}`;
|
|
321
324
|
case "click":
|
|
322
325
|
return `the user ${modPrefix}clicks${target}`;
|
|
323
326
|
case "pointerDown":
|
|
@@ -385,12 +388,12 @@ function checkKey(req, held) {
|
|
|
385
388
|
if (req === "forbidden") return !held;
|
|
386
389
|
return true;
|
|
387
390
|
}
|
|
388
|
-
function matchKey(eventKey,
|
|
391
|
+
function matchKey(eventKey, specKey2) {
|
|
389
392
|
const lower = eventKey.toLowerCase();
|
|
390
|
-
if (Array.isArray(
|
|
391
|
-
return
|
|
393
|
+
if (Array.isArray(specKey2)) {
|
|
394
|
+
return specKey2.some((k) => k.toLowerCase() === lower);
|
|
392
395
|
}
|
|
393
|
-
return
|
|
396
|
+
return specKey2.toLowerCase() === lower;
|
|
394
397
|
}
|
|
395
398
|
function affordanceKindOf(target) {
|
|
396
399
|
if (typeof target !== "object" || target === null) return void 0;
|
|
@@ -485,7 +488,7 @@ function matchSpec(e, spec, isMac, phaseCtx) {
|
|
|
485
488
|
const direction = spec.direction ?? "*";
|
|
486
489
|
if (direction === "up" && !(e.deltaY < 0)) return false;
|
|
487
490
|
if (direction === "down" && !(e.deltaY > 0)) return false;
|
|
488
|
-
return
|
|
491
|
+
return matchTarget(e.affordance, spec.target, e.bodyTarget, e.bodyKind);
|
|
489
492
|
}
|
|
490
493
|
case "click": {
|
|
491
494
|
if (e.kind !== "click") return false;
|
|
@@ -495,12 +498,17 @@ function matchSpec(e, spec, isMac, phaseCtx) {
|
|
|
495
498
|
case "doubleClick": {
|
|
496
499
|
if (e.kind !== "doubleclick") return false;
|
|
497
500
|
if (!matchModifiers(e, spec.mods, isMac)) return false;
|
|
498
|
-
return matchTarget(e.
|
|
501
|
+
return matchTarget(e.affordance, spec.target, e.bodyTarget, e.bodyKind);
|
|
499
502
|
}
|
|
500
503
|
case "contextMenu": {
|
|
501
504
|
if (e.kind !== "contextmenu") return false;
|
|
502
505
|
if (!matchModifiers(e, spec.mods, isMac)) return false;
|
|
503
|
-
return matchTarget(e.
|
|
506
|
+
return matchTarget(e.affordance, spec.target, e.bodyTarget, e.bodyKind);
|
|
507
|
+
}
|
|
508
|
+
case "longPress": {
|
|
509
|
+
if (e.kind !== "longpress") return false;
|
|
510
|
+
if (!matchModifiers(e, spec.mods, isMac)) return false;
|
|
511
|
+
return matchTarget(e.affordance, spec.target, e.bodyTarget, e.bodyKind);
|
|
504
512
|
}
|
|
505
513
|
case "drag": {
|
|
506
514
|
if (e.kind !== "pointerdown") return false;
|
|
@@ -539,8 +547,6 @@ function matchSpec(e, spec, isMac, phaseCtx) {
|
|
|
539
547
|
}
|
|
540
548
|
}
|
|
541
549
|
var EMPTY_ENGAGED = /* @__PURE__ */ new Set();
|
|
542
|
-
|
|
543
|
-
// ../core/dist/chunk-SYM6RAM4.js
|
|
544
550
|
var routing_exports = {};
|
|
545
551
|
__export(routing_exports, {
|
|
546
552
|
GESTURE_DESCRIPTORS: () => GESTURE_DESCRIPTORS,
|
|
@@ -589,7 +595,11 @@ function defineTool(def) {
|
|
|
589
595
|
};
|
|
590
596
|
return {
|
|
591
597
|
id: def.id,
|
|
592
|
-
|
|
598
|
+
eligibility: {
|
|
599
|
+
focus: true,
|
|
600
|
+
capabilities: def.capabilities,
|
|
601
|
+
...def.hotkey ? { offhand: def.hotkey } : {}
|
|
602
|
+
},
|
|
593
603
|
actions: def.actions,
|
|
594
604
|
def,
|
|
595
605
|
presentation: def.presentation,
|
|
@@ -613,6 +623,7 @@ var SPEC_KIND_TO_GESTURE = {
|
|
|
613
623
|
click: "click",
|
|
614
624
|
doubleClick: "dblTap",
|
|
615
625
|
contextMenu: "contextMenu",
|
|
626
|
+
longPress: "longPress",
|
|
616
627
|
drag: "drag",
|
|
617
628
|
pointerDown: "pointerDown",
|
|
618
629
|
multiTouch: void 0,
|
|
@@ -674,6 +685,255 @@ function argOf(spec, fallback) {
|
|
|
674
685
|
if ("direction" in spec) return spec.direction ?? fallback;
|
|
675
686
|
return fallback;
|
|
676
687
|
}
|
|
688
|
+
var DepRegistryContext = createContext(null);
|
|
689
|
+
function DepRegistryProvider({ children }) {
|
|
690
|
+
const sourcesRef = useRef(/* @__PURE__ */ new Map());
|
|
691
|
+
const registry4 = useMemo(() => ({
|
|
692
|
+
register: (name, source) => {
|
|
693
|
+
sourcesRef.current.set(name, source);
|
|
694
|
+
return () => {
|
|
695
|
+
sourcesRef.current.delete(name);
|
|
696
|
+
};
|
|
697
|
+
},
|
|
698
|
+
get: (name) => sourcesRef.current.get(name)?.()
|
|
699
|
+
}), []);
|
|
700
|
+
return /* @__PURE__ */ jsx(DepRegistryContext.Provider, { value: registry4, children });
|
|
701
|
+
}
|
|
702
|
+
function useDepRegistry() {
|
|
703
|
+
const r = useContext(DepRegistryContext);
|
|
704
|
+
if (r === null) {
|
|
705
|
+
throw new Error("useDepRegistry: no DepRegistryProvider in scope. Wrap your tree with <DepRegistryProvider> (typically inside <SceneCanvas>).");
|
|
706
|
+
}
|
|
707
|
+
return r;
|
|
708
|
+
}
|
|
709
|
+
function useOptionalDepRegistry() {
|
|
710
|
+
return useContext(DepRegistryContext);
|
|
711
|
+
}
|
|
712
|
+
function useDepSource(name, source) {
|
|
713
|
+
const registry4 = useDepRegistry();
|
|
714
|
+
const sourceRef = useRef(source);
|
|
715
|
+
sourceRef.current = source;
|
|
716
|
+
useEffect(() => {
|
|
717
|
+
return registry4.register(name, () => sourceRef.current());
|
|
718
|
+
}, [name, registry4]);
|
|
719
|
+
}
|
|
720
|
+
var DEV = (() => {
|
|
721
|
+
try {
|
|
722
|
+
return Boolean(import.meta.env?.DEV);
|
|
723
|
+
} catch {
|
|
724
|
+
return false;
|
|
725
|
+
}
|
|
726
|
+
})();
|
|
727
|
+
function buildDepsFromRequires(action, depRegistry) {
|
|
728
|
+
const requires = action.requires ?? [];
|
|
729
|
+
const entries = requires.map((name) => [name, depRegistry.get(name)]);
|
|
730
|
+
const deps = Object.fromEntries(entries);
|
|
731
|
+
if (DEV) {
|
|
732
|
+
const declared = new Set(requires);
|
|
733
|
+
return new Proxy(deps, {
|
|
734
|
+
get(target, prop, receiver) {
|
|
735
|
+
if (typeof prop === "string" && !declared.has(prop) && prop !== "then") {
|
|
736
|
+
const message = `[weasel:dispatcher] action "${action.id}" read deps.${prop} but did not declare it in \`requires\`. Add \`requires: [...'${prop}']\` to the descriptor or the dep will be undefined at runtime.`;
|
|
737
|
+
console.warn(message);
|
|
738
|
+
throw new Error(message);
|
|
739
|
+
}
|
|
740
|
+
return Reflect.get(target, prop, receiver);
|
|
741
|
+
}
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
return deps;
|
|
745
|
+
}
|
|
746
|
+
function actionBindings(action) {
|
|
747
|
+
const gs = action.defaultBinding;
|
|
748
|
+
if (!gs) return [];
|
|
749
|
+
const raw = Array.isArray(gs) ? gs : [gs];
|
|
750
|
+
return raw.map((entry) => {
|
|
751
|
+
const isBoundObj = !("kind" in entry);
|
|
752
|
+
const spec = isBoundObj ? entry.spec : entry;
|
|
753
|
+
const opts = isBoundObj ? entry.opts : void 0;
|
|
754
|
+
return { spec, actionId: action.id, ...opts !== void 0 ? { opts } : {} };
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
var ActionDisabledReason = {
|
|
758
|
+
SelectionRequired: "selection-required",
|
|
759
|
+
NotApplicable: "not-applicable",
|
|
760
|
+
/** Sentinel: the predicate threw. Surfaced by `evaluateEnabled`'s catch. */
|
|
761
|
+
PredicateThrew: "predicate-threw"
|
|
762
|
+
};
|
|
763
|
+
var enabledSlowWarned = /* @__PURE__ */ new Set();
|
|
764
|
+
var enabledThrewWarned = /* @__PURE__ */ new Set();
|
|
765
|
+
var ENABLED_BUDGET_MS = 4;
|
|
766
|
+
function evaluateEnabled(action, deps) {
|
|
767
|
+
if (!action.enabled) return { enabled: true };
|
|
768
|
+
const isDev = typeof process !== "undefined" ? process.env.NODE_ENV !== "production" : true;
|
|
769
|
+
const now = typeof performance !== "undefined" && typeof performance.now === "function" ? () => performance.now() : () => Date.now();
|
|
770
|
+
const start = isDev ? now() : 0;
|
|
771
|
+
try {
|
|
772
|
+
const result = action.enabled(deps);
|
|
773
|
+
if (isDev) {
|
|
774
|
+
const elapsed = now() - start;
|
|
775
|
+
if (elapsed > ENABLED_BUDGET_MS && !enabledSlowWarned.has(action.id)) {
|
|
776
|
+
enabledSlowWarned.add(action.id);
|
|
777
|
+
console.warn(
|
|
778
|
+
`weasel actions: enabled() for action "${action.id}" took ${elapsed.toFixed(2)}ms (budget ${ENABLED_BUDGET_MS}ms). The predicate must be pure and fast \u2014 see Action.enabled JSDoc.`
|
|
779
|
+
);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
if (result === true) return { enabled: true };
|
|
783
|
+
return { enabled: false, reason: result };
|
|
784
|
+
} catch (e) {
|
|
785
|
+
if (isDev && !enabledThrewWarned.has(action.id)) {
|
|
786
|
+
enabledThrewWarned.add(action.id);
|
|
787
|
+
console.warn(
|
|
788
|
+
`weasel actions: enabled() for action "${action.id}" threw; treating as disabled. The predicate must not throw \u2014 see Action.enabled JSDoc.`,
|
|
789
|
+
e
|
|
790
|
+
);
|
|
791
|
+
}
|
|
792
|
+
return { enabled: false, reason: ActionDisabledReason.PredicateThrew };
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
function validateActionId(id) {
|
|
796
|
+
if (id.length === 0) {
|
|
797
|
+
throw new Error(`weasel: action id may not be empty`);
|
|
798
|
+
}
|
|
799
|
+
if (RESERVED_ID_PREFIXES.has(id[0])) {
|
|
800
|
+
throw new Error(
|
|
801
|
+
`weasel: action id "${id}" starts with reserved sigil "${id[0]}" (reserved set: ${[...RESERVED_ID_PREFIXES].join(" ")})`
|
|
802
|
+
);
|
|
803
|
+
}
|
|
804
|
+
if (RESERVED_ID_NAMES.has(id)) {
|
|
805
|
+
throw new Error(
|
|
806
|
+
`weasel: action id "${id}" collides with a reserved phase keyword (reserved: ${[...RESERVED_ID_NAMES].join(", ")})`
|
|
807
|
+
);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
function validateActionDefaultBinding(action) {
|
|
811
|
+
const gs = action.defaultBinding;
|
|
812
|
+
if (!gs) return;
|
|
813
|
+
const entries = Array.isArray(gs) ? gs : [gs];
|
|
814
|
+
for (const entry of entries) {
|
|
815
|
+
const spec = "kind" in entry ? entry : entry.spec;
|
|
816
|
+
const phase = spec.phase;
|
|
817
|
+
if (phase === void 0) continue;
|
|
818
|
+
const atoms = Array.isArray(phase) ? phase : [{ channel: "&", phase }];
|
|
819
|
+
for (const atom of atoms) {
|
|
820
|
+
if (atom.channel === "&") {
|
|
821
|
+
throw new Error(
|
|
822
|
+
`weasel: action "${action.id}" defaultBinding uses '&' channel which has no owning tool to resolve to. Use a named tool channel (e.g. '[rect:engaged]') or '*' instead, or move this binding to the tool's own Tool.bindings.`
|
|
823
|
+
);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
var ActionsContext = createContext(null);
|
|
829
|
+
function ActionsProvider({ children }) {
|
|
830
|
+
const actionsRef = useRef(/* @__PURE__ */ new Map());
|
|
831
|
+
const versionRef = useRef(0);
|
|
832
|
+
const cachedRef = useRef([]);
|
|
833
|
+
const cachedVerRef = useRef(-1);
|
|
834
|
+
const listenersRef = useRef(/* @__PURE__ */ new Set());
|
|
835
|
+
const depReg = useOptionalDepRegistry();
|
|
836
|
+
const depRegRef = useRef(depReg);
|
|
837
|
+
depRegRef.current = depReg;
|
|
838
|
+
const wiredDepRegRef = useRef(null);
|
|
839
|
+
const dispatcherRef = useRef(null);
|
|
840
|
+
const registry4 = useMemo(() => {
|
|
841
|
+
const snapshot = () => {
|
|
842
|
+
const v = versionRef.current;
|
|
843
|
+
if (cachedVerRef.current === v) return cachedRef.current;
|
|
844
|
+
const out = Object.freeze(Array.from(actionsRef.current.values()));
|
|
845
|
+
cachedRef.current = out;
|
|
846
|
+
cachedVerRef.current = v;
|
|
847
|
+
return out;
|
|
848
|
+
};
|
|
849
|
+
const notify2 = () => {
|
|
850
|
+
for (const l of listenersRef.current) {
|
|
851
|
+
try {
|
|
852
|
+
l();
|
|
853
|
+
} catch (err) {
|
|
854
|
+
console.error("weasel ActionsRegistry: subscriber threw", err);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
};
|
|
858
|
+
return {
|
|
859
|
+
register: (action) => {
|
|
860
|
+
validateActionId(action.id);
|
|
861
|
+
validateActionDefaultBinding(action);
|
|
862
|
+
actionsRef.current.set(action.id, action);
|
|
863
|
+
versionRef.current++;
|
|
864
|
+
notify2();
|
|
865
|
+
return () => {
|
|
866
|
+
const cur = actionsRef.current.get(action.id);
|
|
867
|
+
if (cur === action) {
|
|
868
|
+
actionsRef.current.delete(action.id);
|
|
869
|
+
versionRef.current++;
|
|
870
|
+
notify2();
|
|
871
|
+
}
|
|
872
|
+
};
|
|
873
|
+
},
|
|
874
|
+
unregister: (id) => {
|
|
875
|
+
if (actionsRef.current.delete(id)) {
|
|
876
|
+
versionRef.current++;
|
|
877
|
+
notify2();
|
|
878
|
+
}
|
|
879
|
+
},
|
|
880
|
+
list: () => snapshot(),
|
|
881
|
+
trigger: (id, params) => {
|
|
882
|
+
const a = actionsRef.current.get(id);
|
|
883
|
+
if (!a) return false;
|
|
884
|
+
try {
|
|
885
|
+
if (a.invoker && a.invoker.timing === "immediate") {
|
|
886
|
+
const r = wiredDepRegRef.current ?? depRegRef.current;
|
|
887
|
+
const deps = !r ? {} : a.requires ? buildDepsFromRequires(a, r) : {
|
|
888
|
+
selection: r.get("selection"),
|
|
889
|
+
scene: r.get("scene"),
|
|
890
|
+
history: r.get("history"),
|
|
891
|
+
view: r.get("view"),
|
|
892
|
+
pointer: r.get("pointer"),
|
|
893
|
+
activeTool: r.get("activeTool"),
|
|
894
|
+
booleansAdapter: r.get("booleansAdapter")
|
|
895
|
+
};
|
|
896
|
+
a.invoker.run(deps, params);
|
|
897
|
+
}
|
|
898
|
+
} catch (err) {
|
|
899
|
+
console.error(`weasel ActionsRegistry: action "${id}" threw`, err);
|
|
900
|
+
}
|
|
901
|
+
return true;
|
|
902
|
+
},
|
|
903
|
+
subscribe: (listener) => {
|
|
904
|
+
listenersRef.current.add(listener);
|
|
905
|
+
return () => {
|
|
906
|
+
listenersRef.current.delete(listener);
|
|
907
|
+
};
|
|
908
|
+
},
|
|
909
|
+
setDispatcher: (d) => {
|
|
910
|
+
dispatcherRef.current = d;
|
|
911
|
+
},
|
|
912
|
+
setDepRegistry: (r) => {
|
|
913
|
+
wiredDepRegRef.current = r;
|
|
914
|
+
},
|
|
915
|
+
begin: (id, params) => {
|
|
916
|
+
const disp = dispatcherRef.current;
|
|
917
|
+
if (!disp) return null;
|
|
918
|
+
const r = wiredDepRegRef.current ?? depRegRef.current;
|
|
919
|
+
const deps = r ? {
|
|
920
|
+
selection: r.get("selection"),
|
|
921
|
+
scene: r.get("scene"),
|
|
922
|
+
history: r.get("history"),
|
|
923
|
+
view: r.get("view"),
|
|
924
|
+
pointer: r.get("pointer"),
|
|
925
|
+
activeTool: r.get("activeTool"),
|
|
926
|
+
booleansAdapter: r.get("booleansAdapter")
|
|
927
|
+
} : {};
|
|
928
|
+
return disp.beginUiOngoing(id, deps, params);
|
|
929
|
+
}
|
|
930
|
+
};
|
|
931
|
+
}, []);
|
|
932
|
+
return /* @__PURE__ */ jsx(ActionsContext.Provider, { value: registry4, children });
|
|
933
|
+
}
|
|
934
|
+
function useActionsRegistry() {
|
|
935
|
+
return useContext(ActionsContext);
|
|
936
|
+
}
|
|
677
937
|
function findConflicts(tools) {
|
|
678
938
|
const entries = buildRouteRegistry(tools);
|
|
679
939
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -715,9 +975,15 @@ function predicateId(pred) {
|
|
|
715
975
|
}
|
|
716
976
|
return id;
|
|
717
977
|
}
|
|
978
|
+
function actionAsTool(action) {
|
|
979
|
+
return { id: action.id, eligibility: {}, bindings: actionBindings(action) };
|
|
980
|
+
}
|
|
718
981
|
function findScopedConflicts(scopes) {
|
|
719
982
|
const registry4 = Array.isArray(scopes.registry) ? scopes.registry : Object.values(scopes.registry);
|
|
720
|
-
const
|
|
983
|
+
const actions = scopes.actions ?? [];
|
|
984
|
+
const ambientActions = actions.filter((a) => a.scope !== "hotkey").map(actionAsTool);
|
|
985
|
+
const hotkeyActions = actions.filter((a) => a.scope === "hotkey").map(actionAsTool);
|
|
986
|
+
const ambient = [...scopes.ambient ?? [], ...ambientActions];
|
|
721
987
|
const out = [];
|
|
722
988
|
const seen = /* @__PURE__ */ new Set();
|
|
723
989
|
const add = (conflicts) => {
|
|
@@ -728,12 +994,13 @@ function findScopedConflicts(scopes) {
|
|
|
728
994
|
out.push(c);
|
|
729
995
|
}
|
|
730
996
|
};
|
|
731
|
-
for (const tool of [...registry4, ...ambient]) add(findConflicts([tool]));
|
|
997
|
+
for (const tool of [...registry4, ...scopes.ambient ?? []]) add(findConflicts([tool]));
|
|
732
998
|
if (ambient.length > 1) add(findConflicts(ambient));
|
|
733
|
-
const
|
|
734
|
-
(t) => t.def?.hotkey !== void 0
|
|
735
|
-
|
|
736
|
-
|
|
999
|
+
const hotkey = [
|
|
1000
|
+
...registry4.filter((t) => t.def?.hotkey !== void 0),
|
|
1001
|
+
...hotkeyActions
|
|
1002
|
+
];
|
|
1003
|
+
if (hotkey.length > 1) add(findConflicts(hotkey));
|
|
737
1004
|
return out;
|
|
738
1005
|
}
|
|
739
1006
|
function formatConflict(conflict) {
|
|
@@ -873,7 +1140,7 @@ function transformCoords(coords, m) {
|
|
|
873
1140
|
return out;
|
|
874
1141
|
}
|
|
875
1142
|
|
|
876
|
-
// ../core/dist/chunk-
|
|
1143
|
+
// ../core/dist/chunk-6SHZHXXR.js
|
|
877
1144
|
function resolveUnit(v, unitSystem) {
|
|
878
1145
|
if (typeof v === "number") return v;
|
|
879
1146
|
if (!unitSystem) {
|
|
@@ -1444,6 +1711,28 @@ var RECT_POSE_DESCRIPTOR = {
|
|
|
1444
1711
|
translate: RECT_POSE_DESCRIPTOR.translate,
|
|
1445
1712
|
intersectsRect: RECT_POSE_DESCRIPTOR.intersectsRect,
|
|
1446
1713
|
lerp: RECT_POSE_DESCRIPTOR.lerp});
|
|
1714
|
+
function remapRotatedLeaf(pose, src, dst) {
|
|
1715
|
+
const sx = src.width === 0 ? 1 : dst.width / src.width;
|
|
1716
|
+
const sy = src.height === 0 ? 1 : dst.height / src.height;
|
|
1717
|
+
const theta = pose.rotation ?? 0;
|
|
1718
|
+
const cos = Math.cos(theta);
|
|
1719
|
+
const sin = Math.sin(theta);
|
|
1720
|
+
const ux = sx * cos, uy = sy * sin;
|
|
1721
|
+
const vx = -sx * sin, vy = sy * cos;
|
|
1722
|
+
const width = pose.width * Math.hypot(ux, uy);
|
|
1723
|
+
const height = pose.height * Math.hypot(vx, vy);
|
|
1724
|
+
const rotation = Math.atan2(uy, ux);
|
|
1725
|
+
const cxNew = dst.x + (pose.x + pose.width / 2 - src.x) * sx;
|
|
1726
|
+
const cyNew = dst.y + (pose.y + pose.height / 2 - src.y) * sy;
|
|
1727
|
+
return {
|
|
1728
|
+
...pose,
|
|
1729
|
+
x: cxNew - width / 2,
|
|
1730
|
+
y: cyNew - height / 2,
|
|
1731
|
+
width,
|
|
1732
|
+
height,
|
|
1733
|
+
rotation
|
|
1734
|
+
};
|
|
1735
|
+
}
|
|
1447
1736
|
var pathPoseDescriptor = {
|
|
1448
1737
|
getBounds: (path) => boundsOfPath(path),
|
|
1449
1738
|
remapBounds: (path, src, dst) => {
|
|
@@ -1540,14 +1829,29 @@ var RECT_ORIGIN_PROJECTION = {
|
|
|
1540
1829
|
getOrigin: (p) => ({ x: p.x, y: p.y }),
|
|
1541
1830
|
translate: (p, dx, dy) => ({ ...p, x: p.x + dx, y: p.y + dy })
|
|
1542
1831
|
};
|
|
1543
|
-
|
|
1544
|
-
|
|
1832
|
+
var MIN_SCALE = 1e-9;
|
|
1833
|
+
function pxExtent(px, scale2) {
|
|
1834
|
+
return {
|
|
1835
|
+
x: px / Math.max(MIN_SCALE, Math.abs(scale2.x)),
|
|
1836
|
+
y: px / Math.max(MIN_SCALE, Math.abs(scale2.y))
|
|
1837
|
+
};
|
|
1838
|
+
}
|
|
1839
|
+
function scaleDelta(dx, dy, scale2) {
|
|
1840
|
+
return { x: dx * scale2.x, y: dy * scale2.y };
|
|
1841
|
+
}
|
|
1842
|
+
function withinPxBox(dx, dy, px, scale2) {
|
|
1843
|
+
const s = scaleDelta(dx, dy, scale2);
|
|
1844
|
+
return Math.abs(s.x) <= px && Math.abs(s.y) <= px;
|
|
1845
|
+
}
|
|
1846
|
+
function withinPxRadius(dx, dy, px, scale2) {
|
|
1847
|
+
const s = scaleDelta(dx, dy, scale2);
|
|
1848
|
+
return s.x * s.x + s.y * s.y <= px * px;
|
|
1545
1849
|
}
|
|
1546
1850
|
|
|
1547
1851
|
// ../core/dist/chunk-GVCNT7UH.js
|
|
1548
1852
|
var FACTORIES = /* @__PURE__ */ new Map();
|
|
1549
|
-
function registerOpFactory(name,
|
|
1550
|
-
FACTORIES.set(name,
|
|
1853
|
+
function registerOpFactory(name, build2) {
|
|
1854
|
+
FACTORIES.set(name, build2);
|
|
1551
1855
|
}
|
|
1552
1856
|
function rebuildOp(name, args) {
|
|
1553
1857
|
const f = FACTORIES.get(name);
|
|
@@ -1584,7 +1888,7 @@ function createDeleteOp(args) {
|
|
|
1584
1888
|
}
|
|
1585
1889
|
registerOpFactory("delete", (args) => createDeleteOp(args));
|
|
1586
1890
|
|
|
1587
|
-
// ../core/dist/chunk-
|
|
1891
|
+
// ../core/dist/chunk-UB6A6L77.js
|
|
1588
1892
|
function createTransformOp(args) {
|
|
1589
1893
|
const { id, from, to, label, coalesceKey = `transform:${id}` } = args;
|
|
1590
1894
|
return {
|
|
@@ -1665,7 +1969,7 @@ function snap(strategy, opts = {}) {
|
|
|
1665
1969
|
};
|
|
1666
1970
|
}
|
|
1667
1971
|
|
|
1668
|
-
// ../core/dist/chunk-
|
|
1972
|
+
// ../core/dist/chunk-Z3KNFTTS.js
|
|
1669
1973
|
function lockAspectWithModifier(opts = {}) {
|
|
1670
1974
|
const { key = "shift" } = opts;
|
|
1671
1975
|
return {
|
|
@@ -1861,13 +2165,165 @@ function materializePaste(adapter, snapshot, opts) {
|
|
|
1861
2165
|
return { ops, newIds, created };
|
|
1862
2166
|
}
|
|
1863
2167
|
|
|
1864
|
-
// ../core/dist/chunk-
|
|
2168
|
+
// ../core/dist/chunk-IO5Z75X4.js
|
|
2169
|
+
var counter = 0;
|
|
1865
2170
|
var registry = /* @__PURE__ */ new Map();
|
|
1866
2171
|
function getTexture(id) {
|
|
1867
2172
|
return registry.get(id) ?? null;
|
|
1868
2173
|
}
|
|
1869
|
-
|
|
1870
|
-
|
|
2174
|
+
function registerTexture(image) {
|
|
2175
|
+
const id = `tex_${++counter}`;
|
|
2176
|
+
registry.set(id, { source: image });
|
|
2177
|
+
return { id };
|
|
2178
|
+
}
|
|
2179
|
+
function tileSize(spec) {
|
|
2180
|
+
if (spec.size !== void 0) return spec.size;
|
|
2181
|
+
switch (spec.tile) {
|
|
2182
|
+
case "hatch":
|
|
2183
|
+
return 5;
|
|
2184
|
+
case "crosshatch":
|
|
2185
|
+
return 6;
|
|
2186
|
+
case "dots":
|
|
2187
|
+
return 6;
|
|
2188
|
+
case "chunks":
|
|
2189
|
+
return 48;
|
|
2190
|
+
default:
|
|
2191
|
+
return 8;
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
function mulberry32(seed) {
|
|
2195
|
+
let s = seed | 0;
|
|
2196
|
+
return () => {
|
|
2197
|
+
s = s + 1831565813 | 0;
|
|
2198
|
+
let t = Math.imul(s ^ s >>> 15, 1 | s);
|
|
2199
|
+
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
2200
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
2201
|
+
};
|
|
2202
|
+
}
|
|
2203
|
+
function tileGeometry(spec) {
|
|
2204
|
+
const s = tileSize(spec);
|
|
2205
|
+
const { color } = spec;
|
|
2206
|
+
switch (spec.tile) {
|
|
2207
|
+
case "hatch": {
|
|
2208
|
+
const width = spec.lineWidth ?? 1;
|
|
2209
|
+
return { size: s, shapes: forwardDiagonals(s, width, color) };
|
|
2210
|
+
}
|
|
2211
|
+
case "crosshatch": {
|
|
2212
|
+
const width = spec.lineWidth ?? 0.8;
|
|
2213
|
+
return {
|
|
2214
|
+
size: s,
|
|
2215
|
+
shapes: [...forwardDiagonals(s, width, color), ...backwardDiagonals(s, width, color)]
|
|
2216
|
+
};
|
|
2217
|
+
}
|
|
2218
|
+
case "dots": {
|
|
2219
|
+
const r = spec.radius ?? 1;
|
|
2220
|
+
return { size: s, shapes: [{ kind: "circle", cx: s / 2, cy: s / 2, r, color }] };
|
|
2221
|
+
}
|
|
2222
|
+
case "chunks": {
|
|
2223
|
+
const density = spec.density ?? 0.35;
|
|
2224
|
+
const chunkSize = spec.chunkSize ?? 3;
|
|
2225
|
+
const rand = mulberry32(spec.seed ?? 42);
|
|
2226
|
+
const count = Math.round(s * s * density / (chunkSize * chunkSize));
|
|
2227
|
+
const shapes = [];
|
|
2228
|
+
if (spec.bg !== void 0) {
|
|
2229
|
+
shapes.push({ kind: "rect", x: 0, y: 0, width: s, height: s, color: spec.bg });
|
|
2230
|
+
}
|
|
2231
|
+
for (let i = 0; i < count; i++) {
|
|
2232
|
+
const cx = rand() * s;
|
|
2233
|
+
const cy = rand() * s;
|
|
2234
|
+
const w = chunkSize * (0.5 + rand());
|
|
2235
|
+
const h = chunkSize * (0.5 + rand());
|
|
2236
|
+
const rotation = rand() * Math.PI;
|
|
2237
|
+
shapes.push({ kind: "ellipse", cx, cy, rx: w / 2, ry: h / 2, rotation, color });
|
|
2238
|
+
}
|
|
2239
|
+
return { size: s, shapes };
|
|
2240
|
+
}
|
|
2241
|
+
default:
|
|
2242
|
+
return { size: s, shapes: [] };
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
function forwardDiagonals(s, width, color) {
|
|
2246
|
+
return [
|
|
2247
|
+
{ kind: "line", x1: -1, y1: s + 1, x2: s + 1, y2: -1, width, color },
|
|
2248
|
+
{ kind: "line", x1: -1, y1: 1, x2: 1, y2: -1, width, color },
|
|
2249
|
+
{ kind: "line", x1: s - 1, y1: s + 1, x2: s + 1, y2: s - 1, width, color }
|
|
2250
|
+
];
|
|
2251
|
+
}
|
|
2252
|
+
function backwardDiagonals(s, width, color) {
|
|
2253
|
+
return [
|
|
2254
|
+
{ kind: "line", x1: -1, y1: -1, x2: s + 1, y2: s + 1, width, color },
|
|
2255
|
+
{ kind: "line", x1: s - 1, y1: -1, x2: s + 1, y2: 1, width, color },
|
|
2256
|
+
{ kind: "line", x1: -1, y1: s - 1, x2: 1, y2: s + 1, width, color }
|
|
2257
|
+
];
|
|
2258
|
+
}
|
|
2259
|
+
function drawTileShapes(oc, geometry) {
|
|
2260
|
+
for (const shape of geometry.shapes) {
|
|
2261
|
+
if (shape.kind === "line") {
|
|
2262
|
+
oc.strokeStyle = shape.color;
|
|
2263
|
+
oc.lineWidth = shape.width;
|
|
2264
|
+
oc.beginPath();
|
|
2265
|
+
oc.moveTo(shape.x1, shape.y1);
|
|
2266
|
+
oc.lineTo(shape.x2, shape.y2);
|
|
2267
|
+
oc.stroke();
|
|
2268
|
+
continue;
|
|
2269
|
+
}
|
|
2270
|
+
oc.fillStyle = shape.color;
|
|
2271
|
+
if (shape.kind === "rect") {
|
|
2272
|
+
oc.fillRect(shape.x, shape.y, shape.width, shape.height);
|
|
2273
|
+
continue;
|
|
2274
|
+
}
|
|
2275
|
+
oc.beginPath();
|
|
2276
|
+
if (shape.kind === "circle") {
|
|
2277
|
+
oc.arc(shape.cx, shape.cy, shape.r, 0, Math.PI * 2);
|
|
2278
|
+
} else {
|
|
2279
|
+
oc.ellipse(shape.cx, shape.cy, shape.rx, shape.ry, shape.rotation, 0, Math.PI * 2);
|
|
2280
|
+
}
|
|
2281
|
+
oc.fill();
|
|
2282
|
+
}
|
|
2283
|
+
}
|
|
2284
|
+
function createTilePattern(opts) {
|
|
2285
|
+
let off;
|
|
2286
|
+
try {
|
|
2287
|
+
off = new OffscreenCanvas(opts.size, opts.size);
|
|
2288
|
+
} catch {
|
|
2289
|
+
if (typeof document === "undefined") return null;
|
|
2290
|
+
const c = document.createElement("canvas");
|
|
2291
|
+
c.width = opts.size;
|
|
2292
|
+
c.height = opts.size;
|
|
2293
|
+
off = c;
|
|
2294
|
+
}
|
|
2295
|
+
const oc = off.getContext("2d");
|
|
2296
|
+
if (!oc) return null;
|
|
2297
|
+
opts.draw(oc, opts.size);
|
|
2298
|
+
let bitmap;
|
|
2299
|
+
if (typeof off.transferToImageBitmap === "function") {
|
|
2300
|
+
bitmap = off.transferToImageBitmap();
|
|
2301
|
+
} else {
|
|
2302
|
+
return null;
|
|
2303
|
+
}
|
|
2304
|
+
return registerTexture(bitmap);
|
|
2305
|
+
}
|
|
2306
|
+
function build(spec) {
|
|
2307
|
+
const geometry = tileGeometry(spec);
|
|
2308
|
+
return createTilePattern({
|
|
2309
|
+
size: tileSize(spec),
|
|
2310
|
+
draw: (oc) => drawTileShapes(oc, geometry)
|
|
2311
|
+
});
|
|
2312
|
+
}
|
|
2313
|
+
function hatch(params) {
|
|
2314
|
+
return build({ tile: "hatch", ...params });
|
|
2315
|
+
}
|
|
2316
|
+
function crosshatch(params) {
|
|
2317
|
+
return build({ tile: "crosshatch", ...params });
|
|
2318
|
+
}
|
|
2319
|
+
function dots(params) {
|
|
2320
|
+
return build({ tile: "dots", ...params });
|
|
2321
|
+
}
|
|
2322
|
+
function chunks(params) {
|
|
2323
|
+
return build({ tile: "chunks", ...params });
|
|
2324
|
+
}
|
|
2325
|
+
|
|
2326
|
+
// ../font/dist/index.js
|
|
1871
2327
|
var subscribers = /* @__PURE__ */ new Set();
|
|
1872
2328
|
var generation = 0;
|
|
1873
2329
|
function glyphGeneration() {
|
|
@@ -2003,8 +2459,8 @@ function alphaToSdf(alpha, width, height, radius, cutoff) {
|
|
|
2003
2459
|
edt2d(gridInner, width, height, f, d, v, z);
|
|
2004
2460
|
const out = new Uint8Array(n);
|
|
2005
2461
|
for (let i = 0; i < n; i++) {
|
|
2006
|
-
const
|
|
2007
|
-
const byte = Math.round(255 - 255 * (
|
|
2462
|
+
const dist3 = Math.sqrt(gridOuter[i]) - Math.sqrt(gridInner[i]);
|
|
2463
|
+
const byte = Math.round(255 - 255 * (dist3 / radius + cutoff));
|
|
2008
2464
|
out[i] = byte < 0 ? 0 : byte > 255 ? 255 : byte;
|
|
2009
2465
|
}
|
|
2010
2466
|
return out;
|
|
@@ -2210,17 +2666,17 @@ function dynamicPageTextureId(page) {
|
|
|
2210
2666
|
return `weasel-dyn-sdf-page-${page}`;
|
|
2211
2667
|
}
|
|
2212
2668
|
var uploadedVersions = /* @__PURE__ */ new WeakMap();
|
|
2213
|
-
function syncDynamicPageTexture(
|
|
2669
|
+
function syncDynamicPageTexture(cache7, pageIndex) {
|
|
2214
2670
|
const page = pages[pageIndex];
|
|
2215
2671
|
if (!page) return false;
|
|
2216
2672
|
const id = dynamicPageTextureId(pageIndex);
|
|
2217
|
-
let seen = uploadedVersions.get(
|
|
2673
|
+
let seen = uploadedVersions.get(cache7);
|
|
2218
2674
|
if (!seen) {
|
|
2219
2675
|
seen = /* @__PURE__ */ new Map();
|
|
2220
|
-
uploadedVersions.set(
|
|
2676
|
+
uploadedVersions.set(cache7, seen);
|
|
2221
2677
|
}
|
|
2222
|
-
if (!
|
|
2223
|
-
|
|
2678
|
+
if (!cache7.has(id)) {
|
|
2679
|
+
cache7.uploadR8(id, PAGE_SIZE, PAGE_SIZE, page.data);
|
|
2224
2680
|
seen.set(pageIndex, page.version);
|
|
2225
2681
|
return true;
|
|
2226
2682
|
}
|
|
@@ -2233,7 +2689,7 @@ function syncDynamicPageTexture(cache5, pageIndex) {
|
|
|
2233
2689
|
const src = (patch.y + row) * PAGE_SIZE + patch.x;
|
|
2234
2690
|
tight.set(page.data.subarray(src, src + patch.w), row * patch.w);
|
|
2235
2691
|
}
|
|
2236
|
-
|
|
2692
|
+
cache7.subImageR8(id, patch.x, patch.y, patch.w, patch.h, tight);
|
|
2237
2693
|
}
|
|
2238
2694
|
seen.set(pageIndex, page.version);
|
|
2239
2695
|
return true;
|
|
@@ -2441,6 +2897,23 @@ var slots = /* @__PURE__ */ new Map();
|
|
|
2441
2897
|
function slotKey(family, weight, style) {
|
|
2442
2898
|
return `${family}|${weight}|${style}`;
|
|
2443
2899
|
}
|
|
2900
|
+
function closeContours(d) {
|
|
2901
|
+
let out = "";
|
|
2902
|
+
let start = 0;
|
|
2903
|
+
for (let i = 1; i < d.length; i++) {
|
|
2904
|
+
const c = d[i];
|
|
2905
|
+
if (c !== "M" && c !== "m") continue;
|
|
2906
|
+
out += closeOne(d.slice(start, i));
|
|
2907
|
+
start = i;
|
|
2908
|
+
}
|
|
2909
|
+
return out + closeOne(d.slice(start));
|
|
2910
|
+
}
|
|
2911
|
+
function closeOne(contour) {
|
|
2912
|
+
const trimmed = contour.trimEnd();
|
|
2913
|
+
if (trimmed.length === 0) return "";
|
|
2914
|
+
const last = trimmed[trimmed.length - 1];
|
|
2915
|
+
return last === "Z" || last === "z" ? trimmed : `${trimmed}Z`;
|
|
2916
|
+
}
|
|
2444
2917
|
function glyphOutline(family, weight, style, cp) {
|
|
2445
2918
|
const slot = slots.get(slotKey(family, weight, style));
|
|
2446
2919
|
if (!slot) return null;
|
|
@@ -2453,7 +2926,8 @@ function glyphOutline(family, weight, style, cp) {
|
|
|
2453
2926
|
if (cached2 !== void 0) return cached2;
|
|
2454
2927
|
let d = null;
|
|
2455
2928
|
try {
|
|
2456
|
-
|
|
2929
|
+
const raw = slot.face.glyphD(cp);
|
|
2930
|
+
d = raw === null ? null : closeContours(raw);
|
|
2457
2931
|
} catch (err) {
|
|
2458
2932
|
warnOnce(
|
|
2459
2933
|
`glyph|${slotKey(family, weight, style)}`,
|
|
@@ -2612,8 +3086,8 @@ function eligibleForMode(mode, toolTags) {
|
|
|
2612
3086
|
}
|
|
2613
3087
|
return false;
|
|
2614
3088
|
}
|
|
2615
|
-
function
|
|
2616
|
-
return eligibleForMode(reg.current(),
|
|
3089
|
+
function eligibleContribution(reg, entry) {
|
|
3090
|
+
return eligibleForMode(reg.current(), entry.eligibility?.capabilities ?? []);
|
|
2617
3091
|
}
|
|
2618
3092
|
|
|
2619
3093
|
// ../history/dist/index.js
|
|
@@ -2982,7 +3456,7 @@ function serialToEntry(se, custom, logger) {
|
|
|
2982
3456
|
};
|
|
2983
3457
|
}
|
|
2984
3458
|
|
|
2985
|
-
// ../core/dist/chunk-
|
|
3459
|
+
// ../core/dist/chunk-D2C6K6FO.js
|
|
2986
3460
|
function worldToScreen(worldX, worldY, view) {
|
|
2987
3461
|
return [view.panX + worldX * view.zoom.x, view.panY + worldY * view.zoom.y];
|
|
2988
3462
|
}
|
|
@@ -3233,6 +3707,58 @@ var GRAD_FILL_UNIFORMS = [
|
|
|
3233
3707
|
"u_gradAngle"
|
|
3234
3708
|
];
|
|
3235
3709
|
var GRAD_FILL_ATTRIBUTES = ["a_position"];
|
|
3710
|
+
var PATTERN_VERT_SRC = (
|
|
3711
|
+
/* glsl */
|
|
3712
|
+
`#version 300 es
|
|
3713
|
+
in vec2 a_position;
|
|
3714
|
+
uniform mat3 u_proj;
|
|
3715
|
+
uniform mat3 u_model;
|
|
3716
|
+
uniform mat3 u_worldInv;
|
|
3717
|
+
out vec2 v_world;
|
|
3718
|
+
void main() {
|
|
3719
|
+
vec3 screen = u_model * vec3(a_position, 1.0);
|
|
3720
|
+
vec3 clip = u_proj * vec3(screen.xy, 1.0);
|
|
3721
|
+
gl_Position = vec4(clip.xy, 0.0, 1.0);
|
|
3722
|
+
vec3 world = u_worldInv * vec3(screen.xy, 1.0);
|
|
3723
|
+
v_world = world.xy;
|
|
3724
|
+
}
|
|
3725
|
+
`
|
|
3726
|
+
);
|
|
3727
|
+
var PATTERN_FRAG_SRC = (
|
|
3728
|
+
/* glsl */
|
|
3729
|
+
`#version 300 es
|
|
3730
|
+
precision highp float;
|
|
3731
|
+
in vec2 v_world;
|
|
3732
|
+
uniform sampler2D u_sampler;
|
|
3733
|
+
uniform vec2 u_tileOrigin;
|
|
3734
|
+
uniform vec2 u_tileSize;
|
|
3735
|
+
uniform float u_opacity;
|
|
3736
|
+
uniform float u_alpha;
|
|
3737
|
+
uniform mat4 u_colorMatrix;
|
|
3738
|
+
uniform vec4 u_colorBias;
|
|
3739
|
+
out vec4 outColor;
|
|
3740
|
+
void main() {
|
|
3741
|
+
vec2 uv = (v_world - u_tileOrigin) / max(u_tileSize, vec2(0.0001));
|
|
3742
|
+
vec4 texel = texture(u_sampler, uv);
|
|
3743
|
+
vec4 mapped = clamp(u_colorMatrix * texel + u_colorBias, 0.0, 1.0);
|
|
3744
|
+
float a = mapped.a * u_opacity * u_alpha;
|
|
3745
|
+
outColor = vec4(mapped.rgb * a, a);
|
|
3746
|
+
}
|
|
3747
|
+
`
|
|
3748
|
+
);
|
|
3749
|
+
var PATTERN_FILL_UNIFORMS = [
|
|
3750
|
+
"u_proj",
|
|
3751
|
+
"u_model",
|
|
3752
|
+
"u_worldInv",
|
|
3753
|
+
"u_sampler",
|
|
3754
|
+
"u_tileOrigin",
|
|
3755
|
+
"u_tileSize",
|
|
3756
|
+
"u_opacity",
|
|
3757
|
+
"u_alpha",
|
|
3758
|
+
"u_colorMatrix",
|
|
3759
|
+
"u_colorBias"
|
|
3760
|
+
];
|
|
3761
|
+
var PATTERN_FILL_ATTRIBUTES = ["a_position"];
|
|
3236
3762
|
var GLMeshCache = class {
|
|
3237
3763
|
constructor(gl, aPositionLoc) {
|
|
3238
3764
|
this.gl = gl;
|
|
@@ -3355,17 +3881,20 @@ var GLTextureCache = class {
|
|
|
3355
3881
|
has(id) {
|
|
3356
3882
|
return this.map.has(id);
|
|
3357
3883
|
}
|
|
3358
|
-
|
|
3884
|
+
/** Uploads once per id; `wrap` therefore applies on first upload only.
|
|
3885
|
+
* An id is either a pattern tile or a shader texture, never both. */
|
|
3886
|
+
upload(id, source, wrap = "clamp") {
|
|
3359
3887
|
if (this.map.has(id)) return id;
|
|
3360
3888
|
const gl = this.gl;
|
|
3361
3889
|
const tex = gl.createTexture();
|
|
3362
3890
|
if (!tex) throw new Error(`GLTextureCache: createTexture failed for id="${id}"`);
|
|
3891
|
+
const wrapMode = wrap === "repeat" ? gl.REPEAT : gl.CLAMP_TO_EDGE;
|
|
3363
3892
|
gl.bindTexture(gl.TEXTURE_2D, tex);
|
|
3364
3893
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
|
|
3365
3894
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
|
3366
3895
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
3367
|
-
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S,
|
|
3368
|
-
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T,
|
|
3896
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapMode);
|
|
3897
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapMode);
|
|
3369
3898
|
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
3370
3899
|
this.map.set(id, tex);
|
|
3371
3900
|
return id;
|
|
@@ -3960,6 +4489,18 @@ function scale(m, sx, sy) {
|
|
|
3960
4489
|
const s = create(sx, 0, 0, sy, 0, 0);
|
|
3961
4490
|
return multiply2(m, s);
|
|
3962
4491
|
}
|
|
4492
|
+
function invert(m) {
|
|
4493
|
+
const a = m[0], b = m[1];
|
|
4494
|
+
const c = m[3], d = m[4];
|
|
4495
|
+
const tx = m[6], ty = m[7];
|
|
4496
|
+
const det = a * d - b * c;
|
|
4497
|
+
if (det === 0 || !Number.isFinite(det)) return identity();
|
|
4498
|
+
const ia = d / det;
|
|
4499
|
+
const ib = -b / det;
|
|
4500
|
+
const ic = -c / det;
|
|
4501
|
+
const id = a / det;
|
|
4502
|
+
return create(ia, ib, ic, id, -(ia * tx + ic * ty), -(ib * tx + id * ty));
|
|
4503
|
+
}
|
|
3963
4504
|
function apply(m, x, y) {
|
|
3964
4505
|
const a = m[0], b = m[1];
|
|
3965
4506
|
const c = m[3], d = m[4];
|
|
@@ -3987,6 +4528,7 @@ var mat3 = {
|
|
|
3987
4528
|
multiply: multiply2,
|
|
3988
4529
|
translate: translate2,
|
|
3989
4530
|
scale,
|
|
4531
|
+
invert,
|
|
3990
4532
|
apply,
|
|
3991
4533
|
screenToClip
|
|
3992
4534
|
};
|
|
@@ -4355,6 +4897,16 @@ function extractPolygon(p, opts) {
|
|
|
4355
4897
|
out.push(next);
|
|
4356
4898
|
return next;
|
|
4357
4899
|
};
|
|
4900
|
+
const dropDuplicateClosingPoint = () => {
|
|
4901
|
+
if (!pts || !aA || !aB || !aT) return;
|
|
4902
|
+
const n = pts.length / 2;
|
|
4903
|
+
if (n < 2) return;
|
|
4904
|
+
if (pts[0] !== pts[(n - 1) * 2] || pts[1] !== pts[(n - 1) * 2 + 1]) return;
|
|
4905
|
+
pts.length -= 2;
|
|
4906
|
+
aA.length -= 1;
|
|
4907
|
+
aB.length -= 1;
|
|
4908
|
+
aT.length -= 1;
|
|
4909
|
+
};
|
|
4358
4910
|
const commit = (target) => {
|
|
4359
4911
|
if (!pts || !aA || !aB || !aT) return;
|
|
4360
4912
|
target.anchorA = new Uint32Array(aA);
|
|
@@ -4438,7 +4990,10 @@ function extractPolygon(p, opts) {
|
|
|
4438
4990
|
break;
|
|
4439
4991
|
}
|
|
4440
4992
|
case PATH_Z2: {
|
|
4441
|
-
if (current)
|
|
4993
|
+
if (current) {
|
|
4994
|
+
current.closed = true;
|
|
4995
|
+
dropDuplicateClosingPoint();
|
|
4996
|
+
}
|
|
4442
4997
|
break;
|
|
4443
4998
|
}
|
|
4444
4999
|
default:
|
|
@@ -4919,8 +5474,20 @@ function sameFill(a, b) {
|
|
|
4919
5474
|
}
|
|
4920
5475
|
return false;
|
|
4921
5476
|
}
|
|
4922
|
-
function
|
|
4923
|
-
|
|
5477
|
+
function strokeKey(s) {
|
|
5478
|
+
if (!s) return "-";
|
|
5479
|
+
return [
|
|
5480
|
+
fillKey(s.paint),
|
|
5481
|
+
s.width ?? 1,
|
|
5482
|
+
s.join ?? "miter",
|
|
5483
|
+
s.cap ?? "butt",
|
|
5484
|
+
s.miterLimit ?? "",
|
|
5485
|
+
s.align ?? "center",
|
|
5486
|
+
(s.dash ?? []).join(",")
|
|
5487
|
+
].join(":");
|
|
5488
|
+
}
|
|
5489
|
+
function groupKey(family, weight, style, synthetic, fill, stroke, source, page) {
|
|
5490
|
+
return `${family}|${weight}|${style}|${synthetic.bold ? 1 : 0}${synthetic.italic ? 1 : 0}|${fillKey(fill)}|${strokeKey(stroke)}|${source}|${page}`;
|
|
4924
5491
|
}
|
|
4925
5492
|
function getOrCreateGroup(ctx, run, resolved, page, sourceOverride) {
|
|
4926
5493
|
const atlasFamily = resolved.resolved.family;
|
|
@@ -4933,6 +5500,7 @@ function getOrCreateGroup(ctx, run, resolved, page, sourceOverride) {
|
|
|
4933
5500
|
resolvedStyle,
|
|
4934
5501
|
resolved.synthetic,
|
|
4935
5502
|
run.fill,
|
|
5503
|
+
source === "outline" ? run.stroke : void 0,
|
|
4936
5504
|
source,
|
|
4937
5505
|
page
|
|
4938
5506
|
);
|
|
@@ -4946,6 +5514,9 @@ function getOrCreateGroup(ctx, run, resolved, page, sourceOverride) {
|
|
|
4946
5514
|
source,
|
|
4947
5515
|
page,
|
|
4948
5516
|
fill: run.fill,
|
|
5517
|
+
// Only the outline tier can paint it, and carrying it on an SDF group
|
|
5518
|
+
// would be a promise the renderer cannot keep.
|
|
5519
|
+
...source === "outline" && run.stroke !== void 0 ? { stroke: run.stroke } : {},
|
|
4949
5520
|
quads: [],
|
|
4950
5521
|
glyphs: []
|
|
4951
5522
|
};
|
|
@@ -5542,6 +6113,34 @@ function outlineMesh(key, d) {
|
|
|
5542
6113
|
cache3.set(key, mesh);
|
|
5543
6114
|
return mesh;
|
|
5544
6115
|
}
|
|
6116
|
+
var OUTLINE_STROKE_MESH_CACHE_LIMIT = 2048;
|
|
6117
|
+
var WIDTH_QUANTUM = 1 / 1024;
|
|
6118
|
+
var cache4 = /* @__PURE__ */ new Map();
|
|
6119
|
+
function quantizeEmWidth(emWidth) {
|
|
6120
|
+
return Math.round(emWidth / WIDTH_QUANTUM) * WIDTH_QUANTUM;
|
|
6121
|
+
}
|
|
6122
|
+
function outlineStrokeMesh(glyphKey, d, emWidth, stroke) {
|
|
6123
|
+
if (!(emWidth > 0)) return null;
|
|
6124
|
+
const key = [
|
|
6125
|
+
glyphKey,
|
|
6126
|
+
emWidth,
|
|
6127
|
+
stroke.join ?? "miter",
|
|
6128
|
+
stroke.cap ?? "butt",
|
|
6129
|
+
stroke.miterLimit ?? "",
|
|
6130
|
+
stroke.align ?? "center",
|
|
6131
|
+
(stroke.dash ?? []).join(",")
|
|
6132
|
+
].join("|");
|
|
6133
|
+
const cached2 = cache4.get(key);
|
|
6134
|
+
if (cached2 !== void 0) return cached2.indices.length === 0 ? null : cached2;
|
|
6135
|
+
if (cache4.size >= OUTLINE_STROKE_MESH_CACHE_LIMIT) cache4 = /* @__PURE__ */ new Map();
|
|
6136
|
+
const mesh = tessellateStroke(
|
|
6137
|
+
pathFromD(d),
|
|
6138
|
+
{ ...stroke, width: emWidth },
|
|
6139
|
+
{ flattenTolerance: OUTLINE_FLATTEN_TOLERANCE }
|
|
6140
|
+
);
|
|
6141
|
+
cache4.set(key, mesh);
|
|
6142
|
+
return mesh.indices.length === 0 ? null : mesh;
|
|
6143
|
+
}
|
|
5545
6144
|
function setColorMatrixUniforms(ctx, prog) {
|
|
5546
6145
|
const gl = ctx.gl;
|
|
5547
6146
|
const cm = ctx.state.colorMatrix;
|
|
@@ -5832,28 +6431,42 @@ function drawPathFillPattern(ctx, fill, handle) {
|
|
|
5832
6431
|
if (isDev3) console.warn(`weasel: pattern TextureHandle "${tex.id}" not registered`);
|
|
5833
6432
|
return;
|
|
5834
6433
|
}
|
|
5835
|
-
ctx.textureCache.upload(tex.id, entry.source);
|
|
6434
|
+
ctx.textureCache.upload(tex.id, entry.source, "repeat");
|
|
5836
6435
|
const gl = ctx.gl;
|
|
5837
|
-
gl.useProgram(ctx.
|
|
6436
|
+
gl.useProgram(ctx.patternFill.handle);
|
|
5838
6437
|
gl.bindVertexArray(handle.vao);
|
|
5839
|
-
setProjAndModel(ctx, ctx.
|
|
5840
|
-
setColorMatrixUniforms(ctx, ctx.
|
|
6438
|
+
setProjAndModel(ctx, ctx.patternFill);
|
|
6439
|
+
setColorMatrixUniforms(ctx, ctx.patternFill);
|
|
6440
|
+
gl.uniformMatrix3fv(ctx.patternFill.uniform("u_worldInv"), false, gradientSpaceInverse(ctx, fill.units));
|
|
6441
|
+
const [tw, th] = textureSize(entry.source);
|
|
6442
|
+
const origin = fill.origin ?? { x: 0, y: 0 };
|
|
6443
|
+
gl.uniform2f(ctx.patternFill.uniform("u_tileOrigin"), origin.x, origin.y);
|
|
6444
|
+
gl.uniform2f(ctx.patternFill.uniform("u_tileSize"), tw, th);
|
|
5841
6445
|
ctx.textureCache.bind(tex.id, 0);
|
|
5842
|
-
gl.uniform1i(ctx.
|
|
5843
|
-
gl.uniform1f(ctx.
|
|
5844
|
-
gl.uniform1f(ctx.
|
|
6446
|
+
gl.uniform1i(ctx.patternFill.uniform("u_sampler"), 0);
|
|
6447
|
+
gl.uniform1f(ctx.patternFill.uniform("u_opacity"), fill.opacity ?? 1);
|
|
6448
|
+
gl.uniform1f(ctx.patternFill.uniform("u_alpha"), ctx.state.alpha);
|
|
5845
6449
|
applyClipTest(ctx);
|
|
5846
6450
|
gl.drawElements(gl.TRIANGLES, handle.indexCount, gl.UNSIGNED_INT, 0);
|
|
5847
6451
|
gl.bindVertexArray(null);
|
|
5848
6452
|
}
|
|
6453
|
+
function textureSize(source) {
|
|
6454
|
+
const w = "naturalWidth" in source ? source.naturalWidth : source.width;
|
|
6455
|
+
const h = "naturalHeight" in source ? source.naturalHeight : source.height;
|
|
6456
|
+
return [w || 1, h || 1];
|
|
6457
|
+
}
|
|
6458
|
+
function gradientSpaceInverse(ctx, units) {
|
|
6459
|
+
if (units === "local") return mat3.invert(ctx.state.transform);
|
|
6460
|
+
if (units === "world" && ctx.viewMatrix) return mat3.invert(ctx.viewMatrix);
|
|
6461
|
+
return mat3.identity();
|
|
6462
|
+
}
|
|
5849
6463
|
function drawPathFillGradient(ctx, fill, handle) {
|
|
5850
6464
|
const gl = ctx.gl;
|
|
5851
6465
|
const key = ctx.gradRampCache.upload(fill.stops);
|
|
5852
6466
|
gl.useProgram(ctx.gradFill.handle);
|
|
5853
6467
|
gl.bindVertexArray(handle.vao);
|
|
5854
6468
|
setProjAndModel(ctx, ctx.gradFill);
|
|
5855
|
-
|
|
5856
|
-
gl.uniformMatrix3fv(ctx.gradFill.uniform("u_worldInv"), false, identity3);
|
|
6469
|
+
gl.uniformMatrix3fv(ctx.gradFill.uniform("u_worldInv"), false, gradientSpaceInverse(ctx, fill.units));
|
|
5857
6470
|
ctx.gradRampCache.bind(key, 0);
|
|
5858
6471
|
gl.uniform1i(ctx.gradFill.uniform("u_ramp"), 0);
|
|
5859
6472
|
gl.uniform1f(ctx.gradFill.uniform("u_alpha"), ctx.state.alpha);
|
|
@@ -6115,16 +6728,30 @@ function drawText(ctx, cmd) {
|
|
|
6115
6728
|
var SYNTHETIC_ITALIC_RADIANS = 0.2094;
|
|
6116
6729
|
function drawTextOutlineGroup(ctx, group, dy) {
|
|
6117
6730
|
const mesh = outlineGroupMesh(group, dy);
|
|
6118
|
-
if (
|
|
6119
|
-
|
|
6731
|
+
if (mesh) drawPathFillByKind(ctx, group.fill, ctx.meshCache.uploadTransient(mesh));
|
|
6732
|
+
if (!group.stroke) return;
|
|
6733
|
+
const strokeMesh = outlineGroupStrokeMesh(group, dy);
|
|
6734
|
+
if (strokeMesh) {
|
|
6735
|
+
drawPathFillByKind(ctx, group.stroke.paint, ctx.meshCache.uploadTransient(strokeMesh));
|
|
6736
|
+
}
|
|
6120
6737
|
}
|
|
6121
6738
|
function outlineGroupMesh(group, dy) {
|
|
6739
|
+
return mergeGlyphMeshes(group, dy, (glyph) => outlineMesh(glyph.key, glyph.d));
|
|
6740
|
+
}
|
|
6741
|
+
function outlineGroupStrokeMesh(group, dy) {
|
|
6742
|
+
const stroke = group.stroke;
|
|
6743
|
+
if (!stroke) return null;
|
|
6744
|
+
const width = stroke.width ?? 1;
|
|
6745
|
+
if (!(width > 0)) return null;
|
|
6746
|
+
return mergeGlyphMeshes(group, dy, (glyph) => glyph.scale > 0 ? outlineStrokeMesh(glyph.key, glyph.d, quantizeEmWidth(width / glyph.scale), stroke) : null);
|
|
6747
|
+
}
|
|
6748
|
+
function mergeGlyphMeshes(group, dy, meshFor) {
|
|
6122
6749
|
const parts = [];
|
|
6123
6750
|
let vertexFloats = 0;
|
|
6124
6751
|
let indexCount = 0;
|
|
6125
6752
|
for (const glyph of group.glyphs) {
|
|
6126
|
-
const mesh =
|
|
6127
|
-
if (mesh.indices.length === 0) continue;
|
|
6753
|
+
const mesh = meshFor(glyph);
|
|
6754
|
+
if (!mesh || mesh.indices.length === 0) continue;
|
|
6128
6755
|
parts.push({ mesh, glyph });
|
|
6129
6756
|
vertexFloats += mesh.vertices.length;
|
|
6130
6757
|
indexCount += mesh.indices.length;
|
|
@@ -6359,6 +6986,11 @@ function drawImage(ctx, cmd) {
|
|
|
6359
6986
|
setProjAndModel(ctx, ctx.imageFill);
|
|
6360
6987
|
setColorMatrixUniforms(ctx, ctx.imageFill);
|
|
6361
6988
|
ctx.imageCache.bind(cmd.image, 0);
|
|
6989
|
+
gl.texParameteri(
|
|
6990
|
+
gl.TEXTURE_2D,
|
|
6991
|
+
gl.TEXTURE_MAG_FILTER,
|
|
6992
|
+
cmd.sampling === "nearest" ? gl.NEAREST : gl.LINEAR
|
|
6993
|
+
);
|
|
6362
6994
|
gl.uniform1i(ctx.imageFill.uniform("u_sampler"), 0);
|
|
6363
6995
|
gl.uniform1f(ctx.imageFill.uniform("u_opacity"), cmd.opacity ?? 1);
|
|
6364
6996
|
gl.uniform1f(ctx.imageFill.uniform("u_alpha"), ctx.state.alpha);
|
|
@@ -6445,6 +7077,7 @@ var WeaselRenderer = class {
|
|
|
6445
7077
|
textSdfR8;
|
|
6446
7078
|
imageFill;
|
|
6447
7079
|
gradFill;
|
|
7080
|
+
patternFill;
|
|
6448
7081
|
meshCache;
|
|
6449
7082
|
textureCache;
|
|
6450
7083
|
imageCache;
|
|
@@ -6520,6 +7153,9 @@ var WeaselRenderer = class {
|
|
|
6520
7153
|
this.gradFill = new ShaderProgram(this.gl, GRAD_VERT_SRC, GRAD_FRAG_SRC);
|
|
6521
7154
|
this.gradFill.lookupUniforms(GRAD_FILL_UNIFORMS);
|
|
6522
7155
|
this.gradFill.lookupAttributes(GRAD_FILL_ATTRIBUTES);
|
|
7156
|
+
this.patternFill = new ShaderProgram(this.gl, PATTERN_VERT_SRC, PATTERN_FRAG_SRC);
|
|
7157
|
+
this.patternFill.lookupUniforms(PATTERN_FILL_UNIFORMS);
|
|
7158
|
+
this.patternFill.lookupAttributes(PATTERN_FILL_ATTRIBUTES);
|
|
6523
7159
|
const aPos = this.pathFill.attribute("a_position");
|
|
6524
7160
|
if (aPos === void 0) throw new Error("WeaselRenderer: a_position not found in path-fill shader");
|
|
6525
7161
|
this.meshCache = new GLMeshCache(this.gl, aPos);
|
|
@@ -6628,6 +7264,9 @@ var WeaselRenderer = class {
|
|
|
6628
7264
|
this.gradFill = new ShaderProgram(this.gl, GRAD_VERT_SRC, GRAD_FRAG_SRC);
|
|
6629
7265
|
this.gradFill.lookupUniforms(GRAD_FILL_UNIFORMS);
|
|
6630
7266
|
this.gradFill.lookupAttributes(GRAD_FILL_ATTRIBUTES);
|
|
7267
|
+
this.patternFill = new ShaderProgram(this.gl, PATTERN_VERT_SRC, PATTERN_FRAG_SRC);
|
|
7268
|
+
this.patternFill.lookupUniforms(PATTERN_FILL_UNIFORMS);
|
|
7269
|
+
this.patternFill.lookupAttributes(PATTERN_FILL_ATTRIBUTES);
|
|
6631
7270
|
const aPos = this.pathFill.attribute("a_position");
|
|
6632
7271
|
if (aPos === void 0) throw new Error("a_position missing after restore");
|
|
6633
7272
|
this.meshCache = new GLMeshCache(this.gl, aPos);
|
|
@@ -6699,7 +7338,15 @@ var WeaselRenderer = class {
|
|
|
6699
7338
|
if (this.rectIbo) gl.deleteBuffer(this.rectIbo);
|
|
6700
7339
|
if (this.rectVao) gl.deleteVertexArray(this.rectVao);
|
|
6701
7340
|
}
|
|
6702
|
-
|
|
7341
|
+
/**
|
|
7342
|
+
* Draw one frame.
|
|
7343
|
+
*
|
|
7344
|
+
* `viewMatrix` is the frame's world→screen transform. It is only consulted
|
|
7345
|
+
* by `units: 'world'` gradients, which fall back to screen space without
|
|
7346
|
+
* it — every other command carries its own transform in the stream, so
|
|
7347
|
+
* callers with no view concept can keep calling `render(commands)`.
|
|
7348
|
+
*/
|
|
7349
|
+
render(commands, viewMatrix) {
|
|
6703
7350
|
if (this.contextLost || this.disposed) return;
|
|
6704
7351
|
const gl = this.gl;
|
|
6705
7352
|
this.meshCache.drainPendingDeletes();
|
|
@@ -6714,6 +7361,7 @@ var WeaselRenderer = class {
|
|
|
6714
7361
|
textSdfR8: this.textSdfR8,
|
|
6715
7362
|
imageFill: this.imageFill,
|
|
6716
7363
|
gradFill: this.gradFill,
|
|
7364
|
+
patternFill: this.patternFill,
|
|
6717
7365
|
meshCache: this.meshCache,
|
|
6718
7366
|
textureCache: this.textureCache,
|
|
6719
7367
|
imageCache: this.imageCache,
|
|
@@ -6728,7 +7376,8 @@ var WeaselRenderer = class {
|
|
|
6728
7376
|
heightCss: this.heightCss,
|
|
6729
7377
|
clipDepth: 0,
|
|
6730
7378
|
flattenTolerance: this.flattenTolerance,
|
|
6731
|
-
textOutlineMinScreenSize: this.textOutlineMinScreenSize
|
|
7379
|
+
textOutlineMinScreenSize: this.textOutlineMinScreenSize,
|
|
7380
|
+
viewMatrix
|
|
6732
7381
|
};
|
|
6733
7382
|
for (const cmd of commands) dispatch(ctx, cmd);
|
|
6734
7383
|
this.meshCache.freeTransient();
|
|
@@ -6776,6 +7425,10 @@ var WeaselRenderer = class {
|
|
|
6776
7425
|
return this.gradFill;
|
|
6777
7426
|
}
|
|
6778
7427
|
/** @internal */
|
|
7428
|
+
_patternFill() {
|
|
7429
|
+
return this.patternFill;
|
|
7430
|
+
}
|
|
7431
|
+
/** @internal */
|
|
6779
7432
|
_meshCache() {
|
|
6780
7433
|
return this.meshCache;
|
|
6781
7434
|
}
|
|
@@ -6836,6 +7489,9 @@ function alignedStrokeRect(rect, align, width = 1) {
|
|
|
6836
7489
|
height: rect.height + 2 * d
|
|
6837
7490
|
};
|
|
6838
7491
|
}
|
|
7492
|
+
function meanScale(s) {
|
|
7493
|
+
return Math.sqrt(s.x * s.y);
|
|
7494
|
+
}
|
|
6839
7495
|
var DEFAULT_LINE = {
|
|
6840
7496
|
paint: { fill: "solid", color: "rgba(255, 255, 255, 0.08)" },
|
|
6841
7497
|
width: 1
|
|
@@ -6964,289 +7620,148 @@ function clientToCanvas(canvas, clientX, clientY) {
|
|
|
6964
7620
|
function clientToCanvasRect(rect, clientX, clientY) {
|
|
6965
7621
|
return [clientX - rect.left, clientY - rect.top];
|
|
6966
7622
|
}
|
|
6967
|
-
|
|
6968
|
-
|
|
6969
|
-
const
|
|
6970
|
-
const
|
|
6971
|
-
const
|
|
6972
|
-
const
|
|
6973
|
-
|
|
6974
|
-
|
|
6975
|
-
|
|
6976
|
-
|
|
6977
|
-
|
|
7623
|
+
var COARSE_TARGET_SCALE = 1.75;
|
|
7624
|
+
function resolveDeviceProfile(detected, overrides) {
|
|
7625
|
+
const coarsePointer2 = overrides?.coarsePointer ?? detected.coarsePointer;
|
|
7626
|
+
const canHover2 = overrides?.canHover ?? detected.canHover;
|
|
7627
|
+
const dpr = overrides?.dpr ?? detected.dpr;
|
|
7628
|
+
const targetScale = overrides?.targetScale ?? (coarsePointer2 ? COARSE_TARGET_SCALE : 1);
|
|
7629
|
+
return { coarsePointer: coarsePointer2, canHover: canHover2, dpr, targetScale };
|
|
7630
|
+
}
|
|
7631
|
+
var DEFAULT_DEVICE_PROFILE = resolveDeviceProfile({
|
|
7632
|
+
coarsePointer: false,
|
|
7633
|
+
canHover: true,
|
|
7634
|
+
dpr: 1
|
|
7635
|
+
});
|
|
7636
|
+
var COARSE_QUERY = "(pointer: coarse)";
|
|
7637
|
+
var HOVER_QUERY = "(hover: hover)";
|
|
7638
|
+
function hasMatchMedia() {
|
|
7639
|
+
return typeof window !== "undefined" && typeof window.matchMedia === "function";
|
|
7640
|
+
}
|
|
7641
|
+
function detectOnce() {
|
|
7642
|
+
const dpr = typeof window !== "undefined" && window.devicePixelRatio || 1;
|
|
7643
|
+
if (!hasMatchMedia()) {
|
|
7644
|
+
return {
|
|
7645
|
+
coarsePointer: DEFAULT_DEVICE_PROFILE.coarsePointer,
|
|
7646
|
+
canHover: DEFAULT_DEVICE_PROFILE.canHover,
|
|
7647
|
+
dpr
|
|
7648
|
+
};
|
|
7649
|
+
}
|
|
6978
7650
|
return {
|
|
6979
|
-
|
|
6980
|
-
|
|
6981
|
-
|
|
7651
|
+
coarsePointer: window.matchMedia(COARSE_QUERY).matches,
|
|
7652
|
+
canHover: window.matchMedia(HOVER_QUERY).matches,
|
|
7653
|
+
dpr
|
|
6982
7654
|
};
|
|
6983
7655
|
}
|
|
6984
|
-
function
|
|
6985
|
-
const
|
|
6986
|
-
const visH = canvas.height / view.scale.y;
|
|
6987
|
-
let x;
|
|
6988
|
-
if (visW >= bounds.width) {
|
|
6989
|
-
x = bounds.x + (bounds.width - visW) / 2;
|
|
6990
|
-
} else {
|
|
6991
|
-
const minX = bounds.x;
|
|
6992
|
-
const maxX = bounds.x + bounds.width - visW;
|
|
6993
|
-
x = view.x < minX ? minX : view.x > maxX ? maxX : view.x;
|
|
6994
|
-
}
|
|
6995
|
-
let y;
|
|
6996
|
-
if (visH >= bounds.height) {
|
|
6997
|
-
y = bounds.y + (bounds.height - visH) / 2;
|
|
6998
|
-
} else {
|
|
6999
|
-
const minY = bounds.y;
|
|
7000
|
-
const maxY = bounds.y + bounds.height - visH;
|
|
7001
|
-
y = view.y < minY ? minY : view.y > maxY ? maxY : view.y;
|
|
7002
|
-
}
|
|
7003
|
-
return x === view.x && y === view.y ? view : { x, y, scale: view.scale };
|
|
7004
|
-
}
|
|
7005
|
-
function isEditableTarget2(target) {
|
|
7006
|
-
if (!target || !(target instanceof Element)) return false;
|
|
7007
|
-
const tag = target.tagName;
|
|
7008
|
-
if (tag === "INPUT" || tag === "TEXTAREA") return true;
|
|
7009
|
-
if (target.isContentEditable) return true;
|
|
7010
|
-
const ce = target.getAttribute("contenteditable");
|
|
7011
|
-
return ce === "true" || ce === "";
|
|
7012
|
-
}
|
|
7013
|
-
var DepRegistryContext = createContext(null);
|
|
7014
|
-
function DepRegistryProvider({ children }) {
|
|
7015
|
-
const sourcesRef = useRef(/* @__PURE__ */ new Map());
|
|
7016
|
-
const registry22 = useMemo(() => ({
|
|
7017
|
-
register: (name, source) => {
|
|
7018
|
-
sourcesRef.current.set(name, source);
|
|
7019
|
-
return () => {
|
|
7020
|
-
sourcesRef.current.delete(name);
|
|
7021
|
-
};
|
|
7022
|
-
},
|
|
7023
|
-
get: (name) => sourcesRef.current.get(name)?.()
|
|
7024
|
-
}), []);
|
|
7025
|
-
return /* @__PURE__ */ jsx(DepRegistryContext.Provider, { value: registry22, children });
|
|
7026
|
-
}
|
|
7027
|
-
function useDepRegistry() {
|
|
7028
|
-
const r = useContext(DepRegistryContext);
|
|
7029
|
-
if (r === null) {
|
|
7030
|
-
throw new Error("useDepRegistry: no DepRegistryProvider in scope. Wrap your tree with <DepRegistryProvider> (typically inside <SceneCanvas>).");
|
|
7031
|
-
}
|
|
7032
|
-
return r;
|
|
7033
|
-
}
|
|
7034
|
-
function useOptionalDepRegistry() {
|
|
7035
|
-
return useContext(DepRegistryContext);
|
|
7036
|
-
}
|
|
7037
|
-
function useDepSource(name, source) {
|
|
7038
|
-
const registry22 = useDepRegistry();
|
|
7039
|
-
const sourceRef = useRef(source);
|
|
7040
|
-
sourceRef.current = source;
|
|
7656
|
+
function useDetectedFacts() {
|
|
7657
|
+
const [facts, setFacts] = useState(detectOnce);
|
|
7041
7658
|
useEffect(() => {
|
|
7042
|
-
|
|
7043
|
-
|
|
7044
|
-
|
|
7045
|
-
|
|
7046
|
-
|
|
7047
|
-
|
|
7048
|
-
|
|
7049
|
-
|
|
7050
|
-
|
|
7051
|
-
|
|
7052
|
-
|
|
7053
|
-
|
|
7054
|
-
|
|
7055
|
-
|
|
7056
|
-
|
|
7057
|
-
|
|
7058
|
-
|
|
7059
|
-
|
|
7060
|
-
|
|
7061
|
-
|
|
7062
|
-
console.warn(message);
|
|
7063
|
-
throw new Error(message);
|
|
7064
|
-
}
|
|
7065
|
-
return Reflect.get(target, prop, receiver);
|
|
7066
|
-
}
|
|
7067
|
-
});
|
|
7068
|
-
}
|
|
7069
|
-
return deps;
|
|
7070
|
-
}
|
|
7071
|
-
var ActionDisabledReason = {
|
|
7072
|
-
SelectionRequired: "selection-required",
|
|
7073
|
-
NotApplicable: "not-applicable",
|
|
7074
|
-
/** Sentinel: the predicate threw. Surfaced by `evaluateEnabled`'s catch. */
|
|
7075
|
-
PredicateThrew: "predicate-threw"
|
|
7076
|
-
};
|
|
7077
|
-
var enabledSlowWarned = /* @__PURE__ */ new Set();
|
|
7078
|
-
var enabledThrewWarned = /* @__PURE__ */ new Set();
|
|
7079
|
-
var ENABLED_BUDGET_MS = 4;
|
|
7080
|
-
function evaluateEnabled(action, deps) {
|
|
7081
|
-
if (!action.enabled) return { enabled: true };
|
|
7082
|
-
const isDev3 = typeof process !== "undefined" ? process.env.NODE_ENV !== "production" : true;
|
|
7083
|
-
const now = typeof performance !== "undefined" && typeof performance.now === "function" ? () => performance.now() : () => Date.now();
|
|
7084
|
-
const start = isDev3 ? now() : 0;
|
|
7085
|
-
try {
|
|
7086
|
-
const result = action.enabled(deps);
|
|
7087
|
-
if (isDev3) {
|
|
7088
|
-
const elapsed = now() - start;
|
|
7089
|
-
if (elapsed > ENABLED_BUDGET_MS && !enabledSlowWarned.has(action.id)) {
|
|
7090
|
-
enabledSlowWarned.add(action.id);
|
|
7091
|
-
console.warn(
|
|
7092
|
-
`weasel actions: enabled() for action "${action.id}" took ${elapsed.toFixed(2)}ms (budget ${ENABLED_BUDGET_MS}ms). The predicate must be pure and fast \u2014 see Action.enabled JSDoc.`
|
|
7093
|
-
);
|
|
7094
|
-
}
|
|
7095
|
-
}
|
|
7096
|
-
if (result === true) return { enabled: true };
|
|
7097
|
-
return { enabled: false, reason: result };
|
|
7098
|
-
} catch (e) {
|
|
7099
|
-
if (isDev3 && !enabledThrewWarned.has(action.id)) {
|
|
7100
|
-
enabledThrewWarned.add(action.id);
|
|
7101
|
-
console.warn(
|
|
7102
|
-
`weasel actions: enabled() for action "${action.id}" threw; treating as disabled. The predicate must not throw \u2014 see Action.enabled JSDoc.`,
|
|
7103
|
-
e
|
|
7659
|
+
if (!hasMatchMedia()) return;
|
|
7660
|
+
const coarseMq = window.matchMedia(COARSE_QUERY);
|
|
7661
|
+
const hoverMq = window.matchMedia(HOVER_QUERY);
|
|
7662
|
+
const onCoarse = (e) => setFacts((f) => ({ ...f, coarsePointer: e.matches }));
|
|
7663
|
+
const onHover = (e) => setFacts((f) => ({ ...f, canHover: e.matches }));
|
|
7664
|
+
coarseMq.addEventListener("change", onCoarse);
|
|
7665
|
+
hoverMq.addEventListener("change", onHover);
|
|
7666
|
+
let disposed = false;
|
|
7667
|
+
let densityMq = null;
|
|
7668
|
+
const onDensity = () => {
|
|
7669
|
+
if (disposed) return;
|
|
7670
|
+
const next = window.devicePixelRatio || 1;
|
|
7671
|
+
setFacts((f) => f.dpr === next ? f : { ...f, dpr: next });
|
|
7672
|
+
arm();
|
|
7673
|
+
};
|
|
7674
|
+
function arm() {
|
|
7675
|
+
if (disposed) return;
|
|
7676
|
+
densityMq?.removeEventListener("change", onDensity);
|
|
7677
|
+
densityMq = window.matchMedia(
|
|
7678
|
+
`(resolution: ${window.devicePixelRatio || 1}dppx)`
|
|
7104
7679
|
);
|
|
7680
|
+
densityMq.addEventListener("change", onDensity);
|
|
7105
7681
|
}
|
|
7106
|
-
|
|
7107
|
-
|
|
7108
|
-
|
|
7109
|
-
|
|
7110
|
-
|
|
7111
|
-
|
|
7112
|
-
|
|
7113
|
-
|
|
7114
|
-
|
|
7115
|
-
|
|
7116
|
-
|
|
7117
|
-
}
|
|
7118
|
-
if (RESERVED_ID_NAMES.has(id)) {
|
|
7119
|
-
throw new Error(
|
|
7120
|
-
`weasel: action id "${id}" collides with a reserved phase keyword (reserved: ${[...RESERVED_ID_NAMES].join(", ")})`
|
|
7121
|
-
);
|
|
7122
|
-
}
|
|
7123
|
-
}
|
|
7124
|
-
function validateActionDefaultBinding(action) {
|
|
7125
|
-
const gs = action.defaultBinding;
|
|
7126
|
-
if (!gs) return;
|
|
7127
|
-
const entries = Array.isArray(gs) ? gs : [gs];
|
|
7128
|
-
for (const entry of entries) {
|
|
7129
|
-
const spec = "kind" in entry ? entry : entry.spec;
|
|
7130
|
-
const phase = spec.phase;
|
|
7131
|
-
if (phase === void 0) continue;
|
|
7132
|
-
const atoms = Array.isArray(phase) ? phase : [{ channel: "&", phase }];
|
|
7133
|
-
for (const atom of atoms) {
|
|
7134
|
-
if (atom.channel === "&") {
|
|
7135
|
-
throw new Error(
|
|
7136
|
-
`weasel: action "${action.id}" defaultBinding uses '&' channel which has no owning tool to resolve to. Use a named tool channel (e.g. '[rect:engaged]') or '*' instead, or move this binding to the tool's own Tool.bindings.`
|
|
7137
|
-
);
|
|
7138
|
-
}
|
|
7139
|
-
}
|
|
7140
|
-
}
|
|
7141
|
-
}
|
|
7142
|
-
var ActionsContext = createContext(null);
|
|
7143
|
-
function ActionsProvider({ children }) {
|
|
7144
|
-
const actionsRef = useRef(/* @__PURE__ */ new Map());
|
|
7145
|
-
const versionRef = useRef(0);
|
|
7146
|
-
const cachedRef = useRef([]);
|
|
7147
|
-
const cachedVerRef = useRef(-1);
|
|
7148
|
-
const listenersRef = useRef(/* @__PURE__ */ new Set());
|
|
7149
|
-
const depReg = useOptionalDepRegistry();
|
|
7150
|
-
const depRegRef = useRef(depReg);
|
|
7151
|
-
depRegRef.current = depReg;
|
|
7152
|
-
const wiredDepRegRef = useRef(null);
|
|
7153
|
-
const dispatcherRef = useRef(null);
|
|
7154
|
-
const registry22 = useMemo(() => {
|
|
7155
|
-
const snapshot = () => {
|
|
7156
|
-
const v = versionRef.current;
|
|
7157
|
-
if (cachedVerRef.current === v) return cachedRef.current;
|
|
7158
|
-
const out = Object.freeze(Array.from(actionsRef.current.values()));
|
|
7159
|
-
cachedRef.current = out;
|
|
7160
|
-
cachedVerRef.current = v;
|
|
7161
|
-
return out;
|
|
7162
|
-
};
|
|
7163
|
-
const notify2 = () => {
|
|
7164
|
-
for (const l of listenersRef.current) {
|
|
7165
|
-
try {
|
|
7166
|
-
l();
|
|
7167
|
-
} catch (err) {
|
|
7168
|
-
console.error("weasel ActionsRegistry: subscriber threw", err);
|
|
7169
|
-
}
|
|
7170
|
-
}
|
|
7171
|
-
};
|
|
7172
|
-
return {
|
|
7173
|
-
register: (action) => {
|
|
7174
|
-
validateActionId(action.id);
|
|
7175
|
-
validateActionDefaultBinding(action);
|
|
7176
|
-
actionsRef.current.set(action.id, action);
|
|
7177
|
-
versionRef.current++;
|
|
7178
|
-
notify2();
|
|
7179
|
-
return () => {
|
|
7180
|
-
const cur = actionsRef.current.get(action.id);
|
|
7181
|
-
if (cur === action) {
|
|
7182
|
-
actionsRef.current.delete(action.id);
|
|
7183
|
-
versionRef.current++;
|
|
7184
|
-
notify2();
|
|
7185
|
-
}
|
|
7186
|
-
};
|
|
7187
|
-
},
|
|
7188
|
-
unregister: (id) => {
|
|
7189
|
-
if (actionsRef.current.delete(id)) {
|
|
7190
|
-
versionRef.current++;
|
|
7191
|
-
notify2();
|
|
7192
|
-
}
|
|
7193
|
-
},
|
|
7194
|
-
list: () => snapshot(),
|
|
7195
|
-
trigger: (id, params) => {
|
|
7196
|
-
const a = actionsRef.current.get(id);
|
|
7197
|
-
if (!a) return false;
|
|
7198
|
-
try {
|
|
7199
|
-
if (a.invoker && a.invoker.timing === "immediate") {
|
|
7200
|
-
const r = wiredDepRegRef.current ?? depRegRef.current;
|
|
7201
|
-
const deps = !r ? {} : a.requires ? buildDepsFromRequires(a, r) : {
|
|
7202
|
-
selection: r.get("selection"),
|
|
7203
|
-
scene: r.get("scene"),
|
|
7204
|
-
history: r.get("history"),
|
|
7205
|
-
view: r.get("view"),
|
|
7206
|
-
pointer: r.get("pointer"),
|
|
7207
|
-
activeTool: r.get("activeTool"),
|
|
7208
|
-
booleansAdapter: r.get("booleansAdapter")
|
|
7209
|
-
};
|
|
7210
|
-
a.invoker.run(deps, params);
|
|
7211
|
-
}
|
|
7212
|
-
} catch (err) {
|
|
7213
|
-
console.error(`weasel ActionsRegistry: action "${id}" threw`, err);
|
|
7214
|
-
}
|
|
7215
|
-
return true;
|
|
7216
|
-
},
|
|
7217
|
-
subscribe: (listener) => {
|
|
7218
|
-
listenersRef.current.add(listener);
|
|
7219
|
-
return () => {
|
|
7220
|
-
listenersRef.current.delete(listener);
|
|
7221
|
-
};
|
|
7222
|
-
},
|
|
7223
|
-
setDispatcher: (d) => {
|
|
7224
|
-
dispatcherRef.current = d;
|
|
7225
|
-
},
|
|
7226
|
-
setDepRegistry: (r) => {
|
|
7227
|
-
wiredDepRegRef.current = r;
|
|
7228
|
-
},
|
|
7229
|
-
begin: (id, params) => {
|
|
7230
|
-
const disp = dispatcherRef.current;
|
|
7231
|
-
if (!disp) return null;
|
|
7232
|
-
const r = wiredDepRegRef.current ?? depRegRef.current;
|
|
7233
|
-
const deps = r ? {
|
|
7234
|
-
selection: r.get("selection"),
|
|
7235
|
-
scene: r.get("scene"),
|
|
7236
|
-
history: r.get("history"),
|
|
7237
|
-
view: r.get("view"),
|
|
7238
|
-
pointer: r.get("pointer"),
|
|
7239
|
-
activeTool: r.get("activeTool"),
|
|
7240
|
-
booleansAdapter: r.get("booleansAdapter")
|
|
7241
|
-
} : {};
|
|
7242
|
-
return disp.beginUiOngoing(id, deps, params);
|
|
7243
|
-
}
|
|
7682
|
+
arm();
|
|
7683
|
+
setFacts({
|
|
7684
|
+
coarsePointer: coarseMq.matches,
|
|
7685
|
+
canHover: hoverMq.matches,
|
|
7686
|
+
dpr: window.devicePixelRatio || 1
|
|
7687
|
+
});
|
|
7688
|
+
return () => {
|
|
7689
|
+
disposed = true;
|
|
7690
|
+
coarseMq.removeEventListener("change", onCoarse);
|
|
7691
|
+
hoverMq.removeEventListener("change", onHover);
|
|
7692
|
+
densityMq?.removeEventListener("change", onDensity);
|
|
7244
7693
|
};
|
|
7245
7694
|
}, []);
|
|
7246
|
-
return
|
|
7695
|
+
return facts;
|
|
7696
|
+
}
|
|
7697
|
+
var DeviceProfileContext = createContext(null);
|
|
7698
|
+
function useDeviceProfile(overrides) {
|
|
7699
|
+
const provided = useContext(DeviceProfileContext);
|
|
7700
|
+
const detected = useDetectedFacts();
|
|
7701
|
+
const base = provided ?? detected;
|
|
7702
|
+
const { coarsePointer: coarsePointer2, canHover: canHover2, dpr, targetScale } = overrides ?? {};
|
|
7703
|
+
return useMemo(
|
|
7704
|
+
() => resolveDeviceProfile(base, {
|
|
7705
|
+
...coarsePointer2 !== void 0 ? { coarsePointer: coarsePointer2 } : {},
|
|
7706
|
+
...canHover2 !== void 0 ? { canHover: canHover2 } : {},
|
|
7707
|
+
...dpr !== void 0 ? { dpr } : {},
|
|
7708
|
+
...targetScale !== void 0 ? { targetScale } : {}
|
|
7709
|
+
}),
|
|
7710
|
+
[base.coarsePointer, base.canHover, base.dpr, coarsePointer2, canHover2, dpr, targetScale]
|
|
7711
|
+
);
|
|
7712
|
+
}
|
|
7713
|
+
function DeviceProfileProvider({ value, children }) {
|
|
7714
|
+
const profile = useDeviceProfile(value);
|
|
7715
|
+
return createElement(DeviceProfileContext.Provider, { value: profile }, children);
|
|
7716
|
+
}
|
|
7717
|
+
var HANDLE_BASE_PX = 8;
|
|
7718
|
+
var ANCHOR_HIT_BASE_PX = 8;
|
|
7719
|
+
var ROTATION_HANDLE_BASE_PX = 24;
|
|
7720
|
+
function zoomAt(view, anchor, factor, opts) {
|
|
7721
|
+
const fx = typeof factor === "number" ? factor : factor.x;
|
|
7722
|
+
const fy = typeof factor === "number" ? factor : factor.y;
|
|
7723
|
+
const minX = typeof opts?.min === "number" ? opts.min : opts?.min?.x ?? 0.1;
|
|
7724
|
+
const minY = typeof opts?.min === "number" ? opts.min : opts?.min?.y ?? 0.1;
|
|
7725
|
+
const maxX = typeof opts?.max === "number" ? opts.max : opts?.max?.x ?? 8;
|
|
7726
|
+
const maxY = typeof opts?.max === "number" ? opts.max : opts?.max?.y ?? 8;
|
|
7727
|
+
const nextX = Math.min(maxX, Math.max(minX, view.scale.x * fx));
|
|
7728
|
+
const nextY = Math.min(maxY, Math.max(minY, view.scale.y * fy));
|
|
7729
|
+
const worldX = anchor.x / view.scale.x + view.x;
|
|
7730
|
+
const worldY = anchor.y / view.scale.y + view.y;
|
|
7731
|
+
return {
|
|
7732
|
+
scale: { x: nextX, y: nextY },
|
|
7733
|
+
x: worldX - anchor.x / nextX,
|
|
7734
|
+
y: worldY - anchor.y / nextY
|
|
7735
|
+
};
|
|
7247
7736
|
}
|
|
7248
|
-
function
|
|
7249
|
-
|
|
7737
|
+
function clampView(view, bounds, canvas) {
|
|
7738
|
+
const visW = canvas.width / view.scale.x;
|
|
7739
|
+
const visH = canvas.height / view.scale.y;
|
|
7740
|
+
let x;
|
|
7741
|
+
if (visW >= bounds.width) {
|
|
7742
|
+
x = bounds.x + (bounds.width - visW) / 2;
|
|
7743
|
+
} else {
|
|
7744
|
+
const minX = bounds.x;
|
|
7745
|
+
const maxX = bounds.x + bounds.width - visW;
|
|
7746
|
+
x = view.x < minX ? minX : view.x > maxX ? maxX : view.x;
|
|
7747
|
+
}
|
|
7748
|
+
let y;
|
|
7749
|
+
if (visH >= bounds.height) {
|
|
7750
|
+
y = bounds.y + (bounds.height - visH) / 2;
|
|
7751
|
+
} else {
|
|
7752
|
+
const minY = bounds.y;
|
|
7753
|
+
const maxY = bounds.y + bounds.height - visH;
|
|
7754
|
+
y = view.y < minY ? minY : view.y > maxY ? maxY : view.y;
|
|
7755
|
+
}
|
|
7756
|
+
return x === view.x && y === view.y ? view : { x, y, scale: view.scale };
|
|
7757
|
+
}
|
|
7758
|
+
function isEditableTarget2(target) {
|
|
7759
|
+
if (!target || !(target instanceof Element)) return false;
|
|
7760
|
+
const tag = target.tagName;
|
|
7761
|
+
if (tag === "INPUT" || tag === "TEXTAREA") return true;
|
|
7762
|
+
if (target.isContentEditable) return true;
|
|
7763
|
+
const ce = target.getAttribute("contenteditable");
|
|
7764
|
+
return ce === "true" || ce === "";
|
|
7250
7765
|
}
|
|
7251
7766
|
function resolveParams(params) {
|
|
7252
7767
|
if (params === void 0) return void 0;
|
|
@@ -7782,6 +8297,60 @@ var moveAction = {
|
|
|
7782
8297
|
*/
|
|
7783
8298
|
enabled: () => true
|
|
7784
8299
|
};
|
|
8300
|
+
function poseRotationOf(pose) {
|
|
8301
|
+
const p = pose;
|
|
8302
|
+
if (p.rotation && p.x != null && p.y != null && p.width != null && p.height != null) {
|
|
8303
|
+
return { cx: p.x + p.width / 2, cy: p.y + p.height / 2, rotation: p.rotation };
|
|
8304
|
+
}
|
|
8305
|
+
return null;
|
|
8306
|
+
}
|
|
8307
|
+
function rotatePathAround(path, cx, cy, rotation) {
|
|
8308
|
+
if (path.kind === "rect") {
|
|
8309
|
+
return rotateRectCorners(path.x, path.y, path.width, path.height, cx, cy, rotation);
|
|
8310
|
+
}
|
|
8311
|
+
return rotatePolygon(path, cx, cy, rotation);
|
|
8312
|
+
}
|
|
8313
|
+
function rotatePolygon(path, cx, cy, rotation) {
|
|
8314
|
+
const cos = Math.cos(rotation);
|
|
8315
|
+
const sin = Math.sin(rotation);
|
|
8316
|
+
const { commands, coords } = path;
|
|
8317
|
+
const next = new Float32Array(coords.length);
|
|
8318
|
+
let ci = 0;
|
|
8319
|
+
for (let i = 0; i < commands.length; i++) {
|
|
8320
|
+
const len = COORD_COUNT3[commands[i]];
|
|
8321
|
+
for (let k = 0; k < len; k += 2) {
|
|
8322
|
+
const dx = coords[ci + k] - cx;
|
|
8323
|
+
const dy = coords[ci + k + 1] - cy;
|
|
8324
|
+
next[ci + k] = cx + dx * cos - dy * sin;
|
|
8325
|
+
next[ci + k + 1] = cy + dx * sin + dy * cos;
|
|
8326
|
+
}
|
|
8327
|
+
ci += len;
|
|
8328
|
+
}
|
|
8329
|
+
return { kind: "polygon", commands: path.commands, coords: next, fillRule: path.fillRule };
|
|
8330
|
+
}
|
|
8331
|
+
function rotateRectCorners(x, y, w, h, cx, cy, rotation) {
|
|
8332
|
+
const cos = Math.cos(rotation);
|
|
8333
|
+
const sin = Math.sin(rotation);
|
|
8334
|
+
const rotate2 = (px, py) => {
|
|
8335
|
+
const dx = px - cx;
|
|
8336
|
+
const dy = py - cy;
|
|
8337
|
+
return [cx + dx * cos - dy * sin, cy + dx * sin + dy * cos];
|
|
8338
|
+
};
|
|
8339
|
+
const [x0, y0] = rotate2(x, y);
|
|
8340
|
+
const [x1, y1] = rotate2(x + w, y);
|
|
8341
|
+
const [x2, y2] = rotate2(x + w, y + h);
|
|
8342
|
+
const [x3, y3] = rotate2(x, y + h);
|
|
8343
|
+
const commands = new Uint8Array([PATH_M2, PATH_L2, PATH_L2, PATH_L2, PATH_Z2]);
|
|
8344
|
+
const coords = new Float32Array([x0, y0, x1, y1, x2, y2, x3, y3]);
|
|
8345
|
+
return { kind: "polygon", commands, coords, fillRule: "nonzero" };
|
|
8346
|
+
}
|
|
8347
|
+
var COORD_COUNT3 = {
|
|
8348
|
+
[PATH_M2]: 2,
|
|
8349
|
+
[PATH_L2]: 2,
|
|
8350
|
+
[PATH_C2]: 6,
|
|
8351
|
+
[PATH_Q2]: 4,
|
|
8352
|
+
[PATH_Z2]: 0
|
|
8353
|
+
};
|
|
7785
8354
|
function aabbCenter(bounds) {
|
|
7786
8355
|
return { x: bounds.x + bounds.width / 2, y: bounds.y + bounds.height / 2 };
|
|
7787
8356
|
}
|
|
@@ -8074,6 +8643,13 @@ var resizeAction = {
|
|
|
8074
8643
|
const computePose = (id) => {
|
|
8075
8644
|
const sp = scratch.startPoses.get(id);
|
|
8076
8645
|
if (sp === void 0) return void 0;
|
|
8646
|
+
if (isGroupPath && poseRotationOf(sp) !== null) {
|
|
8647
|
+
return remapRotatedLeaf(
|
|
8648
|
+
sp,
|
|
8649
|
+
scratch.originBounds,
|
|
8650
|
+
proposedBounds
|
|
8651
|
+
);
|
|
8652
|
+
}
|
|
8077
8653
|
return scratch.geometry.remapBounds(sp, scratch.originBounds, proposedBounds);
|
|
8078
8654
|
};
|
|
8079
8655
|
scratch.previews.clear();
|
|
@@ -8694,6 +9270,10 @@ var clearSelectionAction = {
|
|
|
8694
9270
|
return true;
|
|
8695
9271
|
}
|
|
8696
9272
|
};
|
|
9273
|
+
var counter2 = 0;
|
|
9274
|
+
function freshNodeId() {
|
|
9275
|
+
return asNodeId(`n${(counter2++).toString(36)}-${Math.random().toString(36).slice(2, 8)}`);
|
|
9276
|
+
}
|
|
8697
9277
|
function translatePose(pose, dx, dy) {
|
|
8698
9278
|
const p = pose;
|
|
8699
9279
|
return {
|
|
@@ -8702,10 +9282,6 @@ function translatePose(pose, dx, dy) {
|
|
|
8702
9282
|
y: (p["y"] ?? 0) + dy
|
|
8703
9283
|
};
|
|
8704
9284
|
}
|
|
8705
|
-
var cloneIdCounter = 0;
|
|
8706
|
-
function freshCloneId() {
|
|
8707
|
-
return asNodeId(`n${(cloneIdCounter++).toString(36)}-${Math.random().toString(36).slice(2, 8)}`);
|
|
8708
|
-
}
|
|
8709
9285
|
var cloneAction = {
|
|
8710
9286
|
id: "clone",
|
|
8711
9287
|
label: "Clone",
|
|
@@ -8772,7 +9348,7 @@ var cloneAction = {
|
|
|
8772
9348
|
if (origin === void 0 || !originNode) continue;
|
|
8773
9349
|
const newPose = translatePose(origin, dx, dy);
|
|
8774
9350
|
const node = {
|
|
8775
|
-
id:
|
|
9351
|
+
id: freshNodeId(),
|
|
8776
9352
|
kind: originNode.kind,
|
|
8777
9353
|
layer: originNode.layer,
|
|
8778
9354
|
pose: newPose,
|
|
@@ -8913,7 +9489,13 @@ function makeViewportZoomAction(opts = {}) {
|
|
|
8913
9489
|
};
|
|
8914
9490
|
}
|
|
8915
9491
|
makeViewportZoomAction();
|
|
8916
|
-
|
|
9492
|
+
function bodyPredicate(fn) {
|
|
9493
|
+
return Object.assign(fn, { readsAffordance: false });
|
|
9494
|
+
}
|
|
9495
|
+
var isBody = bodyPredicate((_target, bodyTarget) => bodyTarget === "selected-body" || bodyTarget === "unselected-body");
|
|
9496
|
+
bodyPredicate((_target, bodyTarget) => bodyTarget === "selected-body");
|
|
9497
|
+
bodyPredicate((_target, bodyTarget) => bodyTarget === "unselected-body");
|
|
9498
|
+
bodyPredicate((_target, bodyTarget) => bodyTarget === "empty");
|
|
8917
9499
|
function kindOf(target) {
|
|
8918
9500
|
if (typeof target !== "object" || target === null) return void 0;
|
|
8919
9501
|
const k = target.kind;
|
|
@@ -8952,17 +9534,17 @@ function splitCubicAtT(p0, p1, p2, p3, t) {
|
|
|
8952
9534
|
function fitCubicThroughDeletion(prev, next) {
|
|
8953
9535
|
const dx = next.x - prev.x;
|
|
8954
9536
|
const dy = next.y - prev.y;
|
|
8955
|
-
const
|
|
9537
|
+
const dist3 = Math.sqrt(dx * dx + dy * dy);
|
|
8956
9538
|
const c1DirX = prev.outHandle ? prev.outHandle.x - prev.x : dx;
|
|
8957
9539
|
const c1DirY = prev.outHandle ? prev.outHandle.y - prev.y : dy;
|
|
8958
|
-
const c1 = controlOnSide(prev, c1DirX, c1DirY,
|
|
9540
|
+
const c1 = controlOnSide(prev, c1DirX, c1DirY, dist3, 1 / 3);
|
|
8959
9541
|
const c2DirX = next.inHandle ? next.inHandle.x - next.x : -dx;
|
|
8960
9542
|
const c2DirY = next.inHandle ? next.inHandle.y - next.y : -dy;
|
|
8961
|
-
const c2 = controlOnSide(next, c2DirX, c2DirY,
|
|
9543
|
+
const c2 = controlOnSide(next, c2DirX, c2DirY, dist3, 1 / 3);
|
|
8962
9544
|
return { c1, c2 };
|
|
8963
9545
|
}
|
|
8964
|
-
function controlOnSide(anchor, dirX, dirY,
|
|
8965
|
-
const targetDist =
|
|
9546
|
+
function controlOnSide(anchor, dirX, dirY, dist3, fraction) {
|
|
9547
|
+
const targetDist = dist3 * fraction;
|
|
8966
9548
|
const len = Math.hypot(dirX, dirY) || 1;
|
|
8967
9549
|
const ux = dirX / len, uy = dirY / len;
|
|
8968
9550
|
return { x: anchor.x + ux * targetDist, y: anchor.y + uy * targetDist };
|
|
@@ -9219,60 +9801,6 @@ function anchorsInRect(set, rect) {
|
|
|
9219
9801
|
}
|
|
9220
9802
|
return hits;
|
|
9221
9803
|
}
|
|
9222
|
-
function poseRotationOf(pose) {
|
|
9223
|
-
const p = pose;
|
|
9224
|
-
if (p.rotation && p.x != null && p.y != null && p.width != null && p.height != null) {
|
|
9225
|
-
return { cx: p.x + p.width / 2, cy: p.y + p.height / 2, rotation: p.rotation };
|
|
9226
|
-
}
|
|
9227
|
-
return null;
|
|
9228
|
-
}
|
|
9229
|
-
function rotatePathAround(path, cx, cy, rotation) {
|
|
9230
|
-
if (path.kind === "rect") {
|
|
9231
|
-
return rotateRectCorners(path.x, path.y, path.width, path.height, cx, cy, rotation);
|
|
9232
|
-
}
|
|
9233
|
-
return rotatePolygon(path, cx, cy, rotation);
|
|
9234
|
-
}
|
|
9235
|
-
function rotatePolygon(path, cx, cy, rotation) {
|
|
9236
|
-
const cos = Math.cos(rotation);
|
|
9237
|
-
const sin = Math.sin(rotation);
|
|
9238
|
-
const { commands, coords } = path;
|
|
9239
|
-
const next = new Float32Array(coords.length);
|
|
9240
|
-
let ci = 0;
|
|
9241
|
-
for (let i = 0; i < commands.length; i++) {
|
|
9242
|
-
const len = COORD_COUNT3[commands[i]];
|
|
9243
|
-
for (let k = 0; k < len; k += 2) {
|
|
9244
|
-
const dx = coords[ci + k] - cx;
|
|
9245
|
-
const dy = coords[ci + k + 1] - cy;
|
|
9246
|
-
next[ci + k] = cx + dx * cos - dy * sin;
|
|
9247
|
-
next[ci + k + 1] = cy + dx * sin + dy * cos;
|
|
9248
|
-
}
|
|
9249
|
-
ci += len;
|
|
9250
|
-
}
|
|
9251
|
-
return { kind: "polygon", commands: path.commands, coords: next, fillRule: path.fillRule };
|
|
9252
|
-
}
|
|
9253
|
-
function rotateRectCorners(x, y, w, h, cx, cy, rotation) {
|
|
9254
|
-
const cos = Math.cos(rotation);
|
|
9255
|
-
const sin = Math.sin(rotation);
|
|
9256
|
-
const rotate2 = (px, py) => {
|
|
9257
|
-
const dx = px - cx;
|
|
9258
|
-
const dy = py - cy;
|
|
9259
|
-
return [cx + dx * cos - dy * sin, cy + dx * sin + dy * cos];
|
|
9260
|
-
};
|
|
9261
|
-
const [x0, y0] = rotate2(x, y);
|
|
9262
|
-
const [x1, y1] = rotate2(x + w, y);
|
|
9263
|
-
const [x2, y2] = rotate2(x + w, y + h);
|
|
9264
|
-
const [x3, y3] = rotate2(x, y + h);
|
|
9265
|
-
const commands = new Uint8Array([PATH_M2, PATH_L2, PATH_L2, PATH_L2, PATH_Z2]);
|
|
9266
|
-
const coords = new Float32Array([x0, y0, x1, y1, x2, y2, x3, y3]);
|
|
9267
|
-
return { kind: "polygon", commands, coords, fillRule: "nonzero" };
|
|
9268
|
-
}
|
|
9269
|
-
var COORD_COUNT3 = {
|
|
9270
|
-
[PATH_M2]: 2,
|
|
9271
|
-
[PATH_L2]: 2,
|
|
9272
|
-
[PATH_C2]: 6,
|
|
9273
|
-
[PATH_Q2]: 4,
|
|
9274
|
-
[PATH_Z2]: 0
|
|
9275
|
-
};
|
|
9276
9804
|
function pathInPoseFrame(path, pose) {
|
|
9277
9805
|
if (path.kind === "rect") {
|
|
9278
9806
|
if (path.x === pose.x && path.y === pose.y && path.width === pose.width && path.height === pose.height) return path;
|
|
@@ -9555,6 +10083,119 @@ var pinchZoomAction = {
|
|
|
9555
10083
|
},
|
|
9556
10084
|
enabled: () => true
|
|
9557
10085
|
};
|
|
10086
|
+
function hostIndex(scene, id) {
|
|
10087
|
+
const node = scene.get(id);
|
|
10088
|
+
const siblings = node?.parent == null ? scene.roots : scene.childrenOf(node.parent);
|
|
10089
|
+
return siblings.indexOf(id);
|
|
10090
|
+
}
|
|
10091
|
+
function hasSelectedAncestor(scene, id, set) {
|
|
10092
|
+
let parent = scene.get(id)?.parent ?? null;
|
|
10093
|
+
while (parent != null) {
|
|
10094
|
+
if (set.has(parent)) return true;
|
|
10095
|
+
parent = scene.get(parent)?.parent ?? null;
|
|
10096
|
+
}
|
|
10097
|
+
return false;
|
|
10098
|
+
}
|
|
10099
|
+
function buildDeleteOps(scene, ids, label) {
|
|
10100
|
+
const set = new Set(ids);
|
|
10101
|
+
const ops = [];
|
|
10102
|
+
for (const id of ids) {
|
|
10103
|
+
const node = scene.get(id);
|
|
10104
|
+
if (!node) continue;
|
|
10105
|
+
if (hasSelectedAncestor(scene, id, set)) continue;
|
|
10106
|
+
ops.push(createDeleteOp({
|
|
10107
|
+
node,
|
|
10108
|
+
index: hostIndex(scene, id),
|
|
10109
|
+
label
|
|
10110
|
+
}));
|
|
10111
|
+
}
|
|
10112
|
+
return ops;
|
|
10113
|
+
}
|
|
10114
|
+
var deleteAction = {
|
|
10115
|
+
id: "delete",
|
|
10116
|
+
label: "Delete",
|
|
10117
|
+
// Suppressed while any tool is mid-gesture — accidentally hitting
|
|
10118
|
+
// Delete during a drag shouldn't wipe the selection out from under
|
|
10119
|
+
// the in-flight handle.
|
|
10120
|
+
defaultBinding: {
|
|
10121
|
+
kind: "key",
|
|
10122
|
+
key: ["Delete", "Backspace"],
|
|
10123
|
+
phase: [{ channel: "*", phase: "initial" }]
|
|
10124
|
+
},
|
|
10125
|
+
eligible: { capability: "edits-page" },
|
|
10126
|
+
requires: ["scene", "selection", "applyOps"],
|
|
10127
|
+
invoker: {
|
|
10128
|
+
timing: "immediate",
|
|
10129
|
+
run: (deps) => {
|
|
10130
|
+
const selection = deps.selection;
|
|
10131
|
+
const scene = deps.scene;
|
|
10132
|
+
const applyOps = deps.applyOps;
|
|
10133
|
+
if (!selection || !scene) return;
|
|
10134
|
+
const ids = selection.get();
|
|
10135
|
+
if (ids.length === 0) return;
|
|
10136
|
+
const ops = buildDeleteOps(scene, ids, "Delete");
|
|
10137
|
+
if (ops.length > 0) {
|
|
10138
|
+
if (applyOps) applyOps(ops, "Delete");
|
|
10139
|
+
else scene.applyBatch(ops, "Delete", defaultCommitAdapter(scene));
|
|
10140
|
+
}
|
|
10141
|
+
selection.set([]);
|
|
10142
|
+
}
|
|
10143
|
+
},
|
|
10144
|
+
enabled: () => true
|
|
10145
|
+
};
|
|
10146
|
+
function requiresSelection(deps) {
|
|
10147
|
+
const selection = deps?.selection;
|
|
10148
|
+
if (!selection || selection.get().length === 0) {
|
|
10149
|
+
return ActionDisabledReason.SelectionRequired;
|
|
10150
|
+
}
|
|
10151
|
+
return true;
|
|
10152
|
+
}
|
|
10153
|
+
var clipboardCopyAction = {
|
|
10154
|
+
id: "clipboard.copy",
|
|
10155
|
+
label: "Copy",
|
|
10156
|
+
defaultBinding: { kind: "key", key: "c", mods: { mod: true } },
|
|
10157
|
+
// Same gate as `duplicate`: a mode that can neither edit the page nor own a
|
|
10158
|
+
// selection has nothing to copy. Notably excludes `text-edit`, where Cmd+C
|
|
10159
|
+
// must stay the browser's own copy of the caret range.
|
|
10160
|
+
eligible: { capability: ["edits-page", "creates-selection"] },
|
|
10161
|
+
// `selection` is read by the `enabled` gate, not the invoker — the
|
|
10162
|
+
// dispatcher builds deps for both, and an undeclared read is undefined.
|
|
10163
|
+
requires: ["clipboard", "selection"],
|
|
10164
|
+
invoker: {
|
|
10165
|
+
timing: "immediate",
|
|
10166
|
+
run: (deps) => {
|
|
10167
|
+
deps.clipboard?.copy();
|
|
10168
|
+
}
|
|
10169
|
+
},
|
|
10170
|
+
enabled: requiresSelection
|
|
10171
|
+
};
|
|
10172
|
+
var clipboardCutAction = {
|
|
10173
|
+
id: "clipboard.cut",
|
|
10174
|
+
label: "Cut",
|
|
10175
|
+
defaultBinding: { kind: "key", key: "x", mods: { mod: true } },
|
|
10176
|
+
eligible: { capability: "edits-page" },
|
|
10177
|
+
requires: ["clipboard", "scene", "selection", "applyOps"],
|
|
10178
|
+
invoker: {
|
|
10179
|
+
timing: "immediate",
|
|
10180
|
+
run: (deps) => {
|
|
10181
|
+
const clipboard = deps.clipboard;
|
|
10182
|
+
const selection = deps.selection;
|
|
10183
|
+
const scene = deps.scene;
|
|
10184
|
+
const applyOps = deps.applyOps;
|
|
10185
|
+
if (!clipboard || !selection || !scene) return;
|
|
10186
|
+
const ids = selection.get();
|
|
10187
|
+
if (ids.length === 0) return;
|
|
10188
|
+
clipboard.copy();
|
|
10189
|
+
const ops = buildDeleteOps(scene, ids, "Cut");
|
|
10190
|
+
if (ops.length > 0) {
|
|
10191
|
+
if (applyOps) applyOps(ops, "Cut");
|
|
10192
|
+
else scene.applyBatch(ops, "Cut", defaultCommitAdapter(scene));
|
|
10193
|
+
}
|
|
10194
|
+
selection.set([]);
|
|
10195
|
+
}
|
|
10196
|
+
},
|
|
10197
|
+
enabled: requiresSelection
|
|
10198
|
+
};
|
|
9558
10199
|
var enterTextEditAction = {
|
|
9559
10200
|
id: "enterTextEdit",
|
|
9560
10201
|
label: "Enter text edit",
|
|
@@ -9677,26 +10318,47 @@ var selectAllAction = {
|
|
|
9677
10318
|
if (all.length === 0) return;
|
|
9678
10319
|
deps.selection?.set(all);
|
|
9679
10320
|
}
|
|
9680
|
-
}
|
|
9681
|
-
};
|
|
9682
|
-
|
|
9683
|
-
|
|
9684
|
-
const
|
|
9685
|
-
return
|
|
9686
|
-
}
|
|
9687
|
-
|
|
9688
|
-
id
|
|
9689
|
-
|
|
9690
|
-
|
|
9691
|
-
|
|
9692
|
-
|
|
9693
|
-
|
|
9694
|
-
|
|
9695
|
-
|
|
9696
|
-
|
|
9697
|
-
|
|
9698
|
-
|
|
9699
|
-
|
|
10321
|
+
}
|
|
10322
|
+
};
|
|
10323
|
+
var DUPLICATE_OFFSET = 12;
|
|
10324
|
+
function translatePose2(pose, dx, dy) {
|
|
10325
|
+
const p = pose;
|
|
10326
|
+
return { ...p, x: (p["x"] ?? 0) + dx, y: (p["y"] ?? 0) + dy };
|
|
10327
|
+
}
|
|
10328
|
+
function hasSelectedAncestor2(scene, id, set) {
|
|
10329
|
+
let parent = scene.get(id)?.parent ?? null;
|
|
10330
|
+
while (parent != null) {
|
|
10331
|
+
if (set.has(parent)) return true;
|
|
10332
|
+
parent = scene.get(parent)?.parent ?? null;
|
|
10333
|
+
}
|
|
10334
|
+
return false;
|
|
10335
|
+
}
|
|
10336
|
+
function copySubtree(scene, id, parent, offset, offsetDescendants, ops) {
|
|
10337
|
+
const node = scene.get(id);
|
|
10338
|
+
if (!node) return null;
|
|
10339
|
+
const newId = freshNodeId();
|
|
10340
|
+
const copy = {
|
|
10341
|
+
...node,
|
|
10342
|
+
id: newId,
|
|
10343
|
+
pose: translatePose2(node.pose, offset.dx, offset.dy),
|
|
10344
|
+
parent
|
|
10345
|
+
};
|
|
10346
|
+
ops.push(createInsertOp({ node: copy, label: "Duplicate" }));
|
|
10347
|
+
const childOffset = offsetDescendants ? offset : { dx: 0, dy: 0 };
|
|
10348
|
+
for (const child of scene.childrenOf(id)) {
|
|
10349
|
+
copySubtree(scene, child, newId, childOffset, offsetDescendants, ops);
|
|
10350
|
+
}
|
|
10351
|
+
return newId;
|
|
10352
|
+
}
|
|
10353
|
+
var duplicateAction = {
|
|
10354
|
+
id: "duplicate",
|
|
10355
|
+
label: "Duplicate",
|
|
10356
|
+
defaultBinding: { kind: "key", key: "d", mods: { mod: true } },
|
|
10357
|
+
eligible: { capability: ["edits-page", "creates-selection"] },
|
|
10358
|
+
// `selection` is read by the `enabled` gate as well as the invoker. An
|
|
10359
|
+
// undeclared read throws inside the dev-build deps Proxy before the gate can
|
|
10360
|
+
// answer, which is how this action spent a while doing nothing at all.
|
|
10361
|
+
requires: ["scene", "selection", "applyOps", "poseComposition"],
|
|
9700
10362
|
invoker: {
|
|
9701
10363
|
timing: "immediate",
|
|
9702
10364
|
run: (deps) => {
|
|
@@ -9706,40 +10368,22 @@ var deleteAction = {
|
|
|
9706
10368
|
if (!selection || !scene) return;
|
|
9707
10369
|
const ids = selection.get();
|
|
9708
10370
|
if (ids.length === 0) return;
|
|
10371
|
+
const set = new Set(ids);
|
|
10372
|
+
const offset = { dx: DUPLICATE_OFFSET, dy: DUPLICATE_OFFSET };
|
|
9709
10373
|
const ops = [];
|
|
10374
|
+
const newIds = [];
|
|
9710
10375
|
for (const id of ids) {
|
|
9711
|
-
const
|
|
10376
|
+
const nid = asNodeId(id);
|
|
10377
|
+
const node = scene.get(nid);
|
|
9712
10378
|
if (!node) continue;
|
|
9713
|
-
|
|
9714
|
-
|
|
9715
|
-
|
|
9716
|
-
label: "Delete"
|
|
9717
|
-
}));
|
|
9718
|
-
}
|
|
9719
|
-
if (ops.length > 0) {
|
|
9720
|
-
if (applyOps) applyOps(ops, "Delete");
|
|
9721
|
-
else scene.applyBatch(ops, "Delete", defaultCommitAdapter(scene));
|
|
10379
|
+
if (hasSelectedAncestor2(scene, nid, set)) continue;
|
|
10380
|
+
const newId = copySubtree(scene, nid, node.parent ?? null, offset, deps.poseComposition === void 0, ops);
|
|
10381
|
+
if (newId) newIds.push(newId);
|
|
9722
10382
|
}
|
|
9723
|
-
|
|
9724
|
-
|
|
9725
|
-
|
|
9726
|
-
|
|
9727
|
-
};
|
|
9728
|
-
function requiresSelection(deps) {
|
|
9729
|
-
const selection = deps?.selection;
|
|
9730
|
-
if (!selection || selection.get().length === 0) {
|
|
9731
|
-
return ActionDisabledReason.SelectionRequired;
|
|
9732
|
-
}
|
|
9733
|
-
return true;
|
|
9734
|
-
}
|
|
9735
|
-
var duplicateAction = {
|
|
9736
|
-
id: "duplicate",
|
|
9737
|
-
label: "Duplicate",
|
|
9738
|
-
defaultBinding: { kind: "key", key: "d", mods: { mod: true } },
|
|
9739
|
-
eligible: { capability: ["edits-page", "creates-selection"] },
|
|
9740
|
-
invoker: {
|
|
9741
|
-
timing: "immediate",
|
|
9742
|
-
run: (_deps, params) => {
|
|
10383
|
+
if (ops.length === 0) return;
|
|
10384
|
+
if (applyOps) applyOps(ops, "Duplicate");
|
|
10385
|
+
else scene.applyBatch(ops, "Duplicate", defaultCommitAdapter(scene));
|
|
10386
|
+
selection.set(newIds);
|
|
9743
10387
|
}
|
|
9744
10388
|
},
|
|
9745
10389
|
enabled: requiresSelection
|
|
@@ -10709,7 +11353,7 @@ var LABEL = {
|
|
|
10709
11353
|
divide: "Divide",
|
|
10710
11354
|
crop: "Crop"
|
|
10711
11355
|
};
|
|
10712
|
-
function
|
|
11356
|
+
function isEmpty2(p) {
|
|
10713
11357
|
return p.commands.length === 0;
|
|
10714
11358
|
}
|
|
10715
11359
|
function applyBooleanOp(adapter, op) {
|
|
@@ -10747,7 +11391,7 @@ function applyBooleanOp(adapter, op) {
|
|
|
10747
11391
|
break;
|
|
10748
11392
|
}
|
|
10749
11393
|
}
|
|
10750
|
-
results = results.filter((p) => !
|
|
11394
|
+
results = results.filter((p) => !isEmpty2(p));
|
|
10751
11395
|
if (results.length === 0) return { kind: "noop", reason: "empty-result" };
|
|
10752
11396
|
const topmostId = entries[entries.length - 1].id;
|
|
10753
11397
|
const topAnchor = adapter.getZOrder?.(topmostId);
|
|
@@ -11283,14 +11927,24 @@ var DEFAULT_PALETTE = [
|
|
|
11283
11927
|
"#7ab8d4",
|
|
11284
11928
|
"#d47a7a"
|
|
11285
11929
|
];
|
|
11930
|
+
function resolveFill(scratch, prev) {
|
|
11931
|
+
if (scratch.currentPaint) return scratch.currentPaint;
|
|
11932
|
+
const prevColor = typeof prev?.fill === "string" ? prev.fill : DEFAULT_FILL_COLOR;
|
|
11933
|
+
return mergeAlphaFromPrev(scratch.currentColor, prevColor);
|
|
11934
|
+
}
|
|
11286
11935
|
function refreshPreviews(scratch) {
|
|
11287
11936
|
scratch.previews.clear();
|
|
11288
11937
|
for (const id of scratch.ids) {
|
|
11289
11938
|
const prev = scratch.startData.get(id);
|
|
11290
|
-
|
|
11291
|
-
scratch.previews.set(id, { ...prev ?? {}, fill: next });
|
|
11939
|
+
scratch.previews.set(id, { ...prev ?? {}, fill: resolveFill(scratch, prev) });
|
|
11292
11940
|
}
|
|
11293
11941
|
}
|
|
11942
|
+
function readParams(params) {
|
|
11943
|
+
return {
|
|
11944
|
+
color: params?.color,
|
|
11945
|
+
paint: params?.paint
|
|
11946
|
+
};
|
|
11947
|
+
}
|
|
11294
11948
|
var setFillAction = {
|
|
11295
11949
|
id: "setFill",
|
|
11296
11950
|
label: "Set fill",
|
|
@@ -11305,9 +11959,10 @@ var setFillAction = {
|
|
|
11305
11959
|
if (!selection || !scene) return {};
|
|
11306
11960
|
const ids = selection.get();
|
|
11307
11961
|
if (ids.length === 0) return {};
|
|
11308
|
-
const
|
|
11309
|
-
const
|
|
11310
|
-
const
|
|
11962
|
+
const fromCtx = readParams(ctx.params);
|
|
11963
|
+
const fromOpts = readParams(opts?.params);
|
|
11964
|
+
const initialPaint = fromCtx.paint ?? fromOpts.paint;
|
|
11965
|
+
const initialColor = fromCtx.color ?? fromOpts.color ?? DEFAULT_STROKE_COLOR;
|
|
11311
11966
|
const startData = /* @__PURE__ */ new Map();
|
|
11312
11967
|
for (const id of ids) {
|
|
11313
11968
|
const node = scene.get(id);
|
|
@@ -11318,15 +11973,20 @@ var setFillAction = {
|
|
|
11318
11973
|
scene,
|
|
11319
11974
|
startData,
|
|
11320
11975
|
currentColor: initialColor,
|
|
11976
|
+
currentPaint: initialPaint,
|
|
11321
11977
|
previews: /* @__PURE__ */ new Map(),
|
|
11322
11978
|
applyOps
|
|
11323
11979
|
};
|
|
11324
11980
|
refreshPreviews(scratch);
|
|
11325
11981
|
return {
|
|
11326
11982
|
onMove(moveCtx) {
|
|
11327
|
-
const
|
|
11328
|
-
if (
|
|
11329
|
-
scratch.
|
|
11983
|
+
const { color, paint } = readParams(moveCtx.params);
|
|
11984
|
+
if (color === void 0 && paint === void 0) return;
|
|
11985
|
+
if (paint !== void 0) scratch.currentPaint = paint;
|
|
11986
|
+
if (color !== void 0) {
|
|
11987
|
+
scratch.currentColor = color;
|
|
11988
|
+
if (paint === void 0) scratch.currentPaint = void 0;
|
|
11989
|
+
}
|
|
11330
11990
|
refreshPreviews(scratch);
|
|
11331
11991
|
},
|
|
11332
11992
|
onEnd(_endCtx, reason) {
|
|
@@ -11336,8 +11996,7 @@ var setFillAction = {
|
|
|
11336
11996
|
}
|
|
11337
11997
|
const ops = [];
|
|
11338
11998
|
for (const id of scratch.ids) {
|
|
11339
|
-
const
|
|
11340
|
-
const merged = mergeAlphaFromPrev(scratch.currentColor, prev?.fill ?? DEFAULT_FILL_COLOR);
|
|
11999
|
+
const merged = resolveFill(scratch, scratch.startData.get(id));
|
|
11341
12000
|
const nodeNow = scratch.scene.get(id);
|
|
11342
12001
|
if (!nodeNow) continue;
|
|
11343
12002
|
const from = { ...nodeNow.data };
|
|
@@ -11719,7 +12378,8 @@ function resolveTextStyle(style) {
|
|
|
11719
12378
|
selectionColor: style.selectionColor ?? null,
|
|
11720
12379
|
letterSpacing: style.letterSpacing ?? DEFAULT_TEXT_STYLE.letterSpacing,
|
|
11721
12380
|
underline: style.underline ?? DEFAULT_TEXT_STYLE.underline,
|
|
11722
|
-
strikethrough: style.strikethrough ?? DEFAULT_TEXT_STYLE.strikethrough
|
|
12381
|
+
strikethrough: style.strikethrough ?? DEFAULT_TEXT_STYLE.strikethrough,
|
|
12382
|
+
...style.stroke !== void 0 ? { stroke: style.stroke } : {}
|
|
11723
12383
|
};
|
|
11724
12384
|
}
|
|
11725
12385
|
function numericWeight(w) {
|
|
@@ -11740,6 +12400,9 @@ function resolveRuns(runs, style) {
|
|
|
11740
12400
|
fontWeight: run.bold ? 700 : baseWeight,
|
|
11741
12401
|
fontStyle: run.italic ? "italic" : style.fontStyle,
|
|
11742
12402
|
fill: run.fill ?? style.fill,
|
|
12403
|
+
// Unlike the decorations below, a run's stroke *replaces* the node's
|
|
12404
|
+
// rather than adding to it — there is only one outline to paint.
|
|
12405
|
+
...(run.stroke ?? style.stroke) !== void 0 ? { stroke: run.stroke ?? style.stroke } : {},
|
|
11743
12406
|
letterSpacing: run.letterSpacing ?? style.letterSpacing,
|
|
11744
12407
|
// `||`, not `??`: run-level decorations are additive over the node style.
|
|
11745
12408
|
underline: run.underline || style.underline,
|
|
@@ -12116,7 +12779,9 @@ var KIT_STANDARD_DESCRIPTORS = [
|
|
|
12116
12779
|
setStrokeAction,
|
|
12117
12780
|
setFillOpacityAction,
|
|
12118
12781
|
setStrokeOpacityAction,
|
|
12119
|
-
ingestAction
|
|
12782
|
+
ingestAction,
|
|
12783
|
+
clipboardCopyAction,
|
|
12784
|
+
clipboardCutAction
|
|
12120
12785
|
];
|
|
12121
12786
|
function useStandardActions(opts) {
|
|
12122
12787
|
const optsRef = useRef(opts);
|
|
@@ -12191,6 +12856,54 @@ function targetRank(target) {
|
|
|
12191
12856
|
if (target.startsWith("affordance:")) return 2;
|
|
12192
12857
|
return 1;
|
|
12193
12858
|
}
|
|
12859
|
+
function targetConsultsAffordance(specTarget) {
|
|
12860
|
+
if (specTarget === void 0) return false;
|
|
12861
|
+
if (typeof specTarget === "object" && specTarget !== null && "kindOf" in specTarget) {
|
|
12862
|
+
const kindOf2 = specTarget.kindOf;
|
|
12863
|
+
return kindOf2?.readsAffordance !== false;
|
|
12864
|
+
}
|
|
12865
|
+
return typeof specTarget === "string" && specTarget.startsWith("affordance:");
|
|
12866
|
+
}
|
|
12867
|
+
function specTargetOf(spec) {
|
|
12868
|
+
return "target" in spec ? spec.target : void 0;
|
|
12869
|
+
}
|
|
12870
|
+
function claimOf(e) {
|
|
12871
|
+
return "affordance" in e ? e.affordance : void 0;
|
|
12872
|
+
}
|
|
12873
|
+
function claimGestureOf(e) {
|
|
12874
|
+
switch (e.kind) {
|
|
12875
|
+
case "pointerdown":
|
|
12876
|
+
case "click":
|
|
12877
|
+
return "pointer";
|
|
12878
|
+
case "doubleclick":
|
|
12879
|
+
return "doubleClick";
|
|
12880
|
+
case "contextmenu":
|
|
12881
|
+
return "contextMenu";
|
|
12882
|
+
case "longpress":
|
|
12883
|
+
return "longPress";
|
|
12884
|
+
case "wheel":
|
|
12885
|
+
return "wheel";
|
|
12886
|
+
default:
|
|
12887
|
+
return null;
|
|
12888
|
+
}
|
|
12889
|
+
}
|
|
12890
|
+
function isExclusiveClaim(e) {
|
|
12891
|
+
const claim = claimOf(e);
|
|
12892
|
+
if (claim?.strength !== "exclusive") return false;
|
|
12893
|
+
const gesture = claimGestureOf(e);
|
|
12894
|
+
if (gesture === null) return false;
|
|
12895
|
+
return claim.claimedKinds === void 0 || claim.claimedKinds.includes(gesture);
|
|
12896
|
+
}
|
|
12897
|
+
var warnedDeadClaims = /* @__PURE__ */ new Set();
|
|
12898
|
+
function reportDeadClaim(owner, warn) {
|
|
12899
|
+
if (process.env.NODE_ENV === "production") return;
|
|
12900
|
+
const key = String(owner);
|
|
12901
|
+
if (warnedDeadClaims.has(key)) return;
|
|
12902
|
+
warnedDeadClaims.add(key);
|
|
12903
|
+
warn(
|
|
12904
|
+
`[weasel] exclusive claim by "${key}" matched no binding: no \`affordance:\` or \`kindOf\` target resolved against it, so the press was dropped.`
|
|
12905
|
+
);
|
|
12906
|
+
}
|
|
12194
12907
|
function specificity(spec) {
|
|
12195
12908
|
const t = targetRank("target" in spec ? spec.target : void 0);
|
|
12196
12909
|
const mods = "mods" in spec ? spec.mods : void 0;
|
|
@@ -12207,12 +12920,14 @@ function compareSpecificity(a, b) {
|
|
|
12207
12920
|
}
|
|
12208
12921
|
return 0;
|
|
12209
12922
|
}
|
|
12210
|
-
function matchSorted(e, bindings, isMac, engagedChannels) {
|
|
12923
|
+
function matchSorted(e, bindings, isMac, engagedChannels, warn = (m) => console.warn(m)) {
|
|
12211
12924
|
const out = [];
|
|
12212
12925
|
const engaged = engagedChannels ?? EMPTY_ENGAGED2;
|
|
12926
|
+
const exclusive = isExclusiveClaim(e);
|
|
12927
|
+
const pool = exclusive ? bindings.filter((sb) => targetConsultsAffordance(specTargetOf(sb.binding.spec))) : bindings;
|
|
12213
12928
|
for (const scope of SCOPE_PRIORITY) {
|
|
12214
12929
|
const scopeMatches = [];
|
|
12215
|
-
for (const sb of
|
|
12930
|
+
for (const sb of pool) {
|
|
12216
12931
|
if (sb.scope !== scope) continue;
|
|
12217
12932
|
const phaseCtx = { selfChannel: sb.ownerToolId, engagedChannels: engaged };
|
|
12218
12933
|
if (matchSpec(e, sb.binding.spec, isMac, phaseCtx)) {
|
|
@@ -12222,9 +12937,32 @@ function matchSorted(e, bindings, isMac, engagedChannels) {
|
|
|
12222
12937
|
scopeMatches.sort((a, b) => compareSpecificity(a.binding.spec, b.binding.spec));
|
|
12223
12938
|
out.push(...scopeMatches);
|
|
12224
12939
|
}
|
|
12940
|
+
if (exclusive && out.length === 0 && bindings.length > 0) {
|
|
12941
|
+
reportDeadClaim(claimOf(e)?.owner, warn);
|
|
12942
|
+
}
|
|
12225
12943
|
return out;
|
|
12226
12944
|
}
|
|
12227
12945
|
var EMPTY_ENGAGED2 = /* @__PURE__ */ new Set();
|
|
12946
|
+
function liveScope(id, eligibility, state) {
|
|
12947
|
+
const tags = eligibility.capabilities;
|
|
12948
|
+
if (tags && tags.length > 0 && state.allows && !state.allows(tags)) return null;
|
|
12949
|
+
if (state.engagedIds?.has(id)) return "hotkey";
|
|
12950
|
+
if (eligibility.offhand && state.heldTriggers.has(eligibility.offhand)) return "hotkey";
|
|
12951
|
+
if (eligibility.focus && state.focusedId === id) return "active";
|
|
12952
|
+
if (eligibility.always || eligibility.claimed) return "ambient";
|
|
12953
|
+
return null;
|
|
12954
|
+
}
|
|
12955
|
+
function scopeBindings(entries, state) {
|
|
12956
|
+
const out = [];
|
|
12957
|
+
for (const entry of entries) {
|
|
12958
|
+
const scope = liveScope(entry.id, entry.eligibility ?? {}, state);
|
|
12959
|
+
if (scope === null) continue;
|
|
12960
|
+
for (const binding of entry.bindings ?? []) {
|
|
12961
|
+
out.push({ binding, scope, ownerToolId: entry.id });
|
|
12962
|
+
}
|
|
12963
|
+
}
|
|
12964
|
+
return out;
|
|
12965
|
+
}
|
|
12228
12966
|
var ALWAYS = { all: [] };
|
|
12229
12967
|
var NEVER = { any: [] };
|
|
12230
12968
|
function isAllRule(r) {
|
|
@@ -12281,6 +13019,14 @@ function evaluateSelector(s, ctx) {
|
|
|
12281
13019
|
const resizable2 = ctx.selectionResizable ?? true;
|
|
12282
13020
|
if (resizable2 !== s.resizable) return false;
|
|
12283
13021
|
}
|
|
13022
|
+
if (s.coarsePointer !== void 0) {
|
|
13023
|
+
const device = ctx.device ?? DEFAULT_DEVICE_PROFILE;
|
|
13024
|
+
if (device.coarsePointer !== s.coarsePointer) return false;
|
|
13025
|
+
}
|
|
13026
|
+
if (s.canHover !== void 0) {
|
|
13027
|
+
const device = ctx.device ?? DEFAULT_DEVICE_PROFILE;
|
|
13028
|
+
if (device.canHover !== s.canHover) return false;
|
|
13029
|
+
}
|
|
12284
13030
|
return true;
|
|
12285
13031
|
}
|
|
12286
13032
|
var MAX_RULE_DEPTH = 10;
|
|
@@ -12350,6 +13096,8 @@ cond({ selection: { empty: true } });
|
|
|
12350
13096
|
cond({ when: (c) => c.multiActive });
|
|
12351
13097
|
cond({ hovering: true });
|
|
12352
13098
|
cond({ hoveringSelected: true });
|
|
13099
|
+
cond({ coarsePointer: true });
|
|
13100
|
+
cond({ canHover: true });
|
|
12353
13101
|
var DEFAULT_ALLOWED_CAPABILITIES = /* @__PURE__ */ new Set([
|
|
12354
13102
|
...NORMAL.allows,
|
|
12355
13103
|
...IMPLICIT_TAGS
|
|
@@ -12509,6 +13257,7 @@ function filterEligible(matches, actionLookup, ruleCtx) {
|
|
|
12509
13257
|
return isEligible(action, ruleCtx);
|
|
12510
13258
|
});
|
|
12511
13259
|
}
|
|
13260
|
+
var EMPTY_TRIGGERS = /* @__PURE__ */ new Set();
|
|
12512
13261
|
var EMPTY_ENGAGED22 = /* @__PURE__ */ new Set();
|
|
12513
13262
|
function modifiersOf(e) {
|
|
12514
13263
|
return { alt: e.altKey, ctrl: e.ctrlKey, meta: e.metaKey, shift: e.shiftKey };
|
|
@@ -12616,34 +13365,23 @@ function createDispatcher(opts) {
|
|
|
12616
13365
|
return `ongoing-${event.kind}`;
|
|
12617
13366
|
}
|
|
12618
13367
|
function assembleScopedBindings(ctx) {
|
|
12619
|
-
const
|
|
12620
|
-
for (const
|
|
12621
|
-
const tool = ctx.toolsById.get(
|
|
12622
|
-
|
|
12623
|
-
|
|
12624
|
-
|
|
12625
|
-
|
|
12626
|
-
|
|
12627
|
-
|
|
12628
|
-
|
|
12629
|
-
|
|
12630
|
-
|
|
12631
|
-
|
|
12632
|
-
for (const binding of tool?.bindings ?? []) {
|
|
12633
|
-
result.push({ binding, scope: "ambient", ownerToolId: toolId });
|
|
12634
|
-
}
|
|
12635
|
-
}
|
|
13368
|
+
const ordered = [];
|
|
13369
|
+
for (const id of ctx.hotkeyStack) {
|
|
13370
|
+
const tool = ctx.toolsById.get(id);
|
|
13371
|
+
if (tool && !ordered.includes(tool)) ordered.push(tool);
|
|
13372
|
+
}
|
|
13373
|
+
for (const tool of ctx.toolsById.values()) {
|
|
13374
|
+
if (!ordered.includes(tool)) ordered.push(tool);
|
|
13375
|
+
}
|
|
13376
|
+
const result = scopeBindings(ordered, {
|
|
13377
|
+
focusedId: ctx.activeToolId,
|
|
13378
|
+
heldTriggers: EMPTY_TRIGGERS,
|
|
13379
|
+
engagedIds: new Set(ctx.hotkeyStack)
|
|
13380
|
+
});
|
|
12636
13381
|
for (const action of ctx.actions.list()) {
|
|
12637
|
-
const
|
|
12638
|
-
|
|
12639
|
-
|
|
12640
|
-
for (const entry of raw) {
|
|
12641
|
-
const isBoundObj = !("kind" in entry);
|
|
12642
|
-
const spec = isBoundObj ? entry.spec : entry;
|
|
12643
|
-
const opts2 = isBoundObj ? entry.opts : void 0;
|
|
12644
|
-
const defaultBinding = { spec, actionId: action.id, ...opts2 !== void 0 ? { opts: opts2 } : {} };
|
|
12645
|
-
const targetScope = action.scope === "hotkey" ? "hotkey" : "ambient";
|
|
12646
|
-
result.push({ binding: defaultBinding, scope: targetScope, ownerToolId: null });
|
|
13382
|
+
const targetScope = action.scope === "hotkey" ? "hotkey" : "ambient";
|
|
13383
|
+
for (const binding of actionBindings(action)) {
|
|
13384
|
+
result.push({ binding, scope: targetScope, ownerToolId: null });
|
|
12647
13385
|
}
|
|
12648
13386
|
}
|
|
12649
13387
|
return result;
|
|
@@ -12798,15 +13536,26 @@ function createDispatcher(opts) {
|
|
|
12798
13536
|
deltaY: event.deltaY,
|
|
12799
13537
|
clientX: event.clientX,
|
|
12800
13538
|
clientY: event.clientY,
|
|
13539
|
+
affordance: event.affordance,
|
|
12801
13540
|
...resolved
|
|
12802
13541
|
};
|
|
12803
13542
|
} else if (event.kind === "click" || event.kind === "doubleclick") {
|
|
12804
13543
|
params = {
|
|
12805
13544
|
worldX: event.worldX,
|
|
12806
13545
|
worldY: event.worldY,
|
|
13546
|
+
affordance: event.affordance,
|
|
12807
13547
|
// Press point as well as release point — an action that places
|
|
12808
13548
|
// geometry at the click wants the former. See `ClickEvent`.
|
|
12809
|
-
...event.kind === "click" ? { pressX: event.pressX, pressY: event.pressY
|
|
13549
|
+
...event.kind === "click" ? { pressX: event.pressX, pressY: event.pressY } : {},
|
|
13550
|
+
mods: modifiersOf(event),
|
|
13551
|
+
...resolved
|
|
13552
|
+
};
|
|
13553
|
+
} else if (event.kind === "contextmenu" || event.kind === "longpress") {
|
|
13554
|
+
params = {
|
|
13555
|
+
worldX: event.kind === "contextmenu" ? event.worldX : event.x,
|
|
13556
|
+
worldY: event.kind === "contextmenu" ? event.worldY : event.y,
|
|
13557
|
+
affordance: event.affordance,
|
|
13558
|
+
bodyTarget: event.bodyTarget,
|
|
12810
13559
|
mods: modifiersOf(event),
|
|
12811
13560
|
...resolved
|
|
12812
13561
|
};
|
|
@@ -13174,7 +13923,7 @@ function computeMultiTouchGeometry(positions) {
|
|
|
13174
13923
|
return { centroid, spread };
|
|
13175
13924
|
}
|
|
13176
13925
|
function useGestureDispatcher(opts) {
|
|
13177
|
-
const { canvasRef, actions, toolsById,
|
|
13926
|
+
const { canvasRef, actions, toolsById, enabled = true, keyboard = true, affordanceAt, classifyTarget, dispatcher: dispatcherOpt, clientToWorld: clientToWorld2, requestRedraw, getRuleCtx, onDoubleClick } = opts;
|
|
13178
13927
|
const onDoubleClickRef = useRef(onDoubleClick);
|
|
13179
13928
|
onDoubleClickRef.current = onDoubleClick;
|
|
13180
13929
|
const activeTool = useActiveToolContext();
|
|
@@ -13189,7 +13938,6 @@ function useGestureDispatcher(opts) {
|
|
|
13189
13938
|
activeToolId: activeTool.active,
|
|
13190
13939
|
hotkeyStack: activeTool.hotkeyStack,
|
|
13191
13940
|
toolsById,
|
|
13192
|
-
ambientToolIds,
|
|
13193
13941
|
isMac: IS_MAC,
|
|
13194
13942
|
getRuleCtx
|
|
13195
13943
|
});
|
|
@@ -13199,7 +13947,6 @@ function useGestureDispatcher(opts) {
|
|
|
13199
13947
|
activeToolId: activeTool.active,
|
|
13200
13948
|
hotkeyStack: activeTool.hotkeyStack,
|
|
13201
13949
|
toolsById,
|
|
13202
|
-
ambientToolIds,
|
|
13203
13950
|
isMac: IS_MAC,
|
|
13204
13951
|
getRuleCtx
|
|
13205
13952
|
};
|
|
@@ -13255,6 +14002,49 @@ function useGestureDispatcher(opts) {
|
|
|
13255
14002
|
const DRAG_THRESHOLD_PX = 4;
|
|
13256
14003
|
const bufferedDown = /* @__PURE__ */ new Map();
|
|
13257
14004
|
const lastPointerDown = /* @__PURE__ */ new Map();
|
|
14005
|
+
const LONG_PRESS_MS = 500;
|
|
14006
|
+
const longPressTimers = /* @__PURE__ */ new Map();
|
|
14007
|
+
const cancelLongPress = (pointerId) => {
|
|
14008
|
+
const t = longPressTimers.get(pointerId);
|
|
14009
|
+
if (t !== void 0) {
|
|
14010
|
+
clearTimeout(t);
|
|
14011
|
+
longPressTimers.delete(pointerId);
|
|
14012
|
+
}
|
|
14013
|
+
};
|
|
14014
|
+
const cancelAllLongPress = () => {
|
|
14015
|
+
for (const t of longPressTimers.values()) clearTimeout(t);
|
|
14016
|
+
longPressTimers.clear();
|
|
14017
|
+
};
|
|
14018
|
+
const fireLongPress = (pointerId) => {
|
|
14019
|
+
const down = lastPointerDown.get(pointerId);
|
|
14020
|
+
if (!down) return;
|
|
14021
|
+
const shared = {
|
|
14022
|
+
target: down.target,
|
|
14023
|
+
altKey: down.altKey,
|
|
14024
|
+
ctrlKey: down.ctrlKey,
|
|
14025
|
+
metaKey: down.metaKey,
|
|
14026
|
+
shiftKey: down.shiftKey,
|
|
14027
|
+
...down.affordance !== void 0 ? { affordance: down.affordance } : {},
|
|
14028
|
+
...down.bodyTarget !== void 0 ? { bodyTarget: down.bodyTarget } : {},
|
|
14029
|
+
...down.bodyKind !== void 0 ? { bodyKind: down.bodyKind } : {}
|
|
14030
|
+
};
|
|
14031
|
+
const result = dispatch2({
|
|
14032
|
+
kind: "longpress",
|
|
14033
|
+
x: down.worldX,
|
|
14034
|
+
y: down.worldY,
|
|
14035
|
+
clientX: down.clientX,
|
|
14036
|
+
clientY: down.clientY,
|
|
14037
|
+
...shared
|
|
14038
|
+
});
|
|
14039
|
+
if (result === "unhandled") {
|
|
14040
|
+
dispatch2({
|
|
14041
|
+
kind: "contextmenu",
|
|
14042
|
+
worldX: down.worldX,
|
|
14043
|
+
worldY: down.worldY,
|
|
14044
|
+
...shared
|
|
14045
|
+
});
|
|
14046
|
+
}
|
|
14047
|
+
};
|
|
13258
14048
|
const DOUBLE_CLICK_MAX_MS = 600;
|
|
13259
14049
|
const DOUBLE_CLICK_MAX_PX = 8;
|
|
13260
14050
|
const onKeyDown = (e) => {
|
|
@@ -13307,6 +14097,9 @@ function useGestureDispatcher(opts) {
|
|
|
13307
14097
|
const onWheel = (e) => {
|
|
13308
14098
|
const rect = canvas?.getBoundingClientRect();
|
|
13309
14099
|
const [localX, localY] = rect ? clientToCanvasRect(rect, e.clientX, e.clientY) : [e.clientX, e.clientY];
|
|
14100
|
+
const screenPoint = { x: e.clientX, y: e.clientY };
|
|
14101
|
+
const affordance = affordanceAtRef.current?.(screenPoint) ?? void 0;
|
|
14102
|
+
const body = classifyTargetRef.current?.(screenPoint);
|
|
13310
14103
|
const ev = {
|
|
13311
14104
|
kind: "wheel",
|
|
13312
14105
|
altKey: e.altKey,
|
|
@@ -13316,7 +14109,10 @@ function useGestureDispatcher(opts) {
|
|
|
13316
14109
|
deltaX: e.deltaX,
|
|
13317
14110
|
deltaY: e.deltaY,
|
|
13318
14111
|
clientX: localX,
|
|
13319
|
-
clientY: localY
|
|
14112
|
+
clientY: localY,
|
|
14113
|
+
...affordance !== void 0 ? { affordance } : {},
|
|
14114
|
+
...body?.body !== void 0 ? { bodyTarget: body.body } : {},
|
|
14115
|
+
...body?.kind !== void 0 ? { bodyKind: body.kind } : {}
|
|
13320
14116
|
};
|
|
13321
14117
|
const result = dispatch2(ev);
|
|
13322
14118
|
if (result === "handled") {
|
|
@@ -13358,6 +14154,7 @@ function useGestureDispatcher(opts) {
|
|
|
13358
14154
|
bufferedDown.set(e.pointerId, { ev, clientX: e.clientX, clientY: e.clientY });
|
|
13359
14155
|
dispatch2({ ...ev, stage: "press" });
|
|
13360
14156
|
lastPointerDown.set(e.pointerId, {
|
|
14157
|
+
target: e.target,
|
|
13361
14158
|
clientX: e.clientX,
|
|
13362
14159
|
clientY: e.clientY,
|
|
13363
14160
|
worldX: w.x,
|
|
@@ -13370,7 +14167,19 @@ function useGestureDispatcher(opts) {
|
|
|
13370
14167
|
metaKey: e.metaKey,
|
|
13371
14168
|
shiftKey: e.shiftKey
|
|
13372
14169
|
});
|
|
14170
|
+
if ((e.pointerType === "touch" || e.pointerType === "pen") && activePointers.size === 1) {
|
|
14171
|
+
cancelLongPress(e.pointerId);
|
|
14172
|
+
longPressTimers.set(
|
|
14173
|
+
e.pointerId,
|
|
14174
|
+
setTimeout(() => {
|
|
14175
|
+
longPressTimers.delete(e.pointerId);
|
|
14176
|
+
if (disposed) return;
|
|
14177
|
+
fireLongPress(e.pointerId);
|
|
14178
|
+
}, LONG_PRESS_MS)
|
|
14179
|
+
);
|
|
14180
|
+
}
|
|
13373
14181
|
if (activePointers.size >= 2) {
|
|
14182
|
+
cancelAllLongPress();
|
|
13374
14183
|
for (const id of activePointers) bufferedDown.delete(id);
|
|
13375
14184
|
const { centroid, spread } = computeMultiTouchGeometry(pointerPositions);
|
|
13376
14185
|
if (!multiTouchStart) {
|
|
@@ -13401,6 +14210,7 @@ function useGestureDispatcher(opts) {
|
|
|
13401
14210
|
const dy = e.clientY - buffered.clientY;
|
|
13402
14211
|
if (Math.hypot(dx, dy) >= DRAG_THRESHOLD_PX) {
|
|
13403
14212
|
bufferedDown.delete(e.pointerId);
|
|
14213
|
+
cancelLongPress(e.pointerId);
|
|
13404
14214
|
dispatch2(buffered.ev);
|
|
13405
14215
|
}
|
|
13406
14216
|
}
|
|
@@ -13514,6 +14324,7 @@ function useGestureDispatcher(opts) {
|
|
|
13514
14324
|
clearHoverCursor();
|
|
13515
14325
|
};
|
|
13516
14326
|
const onPointerUp = (e) => {
|
|
14327
|
+
cancelLongPress(e.pointerId);
|
|
13517
14328
|
const prevSize = activePointers.size;
|
|
13518
14329
|
activePointers.delete(e.pointerId);
|
|
13519
14330
|
pointerPositions.delete(e.pointerId);
|
|
@@ -13595,6 +14406,7 @@ function useGestureDispatcher(opts) {
|
|
|
13595
14406
|
shiftKey: e.shiftKey,
|
|
13596
14407
|
worldX: wClick.x,
|
|
13597
14408
|
worldY: wClick.y,
|
|
14409
|
+
...down.affordance !== void 0 ? { affordance: down.affordance } : {},
|
|
13598
14410
|
...down.bodyTarget !== void 0 ? { bodyTarget: down.bodyTarget } : {},
|
|
13599
14411
|
...down.bodyKind !== void 0 ? { bodyKind: down.bodyKind } : {}
|
|
13600
14412
|
};
|
|
@@ -13618,6 +14430,7 @@ function useGestureDispatcher(opts) {
|
|
|
13618
14430
|
}, 0);
|
|
13619
14431
|
};
|
|
13620
14432
|
const onPointerCancel = (e) => {
|
|
14433
|
+
cancelLongPress(e.pointerId);
|
|
13621
14434
|
activePointers.delete(e.pointerId);
|
|
13622
14435
|
pointerPositions.delete(e.pointerId);
|
|
13623
14436
|
lastPointerDown.delete(e.pointerId);
|
|
@@ -13635,8 +14448,10 @@ function useGestureDispatcher(opts) {
|
|
|
13635
14448
|
};
|
|
13636
14449
|
const onContextMenu = (e) => {
|
|
13637
14450
|
e.preventDefault();
|
|
13638
|
-
const
|
|
13639
|
-
const
|
|
14451
|
+
const screenPoint = { x: e.clientX, y: e.clientY };
|
|
14452
|
+
const affordance = affordanceAtRef.current?.(screenPoint) ?? void 0;
|
|
14453
|
+
const menuBody = classifyTargetRef.current?.(screenPoint);
|
|
14454
|
+
const w = toWorld(e.clientX, e.clientY);
|
|
13640
14455
|
const ev = {
|
|
13641
14456
|
kind: "contextmenu",
|
|
13642
14457
|
target: e.target,
|
|
@@ -13644,6 +14459,9 @@ function useGestureDispatcher(opts) {
|
|
|
13644
14459
|
ctrlKey: e.ctrlKey,
|
|
13645
14460
|
metaKey: e.metaKey,
|
|
13646
14461
|
shiftKey: e.shiftKey,
|
|
14462
|
+
worldX: w.x,
|
|
14463
|
+
worldY: w.y,
|
|
14464
|
+
...affordance !== void 0 ? { affordance } : {},
|
|
13647
14465
|
...menuBody?.body !== void 0 ? { bodyTarget: menuBody.body } : {},
|
|
13648
14466
|
...menuBody?.kind !== void 0 ? { bodyKind: menuBody.kind } : {}
|
|
13649
14467
|
};
|
|
@@ -13740,6 +14558,7 @@ function useGestureDispatcher(opts) {
|
|
|
13740
14558
|
window.removeEventListener("paste", onPaste);
|
|
13741
14559
|
canvas?.classList.remove(DROPOVER_CLASS);
|
|
13742
14560
|
clearHoverCursor();
|
|
14561
|
+
cancelAllLongPress();
|
|
13743
14562
|
disposed = true;
|
|
13744
14563
|
dispatcher.cancelAll("cancel");
|
|
13745
14564
|
};
|
|
@@ -13941,47 +14760,203 @@ function usePinchGesture(canvasRef, onPinch, enabled = true) {
|
|
|
13941
14760
|
};
|
|
13942
14761
|
}, [canvasRef, enabled]);
|
|
13943
14762
|
}
|
|
13944
|
-
|
|
13945
|
-
|
|
13946
|
-
|
|
14763
|
+
var TOOL_OFFHAND_ID = "tool.offhand";
|
|
14764
|
+
function offhandKeyFor(trigger) {
|
|
14765
|
+
switch (trigger) {
|
|
14766
|
+
case "space":
|
|
14767
|
+
return " ";
|
|
14768
|
+
case "alt":
|
|
14769
|
+
return "Alt";
|
|
14770
|
+
case "ctrl":
|
|
14771
|
+
return "Control";
|
|
14772
|
+
case "meta":
|
|
14773
|
+
return "Meta";
|
|
14774
|
+
case "shift":
|
|
14775
|
+
return "Shift";
|
|
13947
14776
|
}
|
|
14777
|
+
}
|
|
14778
|
+
function buildToolOffhandBindings(specs) {
|
|
14779
|
+
return specs.map(({ toolId, key }) => ({
|
|
14780
|
+
spec: { kind: "key-held", key },
|
|
14781
|
+
opts: { params: { toolId } }
|
|
14782
|
+
}));
|
|
14783
|
+
}
|
|
14784
|
+
function makeToolOffhandAction(bindings) {
|
|
14785
|
+
return {
|
|
14786
|
+
id: TOOL_OFFHAND_ID,
|
|
14787
|
+
label: "Engage tool (offhand)",
|
|
14788
|
+
defaultBinding: bindings,
|
|
14789
|
+
scope: "hotkey",
|
|
14790
|
+
invoker: {
|
|
14791
|
+
timing: "ongoing",
|
|
14792
|
+
start: (ctx, opts) => {
|
|
14793
|
+
const activeTool = ctx.deps.activeTool;
|
|
14794
|
+
const toolId = resolveParams(opts?.params)?.toolId;
|
|
14795
|
+
if (!activeTool || !toolId) return {};
|
|
14796
|
+
activeTool.pushHotkey(toolId);
|
|
14797
|
+
return {
|
|
14798
|
+
onEnd: () => {
|
|
14799
|
+
activeTool.popHotkey();
|
|
14800
|
+
}
|
|
14801
|
+
};
|
|
14802
|
+
}
|
|
14803
|
+
},
|
|
14804
|
+
requires: ["activeTool"]
|
|
14805
|
+
};
|
|
14806
|
+
}
|
|
14807
|
+
var NO_TRIGGERS = /* @__PURE__ */ new Set();
|
|
14808
|
+
function useContributions(opts) {
|
|
13948
14809
|
const ctx = useActiveToolContext();
|
|
14810
|
+
const actionsRegistry = useActionsRegistry();
|
|
13949
14811
|
const hasInitializedRef = useRef(false);
|
|
13950
14812
|
const isFirstRender = !hasInitializedRef.current;
|
|
13951
14813
|
const needsFirstMountSyncRef = useRef(false);
|
|
13952
14814
|
if (isFirstRender) {
|
|
13953
14815
|
hasInitializedRef.current = true;
|
|
13954
|
-
needsFirstMountSyncRef.current = ctx.active === "select" && opts.
|
|
14816
|
+
needsFirstMountSyncRef.current = ctx.active === "select" && opts.focused !== "select";
|
|
13955
14817
|
}
|
|
13956
14818
|
useEffect(() => {
|
|
13957
14819
|
if (!needsFirstMountSyncRef.current) return;
|
|
13958
14820
|
needsFirstMountSyncRef.current = false;
|
|
13959
|
-
ctx.setActive(opts.
|
|
14821
|
+
ctx.setActive(opts.focused);
|
|
14822
|
+
}, []);
|
|
14823
|
+
const focused2 = isFirstRender && ctx.active === "select" && opts.focused !== "select" ? opts.focused : ctx.active;
|
|
14824
|
+
const entriesRef = useRef(opts.entries);
|
|
14825
|
+
entriesRef.current = opts.entries;
|
|
14826
|
+
const focusedRef = useRef(focused2);
|
|
14827
|
+
focusedRef.current = focused2;
|
|
14828
|
+
const engagedRef = useRef(ctx.hotkeyStack);
|
|
14829
|
+
engagedRef.current = ctx.hotkeyStack;
|
|
14830
|
+
const setFocused = useCallback((id) => {
|
|
14831
|
+
ctx.setActive(id);
|
|
14832
|
+
}, [ctx]);
|
|
14833
|
+
const snapshot = useCallback(() => {
|
|
14834
|
+
const engaged = engagedRef.current;
|
|
14835
|
+
const engagedIds = new Set(engaged);
|
|
14836
|
+
const ordered = [];
|
|
14837
|
+
for (const id of engaged) {
|
|
14838
|
+
const entry = entriesRef.current.find((e) => e.id === id);
|
|
14839
|
+
if (entry && !ordered.includes(entry)) ordered.push(entry);
|
|
14840
|
+
}
|
|
14841
|
+
for (const entry of entriesRef.current) {
|
|
14842
|
+
if (!ordered.includes(entry)) ordered.push(entry);
|
|
14843
|
+
}
|
|
14844
|
+
return {
|
|
14845
|
+
ordered,
|
|
14846
|
+
state: { focusedId: focusedRef.current, heldTriggers: NO_TRIGGERS, engagedIds }
|
|
14847
|
+
};
|
|
13960
14848
|
}, []);
|
|
13961
|
-
const
|
|
13962
|
-
|
|
13963
|
-
|
|
13964
|
-
|
|
13965
|
-
|
|
13966
|
-
|
|
13967
|
-
|
|
13968
|
-
|
|
13969
|
-
|
|
14849
|
+
const scopedBindings = useCallback(() => {
|
|
14850
|
+
const { ordered, state } = snapshot();
|
|
14851
|
+
return scopeBindings(ordered, state);
|
|
14852
|
+
}, [snapshot]);
|
|
14853
|
+
const overlays = useCallback(() => {
|
|
14854
|
+
const { ordered, state } = snapshot();
|
|
14855
|
+
const active = [];
|
|
14856
|
+
const hotkey = [];
|
|
14857
|
+
const ambient = [];
|
|
14858
|
+
for (const entry of ordered) {
|
|
14859
|
+
if (!entry.overlay) continue;
|
|
14860
|
+
const scope = liveScope(entry.id, entry.eligibility ?? {}, state);
|
|
14861
|
+
if (scope === "active") active.push(entry.overlay);
|
|
14862
|
+
else if (scope === "hotkey") hotkey.push(entry.overlay);
|
|
14863
|
+
else if (scope === "ambient") ambient.push(entry.overlay);
|
|
14864
|
+
}
|
|
14865
|
+
return [...active, ...hotkey, ...ambient];
|
|
14866
|
+
}, [snapshot]);
|
|
14867
|
+
const offhandSig = opts.entries.map((e) => e.eligibility?.offhand ? `${e.id}:${e.eligibility.offhand}` : "").filter(Boolean).join(",");
|
|
14868
|
+
useEffect(() => {
|
|
14869
|
+
if (!actionsRegistry || offhandSig === "") return;
|
|
14870
|
+
const specs = offhandSig.split(",").map((entry) => {
|
|
14871
|
+
const [toolId, trigger] = entry.split(":");
|
|
14872
|
+
return { toolId, key: offhandKeyFor(trigger) };
|
|
14873
|
+
});
|
|
14874
|
+
return actionsRegistry.register(makeToolOffhandAction(buildToolOffhandBindings(specs)));
|
|
14875
|
+
}, [actionsRegistry, offhandSig]);
|
|
14876
|
+
const lastConflictSigRef = useRef(null);
|
|
14877
|
+
const entrySig = opts.entries.map((e) => e.id).join(",");
|
|
14878
|
+
useEffect(() => {
|
|
14879
|
+
if (process.env.NODE_ENV === "production") return;
|
|
14880
|
+
if (lastConflictSigRef.current === entrySig) return;
|
|
14881
|
+
lastConflictSigRef.current = entrySig;
|
|
14882
|
+
const registry22 = [];
|
|
14883
|
+
const ambient = [];
|
|
14884
|
+
for (const entry of entriesRef.current) {
|
|
14885
|
+
(entry.eligibility?.focus ? registry22 : ambient).push(entry);
|
|
14886
|
+
}
|
|
14887
|
+
reportRouteConflicts({
|
|
14888
|
+
registry: registry22,
|
|
14889
|
+
ambient,
|
|
14890
|
+
actions: actionsRegistry?.list() ?? []
|
|
14891
|
+
});
|
|
14892
|
+
}, [entrySig, actionsRegistry]);
|
|
14893
|
+
return useMemo(
|
|
14894
|
+
() => ({
|
|
14895
|
+
entries: entriesRef.current,
|
|
14896
|
+
focused: focused2,
|
|
14897
|
+
setFocused,
|
|
14898
|
+
scopedBindings,
|
|
14899
|
+
overlays
|
|
14900
|
+
}),
|
|
14901
|
+
[focused2, setFocused, scopedBindings, overlays]
|
|
14902
|
+
);
|
|
14903
|
+
}
|
|
14904
|
+
function declareSlot(tool, slot) {
|
|
14905
|
+
const hotkey = tool.def?.hotkey;
|
|
14906
|
+
const eligibility = {
|
|
14907
|
+
...tool.eligibility,
|
|
14908
|
+
[slot]: true,
|
|
14909
|
+
...hotkey ? { offhand: hotkey } : {}
|
|
14910
|
+
};
|
|
14911
|
+
return sameEligibility(tool.eligibility, eligibility) ? tool : { ...tool, eligibility };
|
|
14912
|
+
}
|
|
14913
|
+
function sameEligibility(a, b) {
|
|
14914
|
+
if (!a) return false;
|
|
14915
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)]);
|
|
14916
|
+
for (const key of keys) if (a[key] !== b[key]) return false;
|
|
14917
|
+
return true;
|
|
14918
|
+
}
|
|
14919
|
+
function useTools(opts) {
|
|
14920
|
+
if (!(opts.active in opts.registry)) {
|
|
14921
|
+
throw new Error(`useTools: active "${opts.active}" not in registry`);
|
|
14922
|
+
}
|
|
14923
|
+
const ctx = useActiveToolContext();
|
|
14924
|
+
const slotted = useMemo(() => {
|
|
14925
|
+
const registry22 = {};
|
|
14926
|
+
for (const [id, tool] of Object.entries(opts.registry)) {
|
|
14927
|
+
registry22[id] = declareSlot(tool, "focus");
|
|
14928
|
+
}
|
|
14929
|
+
const ambient = (opts.ambient ?? []).map((t) => declareSlot(t, "always"));
|
|
14930
|
+
const byId2 = /* @__PURE__ */ new Map();
|
|
14931
|
+
for (const tool of [...Object.values(registry22), ...ambient]) {
|
|
14932
|
+
const prior = byId2.get(tool.id);
|
|
14933
|
+
if (prior) byId2.set(tool.id, { ...tool, eligibility: { ...prior.eligibility, ...tool.eligibility } });
|
|
14934
|
+
else byId2.set(tool.id, tool);
|
|
14935
|
+
}
|
|
14936
|
+
return { registry: registry22, ambient, entries: [...byId2.values()] };
|
|
14937
|
+
}, [opts.registry, opts.ambient]);
|
|
14938
|
+
const contributions = useContributions({ entries: slotted.entries, focused: opts.active });
|
|
14939
|
+
const hotkeyEngaged = ctx.hotkeyStack.at(-1) ?? null;
|
|
14940
|
+
const slottedRef = useRef(slotted);
|
|
14941
|
+
slottedRef.current = slotted;
|
|
14942
|
+
const activeRef = useRef(contributions.focused);
|
|
14943
|
+
activeRef.current = contributions.focused;
|
|
13970
14944
|
const hotkeyRef = useRef(hotkeyEngaged);
|
|
13971
14945
|
hotkeyRef.current = hotkeyEngaged;
|
|
14946
|
+
const setFocused = contributions.setFocused;
|
|
13972
14947
|
const setActive = useCallback(
|
|
13973
14948
|
(id) => {
|
|
13974
|
-
if (!(id in
|
|
14949
|
+
if (!(id in slottedRef.current.registry)) {
|
|
13975
14950
|
throw new Error(`setActive: "${id}" not in registry`);
|
|
13976
14951
|
}
|
|
13977
14952
|
dlog("tools", "active:", activeRef.current, "\u2192", id);
|
|
13978
|
-
|
|
14953
|
+
setFocused(id);
|
|
13979
14954
|
},
|
|
13980
|
-
[
|
|
14955
|
+
[setFocused]
|
|
13981
14956
|
);
|
|
13982
14957
|
const engageHotkey = useCallback(
|
|
13983
14958
|
(id) => {
|
|
13984
|
-
if (!(id in
|
|
14959
|
+
if (!(id in slottedRef.current.registry)) {
|
|
13985
14960
|
throw new Error(`engageHotkey: "${id}" not in registry`);
|
|
13986
14961
|
}
|
|
13987
14962
|
dlog("tools", "hotkey engaged:", id);
|
|
@@ -13993,32 +14968,12 @@ function useTools(opts) {
|
|
|
13993
14968
|
if (hotkeyRef.current) dlog("tools", "hotkey disengaged:", hotkeyRef.current);
|
|
13994
14969
|
ctx.popHotkey();
|
|
13995
14970
|
}, [ctx]);
|
|
13996
|
-
const lastConflictSigRef = useRef(null);
|
|
13997
|
-
const toolSetSig = Object.keys(opts.registry).join(",") + "|" + (opts.ambient ?? []).map((t) => t.id).join(",");
|
|
13998
|
-
useEffect(() => {
|
|
13999
|
-
if (process.env.NODE_ENV === "production") return;
|
|
14000
|
-
if (lastConflictSigRef.current === toolSetSig) return;
|
|
14001
|
-
lastConflictSigRef.current = toolSetSig;
|
|
14002
|
-
reportRouteConflicts({
|
|
14003
|
-
registry: registryRef.current,
|
|
14004
|
-
ambient: ambientRef.current
|
|
14005
|
-
});
|
|
14006
|
-
}, [toolSetSig]);
|
|
14007
14971
|
const has = useCallback(
|
|
14008
|
-
(id) => id in
|
|
14972
|
+
(id) => id in slottedRef.current.registry || slottedRef.current.ambient.some((t) => t.id === id),
|
|
14009
14973
|
[]
|
|
14010
14974
|
);
|
|
14011
|
-
const
|
|
14012
|
-
|
|
14013
|
-
const activeTool = registryRef.current[activeRef.current];
|
|
14014
|
-
if (activeTool?.overlay) out.push(activeTool.overlay);
|
|
14015
|
-
const mod = hotkeyRef.current ? registryRef.current[hotkeyRef.current] : null;
|
|
14016
|
-
if (mod?.overlay) out.push(mod.overlay);
|
|
14017
|
-
for (const t of ambientRef.current) {
|
|
14018
|
-
if (t.overlay) out.push(t.overlay);
|
|
14019
|
-
}
|
|
14020
|
-
return out;
|
|
14021
|
-
}, []);
|
|
14975
|
+
const active = contributions.focused;
|
|
14976
|
+
const getActiveOverlays = contributions.overlays;
|
|
14022
14977
|
return useMemo(
|
|
14023
14978
|
() => ({
|
|
14024
14979
|
active,
|
|
@@ -14026,44 +14981,14 @@ function useTools(opts) {
|
|
|
14026
14981
|
hotkeyEngaged,
|
|
14027
14982
|
engageHotkey,
|
|
14028
14983
|
disengageHotkey,
|
|
14029
|
-
ambient:
|
|
14030
|
-
registry:
|
|
14984
|
+
ambient: slottedRef.current.ambient,
|
|
14985
|
+
registry: slottedRef.current.registry,
|
|
14031
14986
|
has,
|
|
14032
14987
|
getActiveOverlays
|
|
14033
14988
|
}),
|
|
14034
14989
|
[active, setActive, hotkeyEngaged, engageHotkey, disengageHotkey, has, getActiveOverlays]
|
|
14035
14990
|
);
|
|
14036
14991
|
}
|
|
14037
|
-
var TOOL_OFFHAND_ID = "tool.offhand";
|
|
14038
|
-
function buildToolOffhandBindings(specs) {
|
|
14039
|
-
return specs.map(({ toolId, key }) => ({
|
|
14040
|
-
spec: { kind: "key-held", key },
|
|
14041
|
-
opts: { params: { toolId } }
|
|
14042
|
-
}));
|
|
14043
|
-
}
|
|
14044
|
-
function makeToolOffhandAction(bindings) {
|
|
14045
|
-
return {
|
|
14046
|
-
id: TOOL_OFFHAND_ID,
|
|
14047
|
-
label: "Engage tool (offhand)",
|
|
14048
|
-
defaultBinding: bindings,
|
|
14049
|
-
scope: "hotkey",
|
|
14050
|
-
invoker: {
|
|
14051
|
-
timing: "ongoing",
|
|
14052
|
-
start: (ctx, opts) => {
|
|
14053
|
-
const activeTool = ctx.deps.activeTool;
|
|
14054
|
-
const toolId = resolveParams(opts?.params)?.toolId;
|
|
14055
|
-
if (!activeTool || !toolId) return {};
|
|
14056
|
-
activeTool.pushHotkey(toolId);
|
|
14057
|
-
return {
|
|
14058
|
-
onEnd: () => {
|
|
14059
|
-
activeTool.popHotkey();
|
|
14060
|
-
}
|
|
14061
|
-
};
|
|
14062
|
-
}
|
|
14063
|
-
},
|
|
14064
|
-
requires: ["activeTool"]
|
|
14065
|
-
};
|
|
14066
|
-
}
|
|
14067
14992
|
var TOOL_ACTIVATE_ID = "tool.activate";
|
|
14068
14993
|
function buildToolActivateBindings(specs) {
|
|
14069
14994
|
return specs.map(({ toolId, keyOpts }) => {
|
|
@@ -14135,9 +15060,6 @@ var BUILTIN_SELECT_KEYS = {
|
|
|
14135
15060
|
hand: { key: "H" },
|
|
14136
15061
|
pen: { key: "P" }
|
|
14137
15062
|
};
|
|
14138
|
-
var BUILTIN_OFFHAND_ACTIONS = {
|
|
14139
|
-
hand: " "
|
|
14140
|
-
};
|
|
14141
15063
|
function useKeybindings(tools, options = {}) {
|
|
14142
15064
|
const toolsRef = useRef(tools);
|
|
14143
15065
|
toolsRef.current = tools;
|
|
@@ -14145,19 +15067,6 @@ function useKeybindings(tools, options = {}) {
|
|
|
14145
15067
|
optionsRef.current = options;
|
|
14146
15068
|
const initialActiveRef = useRef(tools.active);
|
|
14147
15069
|
const registry22 = useActionsRegistry();
|
|
14148
|
-
useEffect(() => {
|
|
14149
|
-
if (optionsRef.current.disable) return;
|
|
14150
|
-
if (!registry22) return;
|
|
14151
|
-
const specs = [];
|
|
14152
|
-
for (const [toolId, key] of Object.entries(BUILTIN_OFFHAND_ACTIONS)) {
|
|
14153
|
-
if (toolsRef.current.has(toolId)) {
|
|
14154
|
-
specs.push({ toolId, key });
|
|
14155
|
-
}
|
|
14156
|
-
}
|
|
14157
|
-
if (specs.length === 0) return;
|
|
14158
|
-
const bindings = buildToolOffhandBindings(specs);
|
|
14159
|
-
return registry22.register(makeToolOffhandAction(bindings));
|
|
14160
|
-
}, [registry22, tools]);
|
|
14161
15070
|
useEffect(() => {
|
|
14162
15071
|
if (optionsRef.current.disable) return;
|
|
14163
15072
|
if (!registry22) return;
|
|
@@ -14552,7 +15461,82 @@ function textLineBoxes(pose, opts = {}) {
|
|
|
14552
15461
|
}
|
|
14553
15462
|
return out;
|
|
14554
15463
|
}
|
|
14555
|
-
|
|
15464
|
+
function fillInPoseFrame(fill, box) {
|
|
15465
|
+
if (isBoundsPattern(fill)) {
|
|
15466
|
+
const o = fill.origin ?? { x: 0, y: 0 };
|
|
15467
|
+
return { ...fill, origin: { x: box.x + o.x, y: box.y + o.y }, units: "local" };
|
|
15468
|
+
}
|
|
15469
|
+
if (!isBoundsGradient(fill)) return fill;
|
|
15470
|
+
const toBox = (p) => ({
|
|
15471
|
+
x: box.x + p.x * box.width,
|
|
15472
|
+
y: box.y + p.y * box.height
|
|
15473
|
+
});
|
|
15474
|
+
if (fill.fill === "linear-gradient") {
|
|
15475
|
+
return { ...fill, from: toBox(fill.from), to: toBox(fill.to), units: "local" };
|
|
15476
|
+
}
|
|
15477
|
+
if (fill.fill === "radial-gradient") {
|
|
15478
|
+
const scale2 = Math.hypot(box.width, box.height) / Math.SQRT2;
|
|
15479
|
+
return { ...fill, center: toBox(fill.center), radius: fill.radius * scale2, units: "local" };
|
|
15480
|
+
}
|
|
15481
|
+
return { ...fill, center: toBox(fill.center), units: "local" };
|
|
15482
|
+
}
|
|
15483
|
+
function isBoundsPattern(fill) {
|
|
15484
|
+
return fill.fill === "pattern" && fill.units === "bounds";
|
|
15485
|
+
}
|
|
15486
|
+
function isBoundsGradient(fill) {
|
|
15487
|
+
if (fill === void 0) return false;
|
|
15488
|
+
const kind = fill.fill;
|
|
15489
|
+
if (kind !== "linear-gradient" && kind !== "radial-gradient" && kind !== "conic-gradient") {
|
|
15490
|
+
return false;
|
|
15491
|
+
}
|
|
15492
|
+
return fill.units === "bounds";
|
|
15493
|
+
}
|
|
15494
|
+
var cache5 = /* @__PURE__ */ new Map();
|
|
15495
|
+
function specKey(spec) {
|
|
15496
|
+
const keys = Object.keys(spec).sort();
|
|
15497
|
+
const bag = spec;
|
|
15498
|
+
return keys.map((k) => `${k}=${String(bag[k])}`).join("&");
|
|
15499
|
+
}
|
|
15500
|
+
function resolvePatternSpec(spec) {
|
|
15501
|
+
const key = specKey(spec);
|
|
15502
|
+
const hit = cache5.get(key);
|
|
15503
|
+
if (hit !== void 0) return hit;
|
|
15504
|
+
const built = buildTile(spec);
|
|
15505
|
+
cache5.set(key, built);
|
|
15506
|
+
return built;
|
|
15507
|
+
}
|
|
15508
|
+
function buildTile(spec) {
|
|
15509
|
+
const { color, size } = spec;
|
|
15510
|
+
switch (spec.tile) {
|
|
15511
|
+
case "hatch":
|
|
15512
|
+
return hatch({ color, size, lineWidth: spec.lineWidth });
|
|
15513
|
+
case "crosshatch":
|
|
15514
|
+
return crosshatch({ color, size, lineWidth: spec.lineWidth });
|
|
15515
|
+
case "dots":
|
|
15516
|
+
return dots({ color, size, radius: spec.radius });
|
|
15517
|
+
case "chunks":
|
|
15518
|
+
return chunks({
|
|
15519
|
+
color,
|
|
15520
|
+
size,
|
|
15521
|
+
bg: spec.bg,
|
|
15522
|
+
density: spec.density,
|
|
15523
|
+
chunkSize: spec.chunkSize,
|
|
15524
|
+
seed: spec.seed
|
|
15525
|
+
});
|
|
15526
|
+
default:
|
|
15527
|
+
return null;
|
|
15528
|
+
}
|
|
15529
|
+
}
|
|
15530
|
+
function isPatternSpec(payload) {
|
|
15531
|
+
return "tile" in payload;
|
|
15532
|
+
}
|
|
15533
|
+
function resolveFillPattern(fill) {
|
|
15534
|
+
if (fill.fill !== "pattern") return fill;
|
|
15535
|
+
if (!isPatternSpec(fill.pattern)) return fill;
|
|
15536
|
+
const handle = resolvePatternSpec(fill.pattern);
|
|
15537
|
+
return handle ? { ...fill, pattern: handle } : null;
|
|
15538
|
+
}
|
|
15539
|
+
var cache6 = /* @__PURE__ */ new Map();
|
|
14556
15540
|
var subscribers2 = /* @__PURE__ */ new Set();
|
|
14557
15541
|
var defaultLoader = async (src) => {
|
|
14558
15542
|
if (typeof Image === "undefined" || typeof createImageBitmap === "undefined") {
|
|
@@ -14570,28 +15554,28 @@ function notify() {
|
|
|
14570
15554
|
}
|
|
14571
15555
|
function startLoad(src) {
|
|
14572
15556
|
const entry = { status: "loading" };
|
|
14573
|
-
|
|
15557
|
+
cache6.set(src, entry);
|
|
14574
15558
|
loader(src).then(
|
|
14575
15559
|
(bitmap) => {
|
|
14576
|
-
if (
|
|
14577
|
-
|
|
15560
|
+
if (cache6.get(src) !== entry) return;
|
|
15561
|
+
cache6.set(src, { status: "ready", bitmap });
|
|
14578
15562
|
notify();
|
|
14579
15563
|
},
|
|
14580
15564
|
() => {
|
|
14581
|
-
if (
|
|
14582
|
-
|
|
15565
|
+
if (cache6.get(src) !== entry) return;
|
|
15566
|
+
cache6.set(src, { status: "error" });
|
|
14583
15567
|
notify();
|
|
14584
15568
|
}
|
|
14585
15569
|
);
|
|
14586
15570
|
}
|
|
14587
15571
|
function getImageBitmap(src) {
|
|
14588
|
-
const entry =
|
|
15572
|
+
const entry = cache6.get(src);
|
|
14589
15573
|
if (entry) return entry.bitmap;
|
|
14590
15574
|
startLoad(src);
|
|
14591
15575
|
return void 0;
|
|
14592
15576
|
}
|
|
14593
15577
|
function imageStatus(src) {
|
|
14594
|
-
return
|
|
15578
|
+
return cache6.get(src)?.status ?? "idle";
|
|
14595
15579
|
}
|
|
14596
15580
|
function subscribeImageReady(cb) {
|
|
14597
15581
|
subscribers2.add(cb);
|
|
@@ -14685,6 +15669,13 @@ function shapeCoversPoint(node, pose, x, y, opts = {}) {
|
|
|
14685
15669
|
const reach = ink.strokeWidth / 2 + (opts.tolerance ?? 0);
|
|
14686
15670
|
return reach > 0 && strokeHitTest(sil, x, y, reach);
|
|
14687
15671
|
}
|
|
15672
|
+
function withLeafStroke(style, stroke, strokeWidth) {
|
|
15673
|
+
if (style?.stroke !== void 0) return style;
|
|
15674
|
+
if (!stroke || stroke === "none") return style;
|
|
15675
|
+
const width = strokeWidth ?? 1;
|
|
15676
|
+
if (width <= 0) return style;
|
|
15677
|
+
return { ...style, stroke: { paint: { color: stroke }, width } };
|
|
15678
|
+
}
|
|
14688
15679
|
var TEXT_PAINTER = {
|
|
14689
15680
|
id: "kit:text",
|
|
14690
15681
|
matches: (node) => {
|
|
@@ -14703,7 +15694,8 @@ var TEXT_PAINTER = {
|
|
|
14703
15694
|
const d = node.data;
|
|
14704
15695
|
const p = pose;
|
|
14705
15696
|
const y = p.y;
|
|
14706
|
-
|
|
15697
|
+
const style = withLeafStroke(d.style, d.stroke, d.strokeWidth);
|
|
15698
|
+
return d.runs && d.runs.length > 0 ? [textCommandFromRuns(p.x, y, d.runs, style, void 0, p.height)] : [textCommand(p.x, y, d.text, style, void 0, p.height)];
|
|
14707
15699
|
}),
|
|
14708
15700
|
// The pose is a *wrap box*, not a bounding box — "Away" in a 300-unit box
|
|
14709
15701
|
// leaves most of it empty, and a pose-rect silhouette claims all of it. The
|
|
@@ -14761,6 +15753,17 @@ function rectsToPath(rects) {
|
|
|
14761
15753
|
}
|
|
14762
15754
|
return { kind: "polygon", commands, coords, fillRule: "nonzero" };
|
|
14763
15755
|
}
|
|
15756
|
+
function isFillStyle(v) {
|
|
15757
|
+
return typeof v === "object" && v !== null;
|
|
15758
|
+
}
|
|
15759
|
+
function resolveNodeFill(fill, color, hasStroke, fallback) {
|
|
15760
|
+
if (isFillStyle(fill)) return fill;
|
|
15761
|
+
const c = fill ?? color;
|
|
15762
|
+
if (c === "none") return null;
|
|
15763
|
+
if (c !== void 0) return { color: c };
|
|
15764
|
+
if (hasStroke && fallback === null) return null;
|
|
15765
|
+
return fallback === null ? null : { color: fallback };
|
|
15766
|
+
}
|
|
14764
15767
|
var PATH_PAINTER = {
|
|
14765
15768
|
id: "kit:path",
|
|
14766
15769
|
matches: (node) => {
|
|
@@ -14774,12 +15777,12 @@ var PATH_PAINTER = {
|
|
|
14774
15777
|
const d = node.data;
|
|
14775
15778
|
const projected = pathInPoseFrame(d.path, pose);
|
|
14776
15779
|
const hasStroke = !!d.stroke && d.stroke !== "none" && (d.strokeWidth ?? 0) > 0;
|
|
14777
|
-
const
|
|
14778
|
-
const
|
|
15780
|
+
const declared = resolveNodeFill(d.fill, d.color, hasStroke, hasStroke ? null : "#888");
|
|
15781
|
+
const fill = declared && resolveFillPattern(fillInPoseFrame(declared, pose));
|
|
14779
15782
|
const cmd = {
|
|
14780
15783
|
kind: "path",
|
|
14781
15784
|
path: projected,
|
|
14782
|
-
...
|
|
15785
|
+
...fill ? { fill } : {},
|
|
14783
15786
|
...hasStroke ? { stroke: { paint: { color: d.stroke }, width: d.strokeWidth ?? 1 } } : {}
|
|
14784
15787
|
};
|
|
14785
15788
|
return [cmd];
|
|
@@ -14795,9 +15798,8 @@ var PATH_PAINTER = {
|
|
|
14795
15798
|
ink: (node) => {
|
|
14796
15799
|
const d = node.data;
|
|
14797
15800
|
const hasStroke = !!d.stroke && d.stroke !== "none" && (d.strokeWidth ?? 0) > 0;
|
|
14798
|
-
const fillColor = d.fill ?? d.color;
|
|
14799
15801
|
return {
|
|
14800
|
-
filled:
|
|
15802
|
+
filled: resolveNodeFill(d.fill, d.color, hasStroke, hasStroke ? null : "#888") !== null,
|
|
14801
15803
|
strokeWidth: hasStroke ? d.strokeWidth ?? 1 : 0
|
|
14802
15804
|
};
|
|
14803
15805
|
}
|
|
@@ -14814,10 +15816,12 @@ var SHAPE_PAINTER = {
|
|
|
14814
15816
|
paint: (node, pose) => nodeMemo(node, PAINT_SLOT, pose, () => {
|
|
14815
15817
|
const d = node.data;
|
|
14816
15818
|
const path = pathForShape(d, pose);
|
|
15819
|
+
const declaredFill = resolveNodeFill(d.fill, d.color, false, "#888");
|
|
15820
|
+
const shapeFill = declaredFill && resolveFillPattern(fillInPoseFrame(declaredFill, pose));
|
|
14817
15821
|
return [{
|
|
14818
15822
|
kind: "path",
|
|
14819
15823
|
path,
|
|
14820
|
-
|
|
15824
|
+
...shapeFill ? { fill: shapeFill } : {},
|
|
14821
15825
|
...d.stroke && (d.strokeWidth ?? 0) > 0 ? { stroke: { paint: { color: d.stroke }, width: d.strokeWidth ?? 1 } } : {}
|
|
14822
15826
|
}];
|
|
14823
15827
|
}),
|
|
@@ -15213,11 +16217,8 @@ function useSelectTool(adapter, options) {
|
|
|
15213
16217
|
...moveOpts
|
|
15214
16218
|
},
|
|
15215
16219
|
{ spec: { kind: "drag", target: "empty" }, actionId: "areaSelect" },
|
|
15216
|
-
//
|
|
15217
|
-
//
|
|
15218
|
-
// widget that happens to float over empty canvas isn't a click on
|
|
15219
|
-
// empty canvas, and declining in the SPEC lets the chrome's owner
|
|
15220
|
-
// have the click instead of it being consumed here.
|
|
16220
|
+
// resize/rotate bind only drag (no click), so a click on either handle
|
|
16221
|
+
// (body genuinely empty) would hit target:'empty' and wrongly clear selection.
|
|
15221
16222
|
{
|
|
15222
16223
|
spec: {
|
|
15223
16224
|
kind: "click",
|
|
@@ -15301,17 +16302,17 @@ function worldToLocal(xf, wx, wy) {
|
|
|
15301
16302
|
function annulusSemiAxes(shape, view) {
|
|
15302
16303
|
const bandPx = shape.minBandPx ?? 0;
|
|
15303
16304
|
if (bandPx === 0) return { rx: shape.rx, ry: shape.ry };
|
|
15304
|
-
const band = bandPx
|
|
16305
|
+
const band = pxExtent(bandPx, view.scale);
|
|
15305
16306
|
return {
|
|
15306
|
-
rx: Math.max(shape.rx, shape.innerWidth / 2 + band),
|
|
15307
|
-
ry: Math.max(shape.ry, shape.innerHeight / 2 + band)
|
|
16307
|
+
rx: Math.max(shape.rx, shape.innerWidth / 2 + band.x),
|
|
16308
|
+
ry: Math.max(shape.ry, shape.innerHeight / 2 + band.y)
|
|
15308
16309
|
};
|
|
15309
16310
|
}
|
|
15310
16311
|
function hitRegion(region, wx, wy, xf, view) {
|
|
15311
16312
|
const local = worldToLocal(xf, wx, wy);
|
|
15312
16313
|
if (region.shape.kind === "point") {
|
|
15313
|
-
const
|
|
15314
|
-
return
|
|
16314
|
+
const anchor = localToWorld(xf, region.shape.x, region.shape.y);
|
|
16315
|
+
return withinPxBox(anchor.x - wx, anchor.y - wy, region.shape.hitRadiusPx, view.scale);
|
|
15315
16316
|
}
|
|
15316
16317
|
if (region.shape.kind === "annulus") {
|
|
15317
16318
|
const s = region.shape;
|
|
@@ -15375,14 +16376,13 @@ function asIsVisible(data) {
|
|
|
15375
16376
|
function recordRegionHitbox(debug, affordanceId, region, xf, view) {
|
|
15376
16377
|
if (region.shape.kind === "point") {
|
|
15377
16378
|
const w2 = localToWorld(xf, region.shape.x, region.shape.y);
|
|
15378
|
-
const
|
|
16379
|
+
const e = pxExtent(region.shape.hitRadiusPx, view.scale);
|
|
15379
16380
|
debug.recordHitbox(affordanceId, "handle", {
|
|
15380
16381
|
kind: "rect",
|
|
15381
|
-
x: w2.x -
|
|
15382
|
-
y: w2.y -
|
|
15383
|
-
width:
|
|
15384
|
-
height:
|
|
15385
|
-
...xf.identity ? {} : { rotation: Math.atan2(xf.sin, xf.cos) }
|
|
16382
|
+
x: w2.x - e.x,
|
|
16383
|
+
y: w2.y - e.y,
|
|
16384
|
+
width: e.x * 2,
|
|
16385
|
+
height: e.y * 2
|
|
15386
16386
|
});
|
|
15387
16387
|
return;
|
|
15388
16388
|
}
|
|
@@ -15441,7 +16441,7 @@ function paintRegion(region, xf, view, state, viewT, out) {
|
|
|
15441
16441
|
if (paint.kind === "annulus") {
|
|
15442
16442
|
if (region.shape.kind !== "annulus") return;
|
|
15443
16443
|
const s = region.shape;
|
|
15444
|
-
const insetLocal = (paint.insetPx ?? 0
|
|
16444
|
+
const insetLocal = pxExtent(paint.insetPx ?? 0, view.scale);
|
|
15445
16445
|
const cmd = annulusCommand(s, annulusSemiAxes(s, view), insetLocal, xf, viewT, paint.fill, paint.stroke);
|
|
15446
16446
|
if (cmd) out.push(cmd);
|
|
15447
16447
|
return;
|
|
@@ -15458,13 +16458,13 @@ function paintRegion(region, xf, view, state, viewT, out) {
|
|
|
15458
16458
|
}
|
|
15459
16459
|
}
|
|
15460
16460
|
function annulusCommand(shape, semiAxes, insetLocal, xf, viewT, fill, stroke) {
|
|
15461
|
-
const rx = semiAxes.rx - insetLocal;
|
|
15462
|
-
const ry = semiAxes.ry - insetLocal;
|
|
16461
|
+
const rx = semiAxes.rx - insetLocal.x;
|
|
16462
|
+
const ry = semiAxes.ry - insetLocal.y;
|
|
15463
16463
|
if (rx <= 0 || ry <= 0) return null;
|
|
15464
16464
|
const cx = shape.cx;
|
|
15465
16465
|
const cy = shape.cy;
|
|
15466
|
-
const rectHW = shape.innerWidth / 2 + insetLocal;
|
|
15467
|
-
const rectHH = shape.innerHeight / 2 + insetLocal;
|
|
16466
|
+
const rectHW = shape.innerWidth / 2 + insetLocal.x;
|
|
16467
|
+
const rectHH = shape.innerHeight / 2 + insetLocal.y;
|
|
15468
16468
|
const haveTopBottom = rectHH < ry;
|
|
15469
16469
|
const haveLeftRight = rectHW < rx;
|
|
15470
16470
|
if (!haveTopBottom && !haveLeftRight) return null;
|
|
@@ -15711,6 +16711,7 @@ function useHandTool(opts = {}) {
|
|
|
15711
16711
|
() => Object.assign(defineViewportTool({
|
|
15712
16712
|
id: "hand",
|
|
15713
16713
|
capabilities: ["navigation"],
|
|
16714
|
+
hotkey: "space",
|
|
15714
16715
|
hookName: "useHandTool",
|
|
15715
16716
|
presentation: {
|
|
15716
16717
|
label: "Hand",
|
|
@@ -15831,10 +16832,6 @@ function mirrorHandle(anchor, out) {
|
|
|
15831
16832
|
if (!out || anchor.altBroken) return void 0;
|
|
15832
16833
|
return { x: 2 * anchor.x - out.x, y: 2 * anchor.y - out.y };
|
|
15833
16834
|
}
|
|
15834
|
-
function dist2(ax, ay, bx, by) {
|
|
15835
|
-
const dx = ax - bx, dy = ay - by;
|
|
15836
|
-
return Math.hypot(dx, dy);
|
|
15837
|
-
}
|
|
15838
16835
|
function usePenTool(options) {
|
|
15839
16836
|
const { wrapPath, adapter, autoSelect = true, autoCommitOnClose = true, closeHitRadius = 8, snapPoint: snapPoint2 } = options;
|
|
15840
16837
|
const scratchRef = useRef(null);
|
|
@@ -15858,11 +16855,14 @@ function usePenTool(options) {
|
|
|
15858
16855
|
const fn = optsRef.current.snapPoint;
|
|
15859
16856
|
return fn ? fn({ x, y }) : { x, y };
|
|
15860
16857
|
}, []);
|
|
15861
|
-
const
|
|
15862
|
-
|
|
15863
|
-
|
|
15864
|
-
|
|
15865
|
-
|
|
16858
|
+
const withinCloseRadius = useCallback(
|
|
16859
|
+
(deps, ax, ay, wx, wy) => {
|
|
16860
|
+
const view = deps.view?.get();
|
|
16861
|
+
const scale2 = view?.scale ?? { x: 1, y: 1 };
|
|
16862
|
+
return withinPxRadius(ax - wx, ay - wy, optsRef.current.closeHitRadius, scale2);
|
|
16863
|
+
},
|
|
16864
|
+
[]
|
|
16865
|
+
);
|
|
15866
16866
|
const actions = useMemo(() => {
|
|
15867
16867
|
const s = () => scratchRef.current;
|
|
15868
16868
|
return [
|
|
@@ -15878,10 +16878,9 @@ function usePenTool(options) {
|
|
|
15878
16878
|
if (p?.pressX === void 0 || p.pressY === void 0) return;
|
|
15879
16879
|
const { x: wx, y: wy } = snap2(p.pressX, p.pressY);
|
|
15880
16880
|
const scratch = s();
|
|
15881
|
-
const radius = closeRadius(deps);
|
|
15882
16881
|
if (scratch.current && scratch.current.anchors.length >= 3) {
|
|
15883
16882
|
const first = scratch.current.anchors[0];
|
|
15884
|
-
if (
|
|
16883
|
+
if (withinCloseRadius(deps, first.x, first.y, wx, wy)) {
|
|
15885
16884
|
scratch.current.closed = true;
|
|
15886
16885
|
scratch.finishedSubpaths.push(scratch.current);
|
|
15887
16886
|
scratch.current = null;
|
|
@@ -16000,7 +16999,7 @@ function usePenTool(options) {
|
|
|
16000
16999
|
}
|
|
16001
17000
|
}
|
|
16002
17001
|
];
|
|
16003
|
-
}, [commit, snap2,
|
|
17002
|
+
}, [commit, snap2, withinCloseRadius]);
|
|
16004
17003
|
return useMemo(() => {
|
|
16005
17004
|
return defineTool({
|
|
16006
17005
|
id: "pen",
|
|
@@ -16753,7 +17752,7 @@ function buildChromeState(args) {
|
|
|
16753
17752
|
}
|
|
16754
17753
|
};
|
|
16755
17754
|
}
|
|
16756
|
-
var DEFAULT_ROTATION_HANDLE_DISTANCE =
|
|
17755
|
+
var DEFAULT_ROTATION_HANDLE_DISTANCE = ROTATION_HANDLE_BASE_PX;
|
|
16757
17756
|
function rotationHandle(pose, distance = DEFAULT_ROTATION_HANDLE_DISTANCE) {
|
|
16758
17757
|
const rotation = pose.rotation ?? 0;
|
|
16759
17758
|
const c = aabbCenter(pose);
|
|
@@ -16813,7 +17812,7 @@ var DEFAULT_HANDLE_OUTLINE = {
|
|
|
16813
17812
|
paint: { fill: "solid", color: "#1a130d" },
|
|
16814
17813
|
width: 1
|
|
16815
17814
|
};
|
|
16816
|
-
var DEFAULT_HANDLE_SIZE =
|
|
17815
|
+
var DEFAULT_HANDLE_SIZE = HANDLE_BASE_PX;
|
|
16817
17816
|
function defaultHandlesOf(b) {
|
|
16818
17817
|
return [
|
|
16819
17818
|
{ x: b.x, y: b.y },
|
|
@@ -17566,8 +18565,8 @@ function CanvasInner(props, ref) {
|
|
|
17566
18565
|
const isVisible = getIsVisibleRef.current?.() ?? alwaysVisible;
|
|
17567
18566
|
for (const layer of layers2) {
|
|
17568
18567
|
if (!layer.hitTest) continue;
|
|
17569
|
-
const
|
|
17570
|
-
if (
|
|
18568
|
+
const hit = layer.hitTest(worldX, worldY, void 0, view, dims, isVisible);
|
|
18569
|
+
if (hit) return { layerId: layer.id, hit };
|
|
17571
18570
|
}
|
|
17572
18571
|
return null;
|
|
17573
18572
|
}, []);
|
|
@@ -18003,7 +19002,7 @@ function CanvasInner(props, ref) {
|
|
|
18003
19002
|
effectiveView,
|
|
18004
19003
|
{ width, height }
|
|
18005
19004
|
);
|
|
18006
|
-
renderer.render(commands);
|
|
19005
|
+
renderer.render(commands, viewToMat3(effectiveView));
|
|
18007
19006
|
}, [layersWithDebug, width, height, effectiveView, debugSink, redrawNonce, dprProp]);
|
|
18008
19007
|
const shaderIdKey = shaders?.map((h) => h.id).join("|") ?? "";
|
|
18009
19008
|
useEffect(() => {
|
|
@@ -20212,8 +21211,8 @@ var DEFAULT_FILL3 = "#d4c4a8";
|
|
|
20212
21211
|
var DEFAULT_STROKE = "#1a130d";
|
|
20213
21212
|
function createCornerResizeAffordance(opts = {}) {
|
|
20214
21213
|
const {
|
|
20215
|
-
handleHitRadius =
|
|
20216
|
-
handleSize =
|
|
21214
|
+
handleHitRadius = HANDLE_BASE_PX,
|
|
21215
|
+
handleSize = HANDLE_BASE_PX,
|
|
20217
21216
|
fill = DEFAULT_FILL3,
|
|
20218
21217
|
stroke = DEFAULT_STROKE
|
|
20219
21218
|
} = opts;
|
|
@@ -20351,8 +21350,8 @@ function createPathAnchorAffordances(getAnchorState, opts = {}) {
|
|
|
20351
21350
|
};
|
|
20352
21351
|
return [anchors, controls];
|
|
20353
21352
|
}
|
|
20354
|
-
var HANDLE_HIT_RADIUS =
|
|
20355
|
-
var ANCHOR_HIT_RADIUS =
|
|
21353
|
+
var HANDLE_HIT_RADIUS = HANDLE_BASE_PX;
|
|
21354
|
+
var ANCHOR_HIT_RADIUS = ANCHOR_HIT_BASE_PX;
|
|
20356
21355
|
function buildAffordanceAt(opts) {
|
|
20357
21356
|
const {
|
|
20358
21357
|
getChromeState,
|
|
@@ -20384,6 +21383,8 @@ function toAffordanceHit(hit) {
|
|
|
20384
21383
|
const scratch = hit.binding.initialScratch ?? {};
|
|
20385
21384
|
return {
|
|
20386
21385
|
kind: hit.region.hitKind ?? `${hit.affordanceId}:${hit.regionId}`,
|
|
21386
|
+
owner: hit.affordanceId,
|
|
21387
|
+
strength: "shared",
|
|
20387
21388
|
...scratch.targetId !== void 0 ? { targetIds: [scratch.targetId] } : {},
|
|
20388
21389
|
...scratch.anchor !== void 0 ? { anchor: scratch.anchor } : {},
|
|
20389
21390
|
...scratch.fixedPoint !== void 0 ? { fixedPoint: scratch.fixedPoint } : {},
|
|
@@ -20904,7 +21905,7 @@ function chordLengthParam(samples) {
|
|
|
20904
21905
|
ts[0] = 0;
|
|
20905
21906
|
let total = 0;
|
|
20906
21907
|
for (let i = 1; i < n; i++) {
|
|
20907
|
-
total +=
|
|
21908
|
+
total += dist2(samples[i - 1], samples[i]);
|
|
20908
21909
|
ts[i] = total;
|
|
20909
21910
|
}
|
|
20910
21911
|
if (total === 0) return null;
|
|
@@ -20937,13 +21938,13 @@ function solveLS(samples, ts, a, e, tStart, tEnd) {
|
|
|
20937
21938
|
let alpha1;
|
|
20938
21939
|
let alpha2;
|
|
20939
21940
|
if (Math.abs(det) < 1e-12) {
|
|
20940
|
-
const chord =
|
|
21941
|
+
const chord = dist2(a, e);
|
|
20941
21942
|
alpha1 = alpha2 = chord / 3;
|
|
20942
21943
|
} else {
|
|
20943
21944
|
alpha1 = (c22 * x1 - c12 * x2) / det;
|
|
20944
21945
|
alpha2 = (c11 * x2 - c12 * x1) / det;
|
|
20945
21946
|
if (alpha1 < 1e-6 || alpha2 < 1e-6) {
|
|
20946
|
-
const chord =
|
|
21947
|
+
const chord = dist2(a, e);
|
|
20947
21948
|
alpha1 = alpha2 = chord / 3;
|
|
20948
21949
|
}
|
|
20949
21950
|
}
|
|
@@ -20979,7 +21980,7 @@ function newtonRefineT(p, t, p0, p1, p2, p3) {
|
|
|
20979
21980
|
function sub(a, b) {
|
|
20980
21981
|
return { x: a.x - b.x, y: a.y - b.y };
|
|
20981
21982
|
}
|
|
20982
|
-
function
|
|
21983
|
+
function dist2(a, b) {
|
|
20983
21984
|
return Math.hypot(a.x - b.x, a.y - b.y);
|
|
20984
21985
|
}
|
|
20985
21986
|
function unit(v) {
|
|
@@ -21567,7 +22568,6 @@ function makeGetNodeAtPoint(pickEvery, nodeResolver) {
|
|
|
21567
22568
|
return { id, kind, pose, data };
|
|
21568
22569
|
};
|
|
21569
22570
|
}
|
|
21570
|
-
var DEFAULT_HANDLE_SIZE2 = 8;
|
|
21571
22571
|
var IS_DEV = (() => {
|
|
21572
22572
|
try {
|
|
21573
22573
|
return Boolean(import.meta.env?.DEV);
|
|
@@ -21586,10 +22586,10 @@ function recordCoordTrace(entry) {
|
|
|
21586
22586
|
coordTrace.push(entry);
|
|
21587
22587
|
if (coordTrace.length > COORD_TRACE_LIMIT) coordTrace.shift();
|
|
21588
22588
|
}
|
|
21589
|
-
function mergeLayersWithDefaults(user) {
|
|
22589
|
+
function mergeLayersWithDefaults(user, targetScale = 1) {
|
|
21590
22590
|
const defaults = {
|
|
21591
22591
|
scene: { drawOne: defaultDrawOne },
|
|
21592
|
-
selectionOverlay: { handles: { size:
|
|
22592
|
+
selectionOverlay: { handles: { size: HANDLE_BASE_PX * targetScale } }
|
|
21593
22593
|
};
|
|
21594
22594
|
if (!user) return defaults;
|
|
21595
22595
|
const result = { ...user };
|
|
@@ -21677,8 +22677,10 @@ function SceneCanvasInner(props, ref) {
|
|
|
21677
22677
|
chromeVisibility,
|
|
21678
22678
|
getActiveMode,
|
|
21679
22679
|
getFocused: getFocusedProp,
|
|
22680
|
+
device,
|
|
21680
22681
|
...rest
|
|
21681
22682
|
} = props;
|
|
22683
|
+
const deviceProfile = useDeviceProfile(device);
|
|
21682
22684
|
const isSerialized = (s) => typeof s === "object" && s != null && s.version === 1 && Array.isArray(s.nodes);
|
|
21683
22685
|
const [bakedScene] = useState(
|
|
21684
22686
|
() => isSerialized(sceneInput) ? sceneFromJSON(sceneInput, {}) : null
|
|
@@ -21796,7 +22798,7 @@ function SceneCanvasInner(props, ref) {
|
|
|
21796
22798
|
}
|
|
21797
22799
|
}), []);
|
|
21798
22800
|
const selectToolWithDefaults = useMemo(() => ({
|
|
21799
|
-
handleHitRadius:
|
|
22801
|
+
handleHitRadius: HANDLE_BASE_PX * deviceProfile.targetScale,
|
|
21800
22802
|
// Shift-click belongs to the anchor selection while a path is being
|
|
21801
22803
|
// anchor-edited; see `UseSelectToolOptions.extendClickLocked`.
|
|
21802
22804
|
extendClickLocked: () => effectivePathEditingId() !== "",
|
|
@@ -21806,7 +22808,7 @@ function SceneCanvasInner(props, ref) {
|
|
|
21806
22808
|
// pipeline (audit 3.4). The classifier is now `select.pick`, which
|
|
21807
22809
|
// declares that capability itself — one rule, evaluated in one place.
|
|
21808
22810
|
...selectToolOpts
|
|
21809
|
-
}), [selectToolOpts]);
|
|
22811
|
+
}), [selectToolOpts, deviceProfile.targetScale]);
|
|
21810
22812
|
const selectionRef = useRef(selection);
|
|
21811
22813
|
selectionRef.current = selection;
|
|
21812
22814
|
const kindClassifier = useMemo(() => {
|
|
@@ -21938,7 +22940,7 @@ function SceneCanvasInner(props, ref) {
|
|
|
21938
22940
|
const getMode = getActiveModeRef.current;
|
|
21939
22941
|
if (!getMode) return true;
|
|
21940
22942
|
const registry22 = toolsForEligibilityRef.current?.registry;
|
|
21941
|
-
const caps = registry22?.[toolId]?.capabilities ?? [];
|
|
22943
|
+
const caps = registry22?.[toolId]?.eligibility.capabilities ?? [];
|
|
21942
22944
|
if (caps.length === 0) return false;
|
|
21943
22945
|
const allowed = getMode().allowedCapabilities;
|
|
21944
22946
|
for (const c of caps) if (allowed.has(c)) return true;
|
|
@@ -21964,8 +22966,8 @@ function SceneCanvasInner(props, ref) {
|
|
|
21964
22966
|
};
|
|
21965
22967
|
}, [Boolean(onDoubleClick)]);
|
|
21966
22968
|
const mergedLayers = useMemo(
|
|
21967
|
-
() => mergeLayersWithDefaults(layers),
|
|
21968
|
-
[layers]
|
|
22969
|
+
() => mergeLayersWithDefaults(layers, deviceProfile.targetScale),
|
|
22970
|
+
[layers, deviceProfile.targetScale]
|
|
21969
22971
|
);
|
|
21970
22972
|
const resolvedActions = useMemo(() => {
|
|
21971
22973
|
if (selectToolOpts?.rotate !== false) return actions;
|
|
@@ -22024,6 +23026,8 @@ function SceneCanvasInner(props, ref) {
|
|
|
22024
23026
|
chromeVisibilityRef.current = effectiveChromeVisibility;
|
|
22025
23027
|
const getActiveModeRef = useRef(getActiveMode);
|
|
22026
23028
|
getActiveModeRef.current = getActiveMode;
|
|
23029
|
+
const deviceProfileRef = useRef(deviceProfile);
|
|
23030
|
+
deviceProfileRef.current = deviceProfile;
|
|
22027
23031
|
const resizablePredRef = useRef(selectToolOpts?.resize?.resizable);
|
|
22028
23032
|
resizablePredRef.current = selectToolOpts?.resize?.resizable;
|
|
22029
23033
|
const buildCurrentRuleCtx = useCallback(() => {
|
|
@@ -22045,7 +23049,8 @@ function SceneCanvasInner(props, ref) {
|
|
|
22045
23049
|
mode: modeInfo.id,
|
|
22046
23050
|
allowedCapabilities: modeInfo.allowedCapabilities,
|
|
22047
23051
|
selectionResizable,
|
|
22048
|
-
editingAnchors: effectivePathEditingId() !== ""
|
|
23052
|
+
editingAnchors: effectivePathEditingId() !== "",
|
|
23053
|
+
device: deviceProfileRef.current
|
|
22049
23054
|
};
|
|
22050
23055
|
}, [dispatcher, getHover]);
|
|
22051
23056
|
const anchorEditingAllowed = useMemo(
|
|
@@ -22300,7 +23305,7 @@ function SceneCanvasInner(props, ref) {
|
|
|
22300
23305
|
testHookRef.current?._markReady();
|
|
22301
23306
|
}
|
|
22302
23307
|
}, []);
|
|
22303
|
-
return /* @__PURE__ */ jsx(DepRegistryProviderIfRoot, { children: /* @__PURE__ */ jsx(PointerProviderIfRoot, { children: /* @__PURE__ */ jsxs(ActionsProviderIfRoot, { children: [
|
|
23308
|
+
return /* @__PURE__ */ jsx(DeviceProfileProvider, { value: device, children: /* @__PURE__ */ jsx(DepRegistryProviderIfRoot, { children: /* @__PURE__ */ jsx(PointerProviderIfRoot, { children: /* @__PURE__ */ jsxs(ActionsProviderIfRoot, { children: [
|
|
22304
23309
|
canvas,
|
|
22305
23310
|
/* @__PURE__ */ jsx(PointerPublisher, { canvasRef: internalCanvasRef, viewRef: currentViewRef }),
|
|
22306
23311
|
/* @__PURE__ */ jsx(
|
|
@@ -22363,7 +23368,7 @@ function SceneCanvasInner(props, ref) {
|
|
|
22363
23368
|
}
|
|
22364
23369
|
),
|
|
22365
23370
|
children
|
|
22366
|
-
] }) }) });
|
|
23371
|
+
] }) }) }) });
|
|
22367
23372
|
}
|
|
22368
23373
|
function ToolKeybindingsMounter({
|
|
22369
23374
|
internalTools,
|
|
@@ -22409,10 +23414,6 @@ function GestureDispatcherMounter({
|
|
|
22409
23414
|
for (const tool of tools.ambient) m.set(tool.id, tool);
|
|
22410
23415
|
return m;
|
|
22411
23416
|
}, [tools.registry, tools.ambient]);
|
|
22412
|
-
const ambientToolIds = useMemo(
|
|
22413
|
-
() => tools.ambient.map((t) => t.id),
|
|
22414
|
-
[tools.ambient]
|
|
22415
|
-
);
|
|
22416
23417
|
const boundsOfRef = useRef(boundsOf2);
|
|
22417
23418
|
boundsOfRef.current = boundsOf2;
|
|
22418
23419
|
const pickEveryRef = useRef(pickEvery);
|
|
@@ -22524,9 +23525,14 @@ function GestureDispatcherMounter({
|
|
|
22524
23525
|
const worldPoint = clientToWorld2(screenPoint.x, screenPoint.y);
|
|
22525
23526
|
const extra = canvasApiRef?.current?.hitTestExtras?.(worldPoint.x, worldPoint.y);
|
|
22526
23527
|
if (extra) {
|
|
23528
|
+
const claim = extra.hit;
|
|
22527
23529
|
return {
|
|
22528
23530
|
kind: `layer:${extra.layerId}`,
|
|
22529
|
-
|
|
23531
|
+
owner: extra.layerId,
|
|
23532
|
+
strength: claim.strength ?? "shared",
|
|
23533
|
+
...claim.claimedKinds !== void 0 ? { claimedKinds: claim.claimedKinds } : {},
|
|
23534
|
+
...claim.cursor !== void 0 ? { cursor: claim.cursor } : {},
|
|
23535
|
+
...claim.initialScratch !== void 0 ? { payload: claim.initialScratch } : {}
|
|
22530
23536
|
};
|
|
22531
23537
|
}
|
|
22532
23538
|
return affordanceAt ? affordanceAt(worldPoint) : null;
|
|
@@ -22546,7 +23552,6 @@ function GestureDispatcherMounter({
|
|
|
22546
23552
|
canvasRef,
|
|
22547
23553
|
actions: registry22,
|
|
22548
23554
|
toolsById,
|
|
22549
|
-
ambientToolIds,
|
|
22550
23555
|
enabled,
|
|
22551
23556
|
keyboard,
|
|
22552
23557
|
affordanceAt: wrappedAffordanceAt,
|
|
@@ -22777,7 +23782,7 @@ function renderSceneToCanvas(args) {
|
|
|
22777
23782
|
entry.dpr = dpr;
|
|
22778
23783
|
}
|
|
22779
23784
|
const commands = buildSceneViewCommands(scene, view, drawOne, extraCommands);
|
|
22780
|
-
entry.renderer.render(commands);
|
|
23785
|
+
entry.renderer.render(commands, viewToMat3(view));
|
|
22781
23786
|
}
|
|
22782
23787
|
function SceneViewCanvasInner(props) {
|
|
22783
23788
|
const { scene, view, width, height, drawOne, extraCommands, className, canvasRef } = props;
|
|
@@ -23151,6 +24156,6 @@ function oklchToOklab(L, C, h) {
|
|
|
23151
24156
|
return [L, C * Math.cos(h), C * Math.sin(h)];
|
|
23152
24157
|
}
|
|
23153
24158
|
|
|
23154
|
-
export { FALLBACK_FIT_VIEW, MinimapCanvas, SceneViewCanvas, UnknownIcon, buildSceneViewCommands, computeFitView2, computeIndicatorCommand,
|
|
23155
|
-
//# sourceMappingURL=chunk-
|
|
23156
|
-
//# sourceMappingURL=chunk-
|
|
24159
|
+
export { FALLBACK_FIT_VIEW, MinimapCanvas, SceneViewCanvas, UnknownIcon, buildSceneViewCommands, computeFitView2, computeIndicatorCommand, eligibleContribution, evaluateEnabled, oklabToSrgbU8, oklchToOklab, renderSceneToCanvas, rgbaToHex, useActionsRegistry, useOptionalDepRegistry };
|
|
24160
|
+
//# sourceMappingURL=chunk-XVBXYKFJ.js.map
|
|
24161
|
+
//# sourceMappingURL=chunk-XVBXYKFJ.js.map
|