@loomweaver/shell 0.7.6 → 0.7.8

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,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, signal, computed, Service, inject, makeEnvironmentProviders, ApplicationRef, Injector, effect, untracked, provideEnvironmentInitializer, ErrorHandler, isDevMode, input, Directive, CUSTOM_ELEMENTS_SCHEMA, Component, output, ElementRef, EnvironmentInjector, DestroyRef, viewChild, afterRenderEffect, afterNextRender, createComponent, forwardRef, booleanAttribute, ViewEncapsulation, viewChildren, Injectable, provideZonelessChangeDetection, provideBrowserGlobalErrorListeners, provideAppInitializer } from '@angular/core';
2
+ import { InjectionToken, isDevMode, signal, computed, Service, inject, makeEnvironmentProviders, ApplicationRef, Injector, effect, untracked, provideEnvironmentInitializer, ErrorHandler, ElementRef, input, booleanAttribute, Directive, CUSTOM_ELEMENTS_SCHEMA, Component, output, EnvironmentInjector, DestroyRef, viewChild, afterRenderEffect, afterNextRender, createComponent, forwardRef, ViewEncapsulation, viewChildren, Injectable, provideZonelessChangeDetection, provideBrowserGlobalErrorListeners, provideAppInitializer } from '@angular/core';
3
3
  import { ChildrenOutletContexts, Router, NavigationEnd, ActivatedRoute, convertToParamMap, UrlSegment, RouterOutlet, RouteReuseStrategy, provideRouter, withDisabledInitialNavigation } from '@angular/router';
4
4
  import { TranslocoService, TranslocoPipe, provideTransloco, provideTranslocoMissingHandler } from '@jsverse/transloco';
5
5
  import { DOCUMENT, NgComponentOutlet, Location, NgTemplateOutlet } from '@angular/common';
@@ -43,6 +43,40 @@ function provideLayout(layout) {
43
43
  return { provide: SHELL_LAYOUT, useValue: layout };
44
44
  }
45
45
 
46
+ const warned = new Set();
47
+ function menuOnActivate(item) {
48
+ if (!item.menu || item.menuTrigger === undefined || item.menuTrigger === 'context') {
49
+ return undefined;
50
+ }
51
+ return item.workspace === undefined ? item.menu : undefined;
52
+ }
53
+ function menuOnContext(item) {
54
+ if (!item.menu) {
55
+ return undefined;
56
+ }
57
+ return menuOnActivate(item) && item.menuTrigger === 'primary'
58
+ ? undefined
59
+ : item.menu;
60
+ }
61
+ function warnMenuTriggerConflict(item) {
62
+ if (!isDevMode() || warned.has(item.id)) {
63
+ return;
64
+ }
65
+ if (item.workspace !== undefined && item.menuTrigger !== undefined && item.menuTrigger !== 'context') {
66
+ warned.add(item.id);
67
+ console.warn(`Item "${item.id}" switches to workspace "${item.workspace}" and asks for its menu on ` +
68
+ `activation — activating it is the switch, so the menu "${item.menu}" stays on the ` +
69
+ `right-click.`);
70
+ return;
71
+ }
72
+ if (menuOnActivate(item) && (item.command !== undefined || item.run !== undefined)) {
73
+ warned.add(item.id);
74
+ console.warn(`Item "${item.id}" opens the menu "${item.menu}" on activation, so its ` +
75
+ `${item.command === undefined ? 'inline behaviour' : `command "${item.command}"`} is ` +
76
+ `never run from here. Reach it from a menu entry, a shortcut or the palette instead.`);
77
+ }
78
+ }
79
+
46
80
  const PRIMARY_PANE = 'main';
47
81
  const VIEW_PANE_PREFIX = 'view:';
48
82
  const CONTENT_DOCK = 'content';
