@loomweaver/shell 0.7.9 → 0.8.0-preview.2

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.
@@ -71,9 +71,10 @@ function warnMenuTriggerConflict(item) {
71
71
  }
72
72
  if (menuOnActivate(item) && (item.command !== undefined || item.run !== undefined)) {
73
73
  warned.add(item.id);
74
+ const behaviour = item.command === undefined ? 'inline behaviour' : `command "${item.command}"`;
74
75
  console.warn(`Item "${item.id}" opens the menu "${item.menu}" on activation, so its ` +
75
- `${item.command === undefined ? 'inline behaviour' : `command "${item.command}"`} is ` +
76
- `never run from here. Reach it from a menu entry, a shortcut or the palette instead.`);
76
+ `${behaviour} is never run from here. Reach it from a menu entry, a shortcut ` +
77
+ `or the palette instead.`);
77
78
  }
78
79
  }
79
80
 
@@ -1448,6 +1449,30 @@ function healedPrimary(node, candidate) {
1448
1449
  ? candidate
1449
1450
  : collectLeafIds(node)[0];
1450
1451
  }
1452
+ function withoutTabs(node, drop) {
1453
+ if (node.kind === 'leaf') {
1454
+ const kept = node.tabs.filter((tab) => !drop(tab.path));
1455
+ if (kept.length === node.tabs.length) {
1456
+ return { node, dropped: [] };
1457
+ }
1458
+ const dropped = node.tabs
1459
+ .filter((tab) => drop(tab.path))
1460
+ .map((tab) => tab.path);
1461
+ const active = node.active !== undefined && dropped.includes(node.active)
1462
+ ? kept[0]?.path
1463
+ : node.active;
1464
+ return { node: { ...node, tabs: kept, active }, dropped };
1465
+ }
1466
+ const first = withoutTabs(node.first, drop);
1467
+ const second = withoutTabs(node.second, drop);
1468
+ if (first.dropped.length === 0 && second.dropped.length === 0) {
1469
+ return { node, dropped: [] };
1470
+ }
1471
+ return {
1472
+ node: { ...node, first: first.node, second: second.node },
1473
+ dropped: [...first.dropped, ...second.dropped],
1474
+ };
1475
+ }
1451
1476
 
1452
1477
  const DEFAULT_WORKSPACE_ID = 'default';
1453
1478
  function auditWorkspaceDefinitions(definitions, panelRegions) {
@@ -3878,6 +3903,24 @@ function viewTabViews(paneTabs, viewOf, meets, viewPanePrefix) {
3878
3903
  });
3879
3904
  }
3880
3905
 
3906
+ function syncActiveTab(sync, route, root, path) {
3907
+ const { routes, paneTree } = sync;
3908
+ sync.updateOpen((tabs) => {
3909
+ const index = tabs.findIndex((tab) => tabRootOf(routes, tab.path) === root);
3910
+ if (index !== -1) {
3911
+ return withRefreshedPath(tabs, index, path);
3912
+ }
3913
+ const opened = autoOpenedTab(route, root, path);
3914
+ return opened ? [...tabs, opened] : tabs;
3915
+ });
3916
+ const held = paneTree
3917
+ .primaryTabs(CONTENT_DOCK)
3918
+ .find((tab) => tabRootOf(routes, tab.path) === root);
3919
+ if (held) {
3920
+ paneTree.setActiveTab(CONTENT_DOCK, paneTree.primaryId(CONTENT_DOCK), held.path);
3921
+ }
3922
+ }
3923
+
3881
3924
  const TAB_ADDRESS_RESOLVER = new InjectionToken('TAB_ADDRESS_RESOLVER');
