@scion/workbench 21.0.0-beta.2 → 21.0.0-beta.4

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.
@@ -1,15 +1,15 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { effect, untracked, Injectable, inject, Injector, signal, booleanAttribute, numberAttribute, DestroyRef, DOCUMENT, computed, InjectionToken, assertNotInReactiveContext, isSignal, createEnvironmentInjector, EnvironmentInjector, runInInjectionContext, Pipe, isDevMode, ChangeDetectionStrategy, Component, makeEnvironmentProviders, NgZone, ɵZONELESS_ENABLED as _ZONELESS_ENABLED, ApplicationInitStatus, forwardRef, Directive, createComponent, ApplicationRef, ElementRef, viewChild, afterRenderEffect, input, output, ViewContainerRef, TemplateRef, Renderer2, ChangeDetectorRef, linkedSignal, HostListener, HostBinding, viewChildren, IterableDiffers, CUSTOM_ELEMENTS_SCHEMA, provideEnvironmentInitializer } from '@angular/core';
3
3
  import { RouterOutlet, Router, PRIMARY_OUTLET, ChildrenOutletContexts, NavigationStart, ActivationStart, ActivationEnd, UrlSegment, RouterEvent, NavigationEnd, NavigationCancel, NavigationError, ActivatedRoute } from '@angular/router';
