@loomweaver/shell 0.7.7 → 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 });
@@ -6155,13 +6362,17 @@ class ShellRail {
6155
6362
  ...(ngDevMode ? [{ debugName: "connectedRails" }] : /* istanbul ignore next */ []));
6156
6363
  labelKey = computed(() => this.region().dock === 'right' ? 'rail.labelRight' : 'rail.label', /* @ts-ignore */
6157
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 */ []));
6158
6367
  reorderable = this.features.reorder;
6159
6368
  draggable = this.features.reorder || this.features.moveItems;
6160
6369
  registered = computed(() => this.registry
6161
6370
  .railItems()
6162
6371
  .filter((item) => this.railItems.regionOf(item.id, item.rail) === this.region().id)
6163
6372
  .filter((item) => this.auth.visible(item.access))
6164
- .filter((item) => item.workspace !== undefined || this.commands.triggerable(item))
6373
+ .filter((item) => item.workspace !== undefined ||
6374
+ menuOnActivate(item) !== undefined ||
6375
+ this.commands.triggerable(item))
6165
6376
  .toSorted((a, b) => (a.order ?? 0) - (b.order ?? 0)), /* @ts-ignore */
6166
6377
  ...(ngDevMode ? [{ debugName: "registered" }] : /* istanbul ignore next */ []));
6167
6378
  items = computed(() => {
@@ -6174,16 +6385,26 @@ class ShellRail {
6174
6385
  return [...top, ...bottom];
6175
6386
  }, /* @ts-ignore */
6176
6387
  ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
6388
+ brokenPictures = signal(new Set(), /* @ts-ignore */
6389
+ ...(ngDevMode ? [{ debugName: "brokenPictures" }] : /* istanbul ignore next */ []));
6177
6390
  firstBottomId = computed(() => this.items().find((item) => item.anchor === 'bottom')?.id, /* @ts-ignore */
6178
6391
  ...(ngDevMode ? [{ debugName: "firstBottomId" }] : /* istanbul ignore next */ []));
6179
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
+ }
6180
6399
  disabled(item) {
6181
6400
  return this.auth.disabled(item.access);
6182
6401
  }
6183
6402
  menusFor(item) {
6184
- return item.menu
6185
- ? [RAIL_ITEM_CONTEXT_MENU, item.menu]
6186
- : [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);
6187
6408
  }
6188
6409
  onKeydown(event) {
6189
6410
  const dock = this.dockForChord(event);
@@ -6205,6 +6426,10 @@ class ShellRail {
6205
6426
  run(item) {
6206
6427
  if (this.disabled(item))
6207
6428
  return;
6429
+ warnMenuTriggerConflict(item);
6430
+ if (menuOnActivate(item)) {
6431
+ return;
6432
+ }
6208
6433
  const workspace = item.workspace;
6209
6434
  if (workspace !== undefined) {
6210
6435
  void this.workspaces.switchTo(workspace);
@@ -6242,7 +6467,7 @@ class ShellRail {
6242
6467
  return event.key === 'ArrowLeft' ? 'left' : null;
6243
6468
  }
6244
6469
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ShellRail, deps: [], target: i0.ɵɵFactoryTarget.Component });
6245
- 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" }] });
6246
6471
  }
6247
6472
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ShellRail, decorators: [{
6248
6473
  type: Component,
@@ -6251,8 +6476,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
6251
6476
  Reorderable,
6252
6477
  CdkDropList,
6253
6478
  CdkDrag,
6254
- ContextMenuDirective,
6255
- ], 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" }]
6256
6481
  }], propDecorators: { region: [{ type: i0.Input, args: [{ isSignal: true, alias: "region", required: true }] }] } });
6257
6482
 
6258
6483
  const STORAGE_PREFIX$2 = 'lw.shell.view-state:';
@@ -6567,8 +6792,9 @@ class ViewInstanceSwitcher {
6567
6792
  if (!this.viewInstances.isDefault(viewId, activeId)) {
6568
6793
  entries.push({ key: 'rename', label: t('viewInstance.rename') }, { key: 'delete', label: t('viewInstance.delete') });
6569
6794
  }
6570
- const rect = event.currentTarget.getBoundingClientRect();
6571
- 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);
6572
6798
  }
