@memberjunction/ng-explorer-core 5.21.0 → 5.22.0

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.
Files changed (52) hide show
  1. package/dist/generated/lazy-feature-config.d.ts +19 -0
  2. package/dist/generated/lazy-feature-config.d.ts.map +1 -0
  3. package/dist/generated/lazy-feature-config.js +143 -0
  4. package/dist/generated/lazy-feature-config.js.map +1 -0
  5. package/dist/lib/command-palette/command-palette.component.d.ts +10 -1
  6. package/dist/lib/command-palette/command-palette.component.d.ts.map +1 -1
  7. package/dist/lib/command-palette/command-palette.component.js +68 -16
  8. package/dist/lib/command-palette/command-palette.component.js.map +1 -1
  9. package/dist/lib/generic/resource-container-component.d.ts +0 -1
  10. package/dist/lib/generic/resource-container-component.d.ts.map +1 -1
  11. package/dist/lib/generic/resource-container-component.js +3 -12
  12. package/dist/lib/generic/resource-container-component.js.map +1 -1
  13. package/dist/lib/resource-wrappers/chat-collections-resource.component.d.ts +4 -0
  14. package/dist/lib/resource-wrappers/chat-collections-resource.component.d.ts.map +1 -1
  15. package/dist/lib/resource-wrappers/chat-collections-resource.component.js +46 -1
  16. package/dist/lib/resource-wrappers/chat-collections-resource.component.js.map +1 -1
  17. package/dist/lib/resource-wrappers/chat-conversations-resource.component.d.ts +4 -0
  18. package/dist/lib/resource-wrappers/chat-conversations-resource.component.d.ts.map +1 -1
  19. package/dist/lib/resource-wrappers/chat-conversations-resource.component.js +21 -0
  20. package/dist/lib/resource-wrappers/chat-conversations-resource.component.js.map +1 -1
  21. package/dist/lib/resource-wrappers/dashboard-resource.component.d.ts +0 -1
  22. package/dist/lib/resource-wrappers/dashboard-resource.component.d.ts.map +1 -1
  23. package/dist/lib/resource-wrappers/dashboard-resource.component.js +4 -12
  24. package/dist/lib/resource-wrappers/dashboard-resource.component.js.map +1 -1
  25. package/dist/lib/services/lazy-module-registry.d.ts +24 -9
  26. package/dist/lib/services/lazy-module-registry.d.ts.map +1 -1
  27. package/dist/lib/services/lazy-module-registry.js +32 -13
  28. package/dist/lib/services/lazy-module-registry.js.map +1 -1
  29. package/dist/lib/shell/components/tabs/tab-container.component.d.ts +23 -1
  30. package/dist/lib/shell/components/tabs/tab-container.component.d.ts.map +1 -1
  31. package/dist/lib/shell/components/tabs/tab-container.component.js +205 -43
  32. package/dist/lib/shell/components/tabs/tab-container.component.js.map +1 -1
  33. package/dist/lib/shell/shell.component.d.ts +26 -2
  34. package/dist/lib/shell/shell.component.d.ts.map +1 -1
  35. package/dist/lib/shell/shell.component.js +211 -41
  36. package/dist/lib/shell/shell.component.js.map +1 -1
  37. package/dist/lib/user-menu/base-user-menu.d.ts +4 -0
  38. package/dist/lib/user-menu/base-user-menu.d.ts.map +1 -1
  39. package/dist/lib/user-menu/base-user-menu.js +26 -0
  40. package/dist/lib/user-menu/base-user-menu.js.map +1 -1
  41. package/dist/lib/user-menu/user-menu.types.d.ts +20 -0
  42. package/dist/lib/user-menu/user-menu.types.d.ts.map +1 -1
  43. package/dist/lib/user-menu/user-menu.types.js.map +1 -1
  44. package/dist/public-api.d.ts +1 -1
  45. package/dist/public-api.d.ts.map +1 -1
  46. package/dist/public-api.js +1 -1
  47. package/dist/public-api.js.map +1 -1
  48. package/package.json +35 -34
  49. package/dist/lib/services/lazy-feature-config.d.ts +0 -16
  50. package/dist/lib/services/lazy-feature-config.d.ts.map +0 -1
  51. package/dist/lib/services/lazy-feature-config.js +0 -113
  52. package/dist/lib/services/lazy-feature-config.js.map +0 -1
@@ -1,10 +1,10 @@
1
1
  import { Component, ViewChild, createComponent, ViewEncapsulation, HostListener, Output, EventEmitter, inject } from '@angular/core';
2
2
  import { MJGlobal } from '@memberjunction/global';
3
- import { BaseResourceComponent } from '@memberjunction/ng-shared';
3
+ import { BaseResourceComponent, HomeAppPinService } from '@memberjunction/ng-shared';
4
4
  import { ResourceData, ResourcePermissionEngine } from '@memberjunction/core-entities';
5
+ import { MJNotificationService } from '@memberjunction/ng-notifications';
5
6
  import { LogError, Metadata } from '@memberjunction/core';
6
7
  import { ComponentCacheManager } from './component-cache-manager';
7
- import { LazyModuleRegistry } from '../../../services/lazy-module-registry';
8
8
  import * as i0 from "@angular/core";
9
9
  import * as i1 from "@memberjunction/ng-base-application";
10
10
  const _c0 = ["glContainer"];
@@ -15,6 +15,19 @@ function TabContainerComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
15
15
  function TabContainerComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
16
16
  i0.ɵɵelement(0, "div", 4, 1);
17
17
  } }
18
+ function TabContainerComponent_Conditional_3_Conditional_8_Template(rf, ctx) { if (rf & 1) {
19
+ i0.ɵɵelementStart(0, "span");
20
+ i0.ɵɵtext(1, "Pinned to Home");
21
+ i0.ɵɵelementEnd();
22
+ i0.ɵɵelementStart(2, "span", 14);
23
+ i0.ɵɵelement(3, "i", 15);
24
+ i0.ɵɵelementEnd();
25
+ } }
26
+ function TabContainerComponent_Conditional_3_Conditional_9_Template(rf, ctx) { if (rf & 1) {
27
+ i0.ɵɵelementStart(0, "span");
28
+ i0.ɵɵtext(1, "Pin to Home");
29
+ i0.ɵɵelementEnd();
30
+ } }
18
31
  function TabContainerComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
19
32
  const _r1 = i0.ɵɵgetCurrentView();
20
33
  i0.ɵɵelementStart(0, "div", 6)(1, "div", 7);
@@ -25,28 +38,38 @@ function TabContainerComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
25
38
  i0.ɵɵelementEnd()();
26
39
  i0.ɵɵelement(5, "div", 9);
27
40
  i0.ɵɵelementStart(6, "div", 7);
28
- i0.ɵɵlistener("click", function TabContainerComponent_Conditional_3_Template_div_click_6_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onContextClose()); });
41
+ i0.ɵɵlistener("click", function TabContainerComponent_Conditional_3_Template_div_click_6_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onContextPinToHome()); });
29
42
  i0.ɵɵelement(7, "i", 10);
30
- i0.ɵɵelementStart(8, "span");
31
- i0.ɵɵtext(9, "Close Tab");
43
+ i0.ɵɵconditionalCreate(8, TabContainerComponent_Conditional_3_Conditional_8_Template, 4, 0)(9, TabContainerComponent_Conditional_3_Conditional_9_Template, 2, 0, "span");
44
+ i0.ɵɵelementEnd();
45
+ i0.ɵɵelement(10, "div", 9);
46
+ i0.ɵɵelementStart(11, "div", 7);
47
+ i0.ɵɵlistener("click", function TabContainerComponent_Conditional_3_Template_div_click_11_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onContextClose()); });
48
+ i0.ɵɵelement(12, "i", 11);
49
+ i0.ɵɵelementStart(13, "span");
50
+ i0.ɵɵtext(14, "Close Tab");
32
51
  i0.ɵɵelementEnd()();