4
- import { Arrays, Objects as Objects$1, Defined, Observables, Maps, Dictionaries } from '@scion/toolkit/util';
4
+ import { Arrays, Objects, Defined, Observables, Maps, Dictionaries } from '@scion/toolkit/util';
5
5
  import { toObservable, takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
6
- import { skipUntil, mergeMap, filter, observeOn, catchError, map, startWith, take, switchMap as switchMap$1, skip, subscribeOn, finalize, distinctUntilChanged, takeUntil, first, combineLatestWith } from 'rxjs/operators';
6
+ import { skipUntil, mergeMap, observeOn, catchError, map, startWith, take, filter, switchMap as switchMap$1, skip, subscribeOn, finalize, distinctUntilChanged, takeUntil, first, combineLatestWith } from 'rxjs/operators';
7
7
  import { coerceElement } from '@angular/cdk/coercion';
8
8
  import { UUID } from '@scion/toolkit/uuid';
9
- import { Observable, Subject, asapScheduler, AsyncSubject, lastValueFrom, iif, switchMap, race, pairwise, EMPTY, of, firstValueFrom, from, animationFrameScheduler, merge, fromEvent, noop, BehaviorSubject, mergeWith, mergeMap as mergeMap$1, delay, identity, concatWith, withLatestFrom, NEVER, map as map$1, share, timer, ReplaySubject, combineLatest, tap, asyncScheduler } from 'rxjs';
9
+ import { Observable, Subject, asapScheduler, AsyncSubject, lastValueFrom, iif, switchMap, race, pairwise, EMPTY, of, firstValueFrom, from, animationFrameScheduler, merge, fromEvent, noop, BehaviorSubject, mergeWith, mergeMap as mergeMap$1, delay, identity, concatWith, withLatestFrom, NEVER, map as map$1, share, timer, ReplaySubject, combineLatest, tap } from 'rxjs';
10
10
  import { ManifestService, MicrofrontendPlatform, PlatformState, QualifierMatcher, ObservableDecorator, MicrofrontendPlatformConfig, MicrofrontendPlatformHost, APP_IDENTITY, MessageClient, mapToBody, OutletRouter, MessageHeaders, ResponseStatusCodes, ParamMatcher, CapabilityInterceptor, HostManifestInterceptor, IntentInterceptor, IntentClient, PlatformPropertyService } from '@scion/microfrontend-platform';
11
11
  import { tapFirst, subscribeIn, observeIn, filterArray } from '@scion/toolkit/operators';
12
- import { WorkbenchCapabilities, WorkbenchRouter as WorkbenchRouter$1, ɵWorkbenchRouter as _WorkbenchRouter, WorkbenchDialogService as WorkbenchDialogService$1, ɵWorkbenchDialogService as _WorkbenchDialogService, WorkbenchMessageBoxService as WorkbenchMessageBoxService$1, ɵWorkbenchMessageBoxService as _WorkbenchMessageBoxService, WorkbenchPopupService as WorkbenchPopupService$1, ɵWorkbenchPopupService as _WorkbenchPopupService, WorkbenchNotificationService as WorkbenchNotificationService$1, ɵWorkbenchNotificationService as _WorkbenchNotificationService, WorkbenchTextService, ɵWorkbenchTextService as _WorkbenchTextService, ɵTHEME_CONTEXT_KEY as _THEME_CONTEXT_KEY, ɵVIEW_ID_CONTEXT_KEY as _VIEW_ID_CONTEXT_KEY, ɵWorkbenchCommands as _WorkbenchCommands, ɵVIEW_CAPABILITY_ID_PARAM_NAME as _VIEW_CAPABILITY_ID_PARAM_NAME, ɵWORKBENCH_PART_CONTEXT as _WORKBENCH_PART_CONTEXT, eNOTIFICATION_MESSAGE_PARAM, ɵDIALOG_CONTEXT as _DIALOG_CONTEXT, ɵWorkbenchDialogMessageHeaders as _WorkbenchDialogMessageHeaders, ɵMESSAGE_BOX_CONTEXT as _MESSAGE_BOX_CONTEXT, eMESSAGE_BOX_MESSAGE_PARAM, WorkbenchClient, ɵPOPUP_CONTEXT as _POPUP_CONTEXT, ɵWorkbenchPopupMessageHeaders as _WorkbenchPopupMessageHeaders } from '@scion/workbench-client';
12
+ import { WorkbenchCapabilities, WorkbenchRouter as WorkbenchRouter$1, ɵWorkbenchRouter as _WorkbenchRouter, WorkbenchDialogService as WorkbenchDialogService$1, ɵWorkbenchDialogService as _WorkbenchDialogService, WorkbenchMessageBoxService as WorkbenchMessageBoxService$1, ɵWorkbenchMessageBoxService as _WorkbenchMessageBoxService, WorkbenchPopupService as WorkbenchPopupService$1, ɵWorkbenchPopupService as _WorkbenchPopupService, WorkbenchNotificationService as WorkbenchNotificationService$1, ɵWorkbenchNotificationService as _WorkbenchNotificationService, WorkbenchTextService, ɵWorkbenchTextService as _WorkbenchTextService, ɵTHEME_CONTEXT_KEY as _THEME_CONTEXT_KEY, ɵVIEW_ID_CONTEXT_KEY as _VIEW_ID_CONTEXT_KEY, ɵWorkbenchCommands as _WorkbenchCommands, ɵVIEW_CAPABILITY_ID_PARAM_NAME as _VIEW_CAPABILITY_ID_PARAM_NAME, ɵWORKBENCH_PART_CONTEXT as _WORKBENCH_PART_CONTEXT, eNOTIFICATION_MESSAGE_PARAM, ɵNOTIFICATION_CONTEXT as _NOTIFICATION_CONTEXT, ɵDIALOG_CONTEXT as _DIALOG_CONTEXT, ɵWorkbenchDialogMessageHeaders as _WorkbenchDialogMessageHeaders, ɵMESSAGE_BOX_CONTEXT as _MESSAGE_BOX_CONTEXT, eMESSAGE_BOX_MESSAGE_PARAM, WorkbenchClient, ɵPOPUP_CONTEXT as _POPUP_CONTEXT, ɵWorkbenchPopupMessageHeaders as _WorkbenchPopupMessageHeaders } from '@scion/workbench-client';
13
13
  import { fromMutation$, fromResize$ } from '@scion/toolkit/observable';
14
14
  import { boundingClientRect, SciDimensionDirective, dimension } from '@scion/components/dimension';
15
15
  import { SciViewportComponent } from '@scion/components/viewport';
@@ -511,12 +511,6 @@ const WORKBENCH_ROUTE = new InjectionToken('WORKBENCH_ROUTE');
511
511
  function serializeExecution(fn) {
512
512
  return mergeMap(element => fn(element), 1);
513
513
  }
514
- /**
515
- * Mirrors the source except for `null` emissions.
516
- */
517
- function filterNull() {
518
- return filter((item) => item !== null);
519
- }
520
514
 
521
515
  /*
522
516
  * Copyright (c) 2018-2024 Swiss Federal Railways
@@ -1376,7 +1370,7 @@ function provideManifestObjectCache() {
1376
1370
  }
1377
1371
 
1378
1372
  /*
1379
- * Copyright (c) 2018-2025 Swiss Federal Railways
1373
+ * Copyright (c) 2018-2026 Swiss Federal Railways
1380
1374
  *
1381
1375
  * This program and the accompanying materials are made
1382
1376
  * available under the terms of the Eclipse Public License 2.0
@@ -1559,6 +1553,41 @@ function canMatchWorkbenchMessageBoxCapability(qualifier) {
1559
1553
  function canMatchWorkbenchPopupCapability(qualifier) {
1560
1554
  return canMatchWorkbenchCapability('popup', WorkbenchCapabilities.Popup, qualifier);
1561
1555
  }
1556
+ /**
1557
+ * Configures a route to only match workbench notifications navigated to the specified notification capability.
1558
+ *
1559
+ * Use this guard to differentiate microfrontend routes, which must all have an empty path.
1560
+ *
1561
+ * @example - Route matching a notification capability with qualifier {notification: 'info'}
1562
+ * ```ts
1563
+ * import {Routes} from '@angular/router';
1564
+ * import {canMatchWorkbenchNotificationCapability} from '@scion/workbench';
1565
+ *
1566
+ * const routes: Routes = [
1567
+ * {path: '', canMatch: [canMatchWorkbenchNotificationCapability({notification: 'info'})], component: InfoComponent},
1568
+ * ];
1569
+ * ```
1570
+ *
1571
+ * The above route matches the following notification capability:
1572
+ *
1573
+ * ```json
1574
+ * {
1575
+ * "type": "notification",
1576
+ * "qualifier": {
1577
+ * "notification": "info"
1578
+ * },
1579
+ * "properties": {
1580
+ * "path": ""
1581
+ * }
1582
+ * }
1583
+ * ```
1584
+ *
1585
+ * @param qualifier - Identifies the notification capability.
1586
+ * @return guard matching the specified notification capability.
1587
+ */
1588
+ function canMatchWorkbenchNotificationCapability(qualifier) {
1589
+ return canMatchWorkbenchCapability('notification', WorkbenchCapabilities.Notification, qualifier);
1590
+ }
1562
1591
  /**
1563
1592
  * Matches a route if navigated to the specified capability displayed in the specified workbench element.
1564
1593
  */
@@ -1673,6 +1702,12 @@ function isDialogId(dialogId) {
1673
1702
  function isPopupId(popupId) {
1674
1703
  return popupId?.startsWith(POPUP_ID_PREFIX) ?? false;
1675
1704
  }
1705
+ /**
1706
+ * Tests if the given id matches the format of a notification identifier.
1707
+ */
1708
+ function isNotificationId(notificationId) {
1709
+ return notificationId?.startsWith(NOTIFICATION_ID_PREFIX) ?? false;
1710
+ }
1676
1711
  /**
1677
1712
  * Tests if the given id matches the format of an activity identifier.
1678
1713
  */
@@ -1993,7 +2028,7 @@ class UrlSegmentMatcher {
1993
2028
  if (checkPath && segment.path !== this._pattern[index].path) {
1994
2029
  return false;
1995
2030
  }
1996
- if (checkMatrixParams && !Objects$1.isEqual(segment.parameters, this._pattern[index].parameters)) {
2031
+ if (checkMatrixParams && !Objects.isEqual(segment.parameters, this._pattern[index].parameters)) {
1997
2032
  return false;
1998
2033
  }
1999
2034
  return true;
@@ -2001,51 +2036,6 @@ class UrlSegmentMatcher {
2001
2036
  }
2002
2037
  }
2003
2038
 
2004
- /*
2005
- * Copyright (c) 2018-2024 Swiss Federal Railways
2006
- *
2007
- * This program and the accompanying materials are made
2008
- * available under the terms of the Eclipse Public License 2.0
2009
- * which is available at https://www.eclipse.org/legal/epl-2.0/
2010
- *
2011
- * SPDX-License-Identifier: EPL-2.0
2012
- */
2013
- /**
2014
- * Provides helper functions for working with objects.
2015
- */
2016
- const Objects = {
2017
- /**
2018
- * Like {@link Object.keys}, but preserving the data type of keys.
2019
- */
2020
- keys: (object) => {
2021
- return Object.keys(object);
2022
- },
2023
- /**
2024
- * Like {@link Object.values}, but preserving the data type of values and supporting optional properties.
2025
- */
2026
- values: (object) => {
2027
- return Object.values(object);
2028
- },
2029
- /**
2030
- * Like {@link Object.entries}, but preserving the data type of keys and supporting optional properties.
2031
- */
2032
- entries: (object) => {
2033
- return Object.entries(object);
2034
- },
2035
- /**
2036
- * Stringifies given object to matrix notation: a=b;c=d;e=f
2037
- */
2038
- toMatrixNotation: (object) => {
2039
- return Object.entries(object ?? {}).map(([key, value]) => `${key}=${value}`).join(';');
2040
- },
2041
- /**
2042
- * Compares the two objects for shallow equality, ignoring the propery order.
2043
- */
2044
- isEqual: (a, b) => {
2045
- return Objects$1.isEqual(a, b);
2046
- },
2047
- };
2048
-
2049
2039
  /*
2050
2040
  * Copyright (c) 2018-2023 Swiss Federal Railways
2051
2041
  *
@@ -6376,7 +6366,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
6376
6366
  }], ctorParameters: () => [] });
6377
6367
 
6378
6368
  /*
6379
- * Copyright (c) 2018-2025 Swiss Federal Railways
6369
+ * Copyright (c) 2018-2026 Swiss Federal Railways
6380
6370
  *
6381
6371
  * This program and the accompanying materials are made
6382
6372
  * available under the terms of the Eclipse Public License 2.0
@@ -6405,7 +6395,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
6405
6395
  */
6406
6396
  function trackFocus(target, workbenchElement) {
6407
6397
  const focusMonitor = inject(ɵWorkbenchFocusMonitor);
6408
- toObservable(focusMonitor.activeElement)
6398
+ const subscription = toObservable(focusMonitor.activeElement)
6409
6399
  .pipe(startWith(focusMonitor.activeElement()), // Immediately subscribe to `focusin` events, required when the DOM element is focused right after invocation.
6410
6400
  switchMap(activeWorkbenchElement => activeWorkbenchElement === workbenchElement ? EMPTY : merge(fromEvent(target, 'focusin', { once: true }), fromEvent(target, 'sci-microfrontend-focusin', { once: true }))), finalize(() => requestAnimationFrame(() => focusMonitor.unsetActiveElement(workbenchElement))), // Asynchronously unset the active workbench element to prevent a `null` focus during destruction until the next element gains focus.
6411
6401
  takeUntilDestroyed())
@@ -6414,6 +6404,7 @@ function trackFocus(target, workbenchElement) {
6414
6404
  });
6415
6405
  return {
6416
6406
  unsetActiveElement: () => focusMonitor.unsetActiveElement(workbenchElement),
6407
+ destroy: () => subscription.unsubscribe(),
6417
6408
  };
6418
6409
  }
6419
6410
  class ɵWorkbenchFocusMonitor {
@@ -6789,7 +6780,7 @@ function removeEmptyEntries(map) {
6789
6780
  * Compares two arrays of strings for equality, ignoring element order.
6790
6781
  */
6791
6782
  function isEqualArray(a, b) {
6792
- return Arrays.isEqual(a, b, { exactOrder: false });
6783
+ return Objects.isEqual(a, b, { ignoreArrayOrder: true });
6793
6784
  }
6794
6785
 
6795
6786
  /**
@@ -10175,7 +10166,7 @@ class MessageBoxHeaderComponent {
10175
10166
  title = input(undefined, { ...(ngDevMode ? { debugName: "title" } : {}) });
10176
10167
  severity = input.required({ ...(ngDevMode ? { debugName: "severity" } : {}) });
10177
10168
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MessageBoxHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
10178
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: MessageBoxHeaderComponent, isStandalone: true, selector: "wb-message-box-header", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, severity: { classPropertyName: "severity", publicName: "severity", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "attr.data-severity": "severity()" } }, ngImport: i0, template: "@if (title()) {\n <span class=\"title e2e-title\">{{(title() | wbText)()}}</span>\n}\n", styles: ["@charset \"UTF-8\";:host{--\\275message-box-severity-color: initial;display:grid;border-top:var(--sci-workbench-messagebox-severity-indicator-size) solid var(--\\275message-box-severity-color);padding-inline:var(--sci-workbench-messagebox-padding);padding-top:var(--sci-workbench-messagebox-padding);-webkit-user-select:none;user-select:none}:host[data-severity=info]{--\\275message-box-severity-color: var(--sci-color-accent)}:host[data-severity=warn]{--\\275message-box-severity-color: var(--sci-color-notice)}:host[data-severity=error]{--\\275message-box-severity-color: var(--sci-color-negative)}:host>span.title{overflow-wrap:break-word;white-space:pre-line;font-family:var(--sci-workbench-messagebox-title-font-family),sans-serif;font-size:var(--sci-workbench-messagebox-title-font-size);font-weight:var(--sci-workbench-messagebox-title-font-weight);text-align:var(--sci-workbench-messagebox-title-align)}\n"], dependencies: [{ kind: "pipe", type: TextPipe, name: "wbText" }] });
10169
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: MessageBoxHeaderComponent, isStandalone: true, selector: "wb-message-box-header", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, severity: { classPropertyName: "severity", publicName: "severity", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "attr.data-severity": "severity()" } }, ngImport: i0, template: "@if (title()) {\n <span class=\"title e2e-title\">{{(title() | wbText)()}}</span>\n}\n", styles: ["@charset \"UTF-8\";:host{--\\275message-box-severity-color: initial;display:grid;border-top:var(--sci-workbench-messagebox-severity-indicator-size) solid var(--\\275message-box-severity-color);padding-inline:var(--sci-workbench-messagebox-padding);padding-top:var(--sci-workbench-messagebox-padding);-webkit-user-select:none;user-select:none}:host[data-severity=info]{--\\275message-box-severity-color: var(--sci-color-accent)}:host[data-severity=warn]{--\\275message-box-severity-color: var(--sci-color-notice)}:host[data-severity=error]{--\\275message-box-severity-color: var(--sci-color-negative)}:host>span.title{word-break:break-word;white-space:pre-line;font-family:var(--sci-workbench-messagebox-title-font-family),sans-serif;font-size:var(--sci-workbench-messagebox-title-font-size);font-weight:var(--sci-workbench-messagebox-title-font-weight);text-align:var(--sci-workbench-messagebox-title-align)}\n"], dependencies: [{ kind: "pipe", type: TextPipe, name: "wbText" }] });
10179
10170
  }
10180
10171
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MessageBoxHeaderComponent, decorators: [{
10181
10172
  type: Component,
@@ -10183,7 +10174,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
10183
10174
  TextPipe,
10184
10175
  ], host: {
10185
10176
  '[attr.data-severity]': 'severity()',
10186
- }, template: "@if (title()) {\n <span class=\"title e2e-title\">{{(title() | wbText)()}}</span>\n}\n", styles: ["@charset \"UTF-8\";:host{--\\275message-box-severity-color: initial;display:grid;border-top:var(--sci-workbench-messagebox-severity-indicator-size) solid var(--\\275message-box-severity-color);padding-inline:var(--sci-workbench-messagebox-padding);padding-top:var(--sci-workbench-messagebox-padding);-webkit-user-select:none;user-select:none}:host[data-severity=info]{--\\275message-box-severity-color: var(--sci-color-accent)}:host[data-severity=warn]{--\\275message-box-severity-color: var(--sci-color-notice)}:host[data-severity=error]{--\\275message-box-severity-color: var(--sci-color-negative)}:host>span.title{overflow-wrap:break-word;white-space:pre-line;font-family:var(--sci-workbench-messagebox-title-font-family),sans-serif;font-size:var(--sci-workbench-messagebox-title-font-size);font-weight:var(--sci-workbench-messagebox-title-font-weight);text-align:var(--sci-workbench-messagebox-title-align)}\n"] }]
10177
+ }, template: "@if (title()) {\n <span class=\"title e2e-title\">{{(title() | wbText)()}}</span>\n}\n", styles: ["@charset \"UTF-8\";:host{--\\275message-box-severity-color: initial;display:grid;border-top:var(--sci-workbench-messagebox-severity-indicator-size) solid var(--\\275message-box-severity-color);padding-inline:var(--sci-workbench-messagebox-padding);padding-top:var(--sci-workbench-messagebox-padding);-webkit-user-select:none;user-select:none}:host[data-severity=info]{--\\275message-box-severity-color: var(--sci-color-accent)}:host[data-severity=warn]{--\\275message-box-severity-color: var(--sci-color-notice)}:host[data-severity=error]{--\\275message-box-severity-color: var(--sci-color-negative)}:host>span.title{word-break:break-word;white-space:pre-line;font-family:var(--sci-workbench-messagebox-title-font-family),sans-serif;font-size:var(--sci-workbench-messagebox-title-font-size);font-weight:var(--sci-workbench-messagebox-title-font-weight);text-align:var(--sci-workbench-messagebox-title-align)}\n"] }]
10187
10178
  }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], severity: [{ type: i0.Input, args: [{ isSignal: true, alias: "severity", required: true }] }] } });
10188
10179
 
10189
10180
  /*
@@ -10228,20 +10219,17 @@ class WorkbenchMessageBoxComponent {
10228
10219
  message = input.required({ ...(ngDevMode ? { debugName: "message" } : {}), transform: nullIfEmptyMessage });
10229
10220
  options = input(undefined, { ...(ngDevMode ? { debugName: "options" } : {}) });
10230
10221
  _dialog = inject(ɵWorkbenchDialog);
10231
- // Ensure host element to be focusable in order to close the message box on Escape keystroke.
10232
- tabindex = -1;
10233
- empty = false;
10234
- get contentSelectable() {
10235
- return this.options()?.contentSelectable;
10236
- }
10237
- get hasTitle() {
10238
- return !!this.options()?.title;
10239
- }
10222
+ empty = signal(false, { ...(ngDevMode ? { debugName: "empty" } : {}) });
10240
10223
  constructor() {
10241
10224
  this._dialog.closable = false;
10242
10225
  this._dialog.resizable = false;
10243
10226
  this._dialog.padding = false;
10244
- this._dialog.size.maxWidth = 'var(--sci-workbench-messagebox-max-width)';
10227
+ // Limit the maximum messagebox width if text message to break the message.
10228
+ effect(() => {
10229
+ if (typeof this.message() === 'string' || this.message() === null) {
10230
+ this._dialog.size.maxWidth = 'var(--sci-workbench-messagebox-max-width)';
10231
+ }
10232
+ });
10245
10233
  }
10246
10234
  onAction(action) {
10247
10235
  this._dialog.close(action);
@@ -10255,10 +10243,10 @@ class WorkbenchMessageBoxComponent {
10255
10243
  this._dialog.size.minWidth = `${preferredSize}px`;
10256
10244
  }
10257
10245
  onContentDimensionChange(dimension) {
10258
- this.empty = !dimension.offsetHeight;
10246
+ this.empty.set(!dimension.offsetHeight);
10259
10247
  }
10260
10248
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchMessageBoxComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
10261
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: WorkbenchMessageBoxComponent, isStandalone: true, selector: "wb-message-box", inputs: { message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: true, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "keydown.escape": "onEscape()" }, properties: { "attr.tabindex": "this.tabindex", "class.empty": "this.empty", "class.content-selectable": "this.contentSelectable", "class.has-title": "this.hasTitle" } }, ngImport: i0, template: "@let options = this.options() ?? {};\n<ng-template wbDialogHeader [divider]=\"false\">\n <wb-message-box-header [title]=\"options.title\" [severity]=\"options.severity ?? 'info'\"/>\n</ng-template>\n\n@let message = this.message();\n<div class=\"message e2e-message\" [class.text]=\"message | wbTypeof:'string'\" sciDimension (sciDimensionChange)=\"onContentDimensionChange($event)\">\n @if (message | wbTypeof:'string') {\n {{($any(message) | wbText)()}}\n } @else {\n <ng-container *ngComponentOutlet=\"message; inputs: options.inputs\"/>\n }\n</div>\n\n<ng-template wbDialogFooter>\n <wb-message-box-footer [actions]=\"options.actions ?? {ok: '%workbench.ok.action'}\"\n [severity]=\"options.severity ?? 'info'\"\n (action)=\"onAction($event)\"\n (keydown.escape)=\"onEscape()\"\n (preferredSizeChange)=\"onFooterPreferredSizeChange($event)\"/>\n</ng-template>\n", styles: [":host{display:grid;overflow:hidden;outline:none;padding-inline:var(--sci-workbench-messagebox-padding);padding-bottom:var(--sci-workbench-messagebox-padding)}:host.has-title:not(.empty){padding-top:var(--sci-workbench-messagebox-padding)}:host:not(.content-selectable){-webkit-user-select:none;user-select:none}:host>div.message.text{overflow-wrap:break-word;white-space:pre-line;text-align:var(--sci-workbench-messagebox-text-align)}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "directive", type: SciDimensionDirective, selector: "[sciDimension]", inputs: ["emitOutsideAngular"], outputs: ["sciDimensionChange"] }, { kind: "directive", type: WorkbenchDialogHeaderDirective, selector: "ng-template[wbDialogHeader]", inputs: ["divider"] }, { kind: "directive", type: WorkbenchDialogFooterDirective, selector: "ng-template[wbDialogFooter]", inputs: ["divider"] }, { kind: "component", type: MessageBoxHeaderComponent, selector: "wb-message-box-header", inputs: ["title", "severity"] }, { kind: "component", type: MessageBoxFooterComponent, selector: "wb-message-box-footer", inputs: ["actions", "severity"], outputs: ["action", "preferredSizeChange"] }, { kind: "pipe", type: TypeofPipe, name: "wbTypeof" }, { kind: "pipe", type: TextPipe, name: "wbText" }] });
10249
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: WorkbenchMessageBoxComponent, isStandalone: true, selector: "wb-message-box", inputs: { message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: true, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "keydown.escape": "onEscape()" }, properties: { "attr.tabindex": "-1", "class.empty": "empty()", "class.content-selectable": "options()?.contentSelectable", "class.has-title": "!!this.options()?.title" } }, ngImport: i0, template: "@let options = this.options() ?? {};\n<ng-template wbDialogHeader [divider]=\"false\">\n <wb-message-box-header [title]=\"options.title\" [severity]=\"options.severity ?? 'info'\"/>\n</ng-template>\n\n@let message = this.message();\n<div class=\"message e2e-message\" [class.text]=\"message | wbTypeof:'string'\" sciDimension (sciDimensionChange)=\"onContentDimensionChange($event)\">\n @if (message | wbTypeof:'string') {\n {{($any(message) | wbText)()}}\n } @else {\n <ng-container *ngComponentOutlet=\"message; inputs: options.inputs\"/>\n }\n</div>\n\n<ng-template wbDialogFooter>\n <wb-message-box-footer [actions]=\"options.actions ?? {ok: '%workbench.ok.action'}\"\n [severity]=\"options.severity ?? 'info'\"\n (action)=\"onAction($event)\"\n (keydown.escape)=\"onEscape()\"\n (preferredSizeChange)=\"onFooterPreferredSizeChange($event)\"/>\n</ng-template>\n", styles: [":host{display:grid;outline:none;padding-inline:var(--sci-workbench-messagebox-padding);padding-bottom:var(--sci-workbench-messagebox-padding)}:host.has-title:not(.empty){padding-top:var(--sci-workbench-messagebox-padding)}:host:not(.content-selectable){-webkit-user-select:none;user-select:none}:host>div.message.text{word-break:break-word;white-space:pre-line;text-align:var(--sci-workbench-messagebox-text-align)}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "directive", type: SciDimensionDirective, selector: "[sciDimension]", inputs: ["emitOutsideAngular"], outputs: ["sciDimensionChange"] }, { kind: "directive", type: WorkbenchDialogHeaderDirective, selector: "ng-template[wbDialogHeader]", inputs: ["divider"] }, { kind: "directive", type: WorkbenchDialogFooterDirective, selector: "ng-template[wbDialogFooter]", inputs: ["divider"] }, { kind: "component", type: MessageBoxHeaderComponent, selector: "wb-message-box-header", inputs: ["title", "severity"] }, { kind: "component", type: MessageBoxFooterComponent, selector: "wb-message-box-footer", inputs: ["actions", "severity"], outputs: ["action", "preferredSizeChange"] }, { kind: "pipe", type: TypeofPipe, name: "wbTypeof" }, { kind: "pipe", type: TextPipe, name: "wbText" }] });
10262
10250
  }
10263
10251
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchMessageBoxComponent, decorators: [{
10264
10252
  type: Component,
@@ -10271,23 +10259,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
10271
10259
  MessageBoxFooterComponent,
10272
10260
  TypeofPipe,
10273
10261
  TextPipe,
10274
- ], template: "@let options = this.options() ?? {};\n<ng-template wbDialogHeader [divider]=\"false\">\n <wb-message-box-header [title]=\"options.title\" [severity]=\"options.severity ?? 'info'\"/>\n</ng-template>\n\n@let message = this.message();\n<div class=\"message e2e-message\" [class.text]=\"message | wbTypeof:'string'\" sciDimension (sciDimensionChange)=\"onContentDimensionChange($event)\">\n @if (message | wbTypeof:'string') {\n {{($any(message) | wbText)()}}\n } @else {\n <ng-container *ngComponentOutlet=\"message; inputs: options.inputs\"/>\n }\n</div>\n\n<ng-template wbDialogFooter>\n <wb-message-box-footer [actions]=\"options.actions ?? {ok: '%workbench.ok.action'}\"\n [severity]=\"options.severity ?? 'info'\"\n (action)=\"onAction($event)\"\n (keydown.escape)=\"onEscape()\"\n (preferredSizeChange)=\"onFooterPreferredSizeChange($event)\"/>\n</ng-template>\n", styles: [":host{display:grid;overflow:hidden;outline:none;padding-inline:var(--sci-workbench-messagebox-padding);padding-bottom:var(--sci-workbench-messagebox-padding)}:host.has-title:not(.empty){padding-top:var(--sci-workbench-messagebox-padding)}:host:not(.content-selectable){-webkit-user-select:none;user-select:none}:host>div.message.text{overflow-wrap:break-word;white-space:pre-line;text-align:var(--sci-workbench-messagebox-text-align)}\n"] }]
10275
- }], ctorParameters: () => [], propDecorators: { message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: true }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], tabindex: [{
10276
- type: HostBinding,
10277
- args: ['attr.tabindex']
10278
- }], empty: [{
10279
- type: HostBinding,
10280
- args: ['class.empty']
10281
- }], contentSelectable: [{
10282
- type: HostBinding,
10283
- args: ['class.content-selectable']
10284
- }], hasTitle: [{
10285
- type: HostBinding,
10286
- args: ['class.has-title']
10287
- }], onEscape: [{
10288
- type: HostListener,
10289
- args: ['keydown.escape']
10290
- }] } });
10262
+ ], host: {
10263
+ // Ensure host element to be focusable in order to close the message box on Escape keystroke.
10264
+ '[attr.tabindex]': '-1',
10265
+ '[class.empty]': 'empty()',
10266
+ '[class.content-selectable]': 'options()?.contentSelectable',
10267
+ '[class.has-title]': '!!this.options()?.title',
10268
+ '(keydown.escape)': 'onEscape()',
10269
+ }, template: "@let options = this.options() ?? {};\n<ng-template wbDialogHeader [divider]=\"false\">\n <wb-message-box-header [title]=\"options.title\" [severity]=\"options.severity ?? 'info'\"/>\n</ng-template>\n\n@let message = this.message();\n<div class=\"message e2e-message\" [class.text]=\"message | wbTypeof:'string'\" sciDimension (sciDimensionChange)=\"onContentDimensionChange($event)\">\n @if (message | wbTypeof:'string') {\n {{($any(message) | wbText)()}}\n } @else {\n <ng-container *ngComponentOutlet=\"message; inputs: options.inputs\"/>\n }\n</div>\n\n<ng-template wbDialogFooter>\n <wb-message-box-footer [actions]=\"options.actions ?? {ok: '%workbench.ok.action'}\"\n [severity]=\"options.severity ?? 'info'\"\n (action)=\"onAction($event)\"\n (keydown.escape)=\"onEscape()\"\n (preferredSizeChange)=\"onFooterPreferredSizeChange($event)\"/>\n</ng-template>\n", styles: [":host{display:grid;outline:none;padding-inline:var(--sci-workbench-messagebox-padding);padding-bottom:var(--sci-workbench-messagebox-padding)}:host.has-title:not(.empty){padding-top:var(--sci-workbench-messagebox-padding)}:host:not(.content-selectable){-webkit-user-select:none;user-select:none}:host>div.message.text{word-break:break-word;white-space:pre-line;text-align:var(--sci-workbench-messagebox-text-align)}\n"] }]
10270
+ }], ctorParameters: () => [], propDecorators: { message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: true }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }] } });
10291
10271
  function nullIfEmptyMessage(message) {
10292
10272
  return message !== '' ? message : null;
10293
10273
  }
@@ -10424,7 +10404,7 @@ class WorkbenchPopupComponent {
10424
10404
  }, { ...(ngDevMode ? { debugName: "effectRef" } : {}) });
10425
10405
  }
10426
10406
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchPopupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
10427
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.0.1", type: WorkbenchPopupComponent, isStandalone: true, selector: "wb-popup", host: { properties: { "attr.data-popupid": "popup.id", "style.width": "popup.size?.width()", "style.min-width": "popup.size?.minWidth()", "style.max-width": "popup.size?.maxWidth()", "style.height": "popup.size?.height()", "style.min-height": "popup.size?.minHeight()", "style.max-height": "popup.size?.maxHeight()", "class": "popup.cssClass()" } }, providers: [
10407
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.0.1", type: WorkbenchPopupComponent, isStandalone: true, selector: "wb-popup", host: { properties: { "attr.data-popupid": "popup.id", "style.width": "popup.size.width()", "style.min-width": "popup.size.minWidth()", "style.max-width": "popup.size.maxWidth()", "style.height": "popup.size.height()", "style.min-height": "popup.size.minHeight()", "style.max-height": "popup.size.maxHeight()", "class": "popup.cssClass()" } }, providers: [
10428
10408
  configurePopupGlassPane(),
10429
10409
  ], viewQueries: [{ propertyName: "_cdkTrapFocus", first: true, predicate: ["focus_trap"], descendants: true, read: CdkTrapFocus, isSignal: true }], hostDirectives: [{ directive: GlassPaneDirective }], ngImport: i0, template: "<sci-viewport cdkTrapFocus class=\"e2e-popup-viewport\" #focus_trap>\n <ng-container *ngComponentOutlet=\"popup.component; inputs: popup.inputs\"/>\n</sci-viewport>\n", styles: [":host{display:grid;grid-template-columns:100%;grid-template-rows:100%;outline:none;border-radius:var(--sci-corner);overflow:hidden}\n"], dependencies: [{ kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "component", type: SciViewportComponent, selector: "sci-viewport", inputs: ["scrollbarStyle"], outputs: ["scroll"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }] });
10430
10410
  }
@@ -10440,12 +10420,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
10440
10420
  configurePopupGlassPane(),
10441
10421
  ], host: {
10442
10422
  '[attr.data-popupid]': 'popup.id',
10443
- '[style.width]': 'popup.size?.width()',
10444
- '[style.min-width]': 'popup.size?.minWidth()',
10445
- '[style.max-width]': 'popup.size?.maxWidth()',
10446
- '[style.height]': 'popup.size?.height()',
10447
- '[style.min-height]': 'popup.size?.minHeight()',
10448
- '[style.max-height]': 'popup.size?.maxHeight()',
10423
+ '[style.width]': 'popup.size.width()',
10424
+ '[style.min-width]': 'popup.size.minWidth()',
10425
+ '[style.max-width]': 'popup.size.maxWidth()',
10426
+ '[style.height]': 'popup.size.height()',
10427
+ '[style.min-height]': 'popup.size.minHeight()',
10428
+ '[style.max-height]': 'popup.size.maxHeight()',
10449
10429
  '[class]': 'popup.cssClass()',
10450
10430
  }, template: "<sci-viewport cdkTrapFocus class=\"e2e-popup-viewport\" #focus_trap>\n <ng-container *ngComponentOutlet=\"popup.component; inputs: popup.inputs\"/>\n</sci-viewport>\n", styles: [":host{display:grid;grid-template-columns:100%;grid-template-rows:100%;outline:none;border-radius:var(--sci-corner);overflow:hidden}\n"] }]
10451
10431
  }], ctorParameters: () => [], propDecorators: { _cdkTrapFocus: [{ type: i0.ViewChild, args: ['focus_trap', { ...{ read: CdkTrapFocus }, isSignal: true }] }] } });
@@ -10963,7 +10943,7 @@ function createInvocationContext(elementId, options) {
10963
10943
  return {
10964
10944
  elementId: part.id,
10965
10945
  attached: part.slot.portal.attached,
10966
- bounds: computed(() => constrainClientRect(part.slot.bounds(), part.bounds())),
10946
+ bounds: computed(() => constrainClientRect(part.slot.bounds(), part.partBounds())),
10967
10947
  destroyed: part.slot.portal.destroyed,
10968
10948
  peripheral: part.peripheral,
10969
10949
  };
@@ -10973,7 +10953,7 @@ function createInvocationContext(elementId, options) {
10973
10953
  return {
10974
10954
  elementId: view.id,
10975
10955
  attached: view.slot.portal.attached,
10976
- bounds: computed(() => constrainClientRect(view.slot.bounds(), view.part().bounds())),
10956
+ bounds: computed(() => constrainClientRect(view.slot.bounds(), view.part().partBounds())),
10977
10957
  destroyed: view.slot.portal.destroyed,
10978
10958
  peripheral: computed(() => view.part().peripheral()),
10979
10959
  };
@@ -11760,6 +11740,7 @@ class ɵWorkbenchView {
11760
11740
  focused = computed(() => this._focusMonitor.activeElement()?.id === this.id, { ...(ngDevMode ? { debugName: "focused" } : {}) });
11761
11741
  menuItems;
11762
11742
  blockedBy;
11743
+ bounds = computed(() => this.slot.bounds(), { ...(ngDevMode ? { debugName: "bounds" } : {}) });
11763
11744
  slot;
11764
11745
  classList = new ClassList();
11765
11746
  isClosable = computed(() => this._closable() && !this.blockedBy(), { ...(ngDevMode ? { debugName: "isClosable" } : {}) });
@@ -12005,7 +11986,7 @@ class ɵWorkbenchView {
12005
11986
  changes?.forEachAddedItem(({ item: fn }) => menuItems.set(fn, computed(() => runInInjectionContext(constructInjector(this, this.part()), () => fn(this)))));
12006
11987
  changes?.forEachRemovedItem(({ item: fn }) => menuItems.delete(fn));
12007
11988
  return Array.from(menuItems.values()).map(menuItem => menuItem()).filter(menuItem => !!menuItem);
12008
- }, { equal: (a, b) => Arrays.isEqual(a, b) });
11989
+ }, { equal: (a, b) => Objects.isEqual(a, b) });
12009
11990
  function constructInjector(view, part) {
12010
11991
  return Injector.create({
12011
11992
  parent: injector,
@@ -12463,7 +12444,7 @@ class ViewMenuService {
12463
12444
  }
12464
12445
  const key = accelerator.at(-1).toLocaleLowerCase();
12465
12446
  const modifierKeys = accelerator.slice(0, -1);
12466
- return key === eventKey && Arrays.isEqual(modifierKeys, eventModifierKeys, { exactOrder: false });
12447
+ return key === eventKey && Objects.isEqual(modifierKeys, eventModifierKeys, { ignoreArrayOrder: true });
12467
12448
  });
12468
12449
  }
12469
12450
  }
@@ -12657,18 +12638,12 @@ class ViewTabComponent {
12657
12638
  }
12658
12639
  onClose(event) {
12659
12640
  event.stopPropagation(); // prevent the view from being activated
12660
- if (event.altKey) {
12661
- void this.view().close('other-views');
12662
- }
12663
- else {
12664
- void this.view().close();
12665
- }
12641
+ this.closeView(event);
12666
12642
  }
12667
- onMousedown(event) {
12668
- if (event.buttons === AUXILARY_MOUSE_BUTTON$1) {
12669
- void this.view().close();
12670
- event.stopPropagation();
12671
- event.preventDefault();
12643
+ onAuxClick(event) {
12644
+ if (event.button === 1) { // primary aux button
12645
+ event.preventDefault(); // prevent user-agent default action
12646
+ this.closeView(event);
12672
12647
  }
12673
12648
  }
12674
12649
  onContextmenu(event) {
@@ -12713,6 +12688,17 @@ class ViewTabComponent {
12713
12688
  onDragEnd() {
12714
12689
  this.viewDragService.unsetViewDragData();
12715
12690
  }
12691
+ /**
12692
+ * Closes the current view or other views if the 'Alt' key is pressed.
12693
+ */
12694
+ closeView(event) {
12695
+ if (event.altKey) {
12696
+ void this.view().close('other-views');
12697
+ }
12698
+ else {
12699
+ void this.view().close();
12700
+ }
12701
+ }
12716
12702
  installMenuAccelerators() {
12717
12703
  this._viewMenuService.installMenuAccelerators(this.host, this.view);
12718
12704
  }
@@ -12729,7 +12715,7 @@ class ViewTabComponent {
12729
12715
  });
12730
12716
  }
12731
12717
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: ViewTabComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
12732
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: ViewTabComponent, isStandalone: true, selector: "wb-view-tab", inputs: { view: { classPropertyName: "view", publicName: "viewId", isSignal: true, isRequired: true, transformFunction: null } }, host: { listeners: { "click": "onClick()", "mousedown": "onMousedown($event)", "contextmenu": "onContextmenu($event)", "dragstart": "onDragStart($event)", "dragend": "onDragEnd()" }, properties: { "attr.data-viewid": "view().id", "attr.data-active": "view().active() ? '' : null", "attr.data-dirty": "view().dirty() ? '' : null", "attr.data-focus-within-view": "view().focused() ? '' : null", "attr.draggable": "true", "attr.tabindex": "-1", "class.view-drag": "viewDragService.dragging()", "class": "view().classList.asList()", "style.--sci-workbench-tab-title-offset-right": "viewTitleOffsetRight()" } }, ngImport: i0, template: "<!-- IMPORTANT: THIS HTML FILE IS ALSO USED BY `ViewTabDragImageComponent` -->\n\n@if (view().active()) {\n <div class=\"corner-radius start\">\n <div class=\"circle\"></div>\n </div>\n}\n\n<div class=\"content\">\n <ng-container *cdkPortalOutlet=\"viewTabContentPortal()\"/>\n</div>\n\n@if (view().active()) {\n <div class=\"corner-radius end\">\n <div class=\"circle\"></div>\n </div>\n}\n\n@if (view().closable()) {\n <button (click)=\"onClose($event)\"\n [title]=\"('%workbench.close_tab.tooltip;close_others_modifier=Alt' | wbText)()\"\n [disabled]=\"!view().isClosable()\"\n class=\"close e2e-close\">\n <wb-icon icon=\"workbench.close\"/>\n </button>\n}\n", styles: [":host{display:grid;align-items:center;padding-left:var(--sci-workbench-tab-padding-inline);padding-right:var(--sci-workbench-tab-padding-inline);position:relative;-webkit-user-select:none;user-select:none;cursor:var(--sci-workbench-tab-cursor);box-sizing:border-box;outline:none;border-top:var(--sci-workbench-tab-border-top-width) solid transparent;border-left:var(--sci-workbench-tab-border-width) solid transparent;border-right:var(--sci-workbench-tab-border-width) solid transparent;border-top-left-radius:var(--sci-workbench-tab-border-radius);border-top-right-radius:var(--sci-workbench-tab-border-radius)}:host[data-active]{cursor:default;border-left-color:var(--sci-workbench-tab-border-color);border-right-color:var(--sci-workbench-tab-border-color);border-top-color:var(--sci-workbench-tab-border-color);background-color:var(--sci-workbench-view-background-color)}wb-part[data-peripheral] :host[data-active]{background-color:var(--sci-workbench-view-peripheral-background-color)}:host[data-active]>div.content{color:var(--sci-workbench-tab-text-color-active)}:host[data-active][data-focus-within-view]>div.content{color:var(--sci-workbench-part-active-tab-text-color-active)}:host>div.content{display:inline-grid;font-family:var(--sci-workbench-tab-font-family),sans-serif;font-size:var(--sci-workbench-tab-font-size);font-weight:var(--sci-workbench-tab-font-weight);min-width:var(--sci-workbench-tab-min-width);max-width:var(--sci-workbench-tab-max-width);isolation:isolate;transform:translateY(calc(-1 * var(--sci-workbench-tab-border-top-width)))}:host>button.close:is(button,#sci-reset){all:unset;display:inline-grid;place-content:center;place-items:center;padding:.25em;border-radius:var(--sci-corner);-webkit-user-select:none;user-select:none;overflow:hidden;cursor:var(--sci-workbench-button-cursor)}:host>button.close:is(button,#sci-reset):hover:where(:not(:disabled)){background-color:var(--sci-workbench-button-background-color-hover)}:host>button.close:is(button,#sci-reset):active:where(:not(:disabled)){background-color:var(--sci-workbench-button-background-color-active)}:host>button.close:is(button,#sci-reset):focus:not(:focus-visible){outline:none}:host>button.close:is(button,#sci-reset):focus-visible{outline:var(--sci-workbench-button-outline-width-focus) solid var(--sci-color-accent)}:host>button.close:is(button,#sci-reset):disabled{color:var(--sci-color-gray-500)}:host>button.close:is(button,#sci-reset){position:absolute;right:calc(var(--sci-workbench-tab-padding-inline) - .125em);visibility:hidden;padding:.125em;border-radius:var(--sci-corner-small);transform:translateY(calc(-1 * var(--sci-workbench-tab-border-top-width)))}:host[data-active]>button.close:is(button,#sci-reset),:host:hover:not(.view-drag)>button.close:is(button,#sci-reset){visibility:visible}@container style(--sci-workbench-tab-background-color-hover){:host:hover:not([data-active]):not(.view-drag):before{content:\"\";position:absolute;place-self:start center;box-sizing:border-box;height:calc(100% - var(--sci-workbench-tab-padding-block-hover) - var(--sci-workbench-part-bar-border-bottom-width));width:calc(100% - var(--sci-workbench-tab-padding-inline-hover) + 2 * var(--sci-workbench-tab-border-width));background-color:var(--sci-workbench-tab-background-color-hover);border:var(--sci-workbench-tab-border-width) solid var(--sci-workbench-tab-background-color-hover);border-radius:var(--sci-workbench-tab-border-radius);pointer-events:none}:host:hover:not([data-active]):not(.view-drag)>button.close:is(button,#sci-reset):not(:disabled):hover{background-color:color-mix(in srgb,var(--sci-workbench-tab-background-color-hover) 90%,light-dark(var(--sci-static-color-black),var(--sci-static-color-white)))}}:host>div.corner-radius{height:var(--sci-workbench-tab-border-radius);width:var(--sci-workbench-tab-border-radius);overflow:hidden;position:absolute;bottom:0}:host>div.corner-radius>div.circle{position:absolute;top:calc(-2 * var(--sci-workbench-tab-border-radius));width:calc(2 * var(--sci-workbench-tab-border-radius));height:calc(2 * var(--sci-workbench-tab-border-radius));border:var(--sci-workbench-tab-border-radius) solid var(--sci-workbench-view-background-color);border-radius:50%;box-shadow:inset 0 0 0 var(--sci-workbench-tab-border-width) var(--sci-workbench-tab-border-color);box-sizing:content-box}wb-part[data-peripheral] :host>div.corner-radius>div.circle{border-color:var(--sci-workbench-view-peripheral-background-color)}:host>div.corner-radius.start{left:calc(-1 * var(--sci-workbench-tab-border-radius))}:host>div.corner-radius.start>div.circle{left:calc(-2 * var(--sci-workbench-tab-border-radius))}:host>div.corner-radius.end{right:calc(-1 * var(--sci-workbench-tab-border-radius))}:host>div.corner-radius.end>div.circle{right:calc(-2 * var(--sci-workbench-tab-border-radius))}@container viewtab (height >= 3.5rem){:host>button.close:is(button,#sci-reset){top:5px;right:5px}}\n"], dependencies: [{ kind: "directive", type: CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }, { kind: "component", type: IconComponent, selector: "wb-icon", inputs: ["icon"] }, { kind: "pipe", type: TextPipe, name: "wbText" }] });
12718
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: ViewTabComponent, isStandalone: true, selector: "wb-view-tab", inputs: { view: { classPropertyName: "view", publicName: "viewId", isSignal: true, isRequired: true, transformFunction: null } }, host: { listeners: { "click": "onClick()", "auxclick": "onAuxClick($event)", "contextmenu": "onContextmenu($event)", "dragstart": "onDragStart($event)", "dragend": "onDragEnd()" }, properties: { "attr.data-viewid": "view().id", "attr.data-active": "view().active() ? '' : null", "attr.data-dirty": "view().dirty() ? '' : null", "attr.data-focus-within-view": "view().focused() ? '' : null", "attr.draggable": "true", "attr.tabindex": "-1", "class.view-drag": "viewDragService.dragging()", "class": "view().classList.asList()", "style.--sci-workbench-tab-title-offset-right": "viewTitleOffsetRight()" } }, ngImport: i0, template: "<!-- IMPORTANT: THIS HTML FILE IS ALSO USED BY `ViewTabDragImageComponent` -->\n\n@if (view().active()) {\n <div class=\"corner-radius start\">\n <div class=\"circle\"></div>\n </div>\n}\n\n<div class=\"content\">\n <ng-container *cdkPortalOutlet=\"viewTabContentPortal()\"/>\n</div>\n\n@if (view().active()) {\n <div class=\"corner-radius end\">\n <div class=\"circle\"></div>\n </div>\n}\n\n@if (view().closable()) {\n <button (click)=\"onClose($event)\"\n [title]=\"('%workbench.close_tab.tooltip;close_others_modifier=Alt' | wbText)()\"\n [disabled]=\"!view().isClosable()\"\n class=\"close e2e-close\">\n <wb-icon icon=\"workbench.close\"/>\n </button>\n}\n", styles: [":host{display:grid;align-items:center;padding-left:var(--sci-workbench-tab-padding-inline);padding-right:var(--sci-workbench-tab-padding-inline);position:relative;-webkit-user-select:none;user-select:none;cursor:var(--sci-workbench-tab-cursor);box-sizing:border-box;outline:none;border-top:var(--sci-workbench-tab-border-top-width) solid transparent;border-left:var(--sci-workbench-tab-border-width) solid transparent;border-right:var(--sci-workbench-tab-border-width) solid transparent;border-top-left-radius:var(--sci-workbench-tab-border-radius);border-top-right-radius:var(--sci-workbench-tab-border-radius)}:host[data-active]{cursor:default;border-left-color:var(--sci-workbench-tab-border-color);border-right-color:var(--sci-workbench-tab-border-color);border-top-color:var(--sci-workbench-tab-border-color);background-color:var(--sci-workbench-view-background-color)}wb-part[data-peripheral] :host[data-active]{background-color:var(--sci-workbench-view-peripheral-background-color)}:host[data-active]>div.content{color:var(--sci-workbench-tab-text-color-active)}:host[data-active][data-focus-within-view]>div.content{color:var(--sci-workbench-part-active-tab-text-color-active)}:host>div.content{display:inline-grid;font-family:var(--sci-workbench-tab-font-family),sans-serif;font-size:var(--sci-workbench-tab-font-size);font-weight:var(--sci-workbench-tab-font-weight);min-width:var(--sci-workbench-tab-min-width);max-width:var(--sci-workbench-tab-max-width);isolation:isolate;transform:translateY(calc(-1 * var(--sci-workbench-tab-border-top-width)))}:host>button.close:is(button,#sci-reset){all:unset;display:inline-grid;place-content:center;place-items:center;padding:.25em;border-radius:var(--sci-corner);-webkit-user-select:none;user-select:none;overflow:hidden;cursor:var(--sci-workbench-button-cursor)}:host>button.close:is(button,#sci-reset):hover:where(:not(:disabled)){background-color:var(--sci-workbench-button-background-color-hover)}:host>button.close:is(button,#sci-reset):active:where(:not(:disabled)){background-color:var(--sci-workbench-button-background-color-active)}:host>button.close:is(button,#sci-reset):focus:not(:focus-visible){outline:none}:host>button.close:is(button,#sci-reset):focus-visible{outline:var(--sci-workbench-button-outline-width-focus) solid var(--sci-color-accent)}:host>button.close:is(button,#sci-reset):disabled{color:var(--sci-color-gray-500)}:host>button.close:is(button,#sci-reset){position:absolute;right:calc(var(--sci-workbench-tab-padding-inline) - .125em);visibility:hidden;padding:.125em;border-radius:var(--sci-corner-small);transform:translateY(calc(-1 * var(--sci-workbench-tab-border-top-width)))}:host[data-active]>button.close:is(button,#sci-reset),:host:hover:not(.view-drag)>button.close:is(button,#sci-reset){visibility:visible}@container style(--sci-workbench-tab-background-color-hover){:host:hover:not([data-active]):not(.view-drag):before{content:\"\";position:absolute;place-self:start center;box-sizing:border-box;height:calc(100% - var(--sci-workbench-tab-padding-block-hover) - var(--sci-workbench-part-bar-border-bottom-width));width:calc(100% - var(--sci-workbench-tab-padding-inline-hover) + 2 * var(--sci-workbench-tab-border-width));background-color:var(--sci-workbench-tab-background-color-hover);border:var(--sci-workbench-tab-border-width) solid var(--sci-workbench-tab-background-color-hover);border-radius:var(--sci-workbench-tab-border-radius);pointer-events:none}:host:hover:not([data-active]):not(.view-drag)>button.close:is(button,#sci-reset):not(:disabled):hover{background-color:color-mix(in srgb,var(--sci-workbench-tab-background-color-hover) 90%,light-dark(var(--sci-static-color-black),var(--sci-static-color-white)))}}:host>div.corner-radius{height:var(--sci-workbench-tab-border-radius);width:var(--sci-workbench-tab-border-radius);overflow:hidden;position:absolute;bottom:0}:host>div.corner-radius>div.circle{position:absolute;top:calc(-2 * var(--sci-workbench-tab-border-radius));width:calc(2 * var(--sci-workbench-tab-border-radius));height:calc(2 * var(--sci-workbench-tab-border-radius));border:var(--sci-workbench-tab-border-radius) solid var(--sci-workbench-view-background-color);border-radius:50%;box-shadow:inset 0 0 0 var(--sci-workbench-tab-border-width) var(--sci-workbench-tab-border-color);box-sizing:content-box}wb-part[data-peripheral] :host>div.corner-radius>div.circle{border-color:var(--sci-workbench-view-peripheral-background-color)}:host>div.corner-radius.start{left:calc(-1 * var(--sci-workbench-tab-border-radius))}:host>div.corner-radius.start>div.circle{left:calc(-2 * var(--sci-workbench-tab-border-radius))}:host>div.corner-radius.end{right:calc(-1 * var(--sci-workbench-tab-border-radius))}:host>div.corner-radius.end>div.circle{right:calc(-2 * var(--sci-workbench-tab-border-radius))}@container viewtab (height >= 3.5rem){:host>button.close:is(button,#sci-reset){top:5px;right:5px}}\n"], dependencies: [{ kind: "directive", type: CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }, { kind: "component", type: IconComponent, selector: "wb-icon", inputs: ["icon"] }, { kind: "pipe", type: TextPipe, name: "wbText" }] });
12733
12719
  }
