@memberjunction/ng-explorer-core 6.1.4 → 6.2.0-edge.1

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 (184) hide show
  1. package/dist/app-routing.module.d.ts +23 -1
  2. package/dist/app-routing.module.d.ts.map +1 -1
  3. package/dist/app-routing.module.js +73 -17
  4. package/dist/app-routing.module.js.map +1 -1
  5. package/dist/generated/lazy-feature-config.d.ts +1 -1
  6. package/dist/generated/lazy-feature-config.d.ts.map +1 -1
  7. package/dist/generated/lazy-feature-config.js +1 -8
  8. package/dist/generated/lazy-feature-config.js.map +1 -1
  9. package/dist/lib/command-palette/command-palette.component.d.ts +7 -1
  10. package/dist/lib/command-palette/command-palette.component.d.ts.map +1 -1
  11. package/dist/lib/command-palette/command-palette.component.js +28 -14
  12. package/dist/lib/command-palette/command-palette.component.js.map +1 -1
  13. package/dist/lib/conversation-feedback/conversation-feedback.d.ts +103 -13
  14. package/dist/lib/conversation-feedback/conversation-feedback.d.ts.map +1 -1
  15. package/dist/lib/conversation-feedback/conversation-feedback.js +219 -67
  16. package/dist/lib/conversation-feedback/conversation-feedback.js.map +1 -1
  17. package/dist/lib/dashboard-preferences-dialog/dashboard-preferences-dialog.component.d.ts +64 -10
  18. package/dist/lib/dashboard-preferences-dialog/dashboard-preferences-dialog.component.d.ts.map +1 -1
  19. package/dist/lib/dashboard-preferences-dialog/dashboard-preferences-dialog.component.js +172 -66
  20. package/dist/lib/dashboard-preferences-dialog/dashboard-preferences-dialog.component.js.map +1 -1
  21. package/dist/lib/generic/resource-container-component.d.ts +19 -5
  22. package/dist/lib/generic/resource-container-component.d.ts.map +1 -1
  23. package/dist/lib/generic/resource-container-component.js +43 -13
  24. package/dist/lib/generic/resource-container-component.js.map +1 -1
  25. package/dist/lib/guards/entities.guard.d.ts +4 -0
  26. package/dist/lib/guards/entities.guard.d.ts.map +1 -1
  27. package/dist/lib/guards/entities.guard.js +10 -2
  28. package/dist/lib/guards/entities.guard.js.map +1 -1
  29. package/dist/lib/identity-claims/claim-redeem.component.d.ts +4 -0
  30. package/dist/lib/identity-claims/claim-redeem.component.d.ts.map +1 -1
  31. package/dist/lib/identity-claims/claim-redeem.component.js +10 -2
  32. package/dist/lib/identity-claims/claim-redeem.component.js.map +1 -1
  33. package/dist/lib/oauth/oauth-callback.component.d.ts +4 -0
  34. package/dist/lib/oauth/oauth-callback.component.d.ts.map +1 -1
  35. package/dist/lib/oauth/oauth-callback.component.js +10 -2
  36. package/dist/lib/oauth/oauth-callback.component.js.map +1 -1
  37. package/dist/lib/profile/profile-dialog.component.d.ts.map +1 -1
  38. package/dist/lib/profile/profile-dialog.component.js +194 -191
  39. package/dist/lib/profile/profile-dialog.component.js.map +1 -1
  40. package/dist/lib/resource-wrappers/artifact-resource.component.d.ts +20 -2
  41. package/dist/lib/resource-wrappers/artifact-resource.component.d.ts.map +1 -1
  42. package/dist/lib/resource-wrappers/artifact-resource.component.js +33 -9
  43. package/dist/lib/resource-wrappers/artifact-resource.component.js.map +1 -1
  44. package/dist/lib/resource-wrappers/chat-collections-resource.component.d.ts +80 -10
  45. package/dist/lib/resource-wrappers/chat-collections-resource.component.d.ts.map +1 -1
  46. package/dist/lib/resource-wrappers/chat-collections-resource.component.js +168 -56
  47. package/dist/lib/resource-wrappers/chat-collections-resource.component.js.map +1 -1
  48. package/dist/lib/resource-wrappers/chat-conversations-resource.component.d.ts +186 -23
  49. package/dist/lib/resource-wrappers/chat-conversations-resource.component.d.ts.map +1 -1
  50. package/dist/lib/resource-wrappers/chat-conversations-resource.component.js +500 -177
  51. package/dist/lib/resource-wrappers/chat-conversations-resource.component.js.map +1 -1
  52. package/dist/lib/resource-wrappers/chat-deeplink-params.d.ts +2 -0
  53. package/dist/lib/resource-wrappers/chat-deeplink-params.d.ts.map +1 -1
  54. package/dist/lib/resource-wrappers/chat-deeplink-params.js +5 -1
  55. package/dist/lib/resource-wrappers/chat-deeplink-params.js.map +1 -1
  56. package/dist/lib/resource-wrappers/chat-search-routing.d.ts +25 -0
  57. package/dist/lib/resource-wrappers/chat-search-routing.d.ts.map +1 -0
  58. package/dist/lib/resource-wrappers/chat-search-routing.js +24 -0
  59. package/dist/lib/resource-wrappers/chat-search-routing.js.map +1 -0
  60. package/dist/lib/resource-wrappers/chat-tasks-resource.component.d.ts +14 -2
  61. package/dist/lib/resource-wrappers/chat-tasks-resource.component.d.ts.map +1 -1
  62. package/dist/lib/resource-wrappers/chat-tasks-resource.component.js +32 -8
  63. package/dist/lib/resource-wrappers/chat-tasks-resource.component.js.map +1 -1
  64. package/dist/lib/resource-wrappers/dashboard-resource.component.d.ts +54 -8
  65. package/dist/lib/resource-wrappers/dashboard-resource.component.d.ts.map +1 -1
  66. package/dist/lib/resource-wrappers/dashboard-resource.component.js +138 -46
  67. package/dist/lib/resource-wrappers/dashboard-resource.component.js.map +1 -1
  68. package/dist/lib/resource-wrappers/livekit-room-resource.component.d.ts +223 -30
  69. package/dist/lib/resource-wrappers/livekit-room-resource.component.d.ts.map +1 -1
  70. package/dist/lib/resource-wrappers/livekit-room-resource.component.js +438 -118
  71. package/dist/lib/resource-wrappers/livekit-room-resource.component.js.map +1 -1
  72. package/dist/lib/resource-wrappers/search-results-resource.component.d.ts +7 -3
  73. package/dist/lib/resource-wrappers/search-results-resource.component.d.ts.map +1 -1
  74. package/dist/lib/resource-wrappers/search-results-resource.component.js +23 -15
  75. package/dist/lib/resource-wrappers/search-results-resource.component.js.map +1 -1
  76. package/dist/lib/resource-wrappers/view-resource.component.d.ts +68 -10
  77. package/dist/lib/resource-wrappers/view-resource.component.d.ts.map +1 -1
  78. package/dist/lib/resource-wrappers/view-resource.component.js +178 -62
  79. package/dist/lib/resource-wrappers/view-resource.component.js.map +1 -1
  80. package/dist/lib/services/startup-validation.service.d.ts +4 -0
  81. package/dist/lib/services/startup-validation.service.d.ts.map +1 -1
  82. package/dist/lib/services/startup-validation.service.js +10 -2
  83. package/dist/lib/services/startup-validation.service.js.map +1 -1
  84. package/dist/lib/services/system-validation.service.d.ts +13 -1
  85. package/dist/lib/services/system-validation.service.d.ts.map +1 -1
  86. package/dist/lib/services/system-validation.service.js +29 -5
  87. package/dist/lib/services/system-validation.service.js.map +1 -1
  88. package/dist/lib/shell/components/dialogs/app-access-dialog.component.d.ts +54 -4
  89. package/dist/lib/shell/components/dialogs/app-access-dialog.component.d.ts.map +1 -1
  90. package/dist/lib/shell/components/dialogs/app-access-dialog.component.js +130 -40
  91. package/dist/lib/shell/components/dialogs/app-access-dialog.component.js.map +1 -1
  92. package/dist/lib/shell/components/header/app-nav.component.d.ts +29 -1
  93. package/dist/lib/shell/components/header/app-nav.component.d.ts.map +1 -1
  94. package/dist/lib/shell/components/header/app-nav.component.js +56 -12
  95. package/dist/lib/shell/components/header/app-nav.component.js.map +1 -1
  96. package/dist/lib/shell/components/header/app-switcher.component.d.ts +33 -5
  97. package/dist/lib/shell/components/header/app-switcher.component.d.ts.map +1 -1
  98. package/dist/lib/shell/components/header/app-switcher.component.js +85 -29
  99. package/dist/lib/shell/components/header/app-switcher.component.js.map +1 -1
  100. package/dist/lib/shell/components/record-open/record-origin-crumb.component.d.ts +7 -1
  101. package/dist/lib/shell/components/record-open/record-origin-crumb.component.d.ts.map +1 -1
  102. package/dist/lib/shell/components/record-open/record-origin-crumb.component.js +63 -9
  103. package/dist/lib/shell/components/record-open/record-origin-crumb.component.js.map +1 -1
  104. package/dist/lib/shell/components/tabs/component-cache-manager.d.ts +30 -1
  105. package/dist/lib/shell/components/tabs/component-cache-manager.d.ts.map +1 -1
  106. package/dist/lib/shell/components/tabs/component-cache-manager.js +63 -15
  107. package/dist/lib/shell/components/tabs/component-cache-manager.js.map +1 -1
  108. package/dist/lib/shell/components/tabs/tab-container.component.d.ts +99 -9
  109. package/dist/lib/shell/components/tabs/tab-container.component.d.ts.map +1 -1
  110. package/dist/lib/shell/components/tabs/tab-container.component.js +310 -110
  111. package/dist/lib/shell/components/tabs/tab-container.component.js.map +1 -1
  112. package/dist/lib/shell/loading-themes.d.ts +16 -12
  113. package/dist/lib/shell/loading-themes.d.ts.map +1 -1
  114. package/dist/lib/shell/loading-themes.js +145 -137
  115. package/dist/lib/shell/loading-themes.js.map +1 -1
  116. package/dist/lib/shell/readiness-beacon.d.ts +24 -0
  117. package/dist/lib/shell/readiness-beacon.d.ts.map +1 -0
  118. package/dist/lib/shell/readiness-beacon.js +40 -0
  119. package/dist/lib/shell/readiness-beacon.js.map +1 -0
  120. package/dist/lib/shell/services/about-dialog.service.d.ts +6 -0
  121. package/dist/lib/shell/services/about-dialog.service.d.ts.map +1 -1
  122. package/dist/lib/shell/services/about-dialog.service.js +16 -4
  123. package/dist/lib/shell/services/about-dialog.service.js.map +1 -1
  124. package/dist/lib/shell/services/profile-dialog.service.d.ts +6 -0
  125. package/dist/lib/shell/services/profile-dialog.service.d.ts.map +1 -1
  126. package/dist/lib/shell/services/profile-dialog.service.js +16 -4
  127. package/dist/lib/shell/services/profile-dialog.service.js.map +1 -1
  128. package/dist/lib/shell/services/user-sharing-center-dialog.service.d.ts +6 -0
  129. package/dist/lib/shell/services/user-sharing-center-dialog.service.d.ts.map +1 -1
  130. package/dist/lib/shell/services/user-sharing-center-dialog.service.js +15 -3
  131. package/dist/lib/shell/services/user-sharing-center-dialog.service.js.map +1 -1
  132. package/dist/lib/shell/shell.component.d.ts +275 -40
  133. package/dist/lib/shell/shell.component.d.ts.map +1 -1
  134. package/dist/lib/shell/shell.component.js +704 -290
  135. package/dist/lib/shell/shell.component.js.map +1 -1
  136. package/dist/lib/single-dashboard/Components/add-item/add-item.component.d.ts +64 -8
  137. package/dist/lib/single-dashboard/Components/add-item/add-item.component.d.ts.map +1 -1
  138. package/dist/lib/single-dashboard/Components/add-item/add-item.component.js +129 -37
  139. package/dist/lib/single-dashboard/Components/add-item/add-item.component.js.map +1 -1
  140. package/dist/lib/single-dashboard/Components/delete-item/delete-item.component.d.ts +26 -2
  141. package/dist/lib/single-dashboard/Components/delete-item/delete-item.component.d.ts.map +1 -1
  142. package/dist/lib/single-dashboard/Components/delete-item/delete-item.component.js +48 -10
  143. package/dist/lib/single-dashboard/Components/delete-item/delete-item.component.js.map +1 -1
  144. package/dist/lib/single-dashboard/Components/edit-dashboard/edit-dashboard.component.d.ts +84 -6
  145. package/dist/lib/single-dashboard/Components/edit-dashboard/edit-dashboard.component.d.ts.map +1 -1
  146. package/dist/lib/single-dashboard/Components/edit-dashboard/edit-dashboard.component.js +133 -29
  147. package/dist/lib/single-dashboard/Components/edit-dashboard/edit-dashboard.component.js.map +1 -1
  148. package/dist/lib/single-dashboard/single-dashboard.component.d.ts +173 -23
  149. package/dist/lib/single-dashboard/single-dashboard.component.d.ts.map +1 -1
  150. package/dist/lib/single-dashboard/single-dashboard.component.js +383 -109
  151. package/dist/lib/single-dashboard/single-dashboard.component.js.map +1 -1
  152. package/dist/lib/single-list-detail/single-list-detail.component.d.ts +383 -55
  153. package/dist/lib/single-list-detail/single-list-detail.component.d.ts.map +1 -1
  154. package/dist/lib/single-list-detail/single-list-detail.component.js +962 -326
  155. package/dist/lib/single-list-detail/single-list-detail.component.js.map +1 -1
  156. package/dist/lib/single-query/single-query.component.d.ts +22 -2
  157. package/dist/lib/single-query/single-query.component.d.ts.map +1 -1
  158. package/dist/lib/single-query/single-query.component.js +40 -10
  159. package/dist/lib/single-query/single-query.component.js.map +1 -1
  160. package/dist/lib/single-record/single-record.component.d.ts +41 -3
  161. package/dist/lib/single-record/single-record.component.d.ts.map +1 -1
  162. package/dist/lib/single-record/single-record.component.js +71 -11
  163. package/dist/lib/single-record/single-record.component.js.map +1 -1
  164. package/dist/lib/single-search-result/single-search-result.component.d.ts +23 -3
  165. package/dist/lib/single-search-result/single-search-result.component.d.ts.map +1 -1
  166. package/dist/lib/single-search-result/single-search-result.component.js +42 -12
  167. package/dist/lib/single-search-result/single-search-result.component.js.map +1 -1
  168. package/dist/lib/system-validation/system-validation-banner.component.d.ts +12 -2
  169. package/dist/lib/system-validation/system-validation-banner.component.d.ts.map +1 -1
  170. package/dist/lib/system-validation/system-validation-banner.component.js +25 -5
  171. package/dist/lib/system-validation/system-validation-banner.component.js.map +1 -1
  172. package/dist/lib/user-menu/user-menu.types.d.ts +4 -0
  173. package/dist/lib/user-menu/user-menu.types.d.ts.map +1 -1
  174. package/dist/lib/user-menu/user-menu.types.js +10 -2
  175. package/dist/lib/user-menu/user-menu.types.js.map +1 -1
  176. package/dist/lib/user-notifications/user-notifications.component.d.ts +59 -7
  177. package/dist/lib/user-notifications/user-notifications.component.d.ts.map +1 -1
  178. package/dist/lib/user-notifications/user-notifications.component.js +151 -47
  179. package/dist/lib/user-notifications/user-notifications.component.js.map +1 -1
  180. package/dist/lib/user-profile/user-profile.component.d.ts +3 -2
  181. package/dist/lib/user-profile/user-profile.component.d.ts.map +1 -1
  182. package/dist/lib/user-profile/user-profile.component.js +5 -4
  183. package/dist/lib/user-profile/user-profile.component.js.map +1 -1
  184. package/package.json +52 -52
