canvu-react 0.4.19 → 0.4.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/native.js CHANGED
@@ -1998,178 +1998,6 @@ function NativeShapeRenderer({ item }) {
1998
1998
  }
1999
1999
  return null;
2000
2000
  }
2001
- var CURSOR_STROKE = "#18181b";
2002
- var CURSOR_STROKE_WIDTH = 2;
2003
- var CROSSHAIR_STROKE_WIDTH = 1.5;
2004
- function NativeDrawCursor() {
2005
- return /* @__PURE__ */ jsxs(Fragment, { children: [
2006
- /* @__PURE__ */ jsx(
2007
- Path,
2008
- {
2009
- path: "M13 21h8",
2010
- color: CURSOR_STROKE,
2011
- style: "stroke",
2012
- strokeWidth: CURSOR_STROKE_WIDTH,
2013
- strokeCap: "round",
2014
- strokeJoin: "round",
2015
- antiAlias: true
2016
- }
2017
- ),
2018
- /* @__PURE__ */ jsx(
2019
- Path,
2020
- {
2021
- path: "M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z",
2022
- color: CURSOR_STROKE,
2023
- style: "stroke",
2024
- strokeWidth: CURSOR_STROKE_WIDTH,
2025
- strokeCap: "round",
2026
- strokeJoin: "round",
2027
- antiAlias: true
2028
- }
2029
- )
2030
- ] });
2031
- }
2032
- function NativeMarkerCursor() {
2033
- return /* @__PURE__ */ jsxs(Fragment, { children: [
2034
- /* @__PURE__ */ jsx(
2035
- Path,
2036
- {
2037
- path: "m9 11-6 6v3h9l3-3",
2038
- color: CURSOR_STROKE,
2039
- style: "stroke",
2040
- strokeWidth: CURSOR_STROKE_WIDTH,
2041
- strokeCap: "round",
2042
- strokeJoin: "round",
2043
- antiAlias: true
2044
- }
2045
- ),
2046
- /* @__PURE__ */ jsx(
2047
- Path,
2048
- {
2049
- path: "m22 12-4.6 4.6a2 2 0 0 1-2.8 0l-5.2-5.2a2 2 0 0 1 0-2.8L14 4",
2050
- color: CURSOR_STROKE,
2051
- style: "stroke",
2052
- strokeWidth: CURSOR_STROKE_WIDTH,
2053
- strokeCap: "round",
2054
- strokeJoin: "round",
2055
- antiAlias: true
2056
- }
2057
- )
2058
- ] });
2059
- }
2060
- function NativeEraserCursor() {
2061
- return /* @__PURE__ */ jsxs(Fragment, { children: [
2062
- /* @__PURE__ */ jsx(
2063
- Path,
2064
- {
2065
- path: "M21 21H8a2 2 0 0 1-1.42-.587l-3.994-3.999a2 2 0 0 1 0-2.828l10-10a2 2 0 0 1 2.829 0l5.999 6a2 2 0 0 1 0 2.828L12.834 21",
2066
- color: CURSOR_STROKE,
2067
- style: "stroke",
2068
- strokeWidth: CURSOR_STROKE_WIDTH,
2069
- strokeCap: "round",
2070
- strokeJoin: "round",
2071
- antiAlias: true
2072
- }
2073
- ),
2074
- /* @__PURE__ */ jsx(
2075
- Path,
2076
- {
2077
- path: "m5.082 11.09 8.828 8.828",
2078
- color: CURSOR_STROKE,
2079
- style: "stroke",
2080
- strokeWidth: CURSOR_STROKE_WIDTH,
2081
- strokeCap: "round",
2082
- strokeJoin: "round",
2083
- antiAlias: true
2084
- }
2085
- )
2086
- ] });
2087
- }
2088
- function NativeCrosshairCursor() {
2089
- return /* @__PURE__ */ jsxs(Fragment, { children: [
2090
- /* @__PURE__ */ jsx(
2091
- Line,
2092
- {
2093
- p1: vec(12, 3),
2094
- p2: vec(12, 21),
2095
- color: CURSOR_STROKE,
2096
- style: "stroke",
2097
- strokeWidth: CROSSHAIR_STROKE_WIDTH,
2098
- strokeCap: "round",
2099
- antiAlias: true
2100
- }
2101
- ),
2102
- /* @__PURE__ */ jsx(
2103
- Line,
2104
- {
2105
- p1: vec(3, 12),
2106
- p2: vec(21, 12),
2107
- color: CURSOR_STROKE,
2108
- style: "stroke",
2109
- strokeWidth: CROSSHAIR_STROKE_WIDTH,
2110
- strokeCap: "round",
2111
- antiAlias: true
2112
- }
2113
- )
2114
- ] });
2115
- }
2116
- function NativeTextCursor() {
2117
- return /* @__PURE__ */ jsxs(Fragment, { children: [
2118
- /* @__PURE__ */ jsx(
2119
- Line,
2120
- {
2121
- p1: vec(12, 4),
2122
- p2: vec(12, 20),
2123
- color: CURSOR_STROKE,
2124
- style: "stroke",
2125
- strokeWidth: CURSOR_STROKE_WIDTH,
2126
- strokeCap: "round",
2127
- antiAlias: true
2128
- }
2129
- ),
2130
- /* @__PURE__ */ jsx(
2131
- Line,
2132
- {
2133
- p1: vec(8, 4),
2134
- p2: vec(16, 4),
2135
- color: CURSOR_STROKE,
2136
- style: "stroke",
2137
- strokeWidth: CURSOR_STROKE_WIDTH,
2138
- strokeCap: "round",
2139
- antiAlias: true
2140
- }
2141
- ),
2142
- /* @__PURE__ */ jsx(
2143
- Line,
2144
- {
2145
- p1: vec(8, 20),
2146
- p2: vec(16, 20),
2147
- color: CURSOR_STROKE,
2148
- style: "stroke",
2149
- strokeWidth: CURSOR_STROKE_WIDTH,
2150
- strokeCap: "round",
2151
- antiAlias: true
2152
- }
2153
- )
2154
- ] });
2155
- }
2156
- function NativeToolCursorGlyph({
2157
- cursor
2158
- }) {
2159
- if (cursor.kind === "draw") return /* @__PURE__ */ jsx(NativeDrawCursor, {});
2160
- if (cursor.kind === "marker") return /* @__PURE__ */ jsx(NativeMarkerCursor, {});
2161
- if (cursor.kind === "eraser") return /* @__PURE__ */ jsx(NativeEraserCursor, {});
2162
- if (cursor.kind === "text") return /* @__PURE__ */ jsx(NativeTextCursor, {});
2163
- return /* @__PURE__ */ jsx(NativeCrosshairCursor, {});
2164
- }
2165
- function NativeToolCursorRenderer({
2166
- cursor,
2167
- point
2168
- }) {
2169
- const x = point.x - cursor.hotspot.x;
2170
- const y = point.y - cursor.hotspot.y;
2171
- return /* @__PURE__ */ jsx(Group, { transform: [{ translateX: x }, { translateY: y }], children: /* @__PURE__ */ jsx(NativeToolCursorGlyph, { cursor }) });
2172
- }
2173
2001
 