12734
12720
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: ViewTabComponent, decorators: [{
12735
12721
  type: Component,
@@ -12747,27 +12733,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
12747
12733
  '[class.view-drag]': 'viewDragService.dragging()',
12748
12734
  '[class]': 'view().classList.asList()',
12749
12735
  '[style.--sci-workbench-tab-title-offset-right]': 'viewTitleOffsetRight()',
12736
+ '(click)': 'onClick()',
12737
+ '(auxclick)': 'onAuxClick($event)',
12738
+ '(contextmenu)': 'onContextmenu($event)',
12739
+ '(dragstart)': 'onDragStart($event)',
12740
+ '(dragend)': 'onDragEnd()',
12750
12741
  }, template: "<!-- IMPORTANT: THIS HTML FILE IS ALSO USED BY `ViewTabDragImageComponent` -->\n\n@if (view().active()) {\n <div class=\"corner-radius start\">\n <div class=\"circle\"></div>\n </div>\n}\n\n<div class=\"content\">\n <ng-container *cdkPortalOutlet=\"viewTabContentPortal()\"/>\n</div>\n\n@if (view().active()) {\n <div class=\"corner-radius end\">\n <div class=\"circle\"></div>\n </div>\n}\n\n@if (view().closable()) {\n <button (click)=\"onClose($event)\"\n [title]=\"('%workbench.close_tab.tooltip;close_others_modifier=Alt' | wbText)()\"\n [disabled]=\"!view().isClosable()\"\n class=\"close e2e-close\">\n <wb-icon icon=\"workbench.close\"/>\n </button>\n}\n", styles: [":host{display:grid;align-items:center;padding-left:var(--sci-workbench-tab-padding-inline);padding-right:var(--sci-workbench-tab-padding-inline);position:relative;-webkit-user-select:none;user-select:none;cursor:var(--sci-workbench-tab-cursor);box-sizing:border-box;outline:none;border-top:var(--sci-workbench-tab-border-top-width) solid transparent;border-left:var(--sci-workbench-tab-border-width) solid transparent;border-right:var(--sci-workbench-tab-border-width) solid transparent;border-top-left-radius:var(--sci-workbench-tab-border-radius);border-top-right-radius:var(--sci-workbench-tab-border-radius)}:host[data-active]{cursor:default;border-left-color:var(--sci-workbench-tab-border-color);border-right-color:var(--sci-workbench-tab-border-color);border-top-color:var(--sci-workbench-tab-border-color);background-color:var(--sci-workbench-view-background-color)}wb-part[data-peripheral] :host[data-active]{background-color:var(--sci-workbench-view-peripheral-background-color)}:host[data-active]>div.content{color:var(--sci-workbench-tab-text-color-active)}:host[data-active][data-focus-within-view]>div.content{color:var(--sci-workbench-part-active-tab-text-color-active)}:host>div.content{display:inline-grid;font-family:var(--sci-workbench-tab-font-family),sans-serif;font-size:var(--sci-workbench-tab-font-size);font-weight:var(--sci-workbench-tab-font-weight);min-width:var(--sci-workbench-tab-min-width);max-width:var(--sci-workbench-tab-max-width);isolation:isolate;transform:translateY(calc(-1 * var(--sci-workbench-tab-border-top-width)))}:host>button.close:is(button,#sci-reset){all:unset;display:inline-grid;place-content:center;place-items:center;padding:.25em;border-radius:var(--sci-corner);-webkit-user-select:none;user-select:none;overflow:hidden;cursor:var(--sci-workbench-button-cursor)}:host>button.close:is(button,#sci-reset):hover:where(:not(:disabled)){background-color:var(--sci-workbench-button-background-color-hover)}:host>button.close:is(button,#sci-reset):active:where(:not(:disabled)){background-color:var(--sci-workbench-button-background-color-active)}:host>button.close:is(button,#sci-reset):focus:not(:focus-visible){outline:none}:host>button.close:is(button,#sci-reset):focus-visible{outline:var(--sci-workbench-button-outline-width-focus) solid var(--sci-color-accent)}:host>button.close:is(button,#sci-reset):disabled{color:var(--sci-color-gray-500)}:host>button.close:is(button,#sci-reset){position:absolute;right:calc(var(--sci-workbench-tab-padding-inline) - .125em);visibility:hidden;padding:.125em;border-radius:var(--sci-corner-small);transform:translateY(calc(-1 * var(--sci-workbench-tab-border-top-width)))}:host[data-active]>button.close:is(button,#sci-reset),:host:hover:not(.view-drag)>button.close:is(button,#sci-reset){visibility:visible}@container style(--sci-workbench-tab-background-color-hover){:host:hover:not([data-active]):not(.view-drag):before{content:\"\";position:absolute;place-self:start center;box-sizing:border-box;height:calc(100% - var(--sci-workbench-tab-padding-block-hover) - var(--sci-workbench-part-bar-border-bottom-width));width:calc(100% - var(--sci-workbench-tab-padding-inline-hover) + 2 * var(--sci-workbench-tab-border-width));background-color:var(--sci-workbench-tab-background-color-hover);border:var(--sci-workbench-tab-border-width) solid var(--sci-workbench-tab-background-color-hover);border-radius:var(--sci-workbench-tab-border-radius);pointer-events:none}:host:hover:not([data-active]):not(.view-drag)>button.close:is(button,#sci-reset):not(:disabled):hover{background-color:color-mix(in srgb,var(--sci-workbench-tab-background-color-hover) 90%,light-dark(var(--sci-static-color-black),var(--sci-static-color-white)))}}:host>div.corner-radius{height:var(--sci-workbench-tab-border-radius);width:var(--sci-workbench-tab-border-radius);overflow:hidden;position:absolute;bottom:0}:host>div.corner-radius>div.circle{position:absolute;top:calc(-2 * var(--sci-workbench-tab-border-radius));width:calc(2 * var(--sci-workbench-tab-border-radius));height:calc(2 * var(--sci-workbench-tab-border-radius));border:var(--sci-workbench-tab-border-radius) solid var(--sci-workbench-view-background-color);border-radius:50%;box-shadow:inset 0 0 0 var(--sci-workbench-tab-border-width) var(--sci-workbench-tab-border-color);box-sizing:content-box}wb-part[data-peripheral] :host>div.corner-radius>div.circle{border-color:var(--sci-workbench-view-peripheral-background-color)}:host>div.corner-radius.start{left:calc(-1 * var(--sci-workbench-tab-border-radius))}:host>div.corner-radius.start>div.circle{left:calc(-2 * var(--sci-workbench-tab-border-radius))}:host>div.corner-radius.end{right:calc(-1 * var(--sci-workbench-tab-border-radius))}:host>div.corner-radius.end>div.circle{right:calc(-2 * var(--sci-workbench-tab-border-radius))}@container viewtab (height >= 3.5rem){:host>button.close:is(button,#sci-reset){top:5px;right:5px}}\n"] }]
