@tutti-os/workbench-surface 0.0.6 → 0.0.7

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
@@ -1639,7 +1639,7 @@ function areWorkbenchDockNodesEqual(previousNodes, nextNodes) {
1639
1639
  }
1640
1640
  return previousNodes.every((previousNode, index) => {
1641
1641
  const nextNode = nextNodes[index];
1642
- return nextNode !== void 0 && previousNode.id === nextNode.id && previousNode.kind === nextNode.kind && previousNode.title === nextNode.title && previousNode.displayMode === nextNode.displayMode && previousNode.isMinimized === nextNode.isMinimized && previousNode.minimizedAtUnixMs === nextNode.minimizedAtUnixMs && previousNode.data === nextNode.data;
1642
+ return nextNode !== void 0 && previousNode.id === nextNode.id && previousNode.kind === nextNode.kind && previousNode.title === nextNode.title && previousNode.displayMode === nextNode.displayMode && previousNode.isMinimized === nextNode.isMinimized && previousNode.minimizedAtUnixMs === nextNode.minimizedAtUnixMs && previousNode.frame.width === nextNode.frame.width && previousNode.frame.height === nextNode.frame.height && previousNode.data === nextNode.data;
1643
1643
  });
1644
1644
  }
1645
1645
 
@@ -3132,9 +3132,14 @@ function useWorkbenchGenieAnimation({
3132
3132
  const launchNodeFromAnchor = useCallback6(
3133
3133
  (anchorKey, nodeID, launch) => {
3134
3134
  const target = controller.getSnapshot().nodes.find((node) => node.id === nodeID);
3135
- const shouldAnimate = target?.isMinimized === true || !target;
3136
- if (!shouldAnimate) {
3137
- launch();
3135
+ if (!target) {
3136
+ void Promise.resolve(launch()).catch(() => {
3137
+ });
3138
+ return;
3139
+ }
3140
+ if (target.isMinimized !== true) {
3141
+ void Promise.resolve(launch()).catch(() => {
3142
+ });
3138
3143
  return;
3139
3144
  }
3140
3145
  stopAnimation();
@@ -3148,7 +3153,7 @@ function useWorkbenchGenieAnimation({
3148
3153
  };
3149
3154
  const generation = animationGenerationRef.current;
3150
3155
  flushSync(() => {
3151
- launch();
3156
+ void launch();
3152
3157
  });
3153
3158
  void startOpenOrRestoreAnimation(
3154
3159
  nodeID,
@@ -4439,6 +4444,7 @@ function sanitizeMetadata(metadata) {
4439
4444
  // src/host/session.ts
4440
4445
  var initializedMetadataKey = "workbenchHostInitialized";
4441
4446
  var snapshotSaveDelayMs = 400;
4447
+ var launchDiagnosticTextMaxLength = 800;
4442
4448
  function createWorkbenchHostSession(input) {
4443
4449
  return new WorkbenchHostSessionController(input);
4444
4450
  }
@@ -4638,10 +4644,16 @@ var WorkbenchHostSessionController = class {
4638
4644
  if (launchSource !== null) {
4639
4645
  request.launchSource = launchSource;
4640
4646
  }
4641
- const result = this.input.onLaunchRequest ? await this.input.onLaunchRequest(request) : createDefaultLaunchResult(definition, {
4642
- dockEntryId: input.dockEntryId,
4643
- launchSource
4644
- });
4647
+ let result;
4648
+ try {
4649
+ result = this.input.onLaunchRequest ? await this.input.onLaunchRequest(request) : createDefaultLaunchResult(definition, {
4650
+ dockEntryId: input.dockEntryId,
4651
+ launchSource
4652
+ });
4653
+ } catch (error) {
4654
+ this.logLaunchFailure(request, error);
4655
+ return null;
4656
+ }
4645
4657
  if (this.isDisposed || !result || generation !== this.loadGeneration) {
4646
4658
  return null;
4647
4659
  }
@@ -4650,6 +4662,24 @@ var WorkbenchHostSessionController = class {
4650
4662
  launchSource: result.launchSource ?? launchSource
4651
4663
  });
4652
4664
  }
4665
+ logLaunchFailure(request, error) {
4666
+ void Promise.resolve(
4667
+ this.input.debugDiagnostics?.log?.({
4668
+ details: {
4669
+ dockEntryId: request.dockEntryId ?? null,
4670
+ error: diagnosticErrorDetails(error),
4671
+ launchSource: request.launchSource ?? null,
4672
+ payload: diagnosticValueSummary(request.payload),
4673
+ reason: request.reason,
4674
+ typeId: request.typeId
4675
+ },
4676
+ event: "host.launch.failed",
4677
+ level: "error",
4678
+ source: "workbench-host",
4679
+ workspaceId: request.workspaceId
4680
+ })
4681
+ ).catch(() => void 0);
4682
+ }
4653
4683
  async load() {
4654
4684
  if (this.loadPromise && !this.isDisposed) {
4655
4685
  return this.loadPromise;
@@ -5211,6 +5241,42 @@ function resolveWorkbenchHostLaunchSource(input) {
5211
5241
  return null;
5212
5242
  }
5213
5243
  }
5244
+ function diagnosticErrorDetails(error) {
5245
+ if (error instanceof Error) {
5246
+ return {
5247
+ message: limitLaunchDiagnosticText(error.message),
5248
+ name: error.name,
5249
+ stack: limitLaunchDiagnosticText(error.stack)
5250
+ };
5251
+ }
5252
+ return {
5253
+ message: diagnosticValueSummary(error),
5254
+ name: typeof error
5255
+ };
5256
+ }
5257
+ function diagnosticValueSummary(value) {
5258
+ if (value === void 0) {
5259
+ return null;
5260
+ }
5261
+ if (typeof value === "string") {
5262
+ return limitLaunchDiagnosticText(value) ?? "";
5263
+ }
5264
+ if (value === null || typeof value === "number" || typeof value === "boolean") {
5265
+ return String(value);
5266
+ }
5267
+ try {
5268
+ return limitLaunchDiagnosticText(JSON.stringify(value)) ?? null;
5269
+ } catch {
5270
+ return Object.prototype.toString.call(value);
5271
+ }
5272
+ }
5273
+ function limitLaunchDiagnosticText(value) {
5274
+ const trimmed = value?.trim();
5275
+ if (!trimmed) {
5276
+ return null;
5277
+ }
5278
+ return trimmed.length > launchDiagnosticTextMaxLength ? `${trimmed.slice(0, launchDiagnosticTextMaxLength)}...` : trimmed;
5279
+ }
5214
5280
  function noop() {
5215
5281
  }
5216
5282
 
@@ -5384,7 +5450,7 @@ import { useCallback as useCallback11, useMemo as useMemo6 } from "react";
5384
5450
  import {
5385
5451
  useCallback as useCallback10,
5386
5452
  useEffect as useEffect9,
5387
- useLayoutEffect as useLayoutEffect4,
5453
+ useLayoutEffect as useLayoutEffect5,
5388
5454
  useMemo as useMemo5,
5389
5455
  useRef as useRef8,
5390
5456
  useState as useState8
@@ -6057,6 +6123,7 @@ import {
6057
6123
  forwardRef,
6058
6124
  useCallback as useCallback9,
6059
6125
  useEffect as useEffect8,
6126
+ useLayoutEffect as useLayoutEffect4,
6060
6127
  useRef as useRef7,
6061
6128
  useState as useState7
6062
6129
  } from "react";
@@ -6201,6 +6268,9 @@ var dockPopupPanelBorderInlinePx = 2;
6201
6268
  var dockPopupPlacementGapPx = 14;
6202
6269
  var dockPopupMinimizedStackLaunchDisappearMs = 0;
6203
6270
  var dockPopupMinimizedStackPopupZIndex = 100300;
6271
+ var dockPopupPreviewCacheMaxEntries = 64;
6272
+ var dockPopupPreviewByMemoryKey = /* @__PURE__ */ new Map();
6273
+ var pendingDockPopupPreviewMemoryKeys = /* @__PURE__ */ new Set();
6204
6274
  var popupCardMagnificationRange = 160;
6205
6275
  var popupCardMaxScale = 1.16;
6206
6276
  var popupCardMaxLiftPx = 10;
@@ -6239,9 +6309,24 @@ function resolvePopupFanCardStyle(index, count, placement) {
6239
6309
  "--desktop-dock-popup-fan-y": `${Math.round(arcY)}px`
6240
6310
  };
6241
6311
  }
6312
+ function readDockPopupPreviewImage(memoryKey) {
6313
+ return dockPopupPreviewByMemoryKey.get(memoryKey);
6314
+ }
6315
+ function writeDockPopupPreviewImage(memoryKey, preview, revision) {
6316
+ dockPopupPreviewByMemoryKey.delete(memoryKey);
6317
+ dockPopupPreviewByMemoryKey.set(memoryKey, { preview, revision });
6318
+ while (dockPopupPreviewByMemoryKey.size > dockPopupPreviewCacheMaxEntries) {
6319
+ const oldestMemoryKey = dockPopupPreviewByMemoryKey.keys().next().value;
6320
+ if (typeof oldestMemoryKey !== "string") {
6321
+ break;
6322
+ }
6323
+ dockPopupPreviewByMemoryKey.delete(oldestMemoryKey);
6324
+ }
6325
+ }
6242
6326
  function WorkbenchHostDockPopup({
6243
6327
  anchorRect,
6244
6328
  capturePreview,
6329
+ debugDiagnostics,
6245
6330
  dockPreviewCache,
6246
6331
  items,
6247
6332
  label,
@@ -6262,10 +6347,14 @@ function WorkbenchHostDockPopup({
6262
6347
  const isMinimizedStack = resolvedVariant === "minimized-stack";
6263
6348
  const createCardCount = showCreateNew === false ? 0 : 1;
6264
6349
  const cardElementsRef = useRef7(/* @__PURE__ */ new Map());
6350
+ const cardRefCallbacksRef = useRef7(
6351
+ /* @__PURE__ */ new Map()
6352
+ );
6353
+ const popupRootRef = useRef7(null);
6265
6354
  const minimizedStackViewportRef = useRef7(null);
6266
6355
  const [pointer, setPointer] = useState7(null);
6267
6356
  const [minimizedStackScrollOffset, setMinimizedStackScrollOffset] = useState7(0);
6268
- const [capturedPreviewByNodeId, setCapturedPreviewByNodeId] = useState7({});
6357
+ const [capturedPreviewByMemoryKey, setCapturedPreviewByMemoryKey] = useState7({});
6269
6358
  const columnCount = Math.min(Math.max(items.length + createCardCount, 1), 3);
6270
6359
  const popupWidthPx = columnCount * dockPopupCardWidthPx + Math.max(0, columnCount - 1) * dockPopupGridGapPx + dockPopupPanelPaddingInlinePx * 2 + dockPopupPanelBorderInlinePx;
6271
6360
  const popupCenterY = anchorRect.top + anchorRect.height / 2;
@@ -6333,16 +6422,50 @@ function WorkbenchHostDockPopup({
6333
6422
  } : {}
6334
6423
  } : {}
6335
6424
  };
6336
- const registerCard = useCallback9(
6337
- (nodeId) => (element) => {
6425
+ const popupDiagnosticKey = items.map((item) => item.node.id).join("|");
6426
+ useEffect8(() => {
6427
+ logWorkbenchDockPopupDebug("dock.popup.rendered", debugDiagnostics, {
6428
+ hasCapturePreview: Boolean(capturePreview),
6429
+ itemCount: popupDiagnosticKey ? popupDiagnosticKey.split("|").length : 0,
6430
+ nodeIds: popupDiagnosticKey ? popupDiagnosticKey.split("|") : [],
6431
+ placement,
6432
+ variant: resolvedVariant
6433
+ });
6434
+ }, [
6435
+ capturePreview,
6436
+ debugDiagnostics,
6437
+ placement,
6438
+ popupDiagnosticKey,
6439
+ resolvedVariant
6440
+ ]);
6441
+ useLayoutEffect4(() => {
6442
+ const rootElement = popupRootRef.current;
6443
+ const panelElement = rootElement?.querySelector(
6444
+ "[data-desktop-dock-popup-panel]"
6445
+ ) ?? null;
6446
+ logWorkbenchDockPopupDebug("dock.popup.layout", debugDiagnostics, {
6447
+ panelRect: panelElement ? rectToDiagnostic(panelElement) : null,
6448
+ rootRect: rootElement ? rectToDiagnostic(rootElement) : null,
6449
+ rootStyle: rootElement ? styleToDiagnostic(rootElement) : null,
6450
+ viewportHeight: window.innerHeight,
6451
+ viewportWidth: window.innerWidth
6452
+ });
6453
+ }, [debugDiagnostics, popupDiagnosticKey]);
6454
+ const registerCard = useCallback9((nodeId) => {
6455
+ const existing = cardRefCallbacksRef.current.get(nodeId);
6456
+ if (existing) {
6457
+ return existing;
6458
+ }
6459
+ const callback = (element) => {
6338
6460
  if (element) {
6339
6461
  cardElementsRef.current.set(nodeId, element);
6340
6462
  } else {
6341
6463
  cardElementsRef.current.delete(nodeId);
6342
6464
  }
6343
- },
6344
- []
6345
- );
6465
+ };
6466
+ cardRefCallbacksRef.current.set(nodeId, callback);
6467
+ return callback;
6468
+ }, []);
6346
6469
  useEffect8(() => {
6347
6470
  if (!isLeftMinimizedStack) {
6348
6471
  return;
@@ -6380,7 +6503,9 @@ function WorkbenchHostDockPopup({
6380
6503
  window.removeEventListener("keydown", handleKeyDown);
6381
6504
  };
6382
6505
  }, [onClose]);
6383
- const previewCaptureKey = items.map((item) => `${item.node.id}:${item.previewImageUrl ?? ""}`).join("|");
6506
+ const previewCaptureKey = items.map(
6507
+ (item) => `${item.node.id}:${previewCacheToken(item.preview)}:${item.previewRevision ?? ""}`
6508
+ ).join("|");
6384
6509
  useEffect8(() => {
6385
6510
  if (!isMinimizedStack) {
6386
6511
  return;
@@ -6409,82 +6534,216 @@ function WorkbenchHostDockPopup({
6409
6534
  return () => viewport.removeEventListener("wheel", handleWheel);
6410
6535
  }, [isMinimizedStack, minimizedStackMaxScrollOffset]);
6411
6536
  useEffect8(() => {
6537
+ if (!capturePreview) {
6538
+ return;
6539
+ }
6412
6540
  let cancelled = false;
6413
- const missingItems = items.filter(
6414
- (item) => !item.previewImageUrl && !capturedPreviewByNodeId[item.node.id]
6415
- );
6541
+ const missingItems = items.filter((item) => {
6542
+ const revision = item.previewRevision;
6543
+ const previewMemoryKey = resolveDockPopupPreviewMemoryKey(
6544
+ item.node,
6545
+ resolveDockPreviewCacheKey2?.(item.node) ?? null
6546
+ );
6547
+ const capturedPreview = capturedPreviewByMemoryKey[previewMemoryKey] ?? readDockPopupPreviewImage(previewMemoryKey);
6548
+ const hasCapturedPreview = capturedPreview !== void 0 && capturedPreview.revision === revision;
6549
+ const shouldCaptureMissingPreview = !item.preview && !hasCapturedPreview;
6550
+ return shouldCaptureMissingPreview && !pendingDockPopupPreviewMemoryKeys.has(previewMemoryKey);
6551
+ });
6416
6552
  if (missingItems.length === 0) {
6553
+ logWorkbenchDockPopupDebug(
6554
+ "dock.popup.preview_capture.batch",
6555
+ debugDiagnostics,
6556
+ {
6557
+ itemCount: items.length,
6558
+ missingNodeIds: []
6559
+ }
6560
+ );
6417
6561
  return () => {
6418
6562
  cancelled = true;
6419
6563
  };
6420
6564
  }
6421
- void Promise.all(
6422
- missingItems.map(async (item) => {
6423
- const cacheKey = resolveDockPreviewCacheKey2?.(item.node) ?? null;
6565
+ logWorkbenchDockPopupDebug(
6566
+ "dock.popup.preview_capture.batch",
6567
+ debugDiagnostics,
6568
+ {
6569
+ itemCount: items.length,
6570
+ missingNodeIds: missingItems.map((item) => item.node.id)
6571
+ }
6572
+ );
6573
+ for (const item of missingItems) {
6574
+ pendingDockPopupPreviewMemoryKeys.add(
6575
+ resolveDockPopupPreviewMemoryKey(
6576
+ item.node,
6577
+ resolveDockPreviewCacheKey2?.(item.node) ?? null
6578
+ )
6579
+ );
6580
+ }
6581
+ void (async () => {
6582
+ for (const item of missingItems) {
6583
+ if (cancelled) {
6584
+ break;
6585
+ }
6586
+ const revision = item.previewRevision;
6587
+ logWorkbenchDockPopupDebug(
6588
+ "dock.popup.preview_capture.started",
6589
+ debugDiagnostics,
6590
+ {
6591
+ isMinimized: item.isMinimized,
6592
+ nodeId: item.node.id,
6593
+ revision
6594
+ }
6595
+ );
6596
+ const previewMemoryKey = resolveDockPopupPreviewMemoryKey(
6597
+ item.node,
6598
+ resolveDockPreviewCacheKey2?.(item.node) ?? null
6599
+ );
6600
+ const cacheKey = resolveDockPopupPreviewCacheKey(
6601
+ resolveDockPreviewCacheKey2?.(item.node) ?? null,
6602
+ revision
6603
+ );
6424
6604
  if (item.isMinimized && cacheKey) {
6425
6605
  const minimizedPersistedPreview = await readPersistedDockPreview(
6426
6606
  dockPreviewCache,
6427
6607
  cacheKey
6428
6608
  );
6609
+ if (cancelled) {
6610
+ break;
6611
+ }
6429
6612
  if (minimizedPersistedPreview) {
6613
+ const persistedPreview = {
6614
+ kind: "image",
6615
+ src: minimizedPersistedPreview
6616
+ };
6430
6617
  writeCachedWorkbenchNodePreviewImage(
6431
6618
  item.node.id,
6432
6619
  minimizedPersistedPreview
6433
6620
  );
6434
- return {
6435
- nodeId: item.node.id,
6436
- previewImageUrl: minimizedPersistedPreview
6437
- };
6621
+ writeDockPopupPreviewImage(
6622
+ previewMemoryKey,
6623
+ persistedPreview,
6624
+ revision
6625
+ );
6626
+ setCapturedPreviewByMemoryKey((current) => ({
6627
+ ...current,
6628
+ [previewMemoryKey]: {
6629
+ preview: persistedPreview,
6630
+ revision
6631
+ }
6632
+ }));
6633
+ pendingDockPopupPreviewMemoryKeys.delete(previewMemoryKey);
6634
+ continue;
6438
6635
  }
6439
6636
  }
6440
- const previewImageUrl = await capturePreview?.(item) ?? await captureWorkbenchNodePreviewImage(item.node.id, {
6441
- bypassCache: !item.isMinimized
6442
- });
6443
- if (previewImageUrl) {
6444
- writeCachedWorkbenchNodePreviewImage(item.node.id, previewImageUrl);
6445
- if (cacheKey) {
6446
- dockPreviewCache?.write({ key: cacheKey, previewImageUrl });
6447
- }
6448
- return {
6637
+ const preview = normalizeDockPopupPreviewContentResult(
6638
+ item.isMinimized ? await capturePreview?.(item) ?? await captureWorkbenchNodePreviewImage(item.node.id, {
6639
+ bypassCache: false
6640
+ }) : await Promise.resolve(capturePreview?.(item) ?? null).catch(
6641
+ () => null
6642
+ ),
6643
+ revision
6644
+ );
6645
+ if (cancelled) {
6646
+ break;
6647
+ }
6648
+ logWorkbenchDockPopupDebug(
6649
+ "dock.popup.preview_capture.resolved",
6650
+ debugDiagnostics,
6651
+ {
6652
+ hasPreview: Boolean(preview),
6449
6653
  nodeId: item.node.id,
6450
- previewImageUrl
6451
- };
6654
+ providerRevision: preview?.revision ?? null,
6655
+ revision
6656
+ }
6657
+ );
6658
+ if (preview) {
6659
+ if (preview.kind === "image") {
6660
+ writeCachedWorkbenchNodePreviewImage(item.node.id, preview.src);
6661
+ }
6662
+ writeDockPopupPreviewImage(previewMemoryKey, preview, revision);
6663
+ if (cacheKey && preview.kind === "image") {
6664
+ dockPreviewCache?.write({
6665
+ key: cacheKey,
6666
+ previewImageUrl: preview.src
6667
+ });
6668
+ }
6669
+ setCapturedPreviewByMemoryKey((current) => ({
6670
+ ...current,
6671
+ [previewMemoryKey]: { preview, revision }
6672
+ }));
6673
+ pendingDockPopupPreviewMemoryKeys.delete(previewMemoryKey);
6674
+ continue;
6452
6675
  }
6453
6676
  const fallbackPersistedPreview = !item.isMinimized && cacheKey ? await readPersistedDockPreview(dockPreviewCache, cacheKey) : null;
6677
+ if (cancelled) {
6678
+ break;
6679
+ }
6454
6680
  if (fallbackPersistedPreview) {
6455
6681
  writeCachedWorkbenchNodePreviewImage(
6456
6682
  item.node.id,
6457
6683
  fallbackPersistedPreview
6458
6684
  );
6459
6685
  }
6460
- return {
6461
- nodeId: item.node.id,
6462
- previewImageUrl: fallbackPersistedPreview
6463
- };
6464
- })
6465
- ).then((results) => {
6466
- if (cancelled) {
6467
- return;
6686
+ if (fallbackPersistedPreview || item.isMinimized) {
6687
+ const fallbackPreview = fallbackPersistedPreview ? { kind: "image", src: fallbackPersistedPreview } : null;
6688
+ writeDockPopupPreviewImage(
6689
+ previewMemoryKey,
6690
+ fallbackPreview,
6691
+ revision
6692
+ );
6693
+ }
6694
+ if (!cancelled) {
6695
+ const fallbackPreview = fallbackPersistedPreview ? { kind: "image", src: fallbackPersistedPreview } : null;
6696
+ setCapturedPreviewByMemoryKey((current) => ({
6697
+ ...current,
6698
+ [previewMemoryKey]: { preview: fallbackPreview, revision }
6699
+ }));
6700
+ }
6701
+ pendingDockPopupPreviewMemoryKeys.delete(previewMemoryKey);
6468
6702
  }
6469
- const nextEntries = results.filter(
6470
- (result) => Boolean(result.previewImageUrl)
6471
- );
6472
- if (nextEntries.length === 0) {
6473
- return;
6703
+ })().catch(() => {
6704
+ for (const item of missingItems) {
6705
+ const previewMemoryKey = resolveDockPopupPreviewMemoryKey(
6706
+ item.node,
6707
+ resolveDockPreviewCacheKey2?.(item.node) ?? null
6708
+ );
6709
+ writeDockPopupPreviewImage(
6710
+ previewMemoryKey,
6711
+ null,
6712
+ item.previewRevision
6713
+ );
6714
+ pendingDockPopupPreviewMemoryKeys.delete(previewMemoryKey);
6715
+ }
6716
+ if (!cancelled) {
6717
+ setCapturedPreviewByMemoryKey((current) => ({
6718
+ ...current,
6719
+ ...Object.fromEntries(
6720
+ missingItems.map((item) => {
6721
+ const previewMemoryKey = resolveDockPopupPreviewMemoryKey(
6722
+ item.node,
6723
+ resolveDockPreviewCacheKey2?.(item.node) ?? null
6724
+ );
6725
+ return [
6726
+ previewMemoryKey,
6727
+ { preview: null, revision: item.previewRevision }
6728
+ ];
6729
+ })
6730
+ )
6731
+ }));
6474
6732
  }
6475
- setCapturedPreviewByNodeId((current) => ({
6476
- ...current,
6477
- ...Object.fromEntries(
6478
- nextEntries.map((entry) => [entry.nodeId, entry.previewImageUrl])
6479
- )
6480
- }));
6481
6733
  });
6482
6734
  return () => {
6483
6735
  cancelled = true;
6736
+ for (const item of missingItems) {
6737
+ pendingDockPopupPreviewMemoryKeys.delete(
6738
+ resolveDockPopupPreviewMemoryKey(
6739
+ item.node,
6740
+ resolveDockPreviewCacheKey2?.(item.node) ?? null
6741
+ )
6742
+ );
6743
+ }
6484
6744
  };
6485
6745
  }, [
6486
6746
  capturePreview,
6487
- capturedPreviewByNodeId,
6488
6747
  dockPreviewCache,
6489
6748
  items,
6490
6749
  previewCaptureKey,
@@ -6493,6 +6752,7 @@ function WorkbenchHostDockPopup({
6493
6752
  const content = /* @__PURE__ */ jsx9(
6494
6753
  "div",
6495
6754
  {
6755
+ ref: popupRootRef,
6496
6756
  className: "desktop-dock-popup-root",
6497
6757
  "data-dock-placement": placement,
6498
6758
  "data-desktop-dock-popup-root": "true",
@@ -6533,7 +6793,16 @@ function WorkbenchHostDockPopup({
6533
6793
  transform: `translate(${minimizedStackTrackTranslateXPx}px, ${-minimizedStackScrollOffset}px)`
6534
6794
  },
6535
6795
  children: items.map((item, index) => {
6536
- const previewImageUrl = item.previewImageUrl ?? capturedPreviewByNodeId[item.node.id];
6796
+ const previewMemoryKey = resolveDockPopupPreviewMemoryKey(
6797
+ item.node,
6798
+ resolveDockPreviewCacheKey2?.(item.node) ?? null
6799
+ );
6800
+ const capturedPreview = capturedPreviewByMemoryKey[previewMemoryKey] !== void 0 ? capturedPreviewByMemoryKey[previewMemoryKey] : readDockPopupPreviewImage(previewMemoryKey);
6801
+ const previewState = resolveDockPopupItemPreviewState(
6802
+ item,
6803
+ capturedPreview,
6804
+ Boolean(capturePreview)
6805
+ );
6537
6806
  return /* @__PURE__ */ jsx9(
6538
6807
  WorkbenchHostDockPopupCard,
6539
6808
  {
@@ -6543,7 +6812,7 @@ function WorkbenchHostDockPopup({
6543
6812
  labelMode: resolvedLabelMode,
6544
6813
  onCloseNode,
6545
6814
  onSelectNode,
6546
- previewImageUrl,
6815
+ previewState,
6547
6816
  style: {
6548
6817
  ...resolvePopupFanCardStyle(
6549
6818
  index,
@@ -6565,7 +6834,16 @@ function WorkbenchHostDockPopup({
6565
6834
  }
6566
6835
  ) : /* @__PURE__ */ jsxs5("div", { className: "grid max-h-[min(52vh,420px)] grid-cols-[repeat(var(--desktop-dock-popup-columns,2),165px)] gap-2 overflow-auto overscroll-contain", children: [
6567
6836
  items.map((item) => {
6568
- const previewImageUrl = item.previewImageUrl ?? capturedPreviewByNodeId[item.node.id];
6837
+ const previewMemoryKey = resolveDockPopupPreviewMemoryKey(
6838
+ item.node,
6839
+ resolveDockPreviewCacheKey2?.(item.node) ?? null
6840
+ );
6841
+ const capturedPreview = capturedPreviewByMemoryKey[previewMemoryKey] !== void 0 ? capturedPreviewByMemoryKey[previewMemoryKey] : readDockPopupPreviewImage(previewMemoryKey);
6842
+ const previewState = resolveDockPopupItemPreviewState(
6843
+ item,
6844
+ capturedPreview,
6845
+ Boolean(capturePreview)
6846
+ );
6569
6847
  return /* @__PURE__ */ jsx9(
6570
6848
  WorkbenchHostDockPopupCard,
6571
6849
  {
@@ -6575,7 +6853,7 @@ function WorkbenchHostDockPopup({
6575
6853
  labelMode: resolvedLabelMode,
6576
6854
  onCloseNode,
6577
6855
  onSelectNode,
6578
- previewImageUrl,
6856
+ previewState,
6579
6857
  variant: resolvedVariant
6580
6858
  },
6581
6859
  item.node.id
@@ -6617,13 +6895,100 @@ function readPersistedDockPreview(dockPreviewCache, cacheKey) {
6617
6895
  }
6618
6896
  return dockPreviewCache?.read(cacheKey).catch(() => null) ?? Promise.resolve(null);
6619
6897
  }
6898
+ function resolveDockPopupPreviewCacheKey(cacheKey, revision) {
6899
+ return cacheKey ? { ...cacheKey, revision } : null;
6900
+ }
6901
+ function resolveDockPopupPreviewMemoryKey(node, cacheKey) {
6902
+ if (!cacheKey) {
6903
+ return `node:${node.id}`;
6904
+ }
6905
+ return `cache:${JSON.stringify({
6906
+ instanceId: cacheKey.instanceId,
6907
+ instanceKey: cacheKey.instanceKey ?? null,
6908
+ nodeId: cacheKey.nodeId,
6909
+ typeId: cacheKey.typeId,
6910
+ workspaceId: cacheKey.workspaceId
6911
+ })}`;
6912
+ }
6913
+ function normalizeDockPopupPreviewContentResult(preview, revision) {
6914
+ if (!preview) {
6915
+ return null;
6916
+ }
6917
+ if (typeof preview === "string") {
6918
+ return { kind: "image", revision: revision ?? void 0, src: preview };
6919
+ }
6920
+ return {
6921
+ ...preview,
6922
+ revision: preview.revision ?? revision ?? void 0
6923
+ };
6924
+ }
6925
+ function resolveDockPopupItemPreviewState(item, capturedPreview, hasPreviewProvider) {
6926
+ const revision = item.previewRevision;
6927
+ if (item.preview) {
6928
+ return { preview: item.preview, status: "ready" };
6929
+ }
6930
+ if (capturedPreview && capturedPreview.revision === revision && capturedPreview.preview) {
6931
+ return { preview: capturedPreview.preview, status: "ready" };
6932
+ }
6933
+ if (capturedPreview !== void 0 && capturedPreview.revision === revision || !hasPreviewProvider) {
6934
+ return { status: "fallback" };
6935
+ }
6936
+ return { status: "loading" };
6937
+ }
6938
+ function previewCacheToken(preview) {
6939
+ if (!preview) {
6940
+ return "";
6941
+ }
6942
+ switch (preview.kind) {
6943
+ case "component":
6944
+ return `component:${preview.revision ?? ""}`;
6945
+ case "image":
6946
+ return `image:${preview.revision ?? ""}:${preview.src}`;
6947
+ }
6948
+ }
6949
+ function logWorkbenchDockPopupDebug(event, debugDiagnostics, details) {
6950
+ if (!debugDiagnostics?.log) {
6951
+ return;
6952
+ }
6953
+ void Promise.resolve(
6954
+ debugDiagnostics.log({
6955
+ details,
6956
+ event,
6957
+ level: "info",
6958
+ source: "workbench-dock"
6959
+ })
6960
+ ).catch(() => void 0);
6961
+ }
6962
+ function rectToDiagnostic(element) {
6963
+ const rect = element.getBoundingClientRect();
6964
+ return {
6965
+ bottom: Math.round(rect.bottom),
6966
+ height: Math.round(rect.height),
6967
+ left: Math.round(rect.left),
6968
+ right: Math.round(rect.right),
6969
+ top: Math.round(rect.top),
6970
+ width: Math.round(rect.width)
6971
+ };
6972
+ }
6973
+ function styleToDiagnostic(element) {
6974
+ const style = window.getComputedStyle(element);
6975
+ return {
6976
+ display: style.display,
6977
+ opacity: style.opacity,
6978
+ pointerEvents: style.pointerEvents,
6979
+ position: style.position,
6980
+ transform: style.transform,
6981
+ visibility: style.visibility,
6982
+ zIndex: style.zIndex
6983
+ };
6984
+ }
6620
6985
  var WorkbenchHostDockPopupCard = forwardRef(function WorkbenchHostDockPopupCard2({
6621
6986
  closeWindowLabel,
6622
6987
  item,
6623
6988
  labelMode,
6624
6989
  onCloseNode,
6625
6990
  onSelectNode,
6626
- previewImageUrl,
6991
+ previewState,
6627
6992
  style,
6628
6993
  variant
6629
6994
  }, ref) {
@@ -6653,13 +7018,23 @@ var WorkbenchHostDockPopupCard = forwardRef(function WorkbenchHostDockPopupCard2
6653
7018
  onSelectNode(item.node.id);
6654
7019
  }, dockPopupMinimizedStackLaunchDisappearMs);
6655
7020
  }, [isMinimizedStack, item.node.id, onSelectNode]);
7021
+ const handleSelectKeyDown = useCallback9(
7022
+ (event) => {
7023
+ if (event.key !== "Enter" && event.key !== " ") {
7024
+ return;
7025
+ }
7026
+ event.preventDefault();
7027
+ handleSelect();
7028
+ },
7029
+ [handleSelect]
7030
+ );
7031
+ const hasReadyPreview = previewState.status === "ready";
6656
7032
  return /* @__PURE__ */ jsxs5(
6657
7033
  "div",
6658
7034
  {
6659
7035
  ref,
6660
7036
  className: cn(
6661
7037
  "group/dock-popup-card relative flex h-[103px] w-[165px] min-w-0 flex-col overflow-hidden rounded-[8px] border border-[var(--border-1)] bg-background-fronted text-left text-[var(--text-primary)] transition-[border-color,color] duration-150",
6662
- item.isFocused && "border-transparent shadow-[inset_0_0_0_2px_var(--border-focus)]",
6663
7038
  item.isMinimized && "text-[var(--text-secondary)]"
6664
7039
  ),
6665
7040
  "data-active": item.isFocused ? "true" : void 0,
@@ -6670,41 +7045,20 @@ var WorkbenchHostDockPopupCard = forwardRef(function WorkbenchHostDockPopupCard2
6670
7045
  style,
6671
7046
  children: [
6672
7047
  /* @__PURE__ */ jsxs5(
6673
- "button",
7048
+ "div",
6674
7049
  {
6675
7050
  "aria-label": title,
6676
7051
  "data-active": item.isFocused ? "true" : void 0,
6677
- className: "relative flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden rounded-md bg-transparent p-1 text-inherit",
6678
- type: "button",
7052
+ className: cn(
7053
+ "relative flex min-h-0 min-w-0 flex-1 cursor-pointer flex-col overflow-hidden rounded-md bg-transparent text-inherit",
7054
+ hasReadyPreview ? "p-0" : "p-1"
7055
+ ),
7056
+ role: "button",
7057
+ tabIndex: 0,
6679
7058
  onClick: handleSelect,
7059
+ onKeyDown: handleSelectKeyDown,
6680
7060
  children: [
6681
- previewImageUrl ? /* @__PURE__ */ jsx9(
6682
- "span",
6683
- {
6684
- className: "block min-h-0 min-w-0 flex-1 overflow-hidden rounded-md bg-transparency-block",
6685
- "aria-hidden": "true",
6686
- children: /* @__PURE__ */ jsx9(
6687
- "img",
6688
- {
6689
- alt: "",
6690
- className: "block h-full max-h-full w-full max-w-full object-contain object-center",
6691
- draggable: false,
6692
- src: previewImageUrl
6693
- }
6694
- )
6695
- }
6696
- ) : /* @__PURE__ */ jsxs5(
6697
- "span",
6698
- {
6699
- className: "flex size-full flex-col justify-center gap-[7px] rounded-md border border-[var(--border-1)] bg-transparency-block px-3 py-[11px]",
6700
- "aria-hidden": "true",
6701
- children: [
6702
- /* @__PURE__ */ jsx9("span", { className: "block h-[7px] w-[72%] rounded-full bg-transparency-hover" }),
6703
- /* @__PURE__ */ jsx9("span", { className: "block h-[7px] w-[58%] rounded-full bg-transparency-hover" }),
6704
- /* @__PURE__ */ jsx9("span", { className: "block h-[7px] w-[34%] rounded-full bg-transparency-hover" })
6705
- ]
6706
- }
6707
- ),
7061
+ /* @__PURE__ */ jsx9(WorkbenchHostDockPopupCardPreview, { previewState }),
6708
7062
  labelMode === "hover-overlay" && item.title?.trim() ? /* @__PURE__ */ jsx9(WorkbenchHostDockPopupCardLabel, { title: item.title }) : null
6709
7063
  ]
6710
7064
  }
@@ -6726,11 +7080,69 @@ var WorkbenchHostDockPopupCard = forwardRef(function WorkbenchHostDockPopupCard2
6726
7080
  children: /* @__PURE__ */ jsx9(CloseIcon, { className: "size-3.5" })
6727
7081
  }
6728
7082
  ),
7083
+ item.isFocused ? /* @__PURE__ */ jsx9(
7084
+ "span",
7085
+ {
7086
+ "aria-hidden": "true",
7087
+ className: "pointer-events-none absolute inset-0 z-[3] rounded-[8px] shadow-[inset_0_0_0_2px_var(--border-focus)]",
7088
+ "data-desktop-dock-popup-card-active-overlay": "true"
7089
+ }
7090
+ ) : null,
6729
7091
  isMinimizedStack ? /* @__PURE__ */ jsx9("span", { className: "desktop-dock-popup__fan-title-tip", title, children: title }) : null
6730
7092
  ]
6731
7093
  }
6732
7094
  );
6733
7095
  });
7096
+ function WorkbenchHostDockPopupCardPreview({
7097
+ previewState
7098
+ }) {
7099
+ if (previewState.status !== "ready") {
7100
+ return /* @__PURE__ */ jsxs5(
7101
+ "span",
7102
+ {
7103
+ className: "flex min-h-0 min-w-0 flex-1 flex-col justify-center gap-[7px] rounded-md border border-[var(--border-1)] bg-transparency-block px-3 py-[11px]",
7104
+ "aria-hidden": "true",
7105
+ "data-preview-state": previewState.status,
7106
+ children: [
7107
+ /* @__PURE__ */ jsx9("span", { className: "block h-[7px] w-[72%] rounded-full bg-transparency-hover" }),
7108
+ /* @__PURE__ */ jsx9("span", { className: "block h-[7px] w-[58%] rounded-full bg-transparency-hover" }),
7109
+ /* @__PURE__ */ jsx9("span", { className: "block h-[7px] w-[34%] rounded-full bg-transparency-hover" })
7110
+ ]
7111
+ }
7112
+ );
7113
+ }
7114
+ const preview = previewState.preview;
7115
+ if (preview.kind === "component") {
7116
+ return /* @__PURE__ */ jsx9(
7117
+ "span",
7118
+ {
7119
+ className: "block min-h-0 min-w-0 flex-1 overflow-hidden rounded-md",
7120
+ "aria-hidden": "true",
7121
+ "data-preview-kind": preview.kind,
7122
+ "data-preview-state": previewState.status,
7123
+ children: preview.element
7124
+ }
7125
+ );
7126
+ }
7127
+ return /* @__PURE__ */ jsx9(
7128
+ "span",
7129
+ {
7130
+ className: "block min-h-0 min-w-0 flex-1 overflow-hidden rounded-md",
7131
+ "aria-hidden": "true",
7132
+ "data-preview-kind": preview.kind,
7133
+ "data-preview-state": previewState.status,
7134
+ children: /* @__PURE__ */ jsx9(
7135
+ "img",
7136
+ {
7137
+ alt: "",
7138
+ className: "block h-full max-h-full w-full max-w-full object-contain object-center",
7139
+ draggable: false,
7140
+ src: preview.src
7141
+ }
7142
+ )
7143
+ }
7144
+ );
7145
+ }
6734
7146
  function WorkbenchHostDockPopupCardLabel({ title }) {
6735
7147
  return /* @__PURE__ */ jsx9(
6736
7148
  "span",
@@ -6763,7 +7175,9 @@ function isDockVisualMutationActive(element) {
6763
7175
  ) !== null;
6764
7176
  }
6765
7177
  function WorkbenchHostDock({
7178
+ captureNodePreviewImage,
6766
7179
  context,
7180
+ debugDiagnostics,
6767
7181
  dockEntries,
6768
7182
  dockPlacement = "bottom",
6769
7183
  dockPreviewCache,
@@ -6796,6 +7210,9 @@ function WorkbenchHostDock({
6796
7210
  );
6797
7211
  const pendingDockStateRefreshRef = useRef8(false);
6798
7212
  const slotRefs = useRef8(/* @__PURE__ */ new Map());
7213
+ const dockSlotRefCallbacksRef = useRef8(
7214
+ /* @__PURE__ */ new Map()
7215
+ );
6799
7216
  const previousAttentionTokenByEntryId = useRef8(/* @__PURE__ */ new Map());
6800
7217
  const attentionTimeouts = useRef8(
6801
7218
  /* @__PURE__ */ new Map()
@@ -6879,7 +7296,7 @@ function WorkbenchHostDock({
6879
7296
  },
6880
7297
  [clearCollapsingMinimizedLaunch]
6881
7298
  );
6882
- useLayoutEffect4(() => {
7299
+ useLayoutEffect5(() => {
6883
7300
  const element = dockMeasureRef.current;
6884
7301
  if (!element || typeof window === "undefined") {
6885
7302
  return void 0;
@@ -6984,6 +7401,20 @@ function WorkbenchHostDock({
6984
7401
  dockRootRef: dockMeasureRef,
6985
7402
  slotRefs
6986
7403
  });
7404
+ const clearSlotMagnificationRef = useRef8(() => {
7405
+ return;
7406
+ });
7407
+ const registerDockAnchorRef = useRef8(
7408
+ (anchorKey, element) => {
7409
+ context.genie.registerDockAnchor(anchorKey, element);
7410
+ }
7411
+ );
7412
+ clearSlotMagnificationRef.current = (anchorKey) => {
7413
+ clearSlotMagnification(anchorKey);
7414
+ };
7415
+ registerDockAnchorRef.current = (anchorKey, element) => {
7416
+ context.genie.registerDockAnchor(anchorKey, element);
7417
+ };
6987
7418
  const setDockHoverPanelOpen = useCallback10((open) => {
6988
7419
  if (open) {
6989
7420
  dockMeasureRef.current?.setAttribute(
@@ -7268,7 +7699,7 @@ function WorkbenchHostDock({
7268
7699
  (current) => current.canScrollBackward === nextState.canScrollBackward && current.canScrollForward === nextState.canScrollForward && current.hasOverflow === nextState.hasOverflow ? current : nextState
7269
7700
  );
7270
7701
  }, [dockPlacement, dockWidth]);
7271
- useLayoutEffect4(() => {
7702
+ useLayoutEffect5(() => {
7272
7703
  const element = dockItemsRef.current;
7273
7704
  if (!element || typeof window === "undefined") {
7274
7705
  return void 0;
@@ -7367,23 +7798,28 @@ function WorkbenchHostDock({
7367
7798
  []
7368
7799
  );
7369
7800
  const captureMinimizedNodePreview = useCallback10(
7370
- (node) => {
7801
+ async (node) => {
7371
7802
  const capturePreview = nodeDefinitions.get(node.data.typeId)?.window?.minimizedDock?.capturePreview;
7372
- if (!capturePreview) {
7373
- return null;
7374
- }
7375
- return capturePreview({
7376
- externalNodeState: readWorkbenchHostExternalState({
7377
- externalStateSource,
7378
- node,
7379
- workspaceId
7380
- }).externalNodeState,
7381
- isFocused: context.focusedNodeId === node.id,
7382
- isMinimized: node.isMinimized,
7383
- node
7803
+ const externalState = readWorkbenchHostExternalState({
7804
+ externalStateSource,
7805
+ node,
7806
+ workspaceId
7384
7807
  });
7808
+ return await Promise.resolve(
7809
+ capturePreview?.({
7810
+ externalNodeState: externalState.externalNodeState,
7811
+ externalWorkspaceState: externalState.externalWorkspaceState,
7812
+ host,
7813
+ isFocused: context.focusedNodeId === node.id,
7814
+ isMinimized: node.isMinimized,
7815
+ node
7816
+ }) ?? null
7817
+ ).catch(() => null) ?? await Promise.resolve(captureNodePreviewImage?.(node) ?? null).catch(
7818
+ () => null
7819
+ );
7385
7820
  },
7386
7821
  [
7822
+ captureNodePreviewImage,
7387
7823
  context.focusedNodeId,
7388
7824
  externalStateRevision,
7389
7825
  externalStateSource,
@@ -7420,17 +7856,25 @@ function WorkbenchHostDock({
7420
7856
  top: isVertical ? delta : 0
7421
7857
  });
7422
7858
  };
7423
- const registerDockSlot = (anchorKey) => (element) => {
7424
- if (element) {
7425
- slotRefs.current.set(anchorKey, element);
7426
- } else {
7427
- slotRefs.current.delete(anchorKey);
7428
- if (!dockMeasureRef.current?.hasAttribute("data-dock-pointer-active")) {
7429
- clearSlotMagnification(anchorKey);
7430
- }
7859
+ const registerDockSlot = useCallback10((anchorKey) => {
7860
+ const existing = dockSlotRefCallbacksRef.current.get(anchorKey);
7861
+ if (existing) {
7862
+ return existing;
7431
7863
  }
7432
- context.genie.registerDockAnchor(anchorKey, element);
7433
- };
7864
+ const callback = (element) => {
7865
+ if (element) {
7866
+ slotRefs.current.set(anchorKey, element);
7867
+ } else {
7868
+ slotRefs.current.delete(anchorKey);
7869
+ if (!dockMeasureRef.current?.hasAttribute("data-dock-pointer-active")) {
7870
+ clearSlotMagnificationRef.current(anchorKey);
7871
+ }
7872
+ }
7873
+ registerDockAnchorRef.current(anchorKey, element);
7874
+ };
7875
+ dockSlotRefCallbacksRef.current.set(anchorKey, callback);
7876
+ return callback;
7877
+ }, []);
7434
7878
  const popupEntry = activePopup === null ? null : resolvedEntries.find(
7435
7879
  (entry) => entry.entry.id === activePopup.entryId
7436
7880
  ) ?? null;
@@ -7540,6 +7984,19 @@ function WorkbenchHostDock({
7540
7984
  beginDockIconInteraction(anchorKey);
7541
7985
  },
7542
7986
  onClick: (event) => {
7987
+ logWorkbenchDockDebug("dock.click", debugDiagnostics, {
7988
+ anchorKey,
7989
+ clickResolution,
7990
+ dockNodeState: resolvedEntry.dockNodeState,
7991
+ entryId: entry.id,
7992
+ instanceMode: instanceMode ?? null,
7993
+ matchedNodeCount: resolvedEntry.matchedNodes.length,
7994
+ matchedNodeIds: resolvedEntry.matchedNodes.map(
7995
+ (node2) => node2.id
7996
+ ),
7997
+ typeId: entry.typeId,
7998
+ workspaceId
7999
+ });
7543
8000
  switch (clickResolution.kind) {
7544
8001
  case "focus-node":
7545
8002
  closePopup();
@@ -7561,6 +8018,18 @@ function WorkbenchHostDock({
7561
8018
  return;
7562
8019
  case "open-popup": {
7563
8020
  const rect = event.currentTarget.getBoundingClientRect();
8021
+ logWorkbenchDockDebug(
8022
+ "dock.popup.toggle",
8023
+ debugDiagnostics,
8024
+ {
8025
+ anchorKey,
8026
+ entryId: entry.id,
8027
+ matchedNodeCount: resolvedEntry.matchedNodes.length,
8028
+ nextOpen: currentPopup === null,
8029
+ typeId: entry.typeId,
8030
+ workspaceId
8031
+ }
8032
+ );
7564
8033
  setActivePopup(
7565
8034
  (current) => current?.entryId === entry.id ? null : {
7566
8035
  anchorRect: {
@@ -7590,14 +8059,12 @@ function WorkbenchHostDock({
7590
8059
  context.genie.launchNodeFromAnchor(
7591
8060
  anchorKey,
7592
8061
  entry.id,
7593
- () => {
7594
- void host.launchNode({
7595
- dockEntryId: entry.id,
7596
- payload: entry.launchPayload,
7597
- reason: "dock",
7598
- typeId: entry.typeId
7599
- });
7600
- }
8062
+ () => host.launchNode({
8063
+ dockEntryId: entry.id,
8064
+ payload: entry.launchPayload,
8065
+ reason: "dock",
8066
+ typeId: entry.typeId
8067
+ })
7601
8068
  );
7602
8069
  return;
7603
8070
  case "blocked":
@@ -7918,23 +8385,43 @@ function WorkbenchHostDock({
7918
8385
  {
7919
8386
  anchorRect: activePopup.anchorRect,
7920
8387
  placement: dockPlacement,
7921
- capturePreview: popupEntry.entry.capturePopupItemPreview,
8388
+ debugDiagnostics,
8389
+ capturePreview: popupEntry.entry.capturePopupItemPreview ? async (item) => {
8390
+ const previewImageUrl = await Promise.resolve(
8391
+ popupEntry.entry.capturePopupItemPreview?.(item) ?? null
8392
+ ).catch(() => null);
8393
+ return previewImageUrl ? {
8394
+ kind: "image",
8395
+ revision: item.previewRevision ?? void 0,
8396
+ src: previewImageUrl
8397
+ } : null;
8398
+ } : void 0,
7922
8399
  dockPreviewCache,
7923
8400
  items: popupEntry.matchedNodes.map((node) => {
8401
+ const externalState = readWorkbenchHostExternalState({
8402
+ externalStateSource,
8403
+ node,
8404
+ workspaceId
8405
+ });
7924
8406
  const item = {
7925
- externalNodeState: readWorkbenchHostExternalState({
7926
- externalStateSource,
7927
- node,
7928
- workspaceId
7929
- }).externalNodeState,
8407
+ externalNodeState: externalState.externalNodeState,
8408
+ externalWorkspaceState: externalState.externalWorkspaceState,
8409
+ host,
7930
8410
  isFocused: context.focusedNodeId === node.id,
7931
8411
  isMinimized: minimizedNodeIDs.has(node.id),
7932
8412
  node
7933
8413
  };
7934
8414
  const descriptor = popupEntry.entry.resolvePopupItem?.(item) ?? {};
8415
+ const descriptorPreviewImageUrl = descriptor.previewImageUrl ?? null;
8416
+ const descriptorPreview = descriptor.preview ?? (descriptorPreviewImageUrl ? {
8417
+ kind: "image",
8418
+ revision: descriptor.revision ?? null,
8419
+ src: descriptorPreviewImageUrl
8420
+ } : popupEntry.entry.providePopupItemPreview?.(item) ?? null);
7935
8421
  return {
7936
8422
  ...item,
7937
- previewImageUrl: descriptor.previewImageUrl ?? null,
8423
+ preview: descriptorPreview,
8424
+ previewRevision: previewRevision(descriptorPreview) ?? descriptor.revision ?? null,
7938
8425
  subtitle: descriptor.subtitle === void 0 ? node.data.instanceKey ?? node.data.instanceId : descriptor.subtitle,
7939
8426
  title: descriptor.title === void 0 ? node.title : descriptor.title?.trim() || null
7940
8427
  };
@@ -7948,7 +8435,18 @@ function WorkbenchHostDock({
7948
8435
  labelMode: popupEntry.entry.popupCardLabelMode,
7949
8436
  newWindowLabel: i18n.t("newWindow"),
7950
8437
  closeWindowLabel: (title) => i18n.t("closeWindow", { title }),
7951
- onClose: closePopup,
8438
+ onClose: () => {
8439
+ logWorkbenchDockDebug(
8440
+ "dock.popup.close_requested",
8441
+ debugDiagnostics,
8442
+ {
8443
+ entryId: popupEntry.entry.id,
8444
+ itemCount: popupEntry.matchedNodes.length,
8445
+ workspaceId
8446
+ }
8447
+ );
8448
+ closePopup();
8449
+ },
7952
8450
  onCloseNode: (nodeId) => {
7953
8451
  host.requestNodeClose(nodeId);
7954
8452
  const hasRemainingItems = popupEntry.matchedNodes.some(
@@ -7963,14 +8461,12 @@ function WorkbenchHostDock({
7963
8461
  context.genie.launchNodeFromAnchor(
7964
8462
  anchorKeyFromPopupEntry(popupEntry),
7965
8463
  popupEntry.entry.id,
7966
- () => {
7967
- void host.launchNode({
7968
- dockEntryId: popupEntry.entry.id,
7969
- payload: popupEntry.entry.launchPayload,
7970
- reason: "dock",
7971
- typeId: popupEntry.entry.typeId
7972
- });
7973
- }
8464
+ () => host.launchNode({
8465
+ dockEntryId: popupEntry.entry.id,
8466
+ payload: popupEntry.entry.launchPayload,
8467
+ reason: "dock",
8468
+ typeId: popupEntry.entry.typeId
8469
+ })
7974
8470
  );
7975
8471
  },
7976
8472
  onSelectNode: (nodeId) => {
@@ -8003,20 +8499,51 @@ function WorkbenchHostDock({
8003
8499
  {
8004
8500
  anchorRect: activeMinimizedStackPopup,
8005
8501
  placement: dockPlacement,
8006
- capturePreview: (item) => captureMinimizedNodePreview(item.node),
8502
+ debugDiagnostics,
8503
+ capturePreview: async (item) => {
8504
+ const src = await captureMinimizedNodePreview(item.node);
8505
+ return src ? { kind: "image", src } : null;
8506
+ },
8007
8507
  dockPreviewCache,
8008
- items: activeMinimizedStackSlot.nodes.map((node) => ({
8009
- isFocused: context.focusedNodeId === node.id,
8010
- isMinimized: true,
8011
- node,
8012
- previewImageUrl: nodeDefinitions.get(node.data.typeId)?.window?.minimizedDock?.capturePreview ? null : readCachedWorkbenchNodePreviewImage(node.id),
8013
- subtitle: node.data.instanceKey ?? node.data.instanceId,
8014
- title: node.title
8015
- })),
8508
+ items: activeMinimizedStackSlot.nodes.map((node) => {
8509
+ const externalState = readWorkbenchHostExternalState({
8510
+ externalStateSource,
8511
+ node,
8512
+ workspaceId
8513
+ });
8514
+ return {
8515
+ externalNodeState: externalState.externalNodeState,
8516
+ externalWorkspaceState: externalState.externalWorkspaceState,
8517
+ host,
8518
+ isFocused: context.focusedNodeId === node.id,
8519
+ isMinimized: true,
8520
+ node,
8521
+ preview: nodeDefinitions.get(node.data.typeId)?.window?.minimizedDock?.capturePreview ? null : (() => {
8522
+ const previewImageUrl = readCachedWorkbenchNodePreviewImage(
8523
+ node.id
8524
+ );
8525
+ return previewImageUrl ? { kind: "image", src: previewImageUrl } : null;
8526
+ })(),
8527
+ previewRevision: null,
8528
+ subtitle: node.data.instanceKey ?? node.data.instanceId,
8529
+ title: node.title
8530
+ };
8531
+ }),
8016
8532
  label: i18n.t("minimizedWindows"),
8017
8533
  newWindowLabel: i18n.t("newWindow"),
8018
8534
  closeWindowLabel: (title) => i18n.t("closeWindow", { title }),
8019
- onClose: closePopup,
8535
+ onClose: () => {
8536
+ logWorkbenchDockDebug(
8537
+ "dock.popup.close_requested",
8538
+ debugDiagnostics,
8539
+ {
8540
+ entryId: "minimized-stack",
8541
+ itemCount: activeMinimizedStackSlot.nodes.length,
8542
+ workspaceId
8543
+ }
8544
+ );
8545
+ closePopup();
8546
+ },
8020
8547
  onCloseNode: (nodeId) => {
8021
8548
  host.requestNodeClose(nodeId);
8022
8549
  const hasRemainingItems = activeMinimizedStackSlot.nodes.some(
@@ -8259,6 +8786,23 @@ function canCreateNewWindow(entry, instanceMode) {
8259
8786
  function anchorKeyFromPopupEntry(entry) {
8260
8787
  return entry.anchorKey;
8261
8788
  }
8789
+ function previewRevision(preview) {
8790
+ return preview?.revision ?? null;
8791
+ }
8792
+ function logWorkbenchDockDebug(event, debugDiagnostics, details) {
8793
+ if (!debugDiagnostics?.log) {
8794
+ return;
8795
+ }
8796
+ void Promise.resolve(
8797
+ debugDiagnostics.log({
8798
+ details,
8799
+ event,
8800
+ level: "info",
8801
+ source: "workbench-dock",
8802
+ workspaceId: typeof details.workspaceId === "string" ? details.workspaceId : null
8803
+ })
8804
+ ).catch(() => void 0);
8805
+ }
8262
8806
  function minimizedDockSlotNodes(slot) {
8263
8807
  return slot.kind === "stack" ? slot.nodes : [slot.node];
8264
8808
  }
@@ -8694,11 +9238,45 @@ function useWorkbenchHostSurfaceRenderers(input) {
8694
9238
  () => input.renderTopChrome ? () => input.renderTopChrome?.(input.chromeContext) : void 0,
8695
9239
  [input.chromeContext, input.renderTopChrome]
8696
9240
  );
9241
+ const captureNodePreviewImage = useCallback11(
9242
+ async (node) => {
9243
+ const definition = input.nodeDefinitionByType.get(node.data.typeId);
9244
+ const capturePreview = definition?.window?.minimizedDock?.capturePreview;
9245
+ const snapshot = input.hostSession.getSnapshot();
9246
+ const externalState = readWorkbenchHostExternalState({
9247
+ externalStateSource: input.externalStateSource,
9248
+ node,
9249
+ workspaceId: input.workspaceId
9250
+ });
9251
+ const nodePreview = await Promise.resolve(
9252
+ capturePreview?.({
9253
+ externalNodeState: externalState.externalNodeState,
9254
+ externalWorkspaceState: externalState.externalWorkspaceState,
9255
+ host: input.hostSession,
9256
+ isFocused: snapshot.nodeStack.at(-1) === node.id,
9257
+ isMinimized: node.isMinimized,
9258
+ node
9259
+ }) ?? null
9260
+ ).catch(() => null) ?? await Promise.resolve(
9261
+ input.captureNodePreviewImage?.(node) ?? null
9262
+ ).catch(() => null);
9263
+ return nodePreview;
9264
+ },
9265
+ [
9266
+ input.captureNodePreviewImage,
9267
+ input.externalStateSource,
9268
+ input.hostSession,
9269
+ input.nodeDefinitionByType,
9270
+ input.workspaceId
9271
+ ]
9272
+ );
8697
9273
  const renderDock = useCallback11(
8698
9274
  (context) => /* @__PURE__ */ jsx12(
8699
9275
  WorkbenchHostDock,
8700
9276
  {
9277
+ captureNodePreviewImage,
8701
9278
  context,
9279
+ debugDiagnostics: input.debugDiagnostics,
8702
9280
  dockEntries: input.dockEntries,
8703
9281
  dockPlacement: input.dockPlacement,
8704
9282
  dockPreviewCache: input.dockPreviewCache,
@@ -8713,6 +9291,8 @@ function useWorkbenchHostSurfaceRenderers(input) {
8713
9291
  }
8714
9292
  ),
8715
9293
  [
9294
+ captureNodePreviewImage,
9295
+ input.debugDiagnostics,
8716
9296
  input.dockEntries,
8717
9297
  input.dockPlacement,
8718
9298
  input.dockPreviewCache,
@@ -8726,32 +9306,6 @@ function useWorkbenchHostSurfaceRenderers(input) {
8726
9306
  input.workspaceId
8727
9307
  ]
8728
9308
  );
8729
- const captureNodePreviewImage = useCallback11(
8730
- (node) => {
8731
- const definition = input.nodeDefinitionByType.get(node.data.typeId);
8732
- const capturePreview = definition?.window?.minimizedDock?.capturePreview;
8733
- if (!capturePreview) {
8734
- return null;
8735
- }
8736
- const snapshot = input.hostSession.getSnapshot();
8737
- return capturePreview({
8738
- externalNodeState: readWorkbenchHostExternalState({
8739
- externalStateSource: input.externalStateSource,
8740
- node,
8741
- workspaceId: input.workspaceId
8742
- }).externalNodeState,
8743
- isFocused: snapshot.nodeStack.at(-1) === node.id,
8744
- isMinimized: node.isMinimized,
8745
- node
8746
- });
8747
- },
8748
- [
8749
- input.externalStateSource,
8750
- input.hostSession,
8751
- input.nodeDefinitionByType,
8752
- input.workspaceId
8753
- ]
8754
- );
8755
9309
  const renderNode = useCallback11(
8756
9310
  (context) => {
8757
9311
  const definition = input.nodeDefinitionByType.get(
@@ -8887,6 +9441,7 @@ import { jsx as jsx13 } from "react/jsx-runtime";
8887
9441
  var noop3 = () => {
8888
9442
  };
8889
9443
  function WorkbenchHost({
9444
+ captureNodePreviewImage,
8890
9445
  className,
8891
9446
  contributions,
8892
9447
  debugDiagnostics,
@@ -8963,7 +9518,9 @@ function WorkbenchHost({
8963
9518
  workspaceId
8964
9519
  });
8965
9520
  const surfaceRenderers = useWorkbenchHostSurfaceRenderers({
9521
+ captureNodePreviewImage,
8966
9522
  chromeContext,
9523
+ debugDiagnostics,
8967
9524
  dockPreviewCache,
8968
9525
  dockPlacement,
8969
9526
  dockStateSource,