@weasel-js/labkit 1.4.0-pre.0 → 1.4.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.
Files changed (103) hide show
  1. package/README.md +226 -55
  2. package/dist/_dts/{CanvasStackContext-DtAn_3O-.d.ts → CanvasStackContext-D6M0o8cQ.d.ts} +1 -1
  3. package/dist/_dts/{frac-Cp3NivlC.d.ts → frac-QS7XzvpG.d.ts} +11 -4
  4. package/dist/_dts/{index-BDVzvRzQ.d.ts → index-B2X21G1c.d.ts} +8 -2
  5. package/dist/_dts/{types-D6s4b7if.d.ts → types-C1Zw7s5P.d.ts} +1 -0
  6. package/dist/_dts/{types-DYMaEvM5.d.ts → types-ChVJJHvk.d.ts} +3 -0
  7. package/dist/_dts/{useTrialState-gmMvZqPc.d.ts → useTrialState-Cdm13fvl.d.ts} +2 -1
  8. package/dist/canvas/index.d.ts +75 -9
  9. package/dist/canvas/index.js +2 -2
  10. package/dist/chrome/index.d.ts +4 -4
  11. package/dist/chrome/index.js +5 -5
  12. package/dist/chunk-2YILQ7Y5.js +33 -0
  13. package/dist/chunk-2YILQ7Y5.js.map +1 -0
  14. package/dist/{chunk-3P5TTTA7.js → chunk-4AMN4MMB.js} +5 -5
  15. package/dist/{chunk-3P5TTTA7.js.map → chunk-4AMN4MMB.js.map} +1 -1
  16. package/dist/{chunk-ROQDRXKW.js → chunk-7VW3S44V.js} +621 -51
  17. package/dist/chunk-7VW3S44V.js.map +1 -0
  18. package/dist/{chunk-4EY67BKJ.js → chunk-B5VWJBWM.js} +12 -6
  19. package/dist/chunk-B5VWJBWM.js.map +1 -0
  20. package/dist/{chunk-HEORBLVT.js → chunk-L5WBGUVR.js} +1664 -1628
  21. package/dist/chunk-L5WBGUVR.js.map +1 -0
  22. package/dist/{chunk-XJ6N32QP.js → chunk-LJSIUFYD.js} +14 -30
  23. package/dist/chunk-LJSIUFYD.js.map +1 -0
  24. package/dist/{chunk-6JMU56CU.js → chunk-QR5V3AFG.js} +6 -4
  25. package/dist/chunk-QR5V3AFG.js.map +1 -0
  26. package/dist/{chunk-LITE5YFB.js → chunk-UD7TEHZT.js} +15 -15
  27. package/dist/chunk-UD7TEHZT.js.map +1 -0
  28. package/dist/{chunk-3ZELRRBV.js → chunk-VHFQKAUL.js} +86 -6
  29. package/dist/chunk-VHFQKAUL.js.map +1 -0
  30. package/dist/{chunk-YQJNT4XQ.js → chunk-W3KSUXXR.js} +16 -13
  31. package/dist/chunk-W3KSUXXR.js.map +1 -0
  32. package/dist/{chunk-JDDIZSLL.js → chunk-XCBCZEBO.js} +3 -3
  33. package/dist/{chunk-JDDIZSLL.js.map → chunk-XCBCZEBO.js.map} +1 -1
  34. package/dist/{chunk-EQIB2PGC.js → chunk-Y2HK44TG.js} +3 -3
  35. package/dist/{chunk-EQIB2PGC.js.map → chunk-Y2HK44TG.js.map} +1 -1
  36. package/dist/{chunk-5TCEVMPI.js → chunk-YQGQUBKA.js} +3 -3
  37. package/dist/{chunk-5TCEVMPI.js.map → chunk-YQGQUBKA.js.map} +1 -1
  38. package/dist/controls/index.d.ts +2 -2
  39. package/dist/controls/index.js +3 -3
  40. package/dist/dragdrop/index.d.ts +3 -3
  41. package/dist/dragdrop/index.js +2 -2
  42. package/dist/index.d.ts +57 -59
  43. package/dist/index.js +335 -240
  44. package/dist/index.js.map +1 -1
  45. package/dist/layers/index.d.ts +4 -4
  46. package/dist/layers/index.js +3 -3
  47. package/dist/loupe/index.d.ts +5 -5
  48. package/dist/loupe/index.js +2 -2
  49. package/dist/passthrough/weasel-canvas.js +1 -1
  50. package/dist/passthrough/weasel-ui.d.ts +336 -15
  51. package/dist/passthrough/weasel-ui.js +2 -2
  52. package/dist/primitives/index.js +4 -4
  53. package/dist/state/index.d.ts +3 -3
  54. package/dist/state/index.js +4 -2
  55. package/dist/state/index.js.map +1 -1
  56. package/dist/styles.css +64 -5
  57. package/dist/surface/index.d.ts +17 -2
  58. package/dist/surface/index.js +3 -2
  59. package/dist/ui/layers/index.js +2 -2
  60. package/dist/undo/index.d.ts +3 -3
  61. package/package.json +8 -8
  62. package/src/annotations/AnnotationOverlay.tsx +10 -6
  63. package/src/annotations/Annotations.less +6 -0
  64. package/src/annotations/Annotations.overlay.test.tsx +113 -0
  65. package/src/annotations/MarkList.tsx +3 -2
  66. package/src/annotations/store.test.ts +72 -0
  67. package/src/annotations/store.ts +30 -0
  68. package/src/annotations/types.ts +9 -2
  69. package/src/canvas/index.ts +11 -0
  70. package/src/config/builder.test.ts +5 -0
  71. package/src/config/builder.ts +6 -0
  72. package/src/config/types.ts +1 -0
  73. package/src/controls/ControlPanel.test.tsx +23 -0
  74. package/src/controls/ControlPanel.tsx +3 -0
  75. package/src/index.test.ts +1 -0
  76. package/src/index.ts +13 -9
  77. package/src/lab/Lab.surface.test.tsx +8 -3
  78. package/src/lab/LabHeader.test.tsx +12 -0
  79. package/src/lab/LabHeader.tsx +16 -6
  80. package/src/passthrough/weasel-ui.test.ts +9 -0
  81. package/src/passthrough/weasel-ui.ts +4 -0
  82. package/src/primitives/ZoomControl.tsx +2 -0
  83. package/src/state/store.ts +10 -0
  84. package/src/state/types.ts +3 -0
  85. package/src/surface/AGENTS.md +11 -1
  86. package/src/surface/index.ts +7 -1
  87. package/src/surface/useSurfaceTile.ts +20 -2
  88. package/src/surface/useTiledSurface.ts +4 -1
  89. package/src/theme/Interstellar.stories.tsx +4 -3
  90. package/src/theme/base.less +39 -1
  91. package/src/trial/Trial.less +27 -5
  92. package/src/trial/TrialBody.test.tsx +67 -0
  93. package/src/trial/TrialBody.tsx +155 -0
  94. package/src/trial/TrialChrome.tsx +18 -10
  95. package/src/trial/index.ts +2 -0
  96. package/dist/chunk-3ZELRRBV.js.map +0 -1
  97. package/dist/chunk-4EY67BKJ.js.map +0 -1
  98. package/dist/chunk-6JMU56CU.js.map +0 -1
  99. package/dist/chunk-HEORBLVT.js.map +0 -1
  100. package/dist/chunk-LITE5YFB.js.map +0 -1
  101. package/dist/chunk-ROQDRXKW.js.map +0 -1
  102. package/dist/chunk-XJ6N32QP.js.map +0 -1
  103. package/dist/chunk-YQJNT4XQ.js.map +0 -1