12751
- }], ctorParameters: () => [], propDecorators: { view: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewId", required: true }] }], onClick: [{
12752
- type: HostListener,
12753
- args: ['click']
12754
- }], onMousedown: [{
12755
- type: HostListener,
12756
- args: ['mousedown', ['$event']]
12757
- }], onContextmenu: [{
12758
- type: HostListener,
12759
- args: ['contextmenu', ['$event']]
12760
- }], onDragStart: [{
12761
- type: HostListener,
12762
- args: ['dragstart', ['$event']]
12763
- }], onDragEnd: [{
12764
- type: HostListener,
12765
- args: ['dragend']
12766
- }] } });
12767
- /**
12768
- * Indicates that the auxilary mouse button is pressed (usually the mouse wheel button or middle button).
12769
- */
12770
- const AUXILARY_MOUSE_BUTTON$1 = 4;
12742
+ }], ctorParameters: () => [], propDecorators: { view: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewId", required: true }] }] } });
12771
12743
 
12772
12744
  /*
12773
12745
  * Copyright (c) 2018-2022 Swiss Federal Railways
@@ -13027,6 +12999,7 @@ class DragImageWorkbenchView {
13027
12999
  menuItems = signal([], { ...(ngDevMode ? { debugName: "menuItems" } : {}) });
13028
13000
  isClosable;
13029
13001
  activationInstant = signal(0, { ...(ngDevMode ? { debugName: "activationInstant" } : {}) });
13002
+ bounds = signal(undefined, { ...(ngDevMode ? { debugName: "bounds" } : {}) });
13030
13003
  constructor(dragData) {
13031
13004
  this.id = dragData.viewId;
13032
13005
  this.alternativeId = dragData.alternativeViewId;
@@ -13794,7 +13767,9 @@ class ɵWorkbenchPart {
13794
13767
  views;
13795
13768
  classList = new ClassList();
13796
13769
  portal;
13797
- bounds;
13770
+ partBounds;
13771
+ /** Represents the bounds of the slot. Used for public API. Internally, use {@link slot.bounds()} instead. */
13772
+ bounds = computed(() => this.slot.bounds(), { ...(ngDevMode ? { debugName: "bounds" } : {}) });
13798
13773
  blockedBy;
13799
13774
  slot;
13800
13775
  _isInMainArea;
@@ -13807,7 +13782,7 @@ class ɵWorkbenchPart {
13807
13782
  this.views = computeViews(this.mPart);
13808
13783
  this.alternativeId = this.mPart().alternativeId;
13809
13784
  this.portal = this.createPartPortal();
13810
- this.bounds = boundingClientRect(computed(() => this.portal.element()));
13785
+ this.partBounds = boundingClientRect(computed(() => this.portal.element()));
13811
13786
  this.blockedBy = inject(WorkbenchDialogRegistry).top(this.id);
13812
13787
  this.slot = {
13813
13788
  portal: this.createPartSlotPortal(),
@@ -14069,7 +14044,7 @@ function computePartActions(part) {
14069
14044
  changes?.forEachAddedItem(({ item: fn }) => partActions.set(fn, computed(() => constructPartAction(fn, injector))));
14070
14045
  changes?.forEachRemovedItem(({ item: fn }) => partActions.delete(fn));
14071
14046
  return Array.from(partActions.values()).map(partAction => partAction()).filter(partAction => !!partAction);
14072
- }, { equal: (a, b) => Arrays.isEqual(a, b) });
14047
+ }, { equal: (a, b) => Objects.isEqual(a, b) });
14073
14048
  function constructPartAction(factoryFn, injector) {
14074
14049
  const action = runInInjectionContext(injector, () => factoryFn(inject(ɵWorkbenchPart)));
14075
14050
  if (action instanceof TemplateRef || typeof action === 'function') {
@@ -14090,7 +14065,7 @@ function computeActiveView(mPart) {
14090
14065
  */
14091
14066
  function computeViews(mPart) {
14092
14067
  const viewRegistry = inject(WorkbenchViewRegistry);
14093
- return computed(() => mPart().views.map(mView => viewRegistry.get(mView.id)), { equal: (a, b) => Arrays.isEqual(a, b, { exactOrder: true }) });
14068
+ return computed(() => mPart().views.map(mView => viewRegistry.get(mView.id)), { equal: (a, b) => Objects.isEqual(a, b) });
14094
14069
  }
14095
14070
 
14096
14071
  /*
@@ -15244,8 +15219,7 @@ class MicrofrontendViewComponent {
15244
15219
  installNavigator() {
15245
15220
  // Use a root effect to emit even if detached from change detection.
15246
15221
  toRootObservable(this.navigationContext)
15247
- .pipe(tap(context => this.onPreNavigate(context)), filter((context) => !!context.capability), delayIfLazy(), serializeExecution(context => this.onNavigate(context)), subscribeOn(asyncScheduler), // subscribe asynchronously because `onCapabilityChange` triggers a manual change detection cycle for inactive views; would error if called during component construction otherwise.
15248
- takeUntilDestroyed())
15222
+ .pipe(tap(context => this.onPreNavigate(context)), filter((context) => !!context.capability), delayIfLazy(), serializeExecution(context => this.onNavigate(context)), takeUntilDestroyed())
15249
15223
  .subscribe();
15250
15224
  }
15251
15225
  /**
@@ -15268,6 +15242,7 @@ class MicrofrontendViewComponent {
15268
15242
  }),
15269
15243
  referrer: navigationData.referrer,
15270
15244
  }),
15245
+ equal: (a, b) => a.capability?.metadata.id === b.capability?.metadata.id && Objects.isEqual(a.params, b.params), // do not create new navigation context when navigating to the same capability with the same params
15271
15246
  });
15272
15247
  }
15273
15248
  /**
@@ -15483,7 +15458,7 @@ class MicrofrontendViewComponent {
15483
15458
  }
15484
15459
  }))
15485
15460
  .map(accelerator => `keydown.${accelerator.join('.')}{preventDefault=true}`);
15486
- }, { equal: (a, b) => Arrays.isEqual(a, b, { exactOrder: false }) });
15461
+ }, { equal: (a, b) => Objects.isEqual(a, b, { ignoreArrayOrder: true }) });
15487
15462
  }
15488
15463
  propagateWorkbenchTheme() {
15489
15464
  Microfrontends.propagateTheme(this._routerOutletElement);
@@ -15570,6 +15545,12 @@ class ActivatedMicrofrontend {
15570
15545
  * Embeds the microfrontend of a capability provided by the workbench host application.
15571
15546
  */
15572
15547
  class MicrofrontendHostComponent {
15548
+ /**
15549
+ * Parameters passed to the microfrontend, required to reduce inputs for notification capabilities.
15550
+ *
15551
+ * @see MicrofrontendNotificationIntentHandler
15552
+ */
15553
+ params = input(undefined, { ...(ngDevMode ? { debugName: "params" } : {}) });
15573
15554
  _logger = inject(Logger);
15574
15555
  workbenchElement = inject(WORKBENCH_ELEMENT);
15575
15556
  capability = inject(ActivatedMicrofrontend).capability;
@@ -15605,7 +15586,7 @@ class MicrofrontendHostComponent {
15605
15586
  });
15606
15587
  }
15607
15588
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MicrofrontendHostComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
15608
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.1", type: MicrofrontendHostComponent, isStandalone: true, selector: "wb-microfrontend-host", host: { properties: { "attr.data-capabilityid": "capability().metadata!.id", "attr.data-app": "capability().metadata!.appSymbolicName", "attr.data-focus": "workbenchElement.focused() ? '' : null" } }, providers: [
15589
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.1", type: MicrofrontendHostComponent, isStandalone: true, selector: "wb-microfrontend-host", inputs: { params: { classPropertyName: "params", publicName: "params", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-capabilityid": "capability().metadata!.id", "attr.data-app": "capability().metadata!.appSymbolicName", "attr.data-focus": "workbenchElement.focused() ? '' : null" } }, providers: [
15609
15590
  // Provide `ActivatedMicrofrontend` for DI in router outlet. Otherwise, `ActivatedMicrofrontend` would not be available, most likely because provided on the route level.
15610
15591
  // TODO [Angular 22] Check if still required. If not, remove this TODO.
15611
15592
  { provide: ActivatedMicrofrontend, useFactory: () => inject(ActivatedMicrofrontend, { skipSelf: true }) },
@@ -15625,7 +15606,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
15625
15606
  // TODO [Angular 22] Check if still required. If not, remove this TODO.
15626
15607
  { provide: ActivatedMicrofrontend, useFactory: () => inject(ActivatedMicrofrontend, { skipSelf: true }) },
15627
15608
  ], template: "<router-outlet [name]=\"outlet()\" wbRouterOutletRootContext/>\n", styles: [":host{display:grid}:host>router-outlet{position:absolute}\n"] }]
15628
- }], ctorParameters: () => [] });
15609
+ }], ctorParameters: () => [], propDecorators: { params: [{ type: i0.Input, args: [{ isSignal: true, alias: "params", required: false }] }] } });
15629
15610
 
15630
15611
  /*
15631
15612
  * Copyright (c) 2018-2025 Swiss Federal Railways
@@ -15758,7 +15739,7 @@ function provideMicrofrontendViewRoute() {
15758
15739
  path: '',
15759
15740
  component: MicrofrontendHostComponent,
15760
15741
  canMatch: [canMatchMicrofrontendView({ host: true })], // use a single matcher because Angular evaluates matchers in parallel
15761
- providers: [provideActivatedMicrofrontend$4()],
15742
+ providers: [provideActivatedMicrofrontend$5()],
15762
15743
  }),
15763
15744
  multi: true,
15764
15745
  },
@@ -15792,7 +15773,7 @@ function canMatchMicrofrontendView(matcher) {
15792
15773
  /**
15793
15774
  * Provides {@link ActivatedMicrofrontend} for injection in the host microfrontend.
15794
15775
  */