3882
3925
  /**
3883
3926
  * Overrides or extends how the host computes the address of a **following** tab
@@ -3927,6 +3970,7 @@ function paramPrefixes(pattern) {
3927
3970
  }
3928
3971
 
3929
3972
  const STORAGE_KEY$c = 'lw.shell.pane-trees';
3973
+ const WORKSPACES_KEY = 'lw.shell.workspaces';
3930
3974
  const HYDRATION_RETRY_MS = 500;
3931
3975
  function isDefault(entry) {
3932
3976
  return entry.node.kind === 'leaf' && entry.node.tabs.length === 0;
@@ -3950,6 +3994,24 @@ function parse$1(raw) {
3950
3994
  return {};
3951
3995
  }
3952
3996
  }
3997
+ function originsIn(raw) {
3998
+ const origins = new Map();
3999
+ if (!raw) {
4000
+ return origins;
4001
+ }
4002
+ try {
4003
+ for (const saved of JSON.parse(raw)) {
4004
+ const entry = saved;
4005
+ if (typeof entry.id === 'string' && typeof entry.origin === 'string') {
4006
+ origins.set(entry.id, entry.origin);
4007
+ }
4008
+ }
4009
+ }
4010
+ catch {
4011
+ return origins;
4012
+ }
4013
+ return origins;
4014
+ }
3953
4015
  function serializeDocks(docks) {
3954
4016
  const out = {};
3955
4017
  for (const [dock, entry] of Object.entries(docks)) {
@@ -3960,9 +4022,12 @@ function serializeDocks(docks) {
3960
4022
  class PaneTreeStorage {
3961
4023
  store = inject(WORKING_STATE_STORE);
3962
4024
  workspace = inject(ActiveWorkspaceService);
4025
+ definitions = inject(WORKSPACE_DEFINITIONS, {
4026
+ optional: true,
4027
+ })?.flat();
3963
4028
  popout = isPopoutUrl(inject(DOCUMENT).location?.pathname ?? '');
3964
4029
  peek() {
3965
- return parse$1(this.store.peek?.(this.key()));
4030
+ return this.settled(parse$1(this.store.peek?.(this.key())));
3966
4031
  }
3967
4032
  hydrate(apply, settled) {
3968
4033
  if (this.store.peek) {
@@ -3987,7 +4052,35 @@ class PaneTreeStorage {
3987
4052
  return serializeDocks(docks);
3988
4053
  }
3989
4054
  parsed(raw) {
3990
- return parse$1(raw);
4055
+ return this.settled(parse$1(raw));
4056
+ }
4057
+ settled(docks) {
4058
+ const declared = this.definitions ?? [];
4059
+ const here = this.declaredHome();
4060
+ if (declared.every((definition) => definition.id !== here)) {
4061
+ return docks;
4062
+ }
4063
+ const claims = withoutConflicts(claimsOf(declared));
4064
+ const out = {};
4065
+ const dropped = [];
4066
+ for (const [dock, entry] of Object.entries(docks)) {
4067
+ const filtered = withoutTabs(entry.node, (path) => (claimFor(claims, path)?.workspaceId ?? here) !== here);
4068
+ dropped.push(...filtered.dropped);
4069
+ const repaired = { ...entry, node: filtered.node };
4070
+ if (!isDefault(repaired)) {
4071
+ out[dock] = repaired;
4072
+ }
4073
+ }
4074
+ if (dropped.length > 0 && isDevMode()) {
4075
+ console.warn(`Workspace "${this.workspace.id()}": stored content at ` +
4076
+ `${dropped.map((path) => `"${path}"`).join(', ')} belongs to another ` +
4077
+ `workspace that claims it — the tab is dropped rather than restored here.`);
4078
+ }
4079
+ return out;
4080
+ }
4081
+ declaredHome() {
4082
+ const active = this.workspace.id();
4083
+ return originsIn(this.store.peek?.(WORKSPACES_KEY)).get(active) ?? active;
3991
4084
  }
3992
4085
  key() {
3993
4086
  return this.workspace.scopedKey(STORAGE_KEY$c);
@@ -4659,7 +4752,11 @@ class OpenTabsService {
4659
4752
  }
4660
4753
  this.ownNavigation = null;
4661
4754
  }
4662
- this.syncActiveTab(route, root, path);
4755
+ syncActiveTab({
4756
+ routes: this.registry.contentRoutes(),
4757
+ paneTree: this.paneTree,
4758
+ updateOpen: (change) => this.updateOpen(change),
4759
+ }, route, root, path);
4663
4760
  if (root) {
4664
4761
  this.stampActive(root);
4665
4762
  }
@@ -4760,17 +4857,6 @@ class OpenTabsService {
4760
4857
  next.set(root, Date.now());
4761
4858
  this.lastActive.set(next);
4762
4859
  }
4763
- syncActiveTab(route, root, path) {
4764
- const routes = this.registry.contentRoutes();
4765
- this.updateOpen((tabs) => {
4766
- const index = tabs.findIndex((tab) => tabRootOf(routes, tab.path) === root);
4767
- if (index !== -1) {
4768
- return withRefreshedPath(tabs, index, path);
4769
- }
4770
- const opened = autoOpenedTab(route, root, path);
4771
- return opened ? [...tabs, opened] : tabs;
4772
- });
4773
- }
4774
4860
  strippable(routes, tab) {
4775
4861
  if (isHomePath(tab.path)) {
4776
4862
  return false;
@@ -5393,6 +5479,42 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
5393
5479
  type: Service
5394
5480
  }] });
5395
5481
 
5482
+ const NOT_COMPOSED$1 = {
5483
+ wouldSettle: () => false,
5484
+ settle: async () => undefined,
5485
+ };
5486
+ const WORKSPACE_CLAIMS = new InjectionToken('lw.workspace-claims', { providedIn: 'root', factory: () => NOT_COMPOSED$1 });
5487
+
5488
+ class ClaimOrdering {
5489
+ injector = inject(Injector);
5490
+ resolved = null;
5491
+ ordered = Promise.resolve();
5492
+ queued = 0;
5493
+ run(path, work) {
5494
+ if (this.queued === 0 && (path === null || !this.claims.wouldSettle(path))) {
5495
+ work();
5496
+ return;
5497
+ }
5498
+ this.queued += 1;
5499
+ this.ordered = this.ordered
5500
+ .then(() => (path === null ? undefined : this.claims.settle(path)))
5501
+ .then(work)
5502
+ .catch(() => undefined)
5503
+ .then(() => {
5504
+ this.queued -= 1;
5505
+ });
5506
+ }
5507
+ get claims() {
5508
+ this.resolved ??= this.injector.get(WORKSPACE_CLAIMS);
5509
+ return this.resolved;
5510
+ }
5511
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ClaimOrdering, deps: [], target: i0.ɵɵFactoryTarget.Service });
5512
+ static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.0", ngImport: i0, type: ClaimOrdering });
5513
+ }
5514
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ClaimOrdering, decorators: [{
5515
+ type: Service
5516
+ }] });
5517
+
5396
5518
  /**
5397
5519
  * The **URL pane's** tab state: the tabs to show (facet tabs of `follows`
5398
5520
  * surfaces + opened ones), and the active tab — all derived from the URL plus the open set. The open
@@ -5409,6 +5531,7 @@ class ContentTabsService {
5409
5531
  reuse = inject(ContentReuseStrategy);
5410
5532
  features = inject(SHELL_FEATURES).content;
5411
5533
  paneTree = inject(PaneTreeService);
5534
+ claimOrder = inject(ClaimOrdering);
5412
5535
  closeHooks = inject(TabCloseHooks);
5413
5536
  /**
5414
5537
  * The Quick-Open source for the command palette: every currently **open** tab across
@@ -5536,44 +5659,14 @@ class ContentTabsService {
5536
5659
  * mere title/sub-route refinement never promotes); promotion is explicit via {@link keep}.
5537
5660
  */
