@tutti-os/workbench-surface 0.0.48 → 0.0.50

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/index.d.ts CHANGED
@@ -451,6 +451,10 @@ interface WorkbenchHostLaunchResult {
451
451
  payload?: unknown;
452
452
  type: string;
453
453
  } | null;
454
+ cascadeOffset?: {
455
+ x: number;
456
+ y: number;
457
+ };
454
458
  defaultFrame?: WorkbenchFrame;
455
459
  displayMode?: WorkbenchDisplayMode;
456
460
  dockEntryId?: string;
package/dist/index.js CHANGED
@@ -5270,7 +5270,14 @@ function createWorkbenchSnapshotFromState(state, options = {}) {
5270
5270
 
5271
5271
  // src/core/placement.ts
5272
5272
  var WORKBENCH_WINDOW_CASCADE_OFFSET = 28;
5273
+ var WORKBENCH_WINDOW_CASCADE_FALLBACK_LIMIT = 12;
5274
+ var WORKBENCH_WINDOW_CASCADE_POSITION_TOLERANCE = 4;
5273
5275
  function resolveWorkbenchCascadedRect(input) {
5276
+ const hasCustomCascadeOffset = input.cascadeOffset !== void 0;
5277
+ const cascadeOffset = input.cascadeOffset ?? {
5278
+ x: WORKBENCH_WINDOW_CASCADE_OFFSET,
5279
+ y: WORKBENCH_WINDOW_CASCADE_OFFSET
5280
+ };
5274
5281
  const activeNode = input.existingNodes.find(
5275
5282
  (node) => node.id === input.currentNodeStack.at(-1)
5276
5283
  ) ?? input.existingNodes.at(-1);
@@ -5282,17 +5289,119 @@ function resolveWorkbenchCascadedRect(input) {
5282
5289
  input.sizeConstraints
5283
5290
  );
5284
5291
  }
5292
+ const preferredCandidate = createWorkbenchCascadeCandidate({
5293
+ input,
5294
+ x: activeNode.frame.x + cascadeOffset.x,
5295
+ y: activeNode.frame.y + cascadeOffset.y
5296
+ });
5297
+ if ((!hasCustomCascadeOffset || input.existingNodes.length <= 1) && !workbenchCascadeCandidateMatchesExisting(preferredCandidate, input)) {
5298
+ return preferredCandidate;
5299
+ }
5300
+ const candidateOrigins = [
5301
+ {
5302
+ x: activeNode.frame.x + cascadeOffset.x,
5303
+ y: activeNode.frame.y + cascadeOffset.y
5304
+ },
5305
+ {
5306
+ x: activeNode.frame.x - cascadeOffset.x,
5307
+ y: activeNode.frame.y + cascadeOffset.y
5308
+ },
5309
+ {
5310
+ x: activeNode.frame.x + cascadeOffset.x,
5311
+ y: activeNode.frame.y - cascadeOffset.y
5312
+ },
5313
+ {
5314
+ x: activeNode.frame.x - cascadeOffset.x,
5315
+ y: activeNode.frame.y - cascadeOffset.y
5316
+ }
5317
+ ];
5318
+ for (let index = 1; index <= WORKBENCH_WINDOW_CASCADE_FALLBACK_LIMIT; index += 1) {
5319
+ candidateOrigins.push(
5320
+ {
5321
+ x: input.preferredFrame.x + cascadeOffset.x * index,
5322
+ y: input.preferredFrame.y + cascadeOffset.y * index
5323
+ },
5324
+ {
5325
+ x: input.preferredFrame.x - cascadeOffset.x * index,
5326
+ y: input.preferredFrame.y + cascadeOffset.y * index
5327
+ },
5328
+ {
5329
+ x: input.preferredFrame.x + cascadeOffset.x * index,
5330
+ y: input.preferredFrame.y - cascadeOffset.y * index
5331
+ },
5332
+ {
5333
+ x: input.preferredFrame.x - cascadeOffset.x * index,
5334
+ y: input.preferredFrame.y - cascadeOffset.y * index
5335
+ }
5336
+ );
5337
+ }
5338
+ return resolveBestWorkbenchCascadeCandidate({
5339
+ candidateOrigins,
5340
+ fallbackFrame: preferredCandidate,
5341
+ input
5342
+ });
5343
+ }
5344
+ function createWorkbenchCascadeCandidate(input) {
5285
5345
  return clampWorkbenchRect(
5286
5346
  {
5287
- x: activeNode.frame.x + WORKBENCH_WINDOW_CASCADE_OFFSET,
5288
- y: activeNode.frame.y + WORKBENCH_WINDOW_CASCADE_OFFSET,
5289
- width: input.preferredFrame.width,
5290
- height: input.preferredFrame.height
5347
+ x: input.x,
5348
+ y: input.y,
5349
+ width: input.input.preferredFrame.width,
5350
+ height: input.input.preferredFrame.height
5291
5351
  },
5292
- input.surfaceSize,
5293
- input.constraints,
5294
- input.sizeConstraints
5352
+ input.input.surfaceSize,
5353
+ input.input.constraints,
5354
+ input.input.sizeConstraints
5355
+ );
5356
+ }
5357
+ function workbenchCascadeCandidateMatchesExisting(candidate, input) {
5358
+ return input.existingNodes.some((node) => {
5359
+ return Math.abs(node.frame.x - candidate.x) <= WORKBENCH_WINDOW_CASCADE_POSITION_TOLERANCE && Math.abs(node.frame.y - candidate.y) <= WORKBENCH_WINDOW_CASCADE_POSITION_TOLERANCE;
5360
+ });
5361
+ }
5362
+ function resolveBestWorkbenchCascadeCandidate(input) {
5363
+ let bestFrame = null;
5364
+ let bestScore = Number.POSITIVE_INFINITY;
5365
+ const seen = /* @__PURE__ */ new Set();
5366
+ for (const candidate of input.candidateOrigins) {
5367
+ const frame = createWorkbenchCascadeCandidate({
5368
+ input: input.input,
5369
+ x: candidate.x,
5370
+ y: candidate.y
5371
+ });
5372
+ const key = `${frame.x}:${frame.y}:${frame.width}:${frame.height}`;
5373
+ if (seen.has(key)) {
5374
+ continue;
5375
+ }
5376
+ seen.add(key);
5377
+ const score = scoreWorkbenchCascadeCandidate(frame, input.input);
5378
+ if (score < bestScore) {
5379
+ bestFrame = frame;
5380
+ bestScore = score;
5381
+ }
5382
+ }
5383
+ return bestFrame ?? input.fallbackFrame;
5384
+ }
5385
+ function scoreWorkbenchCascadeCandidate(candidate, input) {
5386
+ const overlapArea = input.existingNodes.reduce((total, node) => {
5387
+ return total + workbenchFrameIntersectionArea(candidate, node.frame);
5388
+ }, 0);
5389
+ const positionCollisionPenalty = workbenchCascadeCandidateMatchesExisting(
5390
+ candidate,
5391
+ input
5392
+ ) ? candidate.width * candidate.height : 0;
5393
+ return overlapArea + positionCollisionPenalty;
5394
+ }
5395
+ function workbenchFrameIntersectionArea(a, b) {
5396
+ const width = Math.max(
5397
+ 0,
5398
+ Math.min(a.x + a.width, b.x + b.width) - Math.max(a.x, b.x)
5399
+ );
5400
+ const height = Math.max(
5401
+ 0,
5402
+ Math.min(a.y + a.height, b.y + b.height) - Math.max(a.y, b.y)
5295
5403
  );
5404
+ return width * height;
5296
5405
  }
