@tutti-os/workbench-surface 0.0.207 → 0.0.209

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/README.md CHANGED
@@ -149,6 +149,12 @@ Layout rules:
149
149
  - fullscreen nodes respect top, left, and right safe area, but ignore bottom
150
150
  safe area so immersive content can reach the bottom edge of the workbench
151
151
  surface
152
+ - persisted snapshots record the surface and layout constraints that produced
153
+ their frames; initial restore maps node frames, fullscreen restore frames,
154
+ space frames, and reusable closed-window frames into the current safe layout
155
+ - legacy snapshots without a layout basis remain readable; fullscreen geometry
156
+ is recomputed and hidden restore frames are clamped before they become
157
+ floating windows
152
158
 
153
159
  The package ships a baseline host layout default so new integrations do not
154
160
  start with windows tucked under shared chrome. Today that default uses:
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { WorkbenchFrame as WorkbenchFrame$1, WorkbenchSnapshotDisplayModeV1, WorkbenchSnapshot, WorkbenchSnapshotNode } from '@tutti-os/workbench-snapshot';
1
+ import { WorkbenchFrame as WorkbenchFrame$1, WorkbenchSnapshotDisplayModeV1, WorkbenchSnapshot, WorkbenchSnapshotNode, WorkbenchSnapshotLayoutBasisV1 } from '@tutti-os/workbench-snapshot';
2
2
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
3
  import { HTMLAttributes, ReactNode, CSSProperties } from 'react';
4
4
  import { I18nRuntime } from '@tutti-os/ui-i18n-runtime';