2174
2002
  // src/native/native-overlay-style.ts
2175
2003
  var ERASER_TINT = "#cbd5e1";
@@ -2226,8 +2054,7 @@ function NativeInteractionOverlay({
2226
2054
  eraserTrail,
2227
2055
  laserTrail,
2228
2056
  eraserPreviewItems = [],
2229
- previewStrokeStyle,
2230
- toolCursor
2057
+ previewStrokeStyle
2231
2058
  }) {
2232
2059
  const z = camera.zoom;
2233
2060
  const camTransform = skiaCameraTransform(z, camera.x, camera.y);
@@ -2604,7 +2431,7 @@ function NativeInteractionOverlay({
2604
2431
  ] });
2605
2432
  }, [laserTrail, z]);
2606
2433
  if (width <= 0 || height <= 0) return null;
2607
- return /* @__PURE__ */ jsxs(
2434
+ return /* @__PURE__ */ jsx(
2608
2435
  Canvas,
2609
2436
  {
2610
2437
  style: {
@@ -2615,22 +2442,13 @@ function NativeInteractionOverlay({
2615
2442
  height
2616
2443
  },
2617
2444
  pointerEvents: "none",
2618
- children: [
2619
- /* @__PURE__ */ jsxs(Group, { transform: camTransform, children: [
2620
- previewElements,
2621
- laserTrailElements,
2622
- eraserTrailElements,
2623
- eraserPreviewElements,
2624
- selectionElements
2625
- ] }),
2626
- toolCursor ? /* @__PURE__ */ jsx(
2627
- NativeToolCursorRenderer,
2628
- {
2629
- cursor: toolCursor.cursor,
2630
- point: toolCursor.point
2631
- }
2632
- ) : null
2633
- ]
2445
+ children: /* @__PURE__ */ jsxs(Group, { transform: camTransform, children: [
2446
+ previewElements,
2447
+ laserTrailElements,
2448
+ eraserTrailElements,
2449
+ eraserPreviewElements,
2450
+ selectionElements
2451
+ ] })
2634
2452
  }
2635
2453
  );
2636
2454
  }
@@ -3968,39 +3786,6 @@ function resizeItemByHandle(item, start, handle, currentWorld) {
3968
3786
  return { ...item, x: nb.x, y: nb.y, bounds: nb };
3969
3787
  }
3970
3788
 
3971
- // src/native/native-tool-cursors.ts
3972
- var ICON_SIZE = 24;
3973
- var CENTER_HOTSPOT = { x: 12, y: 12 };
3974
- function nativeCursorForVectorToolId(toolId) {
3975
- switch (toolId) {
3976
- case "rect":
3977
- case "ellipse":
3978
- case "architectural-cloud":
3979
- case "line":
3980
- case "arrow":
3981
- case "laser":
3982
- case "image":
3983
- return { kind: "crosshair", size: ICON_SIZE, hotspot: CENTER_HOTSPOT };
3984
- case "draw":
3985
- return { kind: "draw", size: ICON_SIZE, hotspot: { x: 4, y: 18 } };
3986
- case "marker":
3987
- return { kind: "marker", size: ICON_SIZE, hotspot: { x: 11, y: 14 } };
3988
- case "eraser":
3989
- return { kind: "eraser", size: ICON_SIZE, hotspot: { x: 12, y: 14 } };
3990
- case "text":
3991
- return { kind: "text", size: ICON_SIZE, hotspot: CENTER_HOTSPOT };
3992
- default:
3993
- return null;
3994
- }
3995
- }
3996
- function nativeCrosshairToolCursor() {
3997
- return { kind: "crosshair", size: ICON_SIZE, hotspot: CENTER_HOTSPOT };
3998
- }
3999
- function nativeFallbackToolCursorPoint(size) {
4000
- if (size.width <= 0 || size.height <= 0) return null;
4001
- return { x: size.width / 2, y: size.height / 2 };
4002
- }
4003
-
4004
3789
  // src/native/native-vector-interactions.ts
4005
3790
  var NATIVE_SELECTION_HANDLE_HIT_RADIUS_PX = 24;
4006
3791
  function supportsNativeResizeHandles(item) {
@@ -4145,7 +3930,6 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
4145
3930
  onCameraChange,
4146
3931
  customPlacement,
4147
3932
  customPlacements = [],
4148
- customCrosshairToolIds = [],
4149
3933
  toolbar,
4150
3934
  showStyleInspector = false,
4151
3935
  styleInspectorPlacement = "bottom"
@@ -4168,8 +3952,6 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
4168
3952
  customPlacementRef.current = customPlacement;
4169
3953
  const customPlacementsRef = useRef(customPlacements);
4170
3954
  customPlacementsRef.current = customPlacements;
4171
- const customCrosshairToolIdsRef = useRef(customCrosshairToolIds);
4172
- customCrosshairToolIdsRef.current = customCrosshairToolIds;
4173
3955
  const onSelectionChangeRef = useRef(onSelectionChange);
4174
3956
  onSelectionChangeRef.current = onSelectionChange;
4175
3957
  const itemsRef = useRef(items);
@@ -4182,7 +3964,6 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
4182
3964
  );
4183
3965
  const [eraserTrail, setEraserTrail] = useState([]);
4184
3966
  const [laserTrail, setLaserTrail] = useState([]);
4185
- const [toolCursorPoint, setToolCursorPoint] = useState(null);
4186
3967
  const laserClearTimerRef = useRef(null);
4187
3968
  const strokeStyleRef = useRef({ ...DEFAULT_STROKE_STYLE });
4188
3969
  const markerStrokeStyleRef = useRef({ ...MARKER_TOOL_STYLE });
@@ -4277,60 +4058,14 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
4277
4058
  setCameraTick((n) => n + 1);
4278
4059
  onCameraChangeRef.current?.();
4279
4060
  }, []);
