@tutti-os/workbench-surface 0.0.2 → 0.0.3

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.js CHANGED
@@ -1712,6 +1712,9 @@ function WorkbenchDockFrame({
1712
1712
  },
1713
1713
  registerDockAnchor: (anchorKey, element) => {
1714
1714
  genie.registerDockAnchor(anchorKey, element);
1715
+ },
1716
+ shouldAnimateMinimizedDockEnter: (nodeID) => {
1717
+ return genie.shouldAnimateMinimizedDockEnter(nodeID);
1715
1718
  }
1716
1719
  },
1717
1720
  minimizedNodes,
@@ -2589,6 +2592,7 @@ import { jsx as jsx7 } from "react/jsx-runtime";
2589
2592
  var genieDurationMs = 400;
2590
2593
  var genieMaxDevicePixelRatio = 2;
2591
2594
  var genieSnapshotScale = 1;
2595
+ var minimizedDockSlotEnterAnimationMs = 720;
2592
2596
  var dockPreviewMaxWidth = 260;
2593
2597
  var dockPreviewMaxHeight = 170;
2594
2598
  var dockPreviewImageCacheMaxEntries = 96;
@@ -2868,6 +2872,10 @@ function useWorkbenchGenieAnimation({
2868
2872
  const rafRef = useRef4(null);
2869
2873
  const animationGenerationRef = useRef4(0);
2870
2874
  const animationCleanupRef = useRef4(null);
2875
+ const minimizedDockEnterAnimationNodeIdsRef = useRef4(/* @__PURE__ */ new Set());
2876
+ const minimizedDockEnterAnimationTimersRef = useRef4(
2877
+ /* @__PURE__ */ new Map()
2878
+ );
2871
2879
  const [isCanvasActive, setIsCanvasActive] = useState4(false);
2872
2880
  const [genieHiddenNodeIDs, setGenieHiddenNodeIDs] = useState4(
2873
2881
  () => /* @__PURE__ */ new Set()
@@ -2895,6 +2903,40 @@ function useWorkbenchGenieAnimation({
2895
2903
  (node) => resolveDockAnchorKey?.(node) ?? node.id,
2896
2904
  [resolveDockAnchorKey]
2897
2905
  );
2906
+ const releaseMinimizedDockEnterAnimation = useCallback6((nodeID) => {
2907
+ const timer = minimizedDockEnterAnimationTimersRef.current.get(nodeID);
2908
+ if (timer) {
2909
+ clearTimeout(timer);
2910
+ minimizedDockEnterAnimationTimersRef.current.delete(nodeID);
2911
+ }
2912
+ minimizedDockEnterAnimationNodeIdsRef.current.delete(nodeID);
2913
+ }, []);
2914
+ const registerMinimizedDockEnterAnimation = useCallback6(
2915
+ (nodeID) => {
2916
+ releaseMinimizedDockEnterAnimation(nodeID);
2917
+ minimizedDockEnterAnimationNodeIdsRef.current.add(nodeID);
2918
+ },
2919
+ [releaseMinimizedDockEnterAnimation]
2920
+ );
2921
+ const scheduleReleaseMinimizedDockEnterAnimation = useCallback6(
2922
+ (nodeID, delayMs = minimizedDockSlotEnterAnimationMs) => {
2923
+ const existing = minimizedDockEnterAnimationTimersRef.current.get(nodeID);
2924
+ if (existing) {
2925
+ clearTimeout(existing);
2926
+ }
2927
+ minimizedDockEnterAnimationTimersRef.current.set(
2928
+ nodeID,
2929
+ setTimeout(() => {
2930
+ minimizedDockEnterAnimationTimersRef.current.delete(nodeID);
2931
+ minimizedDockEnterAnimationNodeIdsRef.current.delete(nodeID);
2932
+ }, delayMs)
2933
+ );
2934
+ },
2935
+ []
2936
+ );
2937
+ const shouldAnimateMinimizedDockEnter = useCallback6((nodeID) => {
2938
+ return minimizedDockEnterAnimationNodeIdsRef.current.has(nodeID);
2939
+ }, []);
2898
2940
  const hideNodeForGenie = useCallback6((nodeID) => {
2899
2941
  setGenieHiddenNodeIDs((current) => {
2900
2942
  if (current.has(nodeID)) {
@@ -3198,6 +3240,7 @@ function useWorkbenchGenieAnimation({
3198
3240
  clearCanvas();
3199
3241
  };
3200
3242
  animationCleanupRef.current = cleanupHiddenNode;
3243
+ registerMinimizedDockEnterAnimation(nodeID);
3201
3244
  flushSync(() => {
3202
3245
  hideNodeForGenie(nodeID);
3203
3246
  runMinimize();
@@ -3206,12 +3249,14 @@ function useWorkbenchGenieAnimation({
3206
3249
  window.requestAnimationFrame(() => resolve());
3207
3250
  });
3208
3251
  if (generation !== animationGenerationRef.current) {
3252
+ releaseMinimizedDockEnterAnimation(nodeID);
3209
3253
  return;
3210
3254
  }
3211
3255
  const minimizedTarget = controller.getSnapshot().nodes.find((node) => node.id === nodeID) ?? target;
3212
3256
  const anchorKey = resolveAnchorKeyForNode(minimizedTarget);
3213
3257
  const dockRect = resolveDockAnchorRect(anchorKey);
3214
3258
  if (!dockRect || !isUsableGenieRect(dockRect)) {
3259
+ releaseMinimizedDockEnterAnimation(nodeID);
3215
3260
  animationCleanupRef.current = null;
3216
3261
  cleanupHiddenNode();
3217
3262
  return;
@@ -3220,12 +3265,14 @@ function useWorkbenchGenieAnimation({
3220
3265
  direction: "minimize",
3221
3266
  dockRect,
3222
3267
  onCancel: () => {
3268
+ releaseMinimizedDockEnterAnimation(nodeID);
3223
3269
  flushSync(() => {
3224
3270
  showNodeForGenie(nodeID);
3225
3271
  });
3226
3272
  clearCanvas();
3227
3273
  },
3228
3274
  onComplete: () => {
3275
+ scheduleReleaseMinimizedDockEnterAnimation(nodeID);
3229
3276
  flushSync(() => {
3230
3277
  showNodeForGenie(nodeID);
3231
3278
  });
@@ -3242,16 +3289,29 @@ function useWorkbenchGenieAnimation({
3242
3289
  captureNodePreviewImage,
3243
3290
  dockPreviewCache,
3244
3291
  hideNodeForGenie,
3292
+ registerMinimizedDockEnterAnimation,
3293
+ releaseMinimizedDockEnterAnimation,
3245
3294
  resolveAnchorKeyForNode,
3246
3295
  resolveDockPreviewCacheKey2,
3247
3296
  resolveDockAnchorRect,
3248
3297
  resolveNodeElement,
3249
3298
  runGenieAnimation,
3299
+ scheduleReleaseMinimizedDockEnterAnimation,
3250
3300
  setupCanvas,
3251
3301
  showNodeForGenie,
3252
3302
  stopAnimation
3253
3303
  ]
3254
3304
  );
3305
+ useEffect3(
3306
+ () => () => {
3307
+ for (const timer of minimizedDockEnterAnimationTimersRef.current.values()) {
3308
+ clearTimeout(timer);
3309
+ }
3310
+ minimizedDockEnterAnimationTimersRef.current.clear();
3311
+ minimizedDockEnterAnimationNodeIdsRef.current.clear();
3312
+ },
3313
+ []
3314
+ );
3255
3315
  return {
3256
3316
  genieLayer: typeof document === "undefined" ? null : createPortal(
3257
3317
  /* @__PURE__ */ jsx7(
@@ -3271,7 +3331,8 @@ function useWorkbenchGenieAnimation({
3271
3331
  ),
3272
3332
  launchNodeFromAnchor,
3273
3333
  minimizeNodeToAnchor,
3274
- registerDockAnchor
3334
+ registerDockAnchor,
3335
+ shouldAnimateMinimizedDockEnter
3275
3336
  };
3276
3337
  }
3277
3338
 
@@ -4566,8 +4627,11 @@ var WorkbenchHostSessionController = class {
4566
4627
  return null;
4567
4628
  }
4568
4629
  const launchSource = resolveWorkbenchHostLaunchSource(input);
4630
+ const currentState = this.controller.getSnapshot();
4569
4631
  const request = {
4570
4632
  ...input,
4633
+ layoutConstraints: currentState.layoutConstraints,
4634
+ surfaceSize: currentState.surfaceSize,
4571
4635
  workspaceId: this.input.workspaceId
4572
4636
  };
4573
4637
  if (launchSource !== null) {
@@ -5318,11 +5382,11 @@ import { useCallback as useCallback11, useMemo as useMemo6 } from "react";
5318
5382
  // src/host/WorkbenchHostDock.tsx
5319
5383
  import {
5320
5384
  useCallback as useCallback10,
5321
- useEffect as useEffect8,
5385
+ useEffect as useEffect9,
5322
5386
  useLayoutEffect as useLayoutEffect4,
5323
5387
  useMemo as useMemo5,
5324
- useRef as useRef7,
5325
- useState as useState7
5388
+ useRef as useRef8,
5389
+ useState as useState8
5326
5390
  } from "react";
5327
5391
  import {
5328
5392
  ArrowLeftIcon,
@@ -5330,10 +5394,7 @@ import {
5330
5394
  Button as Button4,
5331
5395
  ChevronDownIcon,
5332
5396
  ChevronUpIcon,
5333
- Tooltip,
5334
- TooltipContent,
5335
- TooltipProvider,
5336
- TooltipTrigger
5397
+ TooltipProvider
5337
5398
  } from "@tutti-os/ui-system";
5338
5399
 
5339
5400
  // src/host/dockEntries.ts
@@ -5437,6 +5498,8 @@ var MAGNIFICATION_SETTLE_EPSILON = 0.2;
5437
5498
  var MAGNIFICATION_SIZE_EPSILON = 0.2;
5438
5499
  var MAX_MAGNIFICATION_STEP_SECONDS = 1 / 30;
5439
5500
  var MAGNIFICATION_INFLUENCE_PADDING = 8;
5501
+ var DOCK_MAGNIFICATION_ENTRY_RAMP_MS = 90;
5502
+ var DOCK_MAGNIFICATION_CROSS_AXIS_PADDING = 8;
5440
5503
  var dockMagnificationShellBySlot = /* @__PURE__ */ new WeakMap();
5441
5504
  function mapDistanceToTargetSize(distance, baseSize = DOCK_ICON_BASE_SIZE, peakSize = DOCK_ICON_PEAK_SIZE, halfRange = DOCK_MAGNIFICATION_HALF_RANGE) {
5442
5505
  const absoluteDistance = Math.abs(distance);
@@ -5446,6 +5509,55 @@ function mapDistanceToTargetSize(distance, baseSize = DOCK_ICON_BASE_SIZE, peakS
5446
5509
  const influence = 1 - absoluteDistance / halfRange;
5447
5510
  return baseSize + (peakSize - baseSize) * influence;
5448
5511
  }
5512
+ function applyDockMagnificationEntryRamp(targetSize, baseSize, progress) {
5513
+ const clampedProgress = Math.min(1, Math.max(0, progress));
5514
+ return baseSize + (targetSize - baseSize) * clampedProgress;
5515
+ }
5516
+ function resolveDockMagnificationHitBounds(slotRects, dockPlacement, crossAxisPadding = DOCK_MAGNIFICATION_CROSS_AXIS_PADDING) {
5517
+ if (slotRects.length === 0) {
5518
+ return null;
5519
+ }
5520
+ let mainStart = Number.POSITIVE_INFINITY;
5521
+ let mainEnd = Number.NEGATIVE_INFINITY;
5522
+ let crossStart = Number.POSITIVE_INFINITY;
5523
+ let crossEnd = Number.NEGATIVE_INFINITY;
5524
+ for (const rect of slotRects) {
5525
+ if (dockPlacement === "left") {
5526
+ mainStart = Math.min(mainStart, rect.top);
5527
+ mainEnd = Math.max(mainEnd, rect.bottom);
5528
+ crossStart = Math.min(crossStart, rect.left);
5529
+ crossEnd = Math.max(crossEnd, rect.right);
5530
+ } else {
5531
+ mainStart = Math.min(mainStart, rect.left);
5532
+ mainEnd = Math.max(mainEnd, rect.right);
5533
+ crossStart = Math.min(crossStart, rect.top);
5534
+ crossEnd = Math.max(crossEnd, rect.bottom);
5535
+ }
5536
+ }
5537
+ const effectiveCrossAxisEndPadding = dockPlacement === "left" ? crossAxisPadding + (DOCK_ICON_PEAK_SIZE - DOCK_ICON_BASE_SIZE) : crossAxisPadding;
5538
+ return {
5539
+ crossEnd: crossEnd + effectiveCrossAxisEndPadding,
5540
+ crossStart: crossStart - crossAxisPadding,
5541
+ mainEnd,
5542
+ mainStart
5543
+ };
5544
+ }
5545
+ function isDockMagnificationPointInsideHitBounds({
5546
+ clientX,
5547
+ clientY,
5548
+ dockPlacement,
5549
+ hitBounds
5550
+ }) {
5551
+ if (!hitBounds) {
5552
+ return false;
5553
+ }
5554
+ const mainAxis = dockPlacement === "left" ? clientY : clientX;
5555
+ const crossAxis = dockPlacement === "left" ? clientX : clientY;
5556
+ return mainAxis >= hitBounds.mainStart && mainAxis <= hitBounds.mainEnd && crossAxis >= hitBounds.crossStart && crossAxis <= hitBounds.crossEnd;
5557
+ }
5558
+ function resolveDockMagnificationSlotCenter(rect, dockPlacement, baseSize = DOCK_ICON_BASE_SIZE) {
5559
+ return dockPlacement === "left" ? rect.top + baseSize / 2 : rect.left + baseSize / 2;
5560
+ }
5449
5561
  var DOCK_MAGNIFICATION_SPRING_SUBSTEPS = 8;
5450
5562
  function advanceDockMagnificationSpring(current, target, deltaSeconds) {
5451
5563
  const subDeltaSeconds = deltaSeconds / DOCK_MAGNIFICATION_SPRING_SUBSTEPS;
@@ -5470,6 +5582,14 @@ function hasAppliedSizeChanged(previous, next) {
5470
5582
  }
5471
5583
  return Math.abs(previous.size - next.size) > MAGNIFICATION_SIZE_EPSILON;
5472
5584
  }
5585
+ function resolveDockMagnificationSlotLayoutSize({
5586
+ size
5587
+ }) {
5588
+ return { height: size, width: size };
5589
+ }
5590
+ function isDockMagnificationSlotLayoutLocked(slotElement) {
5591
+ return slotElement.dataset.collapsing === "true" || slotElement.dataset.presence === "entering" || slotElement.dataset.presence === "exiting";
5592
+ }
5473
5593
  function resolveDockMagnificationShell(slotElement) {
5474
5594
  const cachedShell = dockMagnificationShellBySlot.get(slotElement);
5475
5595
  if (cachedShell !== void 0 && (cachedShell === null || slotElement.contains(cachedShell))) {
@@ -5506,8 +5626,11 @@ function applyDockSlotMagnification(slotElement, size, baseSize, appliedStylesRe
5506
5626
  return;
5507
5627
  }
5508
5628
  const scale = nextStyle.size / baseSize;
5509
- slotElement.style.width = `${nextStyle.size}px`;
5510
- slotElement.style.height = `${nextStyle.size}px`;
5629
+ const layoutSize = resolveDockMagnificationSlotLayoutSize({
5630
+ size: nextStyle.size
5631
+ });
5632
+ slotElement.style.width = `${layoutSize.width}px`;
5633
+ slotElement.style.height = `${layoutSize.height}px`;
5511
5634
  shell.style.transform = `scale(${scale})`;
5512
5635
  }
5513
5636
  function clearDockSlotMagnification(slotElement, appliedStylesRef) {
@@ -5533,7 +5656,9 @@ function useDockMagnification({
5533
5656
  );
5534
5657
  const animationFrameRef = useRef5(null);
5535
5658
  const lastFrameTimeRef = useRef5(null);
5659
+ const entryRampStartedAtRef = useRef5(null);
5536
5660
  const restCentersRef = useRef5(null);
5661
+ const hitBoundsRef = useRef5(null);
5537
5662
  const slotOrderRef = useRef5([]);
5538
5663
  const magnifyActiveRef = useRef5(false);
5539
5664
  const setMagnifyActive = useCallback8(
@@ -5560,14 +5685,25 @@ function useDockMagnification({
5560
5685
  const captureRestCenters = useCallback8(() => {
5561
5686
  const slots = slotRefs.current;
5562
5687
  const centers = /* @__PURE__ */ new Map();
5688
+ const slotRects = [];
5563
5689
  const order = [];
5564
5690
  for (const [anchorKey, slotElement] of slots) {
5565
5691
  order.push(anchorKey);
5566
5692
  const rect = slotElement.getBoundingClientRect();
5567
- const center = dockPlacement === "left" ? rect.top + rect.height / 2 : rect.left + rect.width / 2;
5693
+ slotRects.push({
5694
+ bottom: rect.bottom,
5695
+ left: rect.left,
5696
+ right: rect.right,
5697
+ top: rect.top
5698
+ });
5699
+ const center = resolveDockMagnificationSlotCenter(rect, dockPlacement);
5568
5700
  centers.set(anchorKey, center);
5569
5701
  }
5570
5702
  slotOrderRef.current = order;
5703
+ hitBoundsRef.current = resolveDockMagnificationHitBounds(
5704
+ slotRects,
5705
+ dockPlacement
5706
+ );
5571
5707
  restCentersRef.current = centers;
5572
5708
  }, [dockPlacement, slotRefs]);
5573
5709
  const runAnimationFrame = useCallback8(
@@ -5578,6 +5714,7 @@ function useDockMagnification({
5578
5714
  const pointerAxis = pointerAxisRef.current;
5579
5715
  if (pointerAxis !== null) {
5580
5716
  captureRestCenters();
5717
+ entryRampStartedAtRef.current ??= frameTime;
5581
5718
  }
5582
5719
  const slots = slotRefs.current;
5583
5720
  const order = slotOrderRef.current;
@@ -5590,6 +5727,7 @@ function useDockMagnification({
5590
5727
  let shouldContinue = false;
5591
5728
  let allSettled = true;
5592
5729
  const influenceRadius = DOCK_MAGNIFICATION_HALF_RANGE + MAGNIFICATION_INFLUENCE_PADDING;
5730
+ const entryRampProgress = pointerAxis === null || entryRampStartedAtRef.current === null ? 1 : (frameTime - entryRampStartedAtRef.current) / DOCK_MAGNIFICATION_ENTRY_RAMP_MS;
5593
5731
  for (let index = 0; index < order.length; index += 1) {
5594
5732
  const anchorKey = order[index];
5595
5733
  if (!anchorKey) {
@@ -5602,7 +5740,11 @@ function useDockMagnification({
5602
5740
  const distance = pointerAxis === null ? DOCK_MAGNIFICATION_HALF_RANGE : pointerAxis - center;
5603
5741
  const absoluteDistance = Math.abs(distance);
5604
5742
  const inRange = absoluteDistance < influenceRadius;
5605
- const targetSize = inRange ? mapDistanceToTargetSize(distance) : DOCK_ICON_BASE_SIZE;
5743
+ const targetSize = inRange ? applyDockMagnificationEntryRamp(
5744
+ mapDistanceToTargetSize(distance),
5745
+ DOCK_ICON_BASE_SIZE,
5746
+ entryRampProgress
5747
+ ) : DOCK_ICON_BASE_SIZE;
5606
5748
  const currentSpring = springsRef.current.get(anchorKey) ?? {
5607
5749
  value: DOCK_ICON_BASE_SIZE,
5608
5750
  velocity: 0
@@ -5635,6 +5777,11 @@ function useDockMagnification({
5635
5777
  if (!slotElement) {
5636
5778
  continue;
5637
5779
  }
5780
+ if (isDockMagnificationSlotLayoutLocked(slotElement)) {
5781
+ springsRef.current.delete(anchorKey);
5782
+ clearDockSlotMagnification(slotElement, appliedStylesRef.current);
5783
+ continue;
5784
+ }
5638
5785
  if (pointerAxis === null && isDockMagnificationSpringSettled(nextSpring, DOCK_ICON_BASE_SIZE)) {
5639
5786
  springsRef.current.delete(anchorKey);
5640
5787
  clearDockSlotMagnification(slotElement, appliedStylesRef.current);
@@ -5653,7 +5800,9 @@ function useDockMagnification({
5653
5800
  shouldContinue = true;
5654
5801
  }
5655
5802
  if (pointerAxis === null && allSettled) {
5803
+ entryRampStartedAtRef.current = null;
5656
5804
  restCentersRef.current = null;
5805
+ hitBoundsRef.current = null;
5657
5806
  slotOrderRef.current = [];
5658
5807
  setMagnifyActive(false);
5659
5808
  }
@@ -5674,9 +5823,23 @@ function useDockMagnification({
5674
5823
  }, [runAnimationFrame]);
5675
5824
  const handlePointerMove = useCallback8(
5676
5825
  (clientX, clientY) => {
5677
- pendingPointerAxisRef.current = dockPlacement === "left" ? clientY : clientX;
5678
5826
  if (restCentersRef.current === null) {
5679
5827
  captureRestCenters();
5828
+ }
5829
+ if (!isDockMagnificationPointInsideHitBounds({
5830
+ clientX,
5831
+ clientY,
5832
+ dockPlacement,
5833
+ hitBounds: hitBoundsRef.current
5834
+ })) {
5835
+ pendingPointerAxisRef.current = null;
5836
+ pointerAxisRef.current = null;
5837
+ entryRampStartedAtRef.current = null;
5838
+ scheduleAnimation();
5839
+ return;
5840
+ }
5841
+ pendingPointerAxisRef.current = dockPlacement === "left" ? clientY : clientX;
5842
+ if (!magnifyActiveRef.current) {
5680
5843
  setMagnifyActive(true);
5681
5844
  }
5682
5845
  scheduleAnimation();
@@ -5693,6 +5856,7 @@ function useDockMagnification({
5693
5856
  springsRef.current.delete(anchorKey);
5694
5857
  restCentersRef.current?.delete(anchorKey);
5695
5858
  appliedStylesRef.current.delete(anchorKey);
5859
+ hitBoundsRef.current = null;
5696
5860
  slotOrderRef.current = slotOrderRef.current.filter(
5697
5861
  (key) => key !== anchorKey
5698
5862
  );
@@ -5707,6 +5871,7 @@ function useDockMagnification({
5707
5871
  stopAnimation();
5708
5872
  pendingPointerAxisRef.current = null;
5709
5873
  pointerAxisRef.current = null;
5874
+ entryRampStartedAtRef.current = null;
5710
5875
  lastFrameTimeRef.current = null;
5711
5876
  }, [stopAnimation]);
5712
5877
  const resetMagnification = useCallback8(() => {
@@ -5717,9 +5882,11 @@ function useDockMagnification({
5717
5882
  springsRef.current.clear();
5718
5883
  appliedStylesRef.current.clear();
5719
5884
  restCentersRef.current = null;
5885
+ hitBoundsRef.current = null;
5720
5886
  slotOrderRef.current = [];
5721
5887
  pointerAxisRef.current = null;
5722
5888
  pendingPointerAxisRef.current = null;
5889
+ entryRampStartedAtRef.current = null;
5723
5890
  setMagnifyActive(false);
5724
5891
  }, [setMagnifyActive, slotRefs, stopAnimation]);
5725
5892
  useEffect6(
@@ -5827,13 +5994,67 @@ function orderWorkbenchMinimizedDockNodes(input) {
5827
5994
  }).map(({ node }) => node);
5828
5995
  }
5829
5996
 
5997
+ // src/host/minimizedDockStackPromotion.ts
5998
+ import { useEffect as useEffect7, useRef as useRef6, useState as useState6 } from "react";
5999
+ var minimizedDockStackPromotionDurationMs = 520;
6000
+ function detectMinimizedDockStackPromotion(previous, next) {
6001
+ const previousVisibleNodeIds = new Set(
6002
+ previous.filter(
6003
+ (slot) => slot.kind === "node"
6004
+ ).map((slot) => slot.node.id)
6005
+ );
6006
+ const previousStackNodeIds = previous.find((slot) => slot.kind === "stack")?.nodes.map((node) => node.id) ?? [];
6007
+ for (const slot of next) {
6008
+ if (slot.kind !== "node") {
6009
+ continue;
6010
+ }
6011
+ if (!previousVisibleNodeIds.has(slot.node.id) && previousStackNodeIds.includes(slot.node.id)) {
6012
+ return slot.node.id;
6013
+ }
6014
+ }
6015
+ return null;
6016
+ }
6017
+ function useMinimizedDockStackPromotion(slots) {
6018
+ const previousSlotsRef = useRef6(slots);
6019
+ const promotionTimerRef = useRef6(null);
6020
+ const [promotedNodeId, setPromotedNodeId] = useState6(null);
6021
+ const [stackDispatching, setStackDispatching] = useState6(false);
6022
+ useEffect7(() => {
6023
+ const promotedNodeId2 = detectMinimizedDockStackPromotion(
6024
+ previousSlotsRef.current,
6025
+ slots
6026
+ );
6027
+ previousSlotsRef.current = slots;
6028
+ if (!promotedNodeId2) {
6029
+ return void 0;
6030
+ }
6031
+ setPromotedNodeId(promotedNodeId2);
6032
+ setStackDispatching(true);
6033
+ if (promotionTimerRef.current !== null) {
6034
+ clearTimeout(promotionTimerRef.current);
6035
+ }
6036
+ promotionTimerRef.current = setTimeout(() => {
6037
+ promotionTimerRef.current = null;
6038
+ setPromotedNodeId(null);
6039
+ setStackDispatching(false);
6040
+ }, minimizedDockStackPromotionDurationMs);
6041
+ return () => {
6042
+ if (promotionTimerRef.current !== null) {
6043
+ clearTimeout(promotionTimerRef.current);
6044
+ promotionTimerRef.current = null;
6045
+ }
6046
+ };
6047
+ }, [slots]);
6048
+ return { promotedNodeId, stackDispatching };
6049
+ }
6050
+
5830
6051
  // src/host/WorkbenchHostDockPopup.tsx
5831
6052
  import {
5832
6053
  forwardRef,
5833
6054
  useCallback as useCallback9,
5834
- useEffect as useEffect7,
5835
- useRef as useRef6,
5836
- useState as useState6
6055
+ useEffect as useEffect8,
6056
+ useRef as useRef7,
6057
+ useState as useState7
5837
6058
  } from "react";
5838
6059
  import { createPortal as createPortal2 } from "react-dom";
5839
6060
  import { Button as Button3, CloseIcon, FileCreateIcon, cn } from "@tutti-os/ui-system";
@@ -5845,7 +6066,7 @@ var minimizedStackCardWidthPx = 118;
5845
6066
  var minimizedStackLeftPlacementGapPx = 4;
5846
6067
  var minimizedStackLeftDockClearancePx = 2;
5847
6068
  var workbenchLeftDockLeftInsetPx = 4;
5848
- var workbenchLeftDockViewportWidthPx = 104;
6069
+ var workbenchLeftDockViewportWidthPx = 124;
5849
6070
  var minimizedStackLeftGutterMinPx = 4;
5850
6071
  var minimizedStackLeftPopupNudgeLeftPx = 60;
5851
6072
  var minimizedStackLeftPopupNudgeDownPx = 32;
@@ -5974,7 +6195,7 @@ var dockPopupGridGapPx = 8;
5974
6195
  var dockPopupPanelPaddingInlinePx = 12;
5975
6196
  var dockPopupPanelBorderInlinePx = 2;
5976
6197
  var dockPopupPlacementGapPx = 14;
5977
- var dockPopupMinimizedStackLaunchDisappearMs = 120;
6198
+ var dockPopupMinimizedStackLaunchDisappearMs = 0;
5978
6199
  var dockPopupMinimizedStackPopupZIndex = 100300;
5979
6200
  var popupCardMagnificationRange = 160;
5980
6201
  var popupCardMaxScale = 1.16;
@@ -6036,11 +6257,11 @@ function WorkbenchHostDockPopup({
6036
6257
  const resolvedVariant = variant ?? "default";
6037
6258
  const isMinimizedStack = resolvedVariant === "minimized-stack";
6038
6259
  const createCardCount = showCreateNew === false ? 0 : 1;
6039
- const cardElementsRef = useRef6(/* @__PURE__ */ new Map());
6040
- const minimizedStackViewportRef = useRef6(null);
6041
- const [pointer, setPointer] = useState6(null);
6042
- const [minimizedStackScrollOffset, setMinimizedStackScrollOffset] = useState6(0);
6043
- const [capturedPreviewByNodeId, setCapturedPreviewByNodeId] = useState6({});
6260
+ const cardElementsRef = useRef7(/* @__PURE__ */ new Map());
6261
+ const minimizedStackViewportRef = useRef7(null);
6262
+ const [pointer, setPointer] = useState7(null);
6263
+ const [minimizedStackScrollOffset, setMinimizedStackScrollOffset] = useState7(0);
6264
+ const [capturedPreviewByNodeId, setCapturedPreviewByNodeId] = useState7({});
6044
6265
  const columnCount = Math.min(Math.max(items.length + createCardCount, 1), 3);
6045
6266
  const popupWidthPx = columnCount * dockPopupCardWidthPx + Math.max(0, columnCount - 1) * dockPopupGridGapPx + dockPopupPanelPaddingInlinePx * 2 + dockPopupPanelBorderInlinePx;
6046
6267
  const popupCenterY = anchorRect.top + anchorRect.height / 2;
@@ -6118,7 +6339,7 @@ function WorkbenchHostDockPopup({
6118
6339
  },
6119
6340
  []
6120
6341
  );
6121
- useEffect7(() => {
6342
+ useEffect8(() => {
6122
6343
  if (!isLeftMinimizedStack) {
6123
6344
  return;
6124
6345
  }
@@ -6130,7 +6351,7 @@ function WorkbenchHostDockPopup({
6130
6351
  document.body.removeAttribute("data-desktop-dock-minimized-stack-open");
6131
6352
  };
6132
6353
  }, [isLeftMinimizedStack]);
6133
- useEffect7(() => {
6354
+ useEffect8(() => {
6134
6355
  const handlePointerDown = (event) => {
6135
6356
  if (!(event.target instanceof Element)) {
6136
6357
  onClose();
@@ -6156,13 +6377,13 @@ function WorkbenchHostDockPopup({
6156
6377
  };
6157
6378
  }, [onClose]);
6158
6379
  const previewCaptureKey = items.map((item) => `${item.node.id}:${item.previewImageUrl ?? ""}`).join("|");
6159
- useEffect7(() => {
6380
+ useEffect8(() => {
6160
6381
  if (!isMinimizedStack) {
6161
6382
  return;
6162
6383
  }
6163
6384
  setMinimizedStackScrollOffset(initialMinimizedStackScrollOffset);
6164
6385
  }, [initialMinimizedStackScrollOffset, isMinimizedStack, items.length]);
6165
- useEffect7(() => {
6386
+ useEffect8(() => {
6166
6387
  if (!isMinimizedStack) {
6167
6388
  return;
6168
6389
  }
@@ -6183,7 +6404,7 @@ function WorkbenchHostDockPopup({
6183
6404
  viewport.addEventListener("wheel", handleWheel, { passive: false });
6184
6405
  return () => viewport.removeEventListener("wheel", handleWheel);
6185
6406
  }, [isMinimizedStack, minimizedStackMaxScrollOffset]);
6186
- useEffect7(() => {
6407
+ useEffect8(() => {
6187
6408
  let cancelled = false;
6188
6409
  const missingItems = items.filter(
6189
6410
  (item) => !item.previewImageUrl && !capturedPreviewByNodeId[item.node.id]
@@ -6404,9 +6625,9 @@ var WorkbenchHostDockPopupCard = forwardRef(function WorkbenchHostDockPopupCard2
6404
6625
  }, ref) {
6405
6626
  const title = item.title?.trim() || item.node.title;
6406
6627
  const isMinimizedStack = variant === "minimized-stack";
6407
- const [isLaunching, setIsLaunching] = useState6(false);
6408
- const launchTimerRef = useRef6(null);
6409
- useEffect7(
6628
+ const [isLaunching, setIsLaunching] = useState7(false);
6629
+ const launchTimerRef = useRef7(null);
6630
+ useEffect8(
6410
6631
  () => () => {
6411
6632
  if (launchTimerRef.current !== null) {
6412
6633
  clearTimeout(launchTimerRef.current);
@@ -6530,7 +6751,12 @@ function stripDockDescriptionTerminalPunctuation(value) {
6530
6751
  return trimmed.replace(/[。..]+$/u, "");
6531
6752
  }
6532
6753
  function isDockVisualMutationActive(element) {
6533
- return element?.hasAttribute("data-dock-pointer-active") === true || element?.hasAttribute("data-dock-hover-panel-open") === true;
6754
+ if (!element) {
6755
+ return false;
6756
+ }
6757
+ return element.hasAttribute("data-dock-pointer-active") || element.hasAttribute("data-dock-hover-panel-open") || element.querySelector(
6758
+ '[data-collapsing="true"], [data-presence="entering"], [data-presence="exiting"], [data-stack-dispatching="true"], [data-promoted-from-stack="true"]'
6759
+ ) !== null;
6534
6760
  }
6535
6761
  function WorkbenchHostDock({
6536
6762
  context,
@@ -6550,45 +6776,49 @@ function WorkbenchHostDock({
6550
6776
  () => new Set(context.minimizedNodes.map((node) => node.id)),
6551
6777
  [context.minimizedNodes]
6552
6778
  );
6553
- const dockMeasureRef = useRef7(null);
6554
- const dockItemsRef = useRef7(null);
6555
- const hoverPanelRef = useRef7(null);
6556
- const hoverPanelCloseTimerRef = useRef7(
6779
+ const dockMeasureRef = useRef8(null);
6780
+ const dockItemsRef = useRef8(null);
6781
+ const hoverPanelRef = useRef8(null);
6782
+ const hoverPanelCloseTimerRef = useRef8(
6557
6783
  null
6558
6784
  );
6559
- const hoverPanelOpenTimerRef = useRef7(
6785
+ const hoverPanelOpenTimerRef = useRef8(
6560
6786
  null
6561
6787
  );
6562
- const hoverPanelScheduledPointRef = useRef7(null);
6563
- const minimizedLaunchTimersRef = useRef7(
6564
- /* @__PURE__ */ new Map()
6565
- );
6566
- const hoverPanelRestTargetRef = useRef7(null);
6567
- const activeHoverPanelRef = useRef7(
6788
+ const hoverPanelScheduledPointRef = useRef8(null);
6789
+ const hoverPanelRestTargetRef = useRef8(null);
6790
+ const activeHoverPanelRef = useRef8(
6568
6791
  null
6569
6792
  );
6570
- const pendingDockStateRefreshRef = useRef7(false);
6571
- const slotRefs = useRef7(/* @__PURE__ */ new Map());
6572
- const previousAttentionTokenByEntryId = useRef7(/* @__PURE__ */ new Map());
6573
- const attentionTimeouts = useRef7(
6793
+ const pendingDockStateRefreshRef = useRef8(false);
6794
+ const slotRefs = useRef8(/* @__PURE__ */ new Map());
6795
+ const previousAttentionTokenByEntryId = useRef8(/* @__PURE__ */ new Map());
6796
+ const attentionTimeouts = useRef8(
6574
6797
  /* @__PURE__ */ new Map()
6575
6798
  );
6576
- const [activeAttentionEntryIds, setActiveAttentionEntryIds] = useState7(/* @__PURE__ */ new Set());
6577
- const [activePopup, setActivePopup] = useState7(null);
6578
- const [activeHoverPanel, setActiveHoverPanel] = useState7(null);
6579
- const [activeMinimizedStackPopup, setActiveMinimizedStackPopup] = useState7(null);
6580
- const [pendingActionKeys, setPendingActionKeys] = useState7(
6799
+ const [activeAttentionEntryIds, setActiveAttentionEntryIds] = useState8(/* @__PURE__ */ new Set());
6800
+ const [activePopup, setActivePopup] = useState8(null);
6801
+ const [activeHoverPanel, setActiveHoverPanel] = useState8(null);
6802
+ const [activeMinimizedStackPopup, setActiveMinimizedStackPopup] = useState8(null);
6803
+ const [pendingActionKeys, setPendingActionKeys] = useState8(
6581
6804
  () => /* @__PURE__ */ new Set()
6582
6805
  );
6583
- const [dockFrameSize, setDockFrameSize] = useState7(null);
6806
+ const [dockFrameSize, setDockFrameSize] = useState8(null);
6584
6807
  const { triggerDockBounce } = useDockBounce(slotRefs);
6585
- const [dockScrollState, setDockScrollState] = useState7(() => ({
6808
+ const [dockScrollState, setDockScrollState] = useState8(() => ({
6586
6809
  canScrollBackward: false,
6587
6810
  canScrollForward: false,
6588
6811
  hasOverflow: false
6589
6812
  }));
6590
- const [dockStateRevision, setDockStateRevision] = useState7(0);
6591
- const [externalStateRevision, setExternalStateRevision] = useState7(0);
6813
+ const [dockStateRevision, setDockStateRevision] = useState8(0);
6814
+ const [externalStateRevision, setExternalStateRevision] = useState8(0);
6815
+ const [
6816
+ collapsingMinimizedLaunchAnchorKeys,
6817
+ setCollapsingMinimizedLaunchAnchorKeys
6818
+ ] = useState8(() => /* @__PURE__ */ new Set());
6819
+ const collapsingMinimizedLaunchTimerRef = useRef8(
6820
+ /* @__PURE__ */ new Map()
6821
+ );
6592
6822
  const clearHoverPanelCloseTimer = useCallback10(() => {
6593
6823
  if (hoverPanelCloseTimerRef.current === null) {
6594
6824
  return;
@@ -6604,17 +6834,47 @@ function WorkbenchHostDock({
6604
6834
  hoverPanelOpenTimerRef.current = null;
6605
6835
  hoverPanelScheduledPointRef.current = null;
6606
6836
  }, []);
6607
- useEffect8(
6837
+ useEffect9(
6608
6838
  () => () => {
6609
6839
  clearHoverPanelCloseTimer();
6610
6840
  clearHoverPanelOpenTimer();
6611
- for (const timer of minimizedLaunchTimersRef.current.values()) {
6841
+ for (const timer of collapsingMinimizedLaunchTimerRef.current.values()) {
6612
6842
  clearTimeout(timer);
6613
6843
  }
6614
- minimizedLaunchTimersRef.current.clear();
6844
+ collapsingMinimizedLaunchTimerRef.current.clear();
6615
6845
  },
6616
6846
  [clearHoverPanelCloseTimer, clearHoverPanelOpenTimer]
6617
6847
  );
6848
+ const clearCollapsingMinimizedLaunch = useCallback10((anchorKey) => {
6849
+ const timer = collapsingMinimizedLaunchTimerRef.current.get(anchorKey);
6850
+ if (timer) {
6851
+ clearTimeout(timer);
6852
+ collapsingMinimizedLaunchTimerRef.current.delete(anchorKey);
6853
+ }
6854
+ setCollapsingMinimizedLaunchAnchorKeys((current) => {
6855
+ if (!current.has(anchorKey)) {
6856
+ return current;
6857
+ }
6858
+ const next = new Set(current);
6859
+ next.delete(anchorKey);
6860
+ return next;
6861
+ });
6862
+ }, []);
6863
+ const scheduleCollapsingMinimizedLaunchClear = useCallback10(
6864
+ (anchorKey) => {
6865
+ const existing = collapsingMinimizedLaunchTimerRef.current.get(anchorKey);
6866
+ if (existing) {
6867
+ clearTimeout(existing);
6868
+ }
6869
+ collapsingMinimizedLaunchTimerRef.current.set(
6870
+ anchorKey,
6871
+ setTimeout(() => {
6872
+ clearCollapsingMinimizedLaunch(anchorKey);
6873
+ }, minimizedDockSlotLayoutAnimationMs)
6874
+ );
6875
+ },
6876
+ [clearCollapsingMinimizedLaunch]
6877
+ );
6618
6878
  useLayoutEffect4(() => {
6619
6879
  const element = dockMeasureRef.current;
6620
6880
  if (!element || typeof window === "undefined") {
@@ -6659,7 +6919,7 @@ function WorkbenchHostDock({
6659
6919
  pendingDockStateRefreshRef.current = false;
6660
6920
  setDockStateRevision((revision) => revision + 1);
6661
6921
  }, []);
6662
- useEffect8(() => {
6922
+ useEffect9(() => {
6663
6923
  if (!dockStateSource) {
6664
6924
  return void 0;
6665
6925
  }
@@ -6693,6 +6953,7 @@ function WorkbenchHostDock({
6693
6953
  }),
6694
6954
  [context.nodes, nodeDefinitions]
6695
6955
  );
6956
+ const { promotedNodeId, stackDispatching } = useMinimizedDockStackPromotion(minimizedDockSlots);
6696
6957
  const dockItems = useMemo5(
6697
6958
  () => createWorkbenchHostDockItems({
6698
6959
  minimizedDockSlots,
@@ -6700,7 +6961,10 @@ function WorkbenchHostDock({
6700
6961
  }),
6701
6962
  [minimizedDockSlots, resolvedEntries]
6702
6963
  );
6703
- const presentDockItems = useDockPresenceItems(dockItems);
6964
+ const presentDockItems = useDockPresenceItems(
6965
+ dockItems,
6966
+ (nodeId) => context.genie.shouldAnimateMinimizedDockEnter(nodeId)
6967
+ );
6704
6968
  const dockWidth = useMemo5(
6705
6969
  () => resolveWorkbenchHostDockItemsWidth(dockItems),
6706
6970
  [dockItems]
@@ -6726,7 +6990,7 @@ function WorkbenchHostDock({
6726
6990
  }
6727
6991
  dockMeasureRef.current?.removeAttribute("data-dock-hover-panel-open");
6728
6992
  }, []);
6729
- useEffect8(() => {
6993
+ useEffect9(() => {
6730
6994
  activeHoverPanelRef.current = activeHoverPanel;
6731
6995
  }, [activeHoverPanel]);
6732
6996
  const closeHoverPanelImmediate = useCallback10(
@@ -6857,6 +7121,7 @@ function WorkbenchHostDock({
6857
7121
  hoverPanelRestTargetRef.current = null;
6858
7122
  clearHoverPanelOpenTimer();
6859
7123
  closeHoverPanelImmediate();
7124
+ pauseDockMagnification();
6860
7125
  if (!anchorKey) {
6861
7126
  return false;
6862
7127
  }
@@ -6864,6 +7129,7 @@ function WorkbenchHostDock({
6864
7129
  if (!slotElement) {
6865
7130
  return false;
6866
7131
  }
7132
+ clearSlotMagnification(anchorKey);
6867
7133
  if (slotElement.dataset.collapsing === "true") {
6868
7134
  return true;
6869
7135
  }
@@ -6879,25 +7145,25 @@ function WorkbenchHostDock({
6879
7145
  slotElement.dataset.collapsing = "true";
6880
7146
  return true;
6881
7147
  },
6882
- [clearHoverPanelOpenTimer, closeHoverPanelImmediate]
7148
+ [
7149
+ clearHoverPanelOpenTimer,
7150
+ clearSlotMagnification,
7151
+ closeHoverPanelImmediate,
7152
+ pauseDockMagnification
7153
+ ]
6883
7154
  );
6884
7155
  const runDockMinimizedLaunchAfterCollapse = useCallback10(
6885
7156
  (anchorKey, launch) => {
6886
- const shouldDelayLaunch = beginDockMinimizedInteraction(anchorKey);
6887
- if (!shouldDelayLaunch) {
6888
- launch();
6889
- return;
6890
- }
6891
- if (minimizedLaunchTimersRef.current.has(anchorKey)) {
6892
- return;
6893
- }
6894
- const timer = setTimeout(() => {
6895
- minimizedLaunchTimersRef.current.delete(anchorKey);
6896
- launch();
6897
- }, dockMinimizedSlotCollapseLaunchDelayMs);
6898
- minimizedLaunchTimersRef.current.set(anchorKey, timer);
7157
+ beginDockMinimizedInteraction(anchorKey);
7158
+ setCollapsingMinimizedLaunchAnchorKeys((current) => {
7159
+ const next = new Set(current);
7160
+ next.add(anchorKey);
7161
+ return next;
7162
+ });
7163
+ scheduleCollapsingMinimizedLaunchClear(anchorKey);
7164
+ launch();
6899
7165
  },
6900
- [beginDockMinimizedInteraction]
7166
+ [beginDockMinimizedInteraction, scheduleCollapsingMinimizedLaunchClear]
6901
7167
  );
6902
7168
  const handleDockPointerTravel = useCallback10(
6903
7169
  (clientX, clientY) => {
@@ -6982,7 +7248,7 @@ function WorkbenchHostDock({
6982
7248
  )
6983
7249
  )
6984
7250
  );
6985
- useEffect8(() => {
7251
+ useEffect9(() => {
6986
7252
  const shouldSubscribe = activePopup !== null || hasMinimizedPreviewCapture;
6987
7253
  if (!shouldSubscribe || !externalStateSource?.subscribe) {
6988
7254
  return void 0;
@@ -6991,7 +7257,7 @@ function WorkbenchHostDock({
6991
7257
  setExternalStateRevision((revision) => revision + 1);
6992
7258
  });
6993
7259
  }, [activePopup, externalStateSource, hasMinimizedPreviewCapture]);
6994
- useEffect8(() => {
7260
+ useEffect9(() => {
6995
7261
  const nextAttentionIds = /* @__PURE__ */ new Set();
6996
7262
  for (const entry of renderedDockEntries) {
6997
7263
  const nextToken = entry.attentionToken ?? null;
@@ -7032,7 +7298,7 @@ function WorkbenchHostDock({
7032
7298
  );
7033
7299
  }
7034
7300
  }, [renderedDockEntries]);
7035
- useEffect8(
7301
+ useEffect9(
7036
7302
  () => () => {
7037
7303
  for (const timeout of attentionTimeouts.current.values()) {
7038
7304
  globalThis.clearTimeout(timeout);
@@ -7206,6 +7472,7 @@ function WorkbenchHostDock({
7206
7472
  "aria-disabled": clickResolution.kind === "blocked" && !resolvedEntry.hasMatchingNodes,
7207
7473
  className: "desktop-dock__btn",
7208
7474
  "data-interactive": clickResolution.kind === "blocked" && !resolvedEntry.hasMatchingNodes ? "false" : "true",
7475
+ title: entry.label,
7209
7476
  type: "button",
7210
7477
  onPointerDown: () => {
7211
7478
  if (clickResolution.kind === "blocked" && !resolvedEntry.hasMatchingNodes) {
@@ -7352,17 +7619,7 @@ function WorkbenchHostDock({
7352
7619
  closeHoverPanelImmediate(entry.id);
7353
7620
  handleDockPointerLeave();
7354
7621
  },
7355
- children: hasHoverPanel ? dockButton2 : /* @__PURE__ */ jsxs6(Tooltip, { children: [
7356
- /* @__PURE__ */ jsx10(TooltipTrigger, { asChild: true, children: dockButton2 }),
7357
- /* @__PURE__ */ jsx10(
7358
- TooltipContent,
7359
- {
7360
- side: dockPlacement === "left" ? "right" : "top",
7361
- sideOffset: 26,
7362
- children: entry.label
7363
- }
7364
- )
7365
- ] })
7622
+ children: dockButton2
7366
7623
  },
7367
7624
  dockItem.key
7368
7625
  );
@@ -7378,6 +7635,7 @@ function WorkbenchHostDock({
7378
7635
  "aria-label": i18n.t("minimizedWindows"),
7379
7636
  className: "desktop-dock__btn desktop-dock__minimized-btn",
7380
7637
  "data-interactive": "true",
7638
+ title: i18n.t("minimizedWindows"),
7381
7639
  type: "button",
7382
7640
  onPointerDown: () => {
7383
7641
  beginDockMinimizedInteraction();
@@ -7453,17 +7711,8 @@ function WorkbenchHostDock({
7453
7711
  "data-popup-active": stackPopupActive,
7454
7712
  "data-presence": dockItem.presence,
7455
7713
  "data-section-id": "minimized",
7456
- children: /* @__PURE__ */ jsxs6(Tooltip, { children: [
7457
- /* @__PURE__ */ jsx10(TooltipTrigger, { asChild: true, children: stackButton }),
7458
- /* @__PURE__ */ jsx10(
7459
- TooltipContent,
7460
- {
7461
- side: dockPlacement === "left" ? "right" : "top",
7462
- sideOffset: 26,
7463
- children: i18n.t("minimizedWindows")
7464
- }
7465
- )
7466
- ] })
7714
+ "data-stack-dispatching": stackDispatching ? "true" : void 0,
7715
+ children: stackButton
7467
7716
  },
7468
7717
  dockItem.key
7469
7718
  );
@@ -7475,6 +7724,7 @@ function WorkbenchHostDock({
7475
7724
  "aria-label": i18n.t("launch", { title: node.title }),
7476
7725
  className: "desktop-dock__btn desktop-dock__minimized-btn",
7477
7726
  "data-interactive": "true",
7727
+ title: node.title,
7478
7728
  type: "button",
7479
7729
  onPointerDown: () => {
7480
7730
  beginDockMinimizedInteraction(slot.anchorKey);
@@ -7510,22 +7760,14 @@ function WorkbenchHostDock({
7510
7760
  {
7511
7761
  ref: registerDockSlot(slot.anchorKey),
7512
7762
  className: "desktop-dock__slot desktop-dock__slot--minimized",
7763
+ "data-collapsing": collapsingMinimizedLaunchAnchorKeys.has(slot.anchorKey) ? "true" : void 0,
7513
7764
  "data-desktop-dock-anchor-key": slot.anchorKey,
7514
7765
  "data-desktop-dock-slot": "true",
7515
7766
  "data-node-state": "minimized",
7516
7767
  "data-presence": dockItem.presence,
7768
+ "data-promoted-from-stack": promotedNodeId === node.id ? "true" : void 0,
7517
7769
  "data-section-id": "minimized",
7518
- children: /* @__PURE__ */ jsxs6(Tooltip, { children: [
7519
- /* @__PURE__ */ jsx10(TooltipTrigger, { asChild: true, children: dockButton }),
7520
- /* @__PURE__ */ jsx10(
7521
- TooltipContent,
7522
- {
7523
- side: dockPlacement === "left" ? "right" : "top",
7524
- sideOffset: 26,
7525
- children: node.title
7526
- }
7527
- )
7528
- ] })
7770
+ children: dockButton
7529
7771
  },
7530
7772
  dockItem.key
7531
7773
  );
@@ -7698,13 +7940,15 @@ function WorkbenchHostDock({
7698
7940
  onCreateNew: () => void 0,
7699
7941
  onSelectNode: (nodeId) => {
7700
7942
  closePopup();
7701
- context.genie.launchNodeFromAnchor(
7702
- activeMinimizedStackSlot.anchorKey,
7943
+ const anchorKey = resolveWorkbenchMinimizedDockAnchorKeyForNode({
7703
7944
  nodeId,
7704
- () => {
7945
+ slots: minimizedDockSlots
7946
+ }) ?? activeMinimizedStackSlot.anchorKey;
7947
+ runDockMinimizedLaunchAfterCollapse(anchorKey, () => {
7948
+ context.genie.launchNodeFromAnchor(anchorKey, nodeId, () => {
7705
7949
  host.focusNode(nodeId);
7706
- }
7707
- );
7950
+ });
7951
+ });
7708
7952
  },
7709
7953
  showCreateNew: false,
7710
7954
  resolveDockPreviewCacheKey: (node) => resolveDockPreviewCacheKey(workspaceId, node),
@@ -7722,10 +7966,10 @@ function WorkbenchHostDockMinimizedNodePreview({
7722
7966
  node,
7723
7967
  workspaceId
7724
7968
  }) {
7725
- const [previewImageUrl, setPreviewImageUrl] = useState7(
7969
+ const [previewImageUrl, setPreviewImageUrl] = useState8(
7726
7970
  () => readCachedWorkbenchNodePreviewImage(node.id)
7727
7971
  );
7728
- useEffect8(() => {
7972
+ useEffect9(() => {
7729
7973
  let cancelled = false;
7730
7974
  const cachedPreviewImageUrl = readCachedWorkbenchNodePreviewImage(node.id);
7731
7975
  setPreviewImageUrl(cachedPreviewImageUrl);
@@ -7980,19 +8224,48 @@ function createWorkbenchHostDockItems({
7980
8224
  }
7981
8225
  return items;
7982
8226
  }
7983
- function useDockPresenceItems(items) {
7984
- const latestItemsByKey = useRef7(/* @__PURE__ */ new Map());
8227
+ function resolveMinimizedDockItemNodeId(item) {
8228
+ if (item.kind !== "minimized" || item.slot.kind !== "node") {
8229
+ return null;
8230
+ }
8231
+ return item.slot.node.id;
8232
+ }
8233
+ function resolveNextDockItemPresence(item, initialized, previousPresence, shouldAnimateMinimizedDockEnter) {
8234
+ if (!initialized) {
8235
+ return "present";
8236
+ }
8237
+ if (item.key === "separator:minimized") {
8238
+ return "present";
8239
+ }
8240
+ if (item.kind === "minimized") {
8241
+ const nodeId = resolveMinimizedDockItemNodeId(item);
8242
+ if (nodeId && shouldAnimateMinimizedDockEnter(nodeId)) {
8243
+ if (previousPresence === "exiting") {
8244
+ return "entering";
8245
+ }
8246
+ return previousPresence ?? "entering";
8247
+ }
8248
+ return "present";
8249
+ }
8250
+ if (previousPresence === "exiting") {
8251
+ return "entering";
8252
+ }
8253
+ return previousPresence ?? "entering";
8254
+ }
8255
+ function useDockPresenceItems(items, shouldAnimateMinimizedDockEnter) {
8256
+ const latestItemsByKey = useRef8(/* @__PURE__ */ new Map());
7985
8257
  latestItemsByKey.current = new Map(items.map((item) => [item.key, item]));
7986
8258
  const itemKeys = items.map((item) => item.key).join("\0");
7987
- const [presentItems, setPresentItems] = useState7(
8259
+ const [presentItems, setPresentItems] = useState8(
7988
8260
  () => items.map((item) => ({
7989
8261
  item,
7990
8262
  key: item.key,
7991
8263
  presence: "present"
7992
8264
  }))
7993
8265
  );
7994
- const initialized = useRef7(false);
7995
- useEffect8(() => {
8266
+ const initialized = useRef8(false);
8267
+ useEffect9(() => {
8268
+ let nextSettleMs = dockPresenceAnimationMs;
7996
8269
  setPresentItems((current) => {
7997
8270
  const nextSourceItems = [...latestItemsByKey.current.values()];
7998
8271
  const currentByKey = new Map(current.map((item) => [item.key, item]));
@@ -8033,7 +8306,12 @@ function useDockPresenceItems(items) {
8033
8306
  nextItems.push({
8034
8307
  item,
8035
8308
  key: item.key,
8036
- presence: initialized.current ? currentByKey.get(item.key)?.presence === "exiting" ? "entering" : currentByKey.get(item.key)?.presence ?? "entering" : "present"
8309
+ presence: resolveNextDockItemPresence(
8310
+ item,
8311
+ initialized.current,
8312
+ currentByKey.get(item.key)?.presence,
8313
+ shouldAnimateMinimizedDockEnter
8314
+ )
8037
8315
  });
8038
8316
  }
8039
8317
  for (const currentItem of current) {
@@ -8048,9 +8326,15 @@ function useDockPresenceItems(items) {
8048
8326
  });
8049
8327
  }
8050
8328
  }
8051
- return nextItems.filter(
8329
+ const filteredItems = nextItems.filter(
8052
8330
  (item) => nextByKey.has(item.key) || item.presence === "exiting"
8053
8331
  );
8332
+ if (filteredItems.some(
8333
+ (item) => item.item.kind === "minimized" && (item.presence === "entering" || item.presence === "exiting")
8334
+ )) {
8335
+ nextSettleMs = minimizedDockSlotLayoutAnimationMs;
8336
+ }
8337
+ return filteredItems;
8054
8338
  });
8055
8339
  initialized.current = true;
8056
8340
  const timeout = globalThis.setTimeout(() => {
@@ -8059,18 +8343,17 @@ function useDockPresenceItems(items) {
8059
8343
  (item) => item.presence === "entering" ? { ...item, presence: "present" } : item
8060
8344
  )
8061
8345
  );
8062
- }, dockPresenceAnimationMs);
8346
+ }, nextSettleMs);
8063
8347
  return () => globalThis.clearTimeout(timeout);
8064
- }, [itemKeys]);
8348
+ }, [itemKeys, shouldAnimateMinimizedDockEnter]);
8065
8349
  return presentItems.map((presentItem) => {
8066
8350
  const latestItem = latestItemsByKey.current.get(presentItem.key);
8067
8351
  return latestItem ? { ...presentItem, item: latestItem } : presentItem;
8068
8352
  });
8069
8353
  }
8070
8354
  var DOCK_BOUNCE_MS = 600;
8071
- var dockMinimizedSlotCollapseLaunchDelayMs = 260;
8072
8355
  function useDockBounce(slotRefs) {
8073
- const timeoutsRef = useRef7(/* @__PURE__ */ new Map());
8356
+ const timeoutsRef = useRef8(/* @__PURE__ */ new Map());
8074
8357
  const clearDockBounce = useCallback10(
8075
8358
  (anchorKey) => {
8076
8359
  const timeout = timeoutsRef.current.get(anchorKey);
@@ -8085,7 +8368,7 @@ function useDockBounce(slotRefs) {
8085
8368
  },
8086
8369
  [slotRefs]
8087
8370
  );
8088
- useEffect8(
8371
+ useEffect9(
8089
8372
  () => () => {
8090
8373
  for (const anchorKey of timeoutsRef.current.keys()) {
8091
8374
  clearDockBounce(anchorKey);
@@ -8131,6 +8414,7 @@ var dockHoverPanelOpenDelayMs = 450;
8131
8414
  var dockHoverPanelHitSlopPx = 12;
8132
8415
  var dockHoverPanelPointerRestTolerancePx = 4;
8133
8416
  var dockPresenceAnimationMs = 300;
8417
+ var minimizedDockSlotLayoutAnimationMs = 720;
8134
8418
  var dockItemsGapPx = 10.8;
8135
8419
  var dockItemsHorizontalPaddingPx = 12.6;
8136
8420
  var dockSeparatorOuterWidthPx = 8.1;
@@ -8644,6 +8928,7 @@ export {
8644
8928
  createWorkbenchSnapshotFromState,
8645
8929
  createWorkbenchStateFromSnapshot,
8646
8930
  createWorkbenchWindowChromeI18nRuntime,
8931
+ getWorkbenchLayoutFrame,
8647
8932
  resolveWorkbenchHostPrepareClose,
8648
8933
  useWorkbenchSelector,
8649
8934
  workbenchFocusInputActivationType,