@@ -234,6 +234,7 @@ interface WorkbenchController<TData = unknown> extends WorkbenchStore<TData> {
234
234
 
235
235
  declare function createWorkbenchController<TData = unknown>(initialState?: Partial<WorkbenchState<TData>>, options?: {
236
236
  debugDiagnostics?: WorkbenchDebugDiagnostics;
237
+ onSurfaceSizeMeasured?: () => void;
237
238
  }): WorkbenchController<TData>;
238
239
 
239
240
  type WorkbenchMissionControlMode = "activate" | "layout";
@@ -916,7 +917,8 @@ interface CreateWorkbenchSnapshotFromStateOptions {
916
917
  declare function createWorkbenchNode<TData = unknown>(input: CreateWorkbenchNodeInput<TData>): WorkbenchNode<TData>;
917
918
  declare function createWorkbenchNodeFromSnapshot<TData = unknown>(snapshotNode: WorkbenchSnapshotNode): WorkbenchNode<TData>;
918
919
  declare function createWorkbenchStateFromSnapshot<TData = unknown>(snapshot: WorkbenchSnapshot): Pick<WorkbenchState<TData>, "nodeStack" | "nodes">;
919
- declare function createWorkbenchSnapshotFromState<TData = unknown>(state: Pick<WorkbenchState<TData>, "nodeStack" | "nodes">, options?: CreateWorkbenchSnapshotFromStateOptions): WorkbenchSnapshot;
920
+ declare function createWorkbenchSnapshotFromState<TData = unknown>(state: Pick<WorkbenchState<TData>, "nodeStack" | "nodes"> & Partial<Pick<WorkbenchState<TData>, "layoutConstraints" | "surfaceSize">>, options?: CreateWorkbenchSnapshotFromStateOptions): WorkbenchSnapshot;
921
+ declare function createWorkbenchSnapshotLayoutBasis(state: Partial<Pick<WorkbenchState, "layoutConstraints" | "surfaceSize">>): WorkbenchSnapshotLayoutBasisV1 | undefined;
920
922
 
921
923
  interface WorkbenchHostNodeIdentityInput {
922
924
  instanceId: string;
@@ -980,4 +982,4 @@ interface CreateMultiInstanceDockEntryOptionsInput {
980
982
  declare function createMultiInstanceDockEntryOptions(launchPayload?: unknown, input?: CreateMultiInstanceDockEntryOptionsInput): Pick<WorkbenchHostDockEntry, "allowNewWindowInDockPopup" | "instanceMode" | "launchBehavior" | "launchPayload" | "newWindowLaunchPayload">;
981
983
  declare function resolveWorkbenchDockCountBadge(activeNodeCount: number): WorkbenchHostDockEntry["badge"];
982
984
 
983
- export { type CreateWorkbenchNodeInput, type CreateWorkbenchSnapshotFromStateOptions, type ResolvedWorkbenchHostDockEntry, type WorkbenchContribution, type WorkbenchController, type WorkbenchDebugDiagnostics, type WorkbenchDisplayMode, WorkbenchDockComponentPreviewFrame, type WorkbenchDockComponentPreviewFrameProps, type WorkbenchDockContext, type WorkbenchDockPlacement, type WorkbenchDockPreviewCache, type WorkbenchDockPreviewCacheKey, type WorkbenchDockPreviewCacheKeyResolver, type WorkbenchDockPreviewContent, type WorkbenchFrame, type WorkbenchFullscreenHeaderMode, WorkbenchHost, type WorkbenchHostActivation, type WorkbenchHostActivationTarget, type WorkbenchHostChromeRenderContext, type WorkbenchHostCloseDialogRequest, type WorkbenchHostCloseDialogScope, type WorkbenchHostCloseDialogVariant, type WorkbenchHostCloseEffect, type WorkbenchHostClosePreparationContext, type WorkbenchHostClosePreparer, type WorkbenchHostDockClickResolution, type WorkbenchHostDockEntry, type WorkbenchHostDockEntryAction, type WorkbenchHostDockEntryBadge, type WorkbenchHostDockEntryDynamicState, type WorkbenchHostDockEntryLaunchBehavior, type WorkbenchHostDockEntryPresentationOverride, type WorkbenchHostDockEntryPresentationOverrides, type WorkbenchHostDockEntryState, type WorkbenchHostDockEntryStateKind, type WorkbenchHostDockEntryStateSource, type WorkbenchHostDockEntryVisibility, type WorkbenchHostDockNodeState, type WorkbenchHostDockPopupItemDescriptor, type WorkbenchHostDockPopupItemInput, type WorkbenchHostExternalStateLookupInput, type WorkbenchHostExternalStateSource, type WorkbenchHostHandle, type WorkbenchHostLaunchFramePolicy, type WorkbenchHostLaunchInput, type WorkbenchHostLaunchReason, type WorkbenchHostLaunchRequest, type WorkbenchHostLaunchResult, type WorkbenchHostMissionControlMode, type WorkbenchHostMissionControlProps, type WorkbenchHostMultiInstanceStrategy, type WorkbenchHostNodeBodyContext, type WorkbenchHostNodeCloseDecision, type WorkbenchHostNodeCloseRequest, type WorkbenchHostNodeData, type WorkbenchHostNodeDefinition, type WorkbenchHostNodeHeaderContext, type WorkbenchHostNodeHeaderFrameRenderKey, type WorkbenchHostNodeHeaderWindowActions, type WorkbenchHostNodeIdentityInput, type WorkbenchHostNodeInstanceStrategy, type WorkbenchHostNodeMinimizedDockCapability, type WorkbenchHostNodeWindowCapabilities, type WorkbenchHostProjectedNode, type WorkbenchHostProjectedNodeSubject, type WorkbenchHostProps, type WorkbenchHostSingleInstanceStrategy, type WorkbenchHostSnapshotRepository, type WorkbenchHostWindowCloseEffectContext, type WorkbenchLayoutConstraints, type WorkbenchLayoutConstraintsInput, type WorkbenchLayoutPreset, type WorkbenchMissionControlAdapter, type WorkbenchMissionControlMode, type WorkbenchMissionControlSnapshot, type WorkbenchNode, type WorkbenchNodeRenderFrame, type WorkbenchNodeSizeConstraints, type WorkbenchQuickLayoutTarget, type WorkbenchRenderNode, type WorkbenchRenderNodeContext, type WorkbenchRenderWindowActions, type WorkbenchRenderWindowHeader, type WorkbenchResolveFullscreenHeaderMode, type WorkbenchResolveWindowChromeMode, type WorkbenchResolveWindowChromeModeContext, type WorkbenchResolveWindowHeaderPresentation, type WorkbenchSafeArea, type WorkbenchSize, type WorkbenchState, WorkbenchSurface, type WorkbenchSurfacePresentation, type WorkbenchSurfaceProps, type WorkbenchSurfaceWallpaper, type WorkbenchSurfaceWallpaperFit, type WorkbenchWindowActionContext, WorkbenchWindowChromeI18nRuntime, type WorkbenchWindowChromeMode, type WorkbenchWindowHeaderContext, type WorkbenchWindowHeaderPresentation, type WorkbenchWindowManagementConfig, canCreateNewWindow, canCreateNewWindowInDockPopup, createDockNewWindowLaunchPayload, createMultiInstanceDockEntryOptions, createWorkbenchController, createWorkbenchHostLaunchedNodeId, createWorkbenchHostProjectedNodeId, createWorkbenchNode, createWorkbenchNodeFromSnapshot, createWorkbenchSnapshotFromState, createWorkbenchStateFromSnapshot, getWorkbenchLayoutFrame, isEditableShortcutTarget, matchWorkbenchDockEntryNode, resolveWorkbenchDockCountBadge, resolveWorkbenchDockEntries, resolveWorkbenchDockEntryClick, resolveWorkbenchHostPrepareClose, selectFocusedWorkbenchNode, useWorkbenchSelector, workbenchFocusInputActivationType };
985
+ export { type CreateWorkbenchNodeInput, type CreateWorkbenchSnapshotFromStateOptions, type ResolvedWorkbenchHostDockEntry, type WorkbenchContribution, type WorkbenchController, type WorkbenchDebugDiagnostics, type WorkbenchDisplayMode, WorkbenchDockComponentPreviewFrame, type WorkbenchDockComponentPreviewFrameProps, type WorkbenchDockContext, type WorkbenchDockPlacement, type WorkbenchDockPreviewCache, type WorkbenchDockPreviewCacheKey, type WorkbenchDockPreviewCacheKeyResolver, type WorkbenchDockPreviewContent, type WorkbenchFrame, type WorkbenchFullscreenHeaderMode, WorkbenchHost, type WorkbenchHostActivation, type WorkbenchHostActivationTarget, type WorkbenchHostChromeRenderContext, type WorkbenchHostCloseDialogRequest, type WorkbenchHostCloseDialogScope, type WorkbenchHostCloseDialogVariant, type WorkbenchHostCloseEffect, type WorkbenchHostClosePreparationContext, type WorkbenchHostClosePreparer, type WorkbenchHostDockClickResolution, type WorkbenchHostDockEntry, type WorkbenchHostDockEntryAction, type WorkbenchHostDockEntryBadge, type WorkbenchHostDockEntryDynamicState, type WorkbenchHostDockEntryLaunchBehavior, type WorkbenchHostDockEntryPresentationOverride, type WorkbenchHostDockEntryPresentationOverrides, type WorkbenchHostDockEntryState, type WorkbenchHostDockEntryStateKind, type WorkbenchHostDockEntryStateSource, type WorkbenchHostDockEntryVisibility, type WorkbenchHostDockNodeState, type WorkbenchHostDockPopupItemDescriptor, type WorkbenchHostDockPopupItemInput, type WorkbenchHostExternalStateLookupInput, type WorkbenchHostExternalStateSource, type WorkbenchHostHandle, type WorkbenchHostLaunchFramePolicy, type WorkbenchHostLaunchInput, type WorkbenchHostLaunchReason, type WorkbenchHostLaunchRequest, type WorkbenchHostLaunchResult, type WorkbenchHostMissionControlMode, type WorkbenchHostMissionControlProps, type WorkbenchHostMultiInstanceStrategy, type WorkbenchHostNodeBodyContext, type WorkbenchHostNodeCloseDecision, type WorkbenchHostNodeCloseRequest, type WorkbenchHostNodeData, type WorkbenchHostNodeDefinition, type WorkbenchHostNodeHeaderContext, type WorkbenchHostNodeHeaderFrameRenderKey, type WorkbenchHostNodeHeaderWindowActions, type WorkbenchHostNodeIdentityInput, type WorkbenchHostNodeInstanceStrategy, type WorkbenchHostNodeMinimizedDockCapability, type WorkbenchHostNodeWindowCapabilities, type WorkbenchHostProjectedNode, type WorkbenchHostProjectedNodeSubject, type WorkbenchHostProps, type WorkbenchHostSingleInstanceStrategy, type WorkbenchHostSnapshotRepository, type WorkbenchHostWindowCloseEffectContext, type WorkbenchLayoutConstraints, type WorkbenchLayoutConstraintsInput, type WorkbenchLayoutPreset, type WorkbenchMissionControlAdapter, type WorkbenchMissionControlMode, type WorkbenchMissionControlSnapshot, type WorkbenchNode, type WorkbenchNodeRenderFrame, type WorkbenchNodeSizeConstraints, type WorkbenchQuickLayoutTarget, type WorkbenchRenderNode, type WorkbenchRenderNodeContext, type WorkbenchRenderWindowActions, type WorkbenchRenderWindowHeader, type WorkbenchResolveFullscreenHeaderMode, type WorkbenchResolveWindowChromeMode, type WorkbenchResolveWindowChromeModeContext, type WorkbenchResolveWindowHeaderPresentation, type WorkbenchSafeArea, type WorkbenchSize, type WorkbenchState, WorkbenchSurface, type WorkbenchSurfacePresentation, type WorkbenchSurfaceProps, type WorkbenchSurfaceWallpaper, type WorkbenchSurfaceWallpaperFit, type WorkbenchWindowActionContext, WorkbenchWindowChromeI18nRuntime, type WorkbenchWindowChromeMode, type WorkbenchWindowHeaderContext, type WorkbenchWindowHeaderPresentation, type WorkbenchWindowManagementConfig, canCreateNewWindow, canCreateNewWindowInDockPopup, createDockNewWindowLaunchPayload, createMultiInstanceDockEntryOptions, createWorkbenchController, createWorkbenchHostLaunchedNodeId, createWorkbenchHostProjectedNodeId, createWorkbenchNode, createWorkbenchNodeFromSnapshot, createWorkbenchSnapshotFromState, createWorkbenchSnapshotLayoutBasis, createWorkbenchStateFromSnapshot, getWorkbenchLayoutFrame, isEditableShortcutTarget, matchWorkbenchDockEntryNode, resolveWorkbenchDockCountBadge, resolveWorkbenchDockEntries, resolveWorkbenchDockEntryClick, resolveWorkbenchHostPrepareClose, selectFocusedWorkbenchNode, useWorkbenchSelector, workbenchFocusInputActivationType };
package/dist/index.js CHANGED
@@ -776,7 +776,12 @@ function reduceWorkbenchState(state, action) {
776
776
  return {
777
777
  ...node,
778
778
  displayMode: "floating",
779
- frame: node.restoreFrame ?? node.frame,
779
+ frame: clampWorkbenchRect(
780
+ node.restoreFrame ?? node.frame,
781
+ state.surfaceSize,
782
+ state.layoutConstraints,
783
+ node.sizeConstraints
784
+ ),
780
785
  restoreFrame: null
781
786
  };
782
787
  });
@@ -1427,14 +1432,21 @@ function createWorkbenchStore(initialState = {}, options = {}) {
1427
1432
  dispatch(action) {
1428
1433
  const nextState = reduceWorkbenchState(state, action);
1429
1434
  if (nextState === state) {
1435
+ notifySurfaceSizeMeasured(action, options);
1430
1436
  return;
1431
1437
  }
1432
1438
  logWorkbenchFrameChanges(action, state, nextState, options);
1433
1439
  state = nextState;
1434
1440
  subscriptions.notify();
1441
+ notifySurfaceSizeMeasured(action, options);
1435
1442
  }
1436
1443
  };
1437
1444
  }
1445
+ function notifySurfaceSizeMeasured(action, options) {
1446
+ if (action.type === "setSurfaceSize") {
1447
+ options.onSurfaceSizeMeasured?.();
1448
+ }
1449
+ }
1438
1450
  function logWorkbenchFrameChanges(action, previousState, nextState, options) {
1439
1451
  if (!options.debugDiagnostics?.isEnabled()) {
1440
1452
  return;
@@ -6016,9 +6028,25 @@ function createWorkbenchSnapshotFromState(state, options = {}) {
6016
6028
  activeNodeId: state.nodeStack.at(-1) ?? null,
6017
6029
  spaces: options.spaces,
6018
6030
  activeSpaceId: options.activeSpaceId,
6031
+ layoutBasis: createWorkbenchSnapshotLayoutBasis(state),
6019
6032
  metadata: options.metadata
6020
6033
  });
6021
6034
  }
6035
+ function createWorkbenchSnapshotLayoutBasis(state) {
6036
+ if (!state.surfaceSize || !state.layoutConstraints || !isPositiveFinite(state.surfaceSize.width) || !isPositiveFinite(state.surfaceSize.height)) {
6037
+ return void 0;
6038
+ }
6039
+ return {
6040
+ surfaceSize: { ...state.surfaceSize },
6041
+ layoutConstraints: {
6042
+ ...state.layoutConstraints,
6043
+ safeArea: { ...state.layoutConstraints.safeArea }
6044
+ }
6045
+ };
6046
+ }
6047
+ function isPositiveFinite(value) {
6048
+ return Number.isFinite(value) && value > 0;
6049
+ }
6022
6050
 
6023
6051
  // src/core/placement.ts
6024
6052
  var WORKBENCH_WINDOW_CASCADE_OFFSET = 28;
@@ -6417,50 +6445,6 @@ function resolveCompactWorkbenchPreferredFrame(input) {
6417
6445
  input.sizeConstraints
6418
6446
  );
6419
6447
  }
6420
- function compactRestoredWorkbenchHostNodes(input) {
6421
- if (!shouldUseCompactWorkbenchFrame(input.surfaceSize)) {
6422
- return [...input.nodes];
6423
- }
6424
- return input.nodes.map((node) => {
6425
- const sizeConstraints = node.sizeConstraints ?? input.nodeDefinitionByType.get(node.data.typeId)?.sizeConstraints ?? null;
6426
- if (node.displayMode === "fullscreen") {
6427
- return {
6428
- ...node,
6429
- frame: getWorkbenchFullscreenRect(
6430
- input.surfaceSize,
6431
- input.constraints,
6432
- sizeConstraints
6433
- )
6434
- };
6435
- }
6436
- const defaultFrame = input.nodeDefinitionByType.get(node.data.typeId)?.frame ?? node.frame;
6437
- const compactWidth = Math.round(
6438
- defaultFrame.width * COMPACT_LAUNCH_FRAME_SCALE
6439
- );
6440
- const compactHeight = Math.round(
6441
- defaultFrame.height * COMPACT_LAUNCH_FRAME_SCALE
6442
- );
6443
- const width = Math.min(node.frame.width, compactWidth);
6444
- const height = Math.min(node.frame.height, compactHeight);
6445
- const sizeChanged = width !== node.frame.width || height !== node.frame.height;
6446
- const layoutFrame = getWorkbenchLayoutFrame(
6447
- input.surfaceSize,
6448
- input.constraints
6449
- );
6450
- const frame = clampWorkbenchRect(
6451
- sizeChanged ? {
6452
- height,
6453
- width,
6454
- x: Math.round(layoutFrame.x + (layoutFrame.width - width) / 2),
6455
- y: Math.round(layoutFrame.y + (layoutFrame.height - height) / 2)
6456
- } : node.frame,
6457
- input.surfaceSize,
6458
- input.constraints,
6459
- sizeConstraints
6460
- );
6461
- return rectsEqual(node.frame, frame) ? node : { ...node, frame };
6462
- });
6463
- }
6464
6448
  function shouldUseCompactWorkbenchFrame(surfaceSize) {
6465
6449
  return surfaceSize.width < COMPACT_LAUNCH_WIDTH_THRESHOLD;
6466
6450
  }
@@ -6469,6 +6453,7 @@ function createProjectedNodeID(typeId, instanceId) {
6469
6453
  }
6470
6454
  function persistedWorkbenchState(state, nodeDefinitionByType) {
6471
6455
  return {
6456
+ layoutConstraints: state.layoutConstraints,
6472
6457
  nodeStack: state.nodeStack,
6473
6458
  nodes: state.nodes.filter((node) => {
6474
6459
  return nodeDefinitionByType.get(node.data.typeId)?.window?.persists !== false;
@@ -6482,7 +6467,8 @@ function persistedWorkbenchState(state, nodeDefinitionByType) {
6482
6467
  ...node.data.isProjected === true ? { isProjected: true } : {},
6483
6468
  typeId: node.data.typeId
6484
6469
  }
6485
- }))
6470
+ })),
6471
+ surfaceSize: state.surfaceSize
6486
6472
  };