@@ -158,20 +158,60 @@ export class TabContainerComponent extends BaseAngularComponent {
158
158
  appRef;
159
159
  environmentInjector;
160
160
  cdr;
161
- glContainer;
162
- directContentContainer;
163
- recordsGlContainer;
161
+ GlContainer;
162
+ /** @deprecated Use {@link GlContainer}. */
163
+ get glContainer() {
164
+ return this.GlContainer;
165
+ }
166
+ /** @deprecated Use {@link GlContainer}. */
167
+ set glContainer(value) {
168
+ this.GlContainer = value;
169
+ }
170
+ DirectContentContainer;
171
+ /** @deprecated Use {@link DirectContentContainer}. */
172
+ get directContentContainer() {
173
+ return this.DirectContentContainer;
174
+ }
175
+ /** @deprecated Use {@link DirectContentContainer}. */
176
+ set directContentContainer(value) {
177
+ this.DirectContentContainer = value;
178
+ }
179
+ RecordsGlContainer;
180
+ /** @deprecated Use {@link RecordsGlContainer}. */
181
+ get recordsGlContainer() {
182
+ return this.RecordsGlContainer;
183
+ }
184
+ /** @deprecated Use {@link RecordsGlContainer}. */
185
+ set recordsGlContainer(value) {
186
+ this.RecordsGlContainer = value;
187
+ }
164
188
  /**
165
189
  * Emitted when the first resource component finishes loading.
166
190
  * This allows the shell to keep showing its loading indicator until the first
167
191
  * resource is ready, eliminating the visual gap between shell loading and resource loading.
168
192
  */
169
- firstResourceLoadComplete = new EventEmitter();
193
+ FirstResourceLoadComplete = new EventEmitter();
194
+ /**
195
+ * @deprecated Use {@link FirstResourceLoadComplete}.
196
+ *
197
+ * The same emitter under the old binding name, so a template still binding
198
+ * (firstResourceLoadComplete) keeps working. Must stay AFTER FirstResourceLoadComplete: class fields
199
+ * initialise in order, and the other way round this captures undefined.
200
+ */
201
+ firstResourceLoadComplete = this.FirstResourceLoadComplete;
170
202
  /**
171
203
  * Emitted when Golden Layout fails to initialize after multiple retries.
172
204
  * The shell can use this to show an error dialog and redirect.
173
205
  */
174
- layoutInitError = new EventEmitter();
206
+ LayoutInitError = new EventEmitter();
207
+ /**
208
+ * @deprecated Use {@link LayoutInitError}.
209
+ *
210
+ * The same emitter under the old binding name, so a template still binding
211
+ * (layoutInitError) keeps working. Must stay AFTER LayoutInitError: class fields
212
+ * initialise in order, and the other way round this captures undefined.
213
+ */
214
+ layoutInitError = this.LayoutInitError;
175
215
  pinService = inject(HomeAppPinService);
176
216
  navigationService = inject(NavigationService);
177
217
  recordSwitcher = inject(RecordSwitcherService);
@@ -188,11 +228,28 @@ export class TabContainerComponent extends BaseAngularComponent {
188
228
  // When a tab's content changes while active, both the reload path (workspace config subscription)
189
229
  // and onTabShown can race to call loadTabContent, resulting in duplicate component rendering.
190
230
  tabsCurrentlyLoading = new Set();
231
+ // Record-tab shows GL fired while the records region was HIDDEN, by tab id.
232
+ // Their content loads are deferred until the region is the visible surface —
233
+ // see the TabShown handler in wireRecordsLayoutEvents.
234
+ //
235
+ // A SET, not a map of containers: the replay deliberately re-resolves the live
236
+ // container via GetContainer(tabId), so a parked one would never be read — and
237
+ // holding it would retain a GL container, and its possibly-detached DOM, for as
238
+ // long as the region stays hidden. The type says the id is all that is kept.
239
+ pendingRecordShows = new Set();
191
240
  // NEW: Smart component cache for preserving state across tab switches
192
241
  cacheManager;
193
242
  // Single-resource mode: render component directly without Golden Layout
194
243
  // This avoids the 20px height issue when GL header is hidden
195
- useSingleResourceMode = false;
244
+ UseSingleResourceMode = false;
245
+ /** @deprecated Use {@link UseSingleResourceMode}. */
246
+ get useSingleResourceMode() {
247
+ return this.UseSingleResourceMode;
248
+ }
249
+ /** @deprecated Use {@link UseSingleResourceMode}. */
250
+ set useSingleResourceMode(value) {
251
+ this.UseSingleResourceMode = value;
252
+ }
196
253
  singleResourceComponentRef = null;
197
254
  /** Cache identity of the current single-resource component for detachment */
198
255
  singleResourceCacheIdentity = null;
@@ -200,10 +257,42 @@ export class TabContainerComponent extends BaseAngularComponent {
200
257
  currentSingleResourceSignature = null; // Track loaded content signature to avoid unnecessary reloads
201
258
  isCreatingInitialTabs = false; // Flag to prevent syncTabsWithConfiguration during initial tab creation
202
259
  // Context menu state
203
- contextMenuVisible = false;
204
- contextMenuX = 0;
205
- contextMenuY = 0;
206
- contextMenuTabId = null;
260
+ ContextMenuVisible = false;
261
+ /** @deprecated Use {@link ContextMenuVisible}. */
262
+ get contextMenuVisible() {
263
+ return this.ContextMenuVisible;
264
+ }
265
+ /** @deprecated Use {@link ContextMenuVisible}. */
266
+ set contextMenuVisible(value) {
267
+ this.ContextMenuVisible = value;
268
+ }
269
+ ContextMenuX = 0;
270
+ /** @deprecated Use {@link ContextMenuX}. */
271
+ get contextMenuX() {
272
+ return this.ContextMenuX;
273
+ }
274
+ /** @deprecated Use {@link ContextMenuX}. */
275
+ set contextMenuX(value) {
276
+ this.ContextMenuX = value;
277
+ }
278
+ ContextMenuY = 0;
279
+ /** @deprecated Use {@link ContextMenuY}. */
280
+ get contextMenuY() {
281
+ return this.ContextMenuY;
282
+ }
283
+ /** @deprecated Use {@link ContextMenuY}. */
284
+ set contextMenuY(value) {
285
+ this.ContextMenuY = value;
286
+ }
287
+ ContextMenuTabId = null;
288
+ /** @deprecated Use {@link ContextMenuTabId}. */
289
+ get contextMenuTabId() {
290
+ return this.ContextMenuTabId;
291
+ }
292
+ /** @deprecated Use {@link ContextMenuTabId}. */
293
+ set contextMenuTabId(value) {
294
+ this.ContextMenuTabId = value;
295
+ }
207
296
  /** The control that opened the context menu — focus returns here on close */
208
297
  contextMenuAnchor = null;
209
298
  // ---- RECORDS region (records-style record opens) ----
@@ -420,6 +509,9 @@ export class TabContainerComponent extends BaseAngularComponent {
420
509
  // Flush NOW — the visibility class must land in this pass, not
421
510
  // whenever the next unrelated emission happens to run CD.
422
511
  this.flushRegionCd();
512
+ if (showing) {
513
+ this.flushPendingRecordShows();
514
+ }
423
515
  }
424
516
  if (showing && activeTab && this.recordsLayoutInitialized) {
425
517
  this.focusRecordsTab(activeTab.id);
@@ -536,6 +628,46 @@ export class TabContainerComponent extends BaseAngularComponent {
536
628
  }
537
629
  return true;
538
630
  }
631
+ /**
632
+ * Load a record tab's content into its GL container and mark it loaded —
633
+ * the second half of the TabShown handler, split out so a show parked
634
+ * while the region was hidden can be replayed on reveal.
635
+ */
636
+ async loadShownRecordTab(tabId, container) {
637
+ await this.loadTabContent(tabId, container);
638
+ // Mark loaded ONLY when content actually attached to the LIVE
639
+ // container. During layout restore, GL can re-render item elements
640
+ // while an async load is in flight — the load appends into a
641
+ // detached element and the visible pane stays blank. Leaving the
642
+ // tab unmarked lets the next show retry, which hits the component
643
+ // cache and reattaches instantly into the live element.
644
+ const live = this.recordsLayoutManager.GetContainer(tabId);
645
+ if (live?.element && live.element.childElementCount > 0) {
646
+ this.recordsLayoutManager.MarkTabLoaded(tabId);
647
+ }
648
+ }
649
+ /**
650
+ * Replay the shows parked while the records region was hidden (see the
651
+ * TabShown handler). Always against the LIVE container: a breakpoint
652
+ * rebuild between park and reveal replaces the one GL handed us (and its
653
+ * Destroy fires TabClosed, which drops the stale entry anyway).
654
+ */
655
+ flushPendingRecordShows() {
656
+ if (this.pendingRecordShows.size === 0) {
657
+ return;
658
+ }
659
+ const parked = [...this.pendingRecordShows];
660
+ this.pendingRecordShows.clear();
661
+ for (const tabId of parked) {
662
+ if (this.recordsLayoutManager.IsTabLoaded(tabId)) {
663
+ continue;
664
+ }
665
+ const live = this.recordsLayoutManager.GetContainer(tabId);
666
+ if (live) {
667
+ this.loadShownRecordTab(tabId, live).catch(e => LogError(`Deferred record tab load failed for ${tabId}: ${e}`));
668
+ }
669
+ }
670
+ }
539
671
  /** Focus a records-region tab without feeding back into SetActiveTab loops */
540
672
  focusRecordsTab(tabId) {
541
673
  if (this.recordsRegionActiveTabId !== tabId) {
@@ -554,7 +686,7 @@ export class TabContainerComponent extends BaseAngularComponent {
554
686
  if (this.recordsLayoutInitialized) {
555
687
  return;
556
688
  }
557
- const container = this.recordsGlContainer?.nativeElement;
689
+ const container = this.RecordsGlContainer?.nativeElement;
558
690
  if (!container) {
559
691
  return; // Template not settled yet — the next sync pass retries
560
692
  }
@@ -690,7 +822,7 @@ export class TabContainerComponent extends BaseAngularComponent {
690
822
  }), this.layoutManager.TabDoubleClicked.subscribe(tabId => {
691
823
  this.workspaceManager.TogglePin(tabId);
692
824
  }), this.layoutManager.TabRightClicked.subscribe(event => {
693
- this.showContextMenu(event.x, event.y, event.tabId, event.anchorEl);
825
+ this.ShowContextMenu(event.x, event.y, event.tabId, event.anchorEl);
694
826
  }));
695
827
  this.wireRecordsLayoutEvents();
696
828
  // Breakpoint crossings rebuild the records GL in the other chrome mode
@@ -728,7 +860,7 @@ export class TabContainerComponent extends BaseAngularComponent {
728
860
  // the region is the visible surface for this configuration.
729
861
  this.syncRecordsRegion(config);
730
862
  this.updateRecordSurfaceState(config);
731
- if (this.useSingleResourceMode) {
863
+ if (this.UseSingleResourceMode) {
732
864
  // In single-resource mode, reload content if the tab content changed
733
865
  // The same tab ID can have different content (tab gets reused)
734
866
  const activeTab = config.tabs.find(t => t.id === config.activeTabId) || config.tabs[0];
@@ -781,20 +913,28 @@ export class TabContainerComponent extends BaseAngularComponent {
781
913
  */
782
914
  wireRecordsLayoutEvents() {
783
915
  this.subscriptions.push(this.recordsLayoutManager.TabShown.subscribe(async (event) => {
784
- if (event.isFirstShow) {
785
- await this.loadTabContent(event.tabId, event.container);
786
- // Mark loaded ONLY when content actually attached to the LIVE
787
- // container. During layout restore, GL can re-render item elements
788
- // while an async load is in flight — the load appends into a
789
- // detached element and the visible pane stays blank. Leaving the
790
- // tab unmarked lets the next show retry, which hits the component
791
- // cache and reattaches instantly into the live element.
792
- const live = this.recordsLayoutManager.GetContainer(event.tabId);
793
- if (live?.element && live.element.childElementCount > 0) {
794
- this.recordsLayoutManager.MarkTabLoaded(event.tabId);
795
- }
916
+ if (!event.isFirstShow) {
917
+ return;
918
+ }
919
+ // LAZY content. The records GL initializes EAGERLY (see
920
+ // ensureRecordsLayoutInitialized) so the strip and the pill never
921
+ // drift from the workspace — and as it builds, GL fires 'show' for
922
+ // every stack's active tab while the region is still
923
+ // visibility-hidden behind the main surface. Loading content on
924
+ // those shows means every restored record hydrates at BOOT: an open
925
+ // AI Agent Run pulls its whole run tree (every prompt run, action
926
+ // log, step) before the user has looked at it, and client-side
927
+ // RunView coalescing folds the ACTIVE tab's own reads into the same
928
+ // requests, so the surface the user is looking at waits for records
929
+ // they are not. Park the show; it is replayed when the region shows.
930
+ if (!this.ShowRecordsRegion) {
931
+ this.pendingRecordShows.add(event.tabId);
932
+ return;
796
933
  }
934
+ this.pendingRecordShows.delete(event.tabId);
935
+ await this.loadShownRecordTab(event.tabId, event.container);
797
936
  }), this.recordsLayoutManager.TabClosed.subscribe(async (tabId) => {
937
+ this.pendingRecordShows.delete(tabId);
798
938
  this.cleanupTabComponent(tabId);
799
939
  // REBUILD guard (breakpoint crossing): Destroy() fires TabClosed for
800
940
  // every pane. The tabs are NOT closing — the layout is being rebuilt
@@ -843,12 +983,12 @@ export class TabContainerComponent extends BaseAngularComponent {
843
983
  }), this.recordsLayoutManager.TabDoubleClicked.subscribe(tabId => {
844
984
  this.workspaceManager.TogglePin(tabId);
845
985
  }), this.recordsLayoutManager.TabRightClicked.subscribe(event => {
846
- this.showContextMenu(event.x, event.y, event.tabId, event.anchorEl);
986
+ this.ShowContextMenu(event.x, event.y, event.tabId, event.anchorEl);
847
987
  }));
848
988
  }
849
989
  ngAfterViewInit() {
850
990
  // Initialize Golden Layout only if we're not in single-resource mode
851
- if (!this.useSingleResourceMode) {
991
+ if (!this.UseSingleResourceMode) {
852
992
  this.initializeGoldenLayout();
853
993
  }
854
994
  else {
@@ -862,13 +1002,13 @@ export class TabContainerComponent extends BaseAngularComponent {
862
1002
  */
863
1003
  initializeGoldenLayout(forceCreateTabs = false) {
864
1004
  // If we are in single resource mode we do NOT need to do this work as golden layout should not exist in that state
865
- if (this.useSingleResourceMode)
1005
+ if (this.UseSingleResourceMode)
866
1006
  return;
867
- if (!this.glContainer?.nativeElement) {
1007
+ if (!this.GlContainer?.nativeElement) {
868
1008
  this.layoutInitRetryCount++;
869
1009
  if (this.layoutInitRetryCount > this.MAX_LAYOUT_INIT_RETRIES) {
870
1010
  console.error(`Golden Layout container not available after ${this.MAX_LAYOUT_INIT_RETRIES} retries, emitting error`);
871
- this.layoutInitError.emit();
1011
+ this.LayoutInitError.emit();
872
1012
  return;
873
1013
  }
874
1014
  console.warn(`Golden Layout container not available, retry ${this.layoutInitRetryCount}/${this.MAX_LAYOUT_INIT_RETRIES}...`);
@@ -902,7 +1042,7 @@ export class TabContainerComponent extends BaseAngularComponent {
902
1042
  return;
903
1043
  }
904
1044
  // Initialize Golden Layout (we have config now)
905
- this.layoutManager.Initialize(this.glContainer.nativeElement);
1045
+ this.layoutManager.Initialize(this.GlContainer.nativeElement);
906
1046
  // Mark layout as initialized
907
1047
  this.layoutInitialized = true;
908
1048
  // The MAIN layout hosts only non-record tabs — record tabs live in the
@@ -1002,7 +1142,7 @@ export class TabContainerComponent extends BaseAngularComponent {
1002
1142
  async ReloadAllTabs() {
1003
1143
  // Destroy all cached component instances
1004
1144
  this.cacheManager.clearCache();
1005
- if (this.useSingleResourceMode) {
1145
+ if (this.UseSingleResourceMode) {
1006
1146
  // Force reload by clearing the signature check
1007
1147
  this.currentSingleResourceSignature = null;
1008
1148
  this.singleResourceComponentRef = null;
@@ -1095,7 +1235,7 @@ export class TabContainerComponent extends BaseAngularComponent {
1095
1235
  * ensures the layout is properly sized after browser window changes.
1096
1236
  */
1097
1237
  onWindowResize() {
1098
- if (this.layoutInitialized && !this.useSingleResourceMode) {
1238
+ if (this.layoutInitialized && !this.UseSingleResourceMode) {
1099
1239
  this.layoutManager.updateSize();
1100
1240
  }
1101
1241
  }
@@ -1110,12 +1250,12 @@ export class TabContainerComponent extends BaseAngularComponent {
1110
1250
  this.previousTabBarVisible = tabBarVisible;
1111
1251
  // Determine if we should use single-resource mode
1112
1252
  const shouldUseSingleResourceMode = !tabBarVisible;
1113
- if (shouldUseSingleResourceMode !== this.useSingleResourceMode) {
1114
- this.useSingleResourceMode = shouldUseSingleResourceMode;
1253
+ if (shouldUseSingleResourceMode !== this.UseSingleResourceMode) {
1254
+ this.UseSingleResourceMode = shouldUseSingleResourceMode;
1115
1255
  // Defer detectChanges to next microtask to avoid ExpressionChangedAfterItHasBeenCheckedError
1116
1256
  // when this handler fires during an already-running change detection cycle.
1117
1257
  Promise.resolve().then(() => this.cdr.detectChanges());
1118
- if (this.useSingleResourceMode) {
1258
+ if (this.UseSingleResourceMode) {
1119
1259
  // Transitioning to single-resource mode
1120
1260
  // **CRITICAL FIX**: Wait for the template to render directContentContainer
1121
1261
  // before trying to load content. detectChanges() only marks dirty, doesn't render immediately.
@@ -1217,7 +1357,7 @@ export class TabContainerComponent extends BaseAngularComponent {
1217
1357
  }
1218
1358
  this.currentSingleResourceSignature = newSignature;
1219
1359
  // Get the container element
1220
- const container = this.directContentContainer?.nativeElement;
1360
+ const container = this.DirectContentContainer?.nativeElement;
1221
1361
  if (!container) {
1222
1362
  // Retry after view is updated
1223
1363
  setTimeout(() => this.loadSingleResourceContent(), 50);
@@ -1415,7 +1555,7 @@ export class TabContainerComponent extends BaseAngularComponent {
1415
1555
  // Remove children from the container. This detaches the wrapper DOM element
1416
1556
  // without destroying the Angular component — it lives on in the cache.
1417
1557
  // Using removeChild (not innerHTML='') to avoid aggressive DOM cleanup.
1418
- const container = this.directContentContainer?.nativeElement;
1558
+ const container = this.DirectContentContainer?.nativeElement;
1419
1559
  if (container) {
1420
1560
  while (container.firstChild) {
1421
1561
  container.removeChild(container.firstChild);
@@ -1469,7 +1609,7 @@ export class TabContainerComponent extends BaseAngularComponent {
1469
1609
  // Pre-compute and stamp the new signature so the configuration-subscription path
1470
1610
  // in single-resource mode doesn't see a "content changed" delta and trigger a
1471
1611
  // needless reload cycle (which would destroy the currently attached component).
1472
- if (this.useSingleResourceMode && this.currentSingleResourceSignature !== null) {
1612
+ if (this.UseSingleResourceMode && this.currentSingleResourceSignature !== null) {
1473
1613
  const updatedTab = {
1474
1614
  ...tab,
1475
1615
  resourceRecordId: newRecordId,
@@ -1552,7 +1692,7 @@ export class TabContainerComponent extends BaseAngularComponent {
1552
1692
  this.singleResourceCacheIdentity = null;
1553
1693
  // Clear the host container's DOM so the user isn't briefly looking at the
1554
1694
  // destroyed component's wrapper between CloseTab and the default-tab load.
1555
- const directContainer = this.directContentContainer?.nativeElement;
1695
+ const directContainer = this.DirectContentContainer?.nativeElement;
1556
1696
  if (directContainer) {
1557
1697
  while (directContainer.firstChild)
1558
1698
  directContainer.removeChild(directContainer.firstChild);
@@ -2242,11 +2382,11 @@ export class TabContainerComponent extends BaseAngularComponent {
2242
2382
  /**
2243
2383
  * Show context menu
2244
2384
  */
2245
- showContextMenu(x, y, tabId, anchorEl) {
2246
- this.contextMenuX = x;
2247
- this.contextMenuY = y;
2248
- this.contextMenuTabId = tabId;
2249
- this.contextMenuVisible = true;
2385
+ ShowContextMenu(x, y, tabId, anchorEl) {
2386
+ this.ContextMenuX = x;
2387
+ this.ContextMenuY = y;
2388
+ this.ContextMenuTabId = tabId;
2389
+ this.ContextMenuVisible = true;
2250
2390
  // Remember the invoking control so closing can hand focus back (the
2251
2391
  // slot button passes itself; right-click falls back to whatever was
2252
2392
  // focused). aria-expanded reflects the open menu on the anchor.
@@ -2273,14 +2413,14 @@ export class TabContainerComponent extends BaseAngularComponent {
2273
2413
  const clickHandler = (event) => {
2274
2414
  const target = event.target;
2275
2415
  if (!target.closest('.context-menu')) {
2276
- this.hideContextMenu();
2416
+ this.HideContextMenu();
2277
2417
  document.removeEventListener('click', clickHandler, true);
2278
2418
  document.removeEventListener('keydown', keyHandler, true);
2279
2419
  }
2280
2420
  };
2281
2421
  const keyHandler = (event) => {
2282
2422
  if (event.key === 'Escape') {
2283
- this.hideContextMenu();
2423
+ this.HideContextMenu();
2284
2424
  document.removeEventListener('click', clickHandler, true);
2285
2425
  document.removeEventListener('keydown', keyHandler, true);
2286
2426
  }
@@ -2289,12 +2429,16 @@ export class TabContainerComponent extends BaseAngularComponent {
2289
2429
  document.addEventListener('keydown', keyHandler, true);
2290
2430
  }, 0);
2291
2431
  }
2432
+ /** @deprecated Use {@link ShowContextMenu}. */
2433
+ showContextMenu(x, y, tabId, anchorEl) {
2434
+ return this.ShowContextMenu(x, y, tabId, anchorEl);
2435
+ }
2292
2436
  /**
2293
2437
  * Hide context menu
2294
2438
  */
2295
- hideContextMenu(restoreFocus = true) {
2296
- this.contextMenuVisible = false;
2297
- this.contextMenuTabId = null;
2439
+ HideContextMenu(restoreFocus = true) {
2440
+ this.ContextMenuVisible = false;
2441
+ this.ContextMenuTabId = null;
2298
2442
  const anchor = this.contextMenuAnchor;
2299
2443
  this.contextMenuAnchor = null;
2300
2444
  if (anchor?.classList.contains('mj-tab-type-slot')) {
@@ -2310,12 +2454,16 @@ export class TabContainerComponent extends BaseAngularComponent {
2310
2454
  requestAnimationFrame(() => anchor.focus());
2311
2455
  }
2312
2456
  }
2457
+ /** @deprecated Use {@link HideContextMenu}. */
2458
+ hideContextMenu(restoreFocus = true) {
2459
+ return this.HideContextMenu(restoreFocus);
2460
+ }
2313
2461
  /**
2314
2462
  * role="menu" keyboard model: ArrowUp/Down rove (wrapping) over enabled
2315
2463
  * items, Home/End jump, Escape closes with focus return, Tab dismisses
2316
2464
  * (native menus don't trap Tab).
2317
2465
  */
2318
- onMenuKeydown(event) {
2466
+ OnMenuKeydown(event) {
2319
2467
  const items = Array.from(document.querySelectorAll('.context-menu [role="menuitem"]:not([disabled])'));
2320
2468
  if (items.length === 0) {
2321
2469
  return;
@@ -2343,37 +2491,53 @@ export class TabContainerComponent extends BaseAngularComponent {
2343
2491
  // deferred document-level teardown listener attaches.
2344
2492
  event.preventDefault();
2345
2493
  event.stopPropagation();
2346
- this.hideContextMenu();
2494
+ this.HideContextMenu();
2347
2495
  break;
2348
2496
  case 'Tab':
2349
- this.hideContextMenu(false);
2497
+ this.HideContextMenu(false);
2350
2498
  break;
2351
2499
  }
2352
2500
  }
2501
+ /** @deprecated Use {@link OnMenuKeydown}. */
2502
+ onMenuKeydown(event) {
2503
+ return this.OnMenuKeydown(event);
2504
+ }
2353
2505
  /**
2354
2506
  * Check if context menu tab is pinned
2355
2507
  */
2356
- get isContextTabPinned() {
2357
- if (!this.contextMenuTabId)
2508
+ get IsContextTabPinned() {
2509
+ if (!this.ContextMenuTabId)
2358
2510
  return false;
2359
- const tab = this.workspaceManager.GetTab(this.contextMenuTabId);
2511
+ const tab = this.workspaceManager.GetTab(this.ContextMenuTabId);
2360
2512
  return tab?.isPinned || false;
2361
2513
  }
2514
+ /** @deprecated Use {@link IsContextTabPinned}. */
2515
+ get isContextTabPinned() {
2516
+ return this.IsContextTabPinned;
2517
+ }
2362
2518
  /** Context tab is a records-REGION record — eligible for "Move to Workspace" */
2363
- get canContextMoveToWorkspace() {
2519
+ get CanContextMoveToWorkspace() {
2364
2520
  // Docked/region composition is a DESKTOP concept — no move actions on mobile
2365
- if (!this.contextMenuTabId || !this.RecordsStyleActive || this.mobileRecordsActive)
2521
+ if (!this.ContextMenuTabId || !this.RecordsStyleActive || this.mobileRecordsActive)
2366
2522
  return false;
2367
- const tab = this.workspaceManager.GetTab(this.contextMenuTabId);
2523
+ const tab = this.workspaceManager.GetTab(this.ContextMenuTabId);
2368
2524
  return !!tab && IsRecordsRegionTab(tab.configuration);
2369
2525
  }
2526
+ /** @deprecated Use {@link CanContextMoveToWorkspace}. */
2527
+ get canContextMoveToWorkspace() {
2528
+ return this.CanContextMoveToWorkspace;
2529
+ }
2370
2530
  /** Context tab is a DOCKED record — eligible for "Move to Records" */
2371
- get canContextMoveToRecords() {
2372
- if (!this.contextMenuTabId || !this.RecordsStyleActive || this.mobileRecordsActive)
2531
+ get CanContextMoveToRecords() {
2532
+ if (!this.ContextMenuTabId || !this.RecordsStyleActive || this.mobileRecordsActive)
2373
2533
  return false;
2374
- const tab = this.workspaceManager.GetTab(this.contextMenuTabId);
2534
+ const tab = this.workspaceManager.GetTab(this.ContextMenuTabId);
2375
2535
  return !!tab && IsRecordsTabConfiguration(tab.configuration) && IsRecordDockedToWorkspace(tab.configuration);
2376
2536
  }
2537
+ /** @deprecated Use {@link CanContextMoveToRecords}. */
2538
+ get canContextMoveToRecords() {
2539
+ return this.CanContextMoveToRecords;
2540
+ }
2377
2541
  /**
2378
2542
  * Promote: records region → main workspace layout ("Move to Workspace").
2379
2543
  * ORDER MATTERS: activate FIRST, then flip the flag. With the tab already
@@ -2382,15 +2546,19 @@ export class TabContainerComponent extends BaseAngularComponent {
2382
2546
  * (b) a single-resource→multi-tab transition triggered by the flip exempts
2383
2547
  * it from the force-pin sweep (it IS the activeTabId).
2384
2548
  */
2385
- onContextMoveToWorkspace() {
2386
- const tabId = this.contextMenuTabId;
2387
- this.hideContextMenu(false);
2549
+ OnContextMoveToWorkspace() {
2550
+ const tabId = this.ContextMenuTabId;
2551
+ this.HideContextMenu(false);
2388
2552
  if (!tabId)
2389
2553
  return;
2390
2554
  this.workspaceManager.SetActiveTab(tabId);
2391
2555
  this.workspaceManager.UpdateTabConfiguration(tabId, { [RECORD_DOCKED_TO_WORKSPACE_KEY]: true });
2392
2556
  this.assertMovedTabActivation(tabId);
2393
2557
  }
2558
+ /** @deprecated Use {@link OnContextMoveToWorkspace}. */
2559
+ onContextMoveToWorkspace() {
2560
+ return this.OnContextMoveToWorkspace();
2561
+ }
2394
2562
  /**
2395
2563
  * Demote: main workspace layout → records region ("Move to Records").
2396
2564
  * Same activate-first ordering: the flip's emission then computes
@@ -2398,15 +2566,19 @@ export class TabContainerComponent extends BaseAngularComponent {
2398
2566
  * the returned tab. `false` (not undefined) so the choice is explicit in
2399
2567
  * the persisted configuration.
2400
2568
  */
2401
- onContextMoveToRecords() {
2402
- const tabId = this.contextMenuTabId;
2403
- this.hideContextMenu(false);
2569
+ OnContextMoveToRecords() {
2570
+ const tabId = this.ContextMenuTabId;
2571
+ this.HideContextMenu(false);
2404
2572
  if (!tabId)
2405
2573
  return;
2406
2574
  this.workspaceManager.SetActiveTab(tabId);
2407
2575
  this.workspaceManager.UpdateTabConfiguration(tabId, { [RECORD_DOCKED_TO_WORKSPACE_KEY]: false });
2408
2576
  this.assertMovedTabActivation(tabId);
2409
2577
  }
2578
+ /** @deprecated Use {@link OnContextMoveToRecords}. */
2579
+ onContextMoveToRecords() {
2580
+ return this.OnContextMoveToRecords();
2581
+ }
2410
2582
  /**
2411
2583
  * A move's SOURCE layout auto-activates its next tab when the moved tab is
2412
2584
  * removed, and that GL activation WRITES BACK into the workspace — stomping
@@ -2425,69 +2597,89 @@ export class TabContainerComponent extends BaseAngularComponent {
2425
2597
  /**
2426
2598
  * Toggle pin from context menu
2427
2599
  */
2428
- onContextPin() {
2429
- if (this.contextMenuTabId) {
2430
- this.workspaceManager.TogglePin(this.contextMenuTabId);
2600
+ OnContextPin() {
2601
+ if (this.ContextMenuTabId) {
2602
+ this.workspaceManager.TogglePin(this.ContextMenuTabId);
2431
2603
  }
2432
- this.hideContextMenu();
2604
+ this.HideContextMenu();
2605
+ }
2606
+ /** @deprecated Use {@link OnContextPin}. */
2607
+ onContextPin() {
2608
+ return this.OnContextPin();
2433
2609
  }
2434
2610
  /**
2435
2611
  * Close tab from context menu
2436
2612
  */
2437
- onContextClose() {
2438
- if (this.contextMenuTabId) {
2439
- const tab = this.workspaceManager.GetTab(this.contextMenuTabId);
2613
+ OnContextClose() {
2614
+ if (this.ContextMenuTabId) {
2615
+ const tab = this.workspaceManager.GetTab(this.ContextMenuTabId);
2440
2616
  const manager = tab && this.isRecordTab(tab) ? this.recordsLayoutManager : this.layoutManager;
2441
- manager.RemoveTab(this.contextMenuTabId);
2617
+ manager.RemoveTab(this.ContextMenuTabId);
2442
2618
  }
2443
2619
  // The anchor lives on the closed tab — nothing to return focus to.
2444
- this.hideContextMenu(false);
2620
+ this.HideContextMenu(false);
2621
+ }
2622
+ /** @deprecated Use {@link OnContextClose}. */
2623
+ onContextClose() {
2624
+ return this.OnContextClose();
2445
2625
  }
2446
2626
  /**
2447
2627
  * Close all other tabs from context menu
2448
2628
  */
2449
- onContextCloseOthers() {
2450
- if (this.contextMenuTabId) {
2451
- this.workspaceManager.CloseOtherTabs(this.contextMenuTabId);
2629
+ OnContextCloseOthers() {
2630
+ if (this.ContextMenuTabId) {
2631
+ this.workspaceManager.CloseOtherTabs(this.ContextMenuTabId);
2452
2632
  }
2453
- this.hideContextMenu();
2633
+ this.HideContextMenu();
2634
+ }
2635
+ /** @deprecated Use {@link OnContextCloseOthers}. */
2636
+ onContextCloseOthers() {
2637
+ return this.OnContextCloseOthers();
2454
2638
  }
2455
2639
  /**
2456
2640
  * Close tabs to the right from context menu
2457
2641
  */
2458
- onContextCloseToRight() {
2459
- if (this.contextMenuTabId) {
2460
- this.workspaceManager.CloseTabsToRight(this.contextMenuTabId);
2642
+ OnContextCloseToRight() {
2643
+ if (this.ContextMenuTabId) {
2644
+ this.workspaceManager.CloseTabsToRight(this.ContextMenuTabId);
2461
2645
  }
2462
- this.hideContextMenu();
2646
+ this.HideContextMenu();
2647
+ }
2648
+ /** @deprecated Use {@link OnContextCloseToRight}. */
2649
+ onContextCloseToRight() {
2650
+ return this.OnContextCloseToRight();
2463
2651
  }
2464
2652
  /**
2465
2653
  * Check if context menu tab is pinned to Home dashboard
2466
2654
  */
2467
- get isContextTabPinnedToHome() {
2468
- if (!this.contextMenuTabId)
2655
+ get IsContextTabPinnedToHome() {
2656
+ if (!this.ContextMenuTabId)
2469
2657
  return false;
2470
- const tab = this.workspaceManager.GetTab(this.contextMenuTabId);
2658
+ const tab = this.workspaceManager.GetTab(this.ContextMenuTabId);
2471
2659
  if (!tab)
2472
2660
  return false;
2473
2661
  const resourceType = this.resolveResourceType(tab);
2474
2662
  return this.pinService.IsPinned(resourceType, tab.configuration);
2475
2663
  }
2664
+ /** @deprecated Use {@link IsContextTabPinnedToHome}. */
2665
+ get isContextTabPinnedToHome() {
2666
+ return this.IsContextTabPinnedToHome;
2667
+ }
2476
2668
  /**
2477
2669
  * Pin current context menu tab to Home dashboard
2478
2670
  */
2479
- async onContextPinToHome() {
2480
- if (this.isContextTabPinnedToHome) {
2481
- this.hideContextMenu();
2671
+ async OnContextPinToHome() {
2672
+ if (this.IsContextTabPinnedToHome) {
2673
+ this.HideContextMenu();
2482
2674
  return;
2483
2675
  }
2484
- if (!this.contextMenuTabId) {
2485
- this.hideContextMenu();
2676
+ if (!this.ContextMenuTabId) {
2677
+ this.HideContextMenu();
2486
2678
  return;
2487
2679
  }
2488
- const tab = this.workspaceManager.GetTab(this.contextMenuTabId);
2680
+ const tab = this.workspaceManager.GetTab(this.ContextMenuTabId);
2489
2681
  if (!tab) {
2490
- this.hideContextMenu();
2682
+ this.HideContextMenu();
2491
2683
  return;
2492
2684
  }
2493
2685
  const resourceType = this.resolveResourceType(tab);
@@ -2518,7 +2710,11 @@ export class TabContainerComponent extends BaseAngularComponent {
2518
2710
  else {
2519
2711
  MJNotificationService.Instance.CreateSimpleNotification(`"${tab.title}" is already pinned to Home`, 'info', 3000);
2520
2712
  }
2521
- this.hideContextMenu();
2713
+ this.HideContextMenu();
2714
+ }
2715
+ /** @deprecated Use {@link OnContextPinToHome}. */
2716
+ async onContextPinToHome() {
2717
+ return this.OnContextPinToHome();
2522
2718
  }
2523
2719
  /**
2524
2720
  * Resolve a WorkspaceTab's resource type string for pin matching
@@ -2545,12 +2741,12 @@ export class TabContainerComponent extends BaseAngularComponent {
2545
2741
  try {
2546
2742
  // Find the active content element — differs by mode
2547
2743
  let contentEl = null;
2548
- if (this.useSingleResourceMode) {
2549
- contentEl = this.directContentContainer?.nativeElement ?? null;
2744
+ if (this.UseSingleResourceMode) {
2745
+ contentEl = this.DirectContentContainer?.nativeElement ?? null;
2550
2746
  }
2551
2747
  else {
2552
2748
  // In Golden Layout mode, find the active tab's content pane
2553
- contentEl = this.glContainer?.nativeElement?.querySelector('.lm_item_container .lm_content');
2749
+ contentEl = this.GlContainer?.nativeElement?.querySelector('.lm_item_container .lm_content');
2554
2750
  }
2555
2751
  if (!contentEl)
2556
2752
  return;
@@ -2574,11 +2770,11 @@ export class TabContainerComponent extends BaseAngularComponent {
2574
2770
  async CaptureActiveThumbnail() {
2575
2771
  try {
2576
2772
  let contentEl = null;
2577
- if (this.useSingleResourceMode) {
2578
- contentEl = this.directContentContainer?.nativeElement ?? null;
2773
+ if (this.UseSingleResourceMode) {
2774
+ contentEl = this.DirectContentContainer?.nativeElement ?? null;
2579
2775
  }
2580
2776
  else {
2581
- contentEl = this.glContainer?.nativeElement?.querySelector('.lm_item_container .lm_content');
2777
+ contentEl = this.GlContainer?.nativeElement?.querySelector('.lm_item_container .lm_content');
2582
2778
  }
2583
2779
  if (!contentEl)
2584
2780
  return undefined;
@@ -2594,7 +2790,7 @@ export class TabContainerComponent extends BaseAngularComponent {
2594
2790
  * forever we emit all events upstream
2595
2791
  */
2596
2792
  emitFirstLoadCompleteOnce() {
2597
- this.firstResourceLoadComplete.emit(); // do this each time to be sure we don't suppress messages
2793
+ this.FirstResourceLoadComplete.emit(); // do this each time to be sure we don't suppress messages
2598
2794
  }
2599
2795
  /**
2600
2796
  * Render an inline "missing resource component" error into a host element when
@@ -2655,12 +2851,12 @@ export class TabContainerComponent extends BaseAngularComponent {
2655
2851
  i0.ɵɵviewQuery(_c0, 5)(_c1, 5)(_c2, 5);
2656
2852
  } if (rf & 2) {
2657
2853
  let _t;
2658
- i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.glContainer = _t.first);
2659
- i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.directContentContainer = _t.first);
2660
- i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.recordsGlContainer = _t.first);
2854
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.GlContainer = _t.first);
2855
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.DirectContentContainer = _t.first);
2856
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.RecordsGlContainer = _t.first);
2661
2857
  } }, hostBindings: function TabContainerComponent_HostBindings(rf, ctx) { if (rf & 1) {
2662
2858
  i0.ɵɵlistener("resize", function TabContainerComponent_resize_HostBindingHandler() { return ctx.onWindowResize(); }, i0.ɵɵresolveWindow);
2663
- } }, outputs: { firstResourceLoadComplete: "firstResourceLoadComplete", layoutInitError: "layoutInitError" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 6, vars: 5, consts: [["directContentContainer", ""], ["glContainer", ""], ["recordsGlContainer", ""], [1, "tab-container"], [1, "main-region"], [1, "direct-content-container"], [1, "gl-container"], ["role", "menu", "aria-label", "Tab actions", 1, "context-menu", 3, "left", "top"], [1, "records-region"], [3, "Title", "Icon", "Color", "Count"], [1, "records-gl-container"], [3, "VisibleChange", "ActivateRequested", "CloseRequested", "Entries", "Visible"], [3, "SwitcherRequested", "Title", "Icon", "Color", "Count"], ["role", "menu", "aria-label", "Tab actions", 1, "context-menu", 3, "keydown"], ["type", "button", "role", "menuitem", 1, "context-menu-item", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-thumbtack"], [1, "context-menu-divider"], ["type", "button", "role", "menuitem", 1, "context-menu-item", 3, "click", "disabled"], ["aria-hidden", "true", 1, "fa-solid", "fa-house-chimney"], ["aria-hidden", "true", 1, "fa-solid", "fa-xmark"], ["aria-hidden", "true", 1, "fa-solid", "fa-layer-group"], ["aria-hidden", "true", 1, "fa-solid", "fa-angles-right"], [1, "check"], ["aria-hidden", "true", 1, "fa-solid", "fa-check"], ["type", "button", "role", "menuitem", 1, "context-menu-item"], ["aria-hidden", "true", 1, "fa-solid", "fa-arrow-up-right-from-square"], ["aria-hidden", "true", 1, "fa-solid", "fa-file-lines"]], template: function TabContainerComponent_Template(rf, ctx) { if (rf & 1) {
2859
+ } }, outputs: { FirstResourceLoadComplete: "FirstResourceLoadComplete", firstResourceLoadComplete: "firstResourceLoadComplete", LayoutInitError: "LayoutInitError", layoutInitError: "layoutInitError" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 6, vars: 5, consts: [["directContentContainer", ""], ["glContainer", ""], ["recordsGlContainer", ""], [1, "tab-container"], [1, "main-region"], [1, "direct-content-container"], [1, "gl-container"], ["role", "menu", "aria-label", "Tab actions", 1, "context-menu", 3, "left", "top"], [1, "records-region"], [3, "Title", "Icon", "Color", "Count"], [1, "records-gl-container"], [3, "VisibleChange", "ActivateRequested", "CloseRequested", "Entries", "Visible"], [3, "SwitcherRequested", "Title", "Icon", "Color", "Count"], ["role", "menu", "aria-label", "Tab actions", 1, "context-menu", 3, "keydown"], ["type", "button", "role", "menuitem", 1, "context-menu-item", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-thumbtack"], [1, "context-menu-divider"], ["type", "button", "role", "menuitem", 1, "context-menu-item", 3, "click", "disabled"], ["aria-hidden", "true", 1, "fa-solid", "fa-house-chimney"], ["aria-hidden", "true", 1, "fa-solid", "fa-xmark"], ["aria-hidden", "true", 1, "fa-solid", "fa-layer-group"], ["aria-hidden", "true", 1, "fa-solid", "fa-angles-right"], [1, "check"], ["aria-hidden", "true", 1, "fa-solid", "fa-check"], ["type", "button", "role", "menuitem", 1, "context-menu-item"], ["aria-hidden", "true", 1, "fa-solid", "fa-arrow-up-right-from-square"], ["aria-hidden", "true", 1, "fa-solid", "fa-file-lines"]], template: function TabContainerComponent_Template(rf, ctx) { if (rf & 1) {
2664
2860
  i0.ɵɵelementStart(0, "div", 3)(1, "div", 4);
2665
2861
  i0.ɵɵconditionalCreate(2, TabContainerComponent_Conditional_2_Template, 2, 0, "div", 5)(3, TabContainerComponent_Conditional_3_Template, 2, 0, "div", 6);
2666
2862
  i0.ɵɵelementEnd();
@@ -2681,17 +2877,21 @@ export class TabContainerComponent extends BaseAngularComponent {
2681
2877
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(TabContainerComponent, [{
2682
2878
  type: Component,
2683
2879
  args: [{ standalone: false, selector: 'mj-tab-container', encapsulation: ViewEncapsulation.None, template: "<div class=\"tab-container\">\n\n <!-- Main region: nav pages (single-resource or main Golden Layout).\n Hidden (not destroyed) while the RECORDS region is showing, so nav\n content keeps its state and reappears instantly. -->\n <div class=\"main-region\" [class.region-hidden]=\"ShowRecordsRegion\">\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 </div>\n\n <!-- RECORDS region: a SEPARATE Golden Layout hosting only record tabs\n (records-style record opens). Always in the DOM once created so the\n layout survives visibility flips; shown only while the active tab is\n a record. Native GL tab bar = record tabs, drag-to-split included. -->\n @if (RecordsStyleActive) {\n <!-- Origin crumbs are PANE-LEVEL now (first element inside each record\n pane \u2014 see RecordOriginCrumbComponent); the region needs no bar. -->\n <div class=\"records-region\" [class.region-hidden]=\"!ShowRecordsRegion\">\n <!-- MOBILE: the GL tab strip is unusable at phone widths (one tab\n fits, no overflow UI) \u2014 the record bar replaces it and opens the\n record switcher sheet. The GL itself runs headerless (HideHeaders). -->\n @if (IsMobileRecords) {\n <mj-record-bar\n [Title]=\"RecordBarTitle\"\n [Icon]=\"RecordBarIcon\"\n [Color]=\"RecordBarColor\"\n [Count]=\"OpenRecordCount\"\n (SwitcherRequested)=\"OpenRecordSwitcher()\">\n </mj-record-bar>\n }\n <div #recordsGlContainer class=\"records-gl-container\"></div>\n </div>\n\n <!-- MOBILE record switcher: bottom sheet listing ALL open records\n (region + docked). Fixed-position \u2014 DOM location here is\n visual-neutral; living in tab-container keeps close routing on the\n same path as the tab context menu. -->\n <mj-record-switcher-sheet\n [Entries]=\"SwitcherEntries\"\n [Visible]=\"RecordSwitcherVisible\"\n (VisibleChange)=\"OnSwitcherVisibleChange($event)\"\n (ActivateRequested)=\"OnSwitcherActivate($event)\"\n (CloseRequested)=\"OnSwitcherClose($event)\">\n </mj-record-switcher-sheet>\n }\n\n <!-- Context Menu (only relevant in multi-tab mode). Real buttons with\n menu semantics: arrow keys rove, Escape closes with focus return to\n the invoking control (see onMenuKeydown / hideContextMenu). -->\n @if (contextMenuVisible) {\n <div\n class=\"context-menu\"\n role=\"menu\"\n aria-label=\"Tab actions\"\n (keydown)=\"onMenuKeydown($event)\"\n [style.left.px]=\"contextMenuX\"\n [style.top.px]=\"contextMenuY\">\n <button type=\"button\" role=\"menuitem\" class=\"context-menu-item\" (click)=\"onContextPin()\">\n <i class=\"fa-solid fa-thumbtack\" aria-hidden=\"true\"></i>\n <span>{{ isContextTabPinned ? 'Unpin Tab' : 'Pin Tab' }}</span>\n </button>\n <div class=\"context-menu-divider\"></div>\n <button type=\"button\" role=\"menuitem\" class=\"context-menu-item\" [class.new-item]=\"!isContextTabPinnedToHome\" [class.disabled]=\"isContextTabPinnedToHome\" [disabled]=\"isContextTabPinnedToHome\" (click)=\"onContextPinToHome()\">\n <i class=\"fa-solid fa-house-chimney\" aria-hidden=\"true\"></i>\n @if (isContextTabPinnedToHome) {\n <span>Pinned to Home</span>\n <span class=\"check\"><i class=\"fa-solid fa-check\" aria-hidden=\"true\"></i></span>\n } @else {\n <span>Pin to Home</span>\n }\n </button>\n @if (canContextMoveToWorkspace || canContextMoveToRecords) {\n <div class=\"context-menu-divider\"></div>\n @if (canContextMoveToWorkspace) {\n <button type=\"button\" role=\"menuitem\" class=\"context-menu-item\" (click)=\"onContextMoveToWorkspace()\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\" aria-hidden=\"true\"></i>\n <span>Move to Workspace</span>\n </button>\n }\n @if (canContextMoveToRecords) {\n <button type=\"button\" role=\"menuitem\" class=\"context-menu-item\" (click)=\"onContextMoveToRecords()\">\n <i class=\"fa-solid fa-file-lines\" aria-hidden=\"true\"></i>\n <span>Move to Records</span>\n </button>\n }\n }\n <div class=\"context-menu-divider\"></div>\n <button type=\"button\" role=\"menuitem\" class=\"context-menu-item\" (click)=\"onContextClose()\">\n <i class=\"fa-solid fa-xmark\" aria-hidden=\"true\"></i>\n <span>Close Tab</span>\n </button>\n <button type=\"button\" role=\"menuitem\" class=\"context-menu-item\" (click)=\"onContextCloseOthers()\">\n <i class=\"fa-solid fa-layer-group\" aria-hidden=\"true\"></i>\n <span>Close Others</span>\n </button>\n <button type=\"button\" role=\"menuitem\" class=\"context-menu-item\" (click)=\"onContextCloseToRight()\">\n <i class=\"fa-solid fa-angles-right\" aria-hidden=\"true\"></i>\n <span>Close to Right</span>\n </button>\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/* Main region (nav pages) and the separate RECORDS region \u2014 exactly one is\n visible at a time. CRITICAL: the hidden region keeps its FULL LAYOUT BOX\n (visibility, not display:none). Golden Layout instances live in both;\n display:none would feed GL 0-size resizes and let it initialize/restore\n against a zero baseline \u2014 the root cause of clipped tab headers, stale\n strips, and the collapsed-sliver layout. */\n.tab-container {\n position: relative;\n}\n\n.main-region,\n.records-region {\n position: absolute;\n inset: 0;\n min-height: 0;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-page);\n}\n\n/* The records GL host fills the region below the origin bar. Must keep a\n real layout box at all times (see the region comment above) \u2014 it's a flex\n child of an always-sized absolute region, so it does. */\n.records-gl-container {\n flex: 1;\n min-height: 0;\n position: relative;\n background: var(--mj-bg-page);\n}\n\n/* Pane layout with an origin crumb as the FIRST element: the crumb keeps\n its fixed 32px (component host); the content sibling flex-fills. The\n height override neutralizes inline height:100% that cached wrapper\n elements carry from earlier renders (CSS !important beats inline). */\nmj-tab-container .lm_items > div > mj-record-origin-crumb,\n.direct-content-container > mj-record-origin-crumb {\n flex-shrink: 0;\n}\n\nmj-tab-container .lm_items > div > *:not(mj-record-origin-crumb),\n.direct-content-container > *:not(mj-record-origin-crumb) {\n flex: 1 1 auto;\n min-height: 0;\n height: auto !important;\n}\n\n/* Defense against visibility punch-through: `visibility: visible` on a\n DESCENDANT defeats an ancestor's visibility:hidden (unlike display:none).\n Real case: explorer-app's global `.visible { visibility: visible\n !important }` utility collides with components whose show-state class is\n named `visible` (several bespoke detail panels did this), leaving their\n overlays painted on top of the records region. The two-class specificity\n here outranks the single-class utility even with both !important. */\n.region-hidden,\n.region-hidden * {\n visibility: hidden !important;\n pointer-events: none !important;\n /* Region swaps must be INSTANT: `transition: all` rules on GL internals\n (e.g. .lm_stack) would otherwise transition `visibility` \u2014 which\n interpolates discretely, keeping the outgoing region painted on top for\n the full transition duration (a ~200ms flash of the record form over\n the destination page). */\n transition: none !important;\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 /* Real <button role=\"menuitem\"> now \u2014 neutralize UA chrome */\n width: 100%;\n text-align: left;\n background: transparent;\n border: none;\n font-family: inherit;\n}\n\n/* Keyboard roving highlights like hover; ring omitted on purpose \u2014 the\n moving highlight IS the focus indicator inside a menu. */\n.context-menu .context-menu-item:focus-visible {\n outline: none;\n background: var(--mj-bg-surface-hover);\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/* SPLIT panes get a subtle frame so side-by-side content doesn't bleed\n together (single-pane layouts stay frameless). \"Adjacent to a splitter\"\n is exactly and only the split case \u2014 any orientation, any nesting. */\nmj-tab-container .lm_stack:has(+ .lm_splitter),\nmj-tab-container .lm_splitter + .lm_stack {\n border: 1px solid var(--mj-border-default) !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\n/* SCROLLING TAB STRIP (browser-tab pattern, Matt's call over GL's overflow\n dropdown): tabs keep natural width and the strip scrolls horizontally.\n Works with settingsConfig()'s effectively-infinite tabOverlapAllowance \u2014\n GL then never re-homes tabs to its dropdown, only writes inline zIndex +\n negative marginLeft, both neutralized here. Scrollbar hidden per the\n browser-tab convention; wheel input is translated to horizontal scroll by\n GoldenLayoutManager.onStripWheel, and activation scrolls the tab into view. */\nmj-tab-container .lm_tabs {\n height: 36px !important;\n left: 0;\n right: 0;\n overflow-x: auto;\n overflow-y: hidden;\n scrollbar-width: none;\n}\n\nmj-tab-container .lm_tabs::-webkit-scrollbar {\n display: none;\n}\n\n/* Strip nudge arrows \u2014 the VISIBLE overflow affordance (created by\n GoldenLayoutManager.updateStripNudges, one pair per header, [hidden]\n unless that direction has more tabs). The gradient doubles as the\n \"content continues\" edge fade. */\nmj-tab-container .mj-strip-nudge {\n position: absolute;\n top: 0;\n height: 36px;\n width: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n border: none;\n padding: 0;\n margin: 0;\n z-index: 3; /* above tabs (z 1/2) within the header context */\n font-size: 11px;\n color: var(--mj-text-secondary);\n cursor: pointer;\n}\n\nmj-tab-container .mj-strip-nudge[hidden] {\n display: none;\n}\n\nmj-tab-container .mj-strip-nudge--left {\n left: 0;\n background: linear-gradient(to right, var(--mj-bg-surface-sunken) 55%, transparent);\n justify-content: flex-start;\n padding-left: 6px;\n}\n\nmj-tab-container .mj-strip-nudge--right {\n right: 0;\n background: linear-gradient(to left, var(--mj-bg-surface-sunken) 55%, transparent);\n justify-content: flex-end;\n padding-right: 6px;\n}\n\nmj-tab-container .mj-strip-nudge:hover {\n color: var(--mj-text-primary);\n}\n\nmj-tab-container .mj-strip-nudge:focus-visible {\n outline: 2px solid var(--mj-border-focus);\n outline-offset: -2px;\n}\n\n/* Hide Golden Layout WINDOW controls (popout/maximise/stack-close). The\n scrolling strip replaces GL's lm_tabdropdown overflow menu entirely (it\n never activates with the infinite overlap allowance). */\nmj-tab-container .lm_controls .lm_popout,\nmj-tab-container .lm_controls .lm_maximise,\nmj-tab-container .lm_controls .lm_close {\n display: none !important;\n}\n\nmj-tab-container .lm_header .lm_tab {\n /* Flex centering: the type-icon slot (and any future adornment) must sit\n on the tab's vertical center \u2014 inline baseline alignment inside the\n 35px line-height text line pushed it ~15px low. */\n /* FULL FLEX FLOW (Matt's refactor): [type icon] [title] [pin] [close] are\n ordinary flex siblings \u2014 no absolute anchoring, no padding reservations,\n no vertical transforms. gap owns horizontal rhythm; align-items owns\n vertical centering for every child. */\n display: flex !important;\n align-items: center !important;\n gap: 5px !important;\n padding: 0 9px !important;\n font-size: 13px !important;\n height: 35px !important;\n /* Scrolling strip: tabs are flex items of .lm_tabs \u2014 never let them\n shrink to \"fit\" (that's what the strip's scroll is for), and zero out\n the negative overlap margins GL writes inline when tabs overflow. */\n flex: 0 0 auto !important;\n margin-left: 0 !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/* Pipe separators between INACTIVE tabs (browser-tab pattern): a short\n centered divider on each tab's right edge establishes separation without\n boxing every tab. Suppressed around the ACTIVE tab (its raised card has\n real edges), around the hovered tab, and after the last tab. */\nmj-tab-container .lm_header .lm_tab::after {\n content: '' !important;\n position: absolute !important;\n right: -1px !important;\n top: 50% !important;\n transform: translateY(-50%) !important;\n width: 1px !important;\n height: 16px !important;\n background: var(--mj-border-strong) !important;\n}\n\nmj-tab-container .lm_header .lm_tab.lm_active::after,\nmj-tab-container .lm_header .lm_tab:has(+ .lm_tab.lm_active)::after,\nmj-tab-container .lm_header .lm_tab:hover::after,\nmj-tab-container .lm_header .lm_tab:has(+ .lm_tab:hover)::after,\nmj-tab-container .lm_header .lm_tab:last-child::after {\n content: none !important;\n}\n\n/* Type-icon slot (Matt's tab design, 2026-07-29): every tab leads with its\n TYPE icon tinted in the app color \u2014 entity icon for records, nav-item/app\n icon for pages. The old 3px app-color left-edge bar is GONE; the icon\n tint carries app identity now. Hovering the TAB swaps the icon to an\n ellipsis: the slot is the visible \"this tab has actions\" affordance \u2014\n clicking it opens the tab-actions menu (same menu as right-click). */\nmj-tab-container .lm_header .lm_tab .mj-tab-type-slot {\n display: inline-flex !important;\n align-items: center !important;\n justify-content: center !important;\n /* 24px = WCAG 2.2 AA minimum pointer target (SC 2.5.8); glyph stays 12px */\n width: 24px !important;\n height: 24px !important;\n border-radius: 5px !important;\n flex-shrink: 0 !important;\n cursor: pointer !important;\n /* The slot is a real <button> now \u2014 neutralize UA button chrome */\n background: transparent !important;\n border: none !important;\n padding: 0 !important;\n font: inherit !important;\n color: inherit !important;\n}\n\n/* Keyboard parity with hover: focused slot shows the dots + house focus\n ring (outline idiom \u2014 GL headers clip box-shadows). */\nmj-tab-container .lm_header .lm_tab .mj-tab-type-slot:focus-visible {\n outline: 2px solid var(--mj-border-focus) !important;\n outline-offset: -2px !important;\n}\nmj-tab-container .lm_header .lm_tab .mj-tab-type-slot:focus-visible .mj-tab-type-ico {\n display: none !important;\n}\nmj-tab-container .lm_header .lm_tab .mj-tab-type-slot:focus-visible .mj-tab-dots {\n display: inline-block !important;\n}\n/* GL's base stylesheet styles EVERY `i` inside a tab as its legacy 2x19px\n absolutely-positioned corner decoration (.lm_header .lm_tab i) \u2014 which\n turns our icon elements into slivers the glyph overflows. Restore normal\n flow for the slot's icons. */\nmj-tab-container .lm_header .lm_tab .mj-tab-type-slot i {\n position: static !important;\n width: auto !important;\n height: auto !important;\n}\n\nmj-tab-container .lm_header .lm_tab .mj-tab-type-ico {\n color: var(--app-color, var(--mj-text-muted)) !important;\n font-size: 12px !important;\n line-height: 1 !important;\n}\nmj-tab-container .lm_header .lm_tab .mj-tab-dots {\n display: none !important;\n color: var(--mj-text-secondary) !important;\n font-size: 12px !important;\n line-height: 1 !important;\n}\nmj-tab-container .lm_header .lm_tab:hover .mj-tab-type-ico {\n display: none !important;\n}\nmj-tab-container .lm_header .lm_tab:hover .mj-tab-dots {\n display: inline-block !important;\n}\nmj-tab-container .lm_header .lm_tab .mj-tab-type-slot:hover {\n background: var(--mj-bg-surface-hover) !important;\n}\nmj-tab-container .lm_header .lm_tab .mj-tab-type-slot:hover .mj-tab-dots {\n color: var(--mj-text-primary) !important;\n}\n\nmj-tab-container .lm_header .lm_tab:hover {\n background: var(--mj-bg-surface-hover) !important;\n}\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\nmj-tab-container .lm_title {\n cursor: pointer !important;\n user-select: none !important;\n /* In-flow flex child: normalize the inherited 35px line-height so the\n text box is glyph-height and centers identically to its siblings;\n truncate instead of colliding when GL constrains tab width. */\n line-height: normal !important;\n flex: 0 1 auto !important;\n min-width: 0 !important;\n overflow: hidden !important;\n text-overflow: ellipsis !important;\n white-space: nowrap !important;\n /* Glyph-overhang room: italics (unpinned tabs) paint past the last\n glyph's advance width, and overflow:hidden clips at the padding edge \u2014\n without this the final letter loses its slant. */\n padding-right: 3px !important;\n}\n\nmj-tab-container .lm_close_tab {\n position: static !important;\n order: 10 !important;\n transform: none !important;\n margin: 0 !important;\n /* 24px WCAG target; glyph stays 10px */\n width: 24px !important;\n height: 24px !important;\n border-radius: 5px !important;\n cursor: pointer !important;\n /* ALWAYS visible (Matt's call): quiet at rest, full on its own hover/focus.\n Discoverability shouldn't depend on hovering. */\n opacity: 0.55 !important;\n transition: opacity 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:focus-visible {\n outline: 2px solid var(--mj-border-focus) !important;\n outline-offset: -2px !important;\n opacity: 1 !important;\n}\n\nmj-tab-container .lm_close_tab:hover {\n opacity: 1 !important;\n color: var(--mj-status-error) !important;\n /* background-COLOR, never the shorthand: GL's stock close was a\n background-image PNG and the shorthand silently wiped it (now it's an\n FA ::before glyph, but the discipline stands). */\n background-color: var(--mj-bg-surface-hover) !important;\n}\n\n/* Pinned tabs never show close button (the pin control replaces it) */\nmj-tab-container .lm_header .lm_tab.pinned .lm_close_tab {\n display: none !important;\n}\n\n\n"] }]
2684
- }], () => [{ type: i1.GoldenLayoutManager }, { type: i1.WorkspaceStateManager }, { type: i1.ApplicationManager }, { type: i0.ApplicationRef }, { type: i0.EnvironmentInjector }, { type: i0.ChangeDetectorRef }], { glContainer: [{
2880
+ }], () => [{ type: i1.GoldenLayoutManager }, { type: i1.WorkspaceStateManager }, { type: i1.ApplicationManager }, { type: i0.ApplicationRef }, { type: i0.EnvironmentInjector }, { type: i0.ChangeDetectorRef }], { GlContainer: [{
2685
2881
  type: ViewChild,
2686
2882
  args: ['glContainer', { static: false }]
2687
- }], directContentContainer: [{
2883
+ }], DirectContentContainer: [{
2688
2884
  type: ViewChild,
2689
2885
  args: ['directContentContainer', { static: false }]
2690
- }], recordsGlContainer: [{
2886
+ }], RecordsGlContainer: [{
2691
2887
  type: ViewChild,
2692
2888
  args: ['recordsGlContainer', { static: false }]
2889
+ }], FirstResourceLoadComplete: [{
2890
+ type: Output
2693
2891
  }], firstResourceLoadComplete: [{
2694
2892
  type: Output
2893
+ }], LayoutInitError: [{
2894
+ type: Output
2695
2895
  }], layoutInitError: [{
2696
2896
  type: Output
2697
2897
  }], onWindowResize: [{