@@ -1,9 +1,9 @@
1
- import { createContext, createElement, forwardRef, useMemo, useRef, useCallback, useState, useImperativeHandle, useSyncExternalStore, useEffect, useLayoutEffect, useContext, useId, useReducer } from 'react';
1
+ import { createContext, createElement, forwardRef, useMemo, useRef, useCallback, useState, useEffect, useImperativeHandle, useSyncExternalStore, useLayoutEffect, useContext, useId, useReducer } from 'react';
2
2
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
3
  import earcut from 'earcut';
4
4
  import polygonClipping from 'polygon-clipping';
5
5
 
6
- // ../core/dist/chunk-H6E6KPTU.js
6
+ // ../core/dist/chunk-UTIFY2UU.js
7
7
 
8
8
  // ../gestures/dist/index.js
9
9
  var GESTURE_DESCRIPTORS = [
@@ -287,7 +287,7 @@ function matchSpec(e, spec, isMac, phaseCtx) {
287
287
  }
288
288
  var EMPTY_ENGAGED = /* @__PURE__ */ new Set();
289
289
 
290
- // ../core/dist/chunk-H6E6KPTU.js
290
+ // ../core/dist/chunk-UTIFY2UU.js
291
291
  var DepRegistryContext = createContext(null);
292
292
  function DepRegistryProvider({ children }) {
293
293
  const sourcesRef = useRef(/* @__PURE__ */ new Map());
@@ -3841,6 +3841,403 @@ function eligibleContribution(reg, entry) {
3841
3841
  return eligibleForMode(reg.current(), entry.eligibility?.capabilities ?? []);
3842
3842
  }
3843
3843
 
3844
+ // ../cursor/dist/index.js
3845
+ var CURSOR_INK = "#141418";
3846
+ var CURSOR_HALO = "#ffffff";
3847
+ var CURSOR_HALO_WIDTH = 2.6;
3848
+ var CURSOR_ANGLE_STEPS = 16;
3849
+ var CURSOR_MAX_CSS_PX = 128;
3850
+ function halo(p, k) {
3851
+ switch (p.role) {
3852
+ case "ink":
3853
+ case "accent":
3854
+ return { d: p.d, fill: CURSOR_HALO, stroke: { color: CURSOR_HALO, width: CURSOR_HALO_WIDTH * k } };
3855
+ case "stroke":
3856
+ return { d: p.d, stroke: { color: CURSOR_HALO, width: (p.width + CURSOR_HALO_WIDTH) * k } };
3857
+ // A detail IS halo-coloured and sits on top of the ink; it has no halo.
3858
+ case "detail":
3859
+ return null;
3860
+ }
3861
+ }
3862
+ function ink(p, k) {
3863
+ switch (p.role) {
3864
+ case "ink":
3865
+ return { d: p.d, fill: CURSOR_INK };
3866
+ case "stroke":
3867
+ return { d: p.d, stroke: { color: CURSOR_INK, width: p.width * k } };
3868
+ case "detail":
3869
+ return { d: p.d, stroke: { color: CURSOR_HALO, width: p.width * k } };
3870
+ case "accent":
3871
+ return { d: p.d, fill: p.fill };
3872
+ }
3873
+ }
3874
+ function cursorPaintOps(glyph, opts = {}) {
3875
+ const k = opts.lineWidthScale ?? 1;
3876
+ const out = [];
3877
+ for (const p of glyph.paths) {
3878
+ const h = halo(p, k);
3879
+ if (h) out.push(h);
3880
+ }
3881
+ for (const p of glyph.paths) out.push(ink(p, k));
3882
+ return out;
3883
+ }
3884
+ function cursorPaintMatrix(glyph, placement) {
3885
+ const s = placement.size / glyph.box;
3886
+ const theta = placement.angle ?? 0;
3887
+ const a = s * Math.cos(theta);
3888
+ const b = s * Math.sin(theta);
3889
+ const [hx, hy] = glyph.hotspot;
3890
+ return [a, b, -b, a, placement.at.x - (a * hx - b * hy), placement.at.y - (b * hx + a * hy)];
3891
+ }
3892
+ function cursorWorldSize(glyph, worldRadius, scale2) {
3893
+ const r = glyph.radius ?? glyph.box / 2;
3894
+ return worldRadius * scale2 * glyph.box / r;
3895
+ }
3896
+ function chromeLineWidthScale(glyph, size) {
3897
+ return glyph.box / size;
3898
+ }
3899
+ function quantizeCursorAngle(angle) {
3900
+ const i = Math.round(angle / (Math.PI * 2) * CURSOR_ANGLE_STEPS);
3901
+ return (i % CURSOR_ANGLE_STEPS + CURSOR_ANGLE_STEPS) % CURSOR_ANGLE_STEPS;
3902
+ }
3903
+ function renderOp(op) {
3904
+ const fill = op.fill === void 0 ? "none" : op.fill;
3905
+ const stroke = op.stroke ? ` stroke="${op.stroke.color}" stroke-width="${op.stroke.width}" stroke-linecap="round" stroke-linejoin="round"` : "";
3906
+ return `<path d="${op.d}" fill="${fill}"${stroke}/>`;
3907
+ }
3908
+ function bakeCursor(glyph, opts = {}) {
3909
+ const size = opts.size ?? 24;
3910
+ if (size > CURSOR_MAX_CSS_PX) {
3911
+ throw new RangeError(
3912
+ `cursor size ${size} exceeds the ${CURSOR_MAX_CSS_PX}px cap: the browser would drop the image and silently fall back. Use the painted tier.`
3913
+ );
3914
+ }
3915
+ const paths = cursorPaintOps(glyph).map(renderOp).join("");
3916
+ const c = glyph.box / 2;
3917
+ const step = quantizeCursorAngle(opts.angle ?? 0);
3918
+ const theta = step / CURSOR_ANGLE_STEPS * Math.PI * 2;
3919
+ const body = step === 0 ? paths : `<g transform="rotate(${step * 360 / CURSOR_ANGLE_STEPS} ${c} ${c})">${paths}</g>`;
3920
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 ${glyph.box} ${glyph.box}">${body}</svg>`;
3921
+ const dx = glyph.hotspot[0] - c;
3922
+ const dy = glyph.hotspot[1] - c;
3923
+ const cos = Math.cos(theta);
3924
+ const sin = Math.sin(theta);
3925
+ const hx = Math.round((c + cos * dx - sin * dy) / glyph.box * size);
3926
+ const hy = Math.round((c + sin * dx + cos * dy) / glyph.box * size);
3927
+ const uri = `data:image/svg+xml,${encodeURIComponent(svg)}`;
3928
+ return `url("${uri}") ${hx} ${hy}, ${opts.fallback ?? "default"}`;
3929
+ }
3930
+ var GLYPHS = {
3931
+ "pencil": {
3932
+ "box": 24,
3933
+ "hotspot": [
3934
+ 5,
3935
+ 19
3936
+ ],
3937
+ "paths": [
3938
+ {
3939
+ "role": "ink",
3940
+ "d": "M 5 19 L 7.5 16.5 L 16 8 L 19 11 L 10.5 19.5 L 8 19 Z"
3941
+ },
3942
+ {
3943
+ "role": "detail",
3944
+ "d": "M 14 6 L 19 11",
3945
+ "width": 1.2
3946
+ }
3947
+ ]
3948
+ },
3949
+ "pen": {
3950
+ "box": 24,
3951
+ "hotspot": [
3952
+ 5,
3953
+ 19
3954
+ ],
3955
+ "paths": [
3956
+ {
3957
+ "role": "ink",
3958
+ "d": "M 5 19 L 8.5 9.5 L 13 5 L 18 10 L 13.5 14.5 Z"
3959
+ },
3960
+ {
3961
+ "role": "detail",
3962
+ "d": "M 8.4 13.9 L 12.2 10.1",
3963
+ "width": 0.9
3964
+ }
3965
+ ]
3966
+ },
3967
+ "eyedropper": {
3968
+ "box": 24,
3969
+ "hotspot": [
3970
+ 5,
3971
+ 19
3972
+ ],
3973
+ "paths": [
3974
+ {
3975
+ "role": "ink",
3976
+ "d": "M 5 19 L 6.8 14.8 L 14.4 7.2 L 16.8 9.6 L 9.2 17.2 Z"
3977
+ },
3978
+ {
3979
+ "role": "ink",
3980
+ "d": "M 18.2 2.6 A 3.2 3.2 0 1 0 18.2 9 A 3.2 3.2 0 1 0 18.2 2.6 Z"
3981
+ }
3982
+ ]
3983
+ },
3984
+ "brush": {
3985
+ "box": 24,
3986
+ "hotspot": [
3987
+ 12,
3988
+ 12
3989
+ ],
3990
+ "radius": 9.9,
3991
+ "paths": [
3992
+ {
3993
+ "role": "stroke",
3994
+ "d": "M 12 2.1 A 9.9 9.9 0 1 0 12 21.9 A 9.9 9.9 0 1 0 12 2.1 Z",
3995
+ "width": 1.6
3996
+ }
3997
+ ]
3998
+ },
3999
+ "crosshairRect": {
4000
+ "box": 24,
4001
+ "hotspot": [
4002
+ 9,
4003
+ 9
4004
+ ],
4005
+ "paths": [
4006
+ {
4007
+ "role": "stroke",
4008
+ "d": "M 1.6 9 L 6.4 9",
4009
+ "width": 1.6
4010
+ },
4011
+ {
4012
+ "role": "stroke",
4013
+ "d": "M 11.6 9 L 16.4 9",
4014
+ "width": 1.6
4015
+ },
4016
+ {
4017
+ "role": "stroke",
4018
+ "d": "M 9 1.6 L 9 6.4",
4019
+ "width": 1.6
4020
+ },
4021
+ {
4022
+ "role": "stroke",
4023
+ "d": "M 9 11.6 L 9 16.4",
4024
+ "width": 1.6
4025
+ },
4026
+ {
4027
+ "role": "ink",
4028
+ "d": "M 14.2 14.2 L 21 14.2 L 21 21 L 14.2 21 Z"
4029
+ }
4030
+ ]
4031
+ },
4032
+ "crosshairEllipse": {
4033
+ "box": 24,
4034
+ "hotspot": [
4035
+ 9,
4036
+ 9
4037
+ ],
4038
+ "paths": [
4039
+ {
4040
+ "role": "stroke",
4041
+ "d": "M 1.6 9 L 6.4 9",
4042
+ "width": 1.6
4043
+ },
4044
+ {
4045
+ "role": "stroke",
4046
+ "d": "M 11.6 9 L 16.4 9",
4047
+ "width": 1.6
4048
+ },
4049
+ {
4050
+ "role": "stroke",
4051
+ "d": "M 9 1.6 L 9 6.4",
4052
+ "width": 1.6
4053
+ },
4054
+ {
4055
+ "role": "stroke",
4056
+ "d": "M 9 11.6 L 9 16.4",
4057
+ "width": 1.6
4058
+ },
4059
+ {
4060
+ "role": "ink",
4061
+ "d": "M 17.6 13.9 A 3.7 3.7 0 1 0 17.6 21.3 A 3.7 3.7 0 1 0 17.6 13.9 Z"
4062
+ }
4063
+ ]
4064
+ },
4065
+ "crosshairLine": {
4066
+ "box": 24,
4067
+ "hotspot": [
4068
+ 9,
4069
+ 9
4070
+ ],
4071
+ "paths": [
4072
+ {
4073
+ "role": "stroke",
4074
+ "d": "M 1.6 9 L 6.4 9",
4075
+ "width": 1.6
4076
+ },
4077
+ {
4078
+ "role": "stroke",
4079
+ "d": "M 11.6 9 L 16.4 9",
4080
+ "width": 1.6
4081
+ },
4082
+ {
4083
+ "role": "stroke",
4084
+ "d": "M 9 1.6 L 9 6.4",
4085
+ "width": 1.6
4086
+ },
4087
+ {
4088
+ "role": "stroke",
4089
+ "d": "M 9 11.6 L 9 16.4",
4090
+ "width": 1.6
4091
+ },
4092
+ {
4093
+ "role": "stroke",
4094
+ "d": "M 14.2 21 L 21 14.2",
4095
+ "width": 2.2
4096
+ }
4097
+ ]
4098
+ },
4099
+ "crosshairStar": {
4100
+ "box": 24,
4101
+ "hotspot": [
4102
+ 9,
4103
+ 9
4104
+ ],
4105
+ "paths": [
4106
+ {
4107
+ "role": "stroke",
4108
+ "d": "M 1.6 9 L 6.4 9",
4109
+ "width": 1.6
4110
+ },
4111
+ {
4112
+ "role": "stroke",
4113
+ "d": "M 11.6 9 L 16.4 9",
4114
+ "width": 1.6
4115
+ },
4116
+ {
4117
+ "role": "stroke",
4118
+ "d": "M 9 1.6 L 9 6.4",
4119
+ "width": 1.6
4120
+ },
4121
+ {
4122
+ "role": "stroke",
4123
+ "d": "M 9 11.6 L 9 16.4",
4124
+ "width": 1.6
4125
+ },
4126
+ {
4127
+ "role": "ink",
4128
+ "d": "M 17.6 13.4 L 18.72 16.06 L 21.59 16.3 L 19.41 18.19 L 20.07 21 L 17.6 19.5 L 15.13 21 L 15.79 18.19 L 13.61 16.3 L 16.48 16.06 Z"
4129
+ }
4130
+ ]
4131
+ },
4132
+ "crosshairPolygon": {
4133
+ "box": 24,
4134
+ "hotspot": [
4135
+ 9,
4136
+ 9
4137
+ ],
4138
+ "paths": [
4139
+ {
4140
+ "role": "stroke",
4141
+ "d": "M 1.6 9 L 6.4 9",
4142
+ "width": 1.6
4143
+ },
4144
+ {
4145
+ "role": "stroke",
4146
+ "d": "M 11.6 9 L 16.4 9",
4147
+ "width": 1.6
4148
+ },
4149
+ {
4150
+ "role": "stroke",
4151
+ "d": "M 9 1.6 L 9 6.4",
4152
+ "width": 1.6
4153
+ },
4154
+ {
4155
+ "role": "stroke",
4156
+ "d": "M 9 11.6 L 9 16.4",
4157
+ "width": 1.6
4158
+ },
4159
+ {
4160
+ "role": "ink",
4161
+ "d": "M 17.6 13.5 L 21.5 16.33 L 20.01 20.92 L 15.19 20.92 L 13.7 16.33 Z"
4162
+ }
4163
+ ]
4164
+ },
4165
+ "resize": {
4166
+ "box": 24,
4167
+ "hotspot": [
4168
+ 12,
4169
+ 12
4170
+ ],
4171
+ "paths": [
4172
+ {
4173
+ "role": "ink",
4174
+ "d": "M 2.5 12 L 7 8.8 L 7 10.7 L 17 10.7 L 17 8.8 L 21.5 12 L 17 15.2 L 17 13.3 L 7 13.3 L 7 15.2 Z"
4175
+ }
4176
+ ]
4177
+ },
4178
+ "rotate": {
4179
+ "box": 24,
4180
+ "hotspot": [
4181
+ 12,
4182
+ 12
4183
+ ],
4184
+ "paths": [
4185
+ {
4186
+ "role": "stroke",
4187
+ "d": "M 5.8 9.74 A 6.6 6.6 0 0 1 18.2 9.74",
4188
+ "width": 2.8
4189
+ },
4190
+ {
4191
+ "role": "ink",
4192
+ "d": "M 4.22 14.07 L 2.98 8.72 L 8.62 10.77 Z"
4193
+ },
4194
+ {
4195
+ "role": "ink",
4196
+ "d": "M 19.78 14.07 L 15.38 10.77 L 21.02 8.72 Z"
4197
+ }
4198
+ ]
4199
+ }
4200
+ };
4201
+ var cache2 = /* @__PURE__ */ new Map();
4202
+ function cursorFor(name, opts = {}) {
4203
+ const size = opts.size ?? 24;
4204
+ const fallback = opts.fallback ?? "default";
4205
+ const step = quantizeCursorAngle(opts.angle ?? 0);
4206
+ const key = `${name}|${size}|${step}|${fallback}`;
4207
+ const hit = cache2.get(key);
4208
+ if (hit !== void 0) return hit;
4209
+ const glyph = GLYPHS[name];
4210
+ if (glyph === void 0) {
4211
+ throw new Error(`unknown cursor glyph: ${String(name)}`);
4212
+ }
4213
+ const css = bakeCursor(glyph, { size, fallback, angle: opts.angle ?? 0 });
4214
+ cache2.set(key, css);
4215
+ return css;
4216
+ }
4217
+ function resolveCursorTier(spec) {
4218
+ if (typeof spec === "string") return { kind: "css", css: spec };
4219
+ const { glyph, size, worldRadius, angle = 0, fallback } = spec;
4220
+ if (worldRadius !== void 0) {
4221
+ return { kind: "painted", glyph, angle, worldRadius };
4222
+ }
4223
+ if (size !== void 0 && size > CURSOR_MAX_CSS_PX) {
4224
+ return { kind: "painted", glyph, angle, size };
4225
+ }
4226
+ return {
4227
+ kind: "css",
4228
+ css: cursorFor(glyph, {
4229
+ ...size !== void 0 ? { size } : {},
4230
+ ...fallback !== void 0 ? { fallback } : {},
4231
+ angle
4232
+ })
4233
+ };
4234
+ }
4235
+ function resolveCursor(spec) {
4236
+ if (spec === void 0) return void 0;
4237
+ const r = resolveCursorTier(spec);
4238
+ return r.kind === "css" ? r.css : "none";
4239
+ }
4240
+
3844
4241
  // ../history/dist/index.js
3845
4242
  var RESUMERS = /* @__PURE__ */ new WeakMap();
3846
4243
  function _resumeJournalInternal(j) {
@@ -4263,7 +4660,7 @@ function serialToEntry(se, custom, logger) {
4263
4660
  };
4264
4661
  }
4265
4662
 
4266
- // ../core/dist/chunk-IMFXEINV.js
4663
+ // ../core/dist/chunk-HYJL7OOJ.js
4267
4664
  function worldToScreen(worldX, worldY, view) {
4268
4665
  return [view.panX + worldX * view.zoom.x, view.panY + worldY * view.zoom.y];
4269
4666
  }
@@ -5821,12 +6218,12 @@ function pointInContour(coords, start, end, x, y) {
5821
6218
  }
5822
6219
  return inside;
5823
6220
  }
5824
- var cache2 = /* @__PURE__ */ new WeakMap();
6221
+ var cache3 = /* @__PURE__ */ new WeakMap();
5825
6222
  function getMesh(path) {
5826
- const cached2 = cache2.get(path);
6223
+ const cached2 = cache3.get(path);
5827
6224
  if (cached2 !== void 0) return cached2;
5828
6225
  const mesh = tessellate(path);
5829
- cache2.set(path, mesh);
6226
+ cache3.set(path, mesh);
5830
6227
  return mesh;
5831
6228
  }
5832
6229
  function extractPolylines(path, opts = {}) {
@@ -6846,7 +7243,7 @@ function outlineMesh(key, d) {
6846
7243
  }
6847
7244
  var OUTLINE_STROKE_MESH_CACHE_LIMIT = 2048;
6848
7245
  var WIDTH_QUANTUM = 1 / 1024;
6849
- var cache3 = /* @__PURE__ */ new Map();
7246
+ var cache32 = /* @__PURE__ */ new Map();
6850
7247
  function quantizeEmWidth(emWidth) {
6851
7248
  return Math.round(emWidth / WIDTH_QUANTUM) * WIDTH_QUANTUM;
6852
7249
  }
@@ -6861,15 +7258,15 @@ function outlineStrokeMesh(glyphKey, d, emWidth, stroke) {
6861
7258
  stroke.align ?? "center",
6862
7259
  (stroke.dash ?? []).join(",")
6863
7260
  ].join("|");
6864
- const cached2 = cache3.get(key);
7261
+ const cached2 = cache32.get(key);
6865
7262
  if (cached2 !== void 0) return cached2.indices.length === 0 ? null : cached2;
6866
- if (cache3.size >= OUTLINE_STROKE_MESH_CACHE_LIMIT) cache3 = /* @__PURE__ */ new Map();
7263
+ if (cache32.size >= OUTLINE_STROKE_MESH_CACHE_LIMIT) cache32 = /* @__PURE__ */ new Map();
6867
7264
  const mesh = tessellateStroke(
6868
7265
  pathFromD(d),
6869
7266
  { ...stroke, width: emWidth },
6870
7267
  { flattenTolerance: OUTLINE_FLATTEN_TOLERANCE }
6871
7268
  );
6872
- cache3.set(key, mesh);
7269
+ cache32.set(key, mesh);
6873
7270
  return mesh.indices.length === 0 ? null : mesh;
6874
7271
  }
6875
7272
  function poly(pts, size, close = true) {
@@ -9768,8 +10165,8 @@ function runLayoutPass(scratch, moveCtx) {
9768
10165
  for (const [cid, pose] of srcLayout.childPoses(srcContainer, srcChildren)) {
9769
10166
  const cur = composeWorldPose(poseAdapter, cid, pc.compose);
9770
10167
  const next = pose;
9771
- const same = cur.x === next.x && cur.y === next.y && cur.width === next.width && cur.height === next.height;
9772
- if (same) continue;
10168
+ const same2 = cur.x === next.x && cur.y === next.y && cur.width === next.width && cur.height === next.height;
10169
+ if (same2) continue;
9773
10170
  sourceReflow.set(cid, pose);
9774
10171
  const parent = scene.get(asNodeId(cid))?.parent ?? null;
9775
10172
  scratch.previews.set(asNodeId(cid), rebaseLocalPose(poseAdapter, pose, parent, pc.compose, pc.decompose));
@@ -11123,6 +11520,11 @@ var cloneAction = {
11123
11520
  id: "clone",
11124
11521
  label: "Clone",
11125
11522
  defaultBinding: { kind: "drag" },
11523
+ // The platform's duplicate cursor, and the whole reason this field is
11524
+ // modifier-aware: the select tool binds clone behind `mods: { alt: true }`,
11525
+ // so the pump predicts it — and shows this — only while Alt is down.
11526
+ cursor: "copy",
11527
+ activeCursor: "copy",
11126
11528
  eligible: { capability: ["edits-page", "creates-selection"] },
11127
11529
  requires: ["selection", "scene", "applyOps"],
11128
11530
  invoker: {
@@ -15513,7 +15915,7 @@ function createDispatcher(opts) {
15513
15915
  }
15514
15916
  function inFlightCursor() {
15515
15917
  for (const action of inFlightActions.values()) {
15516
- const cursor = action.activeCursor ?? action.cursor;
15918
+ const cursor = resolveCursor(action.activeCursor ?? action.cursor);
15517
15919
  if (cursor) return cursor;
15518
15920
  }
15519
15921
  return null;
@@ -15735,7 +16137,7 @@ function computeMultiTouchGeometry(positions) {
15735
16137
  return { centroid, spread };
15736
16138
  }
15737
16139
  function useGestureDispatcher(opts) {
15738
- const { canvasRef, actions, toolsById, enabled = true, keyboard = true, affordanceAt, classifyTarget, dispatcher: dispatcherOpt, clientToWorld: clientToWorld2, requestRedraw, getRuleCtx, onDoubleClick, views } = opts;
16140
+ const { canvasRef, actions, toolsById, enabled = true, keyboard = true, affordanceAt, classifyTarget, dispatcher: dispatcherOpt, clientToWorld: clientToWorld2, requestRedraw, paintedCursor, getRuleCtx, onDoubleClick, views } = opts;
15739
16141
  const onDoubleClickRef = useRef(onDoubleClick);
15740
16142
  onDoubleClickRef.current = onDoubleClick;
15741
16143
  const activeTool = useActiveToolContext();
@@ -15784,6 +16186,8 @@ function useGestureDispatcher(opts) {
15784
16186
  ];
15785
16187
  const requestRedrawRef = useRef(requestRedraw);
15786
16188
  requestRedrawRef.current = requestRedraw;
16189
+ const paintedCursorRef = useRef(paintedCursor);
16190
+ paintedCursorRef.current = paintedCursor;
15787
16191
  const lastClickRef = useRef(null);
15788
16192
  const prevActiveRef = useRef(activeTool.active);
15789
16193
  useEffect(() => {
@@ -16123,20 +16527,39 @@ function useGestureDispatcher(opts) {
16123
16527
  let lastHover = null;
16124
16528
  let cursorOverridden = false;
16125
16529
  const clearHoverCursor = () => {
16530
+ paintedCursorRef.current?.()?.setOverride(null);
16126
16531
  if (cursorOverridden && canvas) {
16127
16532
  canvas.style.cursor = "";
16128
16533
  cursorOverridden = false;
16129
16534
  }
16130
16535
  };
16131
- const applyHoverCursor = (cursor) => {
16132
- if (cursor && canvas) {
16133
- canvas.style.cursor = cursor;
16134
- cursorOverridden = true;
16135
- } else {
16536
+ const applyHoverCursor = (spec) => {
16537
+ const tier = spec === null ? null : resolveCursorTier(spec);
16538
+ if (!tier || !canvas) {
16539
+ clearHoverCursor();
16540
+ return;
16541
+ }
16542
+ const painted = tier.kind === "painted" ? tier : null;
16543
+ paintedCursorRef.current?.()?.setOverride(painted);
16544
+ const cursor = tier.kind === "css" ? tier.css : "none";
16545
+ if (!cursor) {
16136
16546
  clearHoverCursor();
16547
+ return;
16137
16548
  }
16549
+ canvas.style.cursor = cursor;
16550
+ cursorOverridden = true;
16138
16551
  };
16552
+ function pushPaintedPointer() {
16553
+ const painted = paintedCursorRef.current?.();
16554
+ if (!painted?.active() || !canvas || !lastHover) return;
16555
+ const r = canvas.getBoundingClientRect();
16556
+ painted.setPointer(lastHover.clientX - r.left, lastHover.clientY - r.top);
16557
+ }
16139
16558
  function refreshHoverCursor() {
16559
+ resolveHoverCursor();
16560
+ pushPaintedPointer();
16561
+ }
16562
+ function resolveHoverCursor() {
16140
16563
  if (!canvas || !lastHover) return;
16141
16564
  if (activePointers.size > 0 || dispatcherNow().inFlight().size > 0) {
16142
16565
  applyHoverCursor(dispatcherNow().inFlightCursor());
@@ -16188,6 +16611,7 @@ function useGestureDispatcher(opts) {
16188
16611
  };
16189
16612
  const onHoverPointerLeave = () => {
16190
16613
  lastHover = null;
16614
+ paintedCursorRef.current?.()?.clearPointer();
16191
16615
  clearHoverCursor();
16192
16616
  };
16193
16617
  const onPointerUp = (e) => {
@@ -17697,7 +18121,7 @@ function validateId(id, kind) {
17697
18121
  }
17698
18122
  function defineTool(def) {
17699
18123
  validateId(def.id, "tool");
17700
- const resolveCursor = (ctx) => {
18124
+ const resolveCursor3 = (ctx) => {
17701
18125
  if (def.cursor == null) return "";
17702
18126
  return typeof def.cursor === "function" ? def.cursor(ctx) : def.cursor;
17703
18127
  };
@@ -17715,7 +18139,7 @@ function defineTool(def) {
17715
18139
  onActivate: def.onActivate,
17716
18140
  onDeactivate: def.onDeactivate,
17717
18141
  initScratch: def.initScratch ?? (() => null),
17718
- cursor: resolveCursor,
18142
+ cursor: resolveCursor3,
17719
18143
  bindings: def.bindings,
17720
18144
  overlay: def.overlay,
17721
18145
  overlayPosition: def.overlayPosition
@@ -18715,10 +19139,10 @@ function findShapeInk(node, pose, ctx) {
18715
19139
  function shapeCoversPoint(node, pose, x, y, opts = {}) {
18716
19140
  const sil = findShapeSilhouette(node, pose, { derivedPath: opts.derivedPath });
18717
19141
  if (sil === null) return true;
18718
- const ink = findShapeInk(node, pose, { scale: opts.scale }) ?? DEFAULT_INK;
19142
+ const ink2 = findShapeInk(node, pose, { scale: opts.scale }) ?? DEFAULT_INK;
18719
19143
  const inside = pathContainsPoint(sil, x, y);
18720
- if (ink.filled && inside) return true;
18721
- const reach = (inside ? ink.inset : ink.outset) + (opts.tolerance ?? 0);
19144
+ if (ink2.filled && inside) return true;
19145
+ const reach = (inside ? ink2.inset : ink2.outset) + (opts.tolerance ?? 0);
18722
19146
  return reach > 0 && strokeHitTest(sil, x, y, reach);
18723
19147
  }
18724
19148
  var TEXT_PAINTER = {
@@ -19617,6 +20041,9 @@ function transformOf(state, targetId) {
19617
20041
  identity: false
19618
20042
  };
19619
20043
  }
20044
+ function angleOf(xf) {
20045
+ return xf.identity ? 0 : Math.atan2(xf.sin, xf.cos);
20046
+ }
19620
20047
  function localToWorld(xf, lx, ly) {
19621
20048
  if (xf.identity) return { x: lx, y: ly };
19622
20049
  const dx = lx - xf.cx;
@@ -19916,11 +20343,7 @@ var DEFAULT_PAINT = {
19916
20343
  insetPx: 10
19917
20344
  };
19918
20345
  function createRotationAffordance(opts = {}) {
19919
- const {
19920
- bandPx = 24,
19921
- paint = DEFAULT_PAINT,
19922
- cursor = "grab"
19923
- } = opts;
20346
+ const { bandPx = 24, paint = DEFAULT_PAINT, cursor } = opts;
19924
20347
  const annulusPaint = (() => {
19925
20348
  if (!paint) return null;
19926
20349
  if ("paint" in paint) {
@@ -19958,7 +20381,14 @@ function createRotationAffordance(opts = {}) {
19958
20381
  },
19959
20382
  ...annulusPaint ? { paint: annulusPaint } : {},
19960
20383
  hitKind: "rotate-handle",
19961
- cursor,
20384
+ // Turned with the target, so the arc follows the selection it rotates.
20385
+ // Resolved per region rather than defaulted in the option list because
20386
+ // the angle is only known once a target is picked.
20387
+ cursor: cursor ?? {
20388
+ glyph: "rotate",
20389
+ angle: angleOf(transformOf(state, target.id)),
20390
+ fallback: "grab"
20391
+ },
19962
20392
  bind: () => ({
19963
20393
  initialScratch: {
19964
20394
  targetId: target.id,
@@ -20368,7 +20798,7 @@ function usePenTool(options) {
20368
20798
  hookName: "usePenTool",
20369
20799
  // Reads the tool's own ref rather than a scratch handed in by a
20370
20800
  // dispatcher — the pen owns this state and nothing else needs it.
20371
- cursor: () => scratchRef.current?.closeHintActive ? "pointer" : "crosshair",
20801
+ cursor: () => scratchRef.current?.closeHintActive ? "pointer" : cursorFor("pen", { fallback: "crosshair" }),
20372
20802
  presentation: {
20373
20803
  label: "Pen",
20374
20804
  icon: createElement(PenIcon),
@@ -20437,7 +20867,7 @@ function useRectTool() {
20437
20867
  id: "rect",
20438
20868
  capabilities: ["creates-shapes"],
20439
20869
  hookName: "useRectTool",
20440
- cursor: "crosshair",
20870
+ cursor: cursorFor("crosshairRect", { fallback: "crosshair" }),
20441
20871
  presentation: {
20442
20872
  label: "Rectangle",
20443
20873
  icon: createElement(RectIcon),
@@ -20463,7 +20893,7 @@ function useEllipseTool() {
20463
20893
  id: "ellipse",
20464
20894
  capabilities: ["creates-shapes"],
20465
20895
  hookName: "useEllipseTool",
20466
- cursor: "crosshair",
20896
+ cursor: cursorFor("crosshairEllipse", { fallback: "crosshair" }),
20467
20897
  presentation: {
20468
20898
  label: "Ellipse",
20469
20899
  group: "shape",
@@ -20486,7 +20916,7 @@ function useLineTool() {
20486
20916
  id: "line",
20487
20917
  capabilities: ["creates-shapes"],
20488
20918
  hookName: "useLineTool",
20489
- cursor: "crosshair",
20919
+ cursor: cursorFor("crosshairLine", { fallback: "crosshair" }),
20490
20920
  presentation: {
20491
20921
  label: "Line",
20492
20922
  group: "shape",
@@ -20597,7 +21027,7 @@ function usePolygonTool(options = {}) {
20597
21027
  id: "polygon",
20598
21028
  capabilities: ["creates-shapes"],
20599
21029
  hookName: "usePolygonTool",
20600
- cursor: "crosshair",
21030
+ cursor: cursorFor("crosshairPolygon", { fallback: "crosshair" }),
20601
21031
  // Registered by <ToolActionsMounter>, inside the ActionsProvider —
20602
21032
  // see `ToolDef.actions` for why the hook can't register it itself.
20603
21033
  actions: [adjustSidesAction],
@@ -20697,7 +21127,7 @@ function useStarTool(options = {}) {
20697
21127
  id: "star",
20698
21128
  capabilities: ["creates-shapes"],
20699
21129
  hookName: "useStarTool",
20700
- cursor: "crosshair",
21130
+ cursor: cursorFor("crosshairStar", { fallback: "crosshair" }),
20701
21131
  // Registered by <ToolActionsMounter>, inside the ActionsProvider —
20702
21132
  // see `ToolDef.actions` for why the hook can't register it itself.
20703
21133
  actions: [adjustPointsAction],
@@ -20753,7 +21183,7 @@ function usePencilTool() {
20753
21183
  id: "pencil",
20754
21184
  capabilities: ["creates-paths"],
20755
21185
  hookName: "usePencilTool",
20756
- cursor: "crosshair",
21186
+ cursor: cursorFor("pencil", { fallback: "crosshair" }),
20757
21187
  presentation: {
20758
21188
  label: "Pencil",
20759
21189
  group: "draw",
@@ -21734,6 +22164,111 @@ function buildSceneTree(adapter, drawOne, view, forLayer, derivedPathOf) {
21734
22164
  }
21735
22165
  return out;
21736
22166
  }
22167
+ function same(a, b) {
22168
+ if (a === b) return true;
22169
+ if (!a || !b) return false;
22170
+ return a.glyph === b.glyph && a.angle === b.angle && a.size === b.size && a.worldRadius === b.worldRadius;
22171
+ }
22172
+ function createPaintedCursorState() {
22173
+ let base = null;
22174
+ let override = null;
22175
+ let at = null;
22176
+ const subs = /* @__PURE__ */ new Set();
22177
+ const notify2 = () => {
22178
+ for (const fn of subs) fn();
22179
+ };
22180
+ return {
22181
+ setBase(cursor) {
22182
+ if (same(base, cursor)) return;
22183
+ base = cursor;
22184
+ notify2();
22185
+ },
22186
+ setOverride(cursor) {
22187
+ if (same(override, cursor)) return;
22188
+ override = cursor;
22189
+ notify2();
22190
+ },
22191
+ setPointer(x, y) {
22192
+ if (at && at.x === x && at.y === y) return;
22193
+ at = { x, y };
22194
+ if (override ?? base) notify2();
22195
+ },
22196
+ clearPointer() {
22197
+ if (at === null) return;
22198
+ at = null;
22199
+ notify2();
22200
+ },
22201
+ active() {
22202
+ return (override ?? base) !== null;
22203
+ },
22204
+ current() {
22205
+ const cursor = override ?? base;
22206
+ if (!cursor || !at) return null;
22207
+ return { cursor, at };
22208
+ },
22209
+ subscribe(fn) {
22210
+ subs.add(fn);
22211
+ return () => {
22212
+ subs.delete(fn);
22213
+ };
22214
+ }
22215
+ };
22216
+ }
22217
+ var PAINTED_CURSOR_LAYER_ID = "painted-cursor";
22218
+ var pathCache = /* @__PURE__ */ new Map();
22219
+ function pathFor(d) {
22220
+ let p = pathCache.get(d);
22221
+ if (p === void 0) {
22222
+ p = pathFromD(d);
22223
+ pathCache.set(d, p);
22224
+ }
22225
+ return p;
22226
+ }
22227
+ function commandFor2(op) {
22228
+ return {
22229
+ kind: "path",
22230
+ path: pathFor(op.d),
22231
+ ...op.fill !== void 0 ? { fill: { color: op.fill } } : {},
22232
+ ...op.stroke !== void 0 ? {
22233
+ stroke: {
22234
+ paint: { color: op.stroke.color },
22235
+ width: op.stroke.width,
22236
+ cap: "round",
22237
+ join: "round"
22238
+ }
22239
+ } : {}
22240
+ };
22241
+ }
22242
+ function createPaintedCursorLayer(state) {
22243
+ return {
22244
+ id: PAINTED_CURSOR_LAYER_ID,
22245
+ label: "Painted cursor",
22246
+ space: "screen",
22247
+ draw: (_data, view) => {
22248
+ const frame = state.current();
22249
+ if (!frame) return [];
22250
+ const { cursor, at } = frame;
22251
+ const glyph = GLYPHS[cursor.glyph];
22252
+ if (!glyph) return [];
22253
+ const scale2 = (view.scale.x + view.scale.y) / 2;
22254
+ const worldSized = cursor.worldRadius !== void 0;
22255
+ const size = worldSized ? cursorWorldSize(glyph, cursor.worldRadius, scale2) : cursor.size ?? 24;
22256
+ if (!(size > 0) || !Number.isFinite(size)) return [];
22257
+ const ops = cursorPaintOps(
22258
+ glyph,
22259
+ worldSized ? { lineWidthScale: chromeLineWidthScale(glyph, size) } : {}
22260
+ );
22261
+ const [a, b, c, d, e, f] = cursorPaintMatrix(glyph, {
22262
+ size,
22263
+ angle: cursor.angle,
22264
+ at
22265
+ });
22266
+ const transform = new Float32Array([a, b, 0, c, d, 0, e, f, 1]);
22267
+ const children = ops.map(commandFor2);
22268
+ return [{ kind: "group", transform, children }];
22269
+ }
22270
+ };
22271
+ }
21737
22272
  function mat3ToRenderMatrix(m) {
21738
22273
  return new Float32Array([m[0], m[1], 0, m[2], m[3], 0, m[4], m[5], 1]);
21739
22274
  }
@@ -22103,11 +22638,11 @@ function resolveToolsCursor(tools, ctxBase) {
22103
22638
  const id = tools.hotkeyEngaged ?? tools.active;
22104
22639
  const tool = tools.registry[id];
22105
22640
  if (!tool?.cursor) return void 0;
22106
- if (typeof tool.cursor === "string") return tool.cursor;
22641
+ if (typeof tool.cursor !== "function") return resolveCursorTier(tool.cursor);
22107
22642
  if (!ctxBase) return void 0;
22108
22643
  try {
22109
22644
  const base = ctxBase();
22110
- return tool.cursor({ ...base, scratch: null });
22645
+ return resolveCursorTier(tool.cursor({ ...base, scratch: null }));
22111
22646
  } catch {
22112
22647
  return void 0;
22113
22648
  }
@@ -22272,6 +22807,14 @@ function CanvasInner(props, ref) {
22272
22807
  viewSubsRef.current.delete(fn);
22273
22808
  };
22274
22809
  }, []);
22810
+ const paintedCursor = useMemo(() => createPaintedCursorState(), []);
22811
+ const paintedCursorLayer = useMemo(
22812
+ () => createPaintedCursorLayer(paintedCursor),
22813
+ [paintedCursor]
22814
+ );
22815
+ useEffect(() => paintedCursor.subscribe(() => {
22816
+ requestRedraw();
22817
+ }), [paintedCursor, requestRedraw]);
22275
22818
  useImperativeHandle(ref, () => ({
22276
22819
  // Named rather than read off `canvasRef` so the handle rebuilds when a
22277
22820
  // detached surface's input element arrives, which is a render later.
@@ -22284,7 +22827,8 @@ function CanvasInner(props, ref) {
22284
22827
  getView,
22285
22828
  setView,
22286
22829
  subscribeView,
22287
- getPaintedVersion
22830
+ getPaintedVersion,
22831
+ paintedCursor
22288
22832
  }), [
22289
22833
  canvasRef,
22290
22834
  ownCanvasRef,
@@ -22298,7 +22842,8 @@ function CanvasInner(props, ref) {
22298
22842
  getView,
22299
22843
  setView,
22300
22844
  subscribeView,
22301
- getPaintedVersion
22845
+ getPaintedVersion,
22846
+ paintedCursor
22302
22847
  ]);
22303
22848
  const glRendererRef = useRef(null);
22304
22849
  const layerCacheRef = useRef(/* @__PURE__ */ new Map());
@@ -22562,8 +23107,16 @@ function CanvasInner(props, ref) {
22562
23107
  const layersWithDebug = useMemo(() => {
22563
23108
  const withViews = viewRegistry ? [...layers, ...viewRegistry.list().map((r) => r.layer)] : layers;
22564
23109
  const base = debugSink && resolvedDebugConfig ? [...withViews, createDebugOverlayLayer({ sink: debugSink, config: resolvedDebugConfig })] : withViews;
22565
- return [...base, ...extrasRef.current];
22566
- }, [layers, debugSink, resolvedDebugConfig, extrasVersion, viewRegistry, viewRegistryVersion]);
23110
+ return [...base, ...extrasRef.current, paintedCursorLayer];
23111
+ }, [
23112
+ layers,
23113
+ debugSink,
23114
+ resolvedDebugConfig,
23115
+ extrasVersion,
23116
+ viewRegistry,
23117
+ viewRegistryVersion,
23118
+ paintedCursorLayer
23119
+ ]);
22567
23120
  const shaderIdKey = shaders?.map((h) => h.id).join("|") ?? "";
22568
23121
  const paintInputsRef = useRef({
22569
23122
  layers: layersWithDebug,
@@ -22685,8 +23238,14 @@ function CanvasInner(props, ref) {
22685
23238
  if (!renderer) return;
22686
23239
  registerShadersOnRenderer(renderer, shaders);
22687
23240
  }, [shaderIdKey]);
22688
- const toolsCursor = tools ? resolveToolsCursor(tools, toolsCtxBase) : void 0;
23241
+ const toolsCursorTier = tools ? resolveToolsCursor(tools, toolsCtxBase) : void 0;
23242
+ const toolsCursor = toolsCursorTier === void 0 ? void 0 : toolsCursorTier.kind === "css" ? toolsCursorTier.css : "none";
22689
23243
  const effectiveStyle = toolsCursor ? { ...style, cursor: toolsCursor } : style;
23244
+ const toolsPainted = toolsCursorTier?.kind === "painted" ? toolsCursorTier : null;
23245
+ const toolsPaintedKey = toolsPainted ? `${toolsPainted.glyph}|${toolsPainted.angle}|${toolsPainted.size}|${toolsPainted.worldRadius}` : "";
23246
+ useEffect(() => {
23247
+ paintedCursor.setBase(toolsPainted);
23248
+ }, [paintedCursor, toolsPaintedKey]);
22690
23249
  const layersForMoveRef = useRef(layersWithDebug);
22691
23250
  layersForMoveRef.current = layersWithDebug;
22692
23251
  useEffect(() => {
@@ -24006,10 +24565,16 @@ function createCornerResizeAffordance(opts = {}) {
24006
24565
  hitKind: c.kind,
24007
24566
  // Diagonal by fixed-corner parity: a matched-axis anchor (min-min /
24008
24567
  // max-max fixed) means the dragged corner sits on the ↘ diagonal;
24009
- // mixed axes sit on the ↗ diagonal. Not rotation-aware — a rotated
24010
- // target keeps the unrotated hint, the same policy as every
24011
- // mainstream editor short of Figma.
24012
- cursor: c.anchor.x === c.anchor.y ? "nwse-resize" : "nesw-resize",
24568
+ // mixed axes sit on the ↗ diagonal. The glyph is horizontal at angle
24569
+ // 0, so the diagonal is a half-quarter-turn either way, plus the
24570
+ // target's own rotation. The keyword is the fallback rather than the
24571
+ // answer: CSS has four of them, which cannot express a target
24572
+ // rotated to anything but a multiple of 45°.
24573
+ cursor: {
24574
+ glyph: "resize",
24575
+ angle: (c.anchor.x === c.anchor.y ? Math.PI / 4 : -Math.PI / 4) + angleOf(xf),
24576
+ fallback: c.anchor.x === c.anchor.y ? "nwse-resize" : "nesw-resize"
24577
+ },
24013
24578
  bind: () => ({
24014
24579
  initialScratch: {
24015
24580
  anchor: c.anchor,
@@ -27946,6 +28511,10 @@ function GestureDispatcherMounter({
27946
28511
  const requestRedraw = useCallback(() => {
27947
28512
  canvasApiRef?.current?.requestRedraw?.();
27948
28513
  }, [canvasApiRef]);
28514
+ const paintedCursor = useCallback(
28515
+ () => canvasApiRef?.current?.paintedCursor,
28516
+ [canvasApiRef]
28517
+ );
27949
28518
  const views = useMemo(() => {
27950
28519
  if (!viewRegistry) return void 0;
27951
28520
  return {
@@ -27964,6 +28533,7 @@ function GestureDispatcherMounter({
27964
28533
  dispatcher,
27965
28534
  clientToWorld: clientToWorld2,
27966
28535
  requestRedraw,
28536
+ paintedCursor,
27967
28537
  getRuleCtx,
27968
28538
  onDoubleClick,
27969
28539
  ...views ? { views } : {}
@@ -28816,5 +29386,5 @@ function oklchToOklab(L, C, h) {
28816
29386
  }
28817
29387
 
28818
29388
  export { ArrowIcon, EllipseIcon, FALLBACK_FIT_VIEW, LineIcon, MinimapCanvas, PATH_C, PATH_L, PATH_M, PATH_Q, PATH_Z, PathBuilder, PencilIcon, RectIcon, SCRIPT_METRICS, SceneCanvas, SceneViewCanvas, SelectIcon, TextIcon, UnknownIcon, WeaselProvider, asNodeId, boundsOfPath, buildSceneViewCommands, computeFitView, computeIndicatorCommand, createScene, eligibleContribution, ellipsePath, evaluateEnabled, getMarker, getPaintKind, isBuiltinToolPref, linePath, markerDrawCommands, oklabToSrgbU8, oklchToOklab, rectPath, renderSceneToCanvas, renderSceneToPixels, resolveStrokeWidth, rgbaToHex, sceneFromJSON, textCommand, tileGeometry, useActionsRegistry, useActiveToolContext, useOptionalDepRegistry, useVisibleRaf };
28819
- //# sourceMappingURL=chunk-ROQDRXKW.js.map
28820
- //# sourceMappingURL=chunk-ROQDRXKW.js.map
29389
+ //# sourceMappingURL=chunk-7VW3S44V.js.map
29390
+ //# sourceMappingURL=chunk-7VW3S44V.js.map