6487
6473
  }
6488
6474
  function stateFromSnapshotOrDefinitions(snapshot, nodeDefinitions, projectedNodes = []) {
@@ -6771,6 +6757,174 @@ function projectedSubjectsEqual(left, right) {
6771
6757
  return left?.id === right?.id && left?.type === right?.type;
6772
6758
  }
6773
6759
 
6760
+ // src/core/snapshotLayout.ts
6761
+ function restoreWorkbenchFrameToLayout(input) {
6762
+ const sourceLayoutBasis = input.sourceLayoutBasis;
6763
+ if (!sourceLayoutBasis) {
6764
+ return normalizeRestoredFrame(
6765
+ clampWorkbenchRect(
6766
+ input.frame,
6767
+ input.targetSurfaceSize,
6768
+ input.targetLayoutConstraints,
6769
+ input.sizeConstraints
6770
+ )
6771
+ );
6772
+ }
6773
+ const sourceLayoutRect = getWorkbenchSafeLayoutRect(
6774
+ sourceLayoutBasis.surfaceSize,
6775
+ sourceLayoutBasis.layoutConstraints
6776
+ );
6777
+ const targetLayoutRect = getWorkbenchSafeLayoutRect(
6778
+ input.targetSurfaceSize,
6779
+ input.targetLayoutConstraints
6780
+ );
6781
+ const normalizedFrame = normalizeWorkbenchFrameToRect(
6782
+ input.frame,
6783
+ sourceLayoutRect
6784
+ );
6785
+ const restoredFrame = denormalizeWorkbenchFrameFromRect(
6786
+ normalizedFrame,
6787
+ targetLayoutRect
6788
+ );
6789
+ return normalizeRestoredFrame(
6790
+ clampWorkbenchRect(
6791
+ restoredFrame,
6792
+ input.targetSurfaceSize,
6793
+ input.targetLayoutConstraints,
6794
+ input.sizeConstraints
6795
+ )
6796
+ );
6797
+ }
6798
+ function normalizeRestoredFrame(frame) {
6799
+ return {
6800
+ x: normalizeCoordinate(frame.x),
6801
+ y: normalizeCoordinate(frame.y),
6802
+ width: normalizeCoordinate(frame.width),
6803
+ height: normalizeCoordinate(frame.height)
6804
+ };
6805
+ }
6806
+ function normalizeCoordinate(value) {
6807
+ const normalized = Number(value.toFixed(3));
6808
+ return Object.is(normalized, -0) ? 0 : normalized;
6809
+ }
6810
+
6811
+ // src/host/snapshotLayout.ts
6812
+ function restoreWorkbenchHostNodesToSurface(input) {
6813
+ const persistedNodeById = new Map(
6814
+ input.persistedNodes?.map((node) => [node.id, node])
6815
+ );
6816
+ return input.nodes.map((node) => {
6817
+ const persistedNode = persistedNodeById.get(node.id);
6818
+ const sourceNode = persistedNode ? {
6819
+ ...node,
6820
+ frame: persistedNode.frame,
6821
+ restoreFrame: persistedNode.restoreFrame ?? null
6822
+ } : node;
6823
+ return restoreNodeToSurface(sourceNode, input);
6824
+ });
6825
+ }
6826
+ function restoreWorkbenchHostSpacesToSurface(input) {
6827
+ return input.spaces?.map((space) => {
6828
+ if (!space.frame) {
6829
+ return space;
6830
+ }
6831
+ const frame = restoreWorkbenchFrameToLayout({
6832
+ frame: space.frame,
6833
+ sourceLayoutBasis: input.layoutBasis,
6834
+ targetLayoutConstraints: input.constraints,
6835
+ targetSurfaceSize: input.surfaceSize
6836
+ });
6837
+ return rectsEqual(space.frame, frame) ? space : { ...space, frame };
6838
+ });
6839
+ }
6840
+ function restoreClosedDockWindowFrameEntriesToSurface(input) {
6841
+ const restoredEntries = /* @__PURE__ */ new Map();
6842
+ for (const entry of input.entries) {
6843
+ const definition = input.nodeDefinitionByType.get(entry.typeId);
6844
+ const restoredEntry = {
6845
+ ...entry,
6846
+ frame: restoreWorkbenchFrameToLayout({
6847
+ frame: entry.frame,
6848
+ sourceLayoutBasis: input.layoutBasis,
6849
+ targetLayoutConstraints: input.constraints,
6850
+ targetSurfaceSize: input.surfaceSize,
6851
+ sizeConstraints: definition?.sizeConstraints
6852
+ })
6853
+ };
6854
+ restoredEntries.set(
6855
+ closedDockWindowFrameEntryKey(restoredEntry),
6856
+ restoredEntry
6857
+ );
6858
+ }
6859
+ return restoredEntries;
6860
+ }
6861
+ function restoreNodeToSurface(node, input) {
6862
+ const sizeConstraints = node.sizeConstraints ?? input.nodeDefinitionByType.get(node.data.typeId)?.sizeConstraints ?? null;
6863
+ const restoreFrame = node.restoreFrame ? restoreWorkbenchFrameToLayout({
6864
+ frame: node.restoreFrame,
6865
+ sourceLayoutBasis: input.layoutBasis,
6866
+ targetLayoutConstraints: input.constraints,
6867
+ targetSurfaceSize: input.surfaceSize,
6868
+ sizeConstraints
6869
+ }) : null;
6870
+ if (node.displayMode === "fullscreen") {
6871
+ const frame2 = getWorkbenchFullscreenRect(
6872
+ input.surfaceSize,
6873
+ input.constraints,
6874
+ sizeConstraints
6875
+ );
6876
+ return rectsEqual(node.frame, frame2) && nullableRectsEqual(node.restoreFrame, restoreFrame) ? node : { ...node, frame: frame2, restoreFrame };
6877
+ }
6878
+ if (input.layoutBasis) {
6879
+ const frame2 = restoreWorkbenchFrameToLayout({
6880
+ frame: node.frame,
6881
+ sourceLayoutBasis: input.layoutBasis,
6882
+ targetLayoutConstraints: input.constraints,
6883
+ targetSurfaceSize: input.surfaceSize,
6884
+ sizeConstraints
6885
+ });
6886
+ return rectsEqual(node.frame, frame2) && nullableRectsEqual(node.restoreFrame, restoreFrame) ? node : { ...node, frame: frame2, restoreFrame };
6887
+ }
6888
+ if (input.surfaceSize.width >= COMPACT_LAUNCH_WIDTH_THRESHOLD) {
6889
+ const frame2 = clampWorkbenchRect(
6890
+ node.frame,
6891
+ input.surfaceSize,
6892
+ input.constraints,
6893
+ sizeConstraints
6894
+ );
6895
+ return rectsEqual(node.frame, frame2) && nullableRectsEqual(node.restoreFrame, restoreFrame) ? node : { ...node, frame: frame2, restoreFrame };
6896
+ }
6897
+ const defaultFrame = input.nodeDefinitionByType.get(node.data.typeId)?.frame ?? node.frame;
6898
+ const compactWidth = Math.round(
6899
+ defaultFrame.width * COMPACT_LAUNCH_FRAME_SCALE
6900
+ );
6901
+ const compactHeight = Math.round(
6902
+ defaultFrame.height * COMPACT_LAUNCH_FRAME_SCALE
6903
+ );
6904
+ const width = Math.min(node.frame.width, compactWidth);
6905
+ const height = Math.min(node.frame.height, compactHeight);
6906
+ const sizeChanged = width !== node.frame.width || height !== node.frame.height;
6907
+ const layoutFrame = getWorkbenchLayoutFrame(
6908
+ input.surfaceSize,
6909
+ input.constraints
6910
+ );
6911
+ const frame = clampWorkbenchRect(
6912
+ sizeChanged ? {
6913
+ height,
6914
+ width,
6915
+ x: Math.round(layoutFrame.x + (layoutFrame.width - width) / 2),
6916
+ y: Math.round(layoutFrame.y + (layoutFrame.height - height) / 2)
6917
+ } : node.frame,
6918
+ input.surfaceSize,
6919
+ input.constraints,
6920
+ sizeConstraints
6921
+ );
6922
+ return rectsEqual(node.frame, frame) && nullableRectsEqual(node.restoreFrame, restoreFrame) ? node : { ...node, frame, restoreFrame };
6923
+ }
6924
+ function nullableRectsEqual(left, right) {
6925
+ return left === right || left !== null && right !== null && rectsEqual(left, right);
6926
+ }
6927
+
6774
6928
  // src/host/externalState.ts
6775
6929
  import { useCallback as useCallback9, useMemo as useMemo7, useSyncExternalStore } from "react";
6776
6930
  var noopSubscribe = () => () => {
@@ -6863,6 +7017,7 @@ function sanitizeWorkbenchHostSnapshot(snapshot) {
6863
7017
  activeNodeId: normalized.activeNodeId,
6864
7018
  spaces: normalized.spaces?.map(sanitizeSpace),
6865
7019
  activeSpaceId: normalized.activeSpaceId,
7020
+ layoutBasis: normalized.layoutBasis,
6866
7021
  metadata: sanitizeMetadata(normalized.metadata)
6867
7022
  });
6868
7023
  }