6573
6799
  onPick(key) {
6574
6800
  const viewId = this.view().id;
@@ -6641,11 +6867,11 @@ class ViewInstanceSwitcher {
6641
6867
  };
6642
6868
  }
6643
6869
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ViewInstanceSwitcher, deps: [], target: i0.ɵɵFactoryTarget.Component });
6644
- 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" }] });
6645
6871
  }
6646
6872
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ViewInstanceSwitcher, decorators: [{
6647
6873
  type: Component,
6648
- 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" }]
6649
6875
  }], propDecorators: { view: [{ type: i0.Input, args: [{ isSignal: true, alias: "view", required: true }] }], beforeSwitch: [{ type: i0.Output, args: ["beforeSwitch"] }] } });
6650
6876
 
6651
6877
  class PaneChromeService {
@@ -7172,7 +7398,8 @@ class PaneTabStrip {
7172
7398
  this.paneDrag.stop();
7173
7399
  }
7174
7400
  openOverflow(event) {
7175
- const anchor = event.currentTarget.getBoundingClientRect();
7401
+ const control = event.currentTarget;
7402
+ const anchor = control.getBoundingClientRect();
7176
7403
  const entries = this.tabs().map((tab) => ({
7177
7404
  key: tab.path,
7178
7405
  label: this.label(tab),
@@ -7184,7 +7411,7 @@ class PaneTabStrip {
7184
7411
  if (tab) {
7185
7412
  this.selectTab.emit(tab);
7186
7413
  }
7187
- });
7414
+ }, control);
7188
7415
  }
7189
7416
  onSelectTab(tab) {
7190
7417
  if (tab.path !== this.activeId()) {
@@ -7297,19 +7524,19 @@ class PaneTabStrip {
7297
7524
  return resolveTitle(tab, (key) => this.transloco.translate(key));
7298
7525
  }
7299
7526
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: PaneTabStrip, deps: [], target: i0.ɵɵFactoryTarget.Component });
7300
- 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" }] });
7301
7528
  }
7302
7529
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: PaneTabStrip, decorators: [{
7303
7530
  type: Component,
7304
7531
  args: [{ selector: 'lw-pane-tab-strip', imports: [
7305
7532
  NgTemplateOutlet,
7306
7533
  TranslocoPipe,
7307
- ContextMenuDirective,
7534
+ MenuTriggerDirective,
7308
7535
  Reorderable,
7309
7536
  CdkDropList,
7310
7537
  CdkDrag,
7311
7538
  CdkDragHandle,
7312
- ], 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" }]
7313
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 }] }] } });
7314
7541
 
7315
7542
  class PaneToolbar {
@@ -8868,7 +9095,7 @@ class PaneTargetPicker {
8868
9095
  present(targets, anchor, onPick) {
8869
9096
  const rect = anchor.getBoundingClientRect();
8870
9097
  const entries = paneTargetEntries(targets, (key) => this.transloco.translate(key));
8871
- 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);
8872
9099
  }
8873
9100
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: PaneTargetPicker, deps: [], target: i0.ɵɵFactoryTarget.Service });
8874
9101
  static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.0", ngImport: i0, type: PaneTargetPicker });
@@ -10075,11 +10302,11 @@ class ShellSidebarHeader {
10075
10302
  return this.layout.regions.find((r) => r.type === 'panel' && r.dock === dock && r.id !== this.region().id);
10076
10303
  }
10077
10304
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ShellSidebarHeader, deps: [], target: i0.ɵɵFactoryTarget.Component });
10078
- 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" }] });
10079
10306
  }
10080
10307
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ShellSidebarHeader, decorators: [{
10081
10308
  type: Component,
10082
- 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" }]
10083
10310
  }], propDecorators: { region: [{ type: i0.Input, args: [{ isSignal: true, alias: "region", required: true }] }], context: [{ type: i0.Input, args: [{ isSignal: true, alias: "context", required: false }] }] } });
