@tutti-os/workbench-surface 0.0.10 → 0.0.12

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.
@@ -62,6 +62,7 @@ var tuttiI18nModule2 = createScopedLocaleObjectsI18nModuleManifest2({
62
62
  });
63
63
  var workbenchMissionControlEn = {
64
64
  layoutSelectionHint: "Select multiple windows to arrange",
65
+ noAvailableLayout: "No layout fits the selected windows",
65
66
  presets: {
66
67
  balanced: "Balanced layout",
67
68
  column: "Vertical equal layout",
@@ -70,6 +71,7 @@ var workbenchMissionControlEn = {
70
71
  };
71
72
  var workbenchMissionControlZhCN = {
72
73
  layoutSelectionHint: "\u8BF7\u9009\u62E9\u591A\u4E2A\u4F60\u8981\u6574\u7406\u5E03\u5C40\u7684\u7A97\u53E3",
74
+ noAvailableLayout: "\u5F53\u524D\u6CA1\u6709\u53EF\u7528\u5E03\u5C40",
73
75
  presets: {
74
76
  balanced: "\u667A\u80FD\u5747\u8861\u5E03\u5C40",
75
77
  column: "\u5782\u76F4\u7B49\u5206\u5E03\u5C40",
@@ -187,4 +189,4 @@ export {
187
189
  workbenchHostI18nResources,
188
190
  createWorkbenchHostI18nRuntime
189
191
  };
190
- //# sourceMappingURL=chunk-QZ3DIAZK.js.map
192
+ //# sourceMappingURL=chunk-64XVF4I6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/react/workbenchWindowI18n.ts","../src/mission-control/workbenchMissionControlI18n.ts","../src/host/workbenchHostI18n.ts"],"sourcesContent":["import {\n createScopedLocaleObjectsI18nModuleManifest,\n createI18nRuntime,\n createScopedI18nRuntime,\n type I18nDictionary,\n type I18nRuntime\n} from \"@tutti-os/ui-i18n-runtime\";\n\ntype WorkbenchWindowChromeI18nLocale = \"en\" | \"zh-CN\";\nexport const workbenchWindowChromeI18nNamespace = \"workbenchWindowChrome\";\nexport const tuttiI18nModule = createScopedLocaleObjectsI18nModuleManifest({\n localeObjectByLocale: {\n en: \"workbenchWindowChromeEn\",\n \"zh-CN\": \"workbenchWindowChromeZhCN\"\n },\n name: \"workbench-window-chrome\",\n namespace: \"workbenchWindowChrome\",\n sourceRoot: \"packages/workbench/surface/src\"\n});\n\nconst workbenchWindowChromeEn = {\n enterFullscreen: \"Full Screen\",\n exitFullscreen: \"Exit Full Screen\"\n} as const satisfies I18nDictionary;\n\nconst workbenchWindowChromeZhCN = {\n enterFullscreen: \"全屏\",\n exitFullscreen: \"取消全屏\"\n} as const satisfies I18nDictionary;\n\nexport type WorkbenchWindowChromeI18nKey = keyof typeof workbenchWindowChromeEn;\n\nexport type WorkbenchWindowChromeI18nRuntime =\n I18nRuntime<WorkbenchWindowChromeI18nKey>;\n\nconst workbenchWindowChromeDefaults: Record<\n WorkbenchWindowChromeI18nLocale,\n I18nDictionary\n> = {\n en: workbenchWindowChromeEn,\n \"zh-CN\": workbenchWindowChromeZhCN\n};\n\nexport const workbenchWindowChromeI18nResources: Record<\n WorkbenchWindowChromeI18nLocale,\n I18nDictionary\n> = {\n en: {\n [workbenchWindowChromeI18nNamespace]: workbenchWindowChromeDefaults.en\n },\n \"zh-CN\": {\n [workbenchWindowChromeI18nNamespace]: workbenchWindowChromeDefaults[\"zh-CN\"]\n }\n};\n\nconst defaultWorkbenchWindowChromeI18n = createI18nRuntime({\n dictionaries: [workbenchWindowChromeI18nResources.en]\n});\n\nexport function createWorkbenchWindowChromeI18nRuntime(\n runtime: I18nRuntime<string> | undefined\n): WorkbenchWindowChromeI18nRuntime {\n return createScopedI18nRuntime<WorkbenchWindowChromeI18nKey>(\n runtime ?? defaultWorkbenchWindowChromeI18n,\n workbenchWindowChromeI18nNamespace\n );\n}\n","import {\n createScopedLocaleObjectsI18nModuleManifest,\n createI18nRuntime,\n createScopedI18nRuntime,\n type I18nDictionary,\n type I18nRuntime\n} from \"@tutti-os/ui-i18n-runtime\";\n\ntype WorkbenchMissionControlI18nLocale = \"en\" | \"zh-CN\";\n\nexport const workbenchMissionControlI18nNamespace = \"workbenchMissionControl\";\n\nexport const tuttiI18nModule = createScopedLocaleObjectsI18nModuleManifest({\n localeObjectByLocale: {\n en: \"workbenchMissionControlEn\",\n \"zh-CN\": \"workbenchMissionControlZhCN\"\n },\n name: \"workbench-mission-control\",\n namespace: \"workbenchMissionControl\",\n sourceRoot: \"packages/workbench/surface/src\"\n});\n\nconst workbenchMissionControlEn = {\n layoutSelectionHint: \"Select multiple windows to arrange\",\n noAvailableLayout: \"No layout fits the selected windows\",\n presets: {\n balanced: \"Balanced layout\",\n column: \"Vertical equal layout\",\n row: \"Horizontal equal layout\"\n }\n} as const satisfies I18nDictionary;\n\nconst workbenchMissionControlZhCN = {\n layoutSelectionHint: \"请选择多个你要整理布局的窗口\",\n noAvailableLayout: \"当前没有可用布局\",\n presets: {\n balanced: \"智能均衡布局\",\n column: \"垂直等分布局\",\n row: \"水平等分布局\"\n }\n} as const satisfies I18nDictionary;\n\nexport type WorkbenchMissionControlI18nKey =\n | \"layoutSelectionHint\"\n | \"noAvailableLayout\"\n | \"presets.balanced\"\n | \"presets.column\"\n | \"presets.row\";\n\nexport type WorkbenchMissionControlI18nRuntime =\n I18nRuntime<WorkbenchMissionControlI18nKey>;\n\nconst workbenchMissionControlDefaults: Record<\n WorkbenchMissionControlI18nLocale,\n I18nDictionary\n> = {\n en: workbenchMissionControlEn,\n \"zh-CN\": workbenchMissionControlZhCN\n};\n\nexport const workbenchMissionControlI18nResources: Record<\n WorkbenchMissionControlI18nLocale,\n I18nDictionary\n> = {\n en: {\n [workbenchMissionControlI18nNamespace]: workbenchMissionControlDefaults.en\n },\n \"zh-CN\": {\n [workbenchMissionControlI18nNamespace]:\n workbenchMissionControlDefaults[\"zh-CN\"]\n }\n};\n\nconst defaultWorkbenchMissionControlI18n = createI18nRuntime({\n dictionaries: [workbenchMissionControlI18nResources.en]\n});\n\nexport function createWorkbenchMissionControlI18nRuntime(\n runtime: I18nRuntime<string> | undefined\n): WorkbenchMissionControlI18nRuntime {\n return createScopedI18nRuntime<WorkbenchMissionControlI18nKey>(\n runtime ?? defaultWorkbenchMissionControlI18n,\n workbenchMissionControlI18nNamespace\n );\n}\n","import {\n createScopedLocaleObjectsI18nModuleManifest,\n createI18nRuntime,\n createScopedI18nRuntime,\n type I18nDictionary,\n type I18nRuntime\n} from \"@tutti-os/ui-i18n-runtime\";\n\ntype WorkbenchHostI18nLocale = \"en\" | \"zh-CN\";\nexport const workbenchHostI18nNamespace = \"workbenchHost\";\nexport const tuttiI18nModule = createScopedLocaleObjectsI18nModuleManifest({\n localeObjectByLocale: {\n en: \"workbenchHostEn\",\n \"zh-CN\": \"workbenchHostZhCN\"\n },\n name: \"workbench-host\",\n namespace: \"workbenchHost\",\n sourceRoot: \"packages/workbench/surface/src\"\n});\n\nconst workbenchHostEn = {\n actions: {\n close: \"Close\",\n minimize: \"Minimize\"\n },\n closeWindow: \"Close {{title}}\",\n dockLabel: \"Workbench dock\",\n launch: \"Open {{title}}\",\n minimizedWindows: \"Minimized windows\",\n newWindow: \"New window\",\n scrollDockDown: \"Scroll dock down\",\n scrollDockLeft: \"Scroll dock left\",\n scrollDockRight: \"Scroll dock right\",\n scrollDockUp: \"Scroll dock up\",\n windowStatus: {\n active: \"Active\",\n minimized: \"Minimized\",\n open: \"Open\"\n }\n} as const satisfies I18nDictionary;\n\nconst workbenchHostZhCN = {\n actions: {\n close: \"关闭\",\n minimize: \"最小化\"\n },\n closeWindow: \"关闭 {{title}}\",\n dockLabel: \"工作台程序坞\",\n launch: \"打开 {{title}}\",\n minimizedWindows: \"已最小化窗口\",\n newWindow: \"新建窗口\",\n scrollDockDown: \"向下滚动程序坞\",\n scrollDockLeft: \"向左滚动程序坞\",\n scrollDockRight: \"向右滚动程序坞\",\n scrollDockUp: \"向上滚动程序坞\",\n windowStatus: {\n active: \"当前窗口\",\n minimized: \"已最小化\",\n open: \"已打开\"\n }\n} as const satisfies I18nDictionary;\n\nexport type WorkbenchHostI18nKey =\n | \"actions.close\"\n | \"actions.minimize\"\n | \"closeWindow\"\n | \"dockLabel\"\n | \"launch\"\n | \"minimizedWindows\"\n | \"newWindow\"\n | \"scrollDockDown\"\n | \"scrollDockLeft\"\n | \"scrollDockRight\"\n | \"scrollDockUp\"\n | \"windowStatus.active\"\n | \"windowStatus.minimized\"\n | \"windowStatus.open\";\n\nexport type WorkbenchHostI18nRuntime = I18nRuntime<WorkbenchHostI18nKey>;\n\nconst workbenchHostDefaults: Record<WorkbenchHostI18nLocale, I18nDictionary> = {\n en: workbenchHostEn,\n \"zh-CN\": workbenchHostZhCN\n};\n\nexport const workbenchHostI18nResources = {\n en: {\n [workbenchHostI18nNamespace]: workbenchHostDefaults.en\n },\n \"zh-CN\": {\n [workbenchHostI18nNamespace]: workbenchHostDefaults[\"zh-CN\"]\n }\n} as const satisfies Record<WorkbenchHostI18nLocale, I18nDictionary>;\n\nconst defaultWorkbenchHostI18n = createI18nRuntime({\n dictionaries: [workbenchHostI18nResources.en]\n});\n\nexport function createWorkbenchHostI18nRuntime(\n runtime: I18nRuntime<string> | undefined\n): WorkbenchHostI18nRuntime {\n return createScopedI18nRuntime<WorkbenchHostI18nKey>(\n runtime ?? defaultWorkbenchHostI18n,\n workbenchHostI18nNamespace\n );\n}\n"],"mappings":"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","names":["createScopedLocaleObjectsI18nModuleManifest","createI18nRuntime","createScopedI18nRuntime","tuttiI18nModule","createScopedLocaleObjectsI18nModuleManifest","createI18nRuntime","createScopedI18nRuntime","tuttiI18nModule"]}
@@ -3,7 +3,7 @@ import { I18nRuntime, I18nDictionary } from '@tutti-os/ui-i18n-runtime';
3
3
 
4
4
  type WorkbenchMissionControlI18nLocale = "en" | "zh-CN";
5
5
  declare const workbenchMissionControlI18nNamespace = "workbenchMissionControl";
6
- type WorkbenchMissionControlI18nKey = "layoutSelectionHint" | "presets.balanced" | "presets.column" | "presets.row";
6
+ type WorkbenchMissionControlI18nKey = "layoutSelectionHint" | "noAvailableLayout" | "presets.balanced" | "presets.column" | "presets.row";
7
7
  type WorkbenchMissionControlI18nRuntime = I18nRuntime<WorkbenchMissionControlI18nKey>;
8
8
  declare const workbenchMissionControlI18nResources: Record<WorkbenchMissionControlI18nLocale, I18nDictionary>;
9
9
  declare function createWorkbenchMissionControlI18nRuntime(runtime: I18nRuntime<string> | undefined): WorkbenchMissionControlI18nRuntime;
@@ -8,7 +8,7 @@ import {
8
8
  workbenchMissionControlI18nResources,
9
9
  workbenchWindowChromeI18nNamespace,
10
10
  workbenchWindowChromeI18nResources
11
- } from "../chunk-QZ3DIAZK.js";
11
+ } from "../chunk-64XVF4I6.js";
12
12
  export {
13
13
  createWorkbenchHostI18nRuntime,
14
14
  createWorkbenchMissionControlI18nRuntime,
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  workbenchHostI18nResources,
6
6
  workbenchWindowChromeI18nNamespace,
7
7
  workbenchWindowChromeI18nResources
8
- } from "./chunk-QZ3DIAZK.js";
8
+ } from "./chunk-64XVF4I6.js";
9
9
 
10
10
  // src/store/commands.ts
11
11
  function createWorkbenchCommands(store) {
@@ -390,7 +390,7 @@ function getBalancedLayoutFrames(itemCount, frame, constraints) {
390
390
  return getGridLayoutFrames(itemCount, 2, frame, constraints);
391
391
  }
392
392
  if (itemCount >= 4) {
393
- return getGridLayoutFrames(itemCount, 2, frame, constraints);
393
+ return getBestBalancedGridLayoutFrames(itemCount, frame, constraints);
394
394
  }
395
395
  const gap = WORKBENCH_LAYOUT_PRESET_GAP_PX;
396
396
  const primaryWidth = Math.floor((frame.width - gap) * 0.58);
@@ -425,6 +425,26 @@ function getBalancedLayoutFrames(itemCount, frame, constraints) {
425
425
  }
426
426
  return frames;
427
427
  }
428
+ function getBestBalancedGridLayoutFrames(itemCount, frame, constraints) {
429
+ let best = null;
430
+ for (let columns = 2; columns <= itemCount; columns += 1) {
431
+ const rows = Math.ceil(itemCount / columns);
432
+ const frames = getGridLayoutFrames(itemCount, columns, frame, constraints);
433
+ if (!frames) {
434
+ continue;
435
+ }
436
+ const candidate = {
437
+ columns,
438
+ emptySlots: rows * columns - itemCount,
439
+ frames,
440
+ shapeDistance: Math.abs(columns - rows)
441
+ };
442
+ if (!best || candidate.shapeDistance < best.shapeDistance || candidate.shapeDistance === best.shapeDistance && candidate.emptySlots < best.emptySlots || candidate.shapeDistance === best.shapeDistance && candidate.emptySlots === best.emptySlots && candidate.columns < best.columns) {
443
+ best = candidate;
444
+ }
445
+ }
446
+ return best?.frames ?? null;
447
+ }
428
448
  function getSingleColumnLayoutFrames(itemCount, frame, constraints) {
429
449
  const verticalGap = WORKBENCH_LAYOUT_PRESET_GAP_PX * Math.max(0, itemCount - 1);
430
450
  const cellHeight = Math.floor((frame.height - verticalGap) / itemCount);
@@ -1035,6 +1055,9 @@ import {
1035
1055
  function shouldShowWorkbenchMissionControlLayoutHint(selectedCount) {
1036
1056
  return selectedCount < 2;
1037
1057
  }
1058
+ function shouldShowWorkbenchMissionControlNoAvailableLayoutMessage(selectedCount, hasUsablePreset) {
1059
+ return selectedCount >= 2 && !hasUsablePreset;
1060
+ }
1038
1061
  function shouldShowWorkbenchMissionControlLayoutPreset(selectedCount, preset) {
1039
1062
  if (selectedCount === 2) {
1040
1063
  return preset.kind !== "balanced";
@@ -1099,6 +1122,16 @@ function WorkbenchMissionControlOverlay({
1099
1122
  option.preset
1100
1123
  )
1101
1124
  );
1125
+ const showLayoutSelectionHint = shouldShowWorkbenchMissionControlLayoutHint(
1126
+ state.selectedCount
1127
+ );
1128
+ const hasUsableLayoutPreset = layoutPresets.some(
1129
+ (option) => state.canUsePreset(option.preset)
1130
+ );
1131
+ const showNoAvailableLayoutMessage = shouldShowWorkbenchMissionControlNoAvailableLayoutMessage(
1132
+ state.selectedCount,
1133
+ hasUsableLayoutPreset
1134
+ );
1102
1135
  useLayoutEffect(() => {
1103
1136
  const element = layoutDockContentRef.current;
1104
1137
  if (!element) {
@@ -1107,7 +1140,13 @@ function WorkbenchMissionControlOverlay({
1107
1140
  const computedStyle = window.getComputedStyle(element.parentElement);
1108
1141
  const horizontalChrome = Number.parseFloat(computedStyle.paddingLeft) + Number.parseFloat(computedStyle.paddingRight) + Number.parseFloat(computedStyle.borderLeftWidth) + Number.parseFloat(computedStyle.borderRightWidth);
1109
1142
  setLayoutDockWidth(element.scrollWidth + horizontalChrome);
1110
- }, [i18n, layoutPresets.length, state.selectedCount]);
1143
+ }, [
1144
+ i18n,
1145
+ layoutPresets.length,
1146
+ showLayoutSelectionHint,
1147
+ showNoAvailableLayoutMessage,
1148
+ state.selectedCount
1149
+ ]);
1111
1150
  return /* @__PURE__ */ jsx(
1112
1151
  "div",
1113
1152
  {
@@ -1124,9 +1163,7 @@ function WorkbenchMissionControlOverlay({
1124
1163
  {
1125
1164
  ref: layoutDockContentRef,
1126
1165
  className: "workbench-mission-control__layout-dock-content",
1127
- children: shouldShowWorkbenchMissionControlLayoutHint(
1128
- state.selectedCount
1129
- ) ? /* @__PURE__ */ jsx("span", { className: "workbench-mission-control__layout-hint", children: i18n.t("layoutSelectionHint") }) : /* @__PURE__ */ jsx("div", { className: "flex items-end gap-2", children: layoutPresets.map((option) => {
1166
+ children: showLayoutSelectionHint ? /* @__PURE__ */ jsx("span", { className: "workbench-mission-control__layout-hint", children: i18n.t("layoutSelectionHint") }) : showNoAvailableLayoutMessage ? /* @__PURE__ */ jsx("span", { className: "workbench-mission-control__layout-hint", children: i18n.t("noAvailableLayout") }) : /* @__PURE__ */ jsx("div", { className: "flex items-end gap-2", children: layoutPresets.map((option) => {
1130
1167
  const canApply = state.canApplyPreset(option.preset);
1131
1168
  const LayoutIcon = option.icon;
1132
1169
  return /* @__PURE__ */ jsx(
@@ -2606,6 +2643,14 @@ function resolveWorkbenchCaptureElement(windowElement) {
2606
2643
  '[data-workbench-window-capture="true"]'
2607
2644
  ) ?? windowElement.querySelector(".workbench-window") ?? windowElement;
2608
2645
  }
2646
+ function waitForNextAnimationFrame() {
2647
+ return new Promise((resolve) => {
2648
+ window.requestAnimationFrame(() => resolve());
2649
+ });
2650
+ }
2651
+ function isFocusedWorkbenchNode(controller, nodeID) {
2652
+ return controller.getSnapshot().nodeStack.at(-1) === nodeID;
2653
+ }
2609
2654
  function shouldReduceMotion() {
2610
2655
  return window.matchMedia?.("(prefers-reduced-motion: reduce)").matches === true;
2611
2656
  }
@@ -3076,9 +3121,7 @@ function useWorkbenchGenieAnimation({
3076
3121
  showNodeForGenie(nodeID);
3077
3122
  return;
3078
3123
  }
3079
- await new Promise((resolve) => {
3080
- window.requestAnimationFrame(() => resolve());
3081
- });
3124
+ await waitForNextAnimationFrame();
3082
3125
  if (generation !== animationGenerationRef.current) {
3083
3126
  return;
3084
3127
  }
@@ -3181,6 +3224,16 @@ function useWorkbenchGenieAnimation({
3181
3224
  }
3182
3225
  const runMinimize = minimize ?? (() => controller.commands.minimizeNode(nodeID));
3183
3226
  if (shouldReduceMotion()) {
3227
+ const wasFocusedForCapture2 = isFocusedWorkbenchNode(
3228
+ controller,
3229
+ nodeID
3230
+ );
3231
+ if (!wasFocusedForCapture2) {
3232
+ flushSync(() => {
3233
+ controller.commands.focusNode(nodeID);
3234
+ });
3235
+ await waitForNextAnimationFrame();
3236
+ }
3184
3237
  await captureWorkbenchNodePreviewImageForNode(target, {
3185
3238
  captureNodePreviewImage,
3186
3239
  dockPreviewCache,
@@ -3202,6 +3255,16 @@ function useWorkbenchGenieAnimation({
3202
3255
  runMinimize();
3203
3256
  return;
3204
3257
  }
3258
+ const wasFocusedForCapture = isFocusedWorkbenchNode(controller, nodeID);
3259
+ if (!wasFocusedForCapture) {
3260
+ flushSync(() => {
3261
+ controller.commands.focusNode(nodeID);
3262
+ });
3263
+ await waitForNextAnimationFrame();
3264
+ if (generation !== animationGenerationRef.current) {
3265
+ return;
3266
+ }
3267
+ }
3205
3268
  const previewImageUrlPromise = Promise.resolve(
3206
3269
  captureNodePreviewImage?.(target) ?? null
3207
3270
  ).catch(() => null);
@@ -3251,9 +3314,7 @@ function useWorkbenchGenieAnimation({
3251
3314
  hideNodeForGenie(nodeID);
3252
3315
  runMinimize();
3253
3316
  });
3254
- await new Promise((resolve) => {
3255
- window.requestAnimationFrame(() => resolve());
3256
- });
3317
+ await waitForNextAnimationFrame();
3257
3318
  if (generation !== animationGenerationRef.current) {
3258
3319
  releaseMinimizedDockEnterAnimation(nodeID);
3259
3320
  return;
@@ -5444,7 +5505,7 @@ function logWorkbenchHostDebug(event, debugDiagnostics, payload) {
5444
5505
  }
5445
5506
 
5446
5507
  // src/host/useWorkbenchHostSurfaceRenderers.tsx
5447
- import { useCallback as useCallback11, useMemo as useMemo6 } from "react";
5508
+ import { Component, useCallback as useCallback11, useMemo as useMemo6 } from "react";
5448
5509
 
5449
5510
  // src/host/WorkbenchHostDock.tsx
5450
5511
  import {
@@ -6118,6 +6179,40 @@ function useMinimizedDockStackPromotion(slots) {
6118
6179
  return { promotedNodeId, stackDispatching };
6119
6180
  }
6120
6181
 
6182
+ // src/host/minimizedDockRestoreIntent.ts
6183
+ function resolveWorkbenchMinimizedDockRestoreIntent(input) {
6184
+ const { source } = input;
6185
+ switch (source.kind) {
6186
+ case "node-slot": {
6187
+ const slot = input.slots.find(
6188
+ (slot2) => slot2.anchorKey === source.anchorKey
6189
+ );
6190
+ if (slot?.kind !== "node" || slot.node.id !== input.nodeId) {
6191
+ return null;
6192
+ }
6193
+ return {
6194
+ anchorKey: slot.anchorKey,
6195
+ kind: "node-slot",
6196
+ nodeId: input.nodeId
6197
+ };
6198
+ }
6199
+ case "stack-popup-card": {
6200
+ const slot = input.slots.find(
6201
+ (slot2) => slot2.anchorKey === source.stackAnchorKey
6202
+ );
6203
+ if (slot?.kind !== "stack" || !slot.nodes.some((node) => node.id === input.nodeId)) {
6204
+ return null;
6205
+ }
6206
+ return {
6207
+ anchorKey: slot.anchorKey,
6208
+ kind: "stack-popup-card",
6209
+ nodeId: input.nodeId,
6210
+ stackAnchorKey: slot.anchorKey
6211
+ };
6212
+ }
6213
+ }
6214
+ }
6215
+
6121
6216
  // src/host/WorkbenchHostDockPopup.tsx
6122
6217
  import {
6123
6218
  forwardRef,
@@ -7272,6 +7367,10 @@ function WorkbenchHostDock({
7272
7367
  clearTimeout(timer);
7273
7368
  collapsingMinimizedLaunchTimerRef.current.delete(anchorKey);
7274
7369
  }
7370
+ const slotElement = slotRefs.current.get(anchorKey);
7371
+ slotElement?.removeAttribute("data-collapsing");
7372
+ slotElement?.style.removeProperty("--desktop-dock-collapse-inline-size");
7373
+ slotElement?.style.removeProperty("--desktop-dock-collapse-block-size");
7275
7374
  setCollapsingMinimizedLaunchAnchorKeys((current) => {
7276
7375
  if (!current.has(anchorKey)) {
7277
7376
  return current;
@@ -7637,7 +7736,8 @@ function WorkbenchHostDock({
7637
7736
  ]
7638
7737
  );
7639
7738
  const runDockMinimizedLaunchAfterCollapse = useCallback10(
7640
- (anchorKey, launch) => {
7739
+ (intent, launch) => {
7740
+ const { anchorKey } = intent;
7641
7741
  beginDockMinimizedInteraction(anchorKey);
7642
7742
  setCollapsingMinimizedLaunchAnchorKeys((current) => {
7643
7743
  const next = new Set(current);
@@ -7645,10 +7745,17 @@ function WorkbenchHostDock({
7645
7745
  return next;
7646
7746
  });
7647
7747
  scheduleCollapsingMinimizedLaunchClear(anchorKey);
7648
- launch();
7748
+ launch(intent);
7649
7749
  },
7650
7750
  [beginDockMinimizedInteraction, scheduleCollapsingMinimizedLaunchClear]
7651
7751
  );
7752
+ const runDockMinimizedStackLaunch = useCallback10(
7753
+ (intent, launch) => {
7754
+ beginDockMinimizedInteraction();
7755
+ launch(intent);
7756
+ },
7757
+ [beginDockMinimizedInteraction]
7758
+ );
7652
7759
  const handleDockPointerTravel = useCallback10(
7653
7760
  (clientX, clientY) => {
7654
7761
  if (activeHoverPanelRef.current !== null) {
@@ -8273,18 +8380,42 @@ function WorkbenchHostDock({
8273
8380
  title: node.title,
8274
8381
  type: "button",
8275
8382
  onPointerDown: () => {
8276
- beginDockMinimizedInteraction(slot.anchorKey);
8383
+ const restoreIntent = resolveWorkbenchMinimizedDockRestoreIntent({
8384
+ nodeId: node.id,
8385
+ slots: minimizedDockSlots,
8386
+ source: {
8387
+ anchorKey: slot.anchorKey,
8388
+ kind: "node-slot"
8389
+ }
8390
+ });
8391
+ if (restoreIntent?.kind !== "node-slot") {
8392
+ clearCollapsingMinimizedLaunch(slot.anchorKey);
8393
+ return;
8394
+ }
8395
+ beginDockMinimizedInteraction();
8277
8396
  },
8278
8397
  onClick: () => {
8398
+ const restoreIntent = resolveWorkbenchMinimizedDockRestoreIntent({
8399
+ nodeId: node.id,
8400
+ slots: minimizedDockSlots,
8401
+ source: {
8402
+ anchorKey: slot.anchorKey,
8403
+ kind: "node-slot"
8404
+ }
8405
+ });
8406
+ if (restoreIntent?.kind !== "node-slot") {
8407
+ clearCollapsingMinimizedLaunch(slot.anchorKey);
8408
+ return;
8409
+ }
8279
8410
  closePopup();
8280
8411
  runDockMinimizedLaunchAfterCollapse(
8281
- slot.anchorKey,
8282
- () => {
8412
+ restoreIntent,
8413
+ (intent) => {
8283
8414
  context.genie.launchNodeFromAnchor(
8284
- slot.anchorKey,
8285
- node.id,
8415
+ intent.anchorKey,
8416
+ intent.nodeId,
8286
8417
  () => {
8287
- host.focusNode(node.id);
8418
+ host.focusNode(intent.nodeId);
8288
8419
  }
8289
8420
  );
8290
8421
  }
@@ -8555,15 +8686,26 @@ function WorkbenchHostDock({
8555
8686
  },
8556
8687
  onCreateNew: () => void 0,
8557
8688
  onSelectNode: (nodeId) => {
8558
- closePopup();
8559
- const anchorKey = resolveWorkbenchMinimizedDockAnchorKeyForNode({
8689
+ const restoreIntent = resolveWorkbenchMinimizedDockRestoreIntent({
8560
8690
  nodeId,
8561
- slots: minimizedDockSlots
8562
- }) ?? activeMinimizedStackSlot.anchorKey;
8563
- runDockMinimizedLaunchAfterCollapse(anchorKey, () => {
8564
- context.genie.launchNodeFromAnchor(anchorKey, nodeId, () => {
8565
- host.focusNode(nodeId);
8566
- });
8691
+ slots: minimizedDockSlots,
8692
+ source: {
8693
+ kind: "stack-popup-card",
8694
+ stackAnchorKey: activeMinimizedStackSlot.anchorKey
8695
+ }
8696
+ });
8697
+ if (restoreIntent?.kind !== "stack-popup-card") {
8698
+ return;
8699
+ }
8700
+ closePopup();
8701
+ runDockMinimizedStackLaunch(restoreIntent, (intent) => {
8702
+ context.genie.launchNodeFromAnchor(
8703
+ intent.anchorKey,
8704
+ intent.nodeId,
8705
+ () => {
8706
+ host.focusNode(intent.nodeId);
8707
+ }
8708
+ );
8567
8709
  });
8568
8710
  },
8569
8711
  showCreateNew: false,
@@ -9230,14 +9372,62 @@ function WorkbenchHostWindowActions({
9230
9372
  // src/host/useWorkbenchHostSurfaceRenderers.tsx
9231
9373
  import { jsx as jsx12 } from "react/jsx-runtime";
9232
9374
  function useWorkbenchHostSurfaceRenderers(input) {
9233
- const renderBottomChrome = useMemo6(
9234
- () => input.renderBottomChrome ? () => input.renderBottomChrome?.(input.chromeContext) : void 0,
9235
- [input.chromeContext, input.renderBottomChrome]
9236
- );
9237
- const renderTopChrome = useMemo6(
9238
- () => input.renderTopChrome ? () => input.renderTopChrome?.(input.chromeContext) : void 0,
9239
- [input.chromeContext, input.renderTopChrome]
9240
- );
9375
+ const renderBottomChrome = useMemo6(() => {
9376
+ const renderChrome = input.renderBottomChrome;
9377
+ return renderChrome ? () => /* @__PURE__ */ jsx12(
9378
+ WorkbenchHostSurfaceRenderErrorBoundary,
9379
+ {
9380
+ debugDiagnostics: input.debugDiagnostics,
9381
+ details: {
9382
+ chromeSlot: "bottom"
9383
+ },
9384
+ event: "workbench.chrome.render_error",
9385
+ fallbackKind: "bottom-chrome",
9386
+ resetKey: `${input.workspaceId}:bottom-chrome`,
9387
+ workspaceId: input.workspaceId,
9388
+ children: /* @__PURE__ */ jsx12(
9389
+ WorkbenchHostChromeRenderer,
9390
+ {
9391
+ context: input.chromeContext,
9392
+ renderChrome
9393
+ }
9394
+ )
9395
+ }
9396
+ ) : void 0;
9397
+ }, [
9398
+ input.chromeContext,
9399
+ input.debugDiagnostics,
9400
+ input.renderBottomChrome,
9401
+ input.workspaceId
9402
+ ]);
9403
+ const renderTopChrome = useMemo6(() => {
9404
+ const renderChrome = input.renderTopChrome;
9405
+ return renderChrome ? () => /* @__PURE__ */ jsx12(
9406
+ WorkbenchHostSurfaceRenderErrorBoundary,
9407
+ {
9408
+ debugDiagnostics: input.debugDiagnostics,
9409
+ details: {
9410
+ chromeSlot: "top"
9411
+ },
9412
+ event: "workbench.chrome.render_error",
9413
+ fallbackKind: "top-chrome",
9414
+ resetKey: `${input.workspaceId}:top-chrome`,
9415
+ workspaceId: input.workspaceId,
9416
+ children: /* @__PURE__ */ jsx12(
9417
+ WorkbenchHostChromeRenderer,
9418
+ {
9419
+ context: input.chromeContext,
9420
+ renderChrome
9421
+ }
9422
+ )
9423
+ }
9424
+ ) : void 0;
9425
+ }, [
9426
+ input.chromeContext,
9427
+ input.debugDiagnostics,
9428
+ input.renderTopChrome,
9429
+ input.workspaceId
9430
+ ]);
9241
9431
  const captureNodePreviewImage = useCallback11(
9242
9432
  async (node) => {
9243
9433
  const definition = input.nodeDefinitionByType.get(node.data.typeId);
@@ -9272,22 +9462,36 @@ function useWorkbenchHostSurfaceRenderers(input) {
9272
9462
  );
9273
9463
  const renderDock = useCallback11(
9274
9464
  (context) => /* @__PURE__ */ jsx12(
9275
- WorkbenchHostDock,
9465
+ WorkbenchHostSurfaceRenderErrorBoundary,
9276
9466
  {
9277
- captureNodePreviewImage,
9278
- context,
9279
9467
  debugDiagnostics: input.debugDiagnostics,
9280
- dockEntries: input.dockEntries,
9281
- dockPlacement: input.dockPlacement,
9282
- dockPreviewCache: input.dockPreviewCache,
9283
- dockStateSource: input.dockStateSource,
9284
- externalStateSource: input.externalStateSource,
9285
- host: input.hostSession,
9286
- i18n: input.hostI18n,
9287
- nodeDefinitions: input.nodeDefinitionByType,
9288
- onDockEntryAction: input.onDockEntryAction,
9289
- onDockEntryClick: input.onDockEntryClick,
9290
- workspaceId: input.workspaceId
9468
+ details: {
9469
+ dockEntryCount: input.dockEntries.length,
9470
+ dockPlacement: input.dockPlacement ?? null
9471
+ },
9472
+ event: "workbench.dock.render_error",
9473
+ fallbackKind: "dock",
9474
+ resetKey: `${input.workspaceId}:dock`,
9475
+ workspaceId: input.workspaceId,
9476
+ children: /* @__PURE__ */ jsx12(
9477
+ WorkbenchHostDock,
9478
+ {
9479
+ captureNodePreviewImage,
9480
+ context,
9481
+ debugDiagnostics: input.debugDiagnostics,
9482
+ dockEntries: input.dockEntries,
9483
+ dockPlacement: input.dockPlacement,
9484
+ dockPreviewCache: input.dockPreviewCache,
9485
+ dockStateSource: input.dockStateSource,
9486
+ externalStateSource: input.externalStateSource,
9487
+ host: input.hostSession,
9488
+ i18n: input.hostI18n,
9489
+ nodeDefinitions: input.nodeDefinitionByType,
9490
+ onDockEntryAction: input.onDockEntryAction,
9491
+ onDockEntryClick: input.onDockEntryClick,
9492
+ workspaceId: input.workspaceId
9493
+ }
9494
+ )
9291
9495
  }
9292
9496
  ),
9293
9497
  [
@@ -9314,17 +9518,32 @@ function useWorkbenchHostSurfaceRenderers(input) {
9314
9518
  if (!definition) {
9315
9519
  return null;
9316
9520
  }
9317
- return definition.renderBody(
9318
- createWorkbenchHostNodeBodyContext({
9319
- context,
9320
- definition,
9321
- externalStateSource: input.externalStateSource,
9322
- host: input.hostSession,
9323
- workspaceId: input.workspaceId
9324
- })
9521
+ const bodyContext = createWorkbenchHostNodeBodyContext({
9522
+ context,
9523
+ definition,
9524
+ externalStateSource: input.externalStateSource,
9525
+ host: input.hostSession,
9526
+ workspaceId: input.workspaceId
9527
+ });
9528
+ return /* @__PURE__ */ jsx12(
9529
+ WorkbenchHostNodeRenderErrorBoundary,
9530
+ {
9531
+ debugDiagnostics: input.debugDiagnostics,
9532
+ node: context.node,
9533
+ resetKey: `${context.node.id}:${context.node.data.typeId}`,
9534
+ workspaceId: input.workspaceId,
9535
+ children: /* @__PURE__ */ jsx12(
9536
+ WorkbenchHostNodeBodyRenderer,
9537
+ {
9538
+ context: bodyContext,
9539
+ definition
9540
+ }
9541
+ )
9542
+ }
9325
9543
  );
9326
9544
  },
9327
9545
  [
9546
+ input.debugDiagnostics,
9328
9547
  input.externalStateSource,
9329
9548
  input.hostSession,
9330
9549
  input.nodeDefinitionByType,
@@ -9435,6 +9654,114 @@ function useWorkbenchHostSurfaceRenderers(input) {
9435
9654
  windowChromeMode
9436
9655
  };
9437
9656
  }
9657
+ var WorkbenchHostSurfaceRenderErrorBoundary = class extends Component {
9658
+ state = {
9659
+ hasError: false
9660
+ };
9661
+ static getDerivedStateFromError() {
9662
+ return { hasError: true };
9663
+ }
9664
+ componentDidUpdate(previousProps) {
9665
+ if (this.state.hasError && previousProps.resetKey !== this.props.resetKey) {
9666
+ this.setState({ hasError: false });
9667
+ }
9668
+ }
9669
+ componentDidCatch(error, info) {
9670
+ try {
9671
+ const result = this.props.debugDiagnostics?.log?.({
9672
+ details: {
9673
+ ...this.props.details ?? {},
9674
+ componentStack: info.componentStack ?? null,
9675
+ errorMessage: error instanceof Error ? error.message : String(error),
9676
+ errorName: error instanceof Error ? error.name : typeof error,
9677
+ errorStack: error instanceof Error ? error.stack ?? null : null
9678
+ },
9679
+ event: this.props.event,
9680
+ level: "error",
9681
+ source: "workbench-host",
9682
+ workspaceId: this.props.workspaceId
9683
+ });
9684
+ void Promise.resolve(result).catch(() => void 0);
9685
+ } catch {
9686
+ }
9687
+ }
9688
+ render() {
9689
+ if (this.state.hasError) {
9690
+ return /* @__PURE__ */ jsx12(
9691
+ "div",
9692
+ {
9693
+ "data-workbench-surface-render-error": this.props.fallbackKind,
9694
+ style: { height: "100%", width: "100%" }
9695
+ }
9696
+ );
9697
+ }
9698
+ return this.props.children;
9699
+ }
9700
+ };
9701
+ var WorkbenchHostNodeRenderErrorBoundary = class extends Component {
9702
+ state = {
9703
+ hasError: false
9704
+ };
9705
+ static getDerivedStateFromError() {
9706
+ return { hasError: true };
9707
+ }
9708
+ componentDidUpdate(previousProps) {
9709
+ if (this.state.hasError && previousProps.resetKey !== this.props.resetKey) {
9710
+ this.setState({ hasError: false });
9711
+ }
9712
+ }
9713
+ componentDidCatch(error, info) {
9714
+ try {
9715
+ const result = this.props.debugDiagnostics?.log?.({
9716
+ details: {
9717
+ componentStack: info.componentStack ?? null,
9718
+ dockEntryId: this.props.node.data.dockEntryId ?? null,
9719
+ displayMode: this.props.node.displayMode,
9720
+ errorMessage: error instanceof Error ? error.message : String(error),
9721
+ errorName: error instanceof Error ? error.name : typeof error,
9722
+ errorStack: error instanceof Error ? error.stack ?? null : null,
9723
+ frame: this.props.node.frame,
9724
+ instanceId: this.props.node.data.instanceId,
9725
+ instanceKey: this.props.node.data.instanceKey ?? null,
9726
+ isMinimized: this.props.node.isMinimized,
9727
+ launchSource: this.props.node.data.launchSource ?? null,
9728
+ nodeId: this.props.node.id,
9729
+ typeId: this.props.node.data.typeId
9730
+ },
9731
+ event: "workbench.node.render_error",
9732
+ level: "error",
9733
+ source: "workbench-host",
9734
+ workspaceId: this.props.workspaceId
9735
+ });
9736
+ void Promise.resolve(result).catch(() => void 0);
9737
+ } catch {
9738
+ }
9739
+ }
9740
+ render() {
9741
+ if (this.state.hasError) {
9742
+ return /* @__PURE__ */ jsx12(
9743
+ "div",
9744
+ {
9745
+ "data-workbench-node-render-error": "true",
9746
+ style: { height: "100%", width: "100%" }
9747
+ }
9748
+ );
9749
+ }
9750
+ return this.props.children;
9751
+ }
9752
+ };
9753
+ function WorkbenchHostNodeBodyRenderer({
9754
+ context,
9755
+ definition
9756
+ }) {
9757
+ return definition.renderBody(context);
9758
+ }
9759
+ function WorkbenchHostChromeRenderer({
9760
+ context,
9761
+ renderChrome
9762
+ }) {
9763
+ return renderChrome(context);
9764
+ }
9438
9765
 
9439
9766
  // src/host/WorkbenchHost.tsx
9440
9767
  import { jsx as jsx13 } from "react/jsx-runtime";