@@ -6940,7 +7095,7 @@ var WorkbenchHostSessionController = class {
6940
7095
  loadPromise = null;
6941
7096
  nodeLeases = /* @__PURE__ */ new Map();
6942
7097
  nextActivationSequence = 1;
6943
- hasAppliedInitialCompactRestoredFrames = false;
7098
+ hasAppliedInitialRestoredLayout = false;
6944
7099
  hasReceivedSurfaceSize = false;
6945
7100
  observedSurfaceSize = null;
6946
7101
  pendingProjectedNodeReconciliation = false;
@@ -6973,17 +7128,23 @@ var WorkbenchHostSessionController = class {
6973
7128
  this.nodeDefinitions,
6974
7129
  this.projectedNodes
6975
7130
  ),
6976
- { debugDiagnostics: input.debugDiagnostics }
7131
+ {
7132
+ debugDiagnostics: input.debugDiagnostics,
7133
+ onSurfaceSizeMeasured: () => {
7134
+ this.hasReceivedSurfaceSize = true;
7135
+ this.applyInitialRestoredLayout();
7136
+ }
7137
+ }
6977
7138
  );
6978
7139
  this.observedSurfaceSize = this.controller.getSnapshot().surfaceSize;
6979
7140
  this.leaseUnsubscribe = this.controller.subscribe(() => {
6980
7141
  this.noteSurfaceSizeChange();
6981
7142
  this.reconcileNodeLeases();
6982
- this.applyInitialCompactRestoredFrames();
7143
+ this.applyInitialRestoredLayout();
6983
7144
  });