5538
5661
  open(input) {
5539
- const routes = this.registry.contentRoutes();
5540
- const path = normalizePath(input.path);
5541
- const root = tabRootOf(routes, path);
5542
- const previewSlot = (input.preview ?? false) && this.features.preview;
5543
- const existing = this.state.openTabRootedAt(routes, root);
5544
- this.closeHooks.set(root, input.onClose);
5545
- if (!existing && this.refineElsewhere(root, input)) {
5546
- return;
5547
- }
5548
- const stored = {
5549
- path: existing?.path ?? path,
5550
- title: input.title,
5551
- literalTitle: input.titleIsLiteral ?? false,
5552
- icon: input.icon,
5553
- onClose: input.onClose,
5554
- preview: existing ? existing.preview : previewSlot,
5555
- pinned: existing ? existing.pinned : false,
5556
- closable: existing ? existing.closable : true,
5557
- };
5558
- if (previewSlot && !existing) {
5559
- this.replacePreviewSlot(root, stored);
5560
- }
5561
- else {
5562
- this.state.updateOpen((tabs) => existing
5563
- ? tabs.map((tab) => tabRootOf(routes, tab.path) === root ? stored : tab)
5564
- : [...tabs, stored]);
5565
- }
5566
- this.navigateTo(stored.path);
5662
+ this.claimOrder.run(normalizePath(input.path), () => this.openHere(input));
5567
5663
  }
