@tutti-os/workbench-surface 0.0.15 → 0.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -449,6 +449,7 @@ interface WorkbenchHostDockEntryAction {
449
449
  label: string;
450
450
  pendingLabel?: string;
451
451
  }
452
+ type WorkbenchHostDockEntryDiagnostics = Record<string, unknown>;
452
453
  interface WorkbenchHostDockPopupItemInput {
453
454
  externalNodeState?: unknown;
454
455
  externalWorkspaceState?: unknown;
@@ -490,6 +491,7 @@ interface WorkbenchHostDockEntry {
490
491
  iconSize?: "default" | "large";
491
492
  id: string;
492
493
  label: string;
494
+ diagnostics?: WorkbenchHostDockEntryDiagnostics;
493
495
  instanceMode?: WorkbenchHostNodeInstanceStrategy["mode"];
494
496
  launchBehavior?: WorkbenchHostDockEntryLaunchBehavior;
495
497
  launchPayload?: unknown;
@@ -504,7 +506,7 @@ interface WorkbenchHostDockEntry {
504
506
  typeId: string;
505
507
  visibility?: WorkbenchHostDockEntryVisibility;
506
508
  }
507
- type WorkbenchHostDockEntryDynamicState = Partial<Pick<WorkbenchHostDockEntry, "attentionToken" | "badge" | "hoverActions" | "launchBehavior" | "order" | "state" | "visibility">>;
509
+ type WorkbenchHostDockEntryDynamicState = Partial<Pick<WorkbenchHostDockEntry, "attentionToken" | "badge" | "diagnostics" | "hoverActions" | "launchBehavior" | "order" | "state" | "visibility">>;
508
510
  interface WorkbenchHostDockEntryStateSource {
509
511
  getEntryState(entryId: string): WorkbenchHostDockEntryDynamicState | null | undefined;
510
512
  subscribe(listener: () => void): () => void;
package/dist/index.js CHANGED
@@ -3896,6 +3896,8 @@ var initializedMetadataKeys = [
3896
3896
  ];
3897
3897
  var COMPACT_LAUNCH_WIDTH_THRESHOLD = 1440;
3898
3898
  var COMPACT_LAUNCH_FRAME_SCALE = 0.85;
3899
+ var closedDockWindowFramesMetadataKey = "workbenchHostClosedDockWindowFrames";
3900
+ var maxClosedDockWindowFrameEntries = 128;
3899
3901
  function createDefaultLaunchResult(definition, input) {
3900
3902
  return {
3901
3903
  defaultFrame: definition.frame,
@@ -4013,6 +4015,81 @@ function resolveWorkbenchLaunchedHostNodeFrame(input) {
4013
4015
  constraints: input.currentState.layoutConstraints
4014
4016
  });
4015
4017
  }
4018
+ function closedDockWindowFrameEntryKey(input) {
4019
+ return JSON.stringify([input.typeId, input.dockEntryId]);
4020
+ }
4021
+ function readClosedDockWindowFrameEntries(metadata) {
4022
+ const value = metadata?.[closedDockWindowFramesMetadataKey];
4023
+ if (!isRecord(value) || value.version !== 1 || !Array.isArray(value.entries)) {
4024
+ return /* @__PURE__ */ new Map();
4025
+ }
4026
+ const entries = /* @__PURE__ */ new Map();
4027
+ for (const entry of value.entries.slice(0, maxClosedDockWindowFrameEntries)) {
4028
+ const normalized = normalizeClosedDockWindowFrameEntry(entry);
4029
+ if (!normalized) {
4030
+ continue;
4031
+ }
4032
+ entries.set(closedDockWindowFrameEntryKey(normalized), normalized);
4033
+ }
4034
+ return entries;
4035
+ }
4036
+ function writeClosedDockWindowFrameEntries(metadata, entries) {
4037
+ const normalizedEntries = Array.from(entries).map(normalizeClosedDockWindowFrameEntry).filter((entry) => entry !== null).slice(-maxClosedDockWindowFrameEntries);
4038
+ const nextMetadata = { ...metadata ?? {} };
4039
+ if (normalizedEntries.length === 0) {
4040
+ delete nextMetadata[closedDockWindowFramesMetadataKey];
4041
+ } else {
4042
+ nextMetadata[closedDockWindowFramesMetadataKey] = {
4043
+ version: 1,
4044
+ entries: normalizedEntries
4045
+ };
4046
+ }
4047
+ return Object.keys(nextMetadata).length > 0 ? nextMetadata : void 0;
4048
+ }
4049
+ function normalizeClosedDockWindowFrameEntry(value) {
4050
+ if (!isRecord(value)) {
4051
+ return null;
4052
+ }
4053
+ const typeId = normalizeNonEmptyString(value.typeId);
4054
+ const dockEntryId = normalizeNonEmptyString(value.dockEntryId);
4055
+ const frame = normalizeWorkbenchFrame(value.frame);
4056
+ if (!typeId || !dockEntryId || !frame) {
4057
+ return null;
4058
+ }
4059
+ return {
4060
+ dockEntryId,
4061
+ frame,
4062
+ typeId
4063
+ };
4064
+ }
4065
+ function normalizeWorkbenchFrame(value) {
4066
+ if (!isRecord(value)) {
4067
+ return null;
4068
+ }
4069
+ const x = normalizeFiniteNumber(value.x);
4070
+ const y = normalizeFiniteNumber(value.y);
4071
+ const width = normalizeFiniteNumber(value.width);
4072
+ const height = normalizeFiniteNumber(value.height);
4073
+ if (x === null || y === null || width === null || height === null) {
4074
+ return null;
4075
+ }
4076
+ if (width < 160 || height < 120) {
4077
+ return null;
4078
+ }
4079
+ return { height, width, x, y };
4080
+ }
4081
+ function normalizeFiniteNumber(value) {
4082
+ if (typeof value !== "number" || !Number.isFinite(value)) {
4083
+ return null;
4084
+ }
4085
+ return Math.round(value);
4086
+ }
4087
+ function normalizeNonEmptyString(value) {
4088
+ return typeof value === "string" && value.trim() ? value.trim() : null;
4089
+ }
4090
+ function isRecord(value) {
4091
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4092
+ }
4016
4093
  function resolveLaunchFramePolicy(result) {
4017
4094
  return result.framePolicy;
4018
4095
  }
@@ -4506,6 +4583,12 @@ function sanitizeMetadata(metadata) {
4506
4583
  sanitized[key] = true;
4507
4584
  }
4508
4585
  }
4586
+ if (metadata?.[closedDockWindowFramesMetadataKey] !== void 0) {
4587
+ return writeClosedDockWindowFrameEntries(
4588
+ sanitized,
4589
+ readClosedDockWindowFrameEntries(metadata).values()
4590
+ );
4591
+ }
4509
4592
  return Object.keys(sanitized).length > 0 ? sanitized : void 0;
4510
4593
  }
4511
4594
 
@@ -4525,6 +4608,7 @@ var WorkbenchHostSessionController = class {
4525
4608
  isDisposed = false;
4526
4609
  isSnapshotLoaded = false;
4527
4610
  loadedSnapshot;
4611
+ closedDockWindowFrameEntries = /* @__PURE__ */ new Map();
4528
4612
  loadGeneration = 0;
4529
4613
  loadPromise = null;
4530
4614
  nodeLeases = /* @__PURE__ */ new Map();
@@ -4550,6 +4634,9 @@ var WorkbenchHostSessionController = class {
4550
4634
  this.nodeDefinitionByType.set(definition.typeId, definition);
4551
4635
  }
4552
4636
  this.loadedSnapshot = this.readCachedSnapshot();
4637
+ this.closedDockWindowFrameEntries = readClosedDockWindowFrameEntries(
4638
+ this.loadedSnapshot?.metadata
4639
+ );
4553
4640
  this.projectedNodes = input.projectedNodes ?? [];
4554
4641
  this.controller = createWorkbenchController(
4555
4642
  stateFromSnapshotOrDefinitions(
@@ -4655,6 +4742,7 @@ var WorkbenchHostSessionController = class {
4655
4742
  }
4656
4743
  closeNode(nodeId) {
4657
4744
  this.runWhenReady(this.loadGeneration, () => {
4745
+ this.rememberClosedDockWindowFrame(nodeId);
4658
4746
  this.controller.commands.closeNode(nodeId);
4659
4747
  });
4660
4748
  }
@@ -4777,6 +4865,9 @@ var WorkbenchHostSessionController = class {
4777
4865
  return;
4778
4866
  }
4779
4867
  this.loadedSnapshot = snapshot;
4868
+ this.closedDockWindowFrameEntries = readClosedDockWindowFrameEntries(
4869
+ snapshot?.metadata
4870
+ );
4780
4871
  this.controller.commands.replaceState(
4781
4872
  stateFromSnapshotOrDefinitions(
4782
4873
  snapshot,
@@ -4790,6 +4881,7 @@ var WorkbenchHostSessionController = class {
4790
4881
  return;
4791
4882
  }
4792
4883
  this.loadedSnapshot = null;
4884
+ this.closedDockWindowFrameEntries = /* @__PURE__ */ new Map();
4793
4885
  this.controller.commands.replaceState(
4794
4886
  stateFromSnapshotOrDefinitions(
4795
4887
  null,
@@ -4833,6 +4925,7 @@ var WorkbenchHostSessionController = class {
4833
4925
  if (this.isDisposed || resolvedDecision === "keep-open" || generation !== this.loadGeneration) {
4834
4926
  return;
4835
4927
  }
4928
+ this.rememberClosedDockWindowFrame(node.id);
4836
4929
  this.controller.commands.closeNode(node.id);
4837
4930
  }, noop);
4838
4931
  }
@@ -4964,18 +5057,19 @@ var WorkbenchHostSessionController = class {
4964
5057
  if (!definition) {
4965
5058
  return null;
4966
5059
  }
5060
+ const launchResult = this.applyClosedDockWindowFrame(result);
4967
5061
  const exactExisting = this.findExistingNodeByTarget({
4968
- instanceId: result.instanceId,
4969
- typeId: result.typeId
5062
+ instanceId: launchResult.instanceId,
5063
+ typeId: launchResult.typeId
4970
5064
  });
4971
- const dockEntryExisting = exactExisting ? null : this.findExistingNodeByDockEntry(result);
5065
+ const dockEntryExisting = exactExisting ? null : this.findExistingNodeByDockEntry(launchResult);
4972
5066
  const existing = exactExisting ?? dockEntryExisting;
4973
5067
  if (existing) {
4974
5068
  this.controller.commands.setNodeSizeConstraints(
4975
5069
  existing.id,
4976
- result.sizeConstraints ?? definition.sizeConstraints ?? null
5070
+ launchResult.sizeConstraints ?? definition.sizeConstraints ?? null
4977
5071
  );
4978
- if (exactExisting && result.framePolicy === "cascade-same-type-centered") {
5072
+ if (exactExisting && launchResult.framePolicy === "cascade-same-type-centered") {
4979
5073
  const currentState = this.controller.getSnapshot();
4980
5074
  const resolvedFrame2 = resolveWorkbenchLaunchedHostNodeFrame({
4981
5075
  currentState: {
@@ -4986,26 +5080,26 @@ var WorkbenchHostSessionController = class {
4986
5080
  )
4987
5081
  },
4988
5082
  definition,
4989
- result
5083
+ result: launchResult
4990
5084
  });
4991
5085
  this.controller.commands.resizeNode(existing.id, resolvedFrame2);
4992
5086
  }
4993
5087
  this.restoreAndFocusNode(existing.id);
4994
- if (result.activation) {
4995
- this.activateNodeNow({ nodeId: existing.id }, result.activation);
5088
+ if (launchResult.activation) {
5089
+ this.activateNodeNow({ nodeId: existing.id }, launchResult.activation);
4996
5090
  }
4997
5091
  return existing.id;
4998
5092
  }
4999
5093
  const resolvedFrame = resolveWorkbenchLaunchedHostNodeFrame({
5000
5094
  currentState: this.controller.getSnapshot(),
5001
5095
  definition,
5002
- result
5096
+ result: launchResult
5003
5097
  });
5004
5098
  const nextNode = createWorkbenchLaunchedHostNode({
5005
- activation: this.createActivationEnvelope(result.activation),
5099
+ activation: this.createActivationEnvelope(launchResult.activation),
5006
5100
  definition,
5007
5101
  resolvedFrame,
5008
- result
5102
+ result: launchResult
5009
5103
  });
5010
5104
  this.controller.commands.openNode(nextNode);
5011
5105
  this.controller.commands.focusNode(nextNode.id);
@@ -5161,10 +5255,13 @@ var WorkbenchHostSessionController = class {
5161
5255
  ),
5162
5256
  {
5163
5257
  activeSpaceId: this.loadedSnapshot?.activeSpaceId,
5164
- metadata: {
5165
- ...this.loadedSnapshot?.metadata ?? {},
5166
- [initializedMetadataKey]: true
5167
- },
5258
+ metadata: writeClosedDockWindowFrameEntries(
5259
+ {
5260
+ ...this.loadedSnapshot?.metadata ?? {},
5261
+ [initializedMetadataKey]: true
5262
+ },
5263
+ this.closedDockWindowFrameEntries.values()
5264
+ ),
5168
5265
  spaces: this.loadedSnapshot?.spaces
5169
5266
  }
5170
5267
  )
@@ -5178,6 +5275,42 @@ var WorkbenchHostSessionController = class {
5178
5275
  this.loadedSnapshot = savedSnapshot;
5179
5276
  }, noop);
5180
5277
  }
5278
+ rememberClosedDockWindowFrame(nodeId) {
5279
+ const node = this.controller.getSnapshot().nodes.find((entry2) => entry2.id === nodeId);
5280
+ const dockEntryId = node?.data.dockEntryId?.trim();
5281
+ if (!node || !dockEntryId) {
5282
+ return;
5283
+ }
5284
+ const entry = {
5285
+ dockEntryId,
5286
+ frame: node.displayMode === "fullscreen" && node.restoreFrame ? node.restoreFrame : node.frame,
5287
+ typeId: node.data.typeId
5288
+ };
5289
+ this.closedDockWindowFrameEntries.set(
5290
+ closedDockWindowFrameEntryKey(entry),
5291
+ entry
5292
+ );
5293
+ }
5294
+ applyClosedDockWindowFrame(result) {
5295
+ const dockEntryId = result.dockEntryId?.trim();
5296
+ if (!dockEntryId) {
5297
+ return result;
5298
+ }
5299
+ const entry = this.closedDockWindowFrameEntries.get(
5300
+ closedDockWindowFrameEntryKey({
5301
+ dockEntryId,
5302
+ typeId: result.typeId
5303
+ })
5304
+ );
5305
+ if (!entry) {
5306
+ return result;
5307
+ }
5308
+ return {
5309
+ ...result,
5310
+ defaultFrame: entry.frame,
5311
+ framePolicy: "absolute"
5312
+ };
5313
+ }
5181
5314
  loadedSnapshotSaveKey() {
5182
5315
  if (!this.loadedSnapshot) {
5183
5316
  return null;
@@ -5602,6 +5735,9 @@ function resolveWorkbenchDockEntryClick(input) {
5602
5735
  }
5603
5736
  return { actionId: input.entry.clickActionId, kind: "action" };
5604
5737
  }
5738
+ if (isWorkbenchDockEntryBlocked(input.entry)) {
5739
+ return { kind: "blocked" };
5740
+ }
5605
5741
  if (input.instanceMode === "multi" && input.matchedNodes.length > 0) {
5606
5742
  return { kind: "open-popup" };
5607
5743
  }
@@ -5611,9 +5747,6 @@ function resolveWorkbenchDockEntryClick(input) {
5611
5747
  if (input.matchedNodes.length > 1) {
5612
5748
  return { kind: "open-popup" };
5613
5749
  }
5614
- if (isWorkbenchDockEntryBlocked(input.entry)) {
5615
- return { kind: "blocked" };
5616
- }
5617
5750
  return { kind: "launch" };
5618
5751
  }
5619
5752
  function isWorkbenchDockEntryBlocked(entry) {
@@ -8107,13 +8240,13 @@ function WorkbenchHostDock({
8107
8240
  "aria-expanded": currentPopup ? true : void 0,
8108
8241
  "aria-haspopup": clickResolution.kind === "open-popup" ? "dialog" : void 0,
8109
8242
  "aria-label": i18n.t("launch", { title: entry.label }),
8110
- "aria-disabled": clickResolution.kind === "blocked" && !resolvedEntry.hasMatchingNodes,
8243
+ "aria-disabled": clickResolution.kind === "blocked" ? true : void 0,
8111
8244
  className: "desktop-dock__btn",
8112
- "data-interactive": clickResolution.kind === "blocked" && !resolvedEntry.hasMatchingNodes ? "false" : "true",
8245
+ "data-interactive": clickResolution.kind === "blocked" ? "false" : "true",
8113
8246
  title: entry.label,
8114
8247
  type: "button",
8115
8248
  onPointerDown: () => {
8116
- if (clickResolution.kind === "blocked" && !resolvedEntry.hasMatchingNodes) {
8249
+ if (clickResolution.kind === "blocked") {
8117
8250
  return;
8118
8251
  }
8119
8252
  beginDockIconInteraction(anchorKey);
@@ -8122,8 +8255,18 @@ function WorkbenchHostDock({
8122
8255
  logWorkbenchDockDebug("dock.click", debugDiagnostics, {
8123
8256
  anchorKey,
8124
8257
  clickResolution,
8258
+ dockDiagnostics: entry.diagnostics ?? null,
8125
8259
  dockNodeState: resolvedEntry.dockNodeState,
8126
8260
  entryId: entry.id,
8261
+ entryLaunchBehavior: entry.launchBehavior ?? "enabled",
8262
+ entryOrder: entry.order ?? null,
8263
+ entryState: entry.state ?? { kind: "enabled" },
8264
+ entryVisibility: entry.visibility ?? "always",
8265
+ hoverActions: entry.hoverActions?.map((action) => ({
8266
+ disabled: action.disabled === true,
8267
+ id: action.id,
8268
+ pending: Boolean(action.pendingLabel)
8269
+ })) ?? [],
8127
8270
  instanceMode: instanceMode ?? null,
8128
8271
  matchedNodeCount: resolvedEntry.matchedNodes.length,
8129
8272
  matchedNodeIds: resolvedEntry.matchedNodes.map(