canvu-react 0.3.35 → 0.3.37

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/react.cjs CHANGED
@@ -6605,7 +6605,7 @@ function InteractionOverlay({
6605
6605
  raw,
6606
6606
  previewStyle,
6607
6607
  tool === "laser" ? "draw" : tool,
6608
- false
6608
+ raw.length === 2
6609
6609
  );
6610
6610
  if (payload) {
6611
6611
  if (payload.kind === "circle") {
@@ -6898,7 +6898,7 @@ function PresenceRemoteLayer({
6898
6898
  markup.points.map((p) => ({ x: p.x, y: p.y })),
6899
6899
  style,
6900
6900
  markup.tool,
6901
- false
6901
+ markup.points.length === 2
6902
6902
  );
6903
6903
  if (payload?.kind === "circle") {
6904
6904
  strokeNode = /* @__PURE__ */ jsxRuntime.jsx(
@@ -7287,6 +7287,8 @@ body[data-canvu-pen-active="true"] [data-slot="shape-context-menu"] * {
7287
7287
  -webkit-touch-callout: none !important;
7288
7288
  }
7289
7289
  `;
7290
+ var STRAIGHT_STROKE_HOLD_DURATION_MS = 1e3;
7291
+ var STRAIGHT_STROKE_JITTER_TOLERANCE_SQUARED = 5 * 5;
7290
7292
  function debugApplePencilPointer(phase, detail) {
7291
7293
  return;
7292
7294
  }
@@ -7434,6 +7436,28 @@ function appendTouchToStrokePoints(points, touch, zoom, screenToWorldFn) {
7434
7436
  );
7435
7437
  return appendInterpolatedPoints(points, nextPoint);
7436
7438
  }
7439
+ function createStraightStrokeState(anchorPoint, clientX, clientY) {
7440
+ return {
7441
+ active: false,
7442
+ anchorPoint,
7443
+ holdScreen: { x: clientX, y: clientY },
7444
+ holdTimer: null
7445
+ };
7446
+ }
7447
+ function clearStraightStrokeHoldTimer(strokeState) {
7448
+ const straightLine = strokeState.straightLine;
7449
+ if (!straightLine?.holdTimer) return;
7450
+ clearTimeout(straightLine.holdTimer);
7451
+ straightLine.holdTimer = null;
7452
+ }
7453
+ function lastStrokePoint(points) {
7454
+ return points[points.length - 1] ?? null;
7455
+ }
7456
+ function hasMovedPastStraightStrokeJitter(straightLine, clientX, clientY) {
7457
+ const deltaX = clientX - straightLine.holdScreen.x;
7458
+ const deltaY = clientY - straightLine.holdScreen.y;
7459
+ return deltaX * deltaX + deltaY * deltaY > STRAIGHT_STROKE_JITTER_TOLERANCE_SQUARED;
7460
+ }
7437
7461
  var VectorViewport = react.forwardRef(
7438
7462
  function VectorViewport2({
7439
7463
  items,
@@ -7968,6 +7992,76 @@ var VectorViewport = react.forwardRef(
7968
7992
  },
7969
7993
  []
7970
7994
  );
7995
+ const updateStrokePreviewPoints = react.useCallback(
7996
+ (strokeState, points) => {
7997
+ strokeState.points = points;
7998
+ if (strokeState.itemId && strokeState.tool !== "laser") {
7999
+ const item = createFreehandStrokeItem(
8000
+ strokeState.itemId,
8001
+ points,
8002
+ strokeState.tool,
8003
+ strokeStyleRef.current
8004
+ );
8005
+ if (item) {
8006
+ renderSceneWithLivePenStroke(item);
8007
+ }
8008
+ setPlacementPreview(null);
8009
+ emitRemoteStrokePreview(strokeState.tool, points);
8010
+ return;
8011
+ }
8012
+ if (strokeState.tool === "laser") return;
8013
+ setPlacementPreview({
8014
+ kind: "stroke",
8015
+ tool: strokeState.tool,
8016
+ points,
8017
+ style: { ...strokeStyleRef.current }
8018
+ });
8019
+ },
8020
+ [emitRemoteStrokePreview, renderSceneWithLivePenStroke]
8021
+ );
8022
+ const setStraightStrokeEndpoint = react.useCallback(
8023
+ (strokeState, endpoint) => {
8024
+ const straightLine = strokeState.straightLine;
8025
+ if (!straightLine) return;
8026
+ straightLine.active = true;
8027
+ updateStrokePreviewPoints(strokeState, [straightLine.anchorPoint, endpoint]);
8028
+ },
8029
+ [updateStrokePreviewPoints]
8030
+ );
8031
+ const startOrRestartStraightStrokeHoldTimer = react.useCallback(
8032
+ (strokeState) => {
8033
+ const straightLine = strokeState.straightLine;
8034
+ if (!straightLine || strokeState.tool !== "draw") return;
8035
+ clearStraightStrokeHoldTimer(strokeState);
8036
+ straightLine.holdTimer = window.setTimeout(() => {
8037
+ straightLine.holdTimer = null;
8038
+ const currentStrokeState = dragStateRef.current;
8039
+ if (currentStrokeState.kind !== "stroke" || currentStrokeState !== strokeState) {
8040
+ return;
8041
+ }
8042
+ const endpoint = lastStrokePoint(currentStrokeState.points);
8043
+ if (!endpoint) return;
8044
+ setStraightStrokeEndpoint(currentStrokeState, endpoint);
8045
+ }, STRAIGHT_STROKE_HOLD_DURATION_MS);
8046
+ },
8047
+ [setStraightStrokeEndpoint]
8048
+ );
8049
+ const updateStraightStrokeForMove = react.useCallback(
8050
+ (strokeState, clientX, clientY, endpoint) => {
8051
+ const straightLine = strokeState.straightLine;
8052
+ if (!straightLine || strokeState.tool !== "draw") return false;
8053
+ if (straightLine.active) {
8054
+ setStraightStrokeEndpoint(strokeState, endpoint);
8055
+ return true;
8056
+ }
8057
+ if (straightLine.holdTimer && hasMovedPastStraightStrokeJitter(straightLine, clientX, clientY)) {
8058
+ straightLine.holdScreen = { x: clientX, y: clientY };
8059
+ startOrRestartStraightStrokeHoldTimer(strokeState);
8060
+ }
8061
+ return false;
8062
+ },
8063
+ [setStraightStrokeEndpoint, startOrRestartStraightStrokeHoldTimer]
8064
+ );
7971
8065
  const finalizeStrokeDragState = react.useCallback(
7972
8066
  (strokeState) => {
7973
8067
  const tool = strokeState.tool;
@@ -7978,6 +8072,7 @@ var VectorViewport = react.forwardRef(
7978
8072
  debugApplePencilPointer("finalize-stroke", {
7979
8073
  points: pts.length
7980
8074
  });
8075
+ clearStraightStrokeHoldTimer(strokeState);
7981
8076
  dragStateRef.current = { kind: "idle" };
7982
8077
  releaseInteractionPointer();
7983
8078
  if (itemId) {
@@ -9089,6 +9184,7 @@ var VectorViewport = react.forwardRef(
9089
9184
  e.clientY,
9090
9185
  e.pointerType === "pen" ? e.pressure : void 0
9091
9186
  );
9187
+ const straightLine = tool === "draw" ? createStraightStrokeState(startPoint, e.clientX, e.clientY) : void 0;
9092
9188
  const directPenStroke = e.pointerType === "pen" && (tool === "draw" || tool === "marker");
9093
9189
  let itemId;
9094
9190
  if (directPenStroke) {
@@ -9103,13 +9199,16 @@ var VectorViewport = react.forwardRef(
9103
9199
  renderSceneWithLivePenStroke(item);
9104
9200
  }
9105
9201
  }
9106
- dragStateRef.current = {
9202
+ const nextDragState = {
9107
9203
  kind: "stroke",
9108
9204
  tool,
9109
9205
  points: [startPoint],
9110
9206
  pointerType: e.pointerType,
9111
- ...itemId ? { itemId } : {}
9207
+ ...itemId ? { itemId } : {},
9208
+ ...straightLine ? { straightLine } : {}
9112
9209
  };
9210
+ dragStateRef.current = nextDragState;
9211
+ startOrRestartStraightStrokeHoldTimer(nextDragState);
9113
9212
  if (tool === "laser") {
9114
9213
  setLaserTrail([{ x: worldX, y: worldY, t: Date.now() }]);
9115
9214
  setPlacementPreview(null);
@@ -9161,7 +9260,8 @@ var VectorViewport = react.forwardRef(
9161
9260
  emitRemoteStrokePreview,
9162
9261
  finalizeStrokeDragState,
9163
9262
  renderSceneWithLivePenStroke,
9164
- screenToWorld
9263
+ screenToWorld,
9264
+ startOrRestartStraightStrokeHoldTimer
9165
9265
  ]
9166
9266
  );
9167
9267
  react.useEffect(() => {
@@ -9197,6 +9297,7 @@ var VectorViewport = react.forwardRef(
9197
9297
  e.clientY,
9198
9298
  e.pressure
9199
9299
  );
9300
+ const straightLine = tool === "draw" ? createStraightStrokeState(startPoint, e.clientX, e.clientY) : void 0;
9200
9301
  const itemId = createShapeId();
9201
9302
  const item = createFreehandStrokeItem(
9202
9303
  itemId,
@@ -9207,13 +9308,16 @@ var VectorViewport = react.forwardRef(
9207
9308
  if (item) {
9208
9309
  renderSceneWithLivePenStroke(item);
9209
9310
  }
9210
- dragStateRef.current = {
9311
+ const nextDragState = {
9211
9312
  kind: "stroke",
9212
9313
  tool,
9213
9314
  points: [startPoint],
9214
9315
  pointerType: e.pointerType,
9215
- itemId
9316
+ itemId,
9317
+ ...straightLine ? { straightLine } : {}
9216
9318
  };
9319
+ dragStateRef.current = nextDragState;
9320
+ startOrRestartStraightStrokeHoldTimer(nextDragState);
9217
9321
  activeInteractionPointerIdRef.current = e.pointerId;
9218
9322
  activeInteractionPointerTargetRef.current = null;
9219
9323
  setPlacementPreview(null);
@@ -9239,7 +9343,8 @@ var VectorViewport = react.forwardRef(
9239
9343
  finalizeStrokeDragState,
9240
9344
  interactive,
9241
9345
  renderSceneWithLivePenStroke,
9242
- screenToWorld
9346
+ screenToWorld,
9347
+ startOrRestartStraightStrokeHoldTimer
9243
9348
  ]);
9244
9349
  react.useEffect(() => {
9245
9350
  if (!interactive) return;
@@ -9298,6 +9403,7 @@ var VectorViewport = react.forwardRef(
9298
9403
  touch.clientY,
9299
9404
  touchPressure(touch)
9300
9405
  );
9406
+ const straightLine = tool === "draw" ? createStraightStrokeState(startPoint, touch.clientX, touch.clientY) : void 0;
9301
9407
  const itemId = createShapeId();
9302
9408
  const item = createFreehandStrokeItem(
9303
9409
  itemId,
@@ -9308,13 +9414,16 @@ var VectorViewport = react.forwardRef(
9308
9414
  if (item) {
9309
9415
  renderSceneWithLivePenStroke(item);
9310
9416
  }
9311
- dragStateRef.current = {
9417
+ const nextDragState = {
9312
9418
  kind: "stroke",
9313
9419
  tool,
9314
9420
  points: [startPoint],
9315
9421
  pointerType: "pen",
9316
- itemId
9422
+ itemId,
9423
+ ...straightLine ? { straightLine } : {}
9317
9424
  };
9425
+ dragStateRef.current = nextDragState;
9426
+ startOrRestartStraightStrokeHoldTimer(nextDragState);
9318
9427
  activeInteractionPointerIdRef.current = null;
9319
9428
  activeInteractionPointerTargetRef.current = null;
9320
9429
  activeInteractionTouchIdRef.current = touch.identifier;
@@ -9333,6 +9442,21 @@ var VectorViewport = react.forwardRef(
9333
9442
  if (!touch) return;
9334
9443
  const cam = cameraRef.current;
9335
9444
  if (!cam) return;
9445
+ const endpoint = pointerSampleToWorldPoint(
9446
+ screenToWorld,
9447
+ touch.clientX,
9448
+ touch.clientY,
9449
+ touchPressure(touch)
9450
+ );
9451
+ if (updateStraightStrokeForMove(st, touch.clientX, touch.clientY, endpoint)) {
9452
+ debugApplePencilPointer("touchmove-stroke", {
9453
+ touchId: touch.identifier,
9454
+ points: st.points.length,
9455
+ force: touchPressure(touch)
9456
+ });
9457
+ stopTouchEvent(ev);
9458
+ return;
9459
+ }
9336
9460
  const interpolated = appendTouchToStrokePoints(
9337
9461
  st.points,
9338
9462
  touch,
@@ -9367,13 +9491,23 @@ var VectorViewport = react.forwardRef(
9367
9491
  if (!touch) return;
9368
9492
  const cam = cameraRef.current;
9369
9493
  if (cam) {
9370
- const completedPoints = appendTouchToStrokePoints(
9371
- st.points,
9372
- touch,
9373
- cam.zoom,
9374
- screenToWorld
9375
- );
9376
- st.points = completedPoints;
9494
+ if (st.straightLine?.active) {
9495
+ const endpoint = pointerSampleToWorldPoint(
9496
+ screenToWorld,
9497
+ touch.clientX,
9498
+ touch.clientY,
9499
+ touchPressure(touch)
9500
+ );
9501
+ setStraightStrokeEndpoint(st, endpoint);
9502
+ } else {
9503
+ const completedPoints = appendTouchToStrokePoints(
9504
+ st.points,
9505
+ touch,
9506
+ cam.zoom,
9507
+ screenToWorld
9508
+ );
9509
+ st.points = completedPoints;
9510
+ }
9377
9511
  }
9378
9512
  debugApplePencilPointer("touchend-stroke", {
9379
9513
  touchId: touch.identifier,
@@ -9418,7 +9552,10 @@ var VectorViewport = react.forwardRef(
9418
9552
  finalizeStrokeDragState,
9419
9553
  interactive,
9420
9554
  renderSceneWithLivePenStroke,
9421
- screenToWorld
9555
+ screenToWorld,
9556
+ setStraightStrokeEndpoint,
9557
+ startOrRestartStraightStrokeHoldTimer,
9558
+ updateStraightStrokeForMove
9422
9559
  ]);
9423
9560
  react.useEffect(() => {
9424
9561
  if (!interactive) return;
@@ -9451,6 +9588,15 @@ var VectorViewport = react.forwardRef(
9451
9588
  pressure: ev.pointerType === "pen" ? ev.pressure : void 0
9452
9589
  });
9453
9590
  }
9591
+ const endpoint = pointerSampleToWorldPoint(
9592
+ screenToWorld,
9593
+ ev.clientX,
9594
+ ev.clientY,
9595
+ ev.pointerType === "pen" ? ev.pressure : void 0
9596
+ );
9597
+ if (updateStraightStrokeForMove(st, ev.clientX, ev.clientY, endpoint)) {
9598
+ return;
9599
+ }
9454
9600
  const interpolated = appendPointerEventSamplesToStrokePoints(
9455
9601
  st.points,
9456
9602
  ev,
@@ -9618,6 +9764,7 @@ var VectorViewport = react.forwardRef(
9618
9764
  const cam = cameraRef.current;
9619
9765
  if (!cam) {
9620
9766
  if (st.kind === "stroke") {
9767
+ clearStraightStrokeHoldTimer(st);
9621
9768
  emitRemoteStrokePreviewClear();
9622
9769
  }
9623
9770
  if (st.kind === "erase") {
@@ -9673,13 +9820,26 @@ var VectorViewport = react.forwardRef(
9673
9820
  return;
9674
9821
  }
9675
9822
  if (st.kind === "stroke") {
9676
- const completedPoints = appendPointerEventSamplesToStrokePoints(
9677
- st.points,
9678
- ev,
9679
- cam.zoom,
9680
- screenToWorld
9681
- );
9682
- st.points = completedPoints;
9823
+ const completedPoints = (() => {
9824
+ if (st.straightLine?.active) {
9825
+ const endpoint = pointerSampleToWorldPoint(
9826
+ screenToWorld,
9827
+ ev.clientX,
9828
+ ev.clientY,
9829
+ ev.pointerType === "pen" ? ev.pressure : void 0
9830
+ );
9831
+ setStraightStrokeEndpoint(st, endpoint);
9832
+ return st.points;
9833
+ }
9834
+ const points = appendPointerEventSamplesToStrokePoints(
9835
+ st.points,
9836
+ ev,
9837
+ cam.zoom,
9838
+ screenToWorld
9839
+ );
9840
+ st.points = points;
9841
+ return points;
9842
+ })();
9683
9843
  debugApplePencilPointer("pointerup-stroke-points", {
9684
9844
  pointerType: ev.pointerType,
9685
9845
  pointerId: ev.pointerId,
@@ -9866,7 +10026,9 @@ var VectorViewport = react.forwardRef(
9866
10026
  renderSceneWithLivePenStroke,
9867
10027
  releaseInteractionPointer,
9868
10028
  requestAutoResetTool,
9869
- screenToWorld
10029
+ screenToWorld,
10030
+ setStraightStrokeEndpoint,
10031
+ updateStraightStrokeForMove
9870
10032
  ]);
9871
10033
  const selectedItemsForOverlay = react.useMemo(() => {
9872
10034
  return effectiveSelectedIds.map((id) => resolvedItems.find((i) => i.id === id)).filter((i) => i != null);