5297
5406
 
5298
5407
  // src/host/nodeIdentity.ts
@@ -5405,6 +5514,7 @@ function resolveWorkbenchLaunchedHostNodeFrame(input) {
5405
5514
  existingSameTypeNodes.map((node) => node.id)
5406
5515
  );
5407
5516
  return resolveWorkbenchCascadedRect({
5517
+ cascadeOffset: input.result.cascadeOffset,
5408
5518
  currentNodeStack: input.currentState.nodeStack.filter(
5409
5519
  (nodeId) => existingSameTypeNodeIds.has(nodeId)
5410
5520
  ),
@@ -5421,6 +5531,7 @@ function resolveWorkbenchLaunchedHostNodeFrame(input) {
5421
5531
  });
5422
5532
  }
5423
5533
  return resolveWorkbenchCascadedRect({
5534
+ cascadeOffset: input.result.cascadeOffset,
5424
5535
  currentNodeStack: input.currentState.nodeStack,
5425
5536
  existingNodes: input.currentState.nodes,
5426
5537
  preferredFrame: responsivePreferredFrame,
@@ -6712,7 +6823,7 @@ var WorkbenchHostSessionController = class {
6712
6823
  }
6713
6824
  applyClosedDockWindowFrame(result) {
6714
6825
  const dockEntryId = result.dockEntryId?.trim();
6715
- if (!dockEntryId) {
6826
+ if (!dockEntryId || result.reuseDockEntryNode === false) {
6716
6827
  return result;
6717
6828
  }
6718
6829
  const entry = this.closedDockWindowFrameEntries.get(
@@ -7178,6 +7289,94 @@ function isWorkbenchDockEntryBlocked(entry) {
7178
7289
 
7179
7290
  // src/host/dockMagnification.ts
7180
7291
  import { useCallback as useCallback8, useEffect as useEffect6, useRef as useRef5 } from "react";
7292
+
7293
+ // src/host/dockMagnificationBounds.ts
7294
+ function resolveDockMagnificationViewportBounds(viewportRect, dockPlacement) {
7295
+ return dockPlacement === "left" ? {
7296
+ crossEnd: viewportRect.right,
7297
+ crossStart: viewportRect.left,
7298
+ mainEnd: viewportRect.bottom,
7299
+ mainStart: viewportRect.top
7300
+ } : {
7301
+ crossEnd: viewportRect.bottom,
7302
+ crossStart: viewportRect.top,
7303
+ mainEnd: viewportRect.right,
7304
+ mainStart: viewportRect.left
7305
+ };
7306
+ }
7307
+ function resolveDockMagnificationVisibleHitBounds({
7308
+ dockPlacement,
7309
+ hitBounds,
7310
+ mainAxisEdgePadding = 0,
7311
+ viewportRect
7312
+ }) {
7313
+ if (!hitBounds || !viewportRect) {
7314
+ return hitBounds;
7315
+ }
7316
+ const viewportBounds = resolveDockMagnificationViewportBounds(
7317
+ viewportRect,
7318
+ dockPlacement
7319
+ );
7320
+ const visibleBounds = {
7321
+ crossEnd: Math.min(hitBounds.crossEnd, viewportBounds.crossEnd),
7322
+ crossStart: Math.max(hitBounds.crossStart, viewportBounds.crossStart),
7323
+ mainEnd: Math.min(
7324
+ hitBounds.mainEnd + mainAxisEdgePadding,
7325
+ viewportBounds.mainEnd + mainAxisEdgePadding
7326
+ ),
7327
+ mainStart: Math.max(
7328
+ hitBounds.mainStart - mainAxisEdgePadding,
7329
+ viewportBounds.mainStart - mainAxisEdgePadding
7330
+ )
7331
+ };
7332
+ if (visibleBounds.mainStart > visibleBounds.mainEnd || visibleBounds.crossStart > visibleBounds.crossEnd) {
7333
+ return null;
7334
+ }
7335
+ return visibleBounds;
7336
+ }
7337
+ function resolveDockMagnificationVisibleSlotRects({
7338
+ slotRects,
7339
+ viewportRect
7340
+ }) {
7341
+ if (!viewportRect) {
7342
+ return [...slotRects];
7343
+ }
7344
+ const visibleSlotRects = [];
7345
+ for (const rect of slotRects) {
7346
+ const visibleRect = {
7347
+ bottom: Math.min(rect.bottom, viewportRect.bottom),
7348
+ left: Math.max(rect.left, viewportRect.left),
7349
+ right: Math.min(rect.right, viewportRect.right),
7350
+ top: Math.max(rect.top, viewportRect.top)
7351
+ };
7352
+ if (visibleRect.left <= visibleRect.right && visibleRect.top <= visibleRect.bottom) {
7353
+ visibleSlotRects.push(visibleRect);
7354
+ }
7355
+ }
7356
+ return visibleSlotRects;
7357
+ }
7358
+ function isDockMagnificationPointInsideHitBounds({
7359
+ clientX,
7360
+ clientY,
7361
+ dockPlacement,
7362
+ hitBounds
7363
+ }) {
7364
+ if (!hitBounds) {
7365
+ return false;
7366
+ }
7367
+ const mainAxis = dockPlacement === "left" ? clientY : clientX;
7368
+ const crossAxis = dockPlacement === "left" ? clientX : clientY;
7369
+ return mainAxis >= hitBounds.mainStart && mainAxis <= hitBounds.mainEnd && crossAxis >= hitBounds.crossStart && crossAxis <= hitBounds.crossEnd;
7370
+ }
7371
+ function isDockMagnificationPointInsideSlotRect({
7372
+ clientX,
7373
+ clientY,
7374
+ rect
7375
+ }) {
7376
+ return clientX >= rect.left && clientX <= rect.right && clientY >= rect.top && clientY <= rect.bottom;
7377
+ }
7378
+
7379
+ // src/host/dockMagnification.ts
7181
7380
  var DOCK_ICON_BASE_SIZE = 43.2;
7182
7381
  var DOCK_ICON_PEAK_SIZE = DOCK_ICON_BASE_SIZE * 1.7;
7183
7382
  var DOCK_MAGNIFICATION_HALF_RANGE = DOCK_ICON_BASE_SIZE * 2.4;
@@ -7190,7 +7389,14 @@ var MAX_MAGNIFICATION_STEP_SECONDS = 1 / 30;
7190
7389
  var MAGNIFICATION_INFLUENCE_PADDING = 8;
7191
7390
  var DOCK_MAGNIFICATION_ENTRY_RAMP_MS = 90;
7192
7391
  var DOCK_MAGNIFICATION_CROSS_AXIS_PADDING = 8;
7392
+ var DOCK_MAGNIFICATION_MAIN_AXIS_EDGE_PADDING = DOCK_ICON_BASE_SIZE / 2;
7393
+ var DOCK_MAGNIFICATION_AMBIENT_EDGE_RANGE = 180;
7394
+ var DOCK_MAGNIFICATION_AMBIENT_VIEWPORT_PADDING = 8;
7193
7395
  var dockMagnificationShellBySlot = /* @__PURE__ */ new WeakMap();
7396
+ var globalPointerListenerOptions = {
7397
+ capture: true,
7398
+ passive: true
7399
+ };
7194
7400
  function mapDistanceToTargetSize(distance, baseSize = DOCK_ICON_BASE_SIZE, peakSize = DOCK_ICON_PEAK_SIZE, halfRange = DOCK_MAGNIFICATION_HALF_RANGE) {
7195
7401
  const absoluteDistance = Math.abs(distance);
7196
7402
  if (absoluteDistance >= halfRange) {
@@ -7232,18 +7438,60 @@ function resolveDockMagnificationHitBounds(slotRects, dockPlacement, crossAxisPa
7232
7438
  mainStart
7233
7439
  };
7234
7440
  }
7235
- function isDockMagnificationPointInsideHitBounds({
7236
- clientX,
7237
- clientY,
7238
- dockPlacement,
7239
- hitBounds
7441
+ function createDockMagnificationGlobalPointerTracker({
7442
+ blurTarget,
7443
+ onPointerCancel,
7444
+ onPointerMove,
7445
+ pointerTarget
7240
7446
  }) {
7241
- if (!hitBounds) {
7242
- return false;
7243
- }
7244
- const mainAxis = dockPlacement === "left" ? clientY : clientX;
7245
- const crossAxis = dockPlacement === "left" ? clientX : clientY;
7246
- return mainAxis >= hitBounds.mainStart && mainAxis <= hitBounds.mainEnd && crossAxis >= hitBounds.crossStart && crossAxis <= hitBounds.crossEnd;
7447
+ let active = false;
7448
+ const handlePointerMove = (event) => {
7449
+ const pointerEvent = event;
7450
+ onPointerMove(pointerEvent.clientX, pointerEvent.clientY);
7451
+ };
7452
+ const handlePointerCancel = () => {
7453
+ stop();
7454
+ onPointerCancel();
7455
+ };
7456
+ const start = () => {
7457
+ if (active) {
7458
+ return;
7459
+ }
7460
+ active = true;
7461
+ pointerTarget.addEventListener(
7462
+ "pointermove",
7463
+ handlePointerMove,
7464
+ globalPointerListenerOptions
7465
+ );
7466
+ pointerTarget.addEventListener(
7467
+ "pointercancel",
7468
+ handlePointerCancel,
7469
+ globalPointerListenerOptions
7470
+ );
7471
+ blurTarget?.addEventListener("blur", handlePointerCancel);
7472
+ };
7473
+ const stop = () => {
7474
+ if (!active) {
7475
+ return;
7476
+ }
7477
+ active = false;
7478
+ pointerTarget.removeEventListener(
7479
+ "pointermove",
7480
+ handlePointerMove,
7481
+ globalPointerListenerOptions
7482
+ );
7483
+ pointerTarget.removeEventListener(
7484
+ "pointercancel",
7485
+ handlePointerCancel,
7486
+ globalPointerListenerOptions
7487
+ );
7488
+ blurTarget?.removeEventListener("blur", handlePointerCancel);
7489
+ };
7490
+ return {
7491
+ isActive: () => active,
7492
+ start,
7493
+ stop
7494
+ };
7247
7495
  }
7248
7496
  function resolveDockMagnificationSlotCenter(rect, dockPlacement, baseSize = DOCK_ICON_BASE_SIZE) {
7249
7497
  return dockPlacement === "left" ? rect.top + baseSize / 2 : rect.left + baseSize / 2;
@@ -7333,9 +7581,30 @@ function clearDockSlotMagnification(slotElement, appliedStylesRef) {
7333
7581
  const shell = resolveDockMagnificationShell(slotElement);
7334
7582
  shell?.style.removeProperty("transform");
7335
7583
  }
7584
+ function isPointNearDockScreenEdge({
7585
+ clientX,
7586
+ clientY,
7587
+ dockPlacement
7588
+ }) {
7589
+ if (typeof window === "undefined") {
7590
+ return true;
7591
+ }
7592
+ return dockPlacement === "left" ? clientX <= DOCK_MAGNIFICATION_AMBIENT_EDGE_RANGE : window.innerHeight - clientY <= DOCK_MAGNIFICATION_AMBIENT_EDGE_RANGE;
7593
+ }
7594
+ function isPointNearDockViewport({
7595
+ clientX,
7596
+ clientY,
7597
+ dockPlacement,
7598
+ viewportRect
7599
+ }) {
7600
+ const horizontalPadding = dockPlacement === "bottom" ? DOCK_MAGNIFICATION_MAIN_AXIS_EDGE_PADDING : DOCK_MAGNIFICATION_AMBIENT_VIEWPORT_PADDING;
7601
+ const verticalPadding = dockPlacement === "left" ? DOCK_MAGNIFICATION_MAIN_AXIS_EDGE_PADDING : DOCK_MAGNIFICATION_AMBIENT_VIEWPORT_PADDING;
7602
+ return clientX >= viewportRect.left - horizontalPadding && clientX <= viewportRect.right + horizontalPadding && clientY >= viewportRect.top - verticalPadding && clientY <= viewportRect.bottom + verticalPadding;
7603
+ }
7336
7604
  function useDockMagnification({
7337
7605
  dockPlacement,
7338
7606
  dockRootRef,
7607
+ dockViewportRef,
7339
7608
  slotRefs
7340
7609
  }) {
7341
7610
  const pointerAxisRef = useRef5(null);
@@ -7349,8 +7618,16 @@ function useDockMagnification({
7349
7618
  const entryRampStartedAtRef = useRef5(null);
7350
7619
  const restCentersRef = useRef5(null);
7351
7620
  const hitBoundsRef = useRef5(null);
7621
+ const visibleSlotRectsRef = useRef5(null);
7352
7622
  const slotOrderRef = useRef5([]);
7353
7623
  const magnifyActiveRef = useRef5(false);
7624
+ const globalPointerTrackerRef = useRef5(null);
7625
+ const handleGlobalPointerMoveRef = useRef5(() => {
7626
+ return;
7627
+ });
7628
+ const handleGlobalPointerCancelRef = useRef5(() => {
7629
+ return;
7630
+ });
7354
7631
  const setMagnifyActive = useCallback8(
7355
7632
  (active) => {
7356
7633
  if (magnifyActiveRef.current === active) {
@@ -7377,6 +7654,7 @@ function useDockMagnification({
7377
7654
  const centers = /* @__PURE__ */ new Map();
7378
7655
  const slotRects = [];
7379
7656
  const order = [];
7657
+ const viewportRect = dockViewportRef.current?.getBoundingClientRect();
7380
7658
  for (const [anchorKey, slotElement] of slots) {
7381
7659
  order.push(anchorKey);
7382
7660
  const rect = slotElement.getBoundingClientRect();
@@ -7390,12 +7668,53 @@ function useDockMagnification({
7390
7668
  centers.set(anchorKey, center);
7391
7669
  }
7392
7670
  slotOrderRef.current = order;
7393
- hitBoundsRef.current = resolveDockMagnificationHitBounds(
7671
+ const visibleViewportRect = viewportRect ? {
7672
+ bottom: viewportRect.bottom,
7673
+ left: viewportRect.left,
7674
+ right: viewportRect.right,
7675
+ top: viewportRect.top
7676
+ } : null;
7677
+ hitBoundsRef.current = resolveDockMagnificationVisibleHitBounds({
7678
+ dockPlacement,
7679
+ hitBounds: resolveDockMagnificationHitBounds(slotRects, dockPlacement),
7680
+ mainAxisEdgePadding: DOCK_MAGNIFICATION_MAIN_AXIS_EDGE_PADDING,
7681
+ viewportRect: visibleViewportRect
7682
+ });
7683
+ visibleSlotRectsRef.current = resolveDockMagnificationVisibleSlotRects({
7394
7684
  slotRects,
7395
- dockPlacement
7396
- );
7685
+ viewportRect: visibleViewportRect
7686
+ });
7397
7687
  restCentersRef.current = centers;
7398
- }, [dockPlacement, slotRefs]);
7688
+ }, [dockPlacement, dockViewportRef, slotRefs]);
7689
+ const isPointerInsideAnyVisibleDockSlot = useCallback8(
7690
+ (clientX, clientY) => {
7691
+ for (const rect of visibleSlotRectsRef.current ?? []) {
7692
+ if (isDockMagnificationPointInsideSlotRect({
7693
+ clientX,
7694
+ clientY,
7695
+ rect
7696
+ })) {
7697
+ return true;
7698
+ }
7699
+ }
7700
+ return false;
7701
+ },
7702
+ []
7703
+ );
7704
+ const ensureDockMagnificationGeometry = useCallback8(() => {
7705
+ if (restCentersRef.current === null || hitBoundsRef.current === null || visibleSlotRectsRef.current === null) {
7706
+ captureRestCenters();
7707
+ }
7708
+ }, [captureRestCenters]);
7709
+ const isPointerInsideDockMagnificationTarget = useCallback8(
7710
+ (clientX, clientY) => isDockMagnificationPointInsideHitBounds({
7711
+ clientX,
7712
+ clientY,
7713
+ dockPlacement,
7714
+ hitBounds: hitBoundsRef.current
7715
+ }) || isPointerInsideAnyVisibleDockSlot(clientX, clientY),
7716
+ [dockPlacement, isPointerInsideAnyVisibleDockSlot]
7717
+ );
7399
7718
  const runAnimationFrame = useCallback8(
7400
7719
  (frameTime) => {
7401
7720
  if (pendingPointerAxisRef.current !== null) {
@@ -7493,6 +7812,7 @@ function useDockMagnification({
7493
7812
  entryRampStartedAtRef.current = null;
7494
7813
  restCentersRef.current = null;
7495
7814
  hitBoundsRef.current = null;
7815
+ visibleSlotRectsRef.current = null;
7496
7816
  slotOrderRef.current = [];
7497
7817
  setMagnifyActive(false);
7498
7818
  }
@@ -7511,31 +7831,144 @@ function useDockMagnification({
7511
7831
  }
7512
7832
  animationFrameRef.current = requestAnimationFrame(runAnimationFrame);
7513
7833
  }, [runAnimationFrame]);
7834
+ const stopGlobalPointerTracking = useCallback8(() => {
7835
+ globalPointerTrackerRef.current?.stop();
7836
+ }, []);
7837
+ const startGlobalPointerTracking = useCallback8(() => {
7838
+ if (typeof document === "undefined") {
7839
+ return;
7840
+ }
7841
+ globalPointerTrackerRef.current ??= createDockMagnificationGlobalPointerTracker({
7842
+ blurTarget: typeof window === "undefined" ? null : window,
7843
+ onPointerCancel: () => {
7844
+ handleGlobalPointerCancelRef.current();
7845
+ },
7846
+ onPointerMove: (clientX, clientY) => {
7847
+ handleGlobalPointerMoveRef.current(clientX, clientY);
7848
+ },
7849
+ pointerTarget: document
7850
+ });
7851
+ globalPointerTrackerRef.current.start();
7852
+ }, []);
7853
+ const clearTrackedPointer = useCallback8(() => {
7854
+ pendingPointerAxisRef.current = null;
7855
+ pointerAxisRef.current = null;
7856
+ entryRampStartedAtRef.current = null;
7857
+ scheduleAnimation();
7858
+ }, [scheduleAnimation]);
7859
+ const handleGlobalPointerCancel = useCallback8(() => {
7860
+ stopGlobalPointerTracking();
7861
+ clearTrackedPointer();
7862
+ }, [clearTrackedPointer, stopGlobalPointerTracking]);
7514
7863
  const handlePointerMove = useCallback8(
7515
7864
  (clientX, clientY) => {
7516
- if (restCentersRef.current === null) {
7517
- captureRestCenters();
7518
- }
7519
- if (!isDockMagnificationPointInsideHitBounds({
7520
- clientX,
7521
- clientY,
7522
- dockPlacement,
7523
- hitBounds: hitBoundsRef.current
7524
- })) {
7525
- pendingPointerAxisRef.current = null;
7526
- pointerAxisRef.current = null;
7527
- entryRampStartedAtRef.current = null;
7528
- scheduleAnimation();
7865
+ ensureDockMagnificationGeometry();
7866
+ if (!isPointerInsideDockMagnificationTarget(clientX, clientY)) {
7867
+ stopGlobalPointerTracking();
7868
+ clearTrackedPointer();
7529
7869
  return;
7530
7870
  }
7531
7871
  pendingPointerAxisRef.current = dockPlacement === "left" ? clientY : clientX;
7532
7872
  if (!magnifyActiveRef.current) {
7533
7873
  setMagnifyActive(true);
7534
7874
  }
7875
+ startGlobalPointerTracking();
7535
7876
  scheduleAnimation();
7536
7877
  },
7537
- [captureRestCenters, dockPlacement, scheduleAnimation, setMagnifyActive]
7878
+ [
7879
+ clearTrackedPointer,
7880
+ dockPlacement,
7881
+ ensureDockMagnificationGeometry,
7882
+ isPointerInsideDockMagnificationTarget,
7883
+ scheduleAnimation,
7884
+ setMagnifyActive,
7885
+ startGlobalPointerTracking,
7886
+ stopGlobalPointerTracking
7887
+ ]
7538
7888
  );
7889
+ handleGlobalPointerMoveRef.current = handlePointerMove;
7890
+ handleGlobalPointerCancelRef.current = handleGlobalPointerCancel;
7891
+ useEffect6(() => {
7892
+ if (typeof document === "undefined") {
7893
+ return;
7894
+ }
7895
+ let animationFrame = null;
7896
+ let latestPoint = null;
7897
+ const clearAmbientPointerSample = () => {
7898
+ latestPoint = null;
7899
+ if (animationFrame !== null) {
7900
+ cancelAnimationFrame(animationFrame);
7901
+ animationFrame = null;
7902
+ }
7903
+ };
7904
+ const runAmbientPointerMove = () => {
7905
+ animationFrame = null;
7906
+ const point = latestPoint;
7907
+ latestPoint = null;
7908
+ if (!point || magnifyActiveRef.current) {
7909
+ return;
7910
+ }
7911
+ const viewportRect = dockViewportRef.current?.getBoundingClientRect();
7912
+ if (!viewportRect) {
7913
+ return;
7914
+ }
7915
+ const visibleViewportRect = {
7916
+ bottom: viewportRect.bottom,
7917
+ left: viewportRect.left,
7918
+ right: viewportRect.right,
7919
+ top: viewportRect.top
7920
+ };
7921
+ if (!isPointNearDockViewport({
7922
+ clientX: point.clientX,
7923
+ clientY: point.clientY,
7924
+ dockPlacement,
7925
+ viewportRect: visibleViewportRect
7926
+ })) {
7927
+ return;
7928
+ }
7929
+ ensureDockMagnificationGeometry();
7930
+ if (isPointerInsideDockMagnificationTarget(point.clientX, point.clientY)) {
7931
+ handlePointerMove(point.clientX, point.clientY);
7932
+ }
7933
+ };
7934
+ const handleAmbientPointerMove = (event) => {
7935
+ if (magnifyActiveRef.current) {
7936
+ clearAmbientPointerSample();
7937
+ return;
7938
+ }
7939
+ if (!isPointNearDockScreenEdge({
7940
+ clientX: event.clientX,
7941
+ clientY: event.clientY,
7942
+ dockPlacement
7943
+ })) {
7944
+ clearAmbientPointerSample();
7945
+ return;
7946
+ }
7947
+ latestPoint = { clientX: event.clientX, clientY: event.clientY };
7948
+ if (animationFrame === null) {
7949
+ animationFrame = requestAnimationFrame(runAmbientPointerMove);
7950
+ }
7951
+ };
7952
+ document.addEventListener(
7953
+ "pointermove",
7954
+ handleAmbientPointerMove,
7955
+ globalPointerListenerOptions
7956
+ );
7957
+ return () => {
7958
+ document.removeEventListener(
7959
+ "pointermove",
7960
+ handleAmbientPointerMove,
7961
+ globalPointerListenerOptions
7962
+ );
7963
+ clearAmbientPointerSample();
7964
+ };
7965
+ }, [
7966
+ dockPlacement,
7967
+ dockViewportRef,
7968
+ ensureDockMagnificationGeometry,
7969
+ handlePointerMove,
7970
+ isPointerInsideDockMagnificationTarget
7971
+ ]);
7539
7972
  const handlePointerLeave = useCallback8(() => {
7540
7973
  pendingPointerAxisRef.current = null;
7541
7974
  pointerAxisRef.current = null;
@@ -7547,6 +7980,7 @@ function useDockMagnification({
7547
7980
  restCentersRef.current?.delete(anchorKey);
7548
7981
  appliedStylesRef.current.delete(anchorKey);
7549
7982
  hitBoundsRef.current = null;
7983
+ visibleSlotRectsRef.current = null;
7550
7984
  slotOrderRef.current = slotOrderRef.current.filter(
7551
7985
  (key) => key !== anchorKey
7552
7986
  );
@@ -7558,13 +7992,15 @@ function useDockMagnification({
7558
7992
  [slotRefs]
7559
7993
  );
7560
7994
  const pauseMagnification = useCallback8(() => {
7995
+ stopGlobalPointerTracking();
7561
7996
  stopAnimation();
7562
7997
  pendingPointerAxisRef.current = null;
7563
7998
  pointerAxisRef.current = null;
7564
7999
  entryRampStartedAtRef.current = null;
7565
8000
  lastFrameTimeRef.current = null;
7566
- }, [stopAnimation]);
8001
+ }, [stopAnimation, stopGlobalPointerTracking]);
7567
8002
  const resetMagnification = useCallback8(() => {
8003
+ stopGlobalPointerTracking();
7568
8004
  stopAnimation();
7569
8005
  for (const slotElement of slotRefs.current.values()) {
7570
8006
  clearDockSlotMagnification(slotElement, appliedStylesRef.current);
@@ -7573,12 +8009,13 @@ function useDockMagnification({
7573
8009
  appliedStylesRef.current.clear();
7574
8010
  restCentersRef.current = null;
7575
8011
  hitBoundsRef.current = null;
8012
+ visibleSlotRectsRef.current = null;
7576
8013
  slotOrderRef.current = [];
7577
8014
  pointerAxisRef.current = null;
7578
8015
  pendingPointerAxisRef.current = null;
7579
8016
  entryRampStartedAtRef.current = null;
7580
8017
  setMagnifyActive(false);
7581
- }, [setMagnifyActive, slotRefs, stopAnimation]);
8018
+ }, [setMagnifyActive, slotRefs, stopAnimation, stopGlobalPointerTracking]);
7582
8019
  useEffect6(
7583
8020
  () => () => {
7584
8021
  resetMagnification();
@@ -9316,6 +9753,7 @@ function WorkbenchHostDock({
9316
9753
  } = useDockMagnification({
9317
9754
  dockPlacement,
9318
9755
  dockRootRef: dockMeasureRef,
9756
+ dockViewportRef: dockItemsRef,
9319
9757
  slotRefs
9320
9758
  });
9321
9759
  const clearSlotMagnificationRef = useRef8(() => {