5568
5664
  /**
5569
5665
  * Promotes the preview tab rooted at `path` to a permanent tab — the programmatic
5570
5666
  * "Keep Open" (`ctx.keepContentTab`, a double-click, or an edit). No-op if it is already permanent.
5571
5667
  */
5572
5668
  keep(path) {
5573
- const { routes, root } = this.state.rootFor(path);
5574
- this.state.updateOpen((tabs) => tabs.map((tab) => tabRootOf(routes, tab.path) === root && tab.preview
5575
- ? { ...tab, preview: false }
5576
- : tab));
5669
+ this.claimOrder.run(null, () => this.keepHere(path));
5577
5670
  }
5578
5671
  /**
5579
5672
  * Pins the tab rooted at `path`: it moves to the front of the strip — after the tabs
@@ -5681,6 +5774,42 @@ class ContentTabsService {
5681
5774
  }
5682
5775
  return foundAnywhere;
5683
5776
  }
5777
+ openHere(input) {
5778
+ const routes = this.registry.contentRoutes();
5779
+ const path = normalizePath(input.path);
5780
+ const root = tabRootOf(routes, path);
5781
+ const previewSlot = (input.preview ?? false) && this.features.preview;
5782
+ const existing = this.state.openTabRootedAt(routes, root);
5783
+ this.closeHooks.set(root, input.onClose);
5784
+ if (!existing && this.refineElsewhere(root, input)) {
5785
+ return;
5786
+ }
5787
+ const stored = {
5788
+ path: existing?.path ?? path,
5789
+ title: input.title,
5790
+ literalTitle: input.titleIsLiteral ?? false,
5791
+ icon: input.icon,
5792
+ onClose: input.onClose,
5793
+ preview: existing ? existing.preview : previewSlot,
5794
+ pinned: existing ? existing.pinned : false,
5795
+ closable: existing ? existing.closable : true,
5796
+ };
5797
+ if (previewSlot && !existing) {
5798
+ this.replacePreviewSlot(root, stored);
5799
+ }
5800
+ else {
5801
+ this.state.updateOpen((tabs) => existing
5802
+ ? tabs.map((tab) => tabRootOf(routes, tab.path) === root ? stored : tab)
5803
+ : [...tabs, stored]);
5804
+ }
5805
+ this.navigateTo(stored.path);
5806
+ }
5807
+ keepHere(path) {
5808
+ const { routes, root } = this.state.rootFor(path);
5809
+ this.state.updateOpen((tabs) => tabs.map((tab) => tabRootOf(routes, tab.path) === root && tab.preview
5810
+ ? { ...tab, preview: false }
5811
+ : tab));
5812
+ }
5684
5813
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ContentTabsService, deps: [], target: i0.ɵɵFactoryTarget.Service });
5685
5814
  static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.0", ngImport: i0, type: ContentTabsService });
5686
5815
  }
@@ -6049,6 +6178,12 @@ function firstFree(candidates, taken) {
6049
6178
  return undefined;
6050
6179
  }
6051
6180
 
6181
+ function activeContentPath(paneTree) {
6182
+ const primary = findLeaf(paneTree.tree(CONTENT_DOCK), paneTree.primaryId(CONTENT_DOCK));
6183
+ const path = primary ? activeTab(primary)?.path : undefined;
6184
+ return !path || path.startsWith(VIEW_PANE_PREFIX) ? '' : path;
6185
+ }
6186
+
6052
6187
  const STORAGE_KEY$a = 'lw.shell.workspaces';
6053
6188
  const HIDDEN_VIEWS_KEY = 'lw.shell.hidden-views';
6054
6189
  const PANE_TREES_KEY = 'lw.shell.pane-trees';
@@ -6164,9 +6299,11 @@ class WorkspaceService {
6164
6299
  const baseline = await this.currentState();
6165
6300
  this.commit(this.list().map((w) => (w.id === id ? { ...w, baseline } : w)));
6166
6301
  }
6302
+ wouldSettle(path) {
6303
+ return this.settlementDestination(path) !== null;
6304
+ }
6167
6305
  async settle(path) {
6168
- const here = this.active.id();
6169
- const destination = settlementFor(this.claims(), this.claimsOfWorkspace(here), here, path);
6306
+ const destination = this.settlementDestination(path);
6170
6307
  if (destination !== null) {
6171
6308
  await this.switchTo(destination, { keepAddress: true });
6172
6309
  }
@@ -6192,14 +6329,14 @@ class WorkspaceService {
6192
6329
  }
6193
6330
  this.warnDeclarationGaps(id);
6194
6331
  if (options.keepAddress !== true) {
6195
- this.tabs.navigateTo(this.activeContentPath());
6332
+ this.tabs.navigateTo(activeContentPath(this.paneTree));
6196
6333
  }
6197
6334
  }
6198
6335
  reset() {
6199
6336
  const id = this.active.id();
6200
6337
  this.applyState(this.baselineOf(id));
6201
6338
  this.warnDeclarationGaps(id);
6202
- this.tabs.navigateTo(this.activeContentPath());
6339
+ this.tabs.navigateTo(activeContentPath(this.paneTree));
6203
6340
  }
6204
6341
  rename(id, name) {
6205
6342
  if (this.definitionOf(id) !== undefined) {
@@ -6308,7 +6445,7 @@ class WorkspaceService {
6308
6445
  this.applyState(this.baselineOf(id));
6309
6446
  this.warnDeclarationGaps(id);
6310
6447
  if (isHomePath(this.bootAddress.path)) {
6311
- this.tabs.navigateTo(this.activeContentPath());
6448
+ this.tabs.navigateTo(activeContentPath(this.paneTree));
6312
6449
  }
6313
6450
  }
6314
6451
  applyState(state) {
@@ -6316,14 +6453,6 @@ class WorkspaceService {
6316
6453
  this.keyed[key].hydrate(state[key]);
6317
6454
  }
6318
6455
  }
6319
- activeContentPath() {
6320
- const primary = findLeaf(this.paneTree.tree(CONTENT_DOCK), this.paneTree.primaryId(CONTENT_DOCK));
6321
- const path = primary ? activeTab(primary)?.path : undefined;
6322
- if (!path || path.startsWith(VIEW_PANE_PREFIX)) {
6323
- return '';
6324
- }
6325
- return path;
6326
- }
6327
6456
  commit(next) {
6328
6457
  this.list.set(next);
6329
6458
  void this.store.set(STORAGE_KEY$a, JSON.stringify(next));
@@ -6331,6 +6460,10 @@ class WorkspaceService {
6331
6460
  layOutAdoptedWorkspaceWhenReady() {
6332
6461
  void this.active.ready.then(() => this.layOutAdoptedWorkspace());
6333
6462
  }
6463
+ settlementDestination(path) {
6464
+ const here = this.active.id();
6465
+ return settlementFor(this.claims(), this.claimsOfWorkspace(here), here, path);
6466
+ }
6334
6467
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: WorkspaceService, deps: [], target: i0.ɵɵFactoryTarget.Service });
6335
6468
  static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.0", ngImport: i0, type: WorkspaceService });
6336
6469
  }
@@ -8901,7 +9034,7 @@ const SURFACE_PADDING = new InjectionToken('lw.surface-padding', {
8901
9034
  factory: () => 'none',
8902
9035
  });
8903
9036
  function effectivePadding(declared, fallback) {
8904
- return declared === undefined ? fallback === 'inset' : declared;
9037
+ return declared ?? fallback === 'inset';
8905
9038
  }
8906
9039
 
8907
9040
  class ContentSecondaryPane {
@@ -11606,7 +11739,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
11606
11739
  // GENERATED — do not edit by hand.
11607
11740
  // Written by tools/stamp-version.mjs from <Version> in Directory.Build.props.
11608
11741
  // Single source of truth: Directory.Build.props (bump via scripts/bump-version.sh).
11609
- const APP_VERSION = '0.7.9';
11742
+ const APP_VERSION = '0.8.0-preview.2';
11743
+
11744
+ const PRERELEASE_IDENTIFIER = /^[0-9A-Za-z-]+$/;
11745
+ function isPreviewVersion(version) {
11746
+ const withoutBuildMetadata = version.split('+', 1)[0];
11747
+ const marker = withoutBuildMetadata.indexOf('-');
11748
+ if (marker === -1) {
11749
+ return false;
11750
+ }
11751
+ return withoutBuildMetadata
11752
+ .slice(marker + 1)
11753
+ .split('.')
11754
+ .every((identifier) => PRERELEASE_IDENTIFIER.test(identifier));
11755
+ }
11610
11756
 
11611
11757
  /**
11612
11758
  * The running build's version, sourced from `<Version>` in Directory.Build.props
@@ -11620,6 +11766,16 @@ class VersionService {
11620
11766
  /** SemVer of the running build, e.g. `0.1.0`. */