15795
- function provideActivatedMicrofrontend$4() {
15776
+ function provideActivatedMicrofrontend$5() {
15796
15777
  return {
15797
15778
  provide: ActivatedMicrofrontend,
15798
15779
  useFactory: () => {
@@ -16078,7 +16059,7 @@ function provideMicrofrontendPartRoute() {
16078
16059
  path: '',
16079
16060
  component: MicrofrontendHostComponent,
16080
16061
  canMatch: [canMatchMicrofrontendPart({ host: true })], // use a single matcher because Angular evaluates matchers in parallel
16081
- providers: [provideActivatedMicrofrontend$3()],
16062
+ providers: [provideActivatedMicrofrontend$4()],
16082
16063
  }),
16083
16064
  multi: true,
16084
16065
  },
@@ -16112,7 +16093,7 @@ function canMatchMicrofrontendPart(matcher) {
16112
16093
  /**
16113
16094
  * Provides {@link ActivatedMicrofrontend} for injection in the host microfrontend.
16114
16095
  */
16115
- function provideActivatedMicrofrontend$3() {
16096
+ function provideActivatedMicrofrontend$4() {
16116
16097
  return {
16117
16098
  provide: ActivatedMicrofrontend,
16118
16099
  useFactory: () => {
@@ -16145,9 +16126,9 @@ class ParamValidator {
16145
16126
  validatePartParams(params, partCapability, context) {
16146
16127
  const { perspectiveCapability, partId } = context;
16147
16128
  return this.validateParams(params, partCapability, {
16148
- deprecated: param => `[PerspectiveDefinitionWarning] Perspective '${qualifier$7(perspectiveCapability)}' of app '${app$7(perspectiveCapability)}' passes the deprecated parameter '${param}' to part '${partId}'. Migrate deprecated parameters as specified in the capability documentation of part '${qualifier$7(partCapability)}'.`,
16149
- missing: param => `[PerspectiveDefinitionError] Perspective '${qualifier$7(perspectiveCapability)}' of app '${app$7(perspectiveCapability)}' does not pass the required parameter '${param}' to part '${partId}'. Pass required parameters as specified in the capability documentation of part '${qualifier$7(partCapability)}'. Ignoring part.`,
16150
- unexpected: param => `[PerspectiveDefinitionError] Perspective '${qualifier$7(perspectiveCapability)}' of app '${app$7(perspectiveCapability)}' passes the unexpected parameter '${param}' to part '${partId}'. Pass parameters as specified in the capability documentation of part '${qualifier$7(partCapability)}'. Ignoring part.`,
16129
+ deprecated: param => `[PerspectiveDefinitionWarning] Perspective '${qualifier$8(perspectiveCapability)}' of app '${app$8(perspectiveCapability)}' passes the deprecated parameter '${param}' to part '${partId}'. Migrate deprecated parameters as specified in the capability documentation of part '${qualifier$8(partCapability)}'.`,
16130
+ missing: param => `[PerspectiveDefinitionError] Perspective '${qualifier$8(perspectiveCapability)}' of app '${app$8(perspectiveCapability)}' does not pass the required parameter '${param}' to part '${partId}'. Pass required parameters as specified in the capability documentation of part '${qualifier$8(partCapability)}'. Ignoring part.`,
16131
+ unexpected: param => `[PerspectiveDefinitionError] Perspective '${qualifier$8(perspectiveCapability)}' of app '${app$8(perspectiveCapability)}' passes the unexpected parameter '${param}' to part '${partId}'. Pass parameters as specified in the capability documentation of part '${qualifier$8(partCapability)}'. Ignoring part.`,
16151
16132
  });
16152
16133
  }
16153
16134
  /**
@@ -16158,9 +16139,9 @@ class ParamValidator {
16158
16139
  validateViewParams(params, viewCapability, context) {
16159
16140
  const { partCapability } = context;
16160
16141
  return this.validateParams(params, viewCapability, {
16161
- deprecated: param => `[PartDefinitionWarning] Part '${qualifier$7(partCapability)}' of app '${app$7(partCapability)}' passes the deprecated parameter '${param}' to view '${qualifier$7(viewCapability)}'. Migrate deprecated parameters as specified in the capability documentation.`,
16162
- missing: param => `[PartDefinitionError] Part '${qualifier$7(partCapability)}' of app '${app$7(partCapability)}' does not pass the required parameter '${param}' to view '${qualifier$7(viewCapability)}'. Pass required parameters as specified in the capability documentation. Ignoring view.`,
16163
- unexpected: param => `[PartDefinitionError] Part '${qualifier$7(partCapability)}' of app '${app$7(partCapability)}' passes the unexpected parameter '${param}' to view '${qualifier$7(viewCapability)}'. Pass parameters as specified in the capability documentation. Ignoring view.`,
16142
+ deprecated: param => `[PartDefinitionWarning] Part '${qualifier$8(partCapability)}' of app '${app$8(partCapability)}' passes the deprecated parameter '${param}' to view '${qualifier$8(viewCapability)}'. Migrate deprecated parameters as specified in the capability documentation.`,
16143
+ missing: param => `[PartDefinitionError] Part '${qualifier$8(partCapability)}' of app '${app$8(partCapability)}' does not pass the required parameter '${param}' to view '${qualifier$8(viewCapability)}'. Pass required parameters as specified in the capability documentation. Ignoring view.`,
16144
+ unexpected: param => `[PartDefinitionError] Part '${qualifier$8(partCapability)}' of app '${app$8(partCapability)}' passes the unexpected parameter '${param}' to view '${qualifier$8(viewCapability)}'. Pass parameters as specified in the capability documentation. Ignoring view.`,
16164
16145
  });
16165
16146
  }
16166
16147
  validateParams(params, capability, messageFactory) {
@@ -16188,13 +16169,13 @@ class ParamValidator {
16188
16169
  /**
16189
16170
  * Returns the qualifier as string.
16190
16171
  */
16191
- function qualifier$7(capability) {
16172
+ function qualifier$8(capability) {
16192
16173
  return Objects.toMatrixNotation(capability.qualifier);
16193
16174
  }
16194
16175
  /**
16195
16176
  * Returns the app symbolic name.
16196
16177
  */
16197
- function app$7(capability) {
16178
+ function app$8(capability) {
16198
16179
  return capability.metadata.appSymbolicName;
16199
16180
  }
16200
16181
 
@@ -16303,7 +16284,7 @@ class MicrofrontendPerspectiveInstaller {
16303
16284
  // If a relative aligned part and the reference part cannot be found, ignore the part.
16304
16285
  // Do not log an error to support conditional parts, i.e., if aligned relative to a conditional docked part.
16305
16286
  if (typeof partRef.position === 'object' && partRef.position.relativeTo && !layout.hasPart(partRef.position.relativeTo)) {
16306
- this._logger.debug(`[PerspectiveDefinitionInfo] Perspective '${qualifier$6(perspectiveCapability)}' of app '${app$6(perspectiveCapability)}' aligns part '${partRef.id}' relative to missing part '${partRef.position.relativeTo}'. The reference part may not be available. Ignoring part.`, LoggerNames.MICROFRONTEND);
16287
+ this._logger.debug(`[PerspectiveDefinitionInfo] Perspective '${qualifier$7(perspectiveCapability)}' of app '${app$7(perspectiveCapability)}' aligns part '${partRef.id}' relative to missing part '${partRef.position.relativeTo}'. The reference part may not be available. Ignoring part.`, LoggerNames.MICROFRONTEND);
16307
16288
  return layout;
16308
16289
  }
16309
16290
  // If capability cannot be found, ignore the part if docked, or add an empty part otherwise, required to support layouts with conditional reference parts.
@@ -16420,13 +16401,13 @@ class MicrofrontendPerspectiveInstaller {
16420
16401
  }
16421
16402
  }
16422
16403
  if (visibleCapabilities.length > 1) {
16423
- this._logger.error(`[PerspectiveDefinitionError] Multiple ${type} capabilities found for qualifier '${Objects.toMatrixNotation(qualifier)}' in ${requester.type} '${Objects.toMatrixNotation(requester.qualifier)}' of app '${app$6(requester)}'. Defaulting to first. Ensure ${type} capabilities to have a unique qualifier.`, LoggerNames.MICROFRONTEND);
16404
+ this._logger.error(`[PerspectiveDefinitionError] Multiple ${type} capabilities found for qualifier '${Objects.toMatrixNotation(qualifier)}' in ${requester.type} '${Objects.toMatrixNotation(requester.qualifier)}' of app '${app$7(requester)}'. Defaulting to first. Ensure ${type} capabilities to have a unique qualifier.`, LoggerNames.MICROFRONTEND);
16424
16405
  }
16425
16406
  else if (!visibleCapabilities.length && capabilities.length) {
16426
- this._logger.error(`[PerspectiveDefinitionError] Application '${app$6(requester)}' is not qualified to use ${type} capability '${Objects.toMatrixNotation(qualifier)}' in ${requester.type} '${Objects.toMatrixNotation(requester.qualifier)}'. Ensure to have declared an intention and the capability is not private.`, LoggerNames.MICROFRONTEND);
16407
+ this._logger.error(`[PerspectiveDefinitionError] Application '${app$7(requester)}' is not qualified to use ${type} capability '${Objects.toMatrixNotation(qualifier)}' in ${requester.type} '${Objects.toMatrixNotation(requester.qualifier)}'. Ensure to have declared an intention and the capability is not private.`, LoggerNames.MICROFRONTEND);
16427
16408
  }
16428
16409
  else if (!visibleCapabilities.length) {
16429
- const message = `No ${type} capability found for qualifier '${Objects.toMatrixNotation(qualifier)}' in ${requester.type} '${Objects.toMatrixNotation(requester.qualifier)}' of app '${app$6(requester)}'. The qualifier may be incorrect, the capability not registered, or the providing application not available.`;
16410
+ const message = `No ${type} capability found for qualifier '${Objects.toMatrixNotation(qualifier)}' in ${requester.type} '${Objects.toMatrixNotation(requester.qualifier)}' of app '${app$7(requester)}'. The qualifier may be incorrect, the capability not registered, or the providing application not available.`;
16430
16411
  if (context.logLevelIfEmpty === 'error') {
16431
16412
  this._logger.error(`[PerspectiveDefinitionError] ${message}`, LoggerNames.MICROFRONTEND);
16432
16413
  }
@@ -16447,14 +16428,14 @@ class MicrofrontendPerspectiveInstaller {
16447
16428
  // Validate main area part not to have views.
16448
16429
  const isMainAreaPart = partRef.id === MAIN_AREA || partRef.id === MAIN_AREA_ALTERNATIVE_ID;
16449
16430
  if (isMainAreaPart && partCapability.properties?.views?.length) {
16450
- this._logger.error(`[PerspectiveDefinitionError] Part '${qualifier$6(partCapability)}' of app '${app$6(partCapability)}' is used as main area part in perspective '${qualifier$6(context.perspectiveCapability)}' and defines views. Views cannot be added to the main area of a perspective. Ignoring part.`, LoggerNames.MICROFRONTEND);
16431
+ this._logger.error(`[PerspectiveDefinitionError] Part '${qualifier$7(partCapability)}' of app '${app$7(partCapability)}' is used as main area part in perspective '${qualifier$7(context.perspectiveCapability)}' and defines views. Views cannot be added to the main area of a perspective. Ignoring part.`, LoggerNames.MICROFRONTEND);
16451
16432
  return false;
16452
16433
  }
16453
16434
  // Validate extras of docked part.
16454
16435
  if ('position' in partRef && typeof partRef.position === 'string') {
16455
16436
  const dockedPartExtras = partCapability.properties?.extras;
16456
16437
  if (!dockedPartExtras?.label || !dockedPartExtras.icon) {
16457
- this._logger.error(`[PerspectiveDefinitionError] Part '${qualifier$6(partCapability)}' of app '${app$6(partCapability)}' is used as a docked part in perspective '${qualifier$6(context.perspectiveCapability)}' but does not define an icon and label. A docked part must define both an icon and a label: { properties: { extras: { icon: '<icon-name>', label: '<text>' } } }. Ignoring part.`, LoggerNames.MICROFRONTEND);
16438
+ this._logger.error(`[PerspectiveDefinitionError] Part '${qualifier$7(partCapability)}' of app '${app$7(partCapability)}' is used as a docked part in perspective '${qualifier$7(context.perspectiveCapability)}' but does not define an icon and label. A docked part must define both an icon and a label: { properties: { extras: { icon: '<icon-name>', label: '<text>' } } }. Ignoring part.`, LoggerNames.MICROFRONTEND);
16458
16439
  return false;
16459
16440
  }
16460
16441
  }
@@ -16476,13 +16457,13 @@ function createCapabilityRemoteTranslatable(translatable, capability, params) {
16476
16457
  /**
16477
16458
  * Returns the qualifier as string.
16478
16459
  */
16479
- function qualifier$6(capability) {
16460
+ function qualifier$7(capability) {
16480
16461
  return Objects.toMatrixNotation(capability.qualifier);
16481
16462
  }
16482
16463
  /**
16483
16464
  * Returns the app symbolic name.
16484
16465
  */
16485
- function app$6(capability) {
16466
+ function app$7(capability) {
16486
16467
  return capability.metadata.appSymbolicName;
16487
16468
  }
16488
16469
 
@@ -16636,61 +16617,61 @@ class MicrofrontendPerspectiveCapabilityValidator {
16636
16617
  const perspectiveCapability = capability;
16637
16618
  // Assert the perspective capability to have a qualifier.
16638
16619
  if (!Object.keys(perspectiveCapability.qualifier ?? {}).length) {
16639
- throw Error(`[PerspectiveDefinitionError] Perspective capability requires a qualifier [app=${app$5(perspectiveCapability)}, perspective=${JSON.stringify(perspectiveCapability)}]`);
16620
+ throw Error(`[PerspectiveDefinitionError] Perspective capability requires a qualifier [app=${app$6(perspectiveCapability)}, perspective=${JSON.stringify(perspectiveCapability)}]`);
16640
16621
  }
16641
16622
  // Assert the perspective capability to have properties.
16642
16623
  if (!perspectiveCapability.properties) {
16643
- throw Error(`[PerspectiveDefinitionError] Perspective capability requires properties [app=${app$5(perspectiveCapability)}, perspective=${qualifier$5(perspectiveCapability)}]`);
16624
+ throw Error(`[PerspectiveDefinitionError] Perspective capability requires properties [app=${app$6(perspectiveCapability)}, perspective=${qualifier$6(perspectiveCapability)}]`);
16644
16625
  }
16645
16626
  // Assert the perspective capability to have parts.
16646
16627
  if (!perspectiveCapability.properties.parts) {
16647
- throw Error(`[PerspectiveDefinitionError] Perspective capability requires the 'parts' property [app=${app$5(perspectiveCapability)}, perspective=${qualifier$5(perspectiveCapability)}]`);
16628
+ throw Error(`[PerspectiveDefinitionError] Perspective capability requires the 'parts' property [app=${app$6(perspectiveCapability)}, perspective=${qualifier$6(perspectiveCapability)}]`);
16648
16629
  }
16649
16630
  // Assert parts to have an id.
16650
16631
  const parts = perspectiveCapability.properties.parts;
16651
16632
  parts.forEach((part, index) => {
16652
16633
  if (!part.id) {
16653
- throw Error(`[PerspectiveDefinitionError] Missing required 'id' property of part at index '${index}' [app=${app$5(perspectiveCapability)}, perspective=${qualifier$5(perspectiveCapability)}]`);
16634
+ throw Error(`[PerspectiveDefinitionError] Missing required 'id' property of part at index '${index}' [app=${app$6(perspectiveCapability)}, perspective=${qualifier$6(perspectiveCapability)}]`);
16654
16635
  }
16655
16636
  });
16656
16637
  // Assert unique part ids.
16657
16638
  if (new Set(parts.map(part => part.id)).size !== parts.length) {
16658
- throw Error(`[PerspectiveDefinitionError] Parts of perspective must have a unique id [app=${app$5(perspectiveCapability)}, perspective=${qualifier$5(perspectiveCapability)}]`);
16639
+ throw Error(`[PerspectiveDefinitionError] Parts of perspective must have a unique id [app=${app$6(perspectiveCapability)}, perspective=${qualifier$6(perspectiveCapability)}]`);
16659
16640
  }
16660
16641
  // Assert the perspective capability to have an initial part.
16661
16642
  const [initialPart, ...otherParts] = parts;
16662
16643
  if (!initialPart) {
16663
- throw Error(`[PerspectiveDefinitionError] Perspective capability requires an initial part [app=${app$5(perspectiveCapability)}, perspective=${qualifier$5(perspectiveCapability)}]`);
16644
+ throw Error(`[PerspectiveDefinitionError] Perspective capability requires an initial part [app=${app$6(perspectiveCapability)}, perspective=${qualifier$6(perspectiveCapability)}]`);
16664
16645
  }
16665
16646
  // Assert initial part not to be positioned.
16666
16647
  if (initialPart.position) {
16667
- throw Error(`[PerspectiveDefinitionError] Initial part '${initialPart.id}' of perspective must not have the 'position' property [app=${app$5(perspectiveCapability)}, perspective=${qualifier$5(perspectiveCapability)}]`);
16648
+ throw Error(`[PerspectiveDefinitionError] Initial part '${initialPart.id}' of perspective must not have the 'position' property [app=${app$6(perspectiveCapability)}, perspective=${qualifier$6(perspectiveCapability)}]`);
16668
16649
  }
16669
16650
  // Assert other parts to be positioned.
16670
16651
  otherParts.forEach(part => {
16671
16652
  if (!part.position) {
16672
- throw Error(`[PerspectiveDefinitionError] Missing required 'position' property in part '${part.id}' [app=${app$5(perspectiveCapability)}, perspective=${qualifier$5(perspectiveCapability)}]`);
16653
+ throw Error(`[PerspectiveDefinitionError] Missing required 'position' property in part '${part.id}' [app=${app$6(perspectiveCapability)}, perspective=${qualifier$6(perspectiveCapability)}]`);
16673
16654
  }
16674
16655
  if (typeof part.position === 'string' && !dockingAreas.has(part.position)) {
16675
- throw Error(`[PerspectiveDefinitionError] Illegal position in docked part '${part.id}': '${part.position}'. Must be one of [${[...dockingAreas].join(',')}] [app=${app$5(perspectiveCapability)}, perspective=${qualifier$5(perspectiveCapability)}]`);
16656
+ throw Error(`[PerspectiveDefinitionError] Illegal position in docked part '${part.id}': '${part.position}'. Must be one of [${[...dockingAreas].join(',')}] [app=${app$6(perspectiveCapability)}, perspective=${qualifier$6(perspectiveCapability)}]`);
16676
16657
  }
16677
16658
  if (typeof part.position === 'object' && !part.position.align) {
16678
- throw Error(`[PerspectiveDefinitionError] Missing required 'align' property in part '${part.id}': { position: { align: 'left|right|top|bottom' } } [app=${app$5(perspectiveCapability)}, perspective=${qualifier$5(perspectiveCapability)}]`);
16659
+ throw Error(`[PerspectiveDefinitionError] Missing required 'align' property in part '${part.id}': { position: { align: 'left|right|top|bottom' } } [app=${app$6(perspectiveCapability)}, perspective=${qualifier$6(perspectiveCapability)}]`);
16679
16660
  }
16680
16661
  if (typeof part.position === 'object' && !align.has(part.position.align)) {
16681
- throw Error(`[PerspectiveDefinitionError] Illegal alignment of part '${part.id}': '${part.position.align}'. Must be one of [${[...align].join(',')}] [app=${app$5(perspectiveCapability)}, perspective=${qualifier$5(perspectiveCapability)}]`);
16662
+ throw Error(`[PerspectiveDefinitionError] Illegal alignment of part '${part.id}': '${part.position.align}'. Must be one of [${[...align].join(',')}] [app=${app$6(perspectiveCapability)}, perspective=${qualifier$6(perspectiveCapability)}]`);
16682
16663
  }
16683
16664
  if (typeof part.position === 'object' && part.position.relativeTo && !parts.some(otherPart => otherPart.id === part.position.relativeTo)) {
16684
- throw Error(`[PerspectiveDefinitionError] Illegal part '${part.position.relativeTo}' referenced in 'relativeTo' of part '${part.id}'. Part not found. [app=${app$5(perspectiveCapability)}, perspective=${qualifier$5(perspectiveCapability)}]`);
16665
+ throw Error(`[PerspectiveDefinitionError] Illegal part '${part.position.relativeTo}' referenced in 'relativeTo' of part '${part.id}'. Part not found. [app=${app$6(perspectiveCapability)}, perspective=${qualifier$6(perspectiveCapability)}]`);
16685
16666
  }
16686
16667
  });
16687
16668
  // Assert parts to have a qualifier.
16688
16669
  parts.forEach(part => {
16689
16670
  if (!Object.keys(part.qualifier ?? {}).length) {
16690
- throw Error(`[PerspectiveDefinitionError] Missing required qualifier for part '${part.id}' [app=${app$5(perspectiveCapability)}, perspective=${qualifier$5(perspectiveCapability)}]`);
16671
+ throw Error(`[PerspectiveDefinitionError] Missing required qualifier for part '${part.id}' [app=${app$6(perspectiveCapability)}, perspective=${qualifier$6(perspectiveCapability)}]`);
16691
16672
  }
16692
16673
  if (Object.entries(part.qualifier).some(([key, value]) => key === '*' || value === '*')) {
16693
- throw Error(`[PerspectiveDefinitionError] Qualifier for part '${part.id}' must be explicit and not contain wildcards: '${Objects.toMatrixNotation(part.qualifier)}' [app=${app$5(perspectiveCapability)}, perspective=${qualifier$5(perspectiveCapability)}]`);
16674
+ throw Error(`[PerspectiveDefinitionError] Qualifier for part '${part.id}' must be explicit and not contain wildcards: '${Objects.toMatrixNotation(part.qualifier)}' [app=${app$6(perspectiveCapability)}, perspective=${qualifier$6(perspectiveCapability)}]`);
16694
16675
  }
16695
16676
  });
16696
16677
  return capability;
@@ -16704,13 +16685,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
16704
16685
  /**
16705
16686
  * Returns the qualifier as string.
16706
16687
  */
16707
- function qualifier$5(capability) {
16688
+ function qualifier$6(capability) {
16708
16689
  return Objects.toMatrixNotation(capability.qualifier);
16709
16690
  }
16710
16691
  /**
16711
16692
  * Returns the app symbolic name.
16712
16693
  */
16713
- function app$5(capability) {
16694
+ function app$6(capability) {
16714
16695
  return capability.metadata.appSymbolicName;
16715
16696
  }
16716
16697
  const dockingAreas = new Set()
@@ -16840,24 +16821,24 @@ class MicrofrontendPartCapabilityValidator {
16840
16821
  const partCapability = capability;
16841
16822
  // Assert the part capability to have a qualifier.
16842
16823
  if (!Object.keys(partCapability.qualifier ?? {}).length) {
16843
- throw Error(`[PartDefinitionError] Part capability requires a qualifier [app=${app$4(partCapability)}, part=${JSON.stringify(partCapability)}]`);
16824
+ throw Error(`[PartDefinitionError] Part capability requires a qualifier [app=${app$5(partCapability)}, part=${JSON.stringify(partCapability)}]`);
16844
16825
  }
16845
16826
  // Assert docked part extras, if set.
16846
16827
  if (partCapability.properties?.extras) {
16847
16828
  if (!partCapability.properties.extras.icon) {
16848
- throw Error(`[PartDefinitionError] Missing required 'icon' property in docked part extras [app=${app$4(partCapability)}, part=${qualifier$4(partCapability)}]`);
16829
+ throw Error(`[PartDefinitionError] Missing required 'icon' property in docked part extras [app=${app$5(partCapability)}, part=${qualifier$5(partCapability)}]`);
16849
16830
  }
16850
16831
  if (!partCapability.properties.extras.label) {
16851
- throw Error(`[PartDefinitionError] Missing required 'label' property in docked part extras [app=${app$4(partCapability)}, part=${qualifier$4(partCapability)}]`);
16832
+ throw Error(`[PartDefinitionError] Missing required 'label' property in docked part extras [app=${app$5(partCapability)}, part=${qualifier$5(partCapability)}]`);
16852
16833
  }
16853
16834
  }
16854
16835
  // Assert referenced views to have a qualifier.
16855
16836
  partCapability.properties?.views?.forEach((view) => {
16856
16837
  if (!Object.keys(view.qualifier ?? {}).length) {
16857
- throw Error(`[PartDefinitionError] Missing required qualifier for view [app=${app$4(partCapability)}, part=${qualifier$4(partCapability)}]`);
16838
+ throw Error(`[PartDefinitionError] Missing required qualifier for view [app=${app$5(partCapability)}, part=${qualifier$5(partCapability)}]`);
16858
16839
  }
16859
16840
  if (Object.entries(view.qualifier).some(([key, value]) => key === '*' || value === '*')) {
16860
- throw Error(`[PartDefinitionError] View qualifier must be explicit and not contain wildcards: '${Objects.toMatrixNotation(view.qualifier)}' [app=${app$4(partCapability)}, part=${qualifier$4(partCapability)}]`);
16841
+ throw Error(`[PartDefinitionError] View qualifier must be explicit and not contain wildcards: '${Objects.toMatrixNotation(view.qualifier)}' [app=${app$5(partCapability)}, part=${qualifier$5(partCapability)}]`);
16861
16842
  }
16862
16843
  });
16863
16844
  // Assert the path of the part capability.
@@ -16866,11 +16847,11 @@ class MicrofrontendPartCapabilityValidator {
16866
16847
  }
16867
16848
  // Assert host part capabilities not to define the "showSplash" property.
16868
16849
  if (Microfrontends.isHostProvider(capability) && partCapability.properties?.showSplash !== undefined) {
16869
- throw Error(`[PartDefinitionError] Property "showSplash" not supported for part capabilities of the host application [app=${app$4(partCapability)}, part=${qualifier$4(partCapability)}]`);
16850
+ throw Error(`[PartDefinitionError] Property "showSplash" not supported for part capabilities of the host application [app=${app$5(partCapability)}, part=${qualifier$5(partCapability)}]`);
16870
16851
  }
16871
16852
  // Assert "showSplash" property not to be defined if part has no path.
16872
16853
  if (partCapability.properties?.path === undefined && partCapability.properties?.showSplash !== undefined) {
16873
- throw Error(`[PartDefinitionError] Property "showSplash" only supported for part capabilities with a path [app=${app$4(partCapability)}, part=${qualifier$4(partCapability)}]`);
16854
+ throw Error(`[PartDefinitionError] Property "showSplash" only supported for part capabilities with a path [app=${app$5(partCapability)}, part=${qualifier$5(partCapability)}]`);
16874
16855
  }
16875
16856
  return capability;
16876
16857
  }
@@ -16878,12 +16859,12 @@ class MicrofrontendPartCapabilityValidator {
16878
16859
  const path = capability.properties?.path;
16879
16860
  if (Microfrontends.isHostProvider(capability)) {
16880
16861
  if (path === null || path.length) {
16881
- throw Error(`[PartDefinitionError] Part capabilities of the host application require an empty path. [app=${app$4(capability)}, part=${qualifier$4(capability)}]. Change the path '${path}' to empty and add 'canMatchWorkbenchPartCapability(${JSON.stringify(capability.qualifier)})' guard to the route.\n\nExample:\nCapability: { type: 'part', qualifier: ${JSON.stringify(capability.qualifier)}, properties: {path: ''} }\nRoute: { path: '', canMatch: [canMatchWorkbenchPartCapability(${JSON.stringify(capability.qualifier)})], component: PartComponent }`);
16862
+ throw Error(`[PartDefinitionError] Part capabilities of the host application require an empty path. [app=${app$5(capability)}, part=${qualifier$5(capability)}]. Change the path '${path}' to empty and add 'canMatchWorkbenchPartCapability(${JSON.stringify(capability.qualifier)})' guard to the route.\n\nExample:\nCapability: { type: 'part', qualifier: ${JSON.stringify(capability.qualifier)}, properties: {path: ''} }\nRoute: { path: '', canMatch: [canMatchWorkbenchPartCapability(${JSON.stringify(capability.qualifier)})], component: PartComponent }`);
16882
16863
  }
16883
16864
  }
16884
16865
  else {
16885
16866
  if (path === null) {
16886
- throw Error(`[PartDefinitionError] Part capabilities require a path. [app=${app$4(capability)}, part=${qualifier$4(capability)}]`);
16867
+ throw Error(`[PartDefinitionError] Part capabilities require a path. [app=${app$5(capability)}, part=${qualifier$5(capability)}]`);
16887
16868
  }
16888
16869
  }
16889
16870
  }
@@ -16896,13 +16877,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
16896
16877
  /**
16897
16878
  * Returns the qualifier as string.
16898
16879
  */
16899
- function qualifier$4(capability) {
16880
+ function qualifier$5(capability) {
16900
16881
  return Objects.toMatrixNotation(capability.qualifier);
16901
16882
  }
16902
16883
  /**
16903
16884
  * Returns the app symbolic name.
16904
16885
  */
16905
- function app$4(capability) {
16886
+ function app$5(capability) {
16906
16887
  return capability.metadata.appSymbolicName;
16907
16888
  }
16908
16889
 
@@ -17391,21 +17372,21 @@ class MicrofrontendViewCapabilityValidator {
17391
17372
  const viewCapability = capability;
17392
17373
  // Assert the view capability to have a qualifier.
17393
17374
  if (!Object.keys(viewCapability.qualifier ?? {}).length) {
17394
- throw Error(`[ViewDefinitionError] View capability requires a qualifier [app=${app$3(viewCapability)}, view=${qualifier$3(viewCapability)}]`);
17375
+ throw Error(`[ViewDefinitionError] View capability requires a qualifier [app=${app$4(viewCapability)}, view=${qualifier$4(viewCapability)}]`);
17395
17376
  }
17396
17377
  // Assert the view capability to have properties.
17397
17378
  if (!viewCapability.properties) {
17398
- throw Error(`[ViewDefinitionError] View capability requires properties [app=${app$3(viewCapability)}, view=${qualifier$3(viewCapability)}]`);
17379
+ throw Error(`[ViewDefinitionError] View capability requires properties [app=${app$4(viewCapability)}, view=${qualifier$4(viewCapability)}]`);
17399
17380
  }
17400
17381
  // Assert the view capability to have a path, unless provided by the host application.
17401
17382
  this.assertPath(viewCapability);
17402
17383
  // Assert host view capabilities not to define the "lazy" property.
17403
17384
  if (Microfrontends.isHostProvider(capability) && viewCapability.properties.lazy !== undefined) {
17404
- throw Error(`[ViewDefinitionError] Property "lazy" not supported for view capabilities of the host application [app=${app$3(viewCapability)}, view=${qualifier$3(viewCapability)}]`);
17385
+ throw Error(`[ViewDefinitionError] Property "lazy" not supported for view capabilities of the host application [app=${app$4(viewCapability)}, view=${qualifier$4(viewCapability)}]`);
17405
17386
  }
17406
17387
  // Assert host view capabilities not to define the "showSplash" property.
17407
17388
  if (Microfrontends.isHostProvider(capability) && viewCapability.properties.showSplash !== undefined) {
17408
- throw Error(`[ViewDefinitionError] Property "showSplash" not supported for view capabilities of the host application [app=${app$3(viewCapability)}, view=${qualifier$3(viewCapability)}]`);
17389
+ throw Error(`[ViewDefinitionError] Property "showSplash" not supported for view capabilities of the host application [app=${app$4(viewCapability)}, view=${qualifier$4(viewCapability)}]`);
17409
17390
  }
17410
17391
  return capability;
17411
17392
  }
@@ -17413,12 +17394,12 @@ class MicrofrontendViewCapabilityValidator {
17413
17394
  const path = capability.properties?.path;
17414
17395
  if (Microfrontends.isHostProvider(capability)) {
17415
17396
  if (path !== '') {
17416
- throw Error(`[ViewDefinitionError] View capabilities of the host application require an empty path. [app=${app$3(capability)}, view=${qualifier$3(capability)}]. Change the path '${path}' to empty and add 'canMatchWorkbenchViewCapability(${JSON.stringify(capability.qualifier)})' guard to the route.\n\nExample:\nCapability: { type: 'view', qualifier: ${JSON.stringify(capability.qualifier)}, properties: {path: ''} }\nRoute: { path: '', canMatch: [canMatchWorkbenchViewCapability(${JSON.stringify(capability.qualifier)})], component: ViewComponent }`);
17397
+ throw Error(`[ViewDefinitionError] View capabilities of the host application require an empty path. [app=${app$4(capability)}, view=${qualifier$4(capability)}]. Change the path '${path}' to empty and add 'canMatchWorkbenchViewCapability(${JSON.stringify(capability.qualifier)})' guard to the route.\n\nExample:\nCapability: { type: 'view', qualifier: ${JSON.stringify(capability.qualifier)}, properties: {path: ''} }\nRoute: { path: '', canMatch: [canMatchWorkbenchViewCapability(${JSON.stringify(capability.qualifier)})], component: ViewComponent }`);
17417
17398
  }
17418
17399
  }
17419
17400
  else {
17420
17401
  if (path === null || path == undefined) {
17421
- throw Error(`[ViewDefinitionError] View capabilities require a path. [app=${app$3(capability)}, view=${qualifier$3(capability)}]`);
17402
+ throw Error(`[ViewDefinitionError] View capabilities require a path. [app=${app$4(capability)}, view=${qualifier$4(capability)}]`);
17422
17403
  }
17423
17404
  }
17424
17405
  }
@@ -17431,13 +17412,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
17431
17412
  /**
17432
17413
  * Returns the qualifier as string.
17433
17414
  */
17434
- function qualifier$3(capability) {
17415
+ function qualifier$4(capability) {
17435
17416
  return Objects.toMatrixNotation(capability.qualifier);
17436
17417
  }
17437
17418
  /**
17438
17419
  * Returns the app symbolic name.
17439
17420
  */
17440
- function app$3(capability) {
17421
+ function app$4(capability) {
17441
17422
  return capability.metadata.appSymbolicName;
17442
17423
  }
17443
17424
 
@@ -17637,9 +17618,107 @@ function provideMicrofrontendView() {
17637
17618
  }
17638
17619
  }
17639
17620
 
17621
+ /*
17622
+ * Copyright (c) 2018-2026 Swiss Federal Railways
17623
+ *
17624
+ * This program and the accompanying materials are made
17625
+ * available under the terms of the Eclipse Public License 2.0
17626
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
17627
+ *
17628
+ * SPDX-License-Identifier: EPL-2.0
17629
+ */
17630
+ /**
17631
+ * Displays the notification for the built-in notification capability.
17632
+ *
17633
+ * This component is designed to be displayed in {@link MicrofrontendHostComponent}.
17634
+ */
17635
+ class NotificationTextMessageComponent {
17636
+ message;
17637
+ constructor() {
17638
+ const { params, referrer } = inject(ActivatedMicrofrontend);
17639
+ const translatable = params().get(eNOTIFICATION_MESSAGE_PARAM);
17640
+ this.message = createRemoteTranslatable(translatable, { appSymbolicName: referrer() });
17641
+ }
17642
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: NotificationTextMessageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
17643
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.1", type: NotificationTextMessageComponent, isStandalone: true, selector: "wb-notification-text-message", host: { properties: { "class.empty": "!message?.length" } }, ngImport: i0, template: "{{(message | wbText)()}}\n", styles: [":host{word-break:break-word;white-space:pre-line}:host.empty{display:none}\n"], dependencies: [{ kind: "pipe", type: TextPipe, name: "wbText" }] });
17644
+ }
17645
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: NotificationTextMessageComponent, decorators: [{
17646
+ type: Component,
17647
+ args: [{ selector: 'wb-notification-text-message', imports: [
17648
+ TextPipe,
17649
+ ], host: {
17650
+ '[class.empty]': '!message?.length',
17651
+ }, template: "{{(message | wbText)()}}\n", styles: [":host{word-break:break-word;white-space:pre-line}:host.empty{display:none}\n"] }]
17652
+ }], ctorParameters: () => [] });
17653
+ /**
17654
+ * Property to identify the built-in text notification capability.
17655
+ */
17656
+ const TEXT_NOTIFICATION_CAPABILITY_IDENTITY_PROPERTY = 'ɵidentity';
17657
+ /**
17658
+ * Value to identify the built-in text notification capability.
17659
+ */
17660
+ const TEXT_NOTIFICATION_CAPABILITY_IDENTITY = UUID.randomUUID();
17661
+
17662
+ var notificationTextMessage_component = /*#__PURE__*/Object.freeze({
17663
+ __proto__: null,
17664
+ TEXT_NOTIFICATION_CAPABILITY_IDENTITY: TEXT_NOTIFICATION_CAPABILITY_IDENTITY,
17665
+ TEXT_NOTIFICATION_CAPABILITY_IDENTITY_PROPERTY: TEXT_NOTIFICATION_CAPABILITY_IDENTITY_PROPERTY,
17666
+ default: NotificationTextMessageComponent
17667
+ });
17668
+
17669
+ /*
17670
+ * Copyright (c) 2018-2026 Swiss Federal Railways
17671
+ *
17672
+ * This program and the accompanying materials are made
17673
+ * available under the terms of the Eclipse Public License 2.0
17674
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
17675
+ *
17676
+ * SPDX-License-Identifier: EPL-2.0
17677
+ */
17678
+ /**
17679
+ * Intercepts the host manifest, registering the built-in text notification capability.
17680
+ */
17681
+ class MicrofrontendTextNotificationCapabilityProvider {
17682
+ intercept(hostManifest) {
17683
+ hostManifest.capabilities = [
17684
+ ...hostManifest.capabilities ?? [],
17685
+ provideBuiltInTextNotificationCapability(),
17686
+ ];
17687
+ }
17688
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MicrofrontendTextNotificationCapabilityProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
17689
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MicrofrontendTextNotificationCapabilityProvider });
17690
+ }
17691
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MicrofrontendTextNotificationCapabilityProvider, decorators: [{
17692
+ type: Injectable
17693
+ }] });
17694
+ /**
17695
+ * Provides the built-in notification capability to display text.
17696
+ *
17697
+ * @see MicrofrontendNotificationIntentHandler
17698
+ */
17699
+ function provideBuiltInTextNotificationCapability() {
17700
+ return {
17701
+ type: WorkbenchCapabilities.Notification,
17702
+ qualifier: {},
17703
+ params: [
17704
+ {
17705
+ name: eNOTIFICATION_MESSAGE_PARAM,
17706
+ required: false,
17707
+ description: 'Text to display in the notification.',
17708
+ },
17709
+ ],
17710
+ properties: {
17711
+ path: '',
17712
+ [TEXT_NOTIFICATION_CAPABILITY_IDENTITY_PROPERTY]: TEXT_NOTIFICATION_CAPABILITY_IDENTITY,
17713
+ },
17714
+ private: false,
17715
+ description: 'Displays a text notification.',
17716
+ };
17717
+ }
17718
+
17640
17719
  /**
17641
- * A notification is a closable message displayed in the upper-right corner that disappears after a few seconds unless hovered.
17642
- * It informs about system events, task completion or errors. The severity indicates importance or urgency.
17720
+ * A notification is a closable message displayed in the upper-right corner that disappears after a few seconds unless hovered or focused.
17721
+ * It informs about system events, task completion, or errors. Severity indicates importance or urgency.
17643
17722
  *
17644
17723
  * The notification component can inject this handle to interact with the notification.
17645
17724
  *
@@ -17763,13 +17842,18 @@ class ɵWorkbenchNotification {
17763
17842
  id;
17764
17843
  _options;
17765
17844
  /** Injector for the notification; destroyed when the notification is closed. */
17766
- _injector = inject(Injector);
17845
+ injector = inject(Injector);
17767
17846
  slot;
17768
17847
  _notificationRegistry = inject(WorkbenchNotificationRegistry);
17848
+ _focusMonitor = inject(WorkbenchFocusMonitor);
17769
17849
  _title;
17770
17850
  _severity;
17771
17851
  _duration;
17772
17852
  _cssClass;
17853
+ size = new ɵWorkbenchNotificationSize();
17854
+ focused = computed(() => this._focusMonitor.activeElement()?.id === this.id, { ...(ngDevMode ? { debugName: "focused" } : {}) });
17855
+ /** Checks if this notification is the most recently displayed notification. */
17856
+ top = computed(() => this._notificationRegistry.top() === this, { ...(ngDevMode ? { debugName: "top" } : {}) });
17773
17857
  destroyed = signal(false, { ...(ngDevMode ? { debugName: "destroyed" } : {}) });
17774
17858
  group;
17775
17859
  constructor(id, content, _options) {
@@ -17785,7 +17869,6 @@ class ɵWorkbenchNotification {
17785
17869
  this._duration = signal(this._options.duration ?? 'medium', { ...(ngDevMode ? { debugName: "_duration" } : {}) });
17786
17870
  this._cssClass = signal(Arrays.coerce(this._options.cssClass), { ...(ngDevMode ? { debugName: "_cssClass" } : {}) });
17787
17871
  this.group = this._options.group;
17788
- this.closeOnEscape();
17789
17872
  inject(DestroyRef).onDestroy(() => this.destroyed.set(true));
17790
17873
  }
17791
17874
  /**
@@ -17798,6 +17881,7 @@ class ɵWorkbenchNotification {
17798
17881
  { provide: ɵWorkbenchNotification, useValue: this },
17799
17882
  { provide: WorkbenchNotification, useExisting: ɵWorkbenchNotification },
17800
17883
  { provide: Notification, useClass: ɵNotification },
17884
+ { provide: WORKBENCH_ELEMENT, useExisting: ɵWorkbenchNotification },
17801
17885
  ...this._options.providers ?? [],
17802
17886
  ],
17803
17887
  });
@@ -17846,36 +17930,178 @@ class ɵWorkbenchNotification {
17846
17930
  get inputs() {
17847
17931
  return this._options.inputs;
17848
17932
  }
17849
- /**
17850
- * Closes the notification on escape keystroke, but only if this is the topmost notification.
17851
- */
17852
- closeOnEscape() {
17853
- const zone = inject(NgZone);
17854
- const document = inject(DOCUMENT);
17855
- const top = inject(WorkbenchNotificationRegistry).top;
17856
- effect(onCleanup => {
17857
- if (top() !== this) {
17858
- return;
17859
- }
17860
- const subscription = fromEvent(document, 'keydown')
17861
- .pipe(subscribeIn(fn => zone.runOutsideAngular(fn)), filter((event) => event.key === 'Escape'), observeIn(fn => zone.run(fn)))
17862
- .subscribe(() => this.close());
17863
- onCleanup(() => subscription.unsubscribe());
17864
- });
17865
- }
17866
17933
  /**
17867
17934
  * Destroys this dialog and associated resources.
17868
17935
  */
17869
17936
  destroy() {
17870
17937
  if (!this.destroyed()) {
17871
- this._injector.destroy();
17938
+ this.injector.destroy();
17872
17939
  this._notificationRegistry.unregister(this.id);
17873
17940
  }
17874
17941
  }
17875
17942
  }
17943
+ /** @inheritDoc */
17944
+ class ɵWorkbenchNotificationSize {
17945
+ _height = signal(undefined, { ...(ngDevMode ? { debugName: "_height" } : {}) });
17946
+ _minHeight = signal(undefined, { ...(ngDevMode ? { debugName: "_minHeight" } : {}) });
17947
+ _maxHeight = signal(undefined, { ...(ngDevMode ? { debugName: "_maxHeight" } : {}) });
17948
+ /** @inheritDoc */
17949
+ get height() {
17950
+ return this._height;
17951
+ }
17952
+ /** @inheritDoc */
17953
+ set height(height) {
17954
+ untracked(() => this._height.set(height));
17955
+ }
17956
+ /** @inheritDoc */
17957
+ get minHeight() {
17958
+ return this._minHeight;
17959
+ }
17960
+ /** @inheritDoc */
17961
+ set minHeight(minHeight) {
17962
+ untracked(() => this._minHeight.set(minHeight));
17963
+ }
17964
+ /** @inheritDoc */
17965
+ get maxHeight() {
17966
+ return this._maxHeight;
17967
+ }
17968
+ /** @inheritDoc */
17969
+ set maxHeight(maxHeight) {
17970
+ untracked(() => this._maxHeight.set(maxHeight));
17971
+ }
17972
+ }
17876
17973
 
17877
17974
  /*
17878
- * Copyright (c) 2018-2025 Swiss Federal Railways
17975
+ * Copyright (c) 2018-2026 Swiss Federal Railways
17976
+ *
17977
+ * This program and the accompanying materials are made
17978
+ * available under the terms of the Eclipse Public License 2.0
17979
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
17980
+ *
17981
+ * SPDX-License-Identifier: EPL-2.0
17982
+ */
17983
+ /**
17984
+ * Displays the microfrontend of a given {@link WorkbenchNotficationCapability}.
17985
+ *
17986
+ * This component is designed to be displayed in a workbench notification.
17987
+ */
17988
+ class MicrofrontendNotificationComponent {
17989
+ capability = input.required({ ...(ngDevMode ? { debugName: "capability" } : {}) });
17990
+ params = input.required({ ...(ngDevMode ? { debugName: "params" } : {}) });
17991
+ referrer = input.required({ ...(ngDevMode ? { debugName: "referrer" } : {}) });
17992
+ _host = inject(ElementRef).nativeElement;
17993
+ _outletRouter = inject(OutletRouter);
17994
+ _messageClient = inject(MessageClient);
17995
+ _logger = inject(Logger);
17996
+ _routerOutletElement = viewChild.required('router_outlet');
17997
+ /** Splash to display until the microfrontend signals readiness. */
17998
+ splash = inject(MicrofrontendPlatformConfig).splash ?? MicrofrontendSplashComponent;
17999
+ workbenchLayoutService = inject(WorkbenchLayoutService);
18000
+ notification = inject(ɵWorkbenchNotification);
18001
+ focusWithin = signal(false, { ...(ngDevMode ? { debugName: "focusWithin" } : {}) });
18002
+ constructor() {
18003
+ this._logger.debug(() => 'Constructing MicrofrontendNotificationComponent.', LoggerNames.MICROFRONTEND);
18004
+ this.installNavigator();
18005
+ this.installNotificationCloseListener();
18006
+ this.installNotificationFocusedPublisher();
18007
+ this.setNotificationProperties();
18008
+ this.propagateNotificationContext();
18009
+ this.propagateWorkbenchTheme();
18010
+ inject(DestroyRef).onDestroy(() => {
18011
+ // Clear the outlet.
18012
+ void this._outletRouter.navigate(null, { outlet: this.notification.id });
18013
+ // Delete retained messages to free resources.
18014
+ void this._messageClient.publish(_WorkbenchCommands.notificationFocusedTopic(this.notification.id), undefined, { retain: true });
18015
+ });
18016
+ }
18017
+ installNavigator() {
18018
+ const manifestService = inject(ManifestService);
18019
+ const injector = inject(Injector);
18020
+ effect(() => {
18021
+ const capability = this.capability();
18022
+ const params = this.params();
18023
+ void untracked(async () => {
18024
+ const application = manifestService.getApplication(capability.metadata.appSymbolicName);
18025
+ this._logger.debug(() => `Loading microfrontend into workbench notification [app=${capability.metadata.appSymbolicName}, baseUrl=${application.baseUrl}, path=${capability.properties.path}].`, LoggerNames.MICROFRONTEND, params, capability);
18026
+ // Wait for the context to be set on the router outlet, as @scion/workbench-client expects it to be available on startup.
18027
+ await Microfrontends.waitForContext(this._routerOutletElement, _NOTIFICATION_CONTEXT, { injector });
18028
+ void this._outletRouter.navigate(capability.properties.path, {
18029
+ outlet: this.notification.id,
18030
+ relativeTo: application.baseUrl,
18031
+ params: params,
18032
+ pushStateToSessionHistoryStack: false,
18033
+ showSplash: capability.properties.showSplash,
18034
+ });
18035
+ });
18036
+ });
18037
+ }
18038
+ installNotificationCloseListener() {
18039
+ this._messageClient.observe$(_WorkbenchCommands.notificationCloseTopic(this.notification.id))
18040
+ .pipe(takeUntilDestroyed())
18041
+ .subscribe(() => {
18042
+ this.notification.close();
18043
+ });
18044
+ }
18045
+ installNotificationFocusedPublisher() {
18046
+ effect(() => {
18047
+ const focused = this.notification.focused();
18048
+ untracked(() => {
18049
+ const commandTopic = _WorkbenchCommands.notificationFocusedTopic(this.notification.id);
18050
+ void this._messageClient.publish(commandTopic, focused, { retain: true });
18051
+ });
18052
+ });
18053
+ }
18054
+ setNotificationProperties() {
18055
+ effect(() => {
18056
+ const properties = this.capability().properties;
18057
+ untracked(() => {
18058
+ this.notification.size.height = properties.size?.height;
18059
+ this.notification.size.minHeight = properties.size?.minHeight;
18060
+ this.notification.size.maxHeight = properties.size?.maxHeight;
18061
+ });
18062
+ });
18063
+ }
18064
+ onFocusWithin(event) {
18065
+ const { detail: focusWithin } = event;
18066
+ this.focusWithin.set(focusWithin);
18067
+ if (focusWithin) {
18068
+ this._host.dispatchEvent(new CustomEvent('sci-microfrontend-focusin', { bubbles: true }));
18069
+ }
18070
+ }
18071
+ /**
18072
+ * Provides the notification context to embedded content.
18073
+ */
18074
+ propagateNotificationContext() {
18075
+ effect(() => {
18076
+ const context = {
18077
+ notificationId: this.notification.id,
18078
+ capability: this.capability(),
18079
+ params: this.params(),
18080
+ referrer: {
18081
+ appSymbolicName: this.referrer(),
18082
+ },
18083
+ };
18084
+ const routerOutletElement = this._routerOutletElement().nativeElement;
18085
+ untracked(() => routerOutletElement.setContextValue(_NOTIFICATION_CONTEXT, context));
18086
+ });
18087
+ }
18088
+ propagateWorkbenchTheme() {
18089
+ Microfrontends.propagateTheme(this._routerOutletElement);
18090
+ }
18091
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MicrofrontendNotificationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
18092
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.0.1", type: MicrofrontendNotificationComponent, isStandalone: true, selector: "wb-microfrontend-notification", inputs: { capability: { classPropertyName: "capability", publicName: "capability", isSignal: true, isRequired: true, transformFunction: null }, params: { classPropertyName: "params", publicName: "params", isSignal: true, isRequired: true, transformFunction: null }, referrer: { classPropertyName: "referrer", publicName: "referrer", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "class.workbench-drag": "workbenchLayoutService.dragging()" } }, viewQueries: [{ propertyName: "_routerOutletElement", first: true, predicate: ["router_outlet"], descendants: true, isSignal: true }], ngImport: i0, template: "<sci-router-outlet #router_outlet\n [name]=\"notification.id\"\n [attr.data-capabilityid]=\"capability().metadata!.id\"\n [attr.data-app]=\"capability().metadata!.appSymbolicName\"\n [attr.data-focus]=\"focusWithin() ? '' : null\"\n keystrokes=\"keydown.escape\"\n [class]=\"notification.cssClass()\"\n (focuswithin)=\"onFocusWithin($event)\">\n <ng-container *ngComponentOutlet=\"splash\"/>\n</sci-router-outlet>\n", styles: [":host{display:grid}:host.workbench-drag>sci-router-outlet{pointer-events:none}:host>sci-router-outlet::part(splash){display:grid}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }] });
18093
+ }
18094
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MicrofrontendNotificationComponent, decorators: [{
18095
+ type: Component,
18096
+ args: [{ selector: 'wb-microfrontend-notification', imports: [
18097
+ NgComponentOutlet,
18098
+ ], schemas: [CUSTOM_ELEMENTS_SCHEMA], host: {
18099
+ '[class.workbench-drag]': 'workbenchLayoutService.dragging()',
18100
+ }, template: "<sci-router-outlet #router_outlet\n [name]=\"notification.id\"\n [attr.data-capabilityid]=\"capability().metadata!.id\"\n [attr.data-app]=\"capability().metadata!.appSymbolicName\"\n [attr.data-focus]=\"focusWithin() ? '' : null\"\n keystrokes=\"keydown.escape\"\n [class]=\"notification.cssClass()\"\n (focuswithin)=\"onFocusWithin($event)\">\n <ng-container *ngComponentOutlet=\"splash\"/>\n</sci-router-outlet>\n", styles: [":host{display:grid}:host.workbench-drag>sci-router-outlet{pointer-events:none}:host>sci-router-outlet::part(splash){display:grid}\n"] }]
18101
+ }], ctorParameters: () => [], propDecorators: { capability: [{ type: i0.Input, args: [{ isSignal: true, alias: "capability", required: true }] }], params: [{ type: i0.Input, args: [{ isSignal: true, alias: "params", required: true }] }], referrer: [{ type: i0.Input, args: [{ isSignal: true, alias: "referrer", required: true }] }], _routerOutletElement: [{ type: i0.ViewChild, args: ['router_outlet', { isSignal: true }] }] } });
18102
+
18103
+ /*
18104
+ * Copyright (c) 2018-2025 Swiss Federal Railways
17879
18105
  *
17880
18106
  * This program and the accompanying materials are made
17881
18107
  * available under the terms of the Eclipse Public License 2.0
@@ -17887,6 +18113,7 @@ class ɵWorkbenchNotification {
17887
18113
  class ɵWorkbenchNotificationService {
17888
18114
  _rootInjector = inject(ApplicationRef).injector;
17889
18115
  _notificationRegistry = inject(WorkbenchNotificationRegistry);
18116
+ _mutex = new SingleTaskExecutor();
17890
18117
  _zone = inject(NgZone);
17891
18118
  /** @inheritDoc */
17892
18119
  show(message, options) {
@@ -17896,17 +18123,20 @@ class ɵWorkbenchNotificationService {
17896
18123
  this._zone.run(() => this.show(message, options));
17897
18124
  return;
17898
18125
  }
17899
- // Replace previous notification of the same group, or add it otherwise.
17900
- const previousNotification = options?.group ? this._notificationRegistry.elements().find(element => element.group === options.group) : undefined;
17901
- if (previousNotification) {
17902
- const reducedInputs = options?.groupInputReduceFn ? options.groupInputReduceFn(previousNotification.inputs ?? {}, options.inputs ?? {}) : options?.inputs;
17903
- const notification = this.createNotification(message, { ...options, inputs: reducedInputs });
17904
- this._notificationRegistry.replace(previousNotification.id, { key: notification.id, element: notification });
17905
- }
17906
- else {
17907
- const notification = this.createNotification(message, options);
17908
- this._notificationRegistry.register(notification.id, notification);
17909
- }
18126
+ // Prevent race conditions with asynchronous group reducer, ensuring to not have a "stale" previous notification.
18127
+ void this._mutex.submit(async () => {
18128
+ const previousNotification = options?.group ? this._notificationRegistry.elements().find(element => element.group === options.group) : undefined;
18129
+ // Replace previous notification of the same group, or add it otherwise.
18130
+ if (previousNotification) {
18131
+ const reducedInputs = options?.groupInputReduceFn ? await options.groupInputReduceFn(previousNotification.inputs ?? {}, options.inputs ?? {}) : options?.inputs;
18132
+ const notification = this.createNotification(message, { ...options, inputs: reducedInputs });
18133
+ this._notificationRegistry.replace(previousNotification.id, { key: notification.id, element: notification });
18134
+ }
18135
+ else {
18136
+ const notification = this.createNotification(message, options);
18137
+ this._notificationRegistry.register(notification.id, notification);
18138
+ }
18139
+ });
17910
18140
  }
17911
18141
  /**
17912
18142
  * Creates the notification handle.
@@ -17941,8 +18171,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
17941
18171
  /**
17942
18172
  * Shows a notification.
17943
18173
  *
17944
- * A notification is a closable message displayed in the upper-right corner that disappears after a few seconds unless hovered.
17945
- * It informs about system events, task completion or errors. The severity indicates importance or urgency.
18174
+ * A notification is a closable message displayed in the upper-right corner that disappears after a few seconds unless hovered or focused.
18175
+ * It informs about system events, task completion, or errors. Severity indicates importance or urgency.
17946
18176
  *
17947
18177
  * Notifications can be grouped. Only the most recent notification within a group is displayed.
17948
18178
  *
@@ -17958,7 +18188,37 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
17958
18188
  }] });
17959
18189
 
17960
18190
  /*
17961
- * Copyright (c) 2018-2022 Swiss Federal Railways
18191
+ * Copyright (c) 2018-2026 Swiss Federal Railways
18192
+ *
18193
+ * This program and the accompanying materials are made
18194
+ * available under the terms of the Eclipse Public License 2.0
18195
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
18196
+ *
18197
+ * SPDX-License-Identifier: EPL-2.0
18198
+ */
18199
+ /** @inheritDoc */
18200
+ class MicrofrontendHostNotification {
18201
+ _notification;
18202
+ capability;
18203
+ params;
18204
+ referrer;
18205
+ constructor(_notification, capability, params, referrer) {
18206
+ this._notification = _notification;
18207
+ this.capability = signal(capability).asReadonly();
18208
+ this.params = signal(params).asReadonly();
18209
+ this.referrer = signal(referrer).asReadonly();
18210
+ this.setNotificationProperties();
18211
+ }
18212
+ setNotificationProperties() {
18213
+ const properties = this.capability().properties;
18214
+ this._notification.size.height = properties.size?.height;
18215
+ this._notification.size.minHeight = properties.size?.minHeight;
18216
+ this._notification.size.maxHeight = properties.size?.maxHeight;
18217
+ }
18218
+ }
18219
+
18220
+ /*
18221
+ * Copyright (c) 2018-2026 Swiss Federal Railways
17962
18222
  *
17963
18223
  * This program and the accompanying materials are made
17964
18224
  * available under the terms of the Eclipse Public License 2.0
@@ -17967,77 +18227,196 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
17967
18227
  * SPDX-License-Identifier: EPL-2.0
17968
18228
  */
17969
18229
  /**
17970
- * Installs an intent handler to show text notifications.
18230
+ * Handles notification intents, opening a notification based on resolved capability.
18231
+ *
18232
+ * Microfrontends of the host are displayed in {@link MicrofrontendHostComponent}, microfrontends of other applications in {@link MicrofrontendNotificationComponent}.
18233
+ *
18234
+ * Notification intents are handled in this interceptor and are not transported to the providing application to support applications not connected to the SCION Workbench.
17971
18235
  */
17972
- function installNotificationIntentHandler() {
17973
- const intentClient = inject(IntentClient);
17974
- const notificationService = inject(WorkbenchNotificationService);
17975
- const logger = inject(Logger);
17976
- intentClient.onIntent({ type: WorkbenchCapabilities.Notification, qualifier: {} }, request => {
17977
- const command = request.body;
17978
- const referrer = request.headers.get(MessageHeaders.AppSymbolicName);
17979
- const isLegacyApi = !request.intent.params?.has(eNOTIFICATION_MESSAGE_PARAM);
17980
- const message = (isLegacyApi ? command.content : request.intent.params?.get(eNOTIFICATION_MESSAGE_PARAM)) ?? '';
17981
- logger.debug(() => 'Showing notification', LoggerNames.MICROFRONTEND, command);
17982
- notificationService.show(createRemoteTranslatable(message, { appSymbolicName: referrer }), prune({
18236
+ class MicrofrontendNotificationIntentHandler {
18237
+ _notificationService = inject(WorkbenchNotificationService);
18238
+ _logger = inject(Logger);
18239
+ /**
18240
+ * Notification intents are handled in this interceptor and then swallowed.
18241
+ */
18242
+ async intercept(intentMessage, next) {
18243
+ if (intentMessage.intent.type === WorkbenchCapabilities.Notification) {
18244
+ const replyTo = intentMessage.headers.get(MessageHeaders.ReplyTo);
18245
+ await this.showNotification(intentMessage);
18246
+ void Beans.get(MessageClient).publish(replyTo, undefined, { headers: new Map().set(MessageHeaders.Status, ResponseStatusCodes.TERMINAL) });
18247
+ // Do not continue the handler chain to swallow the intent.
18248
+ }
18249
+ else {
18250
+ return next.handle(intentMessage);
18251
+ }
18252
+ }
18253
+ /**
18254
+ * Displays the microfrontend declared by the resolved capability in a notification.
18255
+ */
18256
+ async showNotification(intentMessage) {
18257
+ const command = intentMessage.body;
18258
+ const capability = intentMessage.capability;
18259
+ const capabilityId = capability.metadata.id;
18260
+ const groupParamsReducer = capability.properties.groupParamsReducer;
18261
+ const params = intentMessage.intent.params ?? new Map();
18262
+ const referrer = intentMessage.headers.get(MessageHeaders.AppSymbolicName);
18263
+ const isHostProvider = Microfrontends.isHostProvider(capability);
18264
+ const legacyTextNotification = isLegacyTextNotification(capability, intentMessage);
18265
+ if (legacyTextNotification) {
18266
+ params.set(eNOTIFICATION_MESSAGE_PARAM, command.content);
18267
+ }
18268
+ this._logger.debug(() => 'Handling microfrontend notification intent', LoggerNames.MICROFRONTEND, command);
18269
+ this._notificationService.show(isHostProvider ? MicrofrontendHostComponent : MicrofrontendNotificationComponent, prune({
18270
+ inputs: isHostProvider ? { params } : { capability, params, referrer },
18271
+ providers: isHostProvider ? [provideActivatedMicrofrontend$3(capability, params, referrer)] : undefined,
17983
18272
  title: createRemoteTranslatable(command.title, { appSymbolicName: referrer }),
17984
18273
  severity: command.severity,
17985
- duration: isLegacyApi && typeof command.duration === 'number' ? command.duration * 1000 : command.duration,
17986
- group: command.group,
17987
- cssClass: command.cssClass,
18274
+ duration: legacyTextNotification && typeof command.duration === 'number' ? command.duration * 1000 : command.duration,
18275
+ group: command.group ? `${command.group}_${capabilityId}_${referrer}` : undefined, // Use distinct group name for different referrers and capabilities not to overwrite each other
18276
+ groupInputReduceFn: groupParamsReducer ? createParamsReducerFn(groupParamsReducer) : undefined,
18277
+ cssClass: Arrays.coerce(capability.properties.cssClass).concat(Arrays.coerce(command.cssClass)),
17988
18278
  }));
17989
- });
18279
+ }
18280
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MicrofrontendNotificationIntentHandler, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
18281
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MicrofrontendNotificationIntentHandler });
17990
18282
  }
18283
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MicrofrontendNotificationIntentHandler, decorators: [{
18284
+ type: Injectable
18285
+ }] });
17991
18286
  /**
17992
- * Provides a set of DI providers installing an intent handler to show text notifications.
18287
+ * Returns a function to reduce inputs by sending a request to passed topic.
18288
+ */
18289
+ function createParamsReducerFn(reducerTopic) {
18290
+ return async (prevInput, currInput) => {
18291
+ const currParams = Object.fromEntries(currInput['params']);
18292
+ const prevParams = Object.fromEntries(prevInput['params']);
18293
+ const reducedParams = await firstValueFrom(Beans.get(MessageClient).request$(reducerTopic, { prevParams, currParams }, { retain: true }));
18294
+ return { ...currInput, params: new Map(Object.entries(reducedParams.body ?? currParams)) };
18295
+ };
18296
+ }
18297
+ /**
18298
+ * Indicates whether the notification is opened by a legacy client.
17993
18299
  *
17994
- * The notification capability is registered in {@link WorkbenchHostManifestInterceptor}.
18300
+ * TODO [Angular 22] Remove with Angular 22. Used for backward compatiblity.
17995
18301
  */
17996
- function provideNotificationIntentHandler() {
18302
+ function isLegacyTextNotification(capability, intentMessage) {
18303
+ const isTextMessage = capability.properties[TEXT_NOTIFICATION_CAPABILITY_IDENTITY_PROPERTY] === TEXT_NOTIFICATION_CAPABILITY_IDENTITY;
18304
+ return isTextMessage && !intentMessage.intent.params?.has(eNOTIFICATION_MESSAGE_PARAM);
18305
+ }
18306
+ /**
18307
+ * Provides {@link ActivatedMicrofrontend} for injection in the host microfrontend.
18308
+ */
18309
+ function provideActivatedMicrofrontend$3(capability, params, referrer) {
18310
+ return {
18311
+ provide: ActivatedMicrofrontend,
18312
+ useFactory: () => {
18313
+ const notification = inject(ɵWorkbenchNotification);
18314
+ const reducedParams = notification.inputs?.['params'];
18315
+ // Create in notification's injection context to bind 'MicrofrontendNotification' to the notification's lifecycle.
18316
+ return runInInjectionContext(notification.injector, () => new MicrofrontendHostNotification(notification, capability, reducedParams ?? params, referrer));
18317
+ },
18318
+ };
18319
+ }
18320
+
18321
+ /*
18322
+ * Copyright (c) 2018-2026 Swiss Federal Railways
18323
+ *
18324
+ * This program and the accompanying materials are made
18325
+ * available under the terms of the Eclipse Public License 2.0
18326
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
18327
+ *
18328
+ * SPDX-License-Identifier: EPL-2.0
18329
+ */
18330
+ /**
18331
+ * Provides the route for the built-in {@link WorkbenchNotificationCapability}.
18332
+ */
18333
+ function provideMicrofrontendTextNotificationRoute() {
17997
18334
  return makeEnvironmentProviders([
17998
- provideMicrofrontendPlatformInitializer(() => installNotificationIntentHandler()),
18335
+ {
18336
+ provide: WORKBENCH_ROUTE,
18337
+ useFactory: () => ({
18338
+ path: '',
18339
+ loadComponent: () => Promise.resolve().then(function () { return notificationTextMessage_component; }),
18340
+ canMatch: [canMatchWorkbenchNotificationCapability({})],
18341
+ }),
18342
+ multi: true,
18343
+ },
17999
18344
  ]);
18000
18345
  }
18001
18346
 
18002
18347
  /**
18003
- * Intercepts the host manifest, registering the built-in text notification capability.
18348
+ * Asserts notification capabilities to have required properties.
18004
18349
  */
18005
- class MicrofrontendTextNotificationCapabilityProvider {
18006
- intercept(hostManifest) {
18007
- hostManifest.capabilities = [
18008
- ...hostManifest.capabilities ?? [],
18009
- provideBuiltInTextNotificationCapability(),
18010
- ];
18350
+ class MicrofrontendNotificationCapabilityValidator {
18351
+ async intercept(capability) {
18352
+ if (capability.type !== WorkbenchCapabilities.Notification) {
18353
+ return capability;
18354
+ }
18355
+ const notificationCapability = capability;
18356
+ // Assert capability other than the built-in text notification capability to have a qualifier.
18357
+ if (!isBuiltinNotificationCapability(notificationCapability) && !Object.keys(notificationCapability.qualifier ?? {}).length) {
18358
+ throw Error(`[NotificationDefinitionError] Notification capability requires a qualifier [app=${app$3(notificationCapability)}, notification=${qualifier$3(notificationCapability)}]`);
18359
+ }
18360
+ // Assert capability to have properties.
18361
+ if (!notificationCapability.properties) {
18362
+ throw Error(`[NotificationDefinitionError] Notification capability requires properties [app=${app$3(notificationCapability)}, notification=${qualifier$3(notificationCapability)}]`);
18363
+ }
18364
+ // Assert the notification capability to have a path, unless provided by the host application.
18365
+ this.assertPath(notificationCapability);
18366
+ // Assert the notification capability to have a height, unless provided by the host application.
18367
+ this.assertSize(notificationCapability);
18368
+ // Assert host notification capabilities not to define the "showSplash" property.
18369
+ if (Microfrontends.isHostProvider(capability) && notificationCapability.properties.showSplash !== undefined) {
18370
+ throw Error(`[NotificationDefinitionError] Property "showSplash" not supported for notification capabilities of the host application [app=${app$3(notificationCapability)}, notification=${qualifier$3(notificationCapability)}]`);
18371
+ }
18372
+ return capability;
18011
18373
  }
18012
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MicrofrontendTextNotificationCapabilityProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
18013
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MicrofrontendTextNotificationCapabilityProvider });
18374
+ assertPath(capability) {
18375
+ const path = capability.properties?.path;
18376
+ if (Microfrontends.isHostProvider(capability)) {
18377
+ if (path !== '') {
18378
+ throw Error(`[NotificationDefinitionError] Notification capabilities of the host application require an empty path. [app=${app$3(capability)}, notification=${qualifier$3(capability)}]. Change the path '${path}' to empty and add 'canMatchWorkbenchNotificationCapability(${JSON.stringify(capability.qualifier)})' guard to the route.\n\nExample:\nCapability: { type: 'notification', qualifier: ${JSON.stringify(capability.qualifier)}, properties: {path: ''} }\nRoute: { path: '', canMatch: [canMatchWorkbenchNotificationCapability(${JSON.stringify(capability.qualifier)})], component: NotificationComponent }`);
18379
+ }
18380
+ }
18381
+ else {
18382
+ if (path === null || path == undefined) {
18383
+ throw Error(`[NotificationDefinitionError] Notification capabilities require a path. [app=${app$3(capability)}, notification=${qualifier$3(capability)}]`);
18384
+ }
18385
+ }
18386
+ }
18387
+ assertSize(capability) {
18388
+ if (Microfrontends.isHostProvider(capability)) {
18389
+ return;
18390
+ }
18391
+ const size = capability.properties?.size;
18392
+ if (!size?.height) {
18393
+ throw Error(`[NotificationDefinitionError] Notification capability requires the 'size' property with a height [app=${app$3(capability)}, notification=${qualifier$3(capability)}]`);
18394
+ }
18395
+ }
18396
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MicrofrontendNotificationCapabilityValidator, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
18397
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MicrofrontendNotificationCapabilityValidator });
18014
18398
  }
18015
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MicrofrontendTextNotificationCapabilityProvider, decorators: [{
18399
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MicrofrontendNotificationCapabilityValidator, decorators: [{
18016
18400
  type: Injectable
18017
18401
  }] });
18402
+ function isBuiltinNotificationCapability(capability) {
18403
+ return capability.properties?.[TEXT_NOTIFICATION_CAPABILITY_IDENTITY_PROPERTY] === TEXT_NOTIFICATION_CAPABILITY_IDENTITY;
18404
+ }
18018
18405
  /**
18019
- * Provides the built-in notification capability to display text.
18020
- *
18021
- * @see MicrofrontendNotificationIntentHandler
18406
+ * Returns the qualifier as string.
18022
18407
  */
18023
- function provideBuiltInTextNotificationCapability() {
18024
- return {
18025
- type: WorkbenchCapabilities.Notification,
18026
- qualifier: {},
18027
- params: [
18028
- {
18029
- name: eNOTIFICATION_MESSAGE_PARAM,
18030
- required: false,
18031
- description: 'Text to display in the notification.',
18032
- },
18033
- ],
18034
- private: false,
18035
- description: 'Displays a text notification.',
18036
- };
18408
+ function qualifier$3(capability) {
18409
+ return Objects.toMatrixNotation(capability.qualifier);
18410
+ }
18411
+ /**
18412
+ * Returns the app symbolic name.
18413
+ */
18414
+ function app$3(capability) {
18415
+ return capability.metadata.appSymbolicName;
18037
18416
  }
18038
18417
 
18039
18418
  /*
18040
- * Copyright (c) 2018-2025 Swiss Federal Railways
18419
+ * Copyright (c) 2018-2026 Swiss Federal Railways
18041
18420
  *
18042
18421
  * This program and the accompanying materials are made
18043
18422
  * available under the terms of the Eclipse Public License 2.0
@@ -18052,13 +18431,19 @@ function provideBuiltInTextNotificationCapability() {
18052
18431
  */
18053
18432
  function provideMicrofrontendNotification() {
18054
18433
  return makeEnvironmentProviders([
18434
+ MicrofrontendNotificationCapabilityValidator,
18055
18435
  MicrofrontendTextNotificationCapabilityProvider,
18056
- provideNotificationIntentHandler(),
18436
+ MicrofrontendNotificationIntentHandler,
18437
+ provideMicrofrontendTextNotificationRoute(),
18057
18438
  provideMicrofrontendPlatformInitializer(onPreStartup, { phase: MicrofrontendPlatformStartupPhase.PreStartup }),
18058
18439
  ]);
18059
18440
  function onPreStartup() {
18060
18441
  // Register built-in text notification capability in the host manifest.
18061
18442
  Beans.register(HostManifestInterceptor, { useValue: inject(MicrofrontendTextNotificationCapabilityProvider), multi: true });
18443
+ // Register notification capability validator.
18444
+ Beans.register(CapabilityInterceptor, { useValue: inject(MicrofrontendNotificationCapabilityValidator), multi: true });
18445
+ // Register notification intent handler.
18446
+ Beans.register(IntentInterceptor, { useValue: inject(MicrofrontendNotificationIntentHandler), multi: true });
18062
18447
  }
18063
18448
  }
18064
18449
 
@@ -18707,15 +19092,19 @@ class TextMessageComponent {
18707
19092
  const { params, referrer } = inject(ActivatedMicrofrontend);
18708
19093
  const translatable = params().get(eMESSAGE_BOX_MESSAGE_PARAM);
18709
19094
  this.message = createRemoteTranslatable(translatable, { appSymbolicName: referrer() });
19095
+ // Limit the maximum messagebox width to break the message.
19096
+ inject(WorkbenchDialog).size.maxWidth = 'var(--sci-workbench-messagebox-max-width)';
18710
19097
  }
18711
19098
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: TextMessageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
18712
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: TextMessageComponent, isStandalone: true, selector: "wb-text-message", ngImport: i0, template: "@if (message) {\n {{(message | wbText)()}}\n}\n", styles: [":host{overflow-wrap:break-word;white-space:pre-line;text-align:var(--sci-workbench-messagebox-text-align)}\n"], dependencies: [{ kind: "pipe", type: TextPipe, name: "wbText" }] });
19099
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.1", type: TextMessageComponent, isStandalone: true, selector: "wb-text-message", host: { properties: { "class.empty": "!message?.length" } }, ngImport: i0, template: "{{(message | wbText)()}}\n", styles: [":host{word-break:break-word;white-space:pre-line;text-align:var(--sci-workbench-messagebox-text-align)}:host.empty{display:none}\n"], dependencies: [{ kind: "pipe", type: TextPipe, name: "wbText" }] });
18713
19100
  }
18714
19101
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: TextMessageComponent, decorators: [{
18715
19102
  type: Component,
18716
19103
  args: [{ selector: 'wb-text-message', imports: [
18717
19104
  TextPipe,
18718
- ], template: "@if (message) {\n {{(message | wbText)()}}\n}\n", styles: [":host{overflow-wrap:break-word;white-space:pre-line;text-align:var(--sci-workbench-messagebox-text-align)}\n"] }]
19105
+ ], host: {
19106
+ '[class.empty]': '!message?.length',
19107
+ }, template: "{{(message | wbText)()}}\n", styles: [":host{word-break:break-word;white-space:pre-line;text-align:var(--sci-workbench-messagebox-text-align)}:host.empty{display:none}\n"] }]
18719
19108
  }], ctorParameters: () => [] });
18720
19109
  /**
18721
19110
  * Property to identify the built-in text message box capability.
@@ -20022,7 +20411,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
20022
20411
  }] });
20023
20412
 
20024
20413
  /*
20025
- * Copyright (c) 2018-2025 Swiss Federal Railways
20414
+ * Copyright (c) 2018-2026 Swiss Federal Railways
20026
20415
  *
20027
20416
  * This program and the accompanying materials are made
20028
20417
  * available under the terms of the Eclipse Public License 2.0
@@ -20038,23 +20427,50 @@ class WorkbenchNotificationComponent {
20038
20427
  hover = signal(false, { ...(ngDevMode ? { debugName: "hover" } : {}) });
20039
20428
  constructor() {
20040
20429
  this.installAutoCloseTimer();
20430
+ this.installFocusTracker();
20431
+ this.closeOnEscapeIfOnTop();
20041
20432
  }
20042
- onMousedown(event) {
20043
- if (event.buttons === AUXILARY_MOUSE_BUTTON) {
20433
+ onClose() {
20434
+ this.notification().close();
20435
+ }
20436
+ onEscape(event) {
20437
+ if (this.notification().focused()) {
20438
+ event.stopPropagation(); // stop propagation to prevent closing the most recently displayed notification
20044
20439
  this.notification().close();
20045
20440
  }
20046
20441
  }
20047
- onClose() {
20048
- this.notification().close();
20442
+ onAuxClick(event) {
20443
+ if (event.button === 1) { // primary aux button
20444
+ event.preventDefault(); // prevent user-agent default action
20445
+ this.notification().close();
20446
+ }
20447
+ }
20448
+ /**
20449
+ * Closes this notification when pressing escape if it is the most recently displayed notification.
20450
+ */
20451
+ closeOnEscapeIfOnTop() {
20452
+ const zone = inject(NgZone);
20453
+ const document = inject(DOCUMENT);
20454
+ effect(onCleanup => {
20455
+ if (!this.notification().top()) {
20456
+ return;
20457
+ }
20458
+ const subscription = fromEvent(document, 'keydown')
20459
+ .pipe(subscribeIn(fn => zone.runOutsideAngular(fn)), filter((event) => event.key === 'Escape'), observeIn(fn => zone.run(fn)))
20460
+ .subscribe(() => this.notification().close());
20461
+ onCleanup(() => subscription.unsubscribe());
20462
+ });
20049
20463
  }
20050
20464
  /**
20051
20465
  * Installs a timer to close the notification.
20052
20466
  */
20053
20467
  installAutoCloseTimer() {
20054
20468
  effect(onCleanup => {
20055
- const duration = this.notification().duration();
20469
+ const notification = this.notification();
20470
+ const duration = notification.duration();
20471
+ const focus = notification.focused();
20056
20472
  const hover = this.hover();
20057
- if (hover) {
20473
+ if (hover || focus) {
20058
20474
  return;
20059
20475
  }
20060
20476
  untracked(() => {
@@ -20078,8 +20494,19 @@ class WorkbenchNotificationComponent {
20078
20494
  }
20079
20495
  }
20080
20496
  }
20497
+ installFocusTracker() {
20498
+ const host = inject(ElementRef).nativeElement;
20499
+ const injector = inject(Injector);
20500
+ effect(onCleanup => {
20501
+ const notification = this.notification();
20502
+ untracked(() => {
20503
+ const tracker = runInInjectionContext(injector, () => trackFocus(host, notification));
20504
+ onCleanup(() => tracker.destroy());
20505
+ });
20506
+ });
20507
+ }
20081
20508
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchNotificationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
20082
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: WorkbenchNotificationComponent, isStandalone: true, selector: "wb-notification", inputs: { notification: { classPropertyName: "notification", publicName: "notification", isSignal: true, isRequired: true, transformFunction: null } }, host: { listeners: { "mouseenter": "hover.set(true)", "mouseleave": "hover.set(false)", "mousedown": "onMousedown($event)" }, properties: { "attr.data-notificationid": "notification().id", "attr.data-severity": "notification().severity()", "class": "notification().cssClass()" } }, ngImport: i0, template: "<div class=\"outline\">\n <!-- Title -->\n @if (notification().title(); as title) {\n <header class=\"e2e-title\">{{(title | wbText)()}}</header>\n }\n\n <!-- Message -->\n @if (notification().slot.text) {\n <span class=\"e2e-message\">{{(notification().slot.text | wbText)()}}</span>\n } @else {\n <ng-container *ngComponentOutlet=\"notification().slot.component!; inputs: notification().inputs | wbRemoveLegacyInput; injector: notification().slot.injector\"/>\n }\n</div>\n<button (click)=\"onClose()\"\n [title]=\"('%workbench.close.tooltip' | wbText)()\"\n class=\"close e2e-close\">\n <wb-icon icon=\"workbench.close\"/>\n</button>\n", styles: [":host{display:grid;background-color:var(--sci-color-background-elevation);color:var(--sci-color-text);font-size:1rem;border-radius:var(--sci-corner);box-shadow:var(--sci-elevation) var(--sci-static-color-black);border-left:var(--sci-workbench-notification-severity-indicator-size) solid transparent;position:relative}:host[data-severity=info]{border-left-color:var(--sci-color-accent)}:host[data-severity=warn]{border-left-color:var(--sci-color-notice)}:host[data-severity=error]{border-left-color:var(--sci-color-negative)}:host>div.outline{border:1px solid var(--sci-color-border);border-left:none;padding:1em 1.5em;border-top-right-radius:var(--sci-corner);border-bottom-right-radius:var(--sci-corner);font-size:.9em;white-space:pre-line;overflow-wrap:break-word;overflow:hidden}:host>div.outline>header{font-weight:700;margin-bottom:.75em}:host>button.close:is(button,#sci-reset){all:unset;display:inline-grid;place-content:center;place-items:center;padding:.25em;border-radius:var(--sci-corner);-webkit-user-select:none;user-select:none;overflow:hidden;cursor:var(--sci-workbench-button-cursor)}:host>button.close:is(button,#sci-reset):hover:where(:not(:disabled)){background-color:var(--sci-workbench-button-background-color-hover)}:host>button.close:is(button,#sci-reset):active:where(:not(:disabled)){background-color:var(--sci-workbench-button-background-color-active)}:host>button.close:is(button,#sci-reset):focus:not(:focus-visible){outline:none}:host>button.close:is(button,#sci-reset):focus-visible{outline:var(--sci-workbench-button-outline-width-focus) solid var(--sci-color-accent)}:host>button.close:is(button,#sci-reset):disabled{color:var(--sci-color-gray-500)}:host>button.close:is(button,#sci-reset){position:absolute;top:.275em;right:.275em;padding:.125em;border-radius:var(--sci-corner-small)}\n"], dependencies: [{ kind: "component", type: IconComponent, selector: "wb-icon", inputs: ["icon"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "pipe", type: TextPipe, name: "wbText" }, { kind: "pipe", type: RemoveLegacyInputPipe, name: "wbRemoveLegacyInput" }] });
20509
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: WorkbenchNotificationComponent, isStandalone: true, selector: "wb-notification", inputs: { notification: { classPropertyName: "notification", publicName: "notification", isSignal: true, isRequired: true, transformFunction: null } }, host: { listeners: { "mouseenter": "hover.set(true)", "mouseleave": "hover.set(false)", "auxclick": "onAuxClick($event)", "keydown.escape": "onEscape($event)" }, properties: { "attr.data-notificationid": "notification().id", "attr.data-severity": "notification().severity()", "style.min-height": "notification().size.minHeight()", "style.height": "notification().size.height()", "style.max-height": "notification().size.maxHeight()", "attr.tabindex": "-1", "class": "notification().cssClass()" } }, ngImport: i0, template: "<!-- Title -->\n@if (notification().title(); as title) {\n <header class=\"e2e-title\">{{(title | wbText)()}}</header>\n}\n\n<!-- Message -->\n<div class=\"message e2e-message\" [class.text]=\"!!notification().slot.text?.length\">\n @if (notification().slot.text?.length) {\n {{(notification().slot.text | wbText)()}}\n } @else if (notification().slot.component) {\n <sci-viewport class=\"e2e-message-viewport\">\n <ng-container *ngComponentOutlet=\"notification().slot.component!; inputs: notification().inputs | wbRemoveLegacyInput; injector: notification().slot.injector\"/>\n </sci-viewport>\n }\n</div>\n\n<button (click)=\"onClose()\"\n [title]=\"('%workbench.close.tooltip' | wbText)()\"\n class=\"close e2e-close\">\n <wb-icon icon=\"workbench.close\"/>\n</button>\n", styles: [":host{display:flex;flex-direction:column;gap:.75em;background-color:var(--sci-color-background-elevation);color:var(--sci-color-text);font-size:.9em;border:1px solid var(--sci-color-border);border-radius:var(--sci-corner);box-shadow:var(--sci-elevation) var(--sci-static-color-black);padding:1em 0;overflow:hidden;outline:none;position:relative}:host:before{content:\"\";position:absolute;top:0;left:0;bottom:0;width:var(--sci-workbench-notification-severity-indicator-size)}:host[data-severity=info]:before{background-color:var(--sci-color-accent)}:host[data-severity=warn]:before{background-color:var(--sci-color-notice)}:host[data-severity=error]:before{background-color:var(--sci-color-negative)}:host>header{flex:none;font-weight:700;padding:0 1.5em;word-break:break-word;white-space:pre-line}:host>div.message{flex:auto;overflow:hidden}:host>div.message.text{word-break:break-word;white-space:pre-line;padding:0 1.5em}:host>div.message>sci-viewport{height:100%}:host>div.message>sci-viewport::part(content){padding:0 1.5em}:host>button.close:is(button,#sci-reset){all:unset;display:inline-grid;place-content:center;place-items:center;padding:.25em;border-radius:var(--sci-corner);-webkit-user-select:none;user-select:none;overflow:hidden;cursor:var(--sci-workbench-button-cursor)}:host>button.close:is(button,#sci-reset):hover:where(:not(:disabled)){background-color:var(--sci-workbench-button-background-color-hover)}:host>button.close:is(button,#sci-reset):active:where(:not(:disabled)){background-color:var(--sci-workbench-button-background-color-active)}:host>button.close:is(button,#sci-reset):focus:not(:focus-visible){outline:none}:host>button.close:is(button,#sci-reset):focus-visible{outline:var(--sci-workbench-button-outline-width-focus) solid var(--sci-color-accent)}:host>button.close:is(button,#sci-reset):disabled{color:var(--sci-color-gray-500)}:host>button.close:is(button,#sci-reset){position:absolute;top:.275em;right:.275em;padding:.125em;border-radius:var(--sci-corner-small);font-size:1rem}\n"], dependencies: [{ kind: "component", type: IconComponent, selector: "wb-icon", inputs: ["icon"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "component", type: SciViewportComponent, selector: "sci-viewport", inputs: ["scrollbarStyle"], outputs: ["scroll"] }, { kind: "pipe", type: TextPipe, name: "wbText" }, { kind: "pipe", type: RemoveLegacyInputPipe, name: "wbRemoveLegacyInput" }] });
20083
20510
  }
20084
20511
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchNotificationComponent, decorators: [{
20085
20512
  type: Component,
@@ -20088,19 +20515,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
20088
20515
  IconComponent,
20089
20516
  NgComponentOutlet,
20090
20517
  RemoveLegacyInputPipe,
20518
+ SciViewportComponent,
20091
20519
  ], host: {
20092
20520
  '[attr.data-notificationid]': 'notification().id',
20093
20521
  '[attr.data-severity]': 'notification().severity()',
20522
+ '[style.min-height]': 'notification().size.minHeight()',
20523
+ '[style.height]': 'notification().size.height()',
20524
+ '[style.max-height]': 'notification().size.maxHeight()',
20525
+ '[attr.tabindex]': '-1',
20526
+ '[class]': 'notification().cssClass()',
20094
20527
  '(mouseenter)': 'hover.set(true)',
20095
20528
  '(mouseleave)': 'hover.set(false)',
20096
- '(mousedown)': 'onMousedown($event)',
20097
- '[class]': 'notification().cssClass()',
20098
- }, template: "<div class=\"outline\">\n <!-- Title -->\n @if (notification().title(); as title) {\n <header class=\"e2e-title\">{{(title | wbText)()}}</header>\n }\n\n <!-- Message -->\n @if (notification().slot.text) {\n <span class=\"e2e-message\">{{(notification().slot.text | wbText)()}}</span>\n } @else {\n <ng-container *ngComponentOutlet=\"notification().slot.component!; inputs: notification().inputs | wbRemoveLegacyInput; injector: notification().slot.injector\"/>\n }\n</div>\n<button (click)=\"onClose()\"\n [title]=\"('%workbench.close.tooltip' | wbText)()\"\n class=\"close e2e-close\">\n <wb-icon icon=\"workbench.close\"/>\n</button>\n", styles: [":host{display:grid;background-color:var(--sci-color-background-elevation);color:var(--sci-color-text);font-size:1rem;border-radius:var(--sci-corner);box-shadow:var(--sci-elevation) var(--sci-static-color-black);border-left:var(--sci-workbench-notification-severity-indicator-size) solid transparent;position:relative}:host[data-severity=info]{border-left-color:var(--sci-color-accent)}:host[data-severity=warn]{border-left-color:var(--sci-color-notice)}:host[data-severity=error]{border-left-color:var(--sci-color-negative)}:host>div.outline{border:1px solid var(--sci-color-border);border-left:none;padding:1em 1.5em;border-top-right-radius:var(--sci-corner);border-bottom-right-radius:var(--sci-corner);font-size:.9em;white-space:pre-line;overflow-wrap:break-word;overflow:hidden}:host>div.outline>header{font-weight:700;margin-bottom:.75em}:host>button.close:is(button,#sci-reset){all:unset;display:inline-grid;place-content:center;place-items:center;padding:.25em;border-radius:var(--sci-corner);-webkit-user-select:none;user-select:none;overflow:hidden;cursor:var(--sci-workbench-button-cursor)}:host>button.close:is(button,#sci-reset):hover:where(:not(:disabled)){background-color:var(--sci-workbench-button-background-color-hover)}:host>button.close:is(button,#sci-reset):active:where(:not(:disabled)){background-color:var(--sci-workbench-button-background-color-active)}:host>button.close:is(button,#sci-reset):focus:not(:focus-visible){outline:none}:host>button.close:is(button,#sci-reset):focus-visible{outline:var(--sci-workbench-button-outline-width-focus) solid var(--sci-color-accent)}:host>button.close:is(button,#sci-reset):disabled{color:var(--sci-color-gray-500)}:host>button.close:is(button,#sci-reset){position:absolute;top:.275em;right:.275em;padding:.125em;border-radius:var(--sci-corner-small)}\n"] }]
20529
+ '(auxclick)': 'onAuxClick($event)',
20530
+ '(keydown.escape)': 'onEscape($event)',
20531
+ }, template: "<!-- Title -->\n@if (notification().title(); as title) {\n <header class=\"e2e-title\">{{(title | wbText)()}}</header>\n}\n\n<!-- Message -->\n<div class=\"message e2e-message\" [class.text]=\"!!notification().slot.text?.length\">\n @if (notification().slot.text?.length) {\n {{(notification().slot.text | wbText)()}}\n } @else if (notification().slot.component) {\n <sci-viewport class=\"e2e-message-viewport\">\n <ng-container *ngComponentOutlet=\"notification().slot.component!; inputs: notification().inputs | wbRemoveLegacyInput; injector: notification().slot.injector\"/>\n </sci-viewport>\n }\n</div>\n\n<button (click)=\"onClose()\"\n [title]=\"('%workbench.close.tooltip' | wbText)()\"\n class=\"close e2e-close\">\n <wb-icon icon=\"workbench.close\"/>\n</button>\n", styles: [":host{display:flex;flex-direction:column;gap:.75em;background-color:var(--sci-color-background-elevation);color:var(--sci-color-text);font-size:.9em;border:1px solid var(--sci-color-border);border-radius:var(--sci-corner);box-shadow:var(--sci-elevation) var(--sci-static-color-black);padding:1em 0;overflow:hidden;outline:none;position:relative}:host:before{content:\"\";position:absolute;top:0;left:0;bottom:0;width:var(--sci-workbench-notification-severity-indicator-size)}:host[data-severity=info]:before{background-color:var(--sci-color-accent)}:host[data-severity=warn]:before{background-color:var(--sci-color-notice)}:host[data-severity=error]:before{background-color:var(--sci-color-negative)}:host>header{flex:none;font-weight:700;padding:0 1.5em;word-break:break-word;white-space:pre-line}:host>div.message{flex:auto;overflow:hidden}:host>div.message.text{word-break:break-word;white-space:pre-line;padding:0 1.5em}:host>div.message>sci-viewport{height:100%}:host>div.message>sci-viewport::part(content){padding:0 1.5em}:host>button.close:is(button,#sci-reset){all:unset;display:inline-grid;place-content:center;place-items:center;padding:.25em;border-radius:var(--sci-corner);-webkit-user-select:none;user-select:none;overflow:hidden;cursor:var(--sci-workbench-button-cursor)}:host>button.close:is(button,#sci-reset):hover:where(:not(:disabled)){background-color:var(--sci-workbench-button-background-color-hover)}:host>button.close:is(button,#sci-reset):active:where(:not(:disabled)){background-color:var(--sci-workbench-button-background-color-active)}:host>button.close:is(button,#sci-reset):focus:not(:focus-visible){outline:none}:host>button.close:is(button,#sci-reset):focus-visible{outline:var(--sci-workbench-button-outline-width-focus) solid var(--sci-color-accent)}:host>button.close:is(button,#sci-reset):disabled{color:var(--sci-color-gray-500)}:host>button.close:is(button,#sci-reset){position:absolute;top:.275em;right:.275em;padding:.125em;border-radius:var(--sci-corner-small);font-size:1rem}\n"] }]
20099
20532
  }], ctorParameters: () => [], propDecorators: { notification: [{ type: i0.Input, args: [{ isSignal: true, alias: "notification", required: true }] }] } });