6984
7145
  this.isSnapshotLoaded = this.loadedSnapshot !== null;
6985
7146
  if (this.isSnapshotLoaded) {
6986
- this.applyInitialCompactRestoredFrames();
7147
+ this.applyInitialRestoredLayout();
6987
7148
  this.subscribeToPersistence();
6988
7149
  }
6989
7150
  }
@@ -7182,6 +7343,7 @@ var WorkbenchHostSessionController = class {
7182
7343
  return this.loadPromise;
7183
7344
  }
7184
7345
  async loadInitialSnapshot(generation) {
7346
+ this.hasAppliedInitialRestoredLayout = false;
7185
7347
  this.unsubscribe?.();
7186
7348
  this.unsubscribe = null;
7187
7349
  this.disposeExternalStatePersistenceSubscriptions();
@@ -7225,7 +7387,7 @@ var WorkbenchHostSessionController = class {
7225
7387
  }
7226
7388
  this.reconcileNodeLeases();
7227
7389
  this.isSnapshotLoaded = true;
7228
- this.applyInitialCompactRestoredFrames();
7390
+ this.applyInitialRestoredLayout();
7229
7391
  this.subscribeToPersistence();
7230
7392
  this.markReady();
7231
7393
  }
@@ -7499,7 +7661,7 @@ var WorkbenchHostSessionController = class {
7499
7661
  }