33
- i0.ɵɵelementStart(10, "div", 7);
34
- i0.ɵɵlistener("click", function TabContainerComponent_Conditional_3_Template_div_click_10_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onContextCloseOthers()); });
35
- i0.ɵɵelement(11, "i", 11);
36
- i0.ɵɵelementStart(12, "span");
37
- i0.ɵɵtext(13, "Close Others");
52
+ i0.ɵɵelementStart(15, "div", 7);
53
+ i0.ɵɵlistener("click", function TabContainerComponent_Conditional_3_Template_div_click_15_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onContextCloseOthers()); });
54
+ i0.ɵɵelement(16, "i", 12);
55
+ i0.ɵɵelementStart(17, "span");
56
+ i0.ɵɵtext(18, "Close Others");
38
57
  i0.ɵɵelementEnd()();
39
- i0.ɵɵelementStart(14, "div", 7);
40
- i0.ɵɵlistener("click", function TabContainerComponent_Conditional_3_Template_div_click_14_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onContextCloseToRight()); });
41
- i0.ɵɵelement(15, "i", 12);
42
- i0.ɵɵelementStart(16, "span");
43
- i0.ɵɵtext(17, "Close to Right");
58
+ i0.ɵɵelementStart(19, "div", 7);
59
+ i0.ɵɵlistener("click", function TabContainerComponent_Conditional_3_Template_div_click_19_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onContextCloseToRight()); });
60
+ i0.ɵɵelement(20, "i", 13);
61
+ i0.ɵɵelementStart(21, "span");
62
+ i0.ɵɵtext(22, "Close to Right");
44
63
  i0.ɵɵelementEnd()()();
45
64
  } if (rf & 2) {
46
65
  const ctx_r1 = i0.ɵɵnextContext();
47
66
  i0.ɵɵstyleProp("left", ctx_r1.contextMenuX, "px")("top", ctx_r1.contextMenuY, "px");
48
67
  i0.ɵɵadvance(4);
49
68
  i0.ɵɵtextInterpolate(ctx_r1.isContextTabPinned ? "Unpin Tab" : "Pin Tab");
69
+ i0.ɵɵadvance(2);
70
+ i0.ɵɵclassProp("new-item", !ctx_r1.isContextTabPinnedToHome)("disabled", ctx_r1.isContextTabPinnedToHome);
71
+ i0.ɵɵadvance(2);
72
+ i0.ɵɵconditional(ctx_r1.isContextTabPinnedToHome ? 8 : 9);
50
73
  } }
51
74
  /**
52
75
  * Container for Golden Layout tabs with app-colored styling.
@@ -78,7 +101,7 @@ export class TabContainerComponent {
78
101
  * The shell can use this to show an error dialog and redirect.
79
102
  */
80
103
  layoutInitError = new EventEmitter();
81
- lazyRegistry = inject(LazyModuleRegistry);
104
+ pinService = inject(HomeAppPinService);
82
105
  subscriptions = [];
83
106
  layoutInitRetryCount = 0;
84
107
  MAX_LAYOUT_INIT_RETRIES = 5;