20100
- /**
20101
- * Indicates that the auxilary mouse button is pressed (usually the mouse wheel button or middle button).
20102
- */
20103
- const AUXILARY_MOUSE_BUTTON = 4;
20104
20533
 
20105
20534
  /*
20106
20535
  * Copyright (c) 2018-2022 Swiss Federal Railways
@@ -20295,7 +20724,7 @@ class ActivityPanelComponent {
20295
20724
  sash1: `${1 / (1 - ratio)}`,
20296
20725
  sash2: `${1 / ratio}`,
20297
20726
  };
20298
- }, { equal: Objects$1.isEqual });
20727
+ }, { equal: Objects.isEqual });
20299
20728
  }
20300
20729
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: ActivityPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
20301
20730
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: ActivityPanelComponent, isStandalone: true, selector: "wb-activity-panel", inputs: { panel: { classPropertyName: "panel", publicName: "panel", isSignal: true, isRequired: true, transformFunction: null }, activityId1: { classPropertyName: "activityId1", publicName: "activityId1", isSignal: true, isRequired: true, transformFunction: null }, activityId2: { classPropertyName: "activityId2", publicName: "activityId2", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "attr.data-panel": "panel()" } }, ngImport: i0, template: "@if (activityId1() && activityId2()) {\n <sci-sashbox [direction]=\"direction()\" (sashStart)=\"onSashStart()\" (sashEnd)=\"onSashEnd($event)\">\n <ng-template sciSash [size]=\"sashSizes().sash1\" key=\"sash1\">\n <ng-container *ngTemplateOutlet=\"activity_template; context: {$implicit: activityId1()}\"/>\n </ng-template>\n\n <ng-template sciSash [size]=\"sashSizes().sash2\" key=\"sash2\">\n <ng-container *ngTemplateOutlet=\"activity_template; context: {$implicit: activityId2()}\"/>\n </ng-template>\n </sci-sashbox>\n} @else if (activityId1()) {\n <ng-container *ngTemplateOutlet=\"activity_template; context: {$implicit: activityId1()}\"/>\n} @else if (activityId2()) {\n <ng-container *ngTemplateOutlet=\"activity_template; context: {$implicit: activityId2()}\"/>\n}\n\n<ng-template #activity_template let-activityId>\n <wb-grid [grid]=\"layout().grids[activityId]!\"\n [attr.data-grid]=\"activityId\"\n [gridDropZone]=\"{\n dropRegionSize: .15,\n dropPlaceholderSize: .15,\n dropZoneAttributes: {'data-grid': activityId},\n }\"/>\n</ng-template>\n", styles: [":host{display:grid}:host>sci-sashbox{z-index:auto}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: SciSashboxComponent, selector: "sci-sashbox", inputs: ["direction"], outputs: ["sashStart", "sashEnd"] }, { kind: "directive", type: SciSashDirective, selector: "ng-template[sciSash]", inputs: ["size", "minSize", "key", "animate"], exportAs: ["sciSash"] }, { kind: "component", type: GridComponent, selector: "wb-grid", inputs: ["grid", "gridDropZone"] }] });
@@ -21074,8 +21503,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
21074
21503
  /**
21075
21504
  * Shows a notification.
21076
21505
  *
21077
- * A notification is a closable message displayed in the upper-right corner that disappears after a few seconds unless hovered.
21078
- * It informs about system events, task completion or errors. The severity indicates importance or urgency.
21506
+ * A notification is a closable message displayed in the upper-right corner that disappears after a few seconds unless hovered or focused.
21507
+ * It informs about system events, task completion, or errors. Severity indicates importance or urgency.
21079
21508
  *
21080
21509
  * Notifications can be grouped. Only the most recent notification within a group is displayed.
21081
21510
  *
@@ -21205,7 +21634,7 @@ class PopupConfig {
21205
21634
  */