7500
7662
  resetHydrationBarrier() {
7501
7663
  this.isHydratingInitialSnapshot = true;
7502
- this.hasAppliedInitialCompactRestoredFrames = false;
7664
+ this.hasAppliedInitialRestoredLayout = false;
7503
7665
  this.readyPromise = new Promise((resolve) => {
7504
7666
  this.resolveReady = resolve;
7505
7667
  });
@@ -7511,22 +7673,47 @@ var WorkbenchHostSessionController = class {
7511
7673
  }
7512
7674
  this.observedSurfaceSize = currentSurfaceSize;
7513
7675
  }
7514
- applyInitialCompactRestoredFrames() {
7515
- if (this.hasAppliedInitialCompactRestoredFrames || !this.isSnapshotLoaded || !this.hasReceivedSurfaceSize) {
7676
+ applyInitialRestoredLayout() {
7677
+ if (this.hasAppliedInitialRestoredLayout || !this.isSnapshotLoaded || !this.hasReceivedSurfaceSize) {
7516
7678
  return;
7517
7679
  }
7518
7680
  const snapshot = this.controller.getSnapshot();
7519
- const compactedNodes = compactRestoredWorkbenchHostNodes({
7681
+ const sourceLayoutBasis = this.loadedSnapshot?.layoutBasis;
7682
+ const restoredNodes = restoreWorkbenchHostNodesToSurface({
7520
7683
  constraints: snapshot.layoutConstraints,
7684
+ layoutBasis: sourceLayoutBasis,
7521
7685
  nodeDefinitionByType: this.nodeDefinitionByType,
7522
7686
  nodes: snapshot.nodes,
7687
+ persistedNodes: this.loadedSnapshot?.nodes,
7523
7688
  surfaceSize: snapshot.surfaceSize
7524
7689
  });
7525
- this.hasAppliedInitialCompactRestoredFrames = true;
7526
- if (shallowArrayEqual(snapshot.nodes, compactedNodes)) {
7527
- return;
7690
+ this.closedDockWindowFrameEntries = restoreClosedDockWindowFrameEntriesToSurface({
7691
+ constraints: snapshot.layoutConstraints,
7692
+ entries: this.closedDockWindowFrameEntries.values(),
7693
+ layoutBasis: sourceLayoutBasis,
7694
+ nodeDefinitionByType: this.nodeDefinitionByType,
7695
+ surfaceSize: snapshot.surfaceSize
7696
+ });
7697
+ if (this.loadedSnapshot) {
7698
+ this.loadedSnapshot = {
7699
+ ...this.loadedSnapshot,
7700
+ layoutBasis: createWorkbenchSnapshotLayoutBasis(snapshot),
7701
+ metadata: writeClosedDockWindowFrameEntries(
7702
+ this.loadedSnapshot.metadata,
7703
+ this.closedDockWindowFrameEntries.values()
7704
+ ),
7705
+ spaces: restoreWorkbenchHostSpacesToSurface({
7706
+ constraints: snapshot.layoutConstraints,
7707
+ layoutBasis: sourceLayoutBasis,
7708
+ spaces: this.loadedSnapshot.spaces,
7709
+ surfaceSize: snapshot.surfaceSize
7710
+ })
7711
+ };
7712
+ }
7713
+ this.hasAppliedInitialRestoredLayout = true;
7714
+ if (!shallowArrayEqual(snapshot.nodes, restoredNodes)) {
7715
+ this.controller.commands.replaceNodes(restoredNodes);
7528
7716
  }
7529
- this.controller.commands.replaceNodes(compactedNodes);
7530
7717
  }
7531
7718
  queueProjectedNodeReconciliation() {
7532
7719
  if (this.pendingProjectedNodeReconciliation) {
@@ -14428,6 +14615,7 @@ export {
14428
14615
  createWorkbenchNode,
14429
14616
  createWorkbenchNodeFromSnapshot,
14430
14617
  createWorkbenchSnapshotFromState,
14618
+ createWorkbenchSnapshotLayoutBasis,
14431
14619
  createWorkbenchStateFromSnapshot,
14432
14620
  createWorkbenchWindowChromeI18nRuntime,
14433
14621
  getWorkbenchLayoutFrame,