10084
10311
 
10085
10312
  const TAB_CONTEXT_MENU = 'content/tab/context';
@@ -11369,7 +11596,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
11369
11596
  // GENERATED — do not edit by hand.
11370
11597
  // Written by tools/stamp-version.mjs from <Version> in Directory.Build.props.
11371
11598
  // Single source of truth: Directory.Build.props (bump via scripts/bump-version.sh).
11372
- const APP_VERSION = '0.7.7';
11599
+ const APP_VERSION = '0.7.8';
11373
11600
 
11374
11601
  /**
11375
11602
  * The running build's version, sourced from `<Version>` in Directory.Build.props
@@ -13386,6 +13613,37 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
13386
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" }]
13387
13614
  }] });
13388
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
+
13389
13647
  const STORAGE_KEY$3 = 'lw.shell.disabled-plugins';
13390
13648
  /**
13391
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
@@ -13399,15 +13657,27 @@ const STORAGE_KEY$3 = 'lw.shell.disabled-plugins';
13399
13657
  class PluginEnablementService {
13400
13658
  store = inject(SETTINGS_STORE);
13401
13659
  sync = inject(StateSyncService);
13660
+ required = new Set(inject(REQUIRED_PLUGINS));
13402
13661
  disabledSet = signal(parseIdSet(this.store.peek?.(STORAGE_KEY$3)), /* @ts-ignore */
13403
13662
  ...(ngDevMode ? [{ debugName: "disabledSet" }] : /* istanbul ignore next */ []));
13404
13663
  names = signal(new Map(), /* @ts-ignore */
13405
13664
  ...(ngDevMode ? [{ debugName: "names" }] : /* istanbul ignore next */ []));
13406
- /** The disabled plugin ids (reactive) — a runtime reconciles activation against this. */
13407
- 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 */ []));
13408
13678
  /** Every known plugin with its enabled state, for the permissions settings surface. */
13409
13679
  plugins = computed(() => {
13410
- const disabled = this.disabledSet();
13680
+ const disabled = this.disabled();
13411
13681
  return [...this.names().entries()]
13412
13682
  .map(([id, name]) => ({ id, name, enabled: !disabled.has(id) }))
13413
13683
  .toSorted((a, b) => a.name.localeCompare(b.name));
@@ -13417,6 +13687,10 @@ class PluginEnablementService {
13417
13687
  hydrateAsync(this.store, STORAGE_KEY$3, (raw) => this.disabledSet.set(parseIdSet(raw)));
13418
13688
  this.sync.register('settings', STORAGE_KEY$3, (raw) => this.disabledSet.set(parseIdSet(raw)));
13419
13689
  }
13690
+ /** Whether the distribution declared it cannot run without this plugin. */
13691
+ isRequired(id) {
13692
+ return this.required.has(id);
13693
+ }
13420
13694
  /** Records a plugin so the permissions surface can list it (enabled or not). Idempotent. A runtime calls it for every plugin it knows. */
13421
13695
  register(id, name) {
13422
13696
  this.names.update((map) => map.has(id) ? map : new Map(map).set(id, name));
@@ -13434,10 +13708,17 @@ class PluginEnablementService {
13434
13708
  }
13435
13709
  /** Whether `id` is currently enabled (default: yes — a plugin is on until the user turns it off). */
13436
13710
  isEnabled(id) {
13437
- return !this.disabledSet().has(id);
13711
+ return !this.disabled().has(id);
13438
13712
  }
13439
- /** 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
+ */
13440
13718
  setEnabled(id, enabled) {
13719
+ if (!enabled && this.required.has(id)) {
13720
+ return;
13721
+ }
13441
13722
  const next = toggledIdSet(this.disabledSet(), id, !enabled);
13442
13723
  this.disabledSet.set(next);
13443
13724
  void this.store.set(STORAGE_KEY$3, JSON.stringify([...next]));
@@ -13651,10 +13932,12 @@ class PermissionsSettings {
13651
13932
  const caps = this.grants.permissions();
13652
13933
  return this.enablement.plugins().map((plugin) => {
13653
13934
  const provided = this.deployment.isDeployed(plugin.id);
13935
+ const required = this.enablement.isRequired(plugin.id);
13654
13936
  return {
13655
13937
  ...plugin,
13656
13938
  enabled: provided || plugin.enabled,
13657
13939
  provided,
13940
+ required,
13658
13941
  rungNote: rungNoteKey(this.isolation.rungOf(plugin.id)),
13659
13942
  capabilities: caps.find((entry) => entry.pluginId === plugin.id)?.capabilities ??
13660
13943
  [],
@@ -13669,11 +13952,11 @@ class PermissionsSettings {
13669
13952
  this.grants.setGranted(pluginId, capability, event.target.checked);
13670
13953
  }
13671
13954
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: PermissionsSettings, deps: [], target: i0.ɵɵFactoryTarget.Component });
13672
- 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" }] });
13673
13956
  }
13674
13957
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: PermissionsSettings, decorators: [{
13675
13958
  type: Component,
13676
- 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" }]
13677
13960
  }] });
13678
13961
 
13679
13962
  function registerDefaultSettings(settings) {
@@ -15759,6 +16042,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
15759
16042
  type: Service
15760
16043
  }] });