@@ -86,6 +109,10 @@ export class TabContainerComponent {
86
109
  layoutRestorationComplete = false; // True only AFTER layout is fully restored/created
87
110
  // Track component references for cleanup (legacy - keep for backward compat during transition)
88
111
  componentRefs = new Map();
112
+ // Guard against concurrent loadTabContent calls for the same tab.
113
+ // When a tab's content changes while active, both the reload path (workspace config subscription)
114
+ // and onTabShown can race to call loadTabContent, resulting in duplicate component rendering.
115
+ tabsCurrentlyLoading = new Set();
89
116
  // NEW: Smart component cache for preserving state across tab switches
90
117
  cacheManager;
91
118
  // Single-resource mode: render component directly without Golden Layout
@@ -412,14 +439,8 @@ export class TabContainerComponent {
412
439
  this.singleResourceComponentRef = cached.componentRef;
413
440
  return;
414
441
  }
415
- // Get the component registration (with lazy loading fallback)
416
- let resourceReg = MJGlobal.Instance.ClassFactory.GetRegistration(BaseResourceComponent, driverClass);
417
- if (!resourceReg) {
418
- const loaded = await this.lazyRegistry.Load(driverClass);
419
- if (loaded) {
420
- resourceReg = MJGlobal.Instance.ClassFactory.GetRegistration(BaseResourceComponent, driverClass);
421
- }
422
- }
442
+ // Get the component registration (with lazy loading fallback via ClassFactory)
443
+ const resourceReg = await MJGlobal.Instance.ClassFactory.GetRegistrationAsync(BaseResourceComponent, driverClass);
423
444
  if (!resourceReg) {
424
445
  LogError(`Unable to find resource registration for driver class: ${driverClass}`);
425
446
  return;
@@ -439,6 +460,15 @@ export class TabContainerComponent {
439
460
  instance.LoadCompleteEvent = () => {
440
461
  this.emitFirstLoadCompleteOnce();
441
462
  };
463
+ // Wire up display name change for single-resource mode
464
+ // Use a closure that resolves the tab ID at call time, not at wire-up time,
465
+ // because the tab may not be active yet when the component is first created.
466
+ instance.DisplayNameChangedEvent = (newName) => {
467
+ const tabId = this.workspaceManager.GetActiveTabId();
468
+ if (tabId) {
469
+ this.workspaceManager.UpdateTabTitle(tabId, newName);
470
+ }
471
+ };
442
472
  // Get the native element and append to container
443
473
  const nativeElement = componentRef.hostView.rootNodes[0];
444
474
  container.appendChild(nativeElement);
@@ -517,6 +547,13 @@ export class TabContainerComponent {
517
547
  * Uses component cache to reuse components for same resources
518
548
  */
519
549
  async loadTabContent(tabId, container) {
550
+ // Per-tab guard: prevent concurrent loads of the same tab content.
551
+ // This can happen when a tab's content changes while active — both the workspace
552
+ // config subscription reload path and onTabShown can race to call this method.
553
+ if (this.tabsCurrentlyLoading.has(tabId)) {
554
+ return;
555
+ }
556
+ this.tabsCurrentlyLoading.add(tabId);
520
557
  try {
521
558
  const tab = this.workspaceManager.GetTab(tabId);
522
559
  if (!tab) {
@@ -555,14 +592,8 @@ export class TabContainerComponent {
555
592
  }
556
593
  return;
557
594
  }
558
- // Get the component registration using the driver class (with lazy loading fallback)
559
- let resourceReg = MJGlobal.Instance.ClassFactory.GetRegistration(BaseResourceComponent, driverClass);
560
- if (!resourceReg) {
561
- const loaded = await this.lazyRegistry.Load(driverClass);
562
- if (loaded) {
563
- resourceReg = MJGlobal.Instance.ClassFactory.GetRegistration(BaseResourceComponent, driverClass);
564
- }
565
- }
595
+ // Get the component registration using the driver class (with lazy loading fallback via ClassFactory)
596
+ const resourceReg = await MJGlobal.Instance.ClassFactory.GetRegistrationAsync(BaseResourceComponent, driverClass);
566
597
  if (!resourceReg) {
567
598
  LogError(`Unable to find resource registration for driver class: ${driverClass}`);
568
599
  return;
@@ -588,6 +619,11 @@ export class TabContainerComponent {
588
619
  // TODO: Implement UpdateTabTitle in WorkspaceStateManager
589
620
  }
590
621
  };
622
+ // Wire up display name change notifications
623
+ instance.DisplayNameChangedEvent = (newName) => {
624
+ this.layoutManager.UpdateTabStyle(tabId, { title: newName });
625
+ this.workspaceManager.UpdateTabTitle(tabId, newName);
626
+ };
591
627
  // Create a container div for the component
592
628
  const componentElement = document.createElement('div');
593
629
  componentElement.className = 'tab-content-wrapper';
@@ -605,6 +641,9 @@ export class TabContainerComponent {
605
641
  catch (e) {
606
642
  LogError(e);
607
643
  }
644
+ finally {
645
+ this.tabsCurrentlyLoading.delete(tabId);
646
+ }
608
647
  }
609
648
  /**
610
649
  * Update tab display name in background without loading full component
@@ -624,13 +663,7 @@ export class TabContainerComponent {
624
663
  }
625
664
  // Get the resource registration to access GetResourceDisplayName without loading full component
626
665
  const driverClass = resourceData.Configuration?.resourceTypeDriverClass || resourceData.ResourceType;
627
- let resourceReg = MJGlobal.Instance.ClassFactory.GetRegistration(BaseResourceComponent, driverClass);
628
- if (!resourceReg) {
629
- const loaded = await this.lazyRegistry.Load(driverClass);
630
- if (loaded) {
631
- resourceReg = MJGlobal.Instance.ClassFactory.GetRegistration(BaseResourceComponent, driverClass);
632
- }
633
- }
666
+ const resourceReg = await MJGlobal.Instance.ClassFactory.GetRegistrationAsync(BaseResourceComponent, driverClass);
634
667
  if (!resourceReg) {
635
668
  return;
636
669
  }
@@ -973,6 +1006,135 @@ export class TabContainerComponent {
973
1006
  }
974
1007
  this.hideContextMenu();
975
1008
  }
1009
+ /**
1010
+ * Check if context menu tab is pinned to Home dashboard
1011
+ */
1012
+ get isContextTabPinnedToHome() {
1013
+ if (!this.contextMenuTabId)
1014
+ return false;
1015
+ const tab = this.workspaceManager.GetTab(this.contextMenuTabId);
1016
+ if (!tab)
1017
+ return false;
1018
+ const resourceType = this.resolveResourceType(tab);
1019
+ return this.pinService.IsPinned(resourceType, tab.configuration);
1020
+ }
1021
+ /**
1022
+ * Pin current context menu tab to Home dashboard
1023
+ */
1024
+ async onContextPinToHome() {
1025
+ if (this.isContextTabPinnedToHome) {
1026
+ this.hideContextMenu();
1027
+ return;
1028
+ }
1029
+ if (!this.contextMenuTabId) {
1030
+ this.hideContextMenu();
1031
+ return;
1032
+ }
1033
+ const tab = this.workspaceManager.GetTab(this.contextMenuTabId);
1034
+ if (!tab) {
1035
+ this.hideContextMenu();
1036
+ return;
1037
+ }
1038
+ const resourceType = this.resolveResourceType(tab);
1039
+ const activeApp = this.appManager.GetActiveApp();
1040
+ // Resolve nav item icon for Custom pins
1041
+ let pinIcon;
1042
+ if (resourceType === 'Custom' && activeApp) {
1043
+ const navItemName = tab.configuration?.['navItemName'];
1044
+ if (navItemName) {
1045
+ const navItems = await activeApp.GetNavItems();
1046
+ const navItem = navItems.find(ni => ni.Label === navItemName);
1047
+ pinIcon = navItem?.Icon || undefined;
1048
+ }
1049
+ }
1050
+ const added = this.pinService.AddPin({
1051
+ DisplayName: tab.title || 'Untitled',
1052
+ ResourceType: resourceType,
1053
+ ApplicationID: tab.applicationId || activeApp?.ID,
1054
+ ApplicationName: activeApp?.Name,
1055
+ Icon: pinIcon,
1056
+ Color: activeApp?.GetColor() || undefined,
1057
+ Configuration: tab.configuration,
1058
+ });
1059
+ if (added) {
1060
+ MJNotificationService.Instance.CreateSimpleNotification(`Pinned "${tab.title}" to Home`, 'success', 2000);
1061
+ this.captureContextTabThumbnail(tab);
1062
+ }
1063
+ else {
1064
+ MJNotificationService.Instance.CreateSimpleNotification(`"${tab.title}" is already pinned to Home`, 'info', 3000);
1065
+ }
1066
+ this.hideContextMenu();
1067
+ }
1068
+ /**
1069
+ * Resolve a WorkspaceTab's resource type string for pin matching
1070
+ */
1071
+ resolveResourceType(tab) {
1072
+ const config = tab.configuration;
1073
+ const rt = config.resourceType || '';
1074
+ if (rt === 'Dashboards' || config['dashboardId'])
1075
+ return 'Dashboards';
1076
+ if (rt === 'User Views' || rt === 'MJ: User Views' || config['viewId'])
1077
+ return 'User Views';
1078
+ if (rt === 'Queries' || config['queryId'])
1079
+ return 'Queries';
1080
+ if (rt === 'Reports' || config['reportId'])
1081
+ return 'Reports';
1082
+ if (rt === 'Records' || (config['entity'] && config['recordId']))
1083
+ return 'Records';
1084
+ if (rt === 'Custom' || config['navItemName'])
1085
+ return 'Custom';
1086
+ return rt || 'Custom';
1087
+ }
1088
+ /**
1089
+ * Capture thumbnail for a just-pinned tab (async, non-blocking)
1090
+ */
1091
+ async captureContextTabThumbnail(tab) {
1092
+ try {
1093
+ // Find the active content element — differs by mode
1094
+ let contentEl = null;
1095
+ if (this.useSingleResourceMode) {
1096
+ contentEl = this.directContentContainer?.nativeElement ?? null;
1097
+ }
1098
+ else {
1099
+ // In Golden Layout mode, find the active tab's content pane
1100
+ contentEl = this.glContainer?.nativeElement?.querySelector('.lm_item_container .lm_content');
1101
+ }
1102
+ if (!contentEl)
1103
+ return;
1104
+ const thumbnail = await this.pinService.CaptureThumbnail(contentEl);
1105
+ if (thumbnail) {
1106
+ const resourceType = this.resolveResourceType(tab);
1107
+ const pin = this.pinService.FindPin(resourceType, tab.configuration);
1108
+ if (pin) {
1109
+ this.pinService.UpdatePin(pin.Id, { Thumbnail: thumbnail });
1110
+ }
1111
+ }
1112
+ }
1113
+ catch {
1114
+ // Thumbnail capture is best-effort
1115
+ }
1116
+ }
1117
+ /**
1118
+ * Public method for external callers (e.g. shell) to capture a thumbnail
1119
+ * of the currently visible content, regardless of mode.
1120
+ */
1121
+ async CaptureActiveThumbnail() {
1122
+ try {
1123
+ let contentEl = null;
1124
+ if (this.useSingleResourceMode) {
1125
+ contentEl = this.directContentContainer?.nativeElement ?? null;
1126
+ }
1127
+ else {
1128
+ contentEl = this.glContainer?.nativeElement?.querySelector('.lm_item_container .lm_content');
1129
+ }
1130
+ if (!contentEl)
1131
+ return undefined;
1132
+ return await this.pinService.CaptureThumbnail(contentEl);
1133
+ }
1134
+ catch {
1135
+ return undefined;
1136
+ }
1137
+ }
976
1138
  /**
977
1139
  * While the naming implies this is only invoked once, components we DO NOT CONTROL might have race
978
1140
  * conditions that result in unpredictable behavior. To avoid those causing loading screen overaly to show
@@ -990,21 +1152,21 @@ export class TabContainerComponent {
990
1152
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.directContentContainer = _t.first);
991
1153
  } }, hostBindings: function TabContainerComponent_HostBindings(rf, ctx) { if (rf & 1) {
992
1154
  i0.ɵɵlistener("resize", function TabContainerComponent_resize_HostBindingHandler() { return ctx.onWindowResize(); }, i0.ɵɵresolveWindow);
993
- } }, outputs: { firstResourceLoadComplete: "firstResourceLoadComplete", layoutInitError: "layoutInitError" }, standalone: false, decls: 4, vars: 2, consts: [["directContentContainer", ""], ["glContainer", ""], [1, "tab-container"], [1, "direct-content-container"], [1, "gl-container"], [1, "context-menu", 3, "left", "top"], [1, "context-menu"], [1, "context-menu-item", 3, "click"], [1, "fa-solid", "fa-thumbtack"], [1, "context-menu-divider"], [1, "fa-solid", "fa-xmark"], [1, "fa-solid", "fa-layer-group"], [1, "fa-solid", "fa-angles-right"]], template: function TabContainerComponent_Template(rf, ctx) { if (rf & 1) {
1155
+ } }, outputs: { firstResourceLoadComplete: "firstResourceLoadComplete", layoutInitError: "layoutInitError" }, standalone: false, decls: 4, vars: 2, consts: [["directContentContainer", ""], ["glContainer", ""], [1, "tab-container"], [1, "direct-content-container"], [1, "gl-container"], [1, "context-menu", 3, "left", "top"], [1, "context-menu"], [1, "context-menu-item", 3, "click"], [1, "fa-solid", "fa-thumbtack"], [1, "context-menu-divider"], [1, "fa-solid", "fa-house-chimney"], [1, "fa-solid", "fa-xmark"], [1, "fa-solid", "fa-layer-group"], [1, "fa-solid", "fa-angles-right"], [1, "check"], [1, "fa-solid", "fa-check"]], template: function TabContainerComponent_Template(rf, ctx) { if (rf & 1) {
994
1156
  i0.ɵɵelementStart(0, "div", 2);
995
1157
  i0.ɵɵconditionalCreate(1, TabContainerComponent_Conditional_1_Template, 2, 0, "div", 3)(2, TabContainerComponent_Conditional_2_Template, 2, 0, "div", 4);
996
- i0.ɵɵconditionalCreate(3, TabContainerComponent_Conditional_3_Template, 18, 5, "div", 5);
1158
+ i0.ɵɵconditionalCreate(3, TabContainerComponent_Conditional_3_Template, 23, 10, "div", 5);
997
1159
  i0.ɵɵelementEnd();
998
1160
  } if (rf & 2) {
999
1161
  i0.ɵɵadvance();
1000
1162
  i0.ɵɵconditional(ctx.useSingleResourceMode ? 1 : 2);
1001
1163
  i0.ɵɵadvance(2);
1002
1164
  i0.ɵɵconditional(ctx.contextMenuVisible ? 3 : -1);
1003
- } }, styles: [":host {\n display: flex;\n flex: 1;\n height: 100%;\n width: 100%;\n overflow: hidden;\n}\n\n.tab-container {\n display: flex;\n flex-direction: column;\n flex: 1;\n width: 100%;\n overflow: hidden;\n}\n\n.gl-container {\n flex: 1;\n width: 100%;\n height: 100%;\n position: relative;\n background: var(--mj-bg-page);\n}\n\n/* Direct content container for single-resource mode */\n/* Renders components directly without Golden Layout overhead */\n.direct-content-container {\n flex: 1;\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-page);\n overflow: hidden;\n}\n\n.tab-content-container {\n background: var(--mj-bg-page);\n color: var(--mj-text-primary);\n padding: 20px;\n}\n\n/* Context Menu */\n.context-menu {\n position: fixed;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n min-width: 150px;\n z-index: 10001;\n overflow: hidden;\n}\n\n.context-menu .context-menu-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 14px;\n cursor: pointer;\n font-size: 13px;\n color: var(--mj-text-primary);\n transition: background 0.15s;\n}\n\n.context-menu .context-menu-item i {\n width: 16px;\n text-align: center;\n color: var(--mj-text-secondary);\n}\n\n.context-menu .context-menu-item:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.context-menu .context-menu-divider {\n height: 1px;\n background: var(--mj-border-default);\n margin: 4px 0;\n}\n\n/* Override Golden Layout styles */\n/* Global overrides */\n\n/* Ensure GL root container fills available space */\nmj-tab-container .lm_goldenlayout {\n width: 100% !important;\n height: 100% !important;\n}\n\n/* Ensure the root layout item fills space */\nmj-tab-container .lm_root {\n width: 100% !important;\n height: 100% !important;\n}\n\n/* Ensure proper box-sizing for all GL layout elements */\nmj-tab-container .lm_item,\nmj-tab-container .lm_content,\nmj-tab-container .lm_stack,\nmj-tab-container .lm_row,\nmj-tab-container .lm_column {\n box-sizing: border-box !important;\n}\n\n/* Ensure layout items don't overflow */\nmj-tab-container .lm_item {\n overflow: hidden !important;\n}\n\n/* Fix for .lm_items - the content container inside stacks */\n/* This is separate from .lm_item - it holds the actual tab content */\nmj-tab-container .lm_items {\n width: 100% !important;\n height: calc(100% - 38px) !important; /* Account for header height (38px) */\n box-sizing: border-box !important;\n position: relative !important;\n}\n\n/* When tabs are maximized, no header visible */\nmj-tab-container .lm_stack.lm_maximised > .lm_items {\n height: 100% !important;\n}\n\n/* Target the anonymous ComponentItem div inside lm_items (has no class) */\n/* Created in golden-layout/src/ts/items/component-item.ts:51 without a class */\n/* NOTE: Do NOT set display here - GL uses display:none to hide inactive tabs */\nmj-tab-container .lm_items > div {\n width: 100% !important;\n height: 100% !important;\n box-sizing: border-box !important;\n flex-direction: column !important;\n}\n\n/* Only apply flex display to the active/visible tab content div */\nmj-tab-container .lm_items > div:not([style*=\"display: none\"]) {\n display: flex !important;\n}\n\n/* Clearfix for float-based row layout - GL uses float:left for horizontal panes */\nmj-tab-container .lm_row::after {\n content: \"\" !important;\n display: table !important;\n clear: both !important;\n}\n\n/* Force content children to respect parent bounds (GL sets pixel widths inline) */\nmj-tab-container .lm_content > * {\n max-width: 100% !important;\n width: 100% !important;\n}\n\nmj-tab-container .lm_content {\n background: var(--mj-bg-page) !important;\n display: flex !important;\n flex-direction: column !important;\n height: 100% !important;\n width: 100% !important;\n}\n\nmj-tab-container .lm_item_container {\n background: var(--mj-bg-page) !important;\n}\n\n/* Tab content wrapper - allow scrolling */\n.tab-content-wrapper {\n overflow: auto !important;\n}\n\n/* Make tabs larger and easier to click */\nmj-tab-container .lm_header {\n height: 38px !important;\n padding-top: 2px !important;\n padding-left: 4px !important;\n background: var(--mj-bg-surface-sunken) !important;\n border-bottom: 1px solid var(--mj-border-default) !important;\n overflow: visible !important;\n box-sizing: border-box !important;\n}\n\nmj-tab-container .lm_tabs {\n height: 36px !important;\n}\n\n/* Hide Golden Layout window controls */\nmj-tab-container .lm_controls {\n display: none !important;\n}\n\nmj-tab-container .lm_header .lm_tab {\n padding: 0 16px !important;\n font-size: 13px !important;\n height: 35px !important;\n line-height: 35px !important;\n box-sizing: border-box !important;\n cursor: pointer !important;\n user-select: none !important;\n background: transparent !important;\n border: none !important;\n border-bottom: 1px solid var(--mj-border-default) !important;\n transition: all 0.15s ease !important;\n position: relative !important;\n z-index: 1 !important;\n margin-right: 1px !important;\n}\n\n/* App color accent - left edge indicator */\nmj-tab-container .lm_header .lm_tab::before {\n content: '' !important;\n position: absolute !important;\n left: 0 !important;\n top: 4px !important;\n bottom: 4px !important;\n width: 3px !important;\n border-radius: 0 2px 2px 0 !important;\n background-color: var(--app-color, transparent) !important;\n opacity: 0.6 !important;\n transition: all 0.15s ease !important;\n}\n\nmj-tab-container .lm_header .lm_tab:hover {\n background: var(--mj-bg-surface-hover) !important;\n}\n\nmj-tab-container .lm_header .lm_tab:hover .lm_close_tab {\n opacity: 0.7 !important;\n}\n\nmj-tab-container .lm_header .lm_tab:hover::before {\n opacity: 0.8 !important;\n}\n\nmj-tab-container .lm_header .lm_tab.lm_active {\n background: var(--mj-bg-surface) !important;\n height: 36px !important;\n margin-bottom: -1px !important;\n margin-right: 0 !important;\n border: 1px solid var(--mj-border-default) !important;\n border-bottom-color: var(--mj-bg-surface) !important;\n border-radius: 4px 4px 0 0 !important;\n z-index: 2 !important;\n}\n\n/* Enhanced app color accent for active tab */\nmj-tab-container .lm_header .lm_tab.lm_active::before {\n opacity: 1 !important;\n width: 3px !important;\n top: 2px !important;\n bottom: 2px !important;\n box-shadow: 0 0 6px var(--app-color, transparent) !important;\n}\n\nmj-tab-container .lm_title {\n cursor: pointer !important;\n user-select: none !important;\n}\n\nmj-tab-container .lm_close_tab {\n position: absolute !important;\n right: 4px !important;\n top: 50% !important;\n transform: translateY(-50%) !important;\n width: 16px !important;\n height: 16px !important;\n cursor: pointer !important;\n opacity: 0 !important;\n transition: all 0.15s ease !important;\n flex-shrink: 0 !important;\n display: flex !important;\n align-items: center !important;\n justify-content: center !important;\n font-size: 10px !important;\n color: var(--mj-text-secondary) !important;\n}\n\nmj-tab-container .lm_close_tab:hover {\n opacity: 1 !important;\n color: var(--mj-status-error) !important;\n}\n\n/* Show close button on hover (except pinned) */\nmj-tab-container .lm_header .lm_tab:hover:not(.pinned) .lm_close_tab {\n opacity: 0.7 !important;\n}\n\n/* Pinned tabs never show close button */\nmj-tab-container .lm_header .lm_tab.pinned .lm_close_tab {\n display: none !important;\n}\n\n/* Hide close button on active tab by default */\nmj-tab-container .lm_active .lm_close_tab {\n opacity: 0 !important;\n}\n\n/* Show close button when hovering active tab */\nmj-tab-container .lm_active:hover .lm_close_tab {\n opacity: 0.7 !important;\n}\n\n/* Adjust padding for close button */\nmj-tab-container .lm_header .lm_tab {\n padding-right: 24px !important;\n}\n"], encapsulation: 2 });
1165
+ } }, styles: [":host {\n display: flex;\n flex: 1;\n height: 100%;\n width: 100%;\n overflow: hidden;\n}\n\n.tab-container {\n display: flex;\n flex-direction: column;\n flex: 1;\n width: 100%;\n overflow: hidden;\n}\n\n.gl-container {\n flex: 1;\n width: 100%;\n height: 100%;\n position: relative;\n background: var(--mj-bg-page);\n}\n\n/* Direct content container for single-resource mode */\n/* Renders components directly without Golden Layout overhead */\n.direct-content-container {\n flex: 1;\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-page);\n overflow: hidden;\n}\n\n.tab-content-container {\n background: var(--mj-bg-page);\n color: var(--mj-text-primary);\n padding: 20px;\n}\n\n/* Context Menu */\n.context-menu {\n position: fixed;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n min-width: 150px;\n z-index: 10001;\n overflow: hidden;\n}\n\n.context-menu .context-menu-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 14px;\n cursor: pointer;\n font-size: 13px;\n color: var(--mj-text-primary);\n transition: background 0.15s;\n}\n\n.context-menu .context-menu-item i {\n width: 16px;\n text-align: center;\n color: var(--mj-text-secondary);\n}\n\n.context-menu .context-menu-item:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.context-menu .context-menu-divider {\n height: 1px;\n background: var(--mj-border-default);\n margin: 4px 0;\n}\n\n/* Context menu item variants */\n.context-menu-item.new-item i { color: var(--mj-brand-primary); }\n.context-menu-item.new-item { font-weight: 500; }\n.context-menu-item.disabled { color: var(--mj-text-disabled); cursor: default; }\n.context-menu-item.disabled:hover { background: transparent; }\n.context-menu-item.disabled i { color: var(--mj-text-disabled); }\n.context-menu-item .check { margin-left: auto; color: var(--mj-status-success); font-size: 12px; }\n\n/* Override Golden Layout styles */\n/* Global overrides */\n\n/* Ensure GL root container fills available space */\nmj-tab-container .lm_goldenlayout {\n width: 100% !important;\n height: 100% !important;\n}\n\n/* Ensure the root layout item fills space */\nmj-tab-container .lm_root {\n width: 100% !important;\n height: 100% !important;\n}\n\n/* Ensure proper box-sizing for all GL layout elements */\nmj-tab-container .lm_item,\nmj-tab-container .lm_content,\nmj-tab-container .lm_stack,\nmj-tab-container .lm_row,\nmj-tab-container .lm_column {\n box-sizing: border-box !important;\n}\n\n/* Ensure layout items don't overflow */\nmj-tab-container .lm_item {\n overflow: hidden !important;\n}\n\n/* Fix for .lm_items - the content container inside stacks */\n/* This is separate from .lm_item - it holds the actual tab content */\nmj-tab-container .lm_items {\n width: 100% !important;\n height: calc(100% - 38px) !important; /* Account for header height (38px) */\n box-sizing: border-box !important;\n position: relative !important;\n}\n\n/* When tabs are maximized, no header visible */\nmj-tab-container .lm_stack.lm_maximised > .lm_items {\n height: 100% !important;\n}\n\n/* Target the anonymous ComponentItem div inside lm_items (has no class) */\n/* Created in golden-layout/src/ts/items/component-item.ts:51 without a class */\n/* NOTE: Do NOT set display here - GL uses display:none to hide inactive tabs */\nmj-tab-container .lm_items > div {\n width: 100% !important;\n height: 100% !important;\n box-sizing: border-box !important;\n flex-direction: column !important;\n}\n\n/* Only apply flex display to the active/visible tab content div */\nmj-tab-container .lm_items > div:not([style*=\"display: none\"]) {\n display: flex !important;\n}\n\n/* Clearfix for float-based row layout - GL uses float:left for horizontal panes */\nmj-tab-container .lm_row::after {\n content: \"\" !important;\n display: table !important;\n clear: both !important;\n}\n\n/* Force content children to respect parent bounds (GL sets pixel widths inline) */\nmj-tab-container .lm_content > * {\n max-width: 100% !important;\n width: 100% !important;\n}\n\nmj-tab-container .lm_content {\n background: var(--mj-bg-page) !important;\n display: flex !important;\n flex-direction: column !important;\n height: 100% !important;\n width: 100% !important;\n}\n\nmj-tab-container .lm_item_container {\n background: var(--mj-bg-page) !important;\n}\n\n/* Tab content wrapper - allow scrolling */\n.tab-content-wrapper {\n overflow: auto !important;\n}\n\n/* Make tabs larger and easier to click */\nmj-tab-container .lm_header {\n height: 38px !important;\n padding-top: 2px !important;\n padding-left: 4px !important;\n background: var(--mj-bg-surface-sunken) !important;\n border-bottom: 1px solid var(--mj-border-default) !important;\n overflow: visible !important;\n box-sizing: border-box !important;\n}\n\nmj-tab-container .lm_tabs {\n height: 36px !important;\n}\n\n/* Hide Golden Layout window controls */\nmj-tab-container .lm_controls {\n display: none !important;\n}\n\nmj-tab-container .lm_header .lm_tab {\n padding: 0 16px !important;\n font-size: 13px !important;\n height: 35px !important;\n line-height: 35px !important;\n box-sizing: border-box !important;\n cursor: pointer !important;\n user-select: none !important;\n background: transparent !important;\n border: none !important;\n border-bottom: 1px solid var(--mj-border-default) !important;\n transition: all 0.15s ease !important;\n position: relative !important;\n z-index: 1 !important;\n margin-right: 1px !important;\n}\n\n/* App color accent - left edge indicator */\nmj-tab-container .lm_header .lm_tab::before {\n content: '' !important;\n position: absolute !important;\n left: 0 !important;\n top: 4px !important;\n bottom: 4px !important;\n width: 3px !important;\n border-radius: 0 2px 2px 0 !important;\n background-color: var(--app-color, transparent) !important;\n opacity: 0.6 !important;\n transition: all 0.15s ease !important;\n}\n\nmj-tab-container .lm_header .lm_tab:hover {\n background: var(--mj-bg-surface-hover) !important;\n}\n\nmj-tab-container .lm_header .lm_tab:hover .lm_close_tab {\n opacity: 0.7 !important;\n}\n\nmj-tab-container .lm_header .lm_tab:hover::before {\n opacity: 0.8 !important;\n}\n\nmj-tab-container .lm_header .lm_tab.lm_active {\n background: var(--mj-bg-surface) !important;\n height: 36px !important;\n margin-bottom: -1px !important;\n margin-right: 0 !important;\n border: 1px solid var(--mj-border-default) !important;\n border-bottom-color: var(--mj-bg-surface) !important;\n border-radius: 4px 4px 0 0 !important;\n z-index: 2 !important;\n}\n\n/* Enhanced app color accent for active tab */\nmj-tab-container .lm_header .lm_tab.lm_active::before {\n opacity: 1 !important;\n width: 3px !important;\n top: 2px !important;\n bottom: 2px !important;\n box-shadow: 0 0 6px var(--app-color, transparent) !important;\n}\n\nmj-tab-container .lm_title {\n cursor: pointer !important;\n user-select: none !important;\n}\n\nmj-tab-container .lm_close_tab {\n position: absolute !important;\n right: 4px !important;\n top: 50% !important;\n transform: translateY(-50%) !important;\n width: 16px !important;\n height: 16px !important;\n cursor: pointer !important;\n opacity: 0 !important;\n transition: all 0.15s ease !important;\n flex-shrink: 0 !important;\n display: flex !important;\n align-items: center !important;\n justify-content: center !important;\n font-size: 10px !important;\n color: var(--mj-text-secondary) !important;\n}\n\nmj-tab-container .lm_close_tab:hover {\n opacity: 1 !important;\n color: var(--mj-status-error) !important;\n}\n\n/* Show close button on hover (except pinned) */\nmj-tab-container .lm_header .lm_tab:hover:not(.pinned) .lm_close_tab {\n opacity: 0.7 !important;\n}\n\n/* Pinned tabs never show close button */\nmj-tab-container .lm_header .lm_tab.pinned .lm_close_tab {\n display: none !important;\n}\n\n/* Hide close button on active tab by default */\nmj-tab-container .lm_active .lm_close_tab {\n opacity: 0 !important;\n}\n\n/* Show close button when hovering active tab */\nmj-tab-container .lm_active:hover .lm_close_tab {\n opacity: 0.7 !important;\n}\n\n/* Adjust padding for close button */\nmj-tab-container .lm_header .lm_tab {\n padding-right: 24px !important;\n}\n"], encapsulation: 2 });
1004
1166
  }