@@ -2495,8 +2529,24 @@ function reflectAttribute(element, name, value) {
2495
2529
  const LW_MENU_TAG = 'lw-menu';
2496
2530
  const LW_MENU_ITEM_TAG = 'lw-menu-item';
2497
2531
  const VIEWPORT_MARGIN$1 = 4;
2532
+ const MENU_ANCHOR_GAP = 4;
2498
2533
  const LW_MENU_SELECT = 'lw-menu-select';
2499
2534
  const LW_MENU_DISMISS = 'lw-menu-dismiss';
2535
+ function fits(start, size, limit) {
2536
+ return start >= VIEWPORT_MARGIN$1 && start + size <= limit - VIEWPORT_MARGIN$1;
2537
+ }
2538
+ function clamp(start, size, limit) {
2539
+ return Math.max(VIEWPORT_MARGIN$1, Math.min(start, limit - size - VIEWPORT_MARGIN$1));
2540
+ }
2541
+ function beside(preferred, opposite, size, limit) {
2542
+ if (fits(preferred, size, limit)) {
2543
+ return preferred;
2544
+ }
2545
+ return fits(opposite, size, limit) ? opposite : clamp(preferred, size, limit);
2546
+ }
2547
+ function aligned(near, far, size, limit) {
2548
+ return fits(near, size, limit) ? near : clamp(far - size, size, limit);
2549
+ }
2500
2550
  class LwMenuItemElement extends HTMLElement {
2501
2551
  static observedAttributes = [
2502
2552
  'label',
@@ -2590,10 +2640,26 @@ class LwMenuElement extends HTMLElement {
2590
2640
  }
2591
2641
  openAt(x, y) {
2592
2642
  const { width, height } = this.getBoundingClientRect();
2593
- const maxLeft = window.innerWidth - width - VIEWPORT_MARGIN$1;
2594
- const maxTop = window.innerHeight - height - VIEWPORT_MARGIN$1;
2595
- this.style.left = `${Math.max(VIEWPORT_MARGIN$1, Math.min(x, maxLeft))}px`;
2596
- this.style.top = `${Math.max(VIEWPORT_MARGIN$1, Math.min(y, maxTop))}px`;
2643
+ this.place(clamp(x, width, window.innerWidth), clamp(y, height, window.innerHeight));
2644
+ }
2645
+ openBeside(rect, side) {
2646
+ const { width, height } = this.getBoundingClientRect();
2647
+ const viewportWidth = window.innerWidth;
2648
+ const viewportHeight = window.innerHeight;
2649
+ const after = { x: rect.right + MENU_ANCHOR_GAP, y: rect.bottom + MENU_ANCHOR_GAP };
2650
+ const before = {
2651
+ x: rect.left - MENU_ANCHOR_GAP - width,
2652
+ y: rect.top - MENU_ANCHOR_GAP - height,
2653
+ };
2654
+ if (side === 'left' || side === 'right') {
2655
+ const preferred = side === 'right' ? after.x : before.x;
2656
+ const opposite = side === 'right' ? before.x : after.x;
2657
+ this.place(beside(preferred, opposite, width, viewportWidth), aligned(rect.top, rect.bottom, height, viewportHeight));
2658
+ return;
2659
+ }
2660
+ const preferred = side === 'bottom' ? after.y : before.y;
2661
+ const opposite = side === 'bottom' ? before.y : after.y;
2662
+ this.place(aligned(rect.left, rect.right, width, viewportWidth), beside(preferred, opposite, height, viewportHeight));
2597
2663
  }
2598
2664
  onKeydown = (event) => this.handleKeydown(event);
2599
2665
  onClick = (event) => {
@@ -2602,6 +2668,10 @@ class LwMenuElement extends HTMLElement {
2602
2668
  this.select(item);
2603
2669
  }
2604
2670
  };
2671
+ place(left, top) {
2672
+ this.style.left = `${left}px`;
2673
+ this.style.top = `${top}px`;
2674
+ }
2605
2675
  items() {
2606
2676
  return [...this.querySelectorAll('[role^="menuitem"]')].filter((item) => !this.isDisabled(item));
2607
2677
  }
@@ -2678,28 +2748,30 @@ function defineLwMenu() {
2678
2748
  }
2679
2749
  }
2680
2750
 
2681
- const MENU_ANCHOR_GAP = 4;
2682
2751
  class MenuService {
2683
2752
  registry = inject(ContributionRegistry);
2684
2753
  commands = inject(CommandService);
2685
2754
  transloco = inject(TranslocoService);
2755
+ trigger = signal(null, /* @ts-ignore */
2756
+ ...(ngDevMode ? [{ debugName: "trigger" }] : /* istanbul ignore next */ []));
2686
2757
  current;
2687
- open(menuId, context, at) {
2758
+ openTrigger = this.trigger.asReadonly();
2759
+ open(menuId, context, at, options = {}) {
2688
2760
  this.close();
2689
2761
  const resolved = this.resolve(typeof menuId === 'string' ? [menuId] : menuId, context);
2690
2762
  if (resolved.length === 0) {
2691
2763
  return;
2692
2764
  }
2693
- const menu = this.createMenu(resolved);
2765
+ const menu = this.createMenu(resolved, options.header);
2694
2766
  const byKey = new Map(resolved.map((entry) => [entry.key, entry.item]));
2695
2767
  this.present(menu, at, (key) => {
2696
2768
  const item = key === null ? undefined : byKey.get(key);
2697
2769
  if (item) {
2698
2770
  this.run(item, context);
2699
2771
  }
2700
- });
2772
+ }, options.trigger);
2701
2773
  }
2702
- openList(entries, at, onPick) {
2774
+ openList(entries, at, onPick, trigger) {
2703
2775
  this.close();
2704
2776
  if (entries.length === 0) {
2705
2777
  return;
@@ -2709,7 +2781,7 @@ class MenuService {
2709
2781
  if (key !== null) {
2710
2782
  onPick(key);
2711
2783
  }
2712
- });
2784
+ }, trigger);
2713
2785
  }
2714
2786
  close() {
2715
2787
  const open = this.current;
@@ -2717,13 +2789,14 @@ class MenuService {
2717
2789
  return;
2718
2790
  }
2719
2791
  this.current = undefined;
2792
+ this.trigger.set(null);
2720
2793
  clearTimeout(open.listenTimer);
2721
2794
  document.removeEventListener('pointerdown', open.onOutside, true);
2722
2795
  document.body.classList.remove('lw-menu-open');
2723
2796
  open.menu.remove();
2724
2797
  open.restore?.focus?.();
2725
2798
  }
2726
- present(menu, at, onSelect) {
2799
+ present(menu, at, onSelect, trigger) {
2727
2800
  const restore = document.activeElement;
2728
2801
  menu.addEventListener(LW_MENU_SELECT, (event) => {
2729
2802
  const key = event.detail
@@ -2739,7 +2812,13 @@ class MenuService {
2739
2812
  };
2740
2813
  document.body.append(menu);
2741
2814
  document.body.classList.add('lw-menu-open');
2742
- menu.openAt(at.x, at.y);
2815
+ if ('rect' in at) {
2816
+ menu.openBeside(at.rect, at.side);
2817
+ }
2818
+ else {
2819
+ menu.openAt(at.x, at.y);
2820
+ }
2821
+ this.trigger.set(trigger ?? null);
2743
2822
  const listenTimer = setTimeout(() => document.addEventListener('pointerdown', onOutside, { capture: true }), 0);
2744
2823
  this.current = { menu, onOutside, restore, listenTimer };
2745
2824
  }
@@ -2775,11 +2854,14 @@ class MenuService {
2775
2854
  .filter((entry) => entry !== null)
2776
2855
  .toSorted((a, b) => a.group.localeCompare(b.group) || a.order - b.order);
2777
2856
  }
2778
- createMenu(resolved) {
2857
+ createMenu(resolved, header) {
2779
2858
  const menu = document.createElement(LW_MENU_TAG);
2780
2859
  if (resolved.some((entry) => entry.icon || entry.checkbox)) {
2781
2860
  menu.classList.add('lw-menu--leading');
2782
2861
  }
2862
+ if (header) {
2863
+ menu.append(this.createHeader(header, menu));
2864
+ }
2783
2865
  let lastGroup;
2784
2866
  for (const entry of resolved) {
2785
2867
  if (lastGroup !== undefined && entry.group !== lastGroup) {
@@ -2808,6 +2890,66 @@ class MenuService {
2808
2890
  }
2809
2891
  return menu;
2810
2892
  }
2893
+ createHeader(header, menu) {
2894
+ const title = this.transloco.translate(header.title);
2895
+ const detail = header.detail
2896
+ ? this.transloco.translate(header.detail)
2897
+ : undefined;
2898
+ menu.setAttribute('aria-label', detail ? `${title}, ${detail}` : title);
2899
+ const element = document.createElement('div');
2900
+ element.className = 'lw-menu-header';
2901
+ element.setAttribute('aria-hidden', 'true');
2902
+ const mark = this.createHeaderMark(header);
2903
+ if (mark) {
2904
+ element.append(mark);
2905
+ }
2906
+ const lines = document.createElement('span');
2907
+ lines.className = 'lw-menu-header-lines';
2908
+ const name = document.createElement('span');
2909
+ name.className = 'lw-menu-header-title';
2910
+ name.textContent = title;
2911
+ lines.append(name);
2912
+ if (detail) {
2913
+ const second = document.createElement('span');
2914
+ second.className = 'lw-menu-header-detail';
2915
+ second.textContent = detail;
2916
+ lines.append(second);
2917
+ }
2918
+ element.append(lines);
2919
+ return element;
2920
+ }
2921
+ createHeaderMark(header) {
2922
+ if (!header.image && !header.initials && !header.icon) {
2923
+ return undefined;
2924
+ }
2925
+ const mark = document.createElement('span');
2926
+ mark.className = 'lw-menu-header-mark';
2927
+ mark.append(...this.markContent(header));
2928
+ if (header.image) {
2929
+ const picture = mark.firstElementChild;
2930
+ picture.addEventListener('error', () => mark.replaceChildren(...this.markContent({ ...header, image: undefined })));
2931
+ }
2932
+ return mark;
2933
+ }
2934
+ markContent(header) {
2935
+ if (header.image) {
2936
+ const picture = document.createElement('img');
2937
+ picture.src = header.image;
2938
+ picture.alt = '';
2939
+ picture.className = 'lw-menu-header-picture';
2940
+ return [picture];
2941
+ }
2942
+ if (header.initials) {
2943
+ return [document.createTextNode(header.initials)];
2944
+ }
2945
+ if (header.icon) {
2946
+ const icon = document.createElement('lw-icon');
2947
+ icon.setAttribute('name', header.icon);
2948
+ icon.setAttribute('size', '1rem');
2949
+ return [icon];
2950
+ }
2951
+ return [];
2952
+ }
2811
2953
  createListMenu(entries) {
2812
2954
  const menu = document.createElement(LW_MENU_TAG);
2813
2955
  if (entries.some((entry) => entry.icon || entry.active || entry.checked !== undefined)) {
@@ -2859,35 +3001,74 @@ function whenMatches(when, context) {
2859
3001
  return Object.entries(when).every(([key, value]) => context[key] === value);
2860
3002
  }
2861
3003
 
2862
- class ContextMenuDirective {
3004
+ function names(slots) {
3005
+ return slots !== undefined && slots.length > 0;
3006
+ }
3007
+ class MenuTriggerDirective {
2863
3008
  menus = inject(MenuService);
2864
- menu = input(undefined, { ...(ngDevMode ? { debugName: "menu" } : /* istanbul ignore next */ {}), alias: 'lwContextMenu' });
2865
- context = input({}, { ...(ngDevMode ? { debugName: "context" } : /* istanbul ignore next */ {}), alias: 'lwContextMenuContext' });
3009
+ host = inject(ElementRef);
3010
+ menu = input(undefined, { ...(ngDevMode ? { debugName: "menu" } : /* istanbul ignore next */ {}), alias: 'lwMenu' });
3011
+ onActivateMenu = input(undefined, { ...(ngDevMode ? { debugName: "onActivateMenu" } : /* istanbul ignore next */ {}), alias: 'lwMenuOnActivate' });
3012
+ context = input({}, { ...(ngDevMode ? { debugName: "context" } : /* istanbul ignore next */ {}), alias: 'lwMenuContext' });
3013
+ side = input('bottom', { ...(ngDevMode ? { debugName: "side" } : /* istanbul ignore next */ {}), alias: 'lwMenuSide' });
3014
+ header = input(undefined, { ...(ngDevMode ? { debugName: "header" } : /* istanbul ignore next */ {}), alias: 'lwMenuHeader' });
3015
+ state = input(false, { ...(ngDevMode ? { debugName: "state" } : /* istanbul ignore next */ {}), alias: 'lwMenuState',
3016
+ transform: booleanAttribute });
3017
+ announces = computed(() => this.state() || names(this.onActivateMenu()), /* @ts-ignore */
3018
+ ...(ngDevMode ? [{ debugName: "announces" }] : /* istanbul ignore next */ []));
3019
+ hasPopup = computed(() => this.announces() ? 'menu' : null, /* @ts-ignore */
3020
+ ...(ngDevMode ? [{ debugName: "hasPopup" }] : /* istanbul ignore next */ []));
3021
+ expanded = computed(() => this.announces()
3022
+ ? String(this.menus.openTrigger() === this.host.nativeElement)
3023
+ : null, /* @ts-ignore */
3024
+ ...(ngDevMode ? [{ debugName: "expanded" }] : /* istanbul ignore next */ []));
2866
3025
  onContextMenu(event) {
2867
- const slot = this.menu();
2868
- if (!slot || slot.length === 0) {
3026
+ const slots = this.menu();
3027
+ if (!names(slots)) {
2869
3028
  return;
2870
3029
  }
2871
3030
  event.preventDefault();
2872
3031
  event.stopPropagation();
2873
- this.menus.open(slot, this.context(), {
3032
+ this.menus.open(slots, this.context(), {
2874
3033
  x: event.clientX,
2875
3034
  y: event.clientY,
2876
3035
  });
2877
3036
  }
2878
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ContextMenuDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
2879
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.0", type: ContextMenuDirective, isStandalone: true, selector: "[lwContextMenu]", inputs: { menu: { classPropertyName: "menu", publicName: "lwContextMenu", isSignal: true, isRequired: false, transformFunction: null }, context: { classPropertyName: "context", publicName: "lwContextMenuContext", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "contextmenu": "onContextMenu($event)" } }, ngImport: i0 });
3037
+ onActivate(event) {
3038
+ const slots = this.onActivateMenu();
3039
+ if (!names(slots)) {
3040
+ return;
3041
+ }
3042
+ event.preventDefault();
3043
+ event.stopPropagation();
3044
+ const control = this.host.nativeElement;
3045
+ this.menus.open(slots, this.context(), { rect: control.getBoundingClientRect(), side: this.side() }, { trigger: control, header: this.header() });
3046
+ }
3047
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: MenuTriggerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
3048
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.0", type: MenuTriggerDirective, isStandalone: true, selector: "[lwMenu], [lwMenuOnActivate], [lwMenuState]", inputs: { menu: { classPropertyName: "menu", publicName: "lwMenu", isSignal: true, isRequired: false, transformFunction: null }, onActivateMenu: { classPropertyName: "onActivateMenu", publicName: "lwMenuOnActivate", isSignal: true, isRequired: false, transformFunction: null }, context: { classPropertyName: "context", publicName: "lwMenuContext", isSignal: true, isRequired: false, transformFunction: null }, side: { classPropertyName: "side", publicName: "lwMenuSide", isSignal: true, isRequired: false, transformFunction: null }, header: { classPropertyName: "header", publicName: "lwMenuHeader", isSignal: true, isRequired: false, transformFunction: null }, state: { classPropertyName: "state", publicName: "lwMenuState", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "contextmenu": "onContextMenu($event)", "click": "onActivate($event)" }, properties: { "attr.aria-haspopup": "hasPopup()", "attr.aria-expanded": "expanded()" } }, ngImport: i0 });
2880
3049
  }
2881
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ContextMenuDirective, decorators: [{
3050
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: MenuTriggerDirective, decorators: [{
2882
3051
  type: Directive,
2883
3052
  args: [{
2884
- selector: '[lwContextMenu]',
2885
- host: { '(contextmenu)': 'onContextMenu($event)' },
3053
+ selector: '[lwMenu], [lwMenuOnActivate], [lwMenuState]',
3054
+ host: {
3055
+ '(contextmenu)': 'onContextMenu($event)',
3056
+ '(click)': 'onActivate($event)',
3057
+ '[attr.aria-haspopup]': 'hasPopup()',
3058
+ '[attr.aria-expanded]': 'expanded()',
3059
+ },
2886
3060
  }]
2887
- }], propDecorators: { menu: [{ type: i0.Input, args: [{ isSignal: true, alias: "lwContextMenu", required: false }] }], context: [{ type: i0.Input, args: [{ isSignal: true, alias: "lwContextMenuContext", required: false }] }] } });
3061
+ }], propDecorators: { menu: [{ type: i0.Input, args: [{ isSignal: true, alias: "lwMenu", required: false }] }], onActivateMenu: [{ type: i0.Input, args: [{ isSignal: true, alias: "lwMenuOnActivate", required: false }] }], context: [{ type: i0.Input, args: [{ isSignal: true, alias: "lwMenuContext", required: false }] }], side: [{ type: i0.Input, args: [{ isSignal: true, alias: "lwMenuSide", required: false }] }], header: [{ type: i0.Input, args: [{ isSignal: true, alias: "lwMenuHeader", required: false }] }], state: [{ type: i0.Input, args: [{ isSignal: true, alias: "lwMenuState", required: false }] }] } });
2888
3062
 
2889
3063
  const BAR_CONTEXT = new InjectionToken('BAR_CONTEXT');
2890
3064
 
3065
+ const MENU_SIDE_BY_DOCK = {
3066
+ top: 'bottom',
3067
+ bottom: 'top',
3068
+ left: 'right',
3069
+ right: 'left',
3070
+ center: 'bottom',
3071
+ };
2891
3072
  class ShellBarItem {
2892
3073
  item = input.required(/* @ts-ignore */
2893
3074
  ...(ngDevMode ? [{ debugName: "item" }] : /* istanbul ignore next */ []));
@@ -2906,8 +3087,22 @@ class ShellBarItem {
2906
3087
  return 'component' in item ? null : item;
2907
3088
  }, /* @ts-ignore */
2908
3089
  ...(ngDevMode ? [{ debugName: "asButton" }] : /* istanbul ignore next */ []));
3090
+ brokenPicture = signal(false, /* @ts-ignore */
3091
+ ...(ngDevMode ? [{ debugName: "brokenPicture" }] : /* istanbul ignore next */ []));
2909
3092
  tooltipPosition = computed(() => this.dock() === 'bottom' ? 'top' : 'bottom', /* @ts-ignore */
2910
3093
  ...(ngDevMode ? [{ debugName: "tooltipPosition" }] : /* istanbul ignore next */ []));
3094
+ menuSide = computed(() => MENU_SIDE_BY_DOCK[this.dock()], /* @ts-ignore */
3095
+ ...(ngDevMode ? [{ debugName: "menuSide" }] : /* istanbul ignore next */ []));
3096
+ contextMenu = computed(() => {
3097
+ const button = this.asButton();
3098
+ return button ? menuOnContext(button) : undefined;
3099
+ }, /* @ts-ignore */
3100
+ ...(ngDevMode ? [{ debugName: "contextMenu" }] : /* istanbul ignore next */ []));
3101
+ activateMenu = computed(() => {
3102
+ const button = this.asButton();
3103
+ return button ? menuOnActivate(button) : undefined;
3104
+ }, /* @ts-ignore */
3105
+ ...(ngDevMode ? [{ debugName: "activateMenu" }] : /* istanbul ignore next */ []));
2911
3106
  componentInjector = computed(() => {
2912
3107
  const item = this.item();
2913
3108
  return Injector.create({
@@ -2931,17 +3126,27 @@ class ShellBarItem {
2931
3126
  return this.commands.shortcutOf(this.commands.commands().find((entry) => entry.id === button.command));
2932
3127
  }, /* @ts-ignore */
2933
3128
  ...(ngDevMode ? [{ debugName: "shortcut" }] : /* istanbul ignore next */ []));
3129
+ pictureOf(button) {
3130
+ return this.brokenPicture() ? undefined : button.image;
3131
+ }
3132
+ onPictureError() {
3133
+ this.brokenPicture.set(true);
3134
+ }
2934
3135
  run(button) {
2935
3136
  if (this.disabled())
2936
3137
  return;
3138
+ warnMenuTriggerConflict(button);
3139
+ if (menuOnActivate(button)) {
3140
+ return;
3141
+ }
2937
3142
  this.commands.trigger(button);
2938
3143
  }
2939
3144
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ShellBarItem, deps: [], target: i0.ɵɵFactoryTarget.Component });
2940
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: ShellBarItem, isStandalone: true, selector: "lw-shell-bar-item", inputs: { item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: true, transformFunction: null }, dock: { classPropertyName: "dock", publicName: "dock", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "@if (asButton(); as btn) {\n <button\n type=\"button\"\n (click)=\"run(btn)\"\n [disabled]=\"disabled()\"\n [lwContextMenu]=\"btn.menu\"\n [lwContextMenuContext]=\"{ targetKind: 'bar-item', id: btn.id, bar: btn.bar }\"\n class=\"relative flex items-center gap-1 rounded px-1.5 py-0.5 text-content-faint transition-colors hover:bg-surface-overlay hover:text-content disabled:pointer-events-none disabled:opacity-40\"\n [attr.aria-label]=\"(btn.tooltip ?? btn.label ?? '') | transloco\"\n >\n @if (btn.icon; as icon) {\n <lw-icon [name]=\"icon\" size=\"1rem\" />\n }\n @if (btn.label; as label) {\n <span>{{ label | transloco }}</span>\n }\n @if (shortcut(); as chord) {\n <kbd\n class=\"rounded border border-border bg-surface-overlay px-1 text-[0.6875rem] leading-tight text-content-muted\"\n >{{ chord }}</kbd\n >\n }\n @if (btn.tooltip ?? btn.label; as tip) {\n <lw-tooltip [text]=\"tip | transloco\" [position]=\"tooltipPosition()\" />\n }\n </button>\n} @else if (asComponent(); as comp) {\n <ng-container\n *ngComponentOutlet=\"comp.component; injector: componentInjector()\"\n ></ng-container>\n}\n", dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "directive", type: ContextMenuDirective, selector: "[lwContextMenu]", inputs: ["lwContextMenu", "lwContextMenuContext"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
3145
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: ShellBarItem, isStandalone: true, selector: "lw-shell-bar-item", inputs: { item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: true, transformFunction: null }, dock: { classPropertyName: "dock", publicName: "dock", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "@if (asButton(); as btn) {\n <button\n type=\"button\"\n (click)=\"run(btn)\"\n [disabled]=\"disabled()\"\n [lwMenu]=\"contextMenu()\"\n [lwMenuOnActivate]=\"activateMenu()\"\n [lwMenuHeader]=\"btn.menuHeader\"\n [lwMenuSide]=\"menuSide()\"\n [lwMenuContext]=\"{ targetKind: 'bar-item', id: btn.id, bar: btn.bar }\"\n class=\"relative flex items-center gap-1 rounded px-1.5 py-0.5 text-content-faint transition-colors hover:bg-surface-overlay hover:text-content disabled:pointer-events-none disabled:opacity-40\"\n [attr.aria-label]=\"(btn.tooltip ?? btn.label ?? '') | transloco\"\n >\n @if (pictureOf(btn); as picture) {\n <img\n [src]=\"picture\"\n alt=\"\"\n aria-hidden=\"true\"\n data-testid=\"bar-picture\"\n class=\"lw-chrome-picture h-5 w-5\"\n (error)=\"onPictureError()\"\n />\n } @else if (btn.initials) {\n <span aria-hidden=\"true\" data-testid=\"bar-initials\" class=\"lw-rail-initials\">{{\n btn.initials\n }}</span>\n } @else if (btn.icon; as icon) {\n <lw-icon [name]=\"icon\" size=\"1rem\" />\n }\n @if (btn.label; as label) {\n <span>{{ label | transloco }}</span>\n }\n @if (shortcut(); as chord) {\n <kbd\n class=\"rounded border border-border bg-surface-overlay px-1 text-[0.6875rem] leading-tight text-content-muted\"\n >{{ chord }}</kbd\n >\n }\n @if (btn.tooltip ?? btn.label; as tip) {\n <lw-tooltip [text]=\"tip | transloco\" [position]=\"tooltipPosition()\" />\n }\n </button>\n} @else if (asComponent(); as comp) {\n <ng-container\n *ngComponentOutlet=\"comp.component; injector: componentInjector()\"\n ></ng-container>\n}\n", dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "directive", type: MenuTriggerDirective, selector: "[lwMenu], [lwMenuOnActivate], [lwMenuState]", inputs: ["lwMenu", "lwMenuOnActivate", "lwMenuContext", "lwMenuSide", "lwMenuHeader", "lwMenuState"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
2941
3146
  }
2942
3147
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ShellBarItem, decorators: [{
2943
3148
  type: Component,
2944
- args: [{ selector: 'lw-shell-bar-item', imports: [NgComponentOutlet, TranslocoPipe, ContextMenuDirective], schemas: [CUSTOM_ELEMENTS_SCHEMA], template: "@if (asButton(); as btn) {\n <button\n type=\"button\"\n (click)=\"run(btn)\"\n [disabled]=\"disabled()\"\n [lwContextMenu]=\"btn.menu\"\n [lwContextMenuContext]=\"{ targetKind: 'bar-item', id: btn.id, bar: btn.bar }\"\n class=\"relative flex items-center gap-1 rounded px-1.5 py-0.5 text-content-faint transition-colors hover:bg-surface-overlay hover:text-content disabled:pointer-events-none disabled:opacity-40\"\n [attr.aria-label]=\"(btn.tooltip ?? btn.label ?? '') | transloco\"\n >\n @if (btn.icon; as icon) {\n <lw-icon [name]=\"icon\" size=\"1rem\" />\n }\n @if (btn.label; as label) {\n <span>{{ label | transloco }}</span>\n }\n @if (shortcut(); as chord) {\n <kbd\n class=\"rounded border border-border bg-surface-overlay px-1 text-[0.6875rem] leading-tight text-content-muted\"\n >{{ chord }}</kbd\n >\n }\n @if (btn.tooltip ?? btn.label; as tip) {\n <lw-tooltip [text]=\"tip | transloco\" [position]=\"tooltipPosition()\" />\n }\n </button>\n} @else if (asComponent(); as comp) {\n <ng-container\n *ngComponentOutlet=\"comp.component; injector: componentInjector()\"\n ></ng-container>\n}\n" }]
3149
+ args: [{ selector: 'lw-shell-bar-item', imports: [NgComponentOutlet, TranslocoPipe, MenuTriggerDirective], schemas: [CUSTOM_ELEMENTS_SCHEMA], template: "@if (asButton(); as btn) {\n <button\n type=\"button\"\n (click)=\"run(btn)\"\n [disabled]=\"disabled()\"\n [lwMenu]=\"contextMenu()\"\n [lwMenuOnActivate]=\"activateMenu()\"\n [lwMenuHeader]=\"btn.menuHeader\"\n [lwMenuSide]=\"menuSide()\"\n [lwMenuContext]=\"{ targetKind: 'bar-item', id: btn.id, bar: btn.bar }\"\n class=\"relative flex items-center gap-1 rounded px-1.5 py-0.5 text-content-faint transition-colors hover:bg-surface-overlay hover:text-content disabled:pointer-events-none disabled:opacity-40\"\n [attr.aria-label]=\"(btn.tooltip ?? btn.label ?? '') | transloco\"\n >\n @if (pictureOf(btn); as picture) {\n <img\n [src]=\"picture\"\n alt=\"\"\n aria-hidden=\"true\"\n data-testid=\"bar-picture\"\n class=\"lw-chrome-picture h-5 w-5\"\n (error)=\"onPictureError()\"\n />\n } @else if (btn.initials) {\n <span aria-hidden=\"true\" data-testid=\"bar-initials\" class=\"lw-rail-initials\">{{\n btn.initials\n }}</span>\n } @else if (btn.icon; as icon) {\n <lw-icon [name]=\"icon\" size=\"1rem\" />\n }\n @if (btn.label; as label) {\n <span>{{ label | transloco }}</span>\n }\n @if (shortcut(); as chord) {\n <kbd\n class=\"rounded border border-border bg-surface-overlay px-1 text-[0.6875rem] leading-tight text-content-muted\"\n >{{ chord }}</kbd\n >\n }\n @if (btn.tooltip ?? btn.label; as tip) {\n <lw-tooltip [text]=\"tip | transloco\" [position]=\"tooltipPosition()\" />\n }\n </button>\n} @else if (asComponent(); as comp) {\n <ng-container\n *ngComponentOutlet=\"comp.component; injector: componentInjector()\"\n ></ng-container>\n}\n" }]
2945
3150
  }], propDecorators: { item: [{ type: i0.Input, args: [{ isSignal: true, alias: "item", required: true }] }], dock: [{ type: i0.Input, args: [{ isSignal: true, alias: "dock", required: true }] }] } });
2946
3151
 
2947
3152
  class ShellBar {
@@ -2965,7 +3170,9 @@ class ShellBar {
2965
3170
  .barItems()
2966
3171
  .filter((item) => item.bar === this.region().id && item.slot === slot)
2967
3172
  .filter((item) => this.auth.visible(item.access))
2968
- .filter((item) => 'component' in item || this.commands.triggerable(item))
3173
+ .filter((item) => 'component' in item ||
3174
+ menuOnActivate(item) !== undefined ||
3175
+ this.commands.triggerable(item))
2969
3176
  .toSorted((a, b) => (a.order ?? 0) - (b.order ?? 0));
2970
3177
  }
2971
3178
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ShellBar, deps: [], target: i0.ɵɵFactoryTarget.Component });
@@ -4300,6 +4507,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
4300
4507
  type: Service
4301
4508
  }], ctorParameters: () => [] });
4302
4509
 
4510
+ function popoutNavigationRefusal(path) {
4511
+ return (`Content navigation to "${path}" was ignored: this is a pop-out window, which shows one ` +
4512
+ `surface and has no content area to navigate. A command reaches a pop-out's palette only ` +
4513
+ `if it declares popout: true, so leave that off anything that navigates.`);
4514
+ }
4515
+
4303
4516
  class OpenTabsService {
4304
4517
  router = inject(Router);
4305
4518
  registry = inject(ContributionRegistry);
@@ -4438,9 +4651,11 @@ class OpenTabsService {
4438
4651
  this.paneTree.hydrated();
4439
4652
  untracked(() => {
4440
4653
  if (url !== this.lastUrl) {
4654
+ const previous = this.lastUrl;
4441
4655
  this.lastUrl = url;
4442
4656
  if (this.ownNavigation !== normalizePath(url)) {
4443
4657
  this.viewTabSelection.set(null);
4658
+ this.focusHolderOf(path, this.rootFor(previous).root);
4444
4659
  }
4445
4660
  this.ownNavigation = null;
4446
4661
  }
@@ -4459,14 +4674,12 @@ class OpenTabsService {
4459
4674
  navigate(path) {
4460
4675
  if (this.inPopout) {
4461
4676
  if (isDevMode()) {
4462
- console.warn(`Content navigation to "${path}" was ignored: this is a pop-out window, which shows one ` +
4463
- `surface and has no content area to navigate. A command reaches a pop-out's palette only ` +
4464
- `if it declares popout: true, so leave that off anything that navigates.`);
4677
+ console.warn(popoutNavigationRefusal(path));
4465
4678
  }
4466
4679
  return Promise.resolve(false);
4467
4680
  }
4468
4681
  const target = normalizePath(path);
4469
- this.focusHolderOf(target);
4682
+ this.focusHolderOf(target, this.activeTabRoot());
4470
4683
  this.ownNavigation = target;
4471
4684
  this.viewTabSelection.set(null);
4472
4685
  return this.router.navigateByUrl('/' + target + suffixOf(path));
@@ -4522,7 +4735,7 @@ class OpenTabsService {
4522
4735
  return (owner.rest === true ||
4523
4736
  segmentsOf(owner.path).length === segmentsOf(address).length);
4524
4737
  }
4525
- focusHolderOf(target) {
4738
+ focusHolderOf(target, previousContent) {
4526
4739
  const routes = this.registry.contentRoutes();
4527
4740
  const root = tabRootOf(routes, target);
4528
4741
  if (root === '') {
@@ -4540,7 +4753,7 @@ class OpenTabsService {
4540
4753
  return;
4541
4754
  }
4542
4755
  this.paneTree.setActiveTab(CONTENT_DOCK, holder.id, held.path);
4543
- this.paneTree.focusPane(CONTENT_DOCK, holder.id, this.activeTabRoot());
4756
+ this.paneTree.focusPane(CONTENT_DOCK, holder.id, previousContent);
4544
4757
  }
4545
4758
  stampActive(root) {
4546
4759
  const next = new Map(this.lastActive());
@@ -6149,13 +6362,17 @@ class ShellRail {
6149
6362
  ...(ngDevMode ? [{ debugName: "connectedRails" }] : /* istanbul ignore next */ []));
6150
6363
  labelKey = computed(() => this.region().dock === 'right' ? 'rail.labelRight' : 'rail.label', /* @ts-ignore */
6151
6364
  ...(ngDevMode ? [{ debugName: "labelKey" }] : /* istanbul ignore next */ []));
6365
+ menuSide = computed(() => this.region().dock === 'right' ? 'left' : 'right', /* @ts-ignore */
6366
+ ...(ngDevMode ? [{ debugName: "menuSide" }] : /* istanbul ignore next */ []));
6152
6367
  reorderable = this.features.reorder;
6153
6368
  draggable = this.features.reorder || this.features.moveItems;
6154
6369
  registered = computed(() => this.registry
6155
6370
  .railItems()
6156
6371
  .filter((item) => this.railItems.regionOf(item.id, item.rail) === this.region().id)
6157
6372
  .filter((item) => this.auth.visible(item.access))
6158
- .filter((item) => item.workspace !== undefined || this.commands.triggerable(item))
6373
+ .filter((item) => item.workspace !== undefined ||
6374
+ menuOnActivate(item) !== undefined ||
6375
+ this.commands.triggerable(item))
6159
6376
  .toSorted((a, b) => (a.order ?? 0) - (b.order ?? 0)), /* @ts-ignore */
6160
6377
  ...(ngDevMode ? [{ debugName: "registered" }] : /* istanbul ignore next */ []));
6161
6378
  items = computed(() => {
@@ -6168,16 +6385,26 @@ class ShellRail {
6168
6385
  return [...top, ...bottom];
6169
6386
  }, /* @ts-ignore */
6170
6387
  ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
6388
+ brokenPictures = signal(new Set(), /* @ts-ignore */
6389
+ ...(ngDevMode ? [{ debugName: "brokenPictures" }] : /* istanbul ignore next */ []));
6171
6390
  firstBottomId = computed(() => this.items().find((item) => item.anchor === 'bottom')?.id, /* @ts-ignore */
6172
6391
  ...(ngDevMode ? [{ debugName: "firstBottomId" }] : /* istanbul ignore next */ []));
6173
6392
  enterPredicate = (_drag, list) => list.id === this.dropListId() ? this.reorderable : this.features.moveItems;
6393
+ pictureOf(item) {
6394
+ return this.brokenPictures().has(item.id) ? undefined : item.image;
6395
+ }
6396
+ onPictureError(item) {
6397
+ this.brokenPictures.update((broken) => new Set(broken).add(item.id));
6398
+ }
6174
6399
  disabled(item) {
6175
6400
  return this.auth.disabled(item.access);
6176
6401
  }
6177
6402
  menusFor(item) {
6178
- return item.menu
6179
- ? [RAIL_ITEM_CONTEXT_MENU, item.menu]
6180
- : [RAIL_ITEM_CONTEXT_MENU];
6403
+ const own = menuOnContext(item);
6404
+ return own ? [RAIL_ITEM_CONTEXT_MENU, own] : [RAIL_ITEM_CONTEXT_MENU];
6405
+ }
6406
+ activateMenuFor(item) {
6407
+ return menuOnActivate(item);
6181
6408
  }
6182
6409
  onKeydown(event) {
6183
6410
  const dock = this.dockForChord(event);
@@ -6199,6 +6426,10 @@ class ShellRail {
6199
6426
  run(item) {
6200
6427
  if (this.disabled(item))
6201
6428
  return;
6429
+ warnMenuTriggerConflict(item);
6430
+ if (menuOnActivate(item)) {
6431
+ return;
6432
+ }
6202
6433
  const workspace = item.workspace;
6203
6434
  if (workspace !== undefined) {
6204
6435
  void this.workspaces.switchTo(workspace);
@@ -6236,7 +6467,7 @@ class ShellRail {
6236
6467
  return event.key === 'ArrowLeft' ? 'left' : null;
6237
6468
  }
6238
6469
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ShellRail, deps: [], target: i0.ɵɵFactoryTarget.Component });
6239
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: ShellRail, isStandalone: true, selector: "lw-shell-rail", inputs: { region: { classPropertyName: "region", publicName: "region", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<nav\n class=\"flex h-full w-10 shrink-0 flex-col items-center gap-1 bg-surface py-2\"\n [attr.aria-label]=\"labelKey() | transloco\"\n cdkDropList\n cdkDropListOrientation=\"vertical\"\n [id]=\"dropListId()\"\n [cdkDropListConnectedTo]=\"connectedRails()\"\n [cdkDropListDisabled]=\"!draggable\"\n [cdkDropListSortingDisabled]=\"!reorderable\"\n [cdkDropListEnterPredicate]=\"enterPredicate\"\n [cdkDropListSortPredicate]=\"sortPredicate\"\n (cdkDropListDropped)=\"onDrop($event)\"\n [lwReorderable]=\"containerId()\"\n [lwReorderableEnabled]=\"reorderable\"\n (reorder)=\"onReorder($event)\"\n [lwContextMenu]=\"railMenu\"\n>\n @for (item of items(); track item.id) {\n <button\n type=\"button\"\n cdkDrag\n [cdkDragData]=\"item.id\"\n [cdkDragDisabled]=\"!draggable\"\n [disabled]=\"disabled(item)\"\n (click)=\"run(item)\"\n (keydown)=\"onKeydown($event)\"\n [lwContextMenu]=\"menusFor(item)\"\n [lwContextMenuContext]=\"{ targetKind: 'rail-item', id: item.id, region: region().id }\"\n [attr.aria-label]=\"item.title | transloco\"\n [attr.aria-current]=\"current(item) ? 'true' : null\"\n [attr.data-rail-item]=\"item.id\"\n [attr.data-reorder-id]=\"reorderable ? item.id : null\"\n [attr.data-reorder-band]=\"item.anchor ?? 'top'\"\n [class.mt-auto]=\"item.id === firstBottomId()\"\n [class.text-content]=\"current(item)\"\n [class.bg-surface-overlay]=\"current(item)\"\n class=\"relative flex h-9 w-9 items-center justify-center rounded-lg text-content-faint transition-colors hover:bg-surface-overlay hover:text-content disabled:pointer-events-none disabled:opacity-40\"\n >\n @if (current(item)) {\n <span\n aria-hidden=\"true\"\n class=\"absolute inset-y-1 w-0.5 rounded-full bg-brand\"\n [class.left-0]=\"region().dock !== 'right'\"\n [class.right-0]=\"region().dock === 'right'\"\n ></span>\n }\n @if (item.initials) {\n <span\n aria-hidden=\"true\"\n data-testid=\"rail-initials\"\n class=\"lw-rail-initials\"\n >{{ item.initials }}</span\n >\n } @else {\n <lw-icon [name]=\"item.icon\" size=\"1rem\" />\n }\n <lw-tooltip [text]=\"item.title | transloco\" [position]=\"region().dock === 'right' ? 'left' : 'right'\" />\n </button>\n }\n</nav>\n", dependencies: [{ kind: "directive", type: Reorderable, selector: "[lwReorderable]", inputs: ["lwReorderable", "lwReorderableEnabled"], outputs: ["reorder"] }, { kind: "directive", type: CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: ContextMenuDirective, selector: "[lwContextMenu]", inputs: ["lwContextMenu", "lwContextMenuContext"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
6470
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: ShellRail, isStandalone: true, selector: "lw-shell-rail", inputs: { region: { classPropertyName: "region", publicName: "region", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<nav\n class=\"flex h-full w-10 shrink-0 flex-col items-center gap-1 bg-surface py-2\"\n [attr.aria-label]=\"labelKey() | transloco\"\n cdkDropList\n cdkDropListOrientation=\"vertical\"\n [id]=\"dropListId()\"\n [cdkDropListConnectedTo]=\"connectedRails()\"\n [cdkDropListDisabled]=\"!draggable\"\n [cdkDropListSortingDisabled]=\"!reorderable\"\n [cdkDropListEnterPredicate]=\"enterPredicate\"\n [cdkDropListSortPredicate]=\"sortPredicate\"\n (cdkDropListDropped)=\"onDrop($event)\"\n [lwReorderable]=\"containerId()\"\n [lwReorderableEnabled]=\"reorderable\"\n (reorder)=\"onReorder($event)\"\n [lwMenu]=\"railMenu\"\n>\n @for (item of items(); track item.id) {\n <button\n type=\"button\"\n cdkDrag\n [cdkDragData]=\"item.id\"\n [cdkDragDisabled]=\"!draggable\"\n [disabled]=\"disabled(item)\"\n (click)=\"run(item)\"\n (keydown)=\"onKeydown($event)\"\n [lwMenu]=\"menusFor(item)\"\n [lwMenuOnActivate]=\"activateMenuFor(item)\"\n [lwMenuHeader]=\"item.menuHeader\"\n [lwMenuSide]=\"menuSide()\"\n [lwMenuContext]=\"{ targetKind: 'rail-item', id: item.id, region: region().id }\"\n [attr.aria-label]=\"item.title | transloco\"\n [attr.aria-current]=\"current(item) ? 'true' : null\"\n [attr.data-rail-item]=\"item.id\"\n [attr.data-reorder-id]=\"reorderable ? item.id : null\"\n [attr.data-reorder-band]=\"item.anchor ?? 'top'\"\n [class.mt-auto]=\"item.id === firstBottomId()\"\n [class.text-content]=\"current(item)\"\n [class.bg-surface-overlay]=\"current(item)\"\n class=\"relative flex h-9 w-9 items-center justify-center rounded-lg text-content-faint transition-colors hover:bg-surface-overlay hover:text-content disabled:pointer-events-none disabled:opacity-40\"\n >\n @if (current(item)) {\n <span\n aria-hidden=\"true\"\n class=\"absolute inset-y-1 w-0.5 rounded-full bg-brand\"\n [class.left-0]=\"region().dock !== 'right'\"\n [class.right-0]=\"region().dock === 'right'\"\n ></span>\n }\n @if (pictureOf(item); as picture) {\n <img\n [src]=\"picture\"\n alt=\"\"\n aria-hidden=\"true\"\n data-testid=\"rail-picture\"\n class=\"lw-chrome-picture h-6 w-6\"\n (error)=\"onPictureError(item)\"\n />\n } @else if (item.initials) {\n <span\n aria-hidden=\"true\"\n data-testid=\"rail-initials\"\n class=\"lw-rail-initials\"\n >{{ item.initials }}</span\n >\n } @else {\n <lw-icon [name]=\"item.icon\" size=\"1rem\" />\n }\n <lw-tooltip [text]=\"item.title | transloco\" [position]=\"region().dock === 'right' ? 'left' : 'right'\" />\n </button>\n }\n</nav>\n", dependencies: [{ kind: "directive", type: Reorderable, selector: "[lwReorderable]", inputs: ["lwReorderable", "lwReorderableEnabled"], outputs: ["reorder"] }, { kind: "directive", type: CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: MenuTriggerDirective, selector: "[lwMenu], [lwMenuOnActivate], [lwMenuState]", inputs: ["lwMenu", "lwMenuOnActivate", "lwMenuContext", "lwMenuSide", "lwMenuHeader", "lwMenuState"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
6240
6471
  }
6241
6472
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ShellRail, decorators: [{
6242
6473
  type: Component,
@@ -6245,8 +6476,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
6245
6476
  Reorderable,
6246
6477
  CdkDropList,
6247
6478
  CdkDrag,
6248
- ContextMenuDirective,
6249
- ], schemas: [CUSTOM_ELEMENTS_SCHEMA], template: "<nav\n class=\"flex h-full w-10 shrink-0 flex-col items-center gap-1 bg-surface py-2\"\n [attr.aria-label]=\"labelKey() | transloco\"\n cdkDropList\n cdkDropListOrientation=\"vertical\"\n [id]=\"dropListId()\"\n [cdkDropListConnectedTo]=\"connectedRails()\"\n [cdkDropListDisabled]=\"!draggable\"\n [cdkDropListSortingDisabled]=\"!reorderable\"\n [cdkDropListEnterPredicate]=\"enterPredicate\"\n [cdkDropListSortPredicate]=\"sortPredicate\"\n (cdkDropListDropped)=\"onDrop($event)\"\n [lwReorderable]=\"containerId()\"\n [lwReorderableEnabled]=\"reorderable\"\n (reorder)=\"onReorder($event)\"\n [lwContextMenu]=\"railMenu\"\n>\n @for (item of items(); track item.id) {\n <button\n type=\"button\"\n cdkDrag\n [cdkDragData]=\"item.id\"\n [cdkDragDisabled]=\"!draggable\"\n [disabled]=\"disabled(item)\"\n (click)=\"run(item)\"\n (keydown)=\"onKeydown($event)\"\n [lwContextMenu]=\"menusFor(item)\"\n [lwContextMenuContext]=\"{ targetKind: 'rail-item', id: item.id, region: region().id }\"\n [attr.aria-label]=\"item.title | transloco\"\n [attr.aria-current]=\"current(item) ? 'true' : null\"\n [attr.data-rail-item]=\"item.id\"\n [attr.data-reorder-id]=\"reorderable ? item.id : null\"\n [attr.data-reorder-band]=\"item.anchor ?? 'top'\"\n [class.mt-auto]=\"item.id === firstBottomId()\"\n [class.text-content]=\"current(item)\"\n [class.bg-surface-overlay]=\"current(item)\"\n class=\"relative flex h-9 w-9 items-center justify-center rounded-lg text-content-faint transition-colors hover:bg-surface-overlay hover:text-content disabled:pointer-events-none disabled:opacity-40\"\n >\n @if (current(item)) {\n <span\n aria-hidden=\"true\"\n class=\"absolute inset-y-1 w-0.5 rounded-full bg-brand\"\n [class.left-0]=\"region().dock !== 'right'\"\n [class.right-0]=\"region().dock === 'right'\"\n ></span>\n }\n @if (item.initials) {\n <span\n aria-hidden=\"true\"\n data-testid=\"rail-initials\"\n class=\"lw-rail-initials\"\n >{{ item.initials }}</span\n >\n } @else {\n <lw-icon [name]=\"item.icon\" size=\"1rem\" />\n }\n <lw-tooltip [text]=\"item.title | transloco\" [position]=\"region().dock === 'right' ? 'left' : 'right'\" />\n </button>\n }\n</nav>\n" }]
6479
+ MenuTriggerDirective,
6480
+ ], schemas: [CUSTOM_ELEMENTS_SCHEMA], template: "<nav\n class=\"flex h-full w-10 shrink-0 flex-col items-center gap-1 bg-surface py-2\"\n [attr.aria-label]=\"labelKey() | transloco\"\n cdkDropList\n cdkDropListOrientation=\"vertical\"\n [id]=\"dropListId()\"\n [cdkDropListConnectedTo]=\"connectedRails()\"\n [cdkDropListDisabled]=\"!draggable\"\n [cdkDropListSortingDisabled]=\"!reorderable\"\n [cdkDropListEnterPredicate]=\"enterPredicate\"\n [cdkDropListSortPredicate]=\"sortPredicate\"\n (cdkDropListDropped)=\"onDrop($event)\"\n [lwReorderable]=\"containerId()\"\n [lwReorderableEnabled]=\"reorderable\"\n (reorder)=\"onReorder($event)\"\n [lwMenu]=\"railMenu\"\n>\n @for (item of items(); track item.id) {\n <button\n type=\"button\"\n cdkDrag\n [cdkDragData]=\"item.id\"\n [cdkDragDisabled]=\"!draggable\"\n [disabled]=\"disabled(item)\"\n (click)=\"run(item)\"\n (keydown)=\"onKeydown($event)\"\n [lwMenu]=\"menusFor(item)\"\n [lwMenuOnActivate]=\"activateMenuFor(item)\"\n [lwMenuHeader]=\"item.menuHeader\"\n [lwMenuSide]=\"menuSide()\"\n [lwMenuContext]=\"{ targetKind: 'rail-item', id: item.id, region: region().id }\"\n [attr.aria-label]=\"item.title | transloco\"\n [attr.aria-current]=\"current(item) ? 'true' : null\"\n [attr.data-rail-item]=\"item.id\"\n [attr.data-reorder-id]=\"reorderable ? item.id : null\"\n [attr.data-reorder-band]=\"item.anchor ?? 'top'\"\n [class.mt-auto]=\"item.id === firstBottomId()\"\n [class.text-content]=\"current(item)\"\n [class.bg-surface-overlay]=\"current(item)\"\n class=\"relative flex h-9 w-9 items-center justify-center rounded-lg text-content-faint transition-colors hover:bg-surface-overlay hover:text-content disabled:pointer-events-none disabled:opacity-40\"\n >\n @if (current(item)) {\n <span\n aria-hidden=\"true\"\n class=\"absolute inset-y-1 w-0.5 rounded-full bg-brand\"\n [class.left-0]=\"region().dock !== 'right'\"\n [class.right-0]=\"region().dock === 'right'\"\n ></span>\n }\n @if (pictureOf(item); as picture) {\n <img\n [src]=\"picture\"\n alt=\"\"\n aria-hidden=\"true\"\n data-testid=\"rail-picture\"\n class=\"lw-chrome-picture h-6 w-6\"\n (error)=\"onPictureError(item)\"\n />\n } @else if (item.initials) {\n <span\n aria-hidden=\"true\"\n data-testid=\"rail-initials\"\n class=\"lw-rail-initials\"\n >{{ item.initials }}</span\n >\n } @else {\n <lw-icon [name]=\"item.icon\" size=\"1rem\" />\n }\n <lw-tooltip [text]=\"item.title | transloco\" [position]=\"region().dock === 'right' ? 'left' : 'right'\" />\n </button>\n }\n</nav>\n" }]
6250
6481
  }], propDecorators: { region: [{ type: i0.Input, args: [{ isSignal: true, alias: "region", required: true }] }] } });
6251
6482
 
6252
6483
  const STORAGE_PREFIX$2 = 'lw.shell.view-state:';
@@ -6561,8 +6792,9 @@ class ViewInstanceSwitcher {
6561
6792
  if (!this.viewInstances.isDefault(viewId, activeId)) {
6562
6793
  entries.push({ key: 'rename', label: t('viewInstance.rename') }, { key: 'delete', label: t('viewInstance.delete') });
6563
6794
  }
6564
- const rect = event.currentTarget.getBoundingClientRect();
6565
- this.menu.openList(entries, { x: rect.left, y: rect.bottom + MENU_ANCHOR_GAP }, (key) => this.onPick(key));
6795
+ const control = event.currentTarget;
6796
+ const rect = control.getBoundingClientRect();
6797
+ this.menu.openList(entries, { x: rect.left, y: rect.bottom + MENU_ANCHOR_GAP }, (key) => this.onPick(key), control);
6566
6798
  }
6567
6799
  onPick(key) {
6568
6800
  const viewId = this.view().id;
@@ -6635,11 +6867,11 @@ class ViewInstanceSwitcher {
6635
6867
  };
6636
6868
  }
6637
6869
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ViewInstanceSwitcher, deps: [], target: i0.ɵɵFactoryTarget.Component });
6638
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.0", type: ViewInstanceSwitcher, isStandalone: true, selector: "lw-view-instance-switcher", inputs: { view: { classPropertyName: "view", publicName: "view", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { beforeSwitch: "beforeSwitch" }, host: { classAttribute: "flex min-w-0 flex-1" }, ngImport: i0, template: "<button\n type=\"button\"\n class=\"flex min-w-0 flex-1 items-center gap-1 truncate rounded px-1 py-0.5 text-left text-xs font-semibold tracking-wide text-content-faint uppercase transition-colors hover:bg-surface-overlay hover:text-content\"\n (click)=\"openSwitcher($event)\"\n [attr.aria-label]=\"'viewInstance.switcher' | transloco\"\n data-testid=\"view-switcher\"\n>\n <span class=\"truncate\">{{ activeName() || ('viewInstance.default' | transloco) }}</span>\n <lw-icon name=\"chevronDown\" size=\"0.85rem\" class=\"shrink-0\" />\n</button>\n", dependencies: [{ kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
6870
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.0", type: ViewInstanceSwitcher, isStandalone: true, selector: "lw-view-instance-switcher", inputs: { view: { classPropertyName: "view", publicName: "view", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { beforeSwitch: "beforeSwitch" }, host: { classAttribute: "flex min-w-0 flex-1" }, ngImport: i0, template: "<button\n type=\"button\"\n class=\"flex min-w-0 flex-1 items-center gap-1 truncate rounded px-1 py-0.5 text-left text-xs font-semibold tracking-wide text-content-faint uppercase transition-colors hover:bg-surface-overlay hover:text-content\"\n (click)=\"openSwitcher($event)\"\n lwMenuState\n [attr.aria-label]=\"'viewInstance.switcher' | transloco\"\n data-testid=\"view-switcher\"\n>\n <span class=\"truncate\">{{ activeName() || ('viewInstance.default' | transloco) }}</span>\n <lw-icon name=\"chevronDown\" size=\"0.85rem\" class=\"shrink-0\" />\n</button>\n", dependencies: [{ kind: "directive", type: MenuTriggerDirective, selector: "[lwMenu], [lwMenuOnActivate], [lwMenuState]", inputs: ["lwMenu", "lwMenuOnActivate", "lwMenuContext", "lwMenuSide", "lwMenuHeader", "lwMenuState"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
6639
6871
  }
6640
6872
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ViewInstanceSwitcher, decorators: [{
6641
6873
  type: Component,
6642
- args: [{ selector: 'lw-view-instance-switcher', imports: [TranslocoPipe], schemas: [CUSTOM_ELEMENTS_SCHEMA], host: { class: 'flex min-w-0 flex-1' }, template: "<button\n type=\"button\"\n class=\"flex min-w-0 flex-1 items-center gap-1 truncate rounded px-1 py-0.5 text-left text-xs font-semibold tracking-wide text-content-faint uppercase transition-colors hover:bg-surface-overlay hover:text-content\"\n (click)=\"openSwitcher($event)\"\n [attr.aria-label]=\"'viewInstance.switcher' | transloco\"\n data-testid=\"view-switcher\"\n>\n <span class=\"truncate\">{{ activeName() || ('viewInstance.default' | transloco) }}</span>\n <lw-icon name=\"chevronDown\" size=\"0.85rem\" class=\"shrink-0\" />\n</button>\n" }]
6874
+ args: [{ selector: 'lw-view-instance-switcher', imports: [TranslocoPipe, MenuTriggerDirective], schemas: [CUSTOM_ELEMENTS_SCHEMA], host: { class: 'flex min-w-0 flex-1' }, template: "<button\n type=\"button\"\n class=\"flex min-w-0 flex-1 items-center gap-1 truncate rounded px-1 py-0.5 text-left text-xs font-semibold tracking-wide text-content-faint uppercase transition-colors hover:bg-surface-overlay hover:text-content\"\n (click)=\"openSwitcher($event)\"\n lwMenuState\n [attr.aria-label]=\"'viewInstance.switcher' | transloco\"\n data-testid=\"view-switcher\"\n>\n <span class=\"truncate\">{{ activeName() || ('viewInstance.default' | transloco) }}</span>\n <lw-icon name=\"chevronDown\" size=\"0.85rem\" class=\"shrink-0\" />\n</button>\n" }]
6643
6875
  }], propDecorators: { view: [{ type: i0.Input, args: [{ isSignal: true, alias: "view", required: true }] }], beforeSwitch: [{ type: i0.Output, args: ["beforeSwitch"] }] } });
6644
6876
 
6645
6877
  class PaneChromeService {
@@ -7166,7 +7398,8 @@ class PaneTabStrip {
7166
7398
  this.paneDrag.stop();
7167
7399
  }
7168
7400
  openOverflow(event) {
7169
- const anchor = event.currentTarget.getBoundingClientRect();
7401
+ const control = event.currentTarget;
7402
+ const anchor = control.getBoundingClientRect();
7170
7403
  const entries = this.tabs().map((tab) => ({
7171
7404
  key: tab.path,
7172
7405
  label: this.label(tab),
@@ -7178,7 +7411,7 @@ class PaneTabStrip {
7178
7411
  if (tab) {
7179
7412
  this.selectTab.emit(tab);
7180
7413
  }
7181
- });
7414
+ }, control);
7182
7415
  }
7183
7416
  onSelectTab(tab) {
7184
7417
  if (tab.path !== this.activeId()) {
@@ -7291,19 +7524,19 @@ class PaneTabStrip {
7291
7524
  return resolveTitle(tab, (key) => this.transloco.translate(key));
7292
7525
  }
7293
7526
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: PaneTabStrip, deps: [], target: i0.ɵɵFactoryTarget.Component });
7294
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: PaneTabStrip, isStandalone: true, selector: "lw-pane-tab-strip", inputs: { tabs: { classPropertyName: "tabs", publicName: "tabs", isSignal: true, isRequired: true, transformFunction: null }, activeId: { classPropertyName: "activeId", publicName: "activeId", isSignal: true, isRequired: true, transformFunction: null }, reorderable: { classPropertyName: "reorderable", publicName: "reorderable", isSignal: true, isRequired: false, transformFunction: null }, draggable: { classPropertyName: "draggable", publicName: "draggable", isSignal: true, isRequired: false, transformFunction: null }, acceptsTabs: { classPropertyName: "acceptsTabs", publicName: "acceptsTabs", isSignal: true, isRequired: false, transformFunction: null }, source: { classPropertyName: "source", publicName: "source", isSignal: true, isRequired: true, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, urlDriven: { classPropertyName: "urlDriven", publicName: "urlDriven", isSignal: true, isRequired: false, transformFunction: null }, contextMenuSlot: { classPropertyName: "contextMenuSlot", publicName: "contextMenuSlot", isSignal: true, isRequired: false, transformFunction: null }, viewContextMenuSlot: { classPropertyName: "viewContextMenuSlot", publicName: "viewContextMenuSlot", isSignal: true, isRequired: false, transformFunction: null }, contextGroup: { classPropertyName: "contextGroup", publicName: "contextGroup", isSignal: true, isRequired: false, transformFunction: null }, overflow: { classPropertyName: "overflow", publicName: "overflow", isSignal: true, isRequired: false, transformFunction: null }, canAddTab: { classPropertyName: "canAddTab", publicName: "canAddTab", isSignal: true, isRequired: false, transformFunction: null }, paneActions: { classPropertyName: "paneActions", publicName: "paneActions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectTab: "selectTab", escalate: "escalate", closeTab: "closeTab", unpinTab: "unpinTab", reorderTabs: "reorderTabs", runAction: "runAction", addTab: "addTab", revealRequest: "revealRequest" }, viewQueries: [{ propertyName: "strip", first: true, predicate: ["tabStrip"], descendants: true, isSignal: true }], ngImport: i0, template: "<div [class]=\"bandClass()\">\n <div\n #tabStrip\n class=\"flex min-w-0 flex-1 items-center gap-0.5 overflow-hidden\"\n [class.px-2]=\"!icons()\"\n [class.px-1]=\"icons()\"\n role=\"tablist\"\n cdkDropList\n cdkDropListOrientation=\"horizontal\"\n [id]=\"stripId()\"\n [cdkDropListSortingDisabled]=\"!reorderable()\"\n [cdkDropListEnterPredicate]=\"enterPredicate\"\n [cdkDropListConnectedTo]=\"dragZoneIds()\"\n [cdkDropListSortPredicate]=\"sortPredicate\"\n (cdkDropListDropped)=\"onDrop($event)\"\n [lwReorderable]=\"stripId()\"\n [lwReorderableEnabled]=\"reorderable()\"\n (reorder)=\"reorderTabs.emit($event)\"\n >\n @for (tab of tabs(); track tab.path) {\n @let title = tab.literalTitle ? tab.title : (tab.title | transloco);\n @if (icons()) {\n <button\n type=\"button\"\n role=\"tab\"\n cdkDrag\n [cdkDragData]=\"tab.path\"\n [cdkDragDisabled]=\"!canDrag(tab)\"\n (cdkDragStarted)=\"onDragStarted(tab)\"\n (cdkDragEnded)=\"onDragEnded()\"\n [lwContextMenu]=\"menuSlotFor(tab)\"\n [lwContextMenuContext]=\"tabContext(tab)\"\n [attr.aria-selected]=\"isActive(tab)\"\n [attr.aria-label]=\"title\"\n [attr.data-reorder-id]=\"canReorder(tab) ? tab.path : null\"\n [attr.data-reorder-band]=\"tab.pinned ? 'pinned' : 'dynamic'\"\n [attr.data-tab-path]=\"tab.path\"\n (click)=\"onSelectTab(tab)\"\n (dblclick)=\"escalate.emit(tab)\"\n class=\"lw-icon-btn h-10 w-10 rounded-md md:h-7 md:w-7\"\n [class.bg-surface-overlay]=\"isActive(tab)\"\n [class.text-content]=\"isActive(tab)\"\n >\n <lw-icon [name]=\"tab.icon ?? 'document'\" size=\"1rem\" />\n <lw-tooltip [text]=\"title\" position=\"bottom\" />\n </button>\n } @else {\n <div\n class=\"flex max-w-52 min-w-28 items-center border-b-2 transition-colors\"\n cdkDrag\n [cdkDragData]=\"tab.path\"\n [cdkDragDisabled]=\"!canDrag(tab)\"\n (cdkDragStarted)=\"onDragStarted(tab)\"\n (cdkDragEnded)=\"onDragEnded()\"\n [class.border-brand]=\"isActive(tab) && urlDriven()\"\n [class.border-content-faint]=\"isActive(tab) && !urlDriven()\"\n [class.border-transparent]=\"!isActive(tab)\"\n [lwContextMenu]=\"menuSlotFor(tab)\"\n [lwContextMenuContext]=\"tabContext(tab)\"\n >\n <button\n type=\"button\"\n role=\"tab\"\n cdkDragHandle\n [attr.aria-selected]=\"isActive(tab)\"\n [attr.aria-label]=\"title\"\n [attr.data-reorder-id]=\"canReorder(tab) ? tab.path : null\"\n [attr.data-reorder-band]=\"tab.pinned ? 'pinned' : 'dynamic'\"\n [attr.data-tab-path]=\"tab.path\"\n [attr.aria-keyshortcuts]=\"tab.closable && !tab.pinned ? 'Delete' : null\"\n (click)=\"onSelectTab(tab)\"\n (dblclick)=\"escalate.emit(tab)\"\n (keydown)=\"onTabKeydown($event, tab)\"\n class=\"relative flex min-w-0 items-center gap-1.5 py-2 pr-1 pl-3 text-sm transition-colors\"\n [class.text-content]=\"isActive(tab)\"\n [class.text-content-faint]=\"!isActive(tab)\"\n [class.italic]=\"tab.preview\"\n >\n @if (tab.preview) {\n <lw-icon name=\"preview\" size=\"1rem\" />\n } @else if (tab.icon) {\n <lw-icon [name]=\"tab.icon\" size=\"1rem\" />\n }\n <span class=\"min-w-0 truncate\">{{ title }}</span>\n\n <lw-tooltip\n [text]=\"\n tab.preview && escalatable\n ? title + ' \u2014 ' + ('content.previewHint' | transloco)\n : title\n \"\n position=\"bottom\"\n />\n </button>\n\n @if (tab.pinned) {\n <span\n aria-hidden=\"true\"\n data-testid=\"tab-unpin\"\n (click)=\"onUnpin($event, tab)\"\n class=\"relative mr-1 flex h-6 w-6 cursor-pointer items-center justify-center rounded text-brand opacity-90 transition-colors hover:bg-surface-overlay hover:opacity-100\"\n >\n <lw-icon name=\"pin\" size=\"0.85rem\" />\n <lw-tooltip [text]=\"'content.pinnedHint' | transloco\" position=\"bottom\" />\n </span>\n } @else if (tab.closable) {\n <span\n aria-hidden=\"true\"\n data-testid=\"tab-close\"\n (click)=\"onClose($event, tab)\"\n class=\"lw-icon-btn mr-1 h-6 w-6 cursor-pointer opacity-70 hover:opacity-100\"\n >\n <lw-icon name=\"close\" size=\"0.85rem\" />\n <lw-tooltip [text]=\"'content.closeTab' | transloco\" position=\"bottom\" />\n </span>\n }\n </div>\n }\n }\n </div>\n\n @if (canAddTab()) {\n <button\n type=\"button\"\n (click)=\"addTab.emit($event)\"\n class=\"lw-icon-btn w-8\"\n [class.self-stretch]=\"icons()\"\n [attr.aria-label]=\"'content.split.newTab' | transloco\"\n data-testid=\"pane-add-tab\"\n >\n <lw-icon name=\"add\" size=\"1rem\" />\n <lw-tooltip [text]=\"'content.split.newTab' | transloco\" position=\"bottom\" />\n </button>\n }\n\n @if (overflow() && overflowing()) {\n <button\n type=\"button\"\n (click)=\"openOverflow($event)\"\n class=\"lw-icon-btn w-8\"\n [class.self-stretch]=\"icons()\"\n [attr.aria-label]=\"'content.moreTabs' | transloco\"\n >\n <lw-icon name=\"chevronDown\" size=\"1rem\" />\n <lw-tooltip [text]=\"'content.moreTabs' | transloco\" position=\"bottom\" />\n </button>\n }\n\n @if (paneActions(); as actions) {\n <ng-container [ngTemplateOutlet]=\"actions\" />\n }\n @if (activeActions().length) {\n <div class=\"flex shrink-0 items-center gap-0.5 border-l border-border px-2\">\n @for (action of activeActions(); track action.id) {\n <button\n type=\"button\"\n (click)=\"runAction.emit(action)\"\n class=\"lw-icon-btn h-8 w-8 rounded-md\"\n [attr.aria-label]=\"action.title | transloco\"\n >\n <lw-icon [name]=\"action.icon\" size=\"1rem\" />\n <lw-tooltip [text]=\"action.title | transloco\" position=\"bottom\" />\n </button>\n }\n </div>\n }\n</div>\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: ContextMenuDirective, selector: "[lwContextMenu]", inputs: ["lwContextMenu", "lwContextMenuContext"] }, { kind: "directive", type: Reorderable, selector: "[lwReorderable]", inputs: ["lwReorderable", "lwReorderableEnabled"], outputs: ["reorder"] }, { kind: "directive", type: CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
7527
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: PaneTabStrip, isStandalone: true, selector: "lw-pane-tab-strip", inputs: { tabs: { classPropertyName: "tabs", publicName: "tabs", isSignal: true, isRequired: true, transformFunction: null }, activeId: { classPropertyName: "activeId", publicName: "activeId", isSignal: true, isRequired: true, transformFunction: null }, reorderable: { classPropertyName: "reorderable", publicName: "reorderable", isSignal: true, isRequired: false, transformFunction: null }, draggable: { classPropertyName: "draggable", publicName: "draggable", isSignal: true, isRequired: false, transformFunction: null }, acceptsTabs: { classPropertyName: "acceptsTabs", publicName: "acceptsTabs", isSignal: true, isRequired: false, transformFunction: null }, source: { classPropertyName: "source", publicName: "source", isSignal: true, isRequired: true, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, urlDriven: { classPropertyName: "urlDriven", publicName: "urlDriven", isSignal: true, isRequired: false, transformFunction: null }, contextMenuSlot: { classPropertyName: "contextMenuSlot", publicName: "contextMenuSlot", isSignal: true, isRequired: false, transformFunction: null }, viewContextMenuSlot: { classPropertyName: "viewContextMenuSlot", publicName: "viewContextMenuSlot", isSignal: true, isRequired: false, transformFunction: null }, contextGroup: { classPropertyName: "contextGroup", publicName: "contextGroup", isSignal: true, isRequired: false, transformFunction: null }, overflow: { classPropertyName: "overflow", publicName: "overflow", isSignal: true, isRequired: false, transformFunction: null }, canAddTab: { classPropertyName: "canAddTab", publicName: "canAddTab", isSignal: true, isRequired: false, transformFunction: null }, paneActions: { classPropertyName: "paneActions", publicName: "paneActions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectTab: "selectTab", escalate: "escalate", closeTab: "closeTab", unpinTab: "unpinTab", reorderTabs: "reorderTabs", runAction: "runAction", addTab: "addTab", revealRequest: "revealRequest" }, viewQueries: [{ propertyName: "strip", first: true, predicate: ["tabStrip"], descendants: true, isSignal: true }], ngImport: i0, template: "<div [class]=\"bandClass()\">\n <div\n #tabStrip\n class=\"flex min-w-0 flex-1 items-center gap-0.5 overflow-hidden\"\n [class.px-2]=\"!icons()\"\n [class.px-1]=\"icons()\"\n role=\"tablist\"\n cdkDropList\n cdkDropListOrientation=\"horizontal\"\n [id]=\"stripId()\"\n [cdkDropListSortingDisabled]=\"!reorderable()\"\n [cdkDropListEnterPredicate]=\"enterPredicate\"\n [cdkDropListConnectedTo]=\"dragZoneIds()\"\n [cdkDropListSortPredicate]=\"sortPredicate\"\n (cdkDropListDropped)=\"onDrop($event)\"\n [lwReorderable]=\"stripId()\"\n [lwReorderableEnabled]=\"reorderable()\"\n (reorder)=\"reorderTabs.emit($event)\"\n >\n @for (tab of tabs(); track tab.path) {\n @let title = tab.literalTitle ? tab.title : (tab.title | transloco);\n @if (icons()) {\n <button\n type=\"button\"\n role=\"tab\"\n cdkDrag\n [cdkDragData]=\"tab.path\"\n [cdkDragDisabled]=\"!canDrag(tab)\"\n (cdkDragStarted)=\"onDragStarted(tab)\"\n (cdkDragEnded)=\"onDragEnded()\"\n [lwMenu]=\"menuSlotFor(tab)\"\n [lwMenuContext]=\"tabContext(tab)\"\n [attr.aria-selected]=\"isActive(tab)\"\n [attr.aria-label]=\"title\"\n [attr.data-reorder-id]=\"canReorder(tab) ? tab.path : null\"\n [attr.data-reorder-band]=\"tab.pinned ? 'pinned' : 'dynamic'\"\n [attr.data-tab-path]=\"tab.path\"\n (click)=\"onSelectTab(tab)\"\n (dblclick)=\"escalate.emit(tab)\"\n class=\"lw-icon-btn h-10 w-10 rounded-md md:h-7 md:w-7\"\n [class.bg-surface-overlay]=\"isActive(tab)\"\n [class.text-content]=\"isActive(tab)\"\n >\n <lw-icon [name]=\"tab.icon ?? 'document'\" size=\"1rem\" />\n <lw-tooltip [text]=\"title\" position=\"bottom\" />\n </button>\n } @else {\n <div\n class=\"flex max-w-52 min-w-28 items-center border-b-2 transition-colors\"\n cdkDrag\n [cdkDragData]=\"tab.path\"\n [cdkDragDisabled]=\"!canDrag(tab)\"\n (cdkDragStarted)=\"onDragStarted(tab)\"\n (cdkDragEnded)=\"onDragEnded()\"\n [class.border-brand]=\"isActive(tab) && urlDriven()\"\n [class.border-content-faint]=\"isActive(tab) && !urlDriven()\"\n [class.border-transparent]=\"!isActive(tab)\"\n [lwMenu]=\"menuSlotFor(tab)\"\n [lwMenuContext]=\"tabContext(tab)\"\n >\n <button\n type=\"button\"\n role=\"tab\"\n cdkDragHandle\n [attr.aria-selected]=\"isActive(tab)\"\n [attr.aria-label]=\"title\"\n [attr.data-reorder-id]=\"canReorder(tab) ? tab.path : null\"\n [attr.data-reorder-band]=\"tab.pinned ? 'pinned' : 'dynamic'\"\n [attr.data-tab-path]=\"tab.path\"\n [attr.aria-keyshortcuts]=\"tab.closable && !tab.pinned ? 'Delete' : null\"\n (click)=\"onSelectTab(tab)\"\n (dblclick)=\"escalate.emit(tab)\"\n (keydown)=\"onTabKeydown($event, tab)\"\n class=\"relative flex min-w-0 items-center gap-1.5 py-2 pr-1 pl-3 text-sm transition-colors\"\n [class.text-content]=\"isActive(tab)\"\n [class.text-content-faint]=\"!isActive(tab)\"\n [class.italic]=\"tab.preview\"\n >\n @if (tab.preview) {\n <lw-icon name=\"preview\" size=\"1rem\" />\n } @else if (tab.icon) {\n <lw-icon [name]=\"tab.icon\" size=\"1rem\" />\n }\n <span class=\"min-w-0 truncate\">{{ title }}</span>\n\n <lw-tooltip\n [text]=\"\n tab.preview && escalatable\n ? title + ' \u2014 ' + ('content.previewHint' | transloco)\n : title\n \"\n position=\"bottom\"\n />\n </button>\n\n @if (tab.pinned) {\n <span\n aria-hidden=\"true\"\n data-testid=\"tab-unpin\"\n (click)=\"onUnpin($event, tab)\"\n class=\"relative mr-1 flex h-6 w-6 cursor-pointer items-center justify-center rounded text-brand opacity-90 transition-colors hover:bg-surface-overlay hover:opacity-100\"\n >\n <lw-icon name=\"pin\" size=\"0.85rem\" />\n <lw-tooltip [text]=\"'content.pinnedHint' | transloco\" position=\"bottom\" />\n </span>\n } @else if (tab.closable) {\n <span\n aria-hidden=\"true\"\n data-testid=\"tab-close\"\n (click)=\"onClose($event, tab)\"\n class=\"lw-icon-btn mr-1 h-6 w-6 cursor-pointer opacity-70 hover:opacity-100\"\n >\n <lw-icon name=\"close\" size=\"0.85rem\" />\n <lw-tooltip [text]=\"'content.closeTab' | transloco\" position=\"bottom\" />\n </span>\n }\n </div>\n }\n }\n </div>\n\n @if (canAddTab()) {\n <button\n type=\"button\"\n (click)=\"addTab.emit($event)\"\n lwMenuState\n class=\"lw-icon-btn w-8\"\n [class.self-stretch]=\"icons()\"\n [attr.aria-label]=\"'content.split.newTab' | transloco\"\n data-testid=\"pane-add-tab\"\n >\n <lw-icon name=\"add\" size=\"1rem\" />\n <lw-tooltip [text]=\"'content.split.newTab' | transloco\" position=\"bottom\" />\n </button>\n }\n\n @if (overflow() && overflowing()) {\n <button\n type=\"button\"\n (click)=\"openOverflow($event)\"\n lwMenuState\n class=\"lw-icon-btn w-8\"\n [class.self-stretch]=\"icons()\"\n [attr.aria-label]=\"'content.moreTabs' | transloco\"\n >\n <lw-icon name=\"chevronDown\" size=\"1rem\" />\n <lw-tooltip [text]=\"'content.moreTabs' | transloco\" position=\"bottom\" />\n </button>\n }\n\n @if (paneActions(); as actions) {\n <ng-container [ngTemplateOutlet]=\"actions\" />\n }\n @if (activeActions().length) {\n <div class=\"flex shrink-0 items-center gap-0.5 border-l border-border px-2\">\n @for (action of activeActions(); track action.id) {\n <button\n type=\"button\"\n (click)=\"runAction.emit(action)\"\n class=\"lw-icon-btn h-8 w-8 rounded-md\"\n [attr.aria-label]=\"action.title | transloco\"\n >\n <lw-icon [name]=\"action.icon\" size=\"1rem\" />\n <lw-tooltip [text]=\"action.title | transloco\" position=\"bottom\" />\n </button>\n }\n </div>\n }\n</div>\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: MenuTriggerDirective, selector: "[lwMenu], [lwMenuOnActivate], [lwMenuState]", inputs: ["lwMenu", "lwMenuOnActivate", "lwMenuContext", "lwMenuSide", "lwMenuHeader", "lwMenuState"] }, { kind: "directive", type: Reorderable, selector: "[lwReorderable]", inputs: ["lwReorderable", "lwReorderableEnabled"], outputs: ["reorder"] }, { kind: "directive", type: CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
7295
7528
  }
7296
7529
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: PaneTabStrip, decorators: [{
7297
7530
  type: Component,
7298
7531
  args: [{ selector: 'lw-pane-tab-strip', imports: [
7299
7532
  NgTemplateOutlet,
7300
7533
  TranslocoPipe,
7301
- ContextMenuDirective,
7534
+ MenuTriggerDirective,
7302
7535
  Reorderable,
7303
7536
  CdkDropList,
7304
7537
  CdkDrag,
7305
7538
  CdkDragHandle,
7306
- ], schemas: [CUSTOM_ELEMENTS_SCHEMA], template: "<div [class]=\"bandClass()\">\n <div\n #tabStrip\n class=\"flex min-w-0 flex-1 items-center gap-0.5 overflow-hidden\"\n [class.px-2]=\"!icons()\"\n [class.px-1]=\"icons()\"\n role=\"tablist\"\n cdkDropList\n cdkDropListOrientation=\"horizontal\"\n [id]=\"stripId()\"\n [cdkDropListSortingDisabled]=\"!reorderable()\"\n [cdkDropListEnterPredicate]=\"enterPredicate\"\n [cdkDropListConnectedTo]=\"dragZoneIds()\"\n [cdkDropListSortPredicate]=\"sortPredicate\"\n (cdkDropListDropped)=\"onDrop($event)\"\n [lwReorderable]=\"stripId()\"\n [lwReorderableEnabled]=\"reorderable()\"\n (reorder)=\"reorderTabs.emit($event)\"\n >\n @for (tab of tabs(); track tab.path) {\n @let title = tab.literalTitle ? tab.title : (tab.title | transloco);\n @if (icons()) {\n <button\n type=\"button\"\n role=\"tab\"\n cdkDrag\n [cdkDragData]=\"tab.path\"\n [cdkDragDisabled]=\"!canDrag(tab)\"\n (cdkDragStarted)=\"onDragStarted(tab)\"\n (cdkDragEnded)=\"onDragEnded()\"\n [lwContextMenu]=\"menuSlotFor(tab)\"\n [lwContextMenuContext]=\"tabContext(tab)\"\n [attr.aria-selected]=\"isActive(tab)\"\n [attr.aria-label]=\"title\"\n [attr.data-reorder-id]=\"canReorder(tab) ? tab.path : null\"\n [attr.data-reorder-band]=\"tab.pinned ? 'pinned' : 'dynamic'\"\n [attr.data-tab-path]=\"tab.path\"\n (click)=\"onSelectTab(tab)\"\n (dblclick)=\"escalate.emit(tab)\"\n class=\"lw-icon-btn h-10 w-10 rounded-md md:h-7 md:w-7\"\n [class.bg-surface-overlay]=\"isActive(tab)\"\n [class.text-content]=\"isActive(tab)\"\n >\n <lw-icon [name]=\"tab.icon ?? 'document'\" size=\"1rem\" />\n <lw-tooltip [text]=\"title\" position=\"bottom\" />\n </button>\n } @else {\n <div\n class=\"flex max-w-52 min-w-28 items-center border-b-2 transition-colors\"\n cdkDrag\n [cdkDragData]=\"tab.path\"\n [cdkDragDisabled]=\"!canDrag(tab)\"\n (cdkDragStarted)=\"onDragStarted(tab)\"\n (cdkDragEnded)=\"onDragEnded()\"\n [class.border-brand]=\"isActive(tab) && urlDriven()\"\n [class.border-content-faint]=\"isActive(tab) && !urlDriven()\"\n [class.border-transparent]=\"!isActive(tab)\"\n [lwContextMenu]=\"menuSlotFor(tab)\"\n [lwContextMenuContext]=\"tabContext(tab)\"\n >\n <button\n type=\"button\"\n role=\"tab\"\n cdkDragHandle\n [attr.aria-selected]=\"isActive(tab)\"\n [attr.aria-label]=\"title\"\n [attr.data-reorder-id]=\"canReorder(tab) ? tab.path : null\"\n [attr.data-reorder-band]=\"tab.pinned ? 'pinned' : 'dynamic'\"\n [attr.data-tab-path]=\"tab.path\"\n [attr.aria-keyshortcuts]=\"tab.closable && !tab.pinned ? 'Delete' : null\"\n (click)=\"onSelectTab(tab)\"\n (dblclick)=\"escalate.emit(tab)\"\n (keydown)=\"onTabKeydown($event, tab)\"\n class=\"relative flex min-w-0 items-center gap-1.5 py-2 pr-1 pl-3 text-sm transition-colors\"\n [class.text-content]=\"isActive(tab)\"\n [class.text-content-faint]=\"!isActive(tab)\"\n [class.italic]=\"tab.preview\"\n >\n @if (tab.preview) {\n <lw-icon name=\"preview\" size=\"1rem\" />\n } @else if (tab.icon) {\n <lw-icon [name]=\"tab.icon\" size=\"1rem\" />\n }\n <span class=\"min-w-0 truncate\">{{ title }}</span>\n\n <lw-tooltip\n [text]=\"\n tab.preview && escalatable\n ? title + ' \u2014 ' + ('content.previewHint' | transloco)\n : title\n \"\n position=\"bottom\"\n />\n </button>\n\n @if (tab.pinned) {\n <span\n aria-hidden=\"true\"\n data-testid=\"tab-unpin\"\n (click)=\"onUnpin($event, tab)\"\n class=\"relative mr-1 flex h-6 w-6 cursor-pointer items-center justify-center rounded text-brand opacity-90 transition-colors hover:bg-surface-overlay hover:opacity-100\"\n >\n <lw-icon name=\"pin\" size=\"0.85rem\" />\n <lw-tooltip [text]=\"'content.pinnedHint' | transloco\" position=\"bottom\" />\n </span>\n } @else if (tab.closable) {\n <span\n aria-hidden=\"true\"\n data-testid=\"tab-close\"\n (click)=\"onClose($event, tab)\"\n class=\"lw-icon-btn mr-1 h-6 w-6 cursor-pointer opacity-70 hover:opacity-100\"\n >\n <lw-icon name=\"close\" size=\"0.85rem\" />\n <lw-tooltip [text]=\"'content.closeTab' | transloco\" position=\"bottom\" />\n </span>\n }\n </div>\n }\n }\n </div>\n\n @if (canAddTab()) {\n <button\n type=\"button\"\n (click)=\"addTab.emit($event)\"\n class=\"lw-icon-btn w-8\"\n [class.self-stretch]=\"icons()\"\n [attr.aria-label]=\"'content.split.newTab' | transloco\"\n data-testid=\"pane-add-tab\"\n >\n <lw-icon name=\"add\" size=\"1rem\" />\n <lw-tooltip [text]=\"'content.split.newTab' | transloco\" position=\"bottom\" />\n </button>\n }\n\n @if (overflow() && overflowing()) {\n <button\n type=\"button\"\n (click)=\"openOverflow($event)\"\n class=\"lw-icon-btn w-8\"\n [class.self-stretch]=\"icons()\"\n [attr.aria-label]=\"'content.moreTabs' | transloco\"\n >\n <lw-icon name=\"chevronDown\" size=\"1rem\" />\n <lw-tooltip [text]=\"'content.moreTabs' | transloco\" position=\"bottom\" />\n </button>\n }\n\n @if (paneActions(); as actions) {\n <ng-container [ngTemplateOutlet]=\"actions\" />\n }\n @if (activeActions().length) {\n <div class=\"flex shrink-0 items-center gap-0.5 border-l border-border px-2\">\n @for (action of activeActions(); track action.id) {\n <button\n type=\"button\"\n (click)=\"runAction.emit(action)\"\n class=\"lw-icon-btn h-8 w-8 rounded-md\"\n [attr.aria-label]=\"action.title | transloco\"\n >\n <lw-icon [name]=\"action.icon\" size=\"1rem\" />\n <lw-tooltip [text]=\"action.title | transloco\" position=\"bottom\" />\n </button>\n }\n </div>\n }\n</div>\n" }]
7539
+ ], schemas: [CUSTOM_ELEMENTS_SCHEMA], template: "<div [class]=\"bandClass()\">\n <div\n #tabStrip\n class=\"flex min-w-0 flex-1 items-center gap-0.5 overflow-hidden\"\n [class.px-2]=\"!icons()\"\n [class.px-1]=\"icons()\"\n role=\"tablist\"\n cdkDropList\n cdkDropListOrientation=\"horizontal\"\n [id]=\"stripId()\"\n [cdkDropListSortingDisabled]=\"!reorderable()\"\n [cdkDropListEnterPredicate]=\"enterPredicate\"\n [cdkDropListConnectedTo]=\"dragZoneIds()\"\n [cdkDropListSortPredicate]=\"sortPredicate\"\n (cdkDropListDropped)=\"onDrop($event)\"\n [lwReorderable]=\"stripId()\"\n [lwReorderableEnabled]=\"reorderable()\"\n (reorder)=\"reorderTabs.emit($event)\"\n >\n @for (tab of tabs(); track tab.path) {\n @let title = tab.literalTitle ? tab.title : (tab.title | transloco);\n @if (icons()) {\n <button\n type=\"button\"\n role=\"tab\"\n cdkDrag\n [cdkDragData]=\"tab.path\"\n [cdkDragDisabled]=\"!canDrag(tab)\"\n (cdkDragStarted)=\"onDragStarted(tab)\"\n (cdkDragEnded)=\"onDragEnded()\"\n [lwMenu]=\"menuSlotFor(tab)\"\n [lwMenuContext]=\"tabContext(tab)\"\n [attr.aria-selected]=\"isActive(tab)\"\n [attr.aria-label]=\"title\"\n [attr.data-reorder-id]=\"canReorder(tab) ? tab.path : null\"\n [attr.data-reorder-band]=\"tab.pinned ? 'pinned' : 'dynamic'\"\n [attr.data-tab-path]=\"tab.path\"\n (click)=\"onSelectTab(tab)\"\n (dblclick)=\"escalate.emit(tab)\"\n class=\"lw-icon-btn h-10 w-10 rounded-md md:h-7 md:w-7\"\n [class.bg-surface-overlay]=\"isActive(tab)\"\n [class.text-content]=\"isActive(tab)\"\n >\n <lw-icon [name]=\"tab.icon ?? 'document'\" size=\"1rem\" />\n <lw-tooltip [text]=\"title\" position=\"bottom\" />\n </button>\n } @else {\n <div\n class=\"flex max-w-52 min-w-28 items-center border-b-2 transition-colors\"\n cdkDrag\n [cdkDragData]=\"tab.path\"\n [cdkDragDisabled]=\"!canDrag(tab)\"\n (cdkDragStarted)=\"onDragStarted(tab)\"\n (cdkDragEnded)=\"onDragEnded()\"\n [class.border-brand]=\"isActive(tab) && urlDriven()\"\n [class.border-content-faint]=\"isActive(tab) && !urlDriven()\"\n [class.border-transparent]=\"!isActive(tab)\"\n [lwMenu]=\"menuSlotFor(tab)\"\n [lwMenuContext]=\"tabContext(tab)\"\n >\n <button\n type=\"button\"\n role=\"tab\"\n cdkDragHandle\n [attr.aria-selected]=\"isActive(tab)\"\n [attr.aria-label]=\"title\"\n [attr.data-reorder-id]=\"canReorder(tab) ? tab.path : null\"\n [attr.data-reorder-band]=\"tab.pinned ? 'pinned' : 'dynamic'\"\n [attr.data-tab-path]=\"tab.path\"\n [attr.aria-keyshortcuts]=\"tab.closable && !tab.pinned ? 'Delete' : null\"\n (click)=\"onSelectTab(tab)\"\n (dblclick)=\"escalate.emit(tab)\"\n (keydown)=\"onTabKeydown($event, tab)\"\n class=\"relative flex min-w-0 items-center gap-1.5 py-2 pr-1 pl-3 text-sm transition-colors\"\n [class.text-content]=\"isActive(tab)\"\n [class.text-content-faint]=\"!isActive(tab)\"\n [class.italic]=\"tab.preview\"\n >\n @if (tab.preview) {\n <lw-icon name=\"preview\" size=\"1rem\" />\n } @else if (tab.icon) {\n <lw-icon [name]=\"tab.icon\" size=\"1rem\" />\n }\n <span class=\"min-w-0 truncate\">{{ title }}</span>\n\n <lw-tooltip\n [text]=\"\n tab.preview && escalatable\n ? title + ' \u2014 ' + ('content.previewHint' | transloco)\n : title\n \"\n position=\"bottom\"\n />\n </button>\n\n @if (tab.pinned) {\n <span\n aria-hidden=\"true\"\n data-testid=\"tab-unpin\"\n (click)=\"onUnpin($event, tab)\"\n class=\"relative mr-1 flex h-6 w-6 cursor-pointer items-center justify-center rounded text-brand opacity-90 transition-colors hover:bg-surface-overlay hover:opacity-100\"\n >\n <lw-icon name=\"pin\" size=\"0.85rem\" />\n <lw-tooltip [text]=\"'content.pinnedHint' | transloco\" position=\"bottom\" />\n </span>\n } @else if (tab.closable) {\n <span\n aria-hidden=\"true\"\n data-testid=\"tab-close\"\n (click)=\"onClose($event, tab)\"\n class=\"lw-icon-btn mr-1 h-6 w-6 cursor-pointer opacity-70 hover:opacity-100\"\n >\n <lw-icon name=\"close\" size=\"0.85rem\" />\n <lw-tooltip [text]=\"'content.closeTab' | transloco\" position=\"bottom\" />\n </span>\n }\n </div>\n }\n }\n </div>\n\n @if (canAddTab()) {\n <button\n type=\"button\"\n (click)=\"addTab.emit($event)\"\n lwMenuState\n class=\"lw-icon-btn w-8\"\n [class.self-stretch]=\"icons()\"\n [attr.aria-label]=\"'content.split.newTab' | transloco\"\n data-testid=\"pane-add-tab\"\n >\n <lw-icon name=\"add\" size=\"1rem\" />\n <lw-tooltip [text]=\"'content.split.newTab' | transloco\" position=\"bottom\" />\n </button>\n }\n\n @if (overflow() && overflowing()) {\n <button\n type=\"button\"\n (click)=\"openOverflow($event)\"\n lwMenuState\n class=\"lw-icon-btn w-8\"\n [class.self-stretch]=\"icons()\"\n [attr.aria-label]=\"'content.moreTabs' | transloco\"\n >\n <lw-icon name=\"chevronDown\" size=\"1rem\" />\n <lw-tooltip [text]=\"'content.moreTabs' | transloco\" position=\"bottom\" />\n </button>\n }\n\n @if (paneActions(); as actions) {\n <ng-container [ngTemplateOutlet]=\"actions\" />\n }\n @if (activeActions().length) {\n <div class=\"flex shrink-0 items-center gap-0.5 border-l border-border px-2\">\n @for (action of activeActions(); track action.id) {\n <button\n type=\"button\"\n (click)=\"runAction.emit(action)\"\n class=\"lw-icon-btn h-8 w-8 rounded-md\"\n [attr.aria-label]=\"action.title | transloco\"\n >\n <lw-icon [name]=\"action.icon\" size=\"1rem\" />\n <lw-tooltip [text]=\"action.title | transloco\" position=\"bottom\" />\n </button>\n }\n </div>\n }\n</div>\n" }]
7307
7540
  }], ctorParameters: () => [], propDecorators: { tabs: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabs", required: true }] }], activeId: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeId", required: true }] }], reorderable: [{ type: i0.Input, args: [{ isSignal: true, alias: "reorderable", required: false }] }], draggable: [{ type: i0.Input, args: [{ isSignal: true, alias: "draggable", required: false }] }], acceptsTabs: [{ type: i0.Input, args: [{ isSignal: true, alias: "acceptsTabs", required: false }] }], source: [{ type: i0.Input, args: [{ isSignal: true, alias: "source", required: true }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], urlDriven: [{ type: i0.Input, args: [{ isSignal: true, alias: "urlDriven", required: false }] }], contextMenuSlot: [{ type: i0.Input, args: [{ isSignal: true, alias: "contextMenuSlot", required: false }] }], viewContextMenuSlot: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewContextMenuSlot", required: false }] }], contextGroup: [{ type: i0.Input, args: [{ isSignal: true, alias: "contextGroup", required: false }] }], overflow: [{ type: i0.Input, args: [{ isSignal: true, alias: "overflow", required: false }] }], canAddTab: [{ type: i0.Input, args: [{ isSignal: true, alias: "canAddTab", required: false }] }], paneActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "paneActions", required: false }] }], selectTab: [{ type: i0.Output, args: ["selectTab"] }], escalate: [{ type: i0.Output, args: ["escalate"] }], closeTab: [{ type: i0.Output, args: ["closeTab"] }], unpinTab: [{ type: i0.Output, args: ["unpinTab"] }], reorderTabs: [{ type: i0.Output, args: ["reorderTabs"] }], runAction: [{ type: i0.Output, args: ["runAction"] }], addTab: [{ type: i0.Output, args: ["addTab"] }], revealRequest: [{ type: i0.Output, args: ["revealRequest"] }], strip: [{ type: i0.ViewChild, args: ['tabStrip', { isSignal: true }] }] } });
7308
7541
 
7309
7542
  class PaneToolbar {
@@ -8862,7 +9095,7 @@ class PaneTargetPicker {
8862
9095
  present(targets, anchor, onPick) {
8863
9096
  const rect = anchor.getBoundingClientRect();
8864
9097
  const entries = paneTargetEntries(targets, (key) => this.transloco.translate(key));
8865
- this.menu.openList(entries, { x: rect.left, y: rect.bottom + MENU_ANCHOR_GAP }, onPick);
9098
+ this.menu.openList(entries, { x: rect.left, y: rect.bottom + MENU_ANCHOR_GAP }, onPick, anchor);
8866
9099
  }
8867
9100
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: PaneTargetPicker, deps: [], target: i0.ɵɵFactoryTarget.Service });
8868
9101
  static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.0", ngImport: i0, type: PaneTargetPicker });
@@ -10069,11 +10302,11 @@ class ShellSidebarHeader {
10069
10302
  return this.layout.regions.find((r) => r.type === 'panel' && r.dock === dock && r.id !== this.region().id);
10070
10303
  }
10071
10304
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ShellSidebarHeader, deps: [], target: i0.ɵɵFactoryTarget.Component });
10072
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: ShellSidebarHeader, isStandalone: true, selector: "lw-shell-sidebar-header", inputs: { region: { classPropertyName: "region", publicName: "region", isSignal: true, isRequired: true, transformFunction: null }, context: { classPropertyName: "context", publicName: "context", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (showHamburger()) {\n\n <div\n class=\"flex h-12 w-10 shrink-0 items-center justify-center border-b border-border bg-surface\"\n >\n <button\n type=\"button\"\n (click)=\"open()\"\n class=\"lw-icon-btn h-9 w-9 rounded-md\"\n [attr.aria-label]=\"'panel.open' | transloco\"\n >\n <lw-icon name=\"menu\" size=\"1rem\" />\n <lw-tooltip [text]=\"'panel.open' | transloco\" position=\"bottom\" />\n </button>\n </div>\n} @else if (showExpand()) {\n\n <div\n class=\"flex h-12 items-center overflow-hidden border-b border-border bg-surface px-1.5\"\n >\n <button\n type=\"button\"\n (click)=\"expand()\"\n class=\"lw-icon-btn h-7 w-7 rounded-md\"\n [attr.aria-label]=\"'panel.expand' | transloco\"\n >\n <lw-icon [name]=\"expandIcon()\" size=\"1rem\" />\n <lw-tooltip [text]=\"'panel.expand' | transloco\" position=\"bottom\" />\n </button>\n </div>\n} @else if (!collapsed()) {\n\n <lw-pane-tab-strip\n class=\"block h-12\"\n [id]=\"hostId()\"\n variant=\"icons\"\n [tabs]=\"stripTabs()\"\n [activeId]=\"activeTabPath()\"\n [source]=\"source()\"\n [reorderable]=\"viewsReorderable\"\n [draggable]=\"viewsDraggable\"\n [acceptsTabs]=\"acceptsTab\"\n [viewContextMenuSlot]=\"viewContextMenu\"\n [contextGroup]=\"region().id\"\n [overflow]=\"true\"\n [paneActions]=\"trailing\"\n (selectTab)=\"select($event)\"\n (reorderTabs)=\"onReorder($event)\"\n (keydown)=\"onStripKeydown($event)\"\n [lwContextMenu]=\"stripMenu\"\n />\n <ng-template #trailing>\n @if (canCollapse()) {\n <button\n type=\"button\"\n (click)=\"trailingAction()\"\n class=\"lw-icon-btn h-10 w-10 self-center rounded-md md:h-7 md:w-7\"\n [attr.aria-label]=\"(viewport.compact() ? 'panel.close' : 'panel.collapse') | transloco\"\n >\n <lw-icon [name]=\"viewport.compact() ? 'close' : collapseIcon()\" size=\"1rem\" />\n <lw-tooltip [text]=\"(viewport.compact() ? 'panel.close' : 'panel.collapse') | transloco\" position=\"bottom\" />\n </button>\n }\n </ng-template>\n}\n", dependencies: [{ kind: "component", type: PaneTabStrip, selector: "lw-pane-tab-strip", inputs: ["tabs", "activeId", "reorderable", "draggable", "acceptsTabs", "source", "variant", "urlDriven", "contextMenuSlot", "viewContextMenuSlot", "contextGroup", "overflow", "canAddTab", "paneActions"], outputs: ["selectTab", "escalate", "closeTab", "unpinTab", "reorderTabs", "runAction", "addTab", "revealRequest"] }, { kind: "directive", type: ContextMenuDirective, selector: "[lwContextMenu]", inputs: ["lwContextMenu", "lwContextMenuContext"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
10305
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: ShellSidebarHeader, isStandalone: true, selector: "lw-shell-sidebar-header", inputs: { region: { classPropertyName: "region", publicName: "region", isSignal: true, isRequired: true, transformFunction: null }, context: { classPropertyName: "context", publicName: "context", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (showHamburger()) {\n\n <div\n class=\"flex h-12 w-10 shrink-0 items-center justify-center border-b border-border bg-surface\"\n >\n <button\n type=\"button\"\n (click)=\"open()\"\n class=\"lw-icon-btn h-9 w-9 rounded-md\"\n [attr.aria-label]=\"'panel.open' | transloco\"\n >\n <lw-icon name=\"menu\" size=\"1rem\" />\n <lw-tooltip [text]=\"'panel.open' | transloco\" position=\"bottom\" />\n </button>\n </div>\n} @else if (showExpand()) {\n\n <div\n class=\"flex h-12 items-center overflow-hidden border-b border-border bg-surface px-1.5\"\n >\n <button\n type=\"button\"\n (click)=\"expand()\"\n class=\"lw-icon-btn h-7 w-7 rounded-md\"\n [attr.aria-label]=\"'panel.expand' | transloco\"\n >\n <lw-icon [name]=\"expandIcon()\" size=\"1rem\" />\n <lw-tooltip [text]=\"'panel.expand' | transloco\" position=\"bottom\" />\n </button>\n </div>\n} @else if (!collapsed()) {\n\n <lw-pane-tab-strip\n class=\"block h-12\"\n [id]=\"hostId()\"\n variant=\"icons\"\n [tabs]=\"stripTabs()\"\n [activeId]=\"activeTabPath()\"\n [source]=\"source()\"\n [reorderable]=\"viewsReorderable\"\n [draggable]=\"viewsDraggable\"\n [acceptsTabs]=\"acceptsTab\"\n [viewContextMenuSlot]=\"viewContextMenu\"\n [contextGroup]=\"region().id\"\n [overflow]=\"true\"\n [paneActions]=\"trailing\"\n (selectTab)=\"select($event)\"\n (reorderTabs)=\"onReorder($event)\"\n (keydown)=\"onStripKeydown($event)\"\n [lwMenu]=\"stripMenu\"\n />\n <ng-template #trailing>\n @if (canCollapse()) {\n <button\n type=\"button\"\n (click)=\"trailingAction()\"\n class=\"lw-icon-btn h-10 w-10 self-center rounded-md md:h-7 md:w-7\"\n [attr.aria-label]=\"(viewport.compact() ? 'panel.close' : 'panel.collapse') | transloco\"\n >\n <lw-icon [name]=\"viewport.compact() ? 'close' : collapseIcon()\" size=\"1rem\" />\n <lw-tooltip [text]=\"(viewport.compact() ? 'panel.close' : 'panel.collapse') | transloco\" position=\"bottom\" />\n </button>\n }\n </ng-template>\n}\n", dependencies: [{ kind: "component", type: PaneTabStrip, selector: "lw-pane-tab-strip", inputs: ["tabs", "activeId", "reorderable", "draggable", "acceptsTabs", "source", "variant", "urlDriven", "contextMenuSlot", "viewContextMenuSlot", "contextGroup", "overflow", "canAddTab", "paneActions"], outputs: ["selectTab", "escalate", "closeTab", "unpinTab", "reorderTabs", "runAction", "addTab", "revealRequest"] }, { kind: "directive", type: MenuTriggerDirective, selector: "[lwMenu], [lwMenuOnActivate], [lwMenuState]", inputs: ["lwMenu", "lwMenuOnActivate", "lwMenuContext", "lwMenuSide", "lwMenuHeader", "lwMenuState"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
10073
10306
  }
10074
10307
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ShellSidebarHeader, decorators: [{
10075
10308
  type: Component,
10076
- args: [{ selector: 'lw-shell-sidebar-header', imports: [TranslocoPipe, PaneTabStrip, ContextMenuDirective], schemas: [CUSTOM_ELEMENTS_SCHEMA], template: "@if (showHamburger()) {\n\n <div\n class=\"flex h-12 w-10 shrink-0 items-center justify-center border-b border-border bg-surface\"\n >\n <button\n type=\"button\"\n (click)=\"open()\"\n class=\"lw-icon-btn h-9 w-9 rounded-md\"\n [attr.aria-label]=\"'panel.open' | transloco\"\n >\n <lw-icon name=\"menu\" size=\"1rem\" />\n <lw-tooltip [text]=\"'panel.open' | transloco\" position=\"bottom\" />\n </button>\n </div>\n} @else if (showExpand()) {\n\n <div\n class=\"flex h-12 items-center overflow-hidden border-b border-border bg-surface px-1.5\"\n >\n <button\n type=\"button\"\n (click)=\"expand()\"\n class=\"lw-icon-btn h-7 w-7 rounded-md\"\n [attr.aria-label]=\"'panel.expand' | transloco\"\n >\n <lw-icon [name]=\"expandIcon()\" size=\"1rem\" />\n <lw-tooltip [text]=\"'panel.expand' | transloco\" position=\"bottom\" />\n </button>\n </div>\n} @else if (!collapsed()) {\n\n <lw-pane-tab-strip\n class=\"block h-12\"\n [id]=\"hostId()\"\n variant=\"icons\"\n [tabs]=\"stripTabs()\"\n [activeId]=\"activeTabPath()\"\n [source]=\"source()\"\n [reorderable]=\"viewsReorderable\"\n [draggable]=\"viewsDraggable\"\n [acceptsTabs]=\"acceptsTab\"\n [viewContextMenuSlot]=\"viewContextMenu\"\n [contextGroup]=\"region().id\"\n [overflow]=\"true\"\n [paneActions]=\"trailing\"\n (selectTab)=\"select($event)\"\n (reorderTabs)=\"onReorder($event)\"\n (keydown)=\"onStripKeydown($event)\"\n [lwContextMenu]=\"stripMenu\"\n />\n <ng-template #trailing>\n @if (canCollapse()) {\n <button\n type=\"button\"\n (click)=\"trailingAction()\"\n class=\"lw-icon-btn h-10 w-10 self-center rounded-md md:h-7 md:w-7\"\n [attr.aria-label]=\"(viewport.compact() ? 'panel.close' : 'panel.collapse') | transloco\"\n >\n <lw-icon [name]=\"viewport.compact() ? 'close' : collapseIcon()\" size=\"1rem\" />\n <lw-tooltip [text]=\"(viewport.compact() ? 'panel.close' : 'panel.collapse') | transloco\" position=\"bottom\" />\n </button>\n }\n </ng-template>\n}\n" }]
10309
+ args: [{ selector: 'lw-shell-sidebar-header', imports: [TranslocoPipe, PaneTabStrip, MenuTriggerDirective], schemas: [CUSTOM_ELEMENTS_SCHEMA], template: "@if (showHamburger()) {\n\n <div\n class=\"flex h-12 w-10 shrink-0 items-center justify-center border-b border-border bg-surface\"\n >\n <button\n type=\"button\"\n (click)=\"open()\"\n class=\"lw-icon-btn h-9 w-9 rounded-md\"\n [attr.aria-label]=\"'panel.open' | transloco\"\n >\n <lw-icon name=\"menu\" size=\"1rem\" />\n <lw-tooltip [text]=\"'panel.open' | transloco\" position=\"bottom\" />\n </button>\n </div>\n} @else if (showExpand()) {\n\n <div\n class=\"flex h-12 items-center overflow-hidden border-b border-border bg-surface px-1.5\"\n >\n <button\n type=\"button\"\n (click)=\"expand()\"\n class=\"lw-icon-btn h-7 w-7 rounded-md\"\n [attr.aria-label]=\"'panel.expand' | transloco\"\n >\n <lw-icon [name]=\"expandIcon()\" size=\"1rem\" />\n <lw-tooltip [text]=\"'panel.expand' | transloco\" position=\"bottom\" />\n </button>\n </div>\n} @else if (!collapsed()) {\n\n <lw-pane-tab-strip\n class=\"block h-12\"\n [id]=\"hostId()\"\n variant=\"icons\"\n [tabs]=\"stripTabs()\"\n [activeId]=\"activeTabPath()\"\n [source]=\"source()\"\n [reorderable]=\"viewsReorderable\"\n [draggable]=\"viewsDraggable\"\n [acceptsTabs]=\"acceptsTab\"\n [viewContextMenuSlot]=\"viewContextMenu\"\n [contextGroup]=\"region().id\"\n [overflow]=\"true\"\n [paneActions]=\"trailing\"\n (selectTab)=\"select($event)\"\n (reorderTabs)=\"onReorder($event)\"\n (keydown)=\"onStripKeydown($event)\"\n [lwMenu]=\"stripMenu\"\n />\n <ng-template #trailing>\n @if (canCollapse()) {\n <button\n type=\"button\"\n (click)=\"trailingAction()\"\n class=\"lw-icon-btn h-10 w-10 self-center rounded-md md:h-7 md:w-7\"\n [attr.aria-label]=\"(viewport.compact() ? 'panel.close' : 'panel.collapse') | transloco\"\n >\n <lw-icon [name]=\"viewport.compact() ? 'close' : collapseIcon()\" size=\"1rem\" />\n <lw-tooltip [text]=\"(viewport.compact() ? 'panel.close' : 'panel.collapse') | transloco\" position=\"bottom\" />\n </button>\n }\n </ng-template>\n}\n" }]
10077
10310
  }], propDecorators: { region: [{ type: i0.Input, args: [{ isSignal: true, alias: "region", required: true }] }], context: [{ type: i0.Input, args: [{ isSignal: true, alias: "context", required: false }] }] } });
10078
10311
 
10079
10312
  const TAB_CONTEXT_MENU = 'content/tab/context';
@@ -11363,7 +11596,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
11363
11596
  // GENERATED — do not edit by hand.
11364
11597
  // Written by tools/stamp-version.mjs from <Version> in Directory.Build.props.
11365
11598
  // Single source of truth: Directory.Build.props (bump via scripts/bump-version.sh).
11366
- const APP_VERSION = '0.7.6';
11599
+ const APP_VERSION = '0.7.8';
11367
11600
 
11368
11601
  /**
11369
11602
  * The running build's version, sourced from `<Version>` in Directory.Build.props
@@ -13380,6 +13613,37 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
13380
13613
  args: [{ selector: 'lw-text-size-toggle', imports: [TranslocoPipe], schemas: [CUSTOM_ELEMENTS_SCHEMA], template: "<fieldset class=\"lw-segmented\" [attr.aria-label]=\"'textSize.label' | transloco\">\n @for (s of scales; track s) {\n <button\n type=\"button\"\n (click)=\"select(s)\"\n [attr.aria-pressed]=\"scale() === s\"\n [attr.aria-label]=\"'textSize.' + s | transloco\"\n class=\"lw-segmented-item font-semibold\"\n >\n <span [class]=\"glyphClass(s)\" aria-hidden=\"true\">A</span>\n <lw-tooltip [text]=\"'textSize.' + s | transloco\" position=\"bottom\" />\n </button>\n }\n</fieldset>\n" }]
13381
13614
  }] });
13382
13615
 
13616
+ /**
13617
+ * The plugins this distribution declared not optional. Defaults to **none** — every plugin is the
13618
+ * user's to switch off until a distribution says otherwise.
13619
+ */
13620
+ const REQUIRED_PLUGINS = new InjectionToken('REQUIRED_PLUGINS', { providedIn: 'root', factory: () => [] });
13621
+ /**
13622
+ * A distribution declares which of the plugins it composes its application cannot run without. Such
13623
+ * a plugin is listed in the permissions settings with what it holds, and **without a switch to turn
13624
+ * it off**; it stays active whatever the user chose before.
13625
+ *
13626
+ * This is the distribution's statement and not the plugin's, and deliberately so: everything a plugin
13627
+ * declares about itself is a request the distribution grants, so a manifest field here would be the
13628
+ * one exemption a plugin could award itself.
13629
+ *
13630
+ * It withholds the plugin's own switch and nothing else. The capabilities such a plugin was granted
13631
+ * stay revocable, because needing a plugin says nothing about needing everything it asked for — which
13632
+ * is what distinguishes this from a plugin the operator deployed, where both are withheld.
13633
+ *
13634
+ * Naming a plugin this distribution does not compose — neither one it provides in-process nor an
13635
+ * isolated one it declares — is a composition mistake, not a failure: it is reported in development
13636
+ * and otherwise ignored, the same way a capability granted to a plugin that never declared it is. The
13637
+ * report happens once the composed plugins are registered, which is the only moment that set is
13638
+ * settled: a plugin the store installs arrives later and is the user's to switch off in any case.
13639
+ *
13640
+ * @example
13641
+ * provideRequiredPlugins('sign-in')
13642
+ */
13643
+ function provideRequiredPlugins(...pluginIds) {
13644
+ return { provide: REQUIRED_PLUGINS, useValue: pluginIds };
13645
+ }
13646
+
13383
13647
  const STORAGE_KEY$3 = 'lw.shell.disabled-plugins';
13384
13648
  /**
13385
13649
  * Whether each plugin is turned on — plugin enable/disable, distinct from capability revocation. Disabling a plugin is coarse: it does not restrict a power, it unloads the
@@ -13393,15 +13657,27 @@ const STORAGE_KEY$3 = 'lw.shell.disabled-plugins';
13393
13657
  class PluginEnablementService {
13394
13658
  store = inject(SETTINGS_STORE);
13395
13659
  sync = inject(StateSyncService);
13660
+ required = new Set(inject(REQUIRED_PLUGINS));
13396
13661
  disabledSet = signal(parseIdSet(this.store.peek?.(STORAGE_KEY$3)), /* @ts-ignore */
13397
13662
  ...(ngDevMode ? [{ debugName: "disabledSet" }] : /* istanbul ignore next */ []));
13398
13663
  names = signal(new Map(), /* @ts-ignore */
13399
13664
  ...(ngDevMode ? [{ debugName: "names" }] : /* istanbul ignore next */ []));
13400
- /** The disabled plugin ids (reactive) — a runtime reconciles activation against this. */
13401
- disabled = this.disabledSet.asReadonly();
13665
+ /**
13666
+ * The disabled plugin ids (reactive) — a runtime reconciles activation against this. A plugin the
13667
+ * distribution declared its application cannot run without never appears here, whatever is stored,
13668
+ * so the runtime and the permissions surface read one answer rather than two.
13669
+ */
13670
+ disabled = computed(() => {
13671
+ const stored = this.disabledSet();
13672
+ if (this.required.size === 0) {
13673
+ return stored;
13674
+ }
13675
+ return new Set([...stored].filter((id) => !this.required.has(id)));
13676
+ }, /* @ts-ignore */
13677
+ ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
13402
13678
  /** Every known plugin with its enabled state, for the permissions settings surface. */
13403
13679
  plugins = computed(() => {
13404
- const disabled = this.disabledSet();
13680
+ const disabled = this.disabled();
13405
13681
  return [...this.names().entries()]
13406
13682
  .map(([id, name]) => ({ id, name, enabled: !disabled.has(id) }))
13407
13683
  .toSorted((a, b) => a.name.localeCompare(b.name));
@@ -13411,6 +13687,10 @@ class PluginEnablementService {
13411
13687
  hydrateAsync(this.store, STORAGE_KEY$3, (raw) => this.disabledSet.set(parseIdSet(raw)));
13412
13688
  this.sync.register('settings', STORAGE_KEY$3, (raw) => this.disabledSet.set(parseIdSet(raw)));
13413
13689
  }
13690
+ /** Whether the distribution declared it cannot run without this plugin. */
13691
+ isRequired(id) {
13692
+ return this.required.has(id);
13693
+ }
13414
13694
  /** Records a plugin so the permissions surface can list it (enabled or not). Idempotent. A runtime calls it for every plugin it knows. */
13415
13695
  register(id, name) {
13416
13696
  this.names.update((map) => map.has(id) ? map : new Map(map).set(id, name));
@@ -13428,10 +13708,17 @@ class PluginEnablementService {
13428
13708
  }
13429
13709
  /** Whether `id` is currently enabled (default: yes — a plugin is on until the user turns it off). */
13430
13710
  isEnabled(id) {
13431
- return !this.disabledSet().has(id);
13711
+ return !this.disabled().has(id);
13432
13712
  }
13433
- /** Turns a whole plugin on or off (persisted). The runtimes react by loading/unloading it. */
13713
+ /**
13714
+ * Turns a whole plugin on or off (persisted). The runtimes react by loading/unloading it. Turning
13715
+ * off a plugin the distribution declared it cannot run without does nothing: the surface offers no
13716
+ * switch for one, and this is the same answer read from anywhere else.
13717
+ */
13434
13718
  setEnabled(id, enabled) {
13719
+ if (!enabled && this.required.has(id)) {
13720
+ return;
13721
+ }
13435
13722
  const next = toggledIdSet(this.disabledSet(), id, !enabled);
13436
13723
  this.disabledSet.set(next);
13437
13724
  void this.store.set(STORAGE_KEY$3, JSON.stringify([...next]));
@@ -13645,10 +13932,12 @@ class PermissionsSettings {
13645
13932
  const caps = this.grants.permissions();
13646
13933
  return this.enablement.plugins().map((plugin) => {
13647
13934
  const provided = this.deployment.isDeployed(plugin.id);
13935
+ const required = this.enablement.isRequired(plugin.id);
13648
13936
  return {
13649
13937
  ...plugin,
13650
13938
  enabled: provided || plugin.enabled,
13651
13939
  provided,
13940
+ required,
13652
13941
  rungNote: rungNoteKey(this.isolation.rungOf(plugin.id)),
13653
13942
  capabilities: caps.find((entry) => entry.pluginId === plugin.id)?.capabilities ??
13654
13943
  [],
@@ -13663,11 +13952,11 @@ class PermissionsSettings {
13663
13952
  this.grants.setGranted(pluginId, capability, event.target.checked);
13664
13953
  }
13665
13954
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: PermissionsSettings, deps: [], target: i0.ɵɵFactoryTarget.Component });
13666
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: PermissionsSettings, isStandalone: true, selector: "lw-permissions-settings", ngImport: i0, template: "<p class=\"mb-4 text-sm text-content-muted\">\n {{ 'settings.permissionsDesc' | transloco }}\n</p>\n@if (plugins().length === 0) {\n <p class=\"text-sm text-content-muted\">\n {{ 'settings.permissionsEmpty' | transloco }}\n </p>\n} @else {\n <div class=\"flex flex-col gap-6\">\n @for (plugin of plugins(); track plugin.id) {\n <section class=\"flex flex-col gap-3\">\n <div class=\"flex items-center justify-between gap-4\">\n <h3 class=\"text-sm font-semibold text-content\">\n {{ plugin.name }}\n </h3>\n @if (!plugin.provided) {\n <input\n type=\"checkbox\"\n role=\"switch\"\n class=\"lw-switch shrink-0\"\n [checked]=\"plugin.enabled\"\n [attr.aria-checked]=\"plugin.enabled\"\n [attr.aria-label]=\"'settings.pluginEnabled' | transloco\"\n [attr.data-testid]=\"'plugin-enabled-' + plugin.id\"\n (change)=\"togglePlugin(plugin.id, $event)\"\n />\n }\n </div>\n @if (plugin.rungNote; as rungNote) {\n <p\n class=\"text-xs text-content-muted\"\n [attr.data-testid]=\"'perm-level-' + plugin.id\"\n >\n {{ rungNote | transloco }}\n </p>\n }\n @if (plugin.provided) {\n <p\n class=\"text-xs text-content-muted\"\n [attr.data-testid]=\"'perm-provided-' + plugin.id\"\n >\n {{ 'settings.permissionsProvided' | transloco }}\n </p>\n }\n @if (plugin.enabled) {\n <ul class=\"flex flex-col gap-3\">\n @for (cap of plugin.capabilities; track cap.capability) {\n <li class=\"flex items-start justify-between gap-4\">\n <div class=\"flex flex-col\">\n <span class=\"text-sm text-content\">{{\n 'settings.capability.' + cap.capability | transloco\n }}</span>\n <span class=\"text-xs text-content-muted\">{{\n 'settings.capabilityDesc.' + cap.capability | transloco\n }}</span>\n </div>\n @if (!plugin.provided) {\n <input\n type=\"checkbox\"\n role=\"switch\"\n class=\"lw-switch shrink-0\"\n [checked]=\"cap.effective\"\n [attr.aria-checked]=\"cap.effective\"\n [attr.aria-label]=\"\n 'settings.capability.' + cap.capability | transloco\n \"\n [attr.data-testid]=\"\n 'perm-' + plugin.id + '-' + cap.capability\n \"\n (change)=\"\n toggleCapability(plugin.id, cap.capability, $event)\n \"\n />\n }\n </li>\n }\n </ul>\n } @else {\n <p class=\"text-xs text-content-muted\">\n {{ 'settings.pluginDisabled' | transloco }}\n </p>\n }\n </section>\n }\n </div>\n}\n", dependencies: [{ kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
13955
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: PermissionsSettings, isStandalone: true, selector: "lw-permissions-settings", ngImport: i0, template: "<p class=\"mb-4 text-sm text-content-muted\">\n {{ 'settings.permissionsDesc' | transloco }}\n</p>\n@if (plugins().length === 0) {\n <p class=\"text-sm text-content-muted\">\n {{ 'settings.permissionsEmpty' | transloco }}\n </p>\n} @else {\n <div class=\"flex flex-col gap-6\">\n @for (plugin of plugins(); track plugin.id) {\n <section class=\"flex flex-col gap-3\">\n <div class=\"flex items-center justify-between gap-4\">\n <h3 class=\"text-sm font-semibold text-content\">\n {{ plugin.name }}\n </h3>\n @if (!plugin.provided && !plugin.required) {\n <input\n type=\"checkbox\"\n role=\"switch\"\n class=\"lw-switch shrink-0\"\n [checked]=\"plugin.enabled\"\n [attr.aria-checked]=\"plugin.enabled\"\n [attr.aria-label]=\"'settings.pluginEnabled' | transloco\"\n [attr.data-testid]=\"'plugin-enabled-' + plugin.id\"\n (change)=\"togglePlugin(plugin.id, $event)\"\n />\n }\n </div>\n @if (plugin.rungNote; as rungNote) {\n <p\n class=\"text-xs text-content-muted\"\n [attr.data-testid]=\"'perm-level-' + plugin.id\"\n >\n {{ rungNote | transloco }}\n </p>\n }\n @if (plugin.provided) {\n <p\n class=\"text-xs text-content-muted\"\n [attr.data-testid]=\"'perm-provided-' + plugin.id\"\n >\n {{ 'settings.permissionsProvided' | transloco }}\n </p>\n } @else if (plugin.required) {\n <p\n class=\"text-xs text-content-muted\"\n [attr.data-testid]=\"'perm-required-' + plugin.id\"\n >\n {{ 'settings.permissionsRequired' | transloco }}\n </p>\n }\n @if (plugin.enabled) {\n <ul class=\"flex flex-col gap-3\">\n @for (cap of plugin.capabilities; track cap.capability) {\n <li class=\"flex items-start justify-between gap-4\">\n <div class=\"flex flex-col\">\n <span class=\"text-sm text-content\">{{\n 'settings.capability.' + cap.capability | transloco\n }}</span>\n <span class=\"text-xs text-content-muted\">{{\n 'settings.capabilityDesc.' + cap.capability | transloco\n }}</span>\n </div>\n @if (!plugin.provided) {\n <input\n type=\"checkbox\"\n role=\"switch\"\n class=\"lw-switch shrink-0\"\n [checked]=\"cap.effective\"\n [attr.aria-checked]=\"cap.effective\"\n [attr.aria-label]=\"\n 'settings.capability.' + cap.capability | transloco\n \"\n [attr.data-testid]=\"\n 'perm-' + plugin.id + '-' + cap.capability\n \"\n (change)=\"\n toggleCapability(plugin.id, cap.capability, $event)\n \"\n />\n }\n </li>\n }\n </ul>\n } @else {\n <p class=\"text-xs text-content-muted\">\n {{ 'settings.pluginDisabled' | transloco }}\n </p>\n }\n </section>\n }\n </div>\n}\n", dependencies: [{ kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
13667
13956
  }
13668
13957
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: PermissionsSettings, decorators: [{
13669
13958
  type: Component,
13670
- args: [{ selector: 'lw-permissions-settings', imports: [TranslocoPipe], template: "<p class=\"mb-4 text-sm text-content-muted\">\n {{ 'settings.permissionsDesc' | transloco }}\n</p>\n@if (plugins().length === 0) {\n <p class=\"text-sm text-content-muted\">\n {{ 'settings.permissionsEmpty' | transloco }}\n </p>\n} @else {\n <div class=\"flex flex-col gap-6\">\n @for (plugin of plugins(); track plugin.id) {\n <section class=\"flex flex-col gap-3\">\n <div class=\"flex items-center justify-between gap-4\">\n <h3 class=\"text-sm font-semibold text-content\">\n {{ plugin.name }}\n </h3>\n @if (!plugin.provided) {\n <input\n type=\"checkbox\"\n role=\"switch\"\n class=\"lw-switch shrink-0\"\n [checked]=\"plugin.enabled\"\n [attr.aria-checked]=\"plugin.enabled\"\n [attr.aria-label]=\"'settings.pluginEnabled' | transloco\"\n [attr.data-testid]=\"'plugin-enabled-' + plugin.id\"\n (change)=\"togglePlugin(plugin.id, $event)\"\n />\n }\n </div>\n @if (plugin.rungNote; as rungNote) {\n <p\n class=\"text-xs text-content-muted\"\n [attr.data-testid]=\"'perm-level-' + plugin.id\"\n >\n {{ rungNote | transloco }}\n </p>\n }\n @if (plugin.provided) {\n <p\n class=\"text-xs text-content-muted\"\n [attr.data-testid]=\"'perm-provided-' + plugin.id\"\n >\n {{ 'settings.permissionsProvided' | transloco }}\n </p>\n }\n @if (plugin.enabled) {\n <ul class=\"flex flex-col gap-3\">\n @for (cap of plugin.capabilities; track cap.capability) {\n <li class=\"flex items-start justify-between gap-4\">\n <div class=\"flex flex-col\">\n <span class=\"text-sm text-content\">{{\n 'settings.capability.' + cap.capability | transloco\n }}</span>\n <span class=\"text-xs text-content-muted\">{{\n 'settings.capabilityDesc.' + cap.capability | transloco\n }}</span>\n </div>\n @if (!plugin.provided) {\n <input\n type=\"checkbox\"\n role=\"switch\"\n class=\"lw-switch shrink-0\"\n [checked]=\"cap.effective\"\n [attr.aria-checked]=\"cap.effective\"\n [attr.aria-label]=\"\n 'settings.capability.' + cap.capability | transloco\n \"\n [attr.data-testid]=\"\n 'perm-' + plugin.id + '-' + cap.capability\n \"\n (change)=\"\n toggleCapability(plugin.id, cap.capability, $event)\n \"\n />\n }\n </li>\n }\n </ul>\n } @else {\n <p class=\"text-xs text-content-muted\">\n {{ 'settings.pluginDisabled' | transloco }}\n </p>\n }\n </section>\n }\n </div>\n}\n" }]
13959
+ args: [{ selector: 'lw-permissions-settings', imports: [TranslocoPipe], template: "<p class=\"mb-4 text-sm text-content-muted\">\n {{ 'settings.permissionsDesc' | transloco }}\n</p>\n@if (plugins().length === 0) {\n <p class=\"text-sm text-content-muted\">\n {{ 'settings.permissionsEmpty' | transloco }}\n </p>\n} @else {\n <div class=\"flex flex-col gap-6\">\n @for (plugin of plugins(); track plugin.id) {\n <section class=\"flex flex-col gap-3\">\n <div class=\"flex items-center justify-between gap-4\">\n <h3 class=\"text-sm font-semibold text-content\">\n {{ plugin.name }}\n </h3>\n @if (!plugin.provided && !plugin.required) {\n <input\n type=\"checkbox\"\n role=\"switch\"\n class=\"lw-switch shrink-0\"\n [checked]=\"plugin.enabled\"\n [attr.aria-checked]=\"plugin.enabled\"\n [attr.aria-label]=\"'settings.pluginEnabled' | transloco\"\n [attr.data-testid]=\"'plugin-enabled-' + plugin.id\"\n (change)=\"togglePlugin(plugin.id, $event)\"\n />\n }\n </div>\n @if (plugin.rungNote; as rungNote) {\n <p\n class=\"text-xs text-content-muted\"\n [attr.data-testid]=\"'perm-level-' + plugin.id\"\n >\n {{ rungNote | transloco }}\n </p>\n }\n @if (plugin.provided) {\n <p\n class=\"text-xs text-content-muted\"\n [attr.data-testid]=\"'perm-provided-' + plugin.id\"\n >\n {{ 'settings.permissionsProvided' | transloco }}\n </p>\n } @else if (plugin.required) {\n <p\n class=\"text-xs text-content-muted\"\n [attr.data-testid]=\"'perm-required-' + plugin.id\"\n >\n {{ 'settings.permissionsRequired' | transloco }}\n </p>\n }\n @if (plugin.enabled) {\n <ul class=\"flex flex-col gap-3\">\n @for (cap of plugin.capabilities; track cap.capability) {\n <li class=\"flex items-start justify-between gap-4\">\n <div class=\"flex flex-col\">\n <span class=\"text-sm text-content\">{{\n 'settings.capability.' + cap.capability | transloco\n }}</span>\n <span class=\"text-xs text-content-muted\">{{\n 'settings.capabilityDesc.' + cap.capability | transloco\n }}</span>\n </div>\n @if (!plugin.provided) {\n <input\n type=\"checkbox\"\n role=\"switch\"\n class=\"lw-switch shrink-0\"\n [checked]=\"cap.effective\"\n [attr.aria-checked]=\"cap.effective\"\n [attr.aria-label]=\"\n 'settings.capability.' + cap.capability | transloco\n \"\n [attr.data-testid]=\"\n 'perm-' + plugin.id + '-' + cap.capability\n \"\n (change)=\"\n toggleCapability(plugin.id, cap.capability, $event)\n \"\n />\n }\n </li>\n }\n </ul>\n } @else {\n <p class=\"text-xs text-content-muted\">\n {{ 'settings.pluginDisabled' | transloco }}\n </p>\n }\n </section>\n }\n </div>\n}\n" }]
13671
13960
  }] });
13672
13961
 
13673
13962
  function registerDefaultSettings(settings) {
@@ -13861,6 +14150,8 @@ class CommandPalette {
13861
14150
  ? 'tabs'
13862
14151
  : 'commands', /* @ts-ignore */
13863
14152
  ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
14153
+ title = computed(() => this.mode() === 'tabs' ? 'palette.quickOpenTitle' : 'palette.title', /* @ts-ignore */
14154
+ ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
13864
14155
  query = signal('', /* @ts-ignore */
13865
14156
  ...(ngDevMode ? [{ debugName: "query" }] : /* istanbul ignore next */ []));
13866
14157
  rawIndex = signal(0, /* @ts-ignore */
@@ -14026,11 +14317,11 @@ class CommandPalette {
14026
14317
  }
14027
14318
  }
14028
14319
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: CommandPalette, deps: [], target: i0.ɵɵFactoryTarget.Component });
14029
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: CommandPalette, isStandalone: true, selector: "lw-command-palette", ngImport: i0, template: "<div class=\"flex max-h-[60vh] w-full flex-col overflow-hidden\">\n <div class=\"flex items-center gap-2 border-b border-border px-4\">\n <lw-icon name=\"search\" size=\"1.1rem\" class=\"text-content-muted\" />\n <input\n data-lw-autofocus\n type=\"text\"\n role=\"combobox\"\n aria-controls=\"lw-palette-list\"\n [attr.aria-expanded]=\"true\"\n [attr.aria-activedescendant]=\"activeId()\"\n [attr.aria-label]=\"'palette.title' | transloco\"\n [value]=\"query()\"\n [placeholder]=\"\n (mode() === 'tabs' ? 'palette.tabsPlaceholder' : 'palette.placeholder')\n | transloco\n \"\n class=\"flex-1 bg-transparent py-3 text-sm text-content outline-none placeholder:text-content-faint\"\n (input)=\"onQuery($event)\"\n (keydown.arrowdown)=\"move($event, 1)\"\n (keydown.arrowup)=\"move($event, -1)\"\n (keydown.arrowright)=\"openTabActions($event)\"\n (keydown.enter)=\"runActive($event)\"\n />\n </div>\n\n <ul\n id=\"lw-palette-list\"\n role=\"listbox\"\n class=\"min-h-0 flex-1 overflow-y-auto p-1\"\n >\n @for (entry of results(); track entry.id; let i = $index) {\n @if (recentCount() > 0 && i === 0) {\n <li\n role=\"presentation\"\n data-testid=\"palette-section-recent\"\n class=\"px-3 pt-2 pb-1 text-xs font-medium text-content-faint\"\n >\n {{ 'palette.recent' | transloco }}\n </li>\n }\n @if (recentCount() > 0 && i === recentCount()) {\n <li\n role=\"presentation\"\n data-testid=\"palette-section-all\"\n class=\"px-3 pt-2 pb-1 text-xs font-medium text-content-faint\"\n >\n {{ 'palette.all' | transloco }}\n </li>\n }\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <li\n role=\"option\"\n [id]=\"optionId(i)\"\n [attr.aria-selected]=\"i === activeIndex()\"\n class=\"flex cursor-pointer items-center gap-3 rounded-md px-3 py-2 text-sm\"\n [class]=\"\n i === activeIndex()\n ? 'bg-brand/10 text-brand-text'\n : 'text-content hover:bg-surface-overlay'\n \"\n (click)=\"select(entry)\"\n (mouseenter)=\"setActive(i)\"\n >\n @if (entry.icon; as icon) {\n <lw-icon [name]=\"icon\" size=\"1rem\" class=\"text-content-muted\" />\n }\n <span class=\"flex-1 truncate\">{{ entry.label }}</span>\n @if (timeOf(entry); as time) {\n <span class=\"shrink-0 text-xs text-content-faint\">{{ time }}</span>\n }\n @if (shortcutOf(entry); as shortcut) {\n <kbd\n class=\"rounded border border-border px-1.5 py-0.5 text-xs text-content-muted\"\n >{{ shortcut }}</kbd\n >\n }\n </li>\n } @empty {\n <li class=\"px-3 py-6 text-center text-sm text-content-muted\">\n {{\n (mode() === 'tabs' ? 'palette.tabsEmpty' : 'palette.empty') | transloco\n }}\n </li>\n }\n </ul>\n\n <div\n data-testid=\"palette-footer\"\n class=\"flex items-center gap-4 border-t border-border px-4 py-2 text-xs text-content-faint\"\n >\n <span class=\"flex items-center gap-1.5\">\n <kbd class=\"rounded border border-border px-1 py-0.5\">\u2191\u2193</kbd>\n {{ 'palette.hint.navigate' | transloco }}\n </span>\n <span class=\"flex items-center gap-1.5\">\n <kbd class=\"rounded border border-border px-1 py-0.5\">\u21B5</kbd>\n {{ 'palette.hint.run' | transloco }}\n </span>\n @if (mode() === 'tabs') {\n <span class=\"flex items-center gap-1.5\" data-testid=\"palette-hint-actions\">\n <kbd class=\"rounded border border-border px-1 py-0.5\">\u2192</kbd>\n {{ 'palette.hint.actions' | transloco }}\n </span>\n }\n <span class=\"ml-auto flex items-center gap-1.5\">\n <kbd class=\"rounded border border-border px-1 py-0.5\">Esc</kbd>\n {{ 'palette.hint.close' | transloco }}\n </span>\n </div>\n</div>\n", dependencies: [{ kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
14320
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: CommandPalette, isStandalone: true, selector: "lw-command-palette", ngImport: i0, template: "<div class=\"flex max-h-[60vh] w-full flex-col overflow-hidden\">\n <div class=\"flex items-center gap-2 border-b border-border px-4\">\n <lw-icon name=\"search\" size=\"1.1rem\" class=\"text-content-muted\" />\n <input\n data-lw-autofocus\n type=\"text\"\n role=\"combobox\"\n aria-controls=\"lw-palette-list\"\n [attr.aria-expanded]=\"true\"\n [attr.aria-activedescendant]=\"activeId()\"\n [attr.aria-label]=\"title() | transloco\"\n [value]=\"query()\"\n [placeholder]=\"\n (mode() === 'tabs' ? 'palette.tabsPlaceholder' : 'palette.placeholder')\n | transloco\n \"\n class=\"flex-1 bg-transparent py-3 text-sm text-content outline-none placeholder:text-content-faint\"\n (input)=\"onQuery($event)\"\n (keydown.arrowdown)=\"move($event, 1)\"\n (keydown.arrowup)=\"move($event, -1)\"\n (keydown.arrowright)=\"openTabActions($event)\"\n (keydown.enter)=\"runActive($event)\"\n />\n </div>\n\n <ul\n id=\"lw-palette-list\"\n role=\"listbox\"\n class=\"min-h-0 flex-1 overflow-y-auto p-1\"\n >\n @for (entry of results(); track entry.id; let i = $index) {\n @if (recentCount() > 0 && i === 0) {\n <li\n role=\"presentation\"\n data-testid=\"palette-section-recent\"\n class=\"px-3 pt-2 pb-1 text-xs font-medium text-content-faint\"\n >\n {{ 'palette.recent' | transloco }}\n </li>\n }\n @if (recentCount() > 0 && i === recentCount()) {\n <li\n role=\"presentation\"\n data-testid=\"palette-section-all\"\n class=\"px-3 pt-2 pb-1 text-xs font-medium text-content-faint\"\n >\n {{ 'palette.all' | transloco }}\n </li>\n }\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <li\n role=\"option\"\n [id]=\"optionId(i)\"\n [attr.aria-selected]=\"i === activeIndex()\"\n class=\"flex cursor-pointer items-center gap-3 rounded-md px-3 py-2 text-sm\"\n [class]=\"\n i === activeIndex()\n ? 'bg-brand/10 text-brand-text'\n : 'text-content hover:bg-surface-overlay'\n \"\n (click)=\"select(entry)\"\n (mouseenter)=\"setActive(i)\"\n >\n @if (entry.icon; as icon) {\n <lw-icon [name]=\"icon\" size=\"1rem\" class=\"text-content-muted\" />\n }\n <span class=\"flex-1 truncate\">{{ entry.label }}</span>\n @if (timeOf(entry); as time) {\n <span class=\"shrink-0 text-xs text-content-faint\">{{ time }}</span>\n }\n @if (shortcutOf(entry); as shortcut) {\n <kbd\n class=\"rounded border border-border px-1.5 py-0.5 text-xs text-content-muted\"\n >{{ shortcut }}</kbd\n >\n }\n </li>\n } @empty {\n <li class=\"px-3 py-6 text-center text-sm text-content-muted\">\n {{\n (mode() === 'tabs' ? 'palette.tabsEmpty' : 'palette.empty') | transloco\n }}\n </li>\n }\n </ul>\n\n <div\n data-testid=\"palette-footer\"\n class=\"flex items-center gap-4 border-t border-border px-4 py-2 text-xs text-content-faint\"\n >\n <span class=\"flex items-center gap-1.5\">\n <kbd class=\"rounded border border-border px-1 py-0.5\">\u2191\u2193</kbd>\n {{ 'palette.hint.navigate' | transloco }}\n </span>\n <span class=\"flex items-center gap-1.5\">\n <kbd class=\"rounded border border-border px-1 py-0.5\">\u21B5</kbd>\n {{ 'palette.hint.run' | transloco }}\n </span>\n @if (mode() === 'tabs') {\n <span class=\"flex items-center gap-1.5\" data-testid=\"palette-hint-actions\">\n <kbd class=\"rounded border border-border px-1 py-0.5\">\u2192</kbd>\n {{ 'palette.hint.actions' | transloco }}\n </span>\n }\n <span class=\"ml-auto flex items-center gap-1.5\">\n <kbd class=\"rounded border border-border px-1 py-0.5\">Esc</kbd>\n {{ 'palette.hint.close' | transloco }}\n </span>\n </div>\n</div>\n", dependencies: [{ kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
14030
14321
  }
14031
14322
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: CommandPalette, decorators: [{
14032
14323
  type: Component,
14033
- args: [{ selector: 'lw-command-palette', schemas: [CUSTOM_ELEMENTS_SCHEMA], imports: [TranslocoPipe], template: "<div class=\"flex max-h-[60vh] w-full flex-col overflow-hidden\">\n <div class=\"flex items-center gap-2 border-b border-border px-4\">\n <lw-icon name=\"search\" size=\"1.1rem\" class=\"text-content-muted\" />\n <input\n data-lw-autofocus\n type=\"text\"\n role=\"combobox\"\n aria-controls=\"lw-palette-list\"\n [attr.aria-expanded]=\"true\"\n [attr.aria-activedescendant]=\"activeId()\"\n [attr.aria-label]=\"'palette.title' | transloco\"\n [value]=\"query()\"\n [placeholder]=\"\n (mode() === 'tabs' ? 'palette.tabsPlaceholder' : 'palette.placeholder')\n | transloco\n \"\n class=\"flex-1 bg-transparent py-3 text-sm text-content outline-none placeholder:text-content-faint\"\n (input)=\"onQuery($event)\"\n (keydown.arrowdown)=\"move($event, 1)\"\n (keydown.arrowup)=\"move($event, -1)\"\n (keydown.arrowright)=\"openTabActions($event)\"\n (keydown.enter)=\"runActive($event)\"\n />\n </div>\n\n <ul\n id=\"lw-palette-list\"\n role=\"listbox\"\n class=\"min-h-0 flex-1 overflow-y-auto p-1\"\n >\n @for (entry of results(); track entry.id; let i = $index) {\n @if (recentCount() > 0 && i === 0) {\n <li\n role=\"presentation\"\n data-testid=\"palette-section-recent\"\n class=\"px-3 pt-2 pb-1 text-xs font-medium text-content-faint\"\n >\n {{ 'palette.recent' | transloco }}\n </li>\n }\n @if (recentCount() > 0 && i === recentCount()) {\n <li\n role=\"presentation\"\n data-testid=\"palette-section-all\"\n class=\"px-3 pt-2 pb-1 text-xs font-medium text-content-faint\"\n >\n {{ 'palette.all' | transloco }}\n </li>\n }\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <li\n role=\"option\"\n [id]=\"optionId(i)\"\n [attr.aria-selected]=\"i === activeIndex()\"\n class=\"flex cursor-pointer items-center gap-3 rounded-md px-3 py-2 text-sm\"\n [class]=\"\n i === activeIndex()\n ? 'bg-brand/10 text-brand-text'\n : 'text-content hover:bg-surface-overlay'\n \"\n (click)=\"select(entry)\"\n (mouseenter)=\"setActive(i)\"\n >\n @if (entry.icon; as icon) {\n <lw-icon [name]=\"icon\" size=\"1rem\" class=\"text-content-muted\" />\n }\n <span class=\"flex-1 truncate\">{{ entry.label }}</span>\n @if (timeOf(entry); as time) {\n <span class=\"shrink-0 text-xs text-content-faint\">{{ time }}</span>\n }\n @if (shortcutOf(entry); as shortcut) {\n <kbd\n class=\"rounded border border-border px-1.5 py-0.5 text-xs text-content-muted\"\n >{{ shortcut }}</kbd\n >\n }\n </li>\n } @empty {\n <li class=\"px-3 py-6 text-center text-sm text-content-muted\">\n {{\n (mode() === 'tabs' ? 'palette.tabsEmpty' : 'palette.empty') | transloco\n }}\n </li>\n }\n </ul>\n\n <div\n data-testid=\"palette-footer\"\n class=\"flex items-center gap-4 border-t border-border px-4 py-2 text-xs text-content-faint\"\n >\n <span class=\"flex items-center gap-1.5\">\n <kbd class=\"rounded border border-border px-1 py-0.5\">\u2191\u2193</kbd>\n {{ 'palette.hint.navigate' | transloco }}\n </span>\n <span class=\"flex items-center gap-1.5\">\n <kbd class=\"rounded border border-border px-1 py-0.5\">\u21B5</kbd>\n {{ 'palette.hint.run' | transloco }}\n </span>\n @if (mode() === 'tabs') {\n <span class=\"flex items-center gap-1.5\" data-testid=\"palette-hint-actions\">\n <kbd class=\"rounded border border-border px-1 py-0.5\">\u2192</kbd>\n {{ 'palette.hint.actions' | transloco }}\n </span>\n }\n <span class=\"ml-auto flex items-center gap-1.5\">\n <kbd class=\"rounded border border-border px-1 py-0.5\">Esc</kbd>\n {{ 'palette.hint.close' | transloco }}\n </span>\n </div>\n</div>\n" }]
14324
+ args: [{ selector: 'lw-command-palette', schemas: [CUSTOM_ELEMENTS_SCHEMA], imports: [TranslocoPipe], template: "<div class=\"flex max-h-[60vh] w-full flex-col overflow-hidden\">\n <div class=\"flex items-center gap-2 border-b border-border px-4\">\n <lw-icon name=\"search\" size=\"1.1rem\" class=\"text-content-muted\" />\n <input\n data-lw-autofocus\n type=\"text\"\n role=\"combobox\"\n aria-controls=\"lw-palette-list\"\n [attr.aria-expanded]=\"true\"\n [attr.aria-activedescendant]=\"activeId()\"\n [attr.aria-label]=\"title() | transloco\"\n [value]=\"query()\"\n [placeholder]=\"\n (mode() === 'tabs' ? 'palette.tabsPlaceholder' : 'palette.placeholder')\n | transloco\n \"\n class=\"flex-1 bg-transparent py-3 text-sm text-content outline-none placeholder:text-content-faint\"\n (input)=\"onQuery($event)\"\n (keydown.arrowdown)=\"move($event, 1)\"\n (keydown.arrowup)=\"move($event, -1)\"\n (keydown.arrowright)=\"openTabActions($event)\"\n (keydown.enter)=\"runActive($event)\"\n />\n </div>\n\n <ul\n id=\"lw-palette-list\"\n role=\"listbox\"\n class=\"min-h-0 flex-1 overflow-y-auto p-1\"\n >\n @for (entry of results(); track entry.id; let i = $index) {\n @if (recentCount() > 0 && i === 0) {\n <li\n role=\"presentation\"\n data-testid=\"palette-section-recent\"\n class=\"px-3 pt-2 pb-1 text-xs font-medium text-content-faint\"\n >\n {{ 'palette.recent' | transloco }}\n </li>\n }\n @if (recentCount() > 0 && i === recentCount()) {\n <li\n role=\"presentation\"\n data-testid=\"palette-section-all\"\n class=\"px-3 pt-2 pb-1 text-xs font-medium text-content-faint\"\n >\n {{ 'palette.all' | transloco }}\n </li>\n }\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <li\n role=\"option\"\n [id]=\"optionId(i)\"\n [attr.aria-selected]=\"i === activeIndex()\"\n class=\"flex cursor-pointer items-center gap-3 rounded-md px-3 py-2 text-sm\"\n [class]=\"\n i === activeIndex()\n ? 'bg-brand/10 text-brand-text'\n : 'text-content hover:bg-surface-overlay'\n \"\n (click)=\"select(entry)\"\n (mouseenter)=\"setActive(i)\"\n >\n @if (entry.icon; as icon) {\n <lw-icon [name]=\"icon\" size=\"1rem\" class=\"text-content-muted\" />\n }\n <span class=\"flex-1 truncate\">{{ entry.label }}</span>\n @if (timeOf(entry); as time) {\n <span class=\"shrink-0 text-xs text-content-faint\">{{ time }}</span>\n }\n @if (shortcutOf(entry); as shortcut) {\n <kbd\n class=\"rounded border border-border px-1.5 py-0.5 text-xs text-content-muted\"\n >{{ shortcut }}</kbd\n >\n }\n </li>\n } @empty {\n <li class=\"px-3 py-6 text-center text-sm text-content-muted\">\n {{\n (mode() === 'tabs' ? 'palette.tabsEmpty' : 'palette.empty') | transloco\n }}\n </li>\n }\n </ul>\n\n <div\n data-testid=\"palette-footer\"\n class=\"flex items-center gap-4 border-t border-border px-4 py-2 text-xs text-content-faint\"\n >\n <span class=\"flex items-center gap-1.5\">\n <kbd class=\"rounded border border-border px-1 py-0.5\">\u2191\u2193</kbd>\n {{ 'palette.hint.navigate' | transloco }}\n </span>\n <span class=\"flex items-center gap-1.5\">\n <kbd class=\"rounded border border-border px-1 py-0.5\">\u21B5</kbd>\n {{ 'palette.hint.run' | transloco }}\n </span>\n @if (mode() === 'tabs') {\n <span class=\"flex items-center gap-1.5\" data-testid=\"palette-hint-actions\">\n <kbd class=\"rounded border border-border px-1 py-0.5\">\u2192</kbd>\n {{ 'palette.hint.actions' | transloco }}\n </span>\n }\n <span class=\"ml-auto flex items-center gap-1.5\">\n <kbd class=\"rounded border border-border px-1 py-0.5\">Esc</kbd>\n {{ 'palette.hint.close' | transloco }}\n </span>\n </div>\n</div>\n" }]
14034
14325
  }] });
14035
14326
 
14036
14327
  class WorkspaceDialog {
@@ -14264,7 +14555,12 @@ function seedHostCommands(registry, layout, deps) {
14264
14555
  shortcut: 'mod+k',
14265
14556
  popout: true,
14266
14557
  run: () => {
14267
- dialogs.open(CommandPalette, { bare: true, size: 'lg', align: 'top' });
14558
+ dialogs.open(CommandPalette, {
14559
+ bare: true,
14560
+ size: 'lg',
14561
+ align: 'top',
14562
+ title: 'palette.title',
14563
+ });
14268
14564
  },
14269
14565
  });
14270
14566
  registry.addCommand({
@@ -14289,6 +14585,7 @@ function seedHostCommands(registry, layout, deps) {
14289
14585
  bare: true,
14290
14586
  size: 'lg',
14291
14587
  align: 'top',
14588
+ title: 'palette.quickOpenTitle',
14292
14589
  data: { mode: 'tabs' },
14293
14590
  });
14294
14591
  },
@@ -14801,6 +15098,16 @@ function accessCanMatch(access) {
14801
15098
  };
14802
15099
  }
14803
15100
 
15101
+ const keepPopout = (_route, state) => {
15102
+ if (!isPopoutUrl(inject(BootAddress).path)) {
15103
+ return true;
15104
+ }
15105
+ if (isDevMode()) {
15106
+ console.warn(popoutNavigationRefusal(normalizePath(state.url)));
15107
+ }
15108
+ return false;
15109
+ };
15110
+
14804
15111
  const settleWorkspace = async (_route, state) => {
14805
15112
  const claims = inject(WORKSPACE_CLAIMS);
14806
15113
  await claims.settle(normalizePath(state.url));
@@ -14841,6 +15148,7 @@ function buildContentRoutes(contentRoutes, omitted = [], retention = 'destroy')
14841
15148
  const placeholders = omitted.map((route) => ({
14842
15149
  path: route.path,
14843
15150
  component: RouteUnavailableView,
15151
+ canActivate: [keepPopout],
14844
15152
  data: { content: true, routePlaceholder: true },
14845
15153
  }));
14846
15154
  return [...buildRegisteredRoutes(contentRoutes, retention), ...placeholders];
@@ -14886,7 +15194,7 @@ function buildRegisteredRoutes(contentRoutes, retention) {
14886
15194
  const angular = {
14887
15195
  path: route.path,
14888
15196
  ...surfaceRoute(route, retained),
14889
- canActivate: [settleWorkspace],
15197
+ canActivate: [keepPopout, settleWorkspace],
14890
15198
  data: {
14891
15199
  content: true,
14892
15200
  chromeless: route.chromeless,
@@ -14905,6 +15213,7 @@ function buildRegisteredRoutes(contentRoutes, retention) {
14905
15213
  const placeholder = {
14906
15214
  path: route.path,
14907
15215
  component: AuthRequiredView,
15216
+ canActivate: [keepPopout],
14908
15217
  data: {
14909
15218
  content: true,
14910
15219
  authPlaceholder: true,
@@ -15733,6 +16042,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
15733
16042
  type: Service
15734
16043
  }] });
15735
16044
 
16045
+ /** Multi-provider token: each contribution adds one sandboxed plugin to load. */
16046
+ const FRAME_PLUGIN = new InjectionToken('FRAME_PLUGIN');
16047
+
15736
16048
  /** Multi-provider token: each contribution adds one plugin to load. */
15737
16049
  const PLUGIN = new InjectionToken('PLUGIN');
15738
16050
  /**
@@ -15743,6 +16055,8 @@ const PLUGIN = new InjectionToken('PLUGIN');
15743
16055
  class PluginRuntime {
15744
16056
  grants = inject(CapabilityGrantService);
15745
16057
  enablement = inject(PluginEnablementService);
16058
+ required = inject(REQUIRED_PLUGINS);
16059
+ framePlugins = inject(FRAME_PLUGIN, { optional: true }) ?? [];
15746
16060
  factory = inject(HostContextFactory);
15747
16061
  injector = inject(EnvironmentInjector);
15748
16062
  plugins = inject(PLUGIN, { optional: true }) ?? [];
@@ -15756,6 +16070,7 @@ class PluginRuntime {
15756
16070
  for (const plugin of this.plugins) {
15757
16071
  this.enablement.register(plugin.manifest.id, plugin.manifest.name ?? plugin.manifest.id);
15758
16072
  }
16073
+ this.reportUncomposedRequirements();
15759
16074
  this.reconcile(this.enablement.disabled());
15760
16075
  effect(() => {
15761
16076
  const disabled = this.enablement.disabled();
@@ -15822,6 +16137,19 @@ class PluginRuntime {
15822
16137
  this.active.delete(id);
15823
16138
  console.error(`Plugin "${id}" activation failed`, error);
15824
16139
  }
16140
+ reportUncomposedRequirements() {
16141
+ if (!isDevMode()) {
16142
+ return;
16143
+ }
16144
+ const composed = new Set([
16145
+ ...this.plugins.map((plugin) => plugin.manifest.id),
16146
+ ...this.framePlugins.map((plugin) => plugin.id),
16147
+ ]);
16148
+ const missing = this.required.filter((id) => !composed.has(id));
16149
+ if (missing.length > 0) {
16150
+ console.warn(`provideRequiredPlugins names ${missing.join(', ')}, which this distribution does not compose — ignored.`);
16151
+ }
16152
+ }
15825
16153
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: PluginRuntime, deps: [], target: i0.ɵɵFactoryTarget.Service });
15826
16154
  static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.0", ngImport: i0, type: PluginRuntime });
15827
16155
  }
@@ -15840,9 +16168,6 @@ function providePlugins(...plugins) {
15840
16168
  ];
15841
16169
  }
15842
16170
 
15843
- /** Multi-provider token: each contribution adds one sandboxed plugin to load. */
15844
- const FRAME_PLUGIN = new InjectionToken('FRAME_PLUGIN');
15845
-
15846
16171
  const STORAGE_KEY = 'lw.shell.installed-plugins';
15847
16172
  /**
15848
16173
  * The user's installed community plugins. Holds only the state: which catalog entries the
@@ -17246,5 +17571,5 @@ function provideIcons(icons) {
17246
17571
  * Generated bundle index. Do not edit.
17247
17572
  */
17248
17573
 
17249
- export { AUTH_SOURCE, AuthContext, BAR_ITEM, CAPABILITY_GRANTS, COMMAND_INVOKER, CapabilityGrantService, CommandInvocationService, CommandService, ContentTabsService, ContributionRegistry, DEFAULT_LAYOUT, DEFAULT_SHELL_FEATURES, DEVICE_LEVEL_KEYS, DialogOutlet, DialogService, FRAME_PLUGIN, FramePluginRuntime, KeybindingService, LW_BUTTON_TAG, LW_ICON_TAG, LW_MARKDOWN_TAG, LW_TOOLTIP_TAG, LocalStorageStore, LwButton, LwButtonElement, LwIconElement, LwMarkdownElement, LwSettingRow, LwSpinner, LwTooltipElement, LwVersion, NotificationService, PLUGIN, PLUGIN_CATALOG, PluginEnablementService, PluginInstallService, PluginRuntime, PopoutService, RAIL_ITEM, SETTINGS_STORE, SHELL_FEATURES, SHELL_LAYOUT, SettingsService, Shell, StateSyncService, TRANSLATION_NAMESPACES, TRANSLATION_OVERRIDES, ThemeService, ToastOutlet, UpdateBadge, UpdateService, VIEW, VersionService, WORKING_STATE_STORE, WORKSPACE_DEFINITIONS, defineLwButton, defineLwIcon, defineLwMarkdown, defineLwTooltip, effectiveCapabilities, formatChord, provideAuthSource, provideBarItems, provideCapabilityGrants, provideCommandPaletteEntry, provideFramePlugins, provideIcons, provideIdentityScopedStores, provideLayout, providePluginCatalog, providePlugins, provideQuickOpenEntry, provideRailItems, provideSettingsStore, provideShell, provideShellFeatures, provideShellRouter, provideTabAddressResolver, provideTranslationNamespaces, provideTranslationOverrides, provideUnauthorizedRedirect, provideViews, provideWorkingStateStore, provideWorkspaces, urlPluginCatalog };
17574
+ export { AUTH_SOURCE, AuthContext, BAR_ITEM, CAPABILITY_GRANTS, COMMAND_INVOKER, CapabilityGrantService, CommandInvocationService, CommandService, ContentTabsService, ContributionRegistry, DEFAULT_LAYOUT, DEFAULT_SHELL_FEATURES, DEVICE_LEVEL_KEYS, DialogOutlet, DialogService, FRAME_PLUGIN, FramePluginRuntime, KeybindingService, LW_BUTTON_TAG, LW_ICON_TAG, LW_MARKDOWN_TAG, LW_TOOLTIP_TAG, LocalStorageStore, LwButton, LwButtonElement, LwIconElement, LwMarkdownElement, LwSettingRow, LwSpinner, LwTooltipElement, LwVersion, NotificationService, PLUGIN, PLUGIN_CATALOG, PluginEnablementService, PluginInstallService, PluginRuntime, PopoutService, RAIL_ITEM, REQUIRED_PLUGINS, SETTINGS_STORE, SHELL_FEATURES, SHELL_LAYOUT, SettingsService, Shell, StateSyncService, TRANSLATION_NAMESPACES, TRANSLATION_OVERRIDES, ThemeService, ToastOutlet, UpdateBadge, UpdateService, VIEW, VersionService, WORKING_STATE_STORE, WORKSPACE_DEFINITIONS, defineLwButton, defineLwIcon, defineLwMarkdown, defineLwTooltip, effectiveCapabilities, formatChord, provideAuthSource, provideBarItems, provideCapabilityGrants, provideCommandPaletteEntry, provideFramePlugins, provideIcons, provideIdentityScopedStores, provideLayout, providePluginCatalog, providePlugins, provideQuickOpenEntry, provideRailItems, provideRequiredPlugins, provideSettingsStore, provideShell, provideShellFeatures, provideShellRouter, provideTabAddressResolver, provideTranslationNamespaces, provideTranslationOverrides, provideUnauthorizedRedirect, provideViews, provideWorkingStateStore, provideWorkspaces, urlPluginCatalog };
17250
17575
  //# sourceMappingURL=loomweaver-shell.mjs.map