15761
16044
 
16045
+ /** Multi-provider token: each contribution adds one sandboxed plugin to load. */
16046
+ const FRAME_PLUGIN = new InjectionToken('FRAME_PLUGIN');
16047
+
15762
16048
  /** Multi-provider token: each contribution adds one plugin to load. */
15763
16049
  const PLUGIN = new InjectionToken('PLUGIN');
15764
16050
  /**
@@ -15769,6 +16055,8 @@ const PLUGIN = new InjectionToken('PLUGIN');
15769
16055
  class PluginRuntime {
15770
16056
  grants = inject(CapabilityGrantService);
15771
16057
  enablement = inject(PluginEnablementService);
16058
+ required = inject(REQUIRED_PLUGINS);
16059
+ framePlugins = inject(FRAME_PLUGIN, { optional: true }) ?? [];
15772
16060
  factory = inject(HostContextFactory);
15773
16061
  injector = inject(EnvironmentInjector);
15774
16062
  plugins = inject(PLUGIN, { optional: true }) ?? [];
@@ -15782,6 +16070,7 @@ class PluginRuntime {
15782
16070
  for (const plugin of this.plugins) {
15783
16071
  this.enablement.register(plugin.manifest.id, plugin.manifest.name ?? plugin.manifest.id);
15784
16072
  }
16073
+ this.reportUncomposedRequirements();
15785
16074
  this.reconcile(this.enablement.disabled());
15786
16075
  effect(() => {
15787
16076
  const disabled = this.enablement.disabled();
@@ -15848,6 +16137,19 @@ class PluginRuntime {
15848
16137
  this.active.delete(id);
15849
16138
  console.error(`Plugin "${id}" activation failed`, error);
15850
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
+ }
15851
16153
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: PluginRuntime, deps: [], target: i0.ɵɵFactoryTarget.Service });
15852
16154
  static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.0", ngImport: i0, type: PluginRuntime });
15853
16155
  }
@@ -15866,9 +16168,6 @@ function providePlugins(...plugins) {
15866
16168
  ];
15867
16169
  }
15868
16170
 
15869
- /** Multi-provider token: each contribution adds one sandboxed plugin to load. */
15870
- const FRAME_PLUGIN = new InjectionToken('FRAME_PLUGIN');
15871
-
15872
16171
  const STORAGE_KEY = 'lw.shell.installed-plugins';
15873
16172
  /**
15874
16173
  * The user's installed community plugins. Holds only the state: which catalog entries the
@@ -17272,5 +17571,5 @@ function provideIcons(icons) {
17272
17571
  * Generated bundle index. Do not edit.
17273
17572
  */
17274
17573
 
17275
- 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 };
17276
17575
  //# sourceMappingURL=loomweaver-shell.mjs.map