4280
- const cursorForToolId = useCallback((nextToolId) => {
4281
- const builtInCursor = nativeCursorForVectorToolId(nextToolId);
4282
- if (builtInCursor) return builtInCursor;
4283
- return customCrosshairToolIdsRef.current.includes(nextToolId) ? nativeCrosshairToolCursor() : null;
4061
+ const onLayout = useCallback((e) => {
4062
+ const { width, height } = e.nativeEvent.layout;
4063
+ setSize({ width, height });
4064
+ }, []);
4065
+ const updateToolCursorPoint = useCallback((_point) => {
4284
4066
  }, []);
4285
- const onLayout = useCallback(
4286
- (e) => {
4287
- const { width, height } = e.nativeEvent.layout;
4288
- const nextSize = { width, height };
4289
- setSize(nextSize);
4290
- if (!interactive) {
4291
- setToolCursorPoint(null);
4292
- return;
4293
- }
4294
- if (!cursorForToolId(toolIdRef.current)) {
4295
- setToolCursorPoint(null);
4296
- return;
4297
- }
4298
- setToolCursorPoint(
4299
- (current) => current ?? nativeFallbackToolCursorPoint(nextSize)
4300
- );
4301
- },
4302
- [cursorForToolId, interactive]
4303
- );
4304
- const updateToolCursorPoint = useCallback(
4305
- (point) => {
4306
- if (!interactive) return;
4307
- if (!cursorForToolId(toolIdRef.current)) return;
4308
- setToolCursorPoint(point);
4309
- },
4310
- [cursorForToolId, interactive]
4311
- );
4312
4067
  const hideToolCursor = useCallback(() => {
4313
- setToolCursorPoint(null);
4314
4068
  }, []);
4315
- const showFallbackToolCursor = useCallback(
4316
- (nextToolId) => {
4317
- if (!interactive) {
4318
- setToolCursorPoint(null);
4319
- return;
4320
- }
4321
- if (!cursorForToolId(nextToolId)) {
4322
- setToolCursorPoint(null);
4323
- return;
4324
- }
4325
- setToolCursorPoint(
4326
- (current) => current ?? nativeFallbackToolCursorPoint(size)
4327
- );
4328
- },
4329
- [cursorForToolId, interactive, size]
4330
- );
4331
- useEffect(() => {
4332
- showFallbackToolCursor(toolId);
4333
- }, [showFallbackToolCursor, toolId]);
4334
4069
  const selectedItems = useMemo(
4335
4070
  () => items.filter((it) => selectedIds.includes(it.id)),
4336
4071
  [items, selectedIds]
@@ -4343,6 +4078,196 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
4343
4078
  const showResizeHandles = interactive && selectedItems.length === 1 && !selectedItems[0]?.locked && supportsNativeResizeHandles(selectedItems[0]);
4344
4079
  const lastPinchDist = useRef(null);
4345
4080
  const lastPanPoint = useRef(null);
4081
+ const beginDragAtScreenPoint = useCallback(
4082
+ (point) => {
4083
+ lastPinchDist.current = null;
4084
+ lastPanPoint.current = null;
4085
+ const sx = point.x;
4086
+ const sy = point.y;
4087
+ updateToolCursorPoint(point);
4088
+ if (!interactive) {
4089
+ dragStateRef.current = { kind: "pan" };
4090
+ return;
4091
+ }
4092
+ const tool = toolIdRef.current;
4093
+ const cam = cameraRef.current;
4094
+ if (!cam) return;
4095
+ const { worldX, worldY } = screenToWorld(sx, sy);
4096
+ if (tool === "hand") {
4097
+ dragStateRef.current = { kind: "pan" };
4098
+ return;
4099
+ }
4100
+ if (tool === "select") {
4101
+ const currentSelectedIds = selectedIdsRef.current;
4102
+ const selectedItem = currentSelectedIds.length === 1 ? itemsRef.current.find((item) => item.id === currentSelectedIds[0]) : void 0;
4103
+ const selectionHandle = hitTestNativeSelectionHandle({
4104
+ selectedItem,
4105
+ selectedCount: currentSelectedIds.length,
4106
+ worldPoint: { x: worldX, y: worldY },
4107
+ zoom: cam.zoom
4108
+ });
4109
+ if (selectionHandle && selectedItem) {
4110
+ if (selectionHandle.kind === "rotate") {
4111
+ const rotationStart = nativeRotationDragStart({
4112
+ item: selectedItem,
4113
+ worldPoint: { x: worldX, y: worldY }
4114
+ });
4115
+ dragStateRef.current = {
4116
+ kind: "rotate",
4117
+ id: selectedItem.id,
4118
+ snapshot: selectedItem,
4119
+ ...rotationStart
4120
+ };
4121
+ return;
4122
+ }
4123
+ dragStateRef.current = {
4124
+ kind: "resize",
4125
+ id: selectedItem.id,
4126
+ handle: selectionHandle.handle,
4127
+ snapshot: selectedItem,
4128
+ start: {
4129
+ bounds: selectedItem.bounds,
4130
+ line: selectedItem.line
4131
+ }
4132
+ };
4133
+ return;
4134
+ }
4135
+ const hit = hitTestWorldPoint(itemsRef.current, worldX, worldY, {
4136
+ lineHitWorld: 10 / cam.zoom,
4137
+ ignoreLocked: true
4138
+ });
4139
+ if (hit) {
4140
+ const cur = selectedIdsRef.current;
4141
+ const ids = cur.includes(hit.id) ? [...cur] : [hit.id];
4142
+ const snapshots = {};
4143
+ for (const id of ids) {
4144
+ const it = itemsRef.current.find((i) => i.id === id);
4145
+ if (it) snapshots[id] = it;
4146
+ }
4147
+ dragStateRef.current = {
4148
+ kind: "move",
4149
+ ids,
4150
+ snapshots,
4151
+ startWorld: { x: worldX, y: worldY }
4152
+ };
4153
+ if (!cur.includes(hit.id)) {
4154
+ onSelectionChangeRef.current?.([hit.id]);
4155
+ }
4156
+ } else {
4157
+ onSelectionChangeRef.current?.([]);
4158
+ dragStateRef.current = {
4159
+ kind: "marquee",
4160
+ startWorld: { x: worldX, y: worldY }
4161
+ };
4162
+ setPlacementPreview({
4163
+ kind: "marquee",
4164
+ rect: { x: worldX, y: worldY, width: 0, height: 0 }
4165
+ });
4166
+ }
4167
+ return;
4168
+ }
4169
+ if (tool === "draw" || tool === "marker" || tool === "laser") {
4170
+ dragStateRef.current = {
4171
+ kind: "draw",
4172
+ tool,
4173
+ points: [{ x: worldX, y: worldY }]
4174
+ };
4175
+ if (tool === "laser") {
4176
+ if (laserClearTimerRef.current) {
4177
+ clearTimeout(laserClearTimerRef.current);
4178
+ laserClearTimerRef.current = null;
4179
+ }
4180
+ setLaserTrail([{ x: worldX, y: worldY, t: Date.now() }]);
4181
+ } else {
4182
+ setPlacementPreview({
4183
+ kind: "stroke",
4184
+ tool,
4185
+ points: [{ x: worldX, y: worldY }],
4186
+ style: { ...strokeStyleRef.current }
4187
+ });
4188
+ }
4189
+ return;
4190
+ }
4191
+ if (tool === "eraser") {
4192
+ dragStateRef.current = { kind: "erase" };
4193
+ eraserPreviewIdSetRef.current = /* @__PURE__ */ new Set();
4194
+ setEraserPreviewIds([]);
4195
+ setEraserTrail([{ x: worldX, y: worldY, t: Date.now() }]);
4196
+ const toErase = collectEraserTargetsAtWorldPoint(
4197
+ itemsRef.current,
4198
+ worldX,
4199
+ worldY,
4200
+ { lineHitWorld: 10 / cam.zoom, ignoreLocked: true }
4201
+ );
4202
+ for (const id of toErase) {
4203
+ eraserPreviewIdSetRef.current.add(id);
4204
+ }
4205
+ setEraserPreviewIds(Array.from(eraserPreviewIdSetRef.current));
4206
+ return;
4207
+ }
4208
+ if (isPlacementTool(tool)) {
4209
+ dragStateRef.current = {
4210
+ kind: "place",
4211
+ tool,
4212
+ startWorld: { x: worldX, y: worldY },
4213
+ startScreen: { x: sx, y: sy }
4214
+ };
4215
+ setPlacementPreview(
4216
+ placementPreviewForTool(
4217
+ tool,
4218
+ { x: worldX, y: worldY },
4219
+ {
4220
+ x: worldX,
4221
+ y: worldY
4222
+ }
4223
+ )
4224
+ );
4225
+ return;
4226
+ }
4227
+ const customPlacement2 = resolveNativeCustomPlacement(
4228
+ tool,
4229
+ customPlacementRef.current,
4230
+ customPlacementsRef.current
4231
+ );
4232
+ if (customPlacement2) {
4233
+ dragStateRef.current = {
4234
+ kind: "custom-place",
4235
+ tool,
4236
+ placement: customPlacement2,
4237
+ startWorld: { x: worldX, y: worldY },
4238
+ startScreen: { x: sx, y: sy }
4239
+ };
4240
+ setPlacementPreview({
4241
+ kind: "rect",
4242
+ rect: { x: worldX, y: worldY, width: 0, height: 0 }
4243
+ });
4244
+ return;
4245
+ }
4246
+ if (tool === "note" || tool === "text") {
4247
+ dragStateRef.current = {
4248
+ kind: "tap",
4249
+ tool,
4250
+ startWorld: { x: worldX, y: worldY },
4251
+ startScreen: { x: sx, y: sy }
4252
+ };
4253
+ return;
4254
+ }
4255
+ const handleWorldPointerDown = onWorldPointerDownRef.current;
4256
+ if (handleWorldPointerDown) {
4257
+ handleWorldPointerDown({
4258
+ toolId: tool,
4259
+ worldX,
4260
+ worldY,
4261
+ screenX: sx,
4262
+ screenY: sy
4263
+ });
4264
+ requestSelectToolAfterUse();
4265
+ return;
4266
+ }
4267
+ dragStateRef.current = { kind: "pan" };
4268
+ },
4269
+ [interactive, requestSelectToolAfterUse, screenToWorld, updateToolCursorPoint]
4270
+ );
4346
4271
  const applyDragMoveAtScreenPoint = useCallback(
4347
4272
  (point, pagePoint) => {
4348
4273
  const cam = cameraRef.current;
@@ -4479,6 +4404,220 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
4479
4404
  },
4480
4405
  [requestRender, screenToWorld, updateToolCursorPoint]
4481
4406
  );
4407
+ const finishDragAtScreenPoint = useCallback(
4408
+ (point) => {
4409
+ lastPinchDist.current = null;
4410
+ lastPanPoint.current = null;
4411
+ updateToolCursorPoint(point);
4412
+ const st = dragStateRef.current;
4413
+ if (st.kind === "draw") {
4414
+ dragStateRef.current = { kind: "idle" };
4415
+ setPlacementPreview(null);
4416
+ if (st.tool === "laser") {
4417
+ if (laserClearTimerRef.current) {
4418
+ clearTimeout(laserClearTimerRef.current);
4419
+ }
4420
+ laserClearTimerRef.current = setTimeout(() => {
4421
+ setLaserTrail([]);
4422
+ laserClearTimerRef.current = null;
4423
+ }, 650);
4424
+ requestSelectToolAfterUse();
4425
+ return;
4426
+ }
4427
+ if (st.points.length < 1) return;
4428
+ const change = onItemsChangeRef.current;
4429
+ if (!change) return;
4430
+ const id = createShapeId();
4431
+ const item = createFreehandStrokeItem(
4432
+ id,
4433
+ st.points,
4434
+ st.tool,
4435
+ strokeStyleRef.current
4436
+ );
4437
+ if (item) {
4438
+ change([...itemsRef.current, item]);
4439
+ }
4440
+ requestSelectToolAfterUse();
4441
+ return;
4442
+ }
4443
+ if (st.kind === "move") {
4444
+ dragStateRef.current = { kind: "idle" };
4445
+ return;
4446
+ }
4447
+ if (st.kind === "resize" || st.kind === "rotate") {
4448
+ dragStateRef.current = { kind: "idle" };
4449
+ return;
4450
+ }
4451
+ if (st.kind === "marquee") {
4452
+ dragStateRef.current = { kind: "idle" };
4453
+ setPlacementPreview(null);
4454
+ const cam = cameraRef.current;
4455
+ if (!cam) return;
4456
+ const { worldX, worldY } = screenToWorld(point.x, point.y);
4457
+ const a = st.startWorld;
4458
+ if (typeof worldX !== "number" || typeof worldY !== "number") return;
4459
+ const raw = {
4460
+ x: Math.min(a.x, worldX),
4461
+ y: Math.min(a.y, worldY),
4462
+ width: Math.abs(worldX - a.x),
4463
+ height: Math.abs(worldY - a.y)
4464
+ };
4465
+ const picked = collectIdsInRect(itemsRef.current, raw);
4466
+ onSelectionChangeRef.current?.(picked);
4467
+ return;
4468
+ }
4469
+ if (st.kind === "erase") {
4470
+ const change = onItemsChangeRef.current;
4471
+ if (change && eraserPreviewIdSetRef.current.size > 0) {
4472
+ const idSet = new Set(eraserPreviewIdSetRef.current);
4473
+ change(itemsRef.current.filter((i) => !idSet.has(i.id)));
4474
+ }
4475
+ eraserPreviewIdSetRef.current.clear();
4476
+ setEraserPreviewIds([]);
4477
+ setEraserTrail([]);
4478
+ dragStateRef.current = { kind: "idle" };
4479
+ requestSelectToolAfterUse();
4480
+ return;
4481
+ }
4482
+ if (st.kind === "place") {
4483
+ dragStateRef.current = { kind: "idle" };
4484
+ setPlacementPreview(null);
4485
+ const change = onItemsChangeRef.current;
4486
+ if (!change) return;
4487
+ const { worldX, worldY } = screenToWorld(point.x, point.y);
4488
+ const a = st.startWorld;
4489
+ const b = { x: worldX, y: worldY };
4490
+ const screenDx = point.x - st.startScreen.x;
4491
+ const screenDy = point.y - st.startScreen.y;
4492
+ if (st.tool === "arrow" && Math.hypot(screenDx, screenDy) < MIN_ARROW_DRAG_PX) {
4493
+ return;
4494
+ }
4495
+ let raw = rectFromCorners(a, b);
4496
+ let br = normalizeRect(raw);
4497
+ let lineStart = a;
4498
+ let lineEnd = b;
4499
+ if (br.width < MIN_PLACE_SIZE || br.height < MIN_PLACE_SIZE) {
4500
+ const center = { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
4501
+ const defaults = defaultPlacementWorld(st.tool, center);
4502
+ raw = defaults.raw;
4503
+ br = normalizeRect(raw);
4504
+ if (defaults.lineWorld) {
4505
+ const [defaultStart, defaultEnd] = defaults.lineWorld;
4506
+ lineStart = defaultStart;
4507
+ lineEnd = defaultEnd;
4508
+ }
4509
+ }
4510
+ const id = createShapeId();
4511
+ const style = strokeStyleRef.current;
4512
+ if (st.tool === "rect") {
4513
+ change([...itemsRef.current, createRectangleItem(id, raw, style)]);
4514
+ onSelectionChangeRef.current?.([id]);
4515
+ requestSelectToolAfterUse();
4516
+ return;
4517
+ }
4518
+ if (st.tool === "ellipse") {
4519
+ change([...itemsRef.current, createEllipseItem(id, raw, style)]);
4520
+ onSelectionChangeRef.current?.([id]);
4521
+ requestSelectToolAfterUse();
4522
+ return;
4523
+ }
4524
+ if (st.tool === "architectural-cloud") {
4525
+ change([
4526
+ ...itemsRef.current,
4527
+ createArchitecturalCloudItem(id, raw, style)
4528
+ ]);
4529
+ onSelectionChangeRef.current?.([id]);
4530
+ requestSelectToolAfterUse();
4531
+ return;
4532
+ }
4533
+ const line = lineEndpointsToLocal(br, lineStart, lineEnd);
4534
+ change([...itemsRef.current, createLineItem(id, br, line, st.tool, style)]);
4535
+ onSelectionChangeRef.current?.([id]);
4536
+ requestSelectToolAfterUse();
4537
+ return;
4538
+ }
4539
+ if (st.kind === "custom-place") {
4540
+ dragStateRef.current = { kind: "idle" };
4541
+ setPlacementPreview(null);
4542
+ const change = onItemsChangeRef.current;
4543
+ if (!change) return;
4544
+ const { worldX, worldY } = screenToWorld(point.x, point.y);
4545
+ const center = {
4546
+ x: (st.startWorld.x + worldX) / 2,
4547
+ y: (st.startWorld.y + worldY) / 2
4548
+ };
4549
+ const raw = rectFromCorners(st.startWorld, { x: worldX, y: worldY });
4550
+ const normalized = normalizeRect(raw);
4551
+ const bounds = normalized.width < MIN_PLACE_SIZE || normalized.height < MIN_PLACE_SIZE ? {
4552
+ x: center.x - 60,
4553
+ y: center.y - 40,
4554
+ width: 120,
4555
+ height: 80
4556
+ } : normalized;
4557
+ const id = createShapeId();
4558
+ const item = st.placement.createItem({ id, bounds });
4559
+ change([...itemsRef.current, item]);
4560
+ onSelectionChangeRef.current?.([id]);
4561
+ requestSelectToolAfterUse();
4562
+ return;
4563
+ }
4564
+ if (st.kind === "tap") {
4565
+ dragStateRef.current = { kind: "idle" };
4566
+ const screenDx = point.x - st.startScreen.x;
4567
+ const screenDy = point.y - st.startScreen.y;
4568
+ if (Math.hypot(screenDx, screenDy) > TAP_PX) return;
4569
+ const change = onItemsChangeRef.current;
4570
+ if (!change) return;
4571
+ if (st.tool === "text") {
4572
+ const id = createShapeId();
4573
+ const item = createTextItem(
4574
+ id,
4575
+ {
4576
+ x: st.startWorld.x - 4,
4577
+ y: st.startWorld.y - 18,
4578
+ width: 160,
4579
+ height: 26
4580
+ },
4581
+ "Text",
4582
+ strokeStyleRef.current,
4583
+ 18
4584
+ );
4585
+ change([...itemsRef.current, item]);
4586
+ onSelectionChangeRef.current?.([id]);
4587
+ requestSelectToolAfterUse();
4588
+ }
4589
+ if (st.tool === "note") {
4590
+ const id = createShapeId();
4591
+ const note = {
4592
+ id,
4593
+ x: st.startWorld.x - 70,
4594
+ y: st.startWorld.y - 40,
4595
+ bounds: {
4596
+ x: st.startWorld.x - 70,
4597
+ y: st.startWorld.y - 40,
4598
+ width: 140,
4599
+ height: 80
4600
+ },
4601
+ childrenSvg: `<rect width="140" height="80" rx="8" fill="#fef08a" stroke="#facc15" stroke-width="1" /><text x="10" y="22" fill="#1f2937" font-size="12" font-family="system-ui">Nota</text>`,
4602
+ toolKind: "custom"
4603
+ };
4604
+ change([...itemsRef.current, note]);
4605
+ onSelectionChangeRef.current?.([id]);
4606
+ requestSelectToolAfterUse();
4607
+ }
4608
+ return;
4609
+ }
4610
+ dragStateRef.current = { kind: "idle" };
4611
+ },
4612
+ [requestSelectToolAfterUse, screenToWorld, updateToolCursorPoint]
4613
+ );
4614
+ const handlePointerDown = useCallback(
4615
+ (event) => {
4616
+ const point = screenPointFromPointerEvent(event);
4617
+ beginDragAtScreenPoint(point);
4618
+ },
4619
+ [beginDragAtScreenPoint]
4620
+ );
4482
4621
  const handlePointerMove = useCallback(
4483
4622
  (event) => {
4484
4623
  const point = screenPointFromPointerEvent(event);
@@ -4490,13 +4629,18 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
4490
4629
  },
4491
4630
  [applyDragMoveAtScreenPoint, updateToolCursorPoint]
4492
4631
  );
4632
+ const handlePointerUp = useCallback(
4633
+ (event) => {
4634
+ const point = screenPointFromPointerEvent(event);
4635
+ finishDragAtScreenPoint(point);
4636
+ },
4637
+ [finishDragAtScreenPoint]
4638
+ );
4493
4639
  const panResponder = useMemo(
4494
4640
  () => PanResponder.create({
4495
4641
  onStartShouldSetPanResponder: () => true,
4496
4642
  onMoveShouldSetPanResponder: () => true,
4497
4643
  onPanResponderGrant: (evt) => {
4498
- lastPinchDist.current = null;
4499
- lastPanPoint.current = null;
4500
4644
  const touches = evt.nativeEvent.touches;
4501
4645
  const sx = evt.nativeEvent.locationX;
4502
4646
  const sy = evt.nativeEvent.locationY;
@@ -4505,187 +4649,7 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
4505
4649
  dragStateRef.current = { kind: "pan" };
4506
4650
  return;
4507
4651
  }
4508
- updateToolCursorPoint({ x: sx, y: sy });
4509
- if (!interactive) {
4510
- dragStateRef.current = { kind: "pan" };
4511
- return;
4512
- }
4513
- const tool = toolIdRef.current;
4514
- const cam = cameraRef.current;
4515
- if (!cam) return;
4516
- const { worldX, worldY } = screenToWorld(sx, sy);
4517
- if (tool === "hand") {
4518
- dragStateRef.current = { kind: "pan" };
4519
- return;
4520
- }
4521
- if (tool === "select") {
4522
- const currentSelectedIds = selectedIdsRef.current;
4523
- const selectedItem = currentSelectedIds.length === 1 ? itemsRef.current.find((item) => item.id === currentSelectedIds[0]) : void 0;
4524
- const selectionHandle = hitTestNativeSelectionHandle({
4525
- selectedItem,
4526
- selectedCount: currentSelectedIds.length,
4527
- worldPoint: { x: worldX, y: worldY },
4528
- zoom: cam.zoom
4529
- });
4530
- if (selectionHandle && selectedItem) {
4531
- if (selectionHandle.kind === "rotate") {
4532
- const rotationStart = nativeRotationDragStart({
4533
- item: selectedItem,
4534
- worldPoint: { x: worldX, y: worldY }
4535
- });
4536
- dragStateRef.current = {
4537
- kind: "rotate",
4538
- id: selectedItem.id,
4539
- snapshot: selectedItem,
4540
- ...rotationStart
4541
- };
4542
- return;
4543
- }
4544
- dragStateRef.current = {
4545
- kind: "resize",
4546
- id: selectedItem.id,
4547
- handle: selectionHandle.handle,
4548
- snapshot: selectedItem,
4549
- start: {
4550
- bounds: selectedItem.bounds,
4551
- line: selectedItem.line
4552
- }
4553
- };
4554
- return;
4555
- }
4556
- const hit = hitTestWorldPoint(itemsRef.current, worldX, worldY, {
4557
- lineHitWorld: 10 / cam.zoom,
4558
- ignoreLocked: true
4559
- });
4560
- if (hit) {
4561
- const cur = selectedIdsRef.current;
4562
- const ids = cur.includes(hit.id) ? [...cur] : [hit.id];
4563
- const snapshots = {};
4564
- for (const id of ids) {
4565
- const it = itemsRef.current.find((i) => i.id === id);
4566
- if (it) snapshots[id] = it;
4567
- }
4568
- dragStateRef.current = {
4569
- kind: "move",
4570
- ids,
4571
- snapshots,
4572
- startWorld: { x: worldX, y: worldY }
4573
- };
4574
- if (!cur.includes(hit.id)) {
4575
- onSelectionChangeRef.current?.([hit.id]);
4576
- }
4577
- } else {
4578
- onSelectionChangeRef.current?.([]);
4579
- dragStateRef.current = {
4580
- kind: "marquee",
4581
- startWorld: { x: worldX, y: worldY }
4582
- };
4583
- setPlacementPreview({
4584
- kind: "marquee",
4585
- rect: { x: worldX, y: worldY, width: 0, height: 0 }
4586
- });
4587
- }
4588
- return;
4589
- }
4590
- if (tool === "draw" || tool === "marker" || tool === "laser") {
4591
- dragStateRef.current = {
4592
- kind: "draw",
4593
- tool,
4594
- points: [{ x: worldX, y: worldY }]
4595
- };
4596
- if (tool === "laser") {
4597
- if (laserClearTimerRef.current) {
4598
- clearTimeout(laserClearTimerRef.current);
4599
- laserClearTimerRef.current = null;
4600
- }
4601
- setLaserTrail([{ x: worldX, y: worldY, t: Date.now() }]);
4602
- } else {
4603
- setPlacementPreview({
4604
- kind: "stroke",
4605
- tool,
4606
- points: [{ x: worldX, y: worldY }],
4607
- style: { ...strokeStyleRef.current }
4608
- });
4609
- }
4610
- return;
4611
- }
4612
- if (tool === "eraser") {
4613
- dragStateRef.current = { kind: "erase" };
4614
- eraserPreviewIdSetRef.current = /* @__PURE__ */ new Set();
4615
- setEraserPreviewIds([]);
4616
- setEraserTrail([{ x: worldX, y: worldY, t: Date.now() }]);
4617
- const toErase = collectEraserTargetsAtWorldPoint(
4618
- itemsRef.current,
4619
- worldX,
4620
- worldY,
4621
- { lineHitWorld: 10 / cam.zoom, ignoreLocked: true }
4622
- );
4623
- for (const id of toErase) {
4624
- eraserPreviewIdSetRef.current.add(id);
4625
- }
4626
- setEraserPreviewIds(Array.from(eraserPreviewIdSetRef.current));
4627
- return;
4628
- }
4629
- if (isPlacementTool(tool)) {
4630
- dragStateRef.current = {
4631
- kind: "place",
4632
- tool,
4633
- startWorld: { x: worldX, y: worldY },
4634
- startScreen: { x: sx, y: sy }
4635
- };
4636
- setPlacementPreview(
4637
- placementPreviewForTool(
4638
- tool,
4639
- { x: worldX, y: worldY },
4640
- {
4641
- x: worldX,
4642
- y: worldY
4643
- }
4644
- )
4645
- );
4646
- return;
4647
- }
4648
- const customPlacement2 = resolveNativeCustomPlacement(
4649
- tool,
4650
- customPlacementRef.current,
4651
- customPlacementsRef.current
4652
- );
4653
- if (customPlacement2) {
4654
- dragStateRef.current = {
4655
- kind: "custom-place",
4656
- tool,
4657
- placement: customPlacement2,
4658
- startWorld: { x: worldX, y: worldY },
4659
- startScreen: { x: sx, y: sy }
4660
- };
4661
- setPlacementPreview({
4662
- kind: "rect",
4663
- rect: { x: worldX, y: worldY, width: 0, height: 0 }
4664
- });
4665
- return;
4666
- }
4667
- if (tool === "note" || tool === "text") {
4668
- dragStateRef.current = {
4669
- kind: "tap",
4670
- tool,
4671
- startWorld: { x: worldX, y: worldY },
4672
- startScreen: { x: sx, y: sy }
4673
- };
4674
- return;
4675
- }
4676
- const handleWorldPointerDown = onWorldPointerDownRef.current;
4677
- if (handleWorldPointerDown) {
4678
- handleWorldPointerDown({
4679
- toolId: tool,
4680
- worldX,
4681
- worldY,
4682
- screenX: sx,
4683
- screenY: sy
4684
- });
4685
- requestSelectToolAfterUse();
4686
- return;
4687
- }
4688
- dragStateRef.current = { kind: "pan" };
4652
+ beginDragAtScreenPoint({ x: sx, y: sy });
4689
4653
  },
4690
4654
  onPanResponderMove: (evt) => {
4691
4655
  const cam = cameraRef.current;
@@ -4719,223 +4683,10 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
4719
4683
  applyDragMoveAtScreenPoint({ x: sx, y: sy }, { x: pageX, y: pageY });
4720
4684
  },
4721
4685
  onPanResponderRelease: (evt) => {
4722
- lastPinchDist.current = null;
4723
- lastPanPoint.current = null;
4724
- updateToolCursorPoint({
4686
+ finishDragAtScreenPoint({
4725
4687
  x: evt.nativeEvent.locationX,
4726
4688
  y: evt.nativeEvent.locationY
4727
4689
  });
4728
- const st = dragStateRef.current;
4729
- if (st.kind === "draw") {
4730
- dragStateRef.current = { kind: "idle" };
4731
- setPlacementPreview(null);
4732
- if (st.tool === "laser") {
4733
- if (laserClearTimerRef.current) {
4734
- clearTimeout(laserClearTimerRef.current);
4735
- }
4736
- laserClearTimerRef.current = setTimeout(() => {
4737
- setLaserTrail([]);
4738
- laserClearTimerRef.current = null;
4739
- }, 650);
4740
- requestSelectToolAfterUse();
4741
- return;
4742
- }
4743
- if (st.points.length < 1) return;
4744
- const change = onItemsChangeRef.current;
4745
- if (!change) return;
4746
- const id = createShapeId();
4747
- const item = createFreehandStrokeItem(
4748
- id,
4749
- st.points,
4750
- st.tool,
4751
- strokeStyleRef.current
4752
- );
4753
- if (item) {
4754
- change([...itemsRef.current, item]);
4755
- }
4756
- requestSelectToolAfterUse();
4757
- return;
4758
- }
4759
- if (st.kind === "move") {
4760
- dragStateRef.current = { kind: "idle" };
4761
- return;
4762
- }
4763
- if (st.kind === "resize" || st.kind === "rotate") {
4764
- dragStateRef.current = { kind: "idle" };
4765
- return;
4766
- }
4767
- if (st.kind === "marquee") {
4768
- dragStateRef.current = { kind: "idle" };
4769
- setPlacementPreview(null);
4770
- const cam = cameraRef.current;
4771
- if (!cam) return;
4772
- const { worldX, worldY } = screenToWorld(
4773
- evt.nativeEvent.locationX,
4774
- evt.nativeEvent.locationY
4775
- );
4776
- const a = st.startWorld;
4777
- if (typeof worldX !== "number" || typeof worldY !== "number") return;
4778
- const raw = {
4779
- x: Math.min(a.x, worldX),
4780
- y: Math.min(a.y, worldY),
4781
- width: Math.abs(worldX - a.x),
4782
- height: Math.abs(worldY - a.y)
4783
- };
4784
- const picked = collectIdsInRect(itemsRef.current, raw);
4785
- onSelectionChangeRef.current?.(picked);
4786
- return;
4787
- }
4788
- if (st.kind === "erase") {
4789
- const change = onItemsChangeRef.current;
4790
- if (change && eraserPreviewIdSetRef.current.size > 0) {
4791
- const idSet = new Set(eraserPreviewIdSetRef.current);
4792
- change(itemsRef.current.filter((i) => !idSet.has(i.id)));
4793
- }
4794
- eraserPreviewIdSetRef.current.clear();
4795
- setEraserPreviewIds([]);
4796
- setEraserTrail([]);
4797
- dragStateRef.current = { kind: "idle" };
4798
- requestSelectToolAfterUse();
4799
- return;
4800
- }
4801
- if (st.kind === "place") {
4802
- dragStateRef.current = { kind: "idle" };
4803
- setPlacementPreview(null);
4804
- const change = onItemsChangeRef.current;
4805
- if (!change) return;
4806
- const { worldX, worldY } = screenToWorld(
4807
- evt.nativeEvent.locationX,
4808
- evt.nativeEvent.locationY
4809
- );
4810
- const a = st.startWorld;
4811
- const b = { x: worldX, y: worldY };
4812
- const screenDx = evt.nativeEvent.locationX - st.startScreen.x;
4813
- const screenDy = evt.nativeEvent.locationY - st.startScreen.y;
4814
- if (st.tool === "arrow" && Math.hypot(screenDx, screenDy) < MIN_ARROW_DRAG_PX) {
4815
- return;
4816
- }
4817
- let raw = rectFromCorners(a, b);
4818
- let br = normalizeRect(raw);
4819
- let lineStart = a;
4820
- let lineEnd = b;
4821
- if (br.width < MIN_PLACE_SIZE || br.height < MIN_PLACE_SIZE) {
4822
- const center = { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
4823
- const defaults = defaultPlacementWorld(st.tool, center);
4824
- raw = defaults.raw;
4825
- br = normalizeRect(raw);
4826
- if (defaults.lineWorld) {
4827
- const [defaultStart, defaultEnd] = defaults.lineWorld;
4828
- lineStart = defaultStart;
4829
- lineEnd = defaultEnd;
4830
- }
4831
- }
4832
- const id = createShapeId();
4833
- const style = strokeStyleRef.current;
4834
- if (st.tool === "rect") {
4835
- change([...itemsRef.current, createRectangleItem(id, raw, style)]);
4836
- onSelectionChangeRef.current?.([id]);
4837
- requestSelectToolAfterUse();
4838
- return;
4839
- }
4840
- if (st.tool === "ellipse") {
4841
- change([...itemsRef.current, createEllipseItem(id, raw, style)]);
4842
- onSelectionChangeRef.current?.([id]);
4843
- requestSelectToolAfterUse();
4844
- return;
4845
- }
4846
- if (st.tool === "architectural-cloud") {
4847
- change([
4848
- ...itemsRef.current,
4849
- createArchitecturalCloudItem(id, raw, style)
4850
- ]);
4851
- onSelectionChangeRef.current?.([id]);
4852
- requestSelectToolAfterUse();
4853
- return;
4854
- }
4855
- const line = lineEndpointsToLocal(br, lineStart, lineEnd);
4856
- change([
4857
- ...itemsRef.current,
4858
- createLineItem(id, br, line, st.tool, style)
4859
- ]);
4860
- onSelectionChangeRef.current?.([id]);
4861
- requestSelectToolAfterUse();
4862
- return;
4863
- }
4864
- if (st.kind === "custom-place") {
4865
- dragStateRef.current = { kind: "idle" };
4866
- setPlacementPreview(null);
4867
- const change = onItemsChangeRef.current;
4868
- if (!change) return;
4869
- const { worldX, worldY } = screenToWorld(
4870
- evt.nativeEvent.locationX,
4871
- evt.nativeEvent.locationY
4872
- );
4873
- const center = {
4874
- x: (st.startWorld.x + worldX) / 2,
4875
- y: (st.startWorld.y + worldY) / 2
4876
- };
4877
- const raw = rectFromCorners(st.startWorld, { x: worldX, y: worldY });
4878
- const normalized = normalizeRect(raw);
4879
- const bounds = normalized.width < MIN_PLACE_SIZE || normalized.height < MIN_PLACE_SIZE ? {
4880
- x: center.x - 60,
4881
- y: center.y - 40,
4882
- width: 120,
4883
- height: 80
4884
- } : normalized;
4885
- const id = createShapeId();
4886
- const item = st.placement.createItem({ id, bounds });
4887
- change([...itemsRef.current, item]);
4888
- onSelectionChangeRef.current?.([id]);
4889
- requestSelectToolAfterUse();
4890
- return;
4891
- }
4892
- if (st.kind === "tap") {
4893
- dragStateRef.current = { kind: "idle" };
4894
- const screenDx = evt.nativeEvent.locationX - st.startScreen.x;
4895
- const screenDy = evt.nativeEvent.locationY - st.startScreen.y;
4896
- if (Math.hypot(screenDx, screenDy) > TAP_PX) return;
4897
- const change = onItemsChangeRef.current;
4898
- if (!change) return;
4899
- if (st.tool === "text") {
4900
- const id = createShapeId();
4901
- const item = createTextItem(
4902
- id,
4903
- {
4904
- x: st.startWorld.x - 4,
4905
- y: st.startWorld.y - 18,
4906
- width: 160,
4907
- height: 26
4908
- },
4909
- "Text",
4910
- strokeStyleRef.current,
4911
- 18
4912
- );
4913
- change([...itemsRef.current, item]);
4914
- onSelectionChangeRef.current?.([id]);
4915
- requestSelectToolAfterUse();
4916
- }
4917
- if (st.tool === "note") {
4918
- const id = createShapeId();
4919
- const note = {
4920
- id,
4921
- x: st.startWorld.x - 70,
4922
- y: st.startWorld.y - 40,
4923
- bounds: {
4924
- x: st.startWorld.x - 70,
4925
- y: st.startWorld.y - 40,
4926
- width: 140,
4927
- height: 80
4928
- },
4929
- childrenSvg: `<rect width="140" height="80" rx="8" fill="#fef08a" stroke="#facc15" stroke-width="1" /><text x="10" y="22" fill="#1f2937" font-size="12" font-family="system-ui">Nota</text>`,
4930
- toolKind: "custom"
4931
- };
4932
- change([...itemsRef.current, note]);
4933
- onSelectionChangeRef.current?.([id]);
4934
- requestSelectToolAfterUse();
4935
- }
4936
- return;
4937
- }
4938
- dragStateRef.current = { kind: "idle" };
4939
4690
  },
4940
4691
  onPanResponderTerminate: () => {
4941
4692
  lastPinchDist.current = null;
@@ -4951,11 +4702,9 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
4951
4702
  }),
4952
4703
  [
4953
4704
  applyDragMoveAtScreenPoint,
4954
- screenToWorld,
4705
+ beginDragAtScreenPoint,
4706
+ finishDragAtScreenPoint,
4955
4707
  requestRender,
4956
- requestSelectToolAfterUse,
4957
- interactive,
4958
- updateToolCursorPoint,
4959
4708
  hideToolCursor
4960
4709
  ]
4961
4710
  );
@@ -4981,14 +4730,14 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
4981
4730
  [requestRender, size]
4982
4731
  );
4983
4732
  const activeStyleToolId = toolId === "draw" || toolId === "marker" ? toolId : null;
4984
- const activeToolCursor = cursorForToolId(toolId);
4985
- const toolCursor = activeToolCursor && toolCursorPoint ? { cursor: activeToolCursor, point: toolCursorPoint } : null;
4986
4733
  return /* @__PURE__ */ jsx(
4987
4734
  View,
4988
4735
  {
4989
4736
  style: { flex: 1, overflow: "hidden" },
4990
4737
  onLayout,
4738
+ onPointerDown: handlePointerDown,
4991
4739
  onPointerMove: handlePointerMove,
4740
+ onPointerUp: handlePointerUp,
4992
4741
  onPointerEnter: handlePointerMove,
4993
4742
  onPointerLeave: hideToolCursor,
4994
4743
  ...panResponder.panHandlers,
@@ -5016,8 +4765,7 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
5016
4765
  eraserPreviewItems: items.filter(
5017
4766
  (it) => eraserPreviewIds.includes(it.id)
5018
4767
  ),
5019
- previewStrokeStyle: strokeStyleState,
5020
- toolCursor
4768
+ previewStrokeStyle: strokeStyleState
5021
4769
  }
5022
4770
  ),
5023
4771
  interactive && showStyleInspector && activeStyleToolId ? /* @__PURE__ */ jsx(