@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
@@ -322,18 +322,58 @@ export class AppSwitcherComponent extends BaseAngularComponent {
322
322
  appManager;
323
323
  cdr;
324
324
  destroy$ = new Subject();
325
- activeApp = null;
326
- isViewingSystemTab = false;
325
+ ActiveApp = null;
326
+ /** @deprecated Use {@link ActiveApp}. */
327
+ set activeApp(value) {
328
+ this.ActiveApp = value;
329
+ }
330
+ /** @deprecated Use {@link ActiveApp}. */
331
+ get activeApp() {
332
+ return this.ActiveApp;
333
+ }
334
+ IsViewingSystemTab = false;
335
+ /** @deprecated Use {@link IsViewingSystemTab}. */
336
+ set isViewingSystemTab(value) {
337
+ this.IsViewingSystemTab = value;
338
+ }
339
+ /** @deprecated Use {@link IsViewingSystemTab}. */
340
+ get isViewingSystemTab() {
341
+ return this.IsViewingSystemTab;
342
+ }
327
343
  /** ID of the app currently being loaded (shows loading indicator) */
328
- loadingAppId = null;
344
+ LoadingAppId = null;
345
+ /** @deprecated Use {@link LoadingAppId}. */
346
+ set loadingAppId(value) {
347
+ this.LoadingAppId = value;
348
+ }
349
+ /** @deprecated Use {@link LoadingAppId}. */
350
+ get loadingAppId() {
351
+ return this.LoadingAppId;
352
+ }
329
353
  /** Presentation style (from Shell.AppSwitcher.Style instance config) */
330
354
  SwitcherStyle = 'auto';
331
- appSelected = new EventEmitter();
355
+ AppSelected = new EventEmitter();
356
+ /**
357
+ * @deprecated Use {@link AppSelected}.
358
+ *
359
+ * The same emitter under the old binding name, so a template still binding
360
+ * (appSelected) keeps working. Must stay AFTER AppSelected: class fields
361
+ * initialise in order, and the other way round this captures undefined.
362
+ */
363
+ appSelected = this.AppSelected;
332
364
  filterInputRef;
333
365
  triggerRef;
334
366
  panelRef;
335
367
  configContent;
336
- showDropdown = false;
368
+ ShowDropdown = false;
369
+ /** @deprecated Use {@link ShowDropdown}. */
370
+ get showDropdown() {
371
+ return this.ShowDropdown;
372
+ }
373
+ /** @deprecated Use {@link ShowDropdown}. */
374
+ set showDropdown(value) {
375
+ this.ShowDropdown = value;
376
+ }
337
377
  /** When true, the launcher body shows the app-configuration view instead of the card grid */
338
378
  ConfigMode = false;
339
379
  /** Pending action awaiting the inline unsaved-changes discard bar ('exit' = back to grid, 'close' = close launcher) */
@@ -375,7 +415,7 @@ export class AppSwitcherComponent extends BaseAngularComponent {
375
415
  // the same stream the app list uses so the presentation can't go stale.
376
416
  // Optional-chained: lightweight test stubs may not provide the observable.
377
417
  this.appManager.Applications?.pipe(takeUntil(this.destroy$)).subscribe(() => {
378
- if (this.showDropdown) {
418
+ if (this.ShowDropdown) {
379
419
  this.RefreshPresentation();
380
420
  }
381
421
  });
@@ -433,35 +473,39 @@ export class AppSwitcherComponent extends BaseAngularComponent {
433
473
  * Check if the app switcher should show loading state
434
474
  */
435
475
  get isLoading() {
436
- return this.loadingAppId !== null;
476
+ return this.LoadingAppId !== null;
437
477
  }
438
478
  /**
439
479
  * Visible trigger label: the current app's name, or "Apps" when there's no
440
480
  * app context (system tabs, nothing active yet).
441
481
  */
442
482
  get TriggerLabel() {
443
- return this.activeApp && !this.isViewingSystemTab ? this.activeApp.Name : 'Apps';
483
+ return this.ActiveApp && !this.IsViewingSystemTab ? this.ActiveApp.Name : 'Apps';
444
484
  }
445
485
  /** Accessible name for the trigger — names the control AND the current app */
446
486
  get TriggerAriaLabel() {
447
- return this.activeApp && !this.isViewingSystemTab
448
- ? `Switch application — current: ${this.activeApp.Name}`
487
+ return this.ActiveApp && !this.IsViewingSystemTab
488
+ ? `Switch application — current: ${this.ActiveApp.Name}`
449
489
  : 'Switch application';
450
490
  }
451
491
  /**
452
492
  * Get applications that should appear in the app switcher
453
493
  * (NavigationStyle = 'App Switcher' or 'Both')
454
494
  */
455
- get apps() {
495
+ get Apps() {
456
496
  return this.appManager.GetAppSwitcherApps();
457
497
  }
498
+ /** @deprecated Use {@link Apps}. */
499
+ get apps() {
500
+ return this.Apps;
501
+ }
458
502
  /** Recently-used apps (persisted per user), newest first, capped for display.
459
503
  * Compact mode skips the Recent section — with a handful of apps it's noise. */
460
504
  get RecentApps() {
461
505
  if (this.CompactMode) {
462
506
  return [];
463
507
  }
464
- const byId = new Map(this.apps.map(a => [NormalizeUUID(a.ID), a]));
508
+ const byId = new Map(this.Apps.map(a => [NormalizeUUID(a.ID), a]));
465
509
  const result = [];
466
510
  for (const entry of this.recentEntries) {
467
511
  const app = byId.get(NormalizeUUID(entry.id));
@@ -477,8 +521,8 @@ export class AppSwitcherComponent extends BaseAngularComponent {
477
521
  /** All switcher apps in the user's chosen sort (custom Sequence order or A–Z) */
478
522
  get sortedApps() {
479
523
  return this.SortMode === 'alpha'
480
- ? [...this.apps].sort((a, b) => a.Name.localeCompare(b.Name))
481
- : this.apps;
524
+ ? [...this.Apps].sort((a, b) => a.Name.localeCompare(b.Name))
525
+ : this.Apps;
482
526
  }
483
527
  /** The complete "All apps" section, in the user's chosen sort. Apps shown in
484
528
  * Recent appear here TOO — being recently used must not make an app vanish
@@ -499,7 +543,7 @@ export class AppSwitcherComponent extends BaseAngularComponent {
499
543
  /** Distinct app count for the screen-reader status (VisibleApps can contain
500
544
  * the same app twice when it's in both Recent and All apps) */
501
545
  get AnnouncedAppCount() {
502
- return this.IsFiltering ? this.VisibleApps.length : this.applyFilter(this.apps).length;
546
+ return this.IsFiltering ? this.VisibleApps.length : this.applyFilter(this.Apps).length;
503
547
  }
504
548
  /** True when a filter is active (sections collapse into one result list) */
505
549
  get IsFiltering() {
@@ -507,7 +551,7 @@ export class AppSwitcherComponent extends BaseAngularComponent {
507
551
  }
508
552
  /** Compact mode hides the filter under a handful of apps — it's noise there */
509
553
  get ShowFilter() {
510
- return !this.CompactMode || this.apps.length >= COMPACT_FILTER_THRESHOLD;
554
+ return !this.CompactMode || this.Apps.length >= COMPACT_FILTER_THRESHOLD;
511
555
  }
512
556
  applyFilter(apps) {
513
557
  const q = this.FilterText.trim().toLowerCase();
@@ -522,7 +566,7 @@ export class AppSwitcherComponent extends BaseAngularComponent {
522
566
  }
523
567
  /** Trigger handler: open the launcher panel, or close it if already open */
524
568
  ToggleLauncher() {
525
- if (this.showDropdown) {
569
+ if (this.ShowDropdown) {
526
570
  this.closeLauncher();
527
571
  }
528
572
  else {
@@ -557,7 +601,7 @@ export class AppSwitcherComponent extends BaseAngularComponent {
557
601
  if (this.CompactMode) {
558
602
  this.computeCompactAnchor();
559
603
  }
560
- this.showDropdown = true;
604
+ this.ShowDropdown = true;
561
605
  // The dialog element renders under @if next frame: put it in the top
562
606
  // layer via showModal() (jsdom fallback: plain open attribute), then
563
607
  // focus the filter (or the first card when compact mode hides it).
@@ -587,7 +631,7 @@ export class AppSwitcherComponent extends BaseAngularComponent {
587
631
  if (this.SwitcherStyle === 'launcher') {
588
632
  return false;
589
633
  }
590
- return this.apps.length < COMPACT_AUTO_THRESHOLD;
634
+ return this.Apps.length < COMPACT_AUTO_THRESHOLD;
591
635
  }
592
636
  /** Anchor the compact panel under the trigger, clamped to the viewport */
593
637
  computeCompactAnchor() {
@@ -605,7 +649,7 @@ export class AppSwitcherComponent extends BaseAngularComponent {
605
649
  if (dialog?.open && typeof dialog.close === 'function') {
606
650
  dialog.close(); // Browser restores focus to the trigger automatically
607
651
  }
608
- this.showDropdown = false;
652
+ this.ShowDropdown = false;
609
653
  this.FilterText = '';
610
654
  this.ConfigMode = false;
611
655
  this.PendingDiscard = null;
@@ -669,10 +713,10 @@ export class AppSwitcherComponent extends BaseAngularComponent {
669
713
  // the footer advertises "↵ open"); unfiltered it needs an unambiguous
670
714
  // single app.
671
715
  if (this.IsFiltering && visible.length > 0) {
672
- this.selectApp(visible[0]);
716
+ this.SelectApp(visible[0]);
673
717
  }
674
718
  else if (visible.length === 1) {
675
- this.selectApp(visible[0]);
719
+ this.SelectApp(visible[0]);
676
720
  }
677
721
  break;
678
722
  case 'Escape':
@@ -758,20 +802,24 @@ export class AppSwitcherComponent extends BaseAngularComponent {
758
802
  * Select an application.
759
803
  * When viewing a system tab, always emit to allow returning to the app.
760
804
  */
761
- selectApp(app) {
805
+ SelectApp(app) {
762
806
  this.recordRecent(app);
763
807
  this.closeLauncher(false); // Focus moves into the app, not back to the trigger
764
- if (!UUIDsEqual(app.ID, this.activeApp?.ID) || this.isViewingSystemTab) {
765
- this.appSelected.emit(app.ID);
808
+ if (!UUIDsEqual(app.ID, this.ActiveApp?.ID) || this.IsViewingSystemTab) {
809
+ this.AppSelected.emit(app.ID);
766
810
  }
767
811
  }
812
+ /** @deprecated Use {@link SelectApp}. */
813
+ selectApp(app) {
814
+ return this.SelectApp(app);
815
+ }
768
816
  /** Case-insensitive UUID check whether an app is the currently active app. */
769
817
  IsActiveApp(app) {
770
- return UUIDsEqual(app.ID, this.activeApp?.ID);
818
+ return UUIDsEqual(app.ID, this.ActiveApp?.ID);
771
819
  }
772
820
  /** Case-insensitive UUID check whether an app is the one currently loading. */
773
821
  IsLoadingApp(app) {
774
- return UUIDsEqual(app.ID, this.loadingAppId);
822
+ return UUIDsEqual(app.ID, this.LoadingAppId);
775
823
  }
776
824
  // ---- Recents persistence (MJ: User Settings via UserInfoEngine) ----
777
825
  loadRecents() {
@@ -856,7 +904,7 @@ export class AppSwitcherComponent extends BaseAngularComponent {
856
904
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.triggerRef = _t.first);
857
905
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.panelRef = _t.first);
858
906
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.configContent = _t.first);
859
- } }, inputs: { activeApp: "activeApp", isViewingSystemTab: "isViewingSystemTab", loadingAppId: "loadingAppId", SwitcherStyle: "SwitcherStyle" }, outputs: { appSelected: "appSelected" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 11, vars: 9, consts: [["trigger", ""], ["appCard", ""], ["panel", ""], ["filterInput", ""], [1, "app-switcher-container"], ["testId", "app-switcher-trigger", 1, "app-switcher-button", 3, "click", "mjClickable"], ["aria-hidden", "true", 1, "app-icon", "loading-spinner", "fa-solid", "fa-spinner", "fa-spin"], ["aria-hidden", "true", 1, "app-icon", 3, "class", "color"], [1, "app-label"], ["aria-hidden", "true", 1, "dropdown-arrow", "fa-solid", "fa-chevron-down"], ["role", "dialog", "aria-modal", "true", 1, "launcher-panel", 3, "launcher-panel--compact", "--anchor-left", "--anchor-top"], ["aria-hidden", "true", 1, "app-icon"], ["role", "dialog", "aria-modal", "true", 1, "launcher-panel", 3, "click", "cancel", "keydown"], [1, "launcher-head", "launcher-head--config"], ["type", "button", "aria-label", "Back to apps", 1, "launcher-back", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-arrow-left"], ["id", "mj-launcher-config-title", 1, "launcher-title"], ["type", "button", "aria-label", "Close application launcher", 1, "launcher-close", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-xmark"], ["role", "alert", 1, "launcher-discard-bar"], [1, "launcher-config-body", 3, "Saved", "Cancelled", "Provider"], ["aria-hidden", "true", 1, "fa-solid", "fa-triangle-exclamation"], [1, "discard-msg"], ["mjButton", "", "variant", "danger", "size", "sm", 3, "click"], ["mjButton", "", "size", "sm", 3, "click"], [1, "launcher-head"], [1, "launcher-scroll"], [1, "launcher-foot"], ["aria-hidden", "true", 1, "launcher-kbd-hints"], ["type", "button", "testId", "app-switcher-configure", 1, "launcher-configure", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-gear"], ["role", "status", 1, "sr-only"], [1, "launcher-filter"], ["aria-hidden", "true", 1, "fa-solid", "fa-magnifying-glass"], ["type", "text", "placeholder", "Find an app\u2026", "aria-label", "Find an app", "autocomplete", "off", "spellcheck", "false", 3, "input", "keydown", "value"], [1, "launcher-grid"], [1, "launcher-empty"], [4, "ngTemplateOutlet", "ngTemplateOutletContext"], [1, "launcher-sect", "launcher-sect--row"], ["type", "button", 1, "launcher-sort-toggle", 3, "click", "title"], ["aria-hidden", "true"], ["role", "group", "aria-label", "All applications", 1, "launcher-grid"], [1, "launcher-sect"], ["role", "group", "aria-label", "Recent applications", 1, "launcher-grid"], ["role", "link", 1, "app-card", 3, "click", "keydown", "id", "mjClickable", "testId"], [1, "app-card-glyph"], ["aria-hidden", "true", 1, "fa-solid", "fa-spinner", "fa-spin"], ["aria-hidden", "true", 3, "class"], [1, "app-card-text"], [1, "app-card-name"], [1, "app-card-name-label"], [1, "app-card-desc", 3, "id"]], template: function AppSwitcherComponent_Template(rf, ctx) { if (rf & 1) {
907
+ } }, inputs: { ActiveApp: "ActiveApp", activeApp: "activeApp", IsViewingSystemTab: "IsViewingSystemTab", isViewingSystemTab: "isViewingSystemTab", LoadingAppId: "LoadingAppId", loadingAppId: "loadingAppId", SwitcherStyle: "SwitcherStyle" }, outputs: { AppSelected: "AppSelected", appSelected: "appSelected" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 11, vars: 9, consts: [["trigger", ""], ["appCard", ""], ["panel", ""], ["filterInput", ""], [1, "app-switcher-container"], ["testId", "app-switcher-trigger", 1, "app-switcher-button", 3, "click", "mjClickable"], ["aria-hidden", "true", 1, "app-icon", "loading-spinner", "fa-solid", "fa-spinner", "fa-spin"], ["aria-hidden", "true", 1, "app-icon", 3, "class", "color"], [1, "app-label"], ["aria-hidden", "true", 1, "dropdown-arrow", "fa-solid", "fa-chevron-down"], ["role", "dialog", "aria-modal", "true", 1, "launcher-panel", 3, "launcher-panel--compact", "--anchor-left", "--anchor-top"], ["aria-hidden", "true", 1, "app-icon"], ["role", "dialog", "aria-modal", "true", 1, "launcher-panel", 3, "click", "cancel", "keydown"], [1, "launcher-head", "launcher-head--config"], ["type", "button", "aria-label", "Back to apps", 1, "launcher-back", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-arrow-left"], ["id", "mj-launcher-config-title", 1, "launcher-title"], ["type", "button", "aria-label", "Close application launcher", 1, "launcher-close", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-xmark"], ["role", "alert", 1, "launcher-discard-bar"], [1, "launcher-config-body", 3, "Saved", "Cancelled", "Provider"], ["aria-hidden", "true", 1, "fa-solid", "fa-triangle-exclamation"], [1, "discard-msg"], ["mjButton", "", "variant", "danger", "size", "sm", 3, "click"], ["mjButton", "", "size", "sm", 3, "click"], [1, "launcher-head"], [1, "launcher-scroll"], [1, "launcher-foot"], ["aria-hidden", "true", 1, "launcher-kbd-hints"], ["type", "button", "testId", "app-switcher-configure", 1, "launcher-configure", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-gear"], ["role", "status", 1, "sr-only"], [1, "launcher-filter"], ["aria-hidden", "true", 1, "fa-solid", "fa-magnifying-glass"], ["type", "text", "placeholder", "Find an app\u2026", "aria-label", "Find an app", "autocomplete", "off", "spellcheck", "false", 3, "input", "keydown", "value"], [1, "launcher-grid"], [1, "launcher-empty"], [4, "ngTemplateOutlet", "ngTemplateOutletContext"], [1, "launcher-sect", "launcher-sect--row"], ["type", "button", 1, "launcher-sort-toggle", 3, "click", "title"], ["aria-hidden", "true"], ["role", "group", "aria-label", "All applications", 1, "launcher-grid"], [1, "launcher-sect"], ["role", "group", "aria-label", "Recent applications", 1, "launcher-grid"], ["role", "link", 1, "app-card", 3, "click", "keydown", "id", "mjClickable", "testId"], [1, "app-card-glyph"], ["aria-hidden", "true", 1, "fa-solid", "fa-spinner", "fa-spin"], ["aria-hidden", "true", 3, "class"], [1, "app-card-text"], [1, "app-card-name"], [1, "app-card-name-label"], [1, "app-card-desc", 3, "id"]], template: function AppSwitcherComponent_Template(rf, ctx) { if (rf & 1) {
860
908
  i0.ɵɵelementStart(0, "div", 4)(1, "div", 5, 0);
861
909
  i0.ɵɵlistener("click", function AppSwitcherComponent_Template_div_click_1_listener() { return ctx.ToggleLauncher(); });
862
910
  i0.ɵɵconditionalCreate(3, AppSwitcherComponent_Conditional_3_Template, 1, 0, "i", 6);
@@ -887,14 +935,22 @@ export class AppSwitcherComponent extends BaseAngularComponent {
887
935
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(AppSwitcherComponent, [{
888
936
  type: Component,
889
937
  args: [{ standalone: false, selector: 'mj-app-switcher', template: "<div class=\"app-switcher-container\" [class.loading]=\"isLoading\">\n <div class=\"app-switcher-button\"\n #trigger\n [mjClickable]=\"TriggerAriaLabel\"\n testId=\"app-switcher-trigger\"\n [attr.aria-haspopup]=\"'dialog'\"\n [attr.aria-expanded]=\"showDropdown\"\n (click)=\"ToggleLauncher()\">\n <!-- Show spinner when loading, otherwise show app icon -->\n @if (isLoading) {\n <i class=\"app-icon loading-spinner fa-solid fa-spinner fa-spin\" aria-hidden=\"true\"></i>\n }\n @if (!isLoading) {\n <i class=\"app-icon\" [class]=\"activeApp?.Icon || 'fa-solid fa-cube'\"\n [style.color]=\"activeApp?.GetColor()\" aria-hidden=\"true\"></i>\n }\n <!-- Current app name; hidden at the mobile breakpoint (icon-only trigger) -->\n <span class=\"app-label\">{{ TriggerLabel }}</span>\n <i class=\"dropdown-arrow fa-solid fa-chevron-down\" aria-hidden=\"true\"></i>\n </div>\n\n @if (showDropdown) {\n <!-- Native <dialog> + showModal(): lives in the browser TOP LAYER (above\n every app stacking context \u2014 chat FAB, toasts), background is inert\n (real focus trap), and focus restores to the trigger on close. The\n backdrop is ::backdrop; clicks on it arrive as clicks on the dialog\n element itself (OnDialogClick). -->\n <dialog class=\"launcher-panel\"\n #panel\n role=\"dialog\"\n aria-modal=\"true\"\n [class.launcher-panel--compact]=\"CompactMode && !ConfigMode\"\n [style.--anchor-left]=\"AnchorLeft + 'px'\"\n [style.--anchor-top]=\"AnchorTop + 'px'\"\n [attr.aria-label]=\"ConfigMode ? null : 'Application launcher'\"\n [attr.aria-labelledby]=\"ConfigMode ? 'mj-launcher-config-title' : null\"\n (click)=\"OnDialogClick($event)\"\n (cancel)=\"OnDialogCancel($event)\"\n (keydown)=\"OnPanelKeydown($event)\">\n\n @if (ConfigMode) {\n <!-- Configuration view: same panel, body swapped -->\n <div class=\"launcher-head launcher-head--config\">\n <button class=\"launcher-back\" type=\"button\" aria-label=\"Back to apps\" (click)=\"RequestExitConfigMode()\">\n <i class=\"fa-solid fa-arrow-left\" aria-hidden=\"true\"></i>\n </button>\n <h2 class=\"launcher-title\" id=\"mj-launcher-config-title\">Configure apps</h2>\n <button class=\"launcher-close\" type=\"button\" aria-label=\"Close application launcher\" (click)=\"CloseLauncher()\">\n <i class=\"fa-solid fa-xmark\" aria-hidden=\"true\"></i>\n </button>\n </div>\n @if (PendingDiscard) {\n <div class=\"launcher-discard-bar\" role=\"alert\">\n <i class=\"fa-solid fa-triangle-exclamation\" aria-hidden=\"true\"></i>\n <span class=\"discard-msg\">You have unsaved changes.</span>\n <button mjButton variant=\"danger\" size=\"sm\" (click)=\"ConfirmDiscard()\">Discard</button>\n <button mjButton size=\"sm\" (click)=\"CancelDiscard()\">Keep editing</button>\n </div>\n }\n <mj-user-app-config-content\n class=\"launcher-config-body\"\n [Provider]=\"Provider\"\n (Saved)=\"ExitConfigMode()\"\n (Cancelled)=\"RequestExitConfigMode()\">\n </mj-user-app-config-content>\n } @else {\n @if (ShowFilter) {\n <div class=\"launcher-head\">\n <div class=\"launcher-filter\">\n <i class=\"fa-solid fa-magnifying-glass\" aria-hidden=\"true\"></i>\n <input\n #filterInput\n type=\"text\"\n placeholder=\"Find an app\u2026\"\n aria-label=\"Find an app\"\n [value]=\"FilterText\"\n (input)=\"OnFilterInput($event)\"\n (keydown)=\"OnFilterKeydown($event)\"\n autocomplete=\"off\"\n spellcheck=\"false\" />\n </div>\n <button class=\"launcher-close\" type=\"button\" aria-label=\"Close application launcher\" (click)=\"CloseLauncher()\">\n <i class=\"fa-solid fa-xmark\" aria-hidden=\"true\"></i>\n </button>\n </div>\n }\n\n <div class=\"launcher-scroll\">\n @if (IsFiltering) {\n <div class=\"launcher-grid\">\n @for (app of VisibleApps; track app.ID; let i = $index) {\n <ng-container *ngTemplateOutlet=\"appCard; context: { $implicit: app, idx: i }\"></ng-container>\n }\n </div>\n @if (VisibleApps.length === 0) {\n <div class=\"launcher-empty\">No apps match \"{{ FilterText }}\"</div>\n }\n } @else {\n @if (RecentApps.length > 0) {\n <div class=\"launcher-sect\">Recent</div>\n <div class=\"launcher-grid\" role=\"group\" aria-label=\"Recent applications\">\n @for (app of RecentApps; track app.ID; let i = $index) {\n <ng-container *ngTemplateOutlet=\"appCard; context: { $implicit: app, idx: i }\"></ng-container>\n }\n </div>\n }\n <div class=\"launcher-sect launcher-sect--row\">\n <span>All apps</span>\n <button class=\"launcher-sort-toggle\" type=\"button\"\n [attr.aria-pressed]=\"SortMode === 'alpha'\"\n [attr.aria-label]=\"SortMode === 'alpha' ? 'Sorted alphabetically \u2014 switch to your custom order' : 'Sorted by your custom order \u2014 switch to alphabetical'\"\n [title]=\"SortMode === 'alpha' ? 'Sorted A\u2013Z \u2014 click for your custom order' : 'Custom order \u2014 click for A\u2013Z'\"\n (click)=\"ToggleSortMode()\">\n <i [class]=\"SortMode === 'alpha' ? 'fa-solid fa-arrow-down-a-z' : 'fa-solid fa-bars-staggered'\" aria-hidden=\"true\"></i>\n </button>\n </div>\n <div class=\"launcher-grid\" role=\"group\" aria-label=\"All applications\">\n @for (app of OtherApps; track app.ID; let i = $index) {\n <ng-container *ngTemplateOutlet=\"appCard; context: { $implicit: app, idx: i + RecentApps.length }\"></ng-container>\n }\n </div>\n @if (VisibleApps.length === 0) {\n <div class=\"launcher-empty\">\n No applications are enabled for you yet \u2014 use Configure\u2026 below to add some.\n </div>\n }\n }\n </div>\n\n <div class=\"launcher-foot\">\n <span class=\"launcher-kbd-hints\" aria-hidden=\"true\">\n <span><kbd>\u2191</kbd><kbd>\u2193</kbd> navigate</span>\n <span><kbd>\u21B5</kbd> open</span>\n <span><kbd>esc</kbd> close</span>\n </span>\n <button class=\"launcher-configure\" type=\"button\" testId=\"app-switcher-configure\" (click)=\"EnterConfigMode()\">\n <i class=\"fa-solid fa-gear\" aria-hidden=\"true\"></i>\n <span>Configure\u2026</span>\n </button>\n </div>\n\n <span class=\"sr-only\" role=\"status\">{{ AnnouncedAppCount }} applications available</span>\n }\n </dialog>\n }\n</div>\n\n<!-- Shared app-card template: each card is a real focusable control (mjClickable\n adds role, tabindex 0, aria-label, and Enter/Space activation), so Tab walks\n the cards and arrow keys are the fast path. -->\n<ng-template #appCard let-app let-idx=\"idx\">\n <div class=\"app-card\"\n [id]=\"OptionId(idx)\"\n [mjClickable]=\"app.Name\"\n role=\"link\"\n [testId]=\"'app-switcher-item-' + app.Name\"\n [attr.aria-current]=\"IsActiveApp(app) ? 'true' : null\"\n [attr.aria-describedby]=\"app.Description ? OptionId(idx) + '-desc' : null\"\n [class.active]=\"IsActiveApp(app)\"\n [class.loading]=\"IsLoadingApp(app)\"\n [style.--app-color]=\"app.GetColor()\"\n (click)=\"selectApp(app)\"\n (keydown)=\"OnCardKeydown(idx, $event)\">\n <div class=\"app-card-glyph\">\n @if (IsLoadingApp(app)) {\n <i class=\"fa-solid fa-spinner fa-spin\" aria-hidden=\"true\"></i>\n } @else {\n <i [class]=\"app.Icon || 'fa-solid fa-cube'\" aria-hidden=\"true\"></i>\n }\n </div>\n <div class=\"app-card-text\">\n <div class=\"app-card-name\">\n <span class=\"app-card-name-label\">{{ app.Name }}</span>\n </div>\n @if (app.Description) {\n <div class=\"app-card-desc\" [id]=\"OptionId(idx) + '-desc'\">{{ app.Description }}</div>\n }\n </div>\n </div>\n</ng-template>\n\n", styles: [".app-switcher-container {\n position: relative;\n margin-left: calc(-1 * var(--mj-space-0-5)); /* Fine-tune alignment when no nav-bar icons to the left */\n}\n\n/* ---- Trigger button (header) ---- */\n.app-switcher-button {\n display: flex;\n align-items: center;\n gap: var(--mj-space-1-5);\n padding: var(--mj-space-2) var(--mj-space-2-5);\n border-radius: var(--mj-radius-md);\n cursor: pointer;\n transition: background var(--mj-transition-fast);\n user-select: none;\n position: relative;\n}\n.app-switcher-button .app-icon {\n font-size: var(--mj-text-xl);\n width: 24px;\n text-align: center;\n position: relative;\n}\n.app-switcher-button:hover {\n background: var(--mj-bg-surface-hover);\n}\n.app-switcher-button:focus-visible {\n outline: 2px solid var(--mj-border-focus);\n outline-offset: 2px;\n}\n/* Current-app name in the trigger. Capped at 160px: no current catalog name\n truncates at this width \u2014 the ellipsis only guards future/client apps. */\n.app-switcher-button .app-label {\n font-size: var(--mj-text-sm);\n font-weight: var(--mj-font-semibold);\n color: var(--mj-text-primary);\n max-width: 160px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.app-switcher-button .dropdown-arrow {\n font-size: 10px;\n color: var(--mj-text-muted);\n transition: color var(--mj-transition-fast);\n}\n/* text-disabled sat nearly on top of --mj-bg-surface-hover in dark mode \u2014\n the chevron vanished on hover. Muted at rest, secondary on hover. */\n.app-switcher-button:hover .dropdown-arrow {\n color: var(--mj-text-secondary);\n}\n.app-switcher-button .loading-spinner {\n color: var(--mj-text-muted);\n}\n\n/* ---- Launcher overlay ----\n The panel is a native <dialog> opened with showModal(): it renders in the\n browser TOP LAYER (above every app stacking context, incl. the header's\n z-index:500 context, the chat FAB, and toasts), the page behind it is\n inert, and the whole-screen scrim is the dialog's ::backdrop. */\n.launcher-panel {\n /* Reset dialog UA defaults, then apply the command-surface spec */\n padding: 0;\n margin: 0;\n inset: unset;\n max-width: none;\n position: fixed;\n top: calc(60px + var(--mj-connectivity-banner-height, 0px) + var(--mj-space-6));\n left: 50%;\n transform: translateX(-50%);\n width: min(880px, calc(100vw - var(--mj-space-12)));\n max-height: calc(100vh - 60px - var(--mj-connectivity-banner-height, 0px) - var(--mj-space-12));\n background: var(--mj-bg-surface-elevated);\n color: inherit;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg);\n box-shadow: var(--mj-shadow-xl);\n flex-direction: column;\n overflow: hidden;\n animation: launcher-pop .13s ease-out;\n}\n.launcher-panel[open] {\n display: flex;\n}\n.launcher-panel::backdrop {\n background: var(--mj-bg-overlay);\n animation: launcher-fade-in var(--mj-transition-fast);\n}\n\n/* ---- Compact mode (few apps / Shell.AppSwitcher.Style) ----\n Anchored under the trigger like a dropdown: no dim, single-column rows,\n no recents/filter. Entering the Configure view drops this class, promoting\n the panel back to the centered command surface (the config layout needs\n the width). Desktop-only \u2014 the mobile sheet below overrides regardless. */\n@media (min-width: 769px) {\n .launcher-panel--compact {\n left: var(--anchor-left);\n top: var(--anchor-top);\n transform: none;\n width: 320px;\n max-height: calc(100vh - var(--anchor-top) - var(--mj-space-6));\n box-shadow: var(--mj-shadow-lg);\n /* The pop animation animates translateX(-50%), which compact doesn't use */\n animation: launcher-fade-in var(--mj-transition-fast);\n }\n .launcher-panel--compact::backdrop {\n background: transparent;\n animation: none;\n }\n .launcher-panel--compact .launcher-grid {\n grid-template-columns: 1fr;\n gap: var(--mj-space-1);\n }\n .launcher-panel--compact .launcher-scroll {\n padding: var(--mj-space-2);\n }\n .launcher-panel--compact .launcher-sect {\n display: none; /* Single flat list \u2014 \"All apps\" header is noise here */\n }\n /* Compact rows are flat menu rows (not card blocks) with an explicit\n hover fill; active keeps the shared brand tint. */\n .launcher-panel--compact .app-card {\n padding: var(--mj-space-2) var(--mj-space-2-5);\n align-items: center;\n background: transparent;\n border-color: transparent;\n }\n .launcher-panel--compact .app-card:hover {\n background: var(--mj-bg-surface-hover);\n border-color: transparent;\n transform: none;\n }\n .launcher-panel--compact .app-card.active {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n border-color: transparent;\n }\n .launcher-panel--compact .app-card:hover {\n transform: none;\n }\n .launcher-panel--compact .app-card-glyph {\n width: 32px;\n height: 32px;\n font-size: var(--mj-text-base);\n }\n .launcher-panel--compact .app-card-desc {\n display: none;\n }\n .launcher-panel--compact .launcher-kbd-hints {\n display: none;\n }\n}\n\n@keyframes launcher-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n/* Desktop pop matches the omnibar's ob-pop exactly. The from-state keeps the\n layout translateX(-50%); on the mobile sheet (base transform: none) this\n animation is disabled rather than redefined \u2014 redefining keyframes inside\n the media query is what caused the jump-in bug. */\n@keyframes launcher-pop {\n from { opacity: 0; transform: translateX(-50%) scale(.985) translateY(-4px); }\n to { opacity: 1; transform: translateX(-50%) scale(1); }\n}\n\n/* ---- Head: filter + close ---- */\n.launcher-head {\n display: flex;\n align-items: center;\n gap: var(--mj-space-2);\n padding: var(--mj-space-4) var(--mj-space-4) var(--mj-space-2);\n flex-shrink: 0;\n}\n.launcher-filter {\n display: flex;\n align-items: center;\n gap: var(--mj-space-2-5);\n flex: 1;\n height: 46px;\n padding: 0 var(--mj-space-3-5);\n background: var(--mj-bg-surface-sunken);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n transition: border-color var(--mj-transition-fast);\n}\n.launcher-filter:focus-within {\n border-color: var(--mj-border-focus);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n.launcher-filter i {\n color: var(--mj-text-muted);\n font-size: var(--mj-text-sm);\n flex-shrink: 0;\n}\n.launcher-filter input {\n border: none;\n outline: none;\n background: transparent;\n width: 100%;\n font-family: inherit;\n font-size: var(--mj-text-base);\n color: var(--mj-text-primary);\n}\n.launcher-filter input::placeholder {\n color: var(--mj-text-muted);\n}\n.launcher-close {\n width: 40px;\n height: 40px;\n flex-shrink: 0;\n border: none;\n border-radius: var(--mj-radius-md);\n background: transparent;\n color: var(--mj-text-muted);\n font-size: var(--mj-text-base);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background var(--mj-transition-fast);\n}\n.launcher-close:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n.launcher-close:focus-visible {\n outline: 2px solid var(--mj-border-focus);\n outline-offset: 2px;\n}\n\n/* ---- In-panel configuration view (view swap) ---- */\n.launcher-head--config {\n align-items: center;\n gap: var(--mj-space-2-5);\n padding-bottom: var(--mj-space-2);\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n.launcher-back {\n width: 40px;\n height: 40px;\n flex-shrink: 0;\n border: none;\n border-radius: var(--mj-radius-md);\n background: transparent;\n color: var(--mj-text-muted);\n font-size: var(--mj-text-base);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background var(--mj-transition-fast);\n}\n.launcher-back:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n.launcher-back:focus-visible {\n outline: 2px solid var(--mj-border-focus);\n outline-offset: 2px;\n}\n.launcher-title {\n flex: 1;\n margin: 0;\n font-size: var(--mj-text-base);\n font-weight: var(--mj-font-semibold);\n color: var(--mj-text-primary);\n}\n.launcher-config-body {\n display: flex;\n flex-direction: column;\n flex: 1 1 auto;\n min-height: 0;\n overflow: hidden;\n}\n/* Inline unsaved-changes bar: lives inside the panel because a body-appended\n confirm dialog would sit beneath (and inert to) the top layer. */\n.launcher-discard-bar {\n display: flex;\n align-items: center;\n gap: var(--mj-space-2-5);\n margin: var(--mj-space-2) var(--mj-space-4) 0;\n padding: var(--mj-space-2) var(--mj-space-3);\n background: var(--mj-status-warning-bg);\n border: 1px solid var(--mj-status-warning-border);\n border-radius: var(--mj-radius-md);\n flex-shrink: 0;\n}\n.launcher-discard-bar i {\n color: var(--mj-status-warning);\n}\n.launcher-discard-bar .discard-msg {\n flex: 1;\n font-size: var(--mj-text-sm);\n font-weight: var(--mj-font-medium);\n color: var(--mj-status-warning-text);\n}\n\n/* ---- Scrollable card area ---- */\n.launcher-scroll {\n overflow-y: auto;\n padding: 0 var(--mj-space-4) var(--mj-space-4);\n flex: 1 1 auto;\n min-height: 0;\n}\n/* Typography matches the omnibar's .ob-group-label */\n.launcher-sect {\n font-size: 10.5px;\n font-weight: var(--mj-font-bold);\n letter-spacing: .6px;\n text-transform: uppercase;\n color: var(--mj-text-muted);\n padding: var(--mj-space-3) var(--mj-space-1-5) var(--mj-space-1-5);\n}\n.launcher-sect--row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding-right: var(--mj-space-1);\n}\n/* All-apps sort toggle: quiet icon button, brand-tinted when non-default */\n.launcher-sort-toggle {\n width: 28px;\n height: 28px;\n border: none;\n border-radius: var(--mj-radius-sm);\n background: transparent;\n color: var(--mj-text-muted);\n font-size: var(--mj-text-xs);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background var(--mj-transition-fast), color var(--mj-transition-fast);\n}\n.launcher-sort-toggle:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n.launcher-sort-toggle:focus-visible {\n outline: 2px solid var(--mj-border-focus);\n outline-offset: 2px;\n}\n.launcher-sort-toggle[aria-pressed=\"true\"] {\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n}\n.launcher-grid {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: var(--mj-space-2-5);\n}\n.launcher-empty {\n color: var(--mj-text-muted);\n font-size: var(--mj-text-sm);\n text-align: center;\n padding: var(--mj-space-6) var(--mj-space-2);\n}\n\n/* ---- App cards ---- */\n.app-card {\n display: flex;\n align-items: flex-start;\n gap: var(--mj-space-3);\n /* surface-card, NOT surface: on the elevated panel, plain surface is the\n same white in light mode and the cards visually vanish */\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg);\n padding: var(--mj-space-3) var(--mj-space-3-5);\n cursor: pointer;\n min-width: 0;\n transition: border-color var(--mj-transition-fast), background var(--mj-transition-fast), transform var(--mj-transition-fast);\n}\n.app-card:hover {\n border-color: color-mix(in srgb, var(--mj-brand-primary) 45%, var(--mj-border-default));\n background: var(--mj-bg-surface-hover);\n transform: translateY(-1px);\n}\n.app-card:focus-visible {\n outline: 2px solid var(--mj-border-focus);\n outline-offset: -1px;\n}\n.app-card.active {\n /* Shared active-tint recipe */\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n border-color: color-mix(in srgb, var(--mj-brand-primary) 45%, transparent);\n}\n.app-card.loading {\n pointer-events: none;\n opacity: 0.7;\n}\n.app-card-glyph {\n width: 40px;\n height: 40px;\n border-radius: var(--mj-radius-md);\n flex-shrink: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: var(--mj-text-lg);\n color: var(--app-color, var(--mj-text-muted));\n background: color-mix(in srgb, var(--app-color, var(--mj-text-muted)) 14%, var(--mj-bg-surface-card));\n}\n.app-card-text {\n min-width: 0;\n flex: 1;\n}\n.app-card-name {\n display: flex;\n align-items: baseline;\n gap: var(--mj-space-2);\n font-size: var(--mj-text-sm);\n font-weight: var(--mj-font-bold);\n color: var(--mj-text-primary);\n}\n.app-card-name-label {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.app-card.active .app-card-name {\n color: var(--mj-brand-primary);\n}\n.app-card-desc {\n margin-top: var(--mj-space-0-5);\n font-size: var(--mj-text-xs);\n line-height: var(--mj-leading-snug);\n color: var(--mj-text-muted);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n/* ---- Footer ---- */\n/* Footer tone matches the omnibar's .ob-foot */\n.launcher-foot {\n border-top: 1px solid var(--mj-border-subtle);\n background: var(--mj-bg-surface-card);\n padding: var(--mj-space-2-5) var(--mj-space-4);\n display: flex;\n align-items: center;\n flex-shrink: 0;\n}\n/* Footer options button \u2014 shared spec with the omnibar's Settings button:\n quiet labeled gear, right-aligned. */\n.launcher-configure {\n display: flex;\n align-items: center;\n gap: var(--mj-space-2);\n margin-left: auto;\n border: none;\n background: transparent;\n color: var(--mj-text-secondary);\n font-family: inherit;\n font-size: var(--mj-text-sm);\n font-weight: var(--mj-font-semibold);\n padding: var(--mj-space-1-5) var(--mj-space-2);\n border-radius: var(--mj-radius-md);\n cursor: pointer;\n transition: background var(--mj-transition-fast), color var(--mj-transition-fast);\n}\n.launcher-configure:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n.launcher-configure:focus-visible {\n outline: 2px solid var(--mj-border-focus);\n outline-offset: 2px;\n}\n.launcher-kbd-hints {\n display: flex;\n gap: var(--mj-space-4);\n font-size: 11.5px;\n color: var(--mj-text-muted);\n}\n/* kbd chips: the canonical ob-kbd chrome (10.5px / 5px radius / bottom-heavy\n border) \u2014 same spec the omnibar and the header search button use. */\n.launcher-kbd-hints kbd {\n font-family: inherit;\n font-size: 10.5px;\n font-weight: var(--mj-font-semibold);\n color: var(--mj-text-muted);\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-strong);\n border-bottom-width: 2px;\n border-radius: 5px;\n padding: 1.5px var(--mj-space-1-5);\n}\n\n/* ---- Screen-reader-only live region ---- */\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n}\n\n/* ---- Responsive ---- */\n@media (max-width: 900px) {\n .launcher-grid {\n grid-template-columns: repeat(2, 1fr);\n }\n}\n\n/* Mobile: full sheet below the header, single-column cards, 48px+ targets.\n Breakpoint matches the shell's 768px mobile breakpoint. */\n@media (max-width: 768px) {\n /* Full-screen sheet, matching the omnibar's mobile treatment. The pop\n animation is disabled (its from-state translateX no longer applies). */\n .launcher-panel {\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n transform: none;\n width: 100%;\n max-height: none;\n height: 100dvh;\n border-radius: 0;\n border: none;\n animation: none;\n }\n .launcher-grid {\n grid-template-columns: 1fr;\n }\n .app-card {\n min-height: 48px;\n align-items: center;\n }\n .launcher-close {\n width: 46px;\n height: 46px;\n }\n .launcher-kbd-hints {\n display: none; /* No hardware-keyboard hints on touch */\n }\n .app-switcher-button .app-label {\n display: none; /* Icon-only trigger \u2014 the 390px header has no room for a name */\n }\n}\n\n/* ---- Reduced motion ---- */\n@media (prefers-reduced-motion: reduce) {\n .launcher-panel,\n .launcher-panel::backdrop {\n animation: none;\n }\n .app-card,\n .app-card:hover {\n transition: none;\n transform: none;\n }\n}\n"] }]
890
- }], () => [{ type: i1.ApplicationManager }, { type: i0.ChangeDetectorRef }], { activeApp: [{
938
+ }], () => [{ type: i1.ApplicationManager }, { type: i0.ChangeDetectorRef }], { ActiveApp: [{
939
+ type: Input
940
+ }], activeApp: [{
941
+ type: Input
942
+ }], IsViewingSystemTab: [{
891
943
  type: Input
892
944
  }], isViewingSystemTab: [{
893
945
  type: Input
946
+ }], LoadingAppId: [{
947
+ type: Input
894
948
  }], loadingAppId: [{
895
949
  type: Input
896
950
  }], SwitcherStyle: [{
897
951
  type: Input
952
+ }], AppSelected: [{
953
+ type: Output
898
954
  }], appSelected: [{
899
955
  type: Output
900
956
  }], filterInputRef: [{
@@ -1 +1 @@
1
- {"version":3,"file":"app-switcher.component.js","sourceRoot":"","sources":["../../../../../src/lib/shell/components/header/app-switcher.component.ts","../../../../../src/lib/shell/components/header/app-switcher.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, ViewChild, ElementRef, OnInit, OnDestroy, ChangeDetectorRef } from '@angular/core';\nimport { ApplicationManager, BaseApplication } from '@memberjunction/ng-base-application';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { UserAppConfigContentComponent } from '@memberjunction/ng-explorer-settings';\nimport { UUIDsEqual, NormalizeUUID } from '@memberjunction/global';\nimport { UserInfoEngine } from '@memberjunction/core-entities';\nimport { Subject, takeUntil } from 'rxjs';\n\n/** UserInfoEngine setting key holding the recently-switched-to apps */\nconst RECENT_APPS_KEY = 'mj.shell.recentApps.v1';\n/** UserInfoEngine setting key for the launcher's All-apps sort preference */\nconst LAUNCHER_SORT_KEY = 'mj.shell.launcherSort.v1';\n/** How many recent entries to persist (display shows fewer) */\nconst RECENT_STORE_MAX = 8;\n/** How many recent cards to display */\nconst RECENT_DISPLAY_MAX = 3;\n/** 'auto' style: below this many apps the compact anchored panel is used */\nconst COMPACT_AUTO_THRESHOLD = 8;\n/** Compact mode: the filter input appears only at or above this many apps */\nconst COMPACT_FILTER_THRESHOLD = 10;\n\n/**\n * Presentation style for the app switcher, resolved from the\n * `Shell.AppSwitcher.Style` instance-config key:\n * - 'launcher': always the centered card launcher\n * - 'compact': always the anchored compact panel (dropdown-like)\n * - 'auto' (default): compact below {@link COMPACT_AUTO_THRESHOLD} apps\n */\nexport type AppSwitcherStyle = 'launcher' | 'compact' | 'auto';\n\ninterface RecentAppEntry {\n id: string;\n ts: number;\n}\n\n/**\n * App switcher in the header: the trigger shows the active app's identity\n * glyph; activating it opens a centered launcher overlay — a filterable grid\n * of app cards (identity glyph + name + Description summary), with the user's\n * most recently used apps surfaced first.\n *\n * Rendering model: the panel is a native <dialog> opened with showModal(),\n * so it lives in the browser's TOP LAYER — above every stacking context in\n * the app (the shell header's z-index:500 context, chat FAB, toasts). The\n * modal state also makes the rest of the document inert (focus cannot\n * escape) and the browser restores focus to the trigger on close.\n *\n * Accessibility model: every app card is a real focusable control (via\n * mjClickable), so Tab walks filter → close → cards → Configure, and\n * Enter/Space activate the focused card. Arrow keys are the fast path:\n * ArrowDown from the filter focuses the first card; arrows move between\n * cards; ArrowUp from the first card returns to the filter. Escape layers:\n * discard-bar → config view → filter → close. Exiting the config view with\n * unsaved changes shows an inline discard bar (a body-appended confirm\n * dialog would be inert/beneath the top layer).\n */\n@Component({\n standalone: false,\n selector: 'mj-app-switcher',\n templateUrl: './app-switcher.component.html',\n styleUrls: ['./app-switcher.component.css']\n})\nexport class AppSwitcherComponent extends BaseAngularComponent implements OnInit, OnDestroy {\n private destroy$ = new Subject<void>();\n @Input() activeApp: BaseApplication | null = null;\n @Input() isViewingSystemTab = false;\n /** ID of the app currently being loaded (shows loading indicator) */\n @Input() loadingAppId: string | null = null;\n /** Presentation style (from Shell.AppSwitcher.Style instance config) */\n @Input() SwitcherStyle: AppSwitcherStyle = 'auto';\n @Output() appSelected = new EventEmitter<string>();\n\n @ViewChild('filterInput') private filterInputRef?: ElementRef<HTMLInputElement>;\n @ViewChild('trigger') private triggerRef?: ElementRef<HTMLElement>;\n @ViewChild('panel') private panelRef?: ElementRef<HTMLDialogElement>;\n @ViewChild(UserAppConfigContentComponent) private configContent?: UserAppConfigContentComponent;\n\n showDropdown = false;\n /** When true, the launcher body shows the app-configuration view instead of the card grid */\n public ConfigMode = false;\n /** Pending action awaiting the inline unsaved-changes discard bar ('exit' = back to grid, 'close' = close launcher) */\n public PendingDiscard: 'exit' | 'close' | null = null;\n /** True when the panel renders as the compact anchored dropdown (few apps) */\n public CompactMode = false;\n /** All-apps sort: the user's configured order ('custom') or A–Z ('alpha').\n * Per-user preference, persisted via UserInfoEngine. */\n public SortMode: 'custom' | 'alpha' = 'custom';\n /** Anchor position for the compact panel (px, from the trigger's rect) */\n public AnchorLeft = 0;\n public AnchorTop = 0;\n\n /** Live filter text — matches app names AND Description summaries */\n public FilterText = '';\n\n private recentEntries: RecentAppEntry[] = [];\n\n constructor(private appManager: ApplicationManager, private cdr: ChangeDetectorRef) {\n super();\n }\n\n /**\n * UserInfoEngine scoped to this component's provider (multi-provider rule:\n * never reach for the global singleton in per-provider code paths). For the\n * default single-provider shell this resolves to the already-loaded engine\n * registered under the default connection. When no provider is available at\n * all (test environments), fall back to the global singleton rather than\n * letting GetProviderInstance construct a fresh unloaded engine.\n */\n private get userInfoEngine(): UserInfoEngine {\n const provider = this.ProviderToUse;\n return provider\n ? UserInfoEngine.GetProviderInstance<UserInfoEngine>(provider, UserInfoEngine) as UserInfoEngine\n : UserInfoEngine.Instance;\n }\n\n ngOnInit(): void {\n // The launcher's presentation (compact vs full, filter visibility) is\n // resolved from the app COUNT — which can change while the panel is open\n // (the user saves config changes that cross the auto threshold). React to\n // the same stream the app list uses so the presentation can't go stale.\n // Optional-chained: lightweight test stubs may not provide the observable.\n this.appManager.Applications?.pipe(takeUntil(this.destroy$)).subscribe(() => {\n if (this.showDropdown) {\n this.RefreshPresentation();\n }\n });\n }\n\n ngOnDestroy(): void {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n /**\n * Re-resolve the presentation for the CURRENT app count. No-op while the\n * config view is open (the panel is promoted to full size there anyway);\n * called when the app stream emits and when the config view exits.\n */\n RefreshPresentation(): void {\n if (this.ConfigMode) {\n return;\n }\n const wasCompact = this.CompactMode;\n this.CompactMode = this.resolveCompactMode();\n if (this.CompactMode && !wasCompact) {\n this.computeCompactAnchor();\n }\n // Mode flips can add/remove the filter input — if the focused element got\n // torn out of the DOM, focus would drop into the inert background. Re-home\n // it after the view settles.\n // Zoneless: an RxJS-driven mutation needs an explicit CD kick (same\n // pattern as AppNavComponent's workspace subscription).\n this.cdr.markForCheck();\n try {\n this.cdr.detectChanges();\n } catch {\n // Re-entrant CD — harmless.\n }\n if (wasCompact !== this.CompactMode) {\n requestAnimationFrame(() => {\n const panel = this.panelRef?.nativeElement;\n if (panel && !panel.contains(document.activeElement)) {\n if (this.ShowFilter) {\n this.filterInputRef?.nativeElement?.focus();\n } else {\n panel.querySelector<HTMLElement>('.app-card')?.focus();\n }\n }\n });\n }\n }\n\n /** Template handler for the filter input's input event */\n OnFilterInput(event: Event): void {\n this.FilterText = (event.target as HTMLInputElement).value;\n this.OnFilterChange();\n }\n\n /**\n * Check if the app switcher should show loading state\n */\n get isLoading(): boolean {\n return this.loadingAppId !== null;\n }\n\n /**\n * Visible trigger label: the current app's name, or \"Apps\" when there's no\n * app context (system tabs, nothing active yet).\n */\n get TriggerLabel(): string {\n return this.activeApp && !this.isViewingSystemTab ? this.activeApp.Name : 'Apps';\n }\n\n /** Accessible name for the trigger — names the control AND the current app */\n get TriggerAriaLabel(): string {\n return this.activeApp && !this.isViewingSystemTab\n ? `Switch application — current: ${this.activeApp.Name}`\n : 'Switch application';\n }\n\n /**\n * Get applications that should appear in the app switcher\n * (NavigationStyle = 'App Switcher' or 'Both')\n */\n get apps(): BaseApplication[] {\n return this.appManager.GetAppSwitcherApps();\n }\n\n /** Recently-used apps (persisted per user), newest first, capped for display.\n * Compact mode skips the Recent section — with a handful of apps it's noise. */\n get RecentApps(): BaseApplication[] {\n if (this.CompactMode) {\n return [];\n }\n const byId = new Map(this.apps.map(a => [NormalizeUUID(a.ID), a]));\n const result: BaseApplication[] = [];\n for (const entry of this.recentEntries) {\n const app = byId.get(NormalizeUUID(entry.id));\n if (app) {\n result.push(app);\n if (result.length >= RECENT_DISPLAY_MAX) {\n break;\n }\n }\n }\n return this.applyFilter(result);\n }\n\n /** All switcher apps in the user's chosen sort (custom Sequence order or A–Z) */\n private get sortedApps(): BaseApplication[] {\n return this.SortMode === 'alpha'\n ? [...this.apps].sort((a, b) => a.Name.localeCompare(b.Name))\n : this.apps;\n }\n\n /** The complete \"All apps\" section, in the user's chosen sort. Apps shown in\n * Recent appear here TOO — being recently used must not make an app vanish\n * from its expected place in the full list. */\n get OtherApps(): BaseApplication[] {\n return this.applyFilter(this.sortedApps);\n }\n\n /** Flat visible list in RENDERED card order — the keyboard-navigation index\n * space. Filtering collapses the sections into one deduplicated list;\n * otherwise it's Recent followed by the full All-apps list (a recent app\n * intentionally appears twice, matching the two cards on screen). */\n get VisibleApps(): BaseApplication[] {\n if (this.IsFiltering) {\n return this.applyFilter(this.sortedApps);\n }\n return [...this.RecentApps, ...this.OtherApps];\n }\n\n /** Distinct app count for the screen-reader status (VisibleApps can contain\n * the same app twice when it's in both Recent and All apps) */\n get AnnouncedAppCount(): number {\n return this.IsFiltering ? this.VisibleApps.length : this.applyFilter(this.apps).length;\n }\n\n /** True when a filter is active (sections collapse into one result list) */\n get IsFiltering(): boolean {\n return this.FilterText.trim().length > 0;\n }\n\n /** Compact mode hides the filter under a handful of apps — it's noise there */\n get ShowFilter(): boolean {\n return !this.CompactMode || this.apps.length >= COMPACT_FILTER_THRESHOLD;\n }\n\n private applyFilter(apps: BaseApplication[]): BaseApplication[] {\n const q = this.FilterText.trim().toLowerCase();\n if (!q) {\n return apps;\n }\n return apps.filter(a =>\n a.Name.toLowerCase().includes(q) || (a.Description || '').toLowerCase().includes(q));\n }\n\n /** Stable DOM id for a card (arrow-key focus target) */\n OptionId(index: number): string {\n return `mj-launcher-opt-${index}`;\n }\n\n /** Trigger handler: open the launcher panel, or close it if already open */\n ToggleLauncher(): void {\n if (this.showDropdown) {\n this.closeLauncher();\n } else {\n this.openLauncher();\n }\n }\n\n /** Flip the All-apps sort and persist it (fail-silent, like recents) */\n ToggleSortMode(): void {\n this.SortMode = this.SortMode === 'alpha' ? 'custom' : 'alpha';\n try {\n this.userInfoEngine.SetSettingDebounced(LAUNCHER_SORT_KEY, this.SortMode);\n } catch {\n // Preference persistence must never break the launcher\n }\n }\n\n private loadSortMode(): void {\n try {\n this.SortMode = this.userInfoEngine.GetSetting(LAUNCHER_SORT_KEY) === 'alpha' ? 'alpha' : 'custom';\n } catch {\n this.SortMode = 'custom';\n }\n }\n\n private openLauncher(): void {\n this.loadRecents();\n this.loadSortMode();\n this.FilterText = '';\n this.ConfigMode = false;\n this.PendingDiscard = null;\n this.CompactMode = this.resolveCompactMode();\n if (this.CompactMode) {\n this.computeCompactAnchor();\n }\n this.showDropdown = true;\n // The dialog element renders under @if next frame: put it in the top\n // layer via showModal() (jsdom fallback: plain open attribute), then\n // focus the filter (or the first card when compact mode hides it).\n requestAnimationFrame(() => {\n const dialog = this.panelRef?.nativeElement;\n if (dialog && !dialog.open) {\n if (typeof dialog.showModal === 'function') {\n dialog.showModal();\n } else {\n dialog.setAttribute('open', '');\n }\n }\n if (this.ShowFilter) {\n this.filterInputRef?.nativeElement?.focus();\n } else {\n dialog?.querySelector<HTMLElement>('.app-card')?.focus();\n }\n });\n }\n\n /** Resolve the presentation for this open (auto = compact under the threshold) */\n private resolveCompactMode(): boolean {\n if (this.SwitcherStyle === 'compact') {\n return true;\n }\n if (this.SwitcherStyle === 'launcher') {\n return false;\n }\n return this.apps.length < COMPACT_AUTO_THRESHOLD;\n }\n\n /** Anchor the compact panel under the trigger, clamped to the viewport */\n private computeCompactAnchor(): void {\n const rect = this.triggerRef?.nativeElement?.getBoundingClientRect();\n if (!rect) {\n return;\n }\n const panelWidth = 320; // Keep in sync with .launcher-panel--compact width\n const margin = 8;\n this.AnchorLeft = Math.max(margin, Math.min(rect.left, window.innerWidth - panelWidth - margin));\n this.AnchorTop = rect.bottom + margin;\n }\n\n private closeLauncher(restoreFocus = true): void {\n const dialog = this.panelRef?.nativeElement;\n if (dialog?.open && typeof dialog.close === 'function') {\n dialog.close(); // Browser restores focus to the trigger automatically\n }\n this.showDropdown = false;\n this.FilterText = '';\n this.ConfigMode = false;\n this.PendingDiscard = null;\n if (restoreFocus) {\n requestAnimationFrame(() => {\n this.triggerRef?.nativeElement?.focus();\n });\n }\n }\n\n /** Guarded close for template paths (backdrop / close button): unsaved\n * config changes surface the inline discard bar instead of closing. */\n CloseLauncher(): void {\n if (this.ConfigMode && this.configContent?.HasChanges()) {\n this.PendingDiscard = 'close';\n return;\n }\n this.closeLauncher();\n }\n\n /** Backdrop clicks arrive as clicks on the <dialog> element itself */\n OnDialogClick(event: MouseEvent): void {\n if (event.target === this.panelRef?.nativeElement) {\n this.CloseLauncher();\n }\n }\n\n /** Native cancel (Esc while the browser owns the dialog) — route through\n * our layered Escape handling instead of closing unconditionally. */\n OnDialogCancel(event: Event): void {\n event.preventDefault();\n this.handleEscape();\n }\n\n /** Inline discard bar: confirm — perform the pending action, dropping changes */\n ConfirmDiscard(): void {\n const action = this.PendingDiscard;\n this.PendingDiscard = null;\n if (action === 'close') {\n this.closeLauncher();\n } else if (action === 'exit') {\n this.ExitConfigMode();\n }\n }\n\n /** Inline discard bar: keep editing */\n CancelDiscard(): void {\n this.PendingDiscard = null;\n }\n\n OnFilterChange(): void {\n // Filtering re-renders the card set; nothing else to sync — focus stays\n // in the input and Tab/arrows reach the new result set naturally.\n }\n\n /** Keyboard model on the filter input */\n OnFilterKeydown(event: KeyboardEvent): void {\n const visible = this.VisibleApps;\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n this.focusCard(0);\n break;\n case 'Enter':\n event.preventDefault();\n // While filtering, Enter opens the top result (omnibar semantics —\n // the footer advertises \"↵ open\"); unfiltered it needs an unambiguous\n // single app.\n if (this.IsFiltering && visible.length > 0) {\n this.selectApp(visible[0]);\n } else if (visible.length === 1) {\n this.selectApp(visible[0]);\n }\n break;\n case 'Escape':\n event.preventDefault();\n // Stop the bubble: the panel's own Escape handler would otherwise\n // re-evaluate AFTER we've mutated state and double-handle the key.\n event.stopPropagation();\n this.handleEscape();\n break;\n }\n }\n\n /** Arrow-key navigation between focused cards */\n OnCardKeydown(index: number, event: KeyboardEvent): void {\n switch (event.key) {\n case 'ArrowDown':\n case 'ArrowRight':\n event.preventDefault();\n this.focusCard(index + 1);\n break;\n case 'ArrowUp':\n case 'ArrowLeft':\n event.preventDefault();\n if (index === 0) {\n this.filterInputRef?.nativeElement?.focus();\n } else {\n this.focusCard(index - 1);\n }\n break;\n case 'Home':\n event.preventDefault();\n this.focusCard(0);\n break;\n case 'End':\n event.preventDefault();\n this.focusCard(this.VisibleApps.length - 1);\n break;\n }\n }\n\n /** Move DOM focus to the card at the given VisibleApps index (clamped) */\n private focusCard(index: number): void {\n const count = this.VisibleApps.length;\n if (count === 0) {\n return;\n }\n const clamped = Math.min(count - 1, Math.max(0, index));\n const el = document.getElementById(this.OptionId(clamped));\n if (el) {\n el.focus();\n // jsdom (unit tests) doesn't implement scrollIntoView — guard it.\n if (typeof el.scrollIntoView === 'function') {\n el.scrollIntoView({ block: 'nearest' });\n }\n }\n }\n\n /** Keep Tab cycling within the launcher's real focusables (filter, close, Configure) */\n /** Escape layering: discard bar → config view (guarded) → filter → close.\n * Tab is NOT manually trapped — showModal() makes the background inert,\n * which is a real trap (no leak when focus sits on a non-focusable). */\n OnPanelKeydown(event: KeyboardEvent): void {\n if (event.key === 'Escape') {\n event.preventDefault();\n event.stopPropagation();\n this.handleEscape();\n }\n }\n\n private handleEscape(): void {\n if (this.PendingDiscard) {\n this.CancelDiscard();\n } else if (this.ConfigMode) {\n this.RequestExitConfigMode();\n } else if (this.IsFiltering) {\n this.FilterText = '';\n this.filterInputRef?.nativeElement?.focus();\n } else {\n this.closeLauncher();\n }\n }\n\n /**\n * Select an application.\n * When viewing a system tab, always emit to allow returning to the app.\n */\n selectApp(app: BaseApplication): void {\n this.recordRecent(app);\n this.closeLauncher(false); // Focus moves into the app, not back to the trigger\n if (!UUIDsEqual(app.ID, this.activeApp?.ID) || this.isViewingSystemTab) {\n this.appSelected.emit(app.ID);\n }\n }\n\n /** Case-insensitive UUID check whether an app is the currently active app. */\n IsActiveApp(app: BaseApplication): boolean {\n return UUIDsEqual(app.ID, this.activeApp?.ID);\n }\n\n /** Case-insensitive UUID check whether an app is the one currently loading. */\n IsLoadingApp(app: BaseApplication): boolean {\n return UUIDsEqual(app.ID, this.loadingAppId);\n }\n\n // ---- Recents persistence (MJ: User Settings via UserInfoEngine) ----\n\n private loadRecents(): void {\n try {\n const raw = this.userInfoEngine.GetSetting(RECENT_APPS_KEY);\n const parsed = raw ? JSON.parse(raw) : [];\n this.recentEntries = Array.isArray(parsed)\n ? parsed.filter((e: RecentAppEntry) => e && typeof e.id === 'string' && typeof e.ts === 'number')\n : [];\n } catch {\n this.recentEntries = [];\n }\n }\n\n private recordRecent(app: BaseApplication): void {\n const id = NormalizeUUID(app.ID);\n this.recentEntries = [\n { id, ts: Date.now() },\n ...this.recentEntries.filter(e => NormalizeUUID(e.id) !== id)\n ].slice(0, RECENT_STORE_MAX);\n try {\n this.userInfoEngine.SetSettingDebounced(RECENT_APPS_KEY, JSON.stringify(this.recentEntries));\n } catch {\n // Settings persistence must never block app switching (e.g. engine not\n // configured in tests or permission-constrained sessions).\n }\n }\n\n /**\n * Swap the launcher body to the app-configuration view (in-panel, seamless —\n * no separate dialog). The embedded mj-user-app-config-content loads on\n * creation.\n */\n EnterConfigMode(): void {\n this.ConfigMode = true;\n this.FilterText = '';\n this.PendingDiscard = null;\n // Move focus into the config view (the back button is its first focusable)\n requestAnimationFrame(() => {\n this.panelRef?.nativeElement\n ?.querySelector<HTMLElement>('.launcher-back')?.focus();\n });\n }\n\n /** Guarded exit from the config view: unsaved changes surface the inline\n * discard bar instead of silently dropping the user's edits. */\n RequestExitConfigMode(): void {\n if (this.configContent?.HasChanges()) {\n this.PendingDiscard = 'exit';\n return;\n }\n this.ExitConfigMode();\n }\n\n /**\n * Return from the configuration view to the app grid.\n * The app list refreshes reactively after a save: each UserApplication save\n * fires a BaseEntity event, UserInfoEngine's debounced refresh emits\n * DataChange$, and ApplicationManager.syncFromEngine() pushes the new list\n * to applications$, which the `apps` getter reads. No explicit reload needed.\n */\n ExitConfigMode(): void {\n this.ConfigMode = false;\n // The config session may have changed the app count across the compact\n // threshold — re-resolve the presentation for the view we're returning to.\n // (The Applications stream also triggers this when the post-save engine\n // refresh lands, whichever comes last wins with the freshest count.)\n this.RefreshPresentation();\n requestAnimationFrame(() => {\n if (this.ShowFilter) {\n this.filterInputRef?.nativeElement?.focus();\n } else {\n this.panelRef?.nativeElement?.querySelector<HTMLElement>('.app-card')?.focus();\n }\n });\n }\n}\n","<div class=\"app-switcher-container\" [class.loading]=\"isLoading\">\n <div class=\"app-switcher-button\"\n #trigger\n [mjClickable]=\"TriggerAriaLabel\"\n testId=\"app-switcher-trigger\"\n [attr.aria-haspopup]=\"'dialog'\"\n [attr.aria-expanded]=\"showDropdown\"\n (click)=\"ToggleLauncher()\">\n <!-- Show spinner when loading, otherwise show app icon -->\n @if (isLoading) {\n <i class=\"app-icon loading-spinner fa-solid fa-spinner fa-spin\" aria-hidden=\"true\"></i>\n }\n @if (!isLoading) {\n <i class=\"app-icon\" [class]=\"activeApp?.Icon || 'fa-solid fa-cube'\"\n [style.color]=\"activeApp?.GetColor()\" aria-hidden=\"true\"></i>\n }\n <!-- Current app name; hidden at the mobile breakpoint (icon-only trigger) -->\n <span class=\"app-label\">{{ TriggerLabel }}</span>\n <i class=\"dropdown-arrow fa-solid fa-chevron-down\" aria-hidden=\"true\"></i>\n </div>\n\n @if (showDropdown) {\n <!-- Native <dialog> + showModal(): lives in the browser TOP LAYER (above\n every app stacking context — chat FAB, toasts), background is inert\n (real focus trap), and focus restores to the trigger on close. The\n backdrop is ::backdrop; clicks on it arrive as clicks on the dialog\n element itself (OnDialogClick). -->\n <dialog class=\"launcher-panel\"\n #panel\n role=\"dialog\"\n aria-modal=\"true\"\n [class.launcher-panel--compact]=\"CompactMode && !ConfigMode\"\n [style.--anchor-left]=\"AnchorLeft + 'px'\"\n [style.--anchor-top]=\"AnchorTop + 'px'\"\n [attr.aria-label]=\"ConfigMode ? null : 'Application launcher'\"\n [attr.aria-labelledby]=\"ConfigMode ? 'mj-launcher-config-title' : null\"\n (click)=\"OnDialogClick($event)\"\n (cancel)=\"OnDialogCancel($event)\"\n (keydown)=\"OnPanelKeydown($event)\">\n\n @if (ConfigMode) {\n <!-- Configuration view: same panel, body swapped -->\n <div class=\"launcher-head launcher-head--config\">\n <button class=\"launcher-back\" type=\"button\" aria-label=\"Back to apps\" (click)=\"RequestExitConfigMode()\">\n <i class=\"fa-solid fa-arrow-left\" aria-hidden=\"true\"></i>\n </button>\n <h2 class=\"launcher-title\" id=\"mj-launcher-config-title\">Configure apps</h2>\n <button class=\"launcher-close\" type=\"button\" aria-label=\"Close application launcher\" (click)=\"CloseLauncher()\">\n <i class=\"fa-solid fa-xmark\" aria-hidden=\"true\"></i>\n </button>\n </div>\n @if (PendingDiscard) {\n <div class=\"launcher-discard-bar\" role=\"alert\">\n <i class=\"fa-solid fa-triangle-exclamation\" aria-hidden=\"true\"></i>\n <span class=\"discard-msg\">You have unsaved changes.</span>\n <button mjButton variant=\"danger\" size=\"sm\" (click)=\"ConfirmDiscard()\">Discard</button>\n <button mjButton size=\"sm\" (click)=\"CancelDiscard()\">Keep editing</button>\n </div>\n }\n <mj-user-app-config-content\n class=\"launcher-config-body\"\n [Provider]=\"Provider\"\n (Saved)=\"ExitConfigMode()\"\n (Cancelled)=\"RequestExitConfigMode()\">\n </mj-user-app-config-content>\n } @else {\n @if (ShowFilter) {\n <div class=\"launcher-head\">\n <div class=\"launcher-filter\">\n <i class=\"fa-solid fa-magnifying-glass\" aria-hidden=\"true\"></i>\n <input\n #filterInput\n type=\"text\"\n placeholder=\"Find an app…\"\n aria-label=\"Find an app\"\n [value]=\"FilterText\"\n (input)=\"OnFilterInput($event)\"\n (keydown)=\"OnFilterKeydown($event)\"\n autocomplete=\"off\"\n spellcheck=\"false\" />\n </div>\n <button class=\"launcher-close\" type=\"button\" aria-label=\"Close application launcher\" (click)=\"CloseLauncher()\">\n <i class=\"fa-solid fa-xmark\" aria-hidden=\"true\"></i>\n </button>\n </div>\n }\n\n <div class=\"launcher-scroll\">\n @if (IsFiltering) {\n <div class=\"launcher-grid\">\n @for (app of VisibleApps; track app.ID; let i = $index) {\n <ng-container *ngTemplateOutlet=\"appCard; context: { $implicit: app, idx: i }\"></ng-container>\n }\n </div>\n @if (VisibleApps.length === 0) {\n <div class=\"launcher-empty\">No apps match \"{{ FilterText }}\"</div>\n }\n } @else {\n @if (RecentApps.length > 0) {\n <div class=\"launcher-sect\">Recent</div>\n <div class=\"launcher-grid\" role=\"group\" aria-label=\"Recent applications\">\n @for (app of RecentApps; track app.ID; let i = $index) {\n <ng-container *ngTemplateOutlet=\"appCard; context: { $implicit: app, idx: i }\"></ng-container>\n }\n </div>\n }\n <div class=\"launcher-sect launcher-sect--row\">\n <span>All apps</span>\n <button class=\"launcher-sort-toggle\" type=\"button\"\n [attr.aria-pressed]=\"SortMode === 'alpha'\"\n [attr.aria-label]=\"SortMode === 'alpha' ? 'Sorted alphabetically — switch to your custom order' : 'Sorted by your custom order — switch to alphabetical'\"\n [title]=\"SortMode === 'alpha' ? 'Sorted A–Z — click for your custom order' : 'Custom order — click for A–Z'\"\n (click)=\"ToggleSortMode()\">\n <i [class]=\"SortMode === 'alpha' ? 'fa-solid fa-arrow-down-a-z' : 'fa-solid fa-bars-staggered'\" aria-hidden=\"true\"></i>\n </button>\n </div>\n <div class=\"launcher-grid\" role=\"group\" aria-label=\"All applications\">\n @for (app of OtherApps; track app.ID; let i = $index) {\n <ng-container *ngTemplateOutlet=\"appCard; context: { $implicit: app, idx: i + RecentApps.length }\"></ng-container>\n }\n </div>\n @if (VisibleApps.length === 0) {\n <div class=\"launcher-empty\">\n No applications are enabled for you yet — use Configure… below to add some.\n </div>\n }\n }\n </div>\n\n <div class=\"launcher-foot\">\n <span class=\"launcher-kbd-hints\" aria-hidden=\"true\">\n <span><kbd>↑</kbd><kbd>↓</kbd> navigate</span>\n <span><kbd>↵</kbd> open</span>\n <span><kbd>esc</kbd> close</span>\n </span>\n <button class=\"launcher-configure\" type=\"button\" testId=\"app-switcher-configure\" (click)=\"EnterConfigMode()\">\n <i class=\"fa-solid fa-gear\" aria-hidden=\"true\"></i>\n <span>Configure…</span>\n </button>\n </div>\n\n <span class=\"sr-only\" role=\"status\">{{ AnnouncedAppCount }} applications available</span>\n }\n </dialog>\n }\n</div>\n\n<!-- Shared app-card template: each card is a real focusable control (mjClickable\n adds role, tabindex 0, aria-label, and Enter/Space activation), so Tab walks\n the cards and arrow keys are the fast path. -->\n<ng-template #appCard let-app let-idx=\"idx\">\n <div class=\"app-card\"\n [id]=\"OptionId(idx)\"\n [mjClickable]=\"app.Name\"\n role=\"link\"\n [testId]=\"'app-switcher-item-' + app.Name\"\n [attr.aria-current]=\"IsActiveApp(app) ? 'true' : null\"\n [attr.aria-describedby]=\"app.Description ? OptionId(idx) + '-desc' : null\"\n [class.active]=\"IsActiveApp(app)\"\n [class.loading]=\"IsLoadingApp(app)\"\n [style.--app-color]=\"app.GetColor()\"\n (click)=\"selectApp(app)\"\n (keydown)=\"OnCardKeydown(idx, $event)\">\n <div class=\"app-card-glyph\">\n @if (IsLoadingApp(app)) {\n <i class=\"fa-solid fa-spinner fa-spin\" aria-hidden=\"true\"></i>\n } @else {\n <i [class]=\"app.Icon || 'fa-solid fa-cube'\" aria-hidden=\"true\"></i>\n }\n </div>\n <div class=\"app-card-text\">\n <div class=\"app-card-name\">\n <span class=\"app-card-name-label\">{{ app.Name }}</span>\n </div>\n @if (app.Description) {\n <div class=\"app-card-desc\" [id]=\"OptionId(idx) + '-desc'\">{{ app.Description }}</div>\n }\n </div>\n </div>\n</ng-template>\n\n"]}
1
+ {"version":3,"file":"app-switcher.component.js","sourceRoot":"","sources":["../../../../../src/lib/shell/components/header/app-switcher.component.ts","../../../../../src/lib/shell/components/header/app-switcher.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, ViewChild, ElementRef, OnInit, OnDestroy, ChangeDetectorRef } from '@angular/core';\nimport { ApplicationManager, BaseApplication } from '@memberjunction/ng-base-application';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { UserAppConfigContentComponent } from '@memberjunction/ng-explorer-settings';\nimport { UUIDsEqual, NormalizeUUID } from '@memberjunction/global';\nimport { UserInfoEngine } from '@memberjunction/core-entities';\nimport { Subject, takeUntil } from 'rxjs';\n\n/** UserInfoEngine setting key holding the recently-switched-to apps */\nconst RECENT_APPS_KEY = 'mj.shell.recentApps.v1';\n/** UserInfoEngine setting key for the launcher's All-apps sort preference */\nconst LAUNCHER_SORT_KEY = 'mj.shell.launcherSort.v1';\n/** How many recent entries to persist (display shows fewer) */\nconst RECENT_STORE_MAX = 8;\n/** How many recent cards to display */\nconst RECENT_DISPLAY_MAX = 3;\n/** 'auto' style: below this many apps the compact anchored panel is used */\nconst COMPACT_AUTO_THRESHOLD = 8;\n/** Compact mode: the filter input appears only at or above this many apps */\nconst COMPACT_FILTER_THRESHOLD = 10;\n\n/**\n * Presentation style for the app switcher, resolved from the\n * `Shell.AppSwitcher.Style` instance-config key:\n * - 'launcher': always the centered card launcher\n * - 'compact': always the anchored compact panel (dropdown-like)\n * - 'auto' (default): compact below {@link COMPACT_AUTO_THRESHOLD} apps\n */\nexport type AppSwitcherStyle = 'launcher' | 'compact' | 'auto';\n\ninterface RecentAppEntry {\n id: string;\n ts: number;\n}\n\n/**\n * App switcher in the header: the trigger shows the active app's identity\n * glyph; activating it opens a centered launcher overlay — a filterable grid\n * of app cards (identity glyph + name + Description summary), with the user's\n * most recently used apps surfaced first.\n *\n * Rendering model: the panel is a native <dialog> opened with showModal(),\n * so it lives in the browser's TOP LAYER — above every stacking context in\n * the app (the shell header's z-index:500 context, chat FAB, toasts). The\n * modal state also makes the rest of the document inert (focus cannot\n * escape) and the browser restores focus to the trigger on close.\n *\n * Accessibility model: every app card is a real focusable control (via\n * mjClickable), so Tab walks filter → close → cards → Configure, and\n * Enter/Space activate the focused card. Arrow keys are the fast path:\n * ArrowDown from the filter focuses the first card; arrows move between\n * cards; ArrowUp from the first card returns to the filter. Escape layers:\n * discard-bar → config view → filter → close. Exiting the config view with\n * unsaved changes shows an inline discard bar (a body-appended confirm\n * dialog would be inert/beneath the top layer).\n */\n@Component({\n standalone: false,\n selector: 'mj-app-switcher',\n templateUrl: './app-switcher.component.html',\n styleUrls: ['./app-switcher.component.css']\n})\nexport class AppSwitcherComponent extends BaseAngularComponent implements OnInit, OnDestroy {\n private destroy$ = new Subject<void>();\n @Input() ActiveApp: BaseApplication | null = null;\n\n /** @deprecated Use {@link ActiveApp}. */\n @Input() set activeApp(value: BaseApplication | null) {\n this.ActiveApp = value;\n }\n /** @deprecated Use {@link ActiveApp}. */\n get activeApp(): BaseApplication | null {\n return this.ActiveApp;\n }\n @Input() IsViewingSystemTab = false;\n\n /** @deprecated Use {@link IsViewingSystemTab}. */\n @Input() set isViewingSystemTab(value: AppSwitcherComponent['IsViewingSystemTab']) {\n this.IsViewingSystemTab = value;\n }\n /** @deprecated Use {@link IsViewingSystemTab}. */\n get isViewingSystemTab(): AppSwitcherComponent['IsViewingSystemTab'] {\n return this.IsViewingSystemTab;\n }\n /** ID of the app currently being loaded (shows loading indicator) */\n @Input() LoadingAppId: string | null = null;\n\n /** @deprecated Use {@link LoadingAppId}. */\n @Input() set loadingAppId(value: string | null) {\n this.LoadingAppId = value;\n }\n /** @deprecated Use {@link LoadingAppId}. */\n get loadingAppId(): string | null {\n return this.LoadingAppId;\n }\n /** Presentation style (from Shell.AppSwitcher.Style instance config) */\n @Input() SwitcherStyle: AppSwitcherStyle = 'auto';\n @Output() AppSelected = new EventEmitter<string>();\n\n /**\n * @deprecated Use {@link AppSelected}.\n *\n * The same emitter under the old binding name, so a template still binding\n * (appSelected) keeps working. Must stay AFTER AppSelected: class fields\n * initialise in order, and the other way round this captures undefined.\n */\n @Output() appSelected = this.AppSelected;\n\n @ViewChild('filterInput') private filterInputRef?: ElementRef<HTMLInputElement>;\n @ViewChild('trigger') private triggerRef?: ElementRef<HTMLElement>;\n @ViewChild('panel') private panelRef?: ElementRef<HTMLDialogElement>;\n @ViewChild(UserAppConfigContentComponent) private configContent?: UserAppConfigContentComponent;\n\n ShowDropdown = false;\n\n /** @deprecated Use {@link ShowDropdown}. */\n get showDropdown() {\n return this.ShowDropdown;\n }\n /** @deprecated Use {@link ShowDropdown}. */\n set showDropdown(value) {\n this.ShowDropdown = value;\n }\n /** When true, the launcher body shows the app-configuration view instead of the card grid */\n public ConfigMode = false;\n /** Pending action awaiting the inline unsaved-changes discard bar ('exit' = back to grid, 'close' = close launcher) */\n public PendingDiscard: 'exit' | 'close' | null = null;\n /** True when the panel renders as the compact anchored dropdown (few apps) */\n public CompactMode = false;\n /** All-apps sort: the user's configured order ('custom') or A–Z ('alpha').\n * Per-user preference, persisted via UserInfoEngine. */\n public SortMode: 'custom' | 'alpha' = 'custom';\n /** Anchor position for the compact panel (px, from the trigger's rect) */\n public AnchorLeft = 0;\n public AnchorTop = 0;\n\n /** Live filter text — matches app names AND Description summaries */\n public FilterText = '';\n\n private recentEntries: RecentAppEntry[] = [];\n\n constructor(private appManager: ApplicationManager, private cdr: ChangeDetectorRef) {\n super();\n }\n\n /**\n * UserInfoEngine scoped to this component's provider (multi-provider rule:\n * never reach for the global singleton in per-provider code paths). For the\n * default single-provider shell this resolves to the already-loaded engine\n * registered under the default connection. When no provider is available at\n * all (test environments), fall back to the global singleton rather than\n * letting GetProviderInstance construct a fresh unloaded engine.\n */\n private get userInfoEngine(): UserInfoEngine {\n const provider = this.ProviderToUse;\n return provider\n ? UserInfoEngine.GetProviderInstance<UserInfoEngine>(provider, UserInfoEngine) as UserInfoEngine\n : UserInfoEngine.Instance;\n }\n\n ngOnInit(): void {\n // The launcher's presentation (compact vs full, filter visibility) is\n // resolved from the app COUNT — which can change while the panel is open\n // (the user saves config changes that cross the auto threshold). React to\n // the same stream the app list uses so the presentation can't go stale.\n // Optional-chained: lightweight test stubs may not provide the observable.\n this.appManager.Applications?.pipe(takeUntil(this.destroy$)).subscribe(() => {\n if (this.ShowDropdown) {\n this.RefreshPresentation();\n }\n });\n }\n\n ngOnDestroy(): void {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n /**\n * Re-resolve the presentation for the CURRENT app count. No-op while the\n * config view is open (the panel is promoted to full size there anyway);\n * called when the app stream emits and when the config view exits.\n */\n RefreshPresentation(): void {\n if (this.ConfigMode) {\n return;\n }\n const wasCompact = this.CompactMode;\n this.CompactMode = this.resolveCompactMode();\n if (this.CompactMode && !wasCompact) {\n this.computeCompactAnchor();\n }\n // Mode flips can add/remove the filter input — if the focused element got\n // torn out of the DOM, focus would drop into the inert background. Re-home\n // it after the view settles.\n // Zoneless: an RxJS-driven mutation needs an explicit CD kick (same\n // pattern as AppNavComponent's workspace subscription).\n this.cdr.markForCheck();\n try {\n this.cdr.detectChanges();\n } catch {\n // Re-entrant CD — harmless.\n }\n if (wasCompact !== this.CompactMode) {\n requestAnimationFrame(() => {\n const panel = this.panelRef?.nativeElement;\n if (panel && !panel.contains(document.activeElement)) {\n if (this.ShowFilter) {\n this.filterInputRef?.nativeElement?.focus();\n } else {\n panel.querySelector<HTMLElement>('.app-card')?.focus();\n }\n }\n });\n }\n }\n\n /** Template handler for the filter input's input event */\n OnFilterInput(event: Event): void {\n this.FilterText = (event.target as HTMLInputElement).value;\n this.OnFilterChange();\n }\n\n /**\n * Check if the app switcher should show loading state\n */\n get isLoading(): boolean {\n return this.LoadingAppId !== null;\n }\n\n /**\n * Visible trigger label: the current app's name, or \"Apps\" when there's no\n * app context (system tabs, nothing active yet).\n */\n get TriggerLabel(): string {\n return this.ActiveApp && !this.IsViewingSystemTab ? this.ActiveApp.Name : 'Apps';\n }\n\n /** Accessible name for the trigger — names the control AND the current app */\n get TriggerAriaLabel(): string {\n return this.ActiveApp && !this.IsViewingSystemTab\n ? `Switch application — current: ${this.ActiveApp.Name}`\n : 'Switch application';\n }\n\n /**\n * Get applications that should appear in the app switcher\n * (NavigationStyle = 'App Switcher' or 'Both')\n */\n get Apps(): BaseApplication[] {\n return this.appManager.GetAppSwitcherApps();\n }\n\n /** @deprecated Use {@link Apps}. */\n get apps(): BaseApplication[] {\n return this.Apps;\n }\n\n /** Recently-used apps (persisted per user), newest first, capped for display.\n * Compact mode skips the Recent section — with a handful of apps it's noise. */\n get RecentApps(): BaseApplication[] {\n if (this.CompactMode) {\n return [];\n }\n const byId = new Map(this.Apps.map(a => [NormalizeUUID(a.ID), a]));\n const result: BaseApplication[] = [];\n for (const entry of this.recentEntries) {\n const app = byId.get(NormalizeUUID(entry.id));\n if (app) {\n result.push(app);\n if (result.length >= RECENT_DISPLAY_MAX) {\n break;\n }\n }\n }\n return this.applyFilter(result);\n }\n\n /** All switcher apps in the user's chosen sort (custom Sequence order or A–Z) */\n private get sortedApps(): BaseApplication[] {\n return this.SortMode === 'alpha'\n ? [...this.Apps].sort((a, b) => a.Name.localeCompare(b.Name))\n : this.Apps;\n }\n\n /** The complete \"All apps\" section, in the user's chosen sort. Apps shown in\n * Recent appear here TOO — being recently used must not make an app vanish\n * from its expected place in the full list. */\n get OtherApps(): BaseApplication[] {\n return this.applyFilter(this.sortedApps);\n }\n\n /** Flat visible list in RENDERED card order — the keyboard-navigation index\n * space. Filtering collapses the sections into one deduplicated list;\n * otherwise it's Recent followed by the full All-apps list (a recent app\n * intentionally appears twice, matching the two cards on screen). */\n get VisibleApps(): BaseApplication[] {\n if (this.IsFiltering) {\n return this.applyFilter(this.sortedApps);\n }\n return [...this.RecentApps, ...this.OtherApps];\n }\n\n /** Distinct app count for the screen-reader status (VisibleApps can contain\n * the same app twice when it's in both Recent and All apps) */\n get AnnouncedAppCount(): number {\n return this.IsFiltering ? this.VisibleApps.length : this.applyFilter(this.Apps).length;\n }\n\n /** True when a filter is active (sections collapse into one result list) */\n get IsFiltering(): boolean {\n return this.FilterText.trim().length > 0;\n }\n\n /** Compact mode hides the filter under a handful of apps — it's noise there */\n get ShowFilter(): boolean {\n return !this.CompactMode || this.Apps.length >= COMPACT_FILTER_THRESHOLD;\n }\n\n private applyFilter(apps: BaseApplication[]): BaseApplication[] {\n const q = this.FilterText.trim().toLowerCase();\n if (!q) {\n return apps;\n }\n return apps.filter(a =>\n a.Name.toLowerCase().includes(q) || (a.Description || '').toLowerCase().includes(q));\n }\n\n /** Stable DOM id for a card (arrow-key focus target) */\n OptionId(index: number): string {\n return `mj-launcher-opt-${index}`;\n }\n\n /** Trigger handler: open the launcher panel, or close it if already open */\n ToggleLauncher(): void {\n if (this.ShowDropdown) {\n this.closeLauncher();\n } else {\n this.openLauncher();\n }\n }\n\n /** Flip the All-apps sort and persist it (fail-silent, like recents) */\n ToggleSortMode(): void {\n this.SortMode = this.SortMode === 'alpha' ? 'custom' : 'alpha';\n try {\n this.userInfoEngine.SetSettingDebounced(LAUNCHER_SORT_KEY, this.SortMode);\n } catch {\n // Preference persistence must never break the launcher\n }\n }\n\n private loadSortMode(): void {\n try {\n this.SortMode = this.userInfoEngine.GetSetting(LAUNCHER_SORT_KEY) === 'alpha' ? 'alpha' : 'custom';\n } catch {\n this.SortMode = 'custom';\n }\n }\n\n private openLauncher(): void {\n this.loadRecents();\n this.loadSortMode();\n this.FilterText = '';\n this.ConfigMode = false;\n this.PendingDiscard = null;\n this.CompactMode = this.resolveCompactMode();\n if (this.CompactMode) {\n this.computeCompactAnchor();\n }\n this.ShowDropdown = true;\n // The dialog element renders under @if next frame: put it in the top\n // layer via showModal() (jsdom fallback: plain open attribute), then\n // focus the filter (or the first card when compact mode hides it).\n requestAnimationFrame(() => {\n const dialog = this.panelRef?.nativeElement;\n if (dialog && !dialog.open) {\n if (typeof dialog.showModal === 'function') {\n dialog.showModal();\n } else {\n dialog.setAttribute('open', '');\n }\n }\n if (this.ShowFilter) {\n this.filterInputRef?.nativeElement?.focus();\n } else {\n dialog?.querySelector<HTMLElement>('.app-card')?.focus();\n }\n });\n }\n\n /** Resolve the presentation for this open (auto = compact under the threshold) */\n private resolveCompactMode(): boolean {\n if (this.SwitcherStyle === 'compact') {\n return true;\n }\n if (this.SwitcherStyle === 'launcher') {\n return false;\n }\n return this.Apps.length < COMPACT_AUTO_THRESHOLD;\n }\n\n /** Anchor the compact panel under the trigger, clamped to the viewport */\n private computeCompactAnchor(): void {\n const rect = this.triggerRef?.nativeElement?.getBoundingClientRect();\n if (!rect) {\n return;\n }\n const panelWidth = 320; // Keep in sync with .launcher-panel--compact width\n const margin = 8;\n this.AnchorLeft = Math.max(margin, Math.min(rect.left, window.innerWidth - panelWidth - margin));\n this.AnchorTop = rect.bottom + margin;\n }\n\n private closeLauncher(restoreFocus = true): void {\n const dialog = this.panelRef?.nativeElement;\n if (dialog?.open && typeof dialog.close === 'function') {\n dialog.close(); // Browser restores focus to the trigger automatically\n }\n this.ShowDropdown = false;\n this.FilterText = '';\n this.ConfigMode = false;\n this.PendingDiscard = null;\n if (restoreFocus) {\n requestAnimationFrame(() => {\n this.triggerRef?.nativeElement?.focus();\n });\n }\n }\n\n /** Guarded close for template paths (backdrop / close button): unsaved\n * config changes surface the inline discard bar instead of closing. */\n CloseLauncher(): void {\n if (this.ConfigMode && this.configContent?.HasChanges()) {\n this.PendingDiscard = 'close';\n return;\n }\n this.closeLauncher();\n }\n\n /** Backdrop clicks arrive as clicks on the <dialog> element itself */\n OnDialogClick(event: MouseEvent): void {\n if (event.target === this.panelRef?.nativeElement) {\n this.CloseLauncher();\n }\n }\n\n /** Native cancel (Esc while the browser owns the dialog) — route through\n * our layered Escape handling instead of closing unconditionally. */\n OnDialogCancel(event: Event): void {\n event.preventDefault();\n this.handleEscape();\n }\n\n /** Inline discard bar: confirm — perform the pending action, dropping changes */\n ConfirmDiscard(): void {\n const action = this.PendingDiscard;\n this.PendingDiscard = null;\n if (action === 'close') {\n this.closeLauncher();\n } else if (action === 'exit') {\n this.ExitConfigMode();\n }\n }\n\n /** Inline discard bar: keep editing */\n CancelDiscard(): void {\n this.PendingDiscard = null;\n }\n\n OnFilterChange(): void {\n // Filtering re-renders the card set; nothing else to sync — focus stays\n // in the input and Tab/arrows reach the new result set naturally.\n }\n\n /** Keyboard model on the filter input */\n OnFilterKeydown(event: KeyboardEvent): void {\n const visible = this.VisibleApps;\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n this.focusCard(0);\n break;\n case 'Enter':\n event.preventDefault();\n // While filtering, Enter opens the top result (omnibar semantics —\n // the footer advertises \"↵ open\"); unfiltered it needs an unambiguous\n // single app.\n if (this.IsFiltering && visible.length > 0) {\n this.SelectApp(visible[0]);\n } else if (visible.length === 1) {\n this.SelectApp(visible[0]);\n }\n break;\n case 'Escape':\n event.preventDefault();\n // Stop the bubble: the panel's own Escape handler would otherwise\n // re-evaluate AFTER we've mutated state and double-handle the key.\n event.stopPropagation();\n this.handleEscape();\n break;\n }\n }\n\n /** Arrow-key navigation between focused cards */\n OnCardKeydown(index: number, event: KeyboardEvent): void {\n switch (event.key) {\n case 'ArrowDown':\n case 'ArrowRight':\n event.preventDefault();\n this.focusCard(index + 1);\n break;\n case 'ArrowUp':\n case 'ArrowLeft':\n event.preventDefault();\n if (index === 0) {\n this.filterInputRef?.nativeElement?.focus();\n } else {\n this.focusCard(index - 1);\n }\n break;\n case 'Home':\n event.preventDefault();\n this.focusCard(0);\n break;\n case 'End':\n event.preventDefault();\n this.focusCard(this.VisibleApps.length - 1);\n break;\n }\n }\n\n /** Move DOM focus to the card at the given VisibleApps index (clamped) */\n private focusCard(index: number): void {\n const count = this.VisibleApps.length;\n if (count === 0) {\n return;\n }\n const clamped = Math.min(count - 1, Math.max(0, index));\n const el = document.getElementById(this.OptionId(clamped));\n if (el) {\n el.focus();\n // jsdom (unit tests) doesn't implement scrollIntoView — guard it.\n if (typeof el.scrollIntoView === 'function') {\n el.scrollIntoView({ block: 'nearest' });\n }\n }\n }\n\n /** Keep Tab cycling within the launcher's real focusables (filter, close, Configure) */\n /** Escape layering: discard bar → config view (guarded) → filter → close.\n * Tab is NOT manually trapped — showModal() makes the background inert,\n * which is a real trap (no leak when focus sits on a non-focusable). */\n OnPanelKeydown(event: KeyboardEvent): void {\n if (event.key === 'Escape') {\n event.preventDefault();\n event.stopPropagation();\n this.handleEscape();\n }\n }\n\n private handleEscape(): void {\n if (this.PendingDiscard) {\n this.CancelDiscard();\n } else if (this.ConfigMode) {\n this.RequestExitConfigMode();\n } else if (this.IsFiltering) {\n this.FilterText = '';\n this.filterInputRef?.nativeElement?.focus();\n } else {\n this.closeLauncher();\n }\n }\n\n /**\n * Select an application.\n * When viewing a system tab, always emit to allow returning to the app.\n */\n SelectApp(app: BaseApplication): void {\n this.recordRecent(app);\n this.closeLauncher(false); // Focus moves into the app, not back to the trigger\n if (!UUIDsEqual(app.ID, this.ActiveApp?.ID) || this.IsViewingSystemTab) {\n this.AppSelected.emit(app.ID);\n }\n }\n\n /** @deprecated Use {@link SelectApp}. */\n selectApp(app: BaseApplication): void {\n return this.SelectApp(app);\n }\n\n /** Case-insensitive UUID check whether an app is the currently active app. */\n IsActiveApp(app: BaseApplication): boolean {\n return UUIDsEqual(app.ID, this.ActiveApp?.ID);\n }\n\n /** Case-insensitive UUID check whether an app is the one currently loading. */\n IsLoadingApp(app: BaseApplication): boolean {\n return UUIDsEqual(app.ID, this.LoadingAppId);\n }\n\n // ---- Recents persistence (MJ: User Settings via UserInfoEngine) ----\n\n private loadRecents(): void {\n try {\n const raw = this.userInfoEngine.GetSetting(RECENT_APPS_KEY);\n const parsed = raw ? JSON.parse(raw) : [];\n this.recentEntries = Array.isArray(parsed)\n ? parsed.filter((e: RecentAppEntry) => e && typeof e.id === 'string' && typeof e.ts === 'number')\n : [];\n } catch {\n this.recentEntries = [];\n }\n }\n\n private recordRecent(app: BaseApplication): void {\n const id = NormalizeUUID(app.ID);\n this.recentEntries = [\n { id, ts: Date.now() },\n ...this.recentEntries.filter(e => NormalizeUUID(e.id) !== id)\n ].slice(0, RECENT_STORE_MAX);\n try {\n this.userInfoEngine.SetSettingDebounced(RECENT_APPS_KEY, JSON.stringify(this.recentEntries));\n } catch {\n // Settings persistence must never block app switching (e.g. engine not\n // configured in tests or permission-constrained sessions).\n }\n }\n\n /**\n * Swap the launcher body to the app-configuration view (in-panel, seamless —\n * no separate dialog). The embedded mj-user-app-config-content loads on\n * creation.\n */\n EnterConfigMode(): void {\n this.ConfigMode = true;\n this.FilterText = '';\n this.PendingDiscard = null;\n // Move focus into the config view (the back button is its first focusable)\n requestAnimationFrame(() => {\n this.panelRef?.nativeElement\n ?.querySelector<HTMLElement>('.launcher-back')?.focus();\n });\n }\n\n /** Guarded exit from the config view: unsaved changes surface the inline\n * discard bar instead of silently dropping the user's edits. */\n RequestExitConfigMode(): void {\n if (this.configContent?.HasChanges()) {\n this.PendingDiscard = 'exit';\n return;\n }\n this.ExitConfigMode();\n }\n\n /**\n * Return from the configuration view to the app grid.\n * The app list refreshes reactively after a save: each UserApplication save\n * fires a BaseEntity event, UserInfoEngine's debounced refresh emits\n * DataChange$, and ApplicationManager.syncFromEngine() pushes the new list\n * to applications$, which the `apps` getter reads. No explicit reload needed.\n */\n ExitConfigMode(): void {\n this.ConfigMode = false;\n // The config session may have changed the app count across the compact\n // threshold — re-resolve the presentation for the view we're returning to.\n // (The Applications stream also triggers this when the post-save engine\n // refresh lands, whichever comes last wins with the freshest count.)\n this.RefreshPresentation();\n requestAnimationFrame(() => {\n if (this.ShowFilter) {\n this.filterInputRef?.nativeElement?.focus();\n } else {\n this.panelRef?.nativeElement?.querySelector<HTMLElement>('.app-card')?.focus();\n }\n });\n }\n}\n","<div class=\"app-switcher-container\" [class.loading]=\"isLoading\">\n <div class=\"app-switcher-button\"\n #trigger\n [mjClickable]=\"TriggerAriaLabel\"\n testId=\"app-switcher-trigger\"\n [attr.aria-haspopup]=\"'dialog'\"\n [attr.aria-expanded]=\"showDropdown\"\n (click)=\"ToggleLauncher()\">\n <!-- Show spinner when loading, otherwise show app icon -->\n @if (isLoading) {\n <i class=\"app-icon loading-spinner fa-solid fa-spinner fa-spin\" aria-hidden=\"true\"></i>\n }\n @if (!isLoading) {\n <i class=\"app-icon\" [class]=\"activeApp?.Icon || 'fa-solid fa-cube'\"\n [style.color]=\"activeApp?.GetColor()\" aria-hidden=\"true\"></i>\n }\n <!-- Current app name; hidden at the mobile breakpoint (icon-only trigger) -->\n <span class=\"app-label\">{{ TriggerLabel }}</span>\n <i class=\"dropdown-arrow fa-solid fa-chevron-down\" aria-hidden=\"true\"></i>\n </div>\n\n @if (showDropdown) {\n <!-- Native <dialog> + showModal(): lives in the browser TOP LAYER (above\n every app stacking context — chat FAB, toasts), background is inert\n (real focus trap), and focus restores to the trigger on close. The\n backdrop is ::backdrop; clicks on it arrive as clicks on the dialog\n element itself (OnDialogClick). -->\n <dialog class=\"launcher-panel\"\n #panel\n role=\"dialog\"\n aria-modal=\"true\"\n [class.launcher-panel--compact]=\"CompactMode && !ConfigMode\"\n [style.--anchor-left]=\"AnchorLeft + 'px'\"\n [style.--anchor-top]=\"AnchorTop + 'px'\"\n [attr.aria-label]=\"ConfigMode ? null : 'Application launcher'\"\n [attr.aria-labelledby]=\"ConfigMode ? 'mj-launcher-config-title' : null\"\n (click)=\"OnDialogClick($event)\"\n (cancel)=\"OnDialogCancel($event)\"\n (keydown)=\"OnPanelKeydown($event)\">\n\n @if (ConfigMode) {\n <!-- Configuration view: same panel, body swapped -->\n <div class=\"launcher-head launcher-head--config\">\n <button class=\"launcher-back\" type=\"button\" aria-label=\"Back to apps\" (click)=\"RequestExitConfigMode()\">\n <i class=\"fa-solid fa-arrow-left\" aria-hidden=\"true\"></i>\n </button>\n <h2 class=\"launcher-title\" id=\"mj-launcher-config-title\">Configure apps</h2>\n <button class=\"launcher-close\" type=\"button\" aria-label=\"Close application launcher\" (click)=\"CloseLauncher()\">\n <i class=\"fa-solid fa-xmark\" aria-hidden=\"true\"></i>\n </button>\n </div>\n @if (PendingDiscard) {\n <div class=\"launcher-discard-bar\" role=\"alert\">\n <i class=\"fa-solid fa-triangle-exclamation\" aria-hidden=\"true\"></i>\n <span class=\"discard-msg\">You have unsaved changes.</span>\n <button mjButton variant=\"danger\" size=\"sm\" (click)=\"ConfirmDiscard()\">Discard</button>\n <button mjButton size=\"sm\" (click)=\"CancelDiscard()\">Keep editing</button>\n </div>\n }\n <mj-user-app-config-content\n class=\"launcher-config-body\"\n [Provider]=\"Provider\"\n (Saved)=\"ExitConfigMode()\"\n (Cancelled)=\"RequestExitConfigMode()\">\n </mj-user-app-config-content>\n } @else {\n @if (ShowFilter) {\n <div class=\"launcher-head\">\n <div class=\"launcher-filter\">\n <i class=\"fa-solid fa-magnifying-glass\" aria-hidden=\"true\"></i>\n <input\n #filterInput\n type=\"text\"\n placeholder=\"Find an app…\"\n aria-label=\"Find an app\"\n [value]=\"FilterText\"\n (input)=\"OnFilterInput($event)\"\n (keydown)=\"OnFilterKeydown($event)\"\n autocomplete=\"off\"\n spellcheck=\"false\" />\n </div>\n <button class=\"launcher-close\" type=\"button\" aria-label=\"Close application launcher\" (click)=\"CloseLauncher()\">\n <i class=\"fa-solid fa-xmark\" aria-hidden=\"true\"></i>\n </button>\n </div>\n }\n\n <div class=\"launcher-scroll\">\n @if (IsFiltering) {\n <div class=\"launcher-grid\">\n @for (app of VisibleApps; track app.ID; let i = $index) {\n <ng-container *ngTemplateOutlet=\"appCard; context: { $implicit: app, idx: i }\"></ng-container>\n }\n </div>\n @if (VisibleApps.length === 0) {\n <div class=\"launcher-empty\">No apps match \"{{ FilterText }}\"</div>\n }\n } @else {\n @if (RecentApps.length > 0) {\n <div class=\"launcher-sect\">Recent</div>\n <div class=\"launcher-grid\" role=\"group\" aria-label=\"Recent applications\">\n @for (app of RecentApps; track app.ID; let i = $index) {\n <ng-container *ngTemplateOutlet=\"appCard; context: { $implicit: app, idx: i }\"></ng-container>\n }\n </div>\n }\n <div class=\"launcher-sect launcher-sect--row\">\n <span>All apps</span>\n <button class=\"launcher-sort-toggle\" type=\"button\"\n [attr.aria-pressed]=\"SortMode === 'alpha'\"\n [attr.aria-label]=\"SortMode === 'alpha' ? 'Sorted alphabetically — switch to your custom order' : 'Sorted by your custom order — switch to alphabetical'\"\n [title]=\"SortMode === 'alpha' ? 'Sorted A–Z — click for your custom order' : 'Custom order — click for A–Z'\"\n (click)=\"ToggleSortMode()\">\n <i [class]=\"SortMode === 'alpha' ? 'fa-solid fa-arrow-down-a-z' : 'fa-solid fa-bars-staggered'\" aria-hidden=\"true\"></i>\n </button>\n </div>\n <div class=\"launcher-grid\" role=\"group\" aria-label=\"All applications\">\n @for (app of OtherApps; track app.ID; let i = $index) {\n <ng-container *ngTemplateOutlet=\"appCard; context: { $implicit: app, idx: i + RecentApps.length }\"></ng-container>\n }\n </div>\n @if (VisibleApps.length === 0) {\n <div class=\"launcher-empty\">\n No applications are enabled for you yet — use Configure… below to add some.\n </div>\n }\n }\n </div>\n\n <div class=\"launcher-foot\">\n <span class=\"launcher-kbd-hints\" aria-hidden=\"true\">\n <span><kbd>↑</kbd><kbd>↓</kbd> navigate</span>\n <span><kbd>↵</kbd> open</span>\n <span><kbd>esc</kbd> close</span>\n </span>\n <button class=\"launcher-configure\" type=\"button\" testId=\"app-switcher-configure\" (click)=\"EnterConfigMode()\">\n <i class=\"fa-solid fa-gear\" aria-hidden=\"true\"></i>\n <span>Configure…</span>\n </button>\n </div>\n\n <span class=\"sr-only\" role=\"status\">{{ AnnouncedAppCount }} applications available</span>\n }\n </dialog>\n }\n</div>\n\n<!-- Shared app-card template: each card is a real focusable control (mjClickable\n adds role, tabindex 0, aria-label, and Enter/Space activation), so Tab walks\n the cards and arrow keys are the fast path. -->\n<ng-template #appCard let-app let-idx=\"idx\">\n <div class=\"app-card\"\n [id]=\"OptionId(idx)\"\n [mjClickable]=\"app.Name\"\n role=\"link\"\n [testId]=\"'app-switcher-item-' + app.Name\"\n [attr.aria-current]=\"IsActiveApp(app) ? 'true' : null\"\n [attr.aria-describedby]=\"app.Description ? OptionId(idx) + '-desc' : null\"\n [class.active]=\"IsActiveApp(app)\"\n [class.loading]=\"IsLoadingApp(app)\"\n [style.--app-color]=\"app.GetColor()\"\n (click)=\"selectApp(app)\"\n (keydown)=\"OnCardKeydown(idx, $event)\">\n <div class=\"app-card-glyph\">\n @if (IsLoadingApp(app)) {\n <i class=\"fa-solid fa-spinner fa-spin\" aria-hidden=\"true\"></i>\n } @else {\n <i [class]=\"app.Icon || 'fa-solid fa-cube'\" aria-hidden=\"true\"></i>\n }\n </div>\n <div class=\"app-card-text\">\n <div class=\"app-card-name\">\n <span class=\"app-card-name-label\">{{ app.Name }}</span>\n </div>\n @if (app.Description) {\n <div class=\"app-card-desc\" [id]=\"OptionId(idx) + '-desc'\">{{ app.Description }}</div>\n }\n </div>\n </div>\n</ng-template>\n\n"]}
@@ -13,9 +13,15 @@ import * as i0 from "@angular/core";
13
13
  * (fresh loads, cache reattaches after promote/demote, single-resource).
14
14
  */
15
15
  export declare class RecordOriginCrumbComponent {
16
- Origin: RecordSourceContext | null;
16
+ private _origin;
17
+ asyncResolvedLabel?: string;
18
+ set Origin(value: RecordSourceContext | null);
19
+ get Origin(): RecordSourceContext | null;
17
20
  private navigationService;
21
+ private cdr;
18
22
  get Clickable(): boolean;
23
+ get DisplayLabel(): string | undefined;
24
+ private resolveAsyncLabel;
19
25
  /** Page segment: full restore — the captured page, section state included */
20
26
  OnPageClick(): Promise<void>;
21
27
  /** App segment: the app's LANDING page (breadcrumb level semantics) */
@@ -1 +1 @@
1
- {"version":3,"file":"record-origin-crumb.component.d.ts","sourceRoot":"","sources":["../../../../../src/lib/shell/components/record-open/record-origin-crumb.component.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,mBAAmB,EAA+B,MAAM,2BAA2B,CAAC;;AAEhH;;;;;;;;;;;GAWG;AACH,qBA8Fa,0BAA0B;IAC5B,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAAQ;IAEnD,OAAO,CAAC,iBAAiB,CAA6B;IAEtD,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,6EAA6E;IACvE,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAMlC,uEAAuE;IACjE,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;yCAjBtB,0BAA0B;2CAA1B,0BAA0B;CAsBtC"}
1
+ {"version":3,"file":"record-origin-crumb.component.d.ts","sourceRoot":"","sources":["../../../../../src/lib/shell/components/record-open/record-origin-crumb.component.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,mBAAmB,EAA+B,MAAM,2BAA2B,CAAC;;AAGhH;;;;;;;;;;;GAWG;AACH,qBA8Fa,0BAA0B;IACrC,OAAO,CAAC,OAAO,CAAoC;IAC5C,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAEnC,IACI,MAAM,CAAC,KAAK,EAAE,mBAAmB,GAAG,IAAI,EAI3C;IACD,IAAI,MAAM,IAAI,mBAAmB,GAAG,IAAI,CAEvC;IAED,OAAO,CAAC,iBAAiB,CAA6B;IACtD,OAAO,CAAC,GAAG,CAA6B;IAExC,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,IAAI,YAAY,IAAI,MAAM,GAAG,SAAS,CAmBrC;IAED,OAAO,CAAC,iBAAiB;IAwBzB,6EAA6E;IACvE,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAMlC,uEAAuE;IACjE,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;yCA1EtB,0BAA0B;2CAA1B,0BAA0B;CA+EtC"}