21206
21635
 
21207
21636
  /*
21208
- * Copyright (c) 2018-2025 Swiss Federal Railways
21637
+ * Copyright (c) 2018-2026 Swiss Federal Railways
21209
21638
  *
21210
21639
  * This program and the accompanying materials are made
21211
21640
  * available under the terms of the Eclipse Public License 2.0
@@ -21285,7 +21714,7 @@ class PopupConfig {
21285
21714
  */
21286
21715
 
21287
21716
  /*
21288
- * Copyright (c) 2018-2023 Swiss Federal Railways
21717
+ * Copyright (c) 2018-2026 Swiss Federal Railways
21289
21718
  *
21290
21719
  * This program and the accompanying materials are made
21291
21720
  * available under the terms of the Eclipse Public License 2.0
@@ -21311,5 +21740,5 @@ class PopupConfig {
21311
21740
  * Generated bundle index. Do not edit.
21312
21741
  */
21313
21742
 
21314
- export { ActivatedMicrofrontend, IconComponent, LogAppender, LogLevel, Logger, LoggerName, MAIN_AREA, MAIN_AREA_INITIAL_PART_ID, MicrofrontendPlatformConfigLoader, MicrofrontendPlatformStartupPhase, Notification, NotificationService, Popup, PopupConfig, PopupService, TextPipe, VIEW_TAB_RENDERING_CONTEXT, WORKBENCH_DIALOG_CONTEXT, WORKBENCH_ID, WORKBENCH_PART_CONTEXT, WORKBENCH_POPUP_CONTEXT, WORKBENCH_POPUP_REFERRER, WORKBENCH_VIEW_CONTEXT, WorkbenchComponent, WorkbenchConfig, WorkbenchDesktopDirective, WorkbenchDialog, WorkbenchDialogActionDirective, WorkbenchDialogFooterDirective, WorkbenchDialogHeaderDirective, WorkbenchDialogService, WorkbenchLauncher, WorkbenchLayoutFactory, WorkbenchMessageBoxService, WorkbenchNotification, WorkbenchNotificationService, WorkbenchPart, WorkbenchPartActionDirective, WorkbenchPerspectiveData, WorkbenchPopup, WorkbenchPopupService, WorkbenchRouteData, WorkbenchRouter, WorkbenchRouterLinkDirective, WorkbenchService, WorkbenchStartup, WorkbenchStartupPhase, WorkbenchStorage, WorkbenchView, WorkbenchViewMenuItemDirective, canMatchWorkbenchDialogCapability, canMatchWorkbenchMessageBoxCapability, canMatchWorkbenchOutlet, canMatchWorkbenchPart, canMatchWorkbenchPartCapability, canMatchWorkbenchPerspective, canMatchWorkbenchPopupCapability, canMatchWorkbenchView, canMatchWorkbenchViewCapability, provideMicrofrontendPlatformInitializer, provideWorkbench, provideWorkbenchInitializer, text };
21743
+ export { ActivatedMicrofrontend, IconComponent, LogAppender, LogLevel, Logger, LoggerName, MAIN_AREA, MAIN_AREA_INITIAL_PART_ID, MicrofrontendPlatformConfigLoader, MicrofrontendPlatformStartupPhase, Notification, NotificationService, Popup, PopupConfig, PopupService, TextPipe, VIEW_TAB_RENDERING_CONTEXT, WORKBENCH_DIALOG_CONTEXT, WORKBENCH_ID, WORKBENCH_PART_CONTEXT, WORKBENCH_POPUP_CONTEXT, WORKBENCH_POPUP_REFERRER, WORKBENCH_VIEW_CONTEXT, WorkbenchComponent, WorkbenchConfig, WorkbenchDesktopDirective, WorkbenchDialog, WorkbenchDialogActionDirective, WorkbenchDialogFooterDirective, WorkbenchDialogHeaderDirective, WorkbenchDialogService, WorkbenchLauncher, WorkbenchLayoutFactory, WorkbenchMessageBoxService, WorkbenchNotification, WorkbenchNotificationService, WorkbenchPart, WorkbenchPartActionDirective, WorkbenchPerspectiveData, WorkbenchPopup, WorkbenchPopupService, WorkbenchRouteData, WorkbenchRouter, WorkbenchRouterLinkDirective, WorkbenchService, WorkbenchStartup, WorkbenchStartupPhase, WorkbenchStorage, WorkbenchView, WorkbenchViewMenuItemDirective, canMatchWorkbenchDialogCapability, canMatchWorkbenchMessageBoxCapability, canMatchWorkbenchNotificationCapability, canMatchWorkbenchOutlet, canMatchWorkbenchPart, canMatchWorkbenchPartCapability, canMatchWorkbenchPerspective, canMatchWorkbenchPopupCapability, canMatchWorkbenchView, canMatchWorkbenchViewCapability, provideMicrofrontendPlatformInitializer, provideWorkbench, provideWorkbenchInitializer, text };
21315
21744
  //# sourceMappingURL=scion-workbench.mjs.map