11621
11767
  version = signal(APP_VERSION, /* @ts-ignore */
11622
11768
  ...(ngDevMode ? [{ debugName: "version" }] : /* istanbul ignore next */ []));
11769
+ /**
11770
+ * Whether {@link version} is a preview of a line that has not been released — `0.8.0-preview.3`
11771
+ * rather than `0.7.9`. Ask this instead of taking the version apart yourself.
11772
+ *
11773
+ * **Announcing it is yours.** The workbench marks a preview nowhere on its own: how loudly a
11774
+ * product tells its users that it is running something unfinished is the product's judgement.
11775
+ * A distribution that wants it visible draws it, from this.
11776
+ */
11777
+ isPreview = computed(() => isPreviewVersion(this.version()), /* @ts-ignore */
11778
+ ...(ngDevMode ? [{ debugName: "isPreview" }] : /* istanbul ignore next */ []));
11623
11779
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: VersionService, deps: [], target: i0.ɵɵFactoryTarget.Service });
11624
11780
  static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.0", ngImport: i0, type: VersionService });
11625
11781
  }
@@ -12054,7 +12210,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
12054
12210
  type: Injectable
12055
12211
  }] });
12056
12212
 
12057
- const NOT_COMPOSED$1 = {
12213
+ const NOT_COMPOSED = {
12058
12214
  invocable: () => [],
12059
12215
  invoke: () => Promise.resolve({
12060
12216
  outcome: 'refused',
@@ -12065,14 +12221,9 @@ const NOT_COMPOSED$1 = {
12065
12221
  };
12066
12222
  const COMMAND_INVOKER = new InjectionToken('lw.command-invoker', {
12067
12223
  providedIn: 'root',
12068
- factory: () => NOT_COMPOSED$1,
12224
+ factory: () => NOT_COMPOSED,
12069
12225
  });
12070
12226
 
12071
- const NOT_COMPOSED = {
12072
- settle: async () => undefined,
12073
- };
12074
- const WORKSPACE_CLAIMS = new InjectionToken('lw.workspace-claims', { providedIn: 'root', factory: () => NOT_COMPOSED });
12075
-
12076
12227
  const MAX_ANSWER_DEPTH = 8;
12077
12228
  function checkArguments(declared, args) {
12078
12229
  if (args !== undefined && !isPlainObject(args)) {
@@ -13413,6 +13564,7 @@ class CompositionReport {
13413
13564
  barItems = inject(BAR_ITEM, { optional: true }) ?? [];
13414
13565
  railItems = inject(RAIL_ITEM, { optional: true }) ?? [];
13415
13566
  views = inject(VIEW, { optional: true }) ?? [];
13567
+ versions = inject(VersionService);
13416
13568
  checkStaticContributions() {
13417
13569
  for (const problem of this.staticProblems()) {
13418
13570
  console.warn(problem);
@@ -13420,6 +13572,7 @@ class CompositionReport {
13420
13572
  }
13421
13573
  print() {
13422
13574
  const lines = [
13575
+ `Version: ${this.versions.version()}${this.versions.isPreview() ? ' (preview)' : ''}`,
13423
13576
  `Layout: ${this.layout.regions
13424
13577
  .map((region) => `${region.id} (${region.type}/${region.dock})`)
13425
13578
  .join(', ')}`,
@@ -15859,6 +16012,7 @@ class HostPluginContext {
15859
16012
  };
15860
16013
  this.hostFacts = {
15861
16014
  version: version.version,
16015
+ isPreview: version.isPreview,
15862
16016
  updateAvailable: update.updateAvailable,
15863
16017
  updatesEnabled: update.enabled,
15864
16018
  checkForUpdate: () => update.checkForUpdate(),