1005
1167
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(TabContainerComponent, [{
1006
1168
  type: Component,
1007
- args: [{ standalone: false, selector: 'mj-tab-container', encapsulation: ViewEncapsulation.None, template: "<div class=\"tab-container\">\n\n <!-- Single-Resource Mode: Direct component rendering without Golden Layout -->\n <!-- This avoids the 20px height issue when GL header is hidden -->\n @if (useSingleResourceMode) {\n <div #directContentContainer class=\"direct-content-container\"></div>\n } @else {\n <!-- Multi-Tab Mode: Golden Layout Container -->\n <div #glContainer class=\"gl-container\"></div>\n }\n\n <!-- Context Menu (only relevant in multi-tab mode) -->\n @if (contextMenuVisible) {\n <div\n class=\"context-menu\"\n [style.left.px]=\"contextMenuX\"\n [style.top.px]=\"contextMenuY\">\n <div class=\"context-menu-item\" (click)=\"onContextPin()\">\n <i class=\"fa-solid fa-thumbtack\"></i>\n <span>{{ isContextTabPinned ? 'Unpin Tab' : 'Pin Tab' }}</span>\n </div>\n <div class=\"context-menu-divider\"></div>\n <div class=\"context-menu-item\" (click)=\"onContextClose()\">\n <i class=\"fa-solid fa-xmark\"></i>\n <span>Close Tab</span>\n </div>\n <div class=\"context-menu-item\" (click)=\"onContextCloseOthers()\">\n <i class=\"fa-solid fa-layer-group\"></i>\n <span>Close Others</span>\n </div>\n <div class=\"context-menu-item\" (click)=\"onContextCloseToRight()\">\n <i class=\"fa-solid fa-angles-right\"></i>\n <span>Close to Right</span>\n </div>\n </div>\n }\n</div>\n", styles: [":host {\n display: flex;\n flex: 1;\n height: 100%;\n width: 100%;\n overflow: hidden;\n}\n\n.tab-container {\n display: flex;\n flex-direction: column;\n flex: 1;\n width: 100%;\n overflow: hidden;\n}\n\n.gl-container {\n flex: 1;\n width: 100%;\n height: 100%;\n position: relative;\n background: var(--mj-bg-page);\n}\n\n/* Direct content container for single-resource mode */\n/* Renders components directly without Golden Layout overhead */\n.direct-content-container {\n flex: 1;\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-page);\n overflow: hidden;\n}\n\n.tab-content-container {\n background: var(--mj-bg-page);\n color: var(--mj-text-primary);\n padding: 20px;\n}\n\n/* Context Menu */\n.context-menu {\n position: fixed;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n min-width: 150px;\n z-index: 10001;\n overflow: hidden;\n}\n\n.context-menu .context-menu-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 14px;\n cursor: pointer;\n font-size: 13px;\n color: var(--mj-text-primary);\n transition: background 0.15s;\n}\n\n.context-menu .context-menu-item i {\n width: 16px;\n text-align: center;\n color: var(--mj-text-secondary);\n}\n\n.context-menu .context-menu-item:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.context-menu .context-menu-divider {\n height: 1px;\n background: var(--mj-border-default);\n margin: 4px 0;\n}\n\n/* Override Golden Layout styles */\n/* Global overrides */\n\n/* Ensure GL root container fills available space */\nmj-tab-container .lm_goldenlayout {\n width: 100% !important;\n height: 100% !important;\n}\n\n/* Ensure the root layout item fills space */\nmj-tab-container .lm_root {\n width: 100% !important;\n height: 100% !important;\n}\n\n/* Ensure proper box-sizing for all GL layout elements */\nmj-tab-container .lm_item,\nmj-tab-container .lm_content,\nmj-tab-container .lm_stack,\nmj-tab-container .lm_row,\nmj-tab-container .lm_column {\n box-sizing: border-box !important;\n}\n\n/* Ensure layout items don't overflow */\nmj-tab-container .lm_item {\n overflow: hidden !important;\n}\n\n/* Fix for .lm_items - the content container inside stacks */\n/* This is separate from .lm_item - it holds the actual tab content */\nmj-tab-container .lm_items {\n width: 100% !important;\n height: calc(100% - 38px) !important; /* Account for header height (38px) */\n box-sizing: border-box !important;\n position: relative !important;\n}\n\n/* When tabs are maximized, no header visible */\nmj-tab-container .lm_stack.lm_maximised > .lm_items {\n height: 100% !important;\n}\n\n/* Target the anonymous ComponentItem div inside lm_items (has no class) */\n/* Created in golden-layout/src/ts/items/component-item.ts:51 without a class */\n/* NOTE: Do NOT set display here - GL uses display:none to hide inactive tabs */\nmj-tab-container .lm_items > div {\n width: 100% !important;\n height: 100% !important;\n box-sizing: border-box !important;\n flex-direction: column !important;\n}\n\n/* Only apply flex display to the active/visible tab content div */\nmj-tab-container .lm_items > div:not([style*=\"display: none\"]) {\n display: flex !important;\n}\n\n/* Clearfix for float-based row layout - GL uses float:left for horizontal panes */\nmj-tab-container .lm_row::after {\n content: \"\" !important;\n display: table !important;\n clear: both !important;\n}\n\n/* Force content children to respect parent bounds (GL sets pixel widths inline) */\nmj-tab-container .lm_content > * {\n max-width: 100% !important;\n width: 100% !important;\n}\n\nmj-tab-container .lm_content {\n background: var(--mj-bg-page) !important;\n display: flex !important;\n flex-direction: column !important;\n height: 100% !important;\n width: 100% !important;\n}\n\nmj-tab-container .lm_item_container {\n background: var(--mj-bg-page) !important;\n}\n\n/* Tab content wrapper - allow scrolling */\n.tab-content-wrapper {\n overflow: auto !important;\n}\n\n/* Make tabs larger and easier to click */\nmj-tab-container .lm_header {\n height: 38px !important;\n padding-top: 2px !important;\n padding-left: 4px !important;\n background: var(--mj-bg-surface-sunken) !important;\n border-bottom: 1px solid var(--mj-border-default) !important;\n overflow: visible !important;\n box-sizing: border-box !important;\n}\n\nmj-tab-container .lm_tabs {\n height: 36px !important;\n}\n\n/* Hide Golden Layout window controls */\nmj-tab-container .lm_controls {\n display: none !important;\n}\n\nmj-tab-container .lm_header .lm_tab {\n padding: 0 16px !important;\n font-size: 13px !important;\n height: 35px !important;\n line-height: 35px !important;\n box-sizing: border-box !important;\n cursor: pointer !important;\n user-select: none !important;\n background: transparent !important;\n border: none !important;\n border-bottom: 1px solid var(--mj-border-default) !important;\n transition: all 0.15s ease !important;\n position: relative !important;\n z-index: 1 !important;\n margin-right: 1px !important;\n}\n\n/* App color accent - left edge indicator */\nmj-tab-container .lm_header .lm_tab::before {\n content: '' !important;\n position: absolute !important;\n left: 0 !important;\n top: 4px !important;\n bottom: 4px !important;\n width: 3px !important;\n border-radius: 0 2px 2px 0 !important;\n background-color: var(--app-color, transparent) !important;\n opacity: 0.6 !important;\n transition: all 0.15s ease !important;\n}\n\nmj-tab-container .lm_header .lm_tab:hover {\n background: var(--mj-bg-surface-hover) !important;\n}\n\nmj-tab-container .lm_header .lm_tab:hover .lm_close_tab {\n opacity: 0.7 !important;\n}\n\nmj-tab-container .lm_header .lm_tab:hover::before {\n opacity: 0.8 !important;\n}\n\nmj-tab-container .lm_header .lm_tab.lm_active {\n background: var(--mj-bg-surface) !important;\n height: 36px !important;\n margin-bottom: -1px !important;\n margin-right: 0 !important;\n border: 1px solid var(--mj-border-default) !important;\n border-bottom-color: var(--mj-bg-surface) !important;\n border-radius: 4px 4px 0 0 !important;\n z-index: 2 !important;\n}\n\n/* Enhanced app color accent for active tab */\nmj-tab-container .lm_header .lm_tab.lm_active::before {\n opacity: 1 !important;\n width: 3px !important;\n top: 2px !important;\n bottom: 2px !important;\n box-shadow: 0 0 6px var(--app-color, transparent) !important;\n}\n\nmj-tab-container .lm_title {\n cursor: pointer !important;\n user-select: none !important;\n}\n\nmj-tab-container .lm_close_tab {\n position: absolute !important;\n right: 4px !important;\n top: 50% !important;\n transform: translateY(-50%) !important;\n width: 16px !important;\n height: 16px !important;\n cursor: pointer !important;\n opacity: 0 !important;\n transition: all 0.15s ease !important;\n flex-shrink: 0 !important;\n display: flex !important;\n align-items: center !important;\n justify-content: center !important;\n font-size: 10px !important;\n color: var(--mj-text-secondary) !important;\n}\n\nmj-tab-container .lm_close_tab:hover {\n opacity: 1 !important;\n color: var(--mj-status-error) !important;\n}\n\n/* Show close button on hover (except pinned) */\nmj-tab-container .lm_header .lm_tab:hover:not(.pinned) .lm_close_tab {\n opacity: 0.7 !important;\n}\n\n/* Pinned tabs never show close button */\nmj-tab-container .lm_header .lm_tab.pinned .lm_close_tab {\n display: none !important;\n}\n\n/* Hide close button on active tab by default */\nmj-tab-container .lm_active .lm_close_tab {\n opacity: 0 !important;\n}\n\n/* Show close button when hovering active tab */\nmj-tab-container .lm_active:hover .lm_close_tab {\n opacity: 0.7 !important;\n}\n\n/* Adjust padding for close button */\nmj-tab-container .lm_header .lm_tab {\n padding-right: 24px !important;\n}\n"] }]
1169
+ args: [{ standalone: false, selector: 'mj-tab-container', encapsulation: ViewEncapsulation.None, template: "<div class=\"tab-container\">\n\n <!-- Single-Resource Mode: Direct component rendering without Golden Layout -->\n <!-- This avoids the 20px height issue when GL header is hidden -->\n @if (useSingleResourceMode) {\n <div #directContentContainer class=\"direct-content-container\"></div>\n } @else {\n <!-- Multi-Tab Mode: Golden Layout Container -->\n <div #glContainer class=\"gl-container\"></div>\n }\n\n <!-- Context Menu (only relevant in multi-tab mode) -->\n @if (contextMenuVisible) {\n <div\n class=\"context-menu\"\n [style.left.px]=\"contextMenuX\"\n [style.top.px]=\"contextMenuY\">\n <div class=\"context-menu-item\" (click)=\"onContextPin()\">\n <i class=\"fa-solid fa-thumbtack\"></i>\n <span>{{ isContextTabPinned ? 'Unpin Tab' : 'Pin Tab' }}</span>\n </div>\n <div class=\"context-menu-divider\"></div>\n <div class=\"context-menu-item\" [class.new-item]=\"!isContextTabPinnedToHome\" [class.disabled]=\"isContextTabPinnedToHome\" (click)=\"onContextPinToHome()\">\n <i class=\"fa-solid fa-house-chimney\"></i>\n @if (isContextTabPinnedToHome) {\n <span>Pinned to Home</span>\n <span class=\"check\"><i class=\"fa-solid fa-check\"></i></span>\n } @else {\n <span>Pin to Home</span>\n }\n </div>\n <div class=\"context-menu-divider\"></div>\n <div class=\"context-menu-item\" (click)=\"onContextClose()\">\n <i class=\"fa-solid fa-xmark\"></i>\n <span>Close Tab</span>\n </div>\n <div class=\"context-menu-item\" (click)=\"onContextCloseOthers()\">\n <i class=\"fa-solid fa-layer-group\"></i>\n <span>Close Others</span>\n </div>\n <div class=\"context-menu-item\" (click)=\"onContextCloseToRight()\">\n <i class=\"fa-solid fa-angles-right\"></i>\n <span>Close to Right</span>\n </div>\n </div>\n }\n</div>\n", styles: [":host {\n display: flex;\n flex: 1;\n height: 100%;\n width: 100%;\n overflow: hidden;\n}\n\n.tab-container {\n display: flex;\n flex-direction: column;\n flex: 1;\n width: 100%;\n overflow: hidden;\n}\n\n.gl-container {\n flex: 1;\n width: 100%;\n height: 100%;\n position: relative;\n background: var(--mj-bg-page);\n}\n\n/* Direct content container for single-resource mode */\n/* Renders components directly without Golden Layout overhead */\n.direct-content-container {\n flex: 1;\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-page);\n overflow: hidden;\n}\n\n.tab-content-container {\n background: var(--mj-bg-page);\n color: var(--mj-text-primary);\n padding: 20px;\n}\n\n/* Context Menu */\n.context-menu {\n position: fixed;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n min-width: 150px;\n z-index: 10001;\n overflow: hidden;\n}\n\n.context-menu .context-menu-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 14px;\n cursor: pointer;\n font-size: 13px;\n color: var(--mj-text-primary);\n transition: background 0.15s;\n}\n\n.context-menu .context-menu-item i {\n width: 16px;\n text-align: center;\n color: var(--mj-text-secondary);\n}\n\n.context-menu .context-menu-item:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.context-menu .context-menu-divider {\n height: 1px;\n background: var(--mj-border-default);\n margin: 4px 0;\n}\n\n/* Context menu item variants */\n.context-menu-item.new-item i { color: var(--mj-brand-primary); }\n.context-menu-item.new-item { font-weight: 500; }\n.context-menu-item.disabled { color: var(--mj-text-disabled); cursor: default; }\n.context-menu-item.disabled:hover { background: transparent; }\n.context-menu-item.disabled i { color: var(--mj-text-disabled); }\n.context-menu-item .check { margin-left: auto; color: var(--mj-status-success); font-size: 12px; }\n\n/* Override Golden Layout styles */\n/* Global overrides */\n\n/* Ensure GL root container fills available space */\nmj-tab-container .lm_goldenlayout {\n width: 100% !important;\n height: 100% !important;\n}\n\n/* Ensure the root layout item fills space */\nmj-tab-container .lm_root {\n width: 100% !important;\n height: 100% !important;\n}\n\n/* Ensure proper box-sizing for all GL layout elements */\nmj-tab-container .lm_item,\nmj-tab-container .lm_content,\nmj-tab-container .lm_stack,\nmj-tab-container .lm_row,\nmj-tab-container .lm_column {\n box-sizing: border-box !important;\n}\n\n/* Ensure layout items don't overflow */\nmj-tab-container .lm_item {\n overflow: hidden !important;\n}\n\n/* Fix for .lm_items - the content container inside stacks */\n/* This is separate from .lm_item - it holds the actual tab content */\nmj-tab-container .lm_items {\n width: 100% !important;\n height: calc(100% - 38px) !important; /* Account for header height (38px) */\n box-sizing: border-box !important;\n position: relative !important;\n}\n\n/* When tabs are maximized, no header visible */\nmj-tab-container .lm_stack.lm_maximised > .lm_items {\n height: 100% !important;\n}\n\n/* Target the anonymous ComponentItem div inside lm_items (has no class) */\n/* Created in golden-layout/src/ts/items/component-item.ts:51 without a class */\n/* NOTE: Do NOT set display here - GL uses display:none to hide inactive tabs */\nmj-tab-container .lm_items > div {\n width: 100% !important;\n height: 100% !important;\n box-sizing: border-box !important;\n flex-direction: column !important;\n}\n\n/* Only apply flex display to the active/visible tab content div */\nmj-tab-container .lm_items > div:not([style*=\"display: none\"]) {\n display: flex !important;\n}\n\n/* Clearfix for float-based row layout - GL uses float:left for horizontal panes */\nmj-tab-container .lm_row::after {\n content: \"\" !important;\n display: table !important;\n clear: both !important;\n}\n\n/* Force content children to respect parent bounds (GL sets pixel widths inline) */\nmj-tab-container .lm_content > * {\n max-width: 100% !important;\n width: 100% !important;\n}\n\nmj-tab-container .lm_content {\n background: var(--mj-bg-page) !important;\n display: flex !important;\n flex-direction: column !important;\n height: 100% !important;\n width: 100% !important;\n}\n\nmj-tab-container .lm_item_container {\n background: var(--mj-bg-page) !important;\n}\n\n/* Tab content wrapper - allow scrolling */\n.tab-content-wrapper {\n overflow: auto !important;\n}\n\n/* Make tabs larger and easier to click */\nmj-tab-container .lm_header {\n height: 38px !important;\n padding-top: 2px !important;\n padding-left: 4px !important;\n background: var(--mj-bg-surface-sunken) !important;\n border-bottom: 1px solid var(--mj-border-default) !important;\n overflow: visible !important;\n box-sizing: border-box !important;\n}\n\nmj-tab-container .lm_tabs {\n height: 36px !important;\n}\n\n/* Hide Golden Layout window controls */\nmj-tab-container .lm_controls {\n display: none !important;\n}\n\nmj-tab-container .lm_header .lm_tab {\n padding: 0 16px !important;\n font-size: 13px !important;\n height: 35px !important;\n line-height: 35px !important;\n box-sizing: border-box !important;\n cursor: pointer !important;\n user-select: none !important;\n background: transparent !important;\n border: none !important;\n border-bottom: 1px solid var(--mj-border-default) !important;\n transition: all 0.15s ease !important;\n position: relative !important;\n z-index: 1 !important;\n margin-right: 1px !important;\n}\n\n/* App color accent - left edge indicator */\nmj-tab-container .lm_header .lm_tab::before {\n content: '' !important;\n position: absolute !important;\n left: 0 !important;\n top: 4px !important;\n bottom: 4px !important;\n width: 3px !important;\n border-radius: 0 2px 2px 0 !important;\n background-color: var(--app-color, transparent) !important;\n opacity: 0.6 !important;\n transition: all 0.15s ease !important;\n}\n\nmj-tab-container .lm_header .lm_tab:hover {\n background: var(--mj-bg-surface-hover) !important;\n}\n\nmj-tab-container .lm_header .lm_tab:hover .lm_close_tab {\n opacity: 0.7 !important;\n}\n\nmj-tab-container .lm_header .lm_tab:hover::before {\n opacity: 0.8 !important;\n}\n\nmj-tab-container .lm_header .lm_tab.lm_active {\n background: var(--mj-bg-surface) !important;\n height: 36px !important;\n margin-bottom: -1px !important;\n margin-right: 0 !important;\n border: 1px solid var(--mj-border-default) !important;\n border-bottom-color: var(--mj-bg-surface) !important;\n border-radius: 4px 4px 0 0 !important;\n z-index: 2 !important;\n}\n\n/* Enhanced app color accent for active tab */\nmj-tab-container .lm_header .lm_tab.lm_active::before {\n opacity: 1 !important;\n width: 3px !important;\n top: 2px !important;\n bottom: 2px !important;\n box-shadow: 0 0 6px var(--app-color, transparent) !important;\n}\n\nmj-tab-container .lm_title {\n cursor: pointer !important;\n user-select: none !important;\n}\n\nmj-tab-container .lm_close_tab {\n position: absolute !important;\n right: 4px !important;\n top: 50% !important;\n transform: translateY(-50%) !important;\n width: 16px !important;\n height: 16px !important;\n cursor: pointer !important;\n opacity: 0 !important;\n transition: all 0.15s ease !important;\n flex-shrink: 0 !important;\n display: flex !important;\n align-items: center !important;\n justify-content: center !important;\n font-size: 10px !important;\n color: var(--mj-text-secondary) !important;\n}\n\nmj-tab-container .lm_close_tab:hover {\n opacity: 1 !important;\n color: var(--mj-status-error) !important;\n}\n\n/* Show close button on hover (except pinned) */\nmj-tab-container .lm_header .lm_tab:hover:not(.pinned) .lm_close_tab {\n opacity: 0.7 !important;\n}\n\n/* Pinned tabs never show close button */\nmj-tab-container .lm_header .lm_tab.pinned .lm_close_tab {\n display: none !important;\n}\n\n/* Hide close button on active tab by default */\nmj-tab-container .lm_active .lm_close_tab {\n opacity: 0 !important;\n}\n\n/* Show close button when hovering active tab */\nmj-tab-container .lm_active:hover .lm_close_tab {\n opacity: 0.7 !important;\n}\n\n/* Adjust padding for close button */\nmj-tab-container .lm_header .lm_tab {\n padding-right: 24px !important;\n}\n"] }]
1008
1170
  }], () => [{ type: i1.GoldenLayoutManager }, { type: i1.WorkspaceStateManager }, { type: i1.ApplicationManager }, { type: i0.ApplicationRef }, { type: i0.EnvironmentInjector }, { type: i0.ChangeDetectorRef }], { glContainer: [{
1009
1171
  type: ViewChild,
1010
1172
  args: ['glContainer', { static: false }]