@memberjunction/ng-explorer-core 6.2.0-edge.0 → 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 (178) 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 +50 -14
  4. package/dist/app-routing.module.js.map +1 -1
  5. package/dist/lib/command-palette/command-palette.component.d.ts +7 -1
  6. package/dist/lib/command-palette/command-palette.component.d.ts.map +1 -1
  7. package/dist/lib/command-palette/command-palette.component.js +28 -14
  8. package/dist/lib/command-palette/command-palette.component.js.map +1 -1
  9. package/dist/lib/conversation-feedback/conversation-feedback.d.ts +103 -13
  10. package/dist/lib/conversation-feedback/conversation-feedback.d.ts.map +1 -1
  11. package/dist/lib/conversation-feedback/conversation-feedback.js +219 -67
  12. package/dist/lib/conversation-feedback/conversation-feedback.js.map +1 -1
  13. package/dist/lib/dashboard-preferences-dialog/dashboard-preferences-dialog.component.d.ts +64 -10
  14. package/dist/lib/dashboard-preferences-dialog/dashboard-preferences-dialog.component.d.ts.map +1 -1
  15. package/dist/lib/dashboard-preferences-dialog/dashboard-preferences-dialog.component.js +172 -66
  16. package/dist/lib/dashboard-preferences-dialog/dashboard-preferences-dialog.component.js.map +1 -1
  17. package/dist/lib/generic/resource-container-component.d.ts +19 -5
  18. package/dist/lib/generic/resource-container-component.d.ts.map +1 -1
  19. package/dist/lib/generic/resource-container-component.js +43 -13
  20. package/dist/lib/generic/resource-container-component.js.map +1 -1
  21. package/dist/lib/guards/entities.guard.d.ts +4 -0
  22. package/dist/lib/guards/entities.guard.d.ts.map +1 -1
  23. package/dist/lib/guards/entities.guard.js +10 -2
  24. package/dist/lib/guards/entities.guard.js.map +1 -1
  25. package/dist/lib/identity-claims/claim-redeem.component.d.ts +4 -0
  26. package/dist/lib/identity-claims/claim-redeem.component.d.ts.map +1 -1
  27. package/dist/lib/identity-claims/claim-redeem.component.js +10 -2
  28. package/dist/lib/identity-claims/claim-redeem.component.js.map +1 -1
  29. package/dist/lib/oauth/oauth-callback.component.d.ts +4 -0
  30. package/dist/lib/oauth/oauth-callback.component.d.ts.map +1 -1
  31. package/dist/lib/oauth/oauth-callback.component.js +10 -2
  32. package/dist/lib/oauth/oauth-callback.component.js.map +1 -1
  33. package/dist/lib/profile/profile-dialog.component.d.ts.map +1 -1
  34. package/dist/lib/profile/profile-dialog.component.js +194 -191
  35. package/dist/lib/profile/profile-dialog.component.js.map +1 -1
  36. package/dist/lib/resource-wrappers/artifact-resource.component.d.ts +20 -2
  37. package/dist/lib/resource-wrappers/artifact-resource.component.d.ts.map +1 -1
  38. package/dist/lib/resource-wrappers/artifact-resource.component.js +33 -9
  39. package/dist/lib/resource-wrappers/artifact-resource.component.js.map +1 -1
  40. package/dist/lib/resource-wrappers/chat-collections-resource.component.d.ts +80 -10
  41. package/dist/lib/resource-wrappers/chat-collections-resource.component.d.ts.map +1 -1
  42. package/dist/lib/resource-wrappers/chat-collections-resource.component.js +168 -56
  43. package/dist/lib/resource-wrappers/chat-collections-resource.component.js.map +1 -1
  44. package/dist/lib/resource-wrappers/chat-conversations-resource.component.d.ts +186 -23
  45. package/dist/lib/resource-wrappers/chat-conversations-resource.component.d.ts.map +1 -1
  46. package/dist/lib/resource-wrappers/chat-conversations-resource.component.js +500 -177
  47. package/dist/lib/resource-wrappers/chat-conversations-resource.component.js.map +1 -1
  48. package/dist/lib/resource-wrappers/chat-deeplink-params.d.ts +2 -0
  49. package/dist/lib/resource-wrappers/chat-deeplink-params.d.ts.map +1 -1
  50. package/dist/lib/resource-wrappers/chat-deeplink-params.js +5 -1
  51. package/dist/lib/resource-wrappers/chat-deeplink-params.js.map +1 -1
  52. package/dist/lib/resource-wrappers/chat-search-routing.d.ts +25 -0
  53. package/dist/lib/resource-wrappers/chat-search-routing.d.ts.map +1 -0
  54. package/dist/lib/resource-wrappers/chat-search-routing.js +24 -0
  55. package/dist/lib/resource-wrappers/chat-search-routing.js.map +1 -0
  56. package/dist/lib/resource-wrappers/chat-tasks-resource.component.d.ts +14 -2
  57. package/dist/lib/resource-wrappers/chat-tasks-resource.component.d.ts.map +1 -1
  58. package/dist/lib/resource-wrappers/chat-tasks-resource.component.js +32 -8
  59. package/dist/lib/resource-wrappers/chat-tasks-resource.component.js.map +1 -1
  60. package/dist/lib/resource-wrappers/dashboard-resource.component.d.ts +54 -8
  61. package/dist/lib/resource-wrappers/dashboard-resource.component.d.ts.map +1 -1
  62. package/dist/lib/resource-wrappers/dashboard-resource.component.js +138 -46
  63. package/dist/lib/resource-wrappers/dashboard-resource.component.js.map +1 -1
  64. package/dist/lib/resource-wrappers/livekit-room-resource.component.d.ts +223 -30
  65. package/dist/lib/resource-wrappers/livekit-room-resource.component.d.ts.map +1 -1
  66. package/dist/lib/resource-wrappers/livekit-room-resource.component.js +438 -118
  67. package/dist/lib/resource-wrappers/livekit-room-resource.component.js.map +1 -1
  68. package/dist/lib/resource-wrappers/search-results-resource.component.d.ts +7 -3
  69. package/dist/lib/resource-wrappers/search-results-resource.component.d.ts.map +1 -1
  70. package/dist/lib/resource-wrappers/search-results-resource.component.js +23 -15
  71. package/dist/lib/resource-wrappers/search-results-resource.component.js.map +1 -1
  72. package/dist/lib/resource-wrappers/view-resource.component.d.ts +68 -10
  73. package/dist/lib/resource-wrappers/view-resource.component.d.ts.map +1 -1
  74. package/dist/lib/resource-wrappers/view-resource.component.js +178 -62
  75. package/dist/lib/resource-wrappers/view-resource.component.js.map +1 -1
  76. package/dist/lib/services/startup-validation.service.d.ts +4 -0
  77. package/dist/lib/services/startup-validation.service.d.ts.map +1 -1
  78. package/dist/lib/services/startup-validation.service.js +10 -2
  79. package/dist/lib/services/startup-validation.service.js.map +1 -1
  80. package/dist/lib/services/system-validation.service.d.ts +13 -1
  81. package/dist/lib/services/system-validation.service.d.ts.map +1 -1
  82. package/dist/lib/services/system-validation.service.js +29 -5
  83. package/dist/lib/services/system-validation.service.js.map +1 -1
  84. package/dist/lib/shell/components/dialogs/app-access-dialog.component.d.ts +54 -4
  85. package/dist/lib/shell/components/dialogs/app-access-dialog.component.d.ts.map +1 -1
  86. package/dist/lib/shell/components/dialogs/app-access-dialog.component.js +130 -40
  87. package/dist/lib/shell/components/dialogs/app-access-dialog.component.js.map +1 -1
  88. package/dist/lib/shell/components/header/app-nav.component.d.ts +29 -1
  89. package/dist/lib/shell/components/header/app-nav.component.d.ts.map +1 -1
  90. package/dist/lib/shell/components/header/app-nav.component.js +56 -12
  91. package/dist/lib/shell/components/header/app-nav.component.js.map +1 -1
  92. package/dist/lib/shell/components/header/app-switcher.component.d.ts +33 -5
  93. package/dist/lib/shell/components/header/app-switcher.component.d.ts.map +1 -1
  94. package/dist/lib/shell/components/header/app-switcher.component.js +85 -29
  95. package/dist/lib/shell/components/header/app-switcher.component.js.map +1 -1
  96. package/dist/lib/shell/components/record-open/record-origin-crumb.component.js +1 -1
  97. package/dist/lib/shell/components/record-open/record-origin-crumb.component.js.map +1 -1
  98. package/dist/lib/shell/components/tabs/component-cache-manager.d.ts +50 -6
  99. package/dist/lib/shell/components/tabs/component-cache-manager.d.ts.map +1 -1
  100. package/dist/lib/shell/components/tabs/component-cache-manager.js +95 -22
  101. package/dist/lib/shell/components/tabs/component-cache-manager.js.map +1 -1
  102. package/dist/lib/shell/components/tabs/tab-container.component.d.ts +91 -9
  103. package/dist/lib/shell/components/tabs/tab-container.component.d.ts.map +1 -1
  104. package/dist/lib/shell/components/tabs/tab-container.component.js +257 -101
  105. package/dist/lib/shell/components/tabs/tab-container.component.js.map +1 -1
  106. package/dist/lib/shell/loading-themes.d.ts +16 -12
  107. package/dist/lib/shell/loading-themes.d.ts.map +1 -1
  108. package/dist/lib/shell/loading-themes.js +145 -137
  109. package/dist/lib/shell/loading-themes.js.map +1 -1
  110. package/dist/lib/shell/readiness-beacon.d.ts +2 -0
  111. package/dist/lib/shell/readiness-beacon.d.ts.map +1 -1
  112. package/dist/lib/shell/readiness-beacon.js +5 -1
  113. package/dist/lib/shell/readiness-beacon.js.map +1 -1
  114. package/dist/lib/shell/services/about-dialog.service.d.ts +6 -0
  115. package/dist/lib/shell/services/about-dialog.service.d.ts.map +1 -1
  116. package/dist/lib/shell/services/about-dialog.service.js +16 -4
  117. package/dist/lib/shell/services/about-dialog.service.js.map +1 -1
  118. package/dist/lib/shell/services/profile-dialog.service.d.ts +6 -0
  119. package/dist/lib/shell/services/profile-dialog.service.d.ts.map +1 -1
  120. package/dist/lib/shell/services/profile-dialog.service.js +16 -4
  121. package/dist/lib/shell/services/profile-dialog.service.js.map +1 -1
  122. package/dist/lib/shell/services/user-sharing-center-dialog.service.d.ts +6 -0
  123. package/dist/lib/shell/services/user-sharing-center-dialog.service.d.ts.map +1 -1
  124. package/dist/lib/shell/services/user-sharing-center-dialog.service.js +15 -3
  125. package/dist/lib/shell/services/user-sharing-center-dialog.service.js.map +1 -1
  126. package/dist/lib/shell/shell.component.d.ts +262 -39
  127. package/dist/lib/shell/shell.component.d.ts.map +1 -1
  128. package/dist/lib/shell/shell.component.js +688 -292
  129. package/dist/lib/shell/shell.component.js.map +1 -1
  130. package/dist/lib/single-dashboard/Components/add-item/add-item.component.d.ts +64 -8
  131. package/dist/lib/single-dashboard/Components/add-item/add-item.component.d.ts.map +1 -1
  132. package/dist/lib/single-dashboard/Components/add-item/add-item.component.js +129 -37
  133. package/dist/lib/single-dashboard/Components/add-item/add-item.component.js.map +1 -1
  134. package/dist/lib/single-dashboard/Components/delete-item/delete-item.component.d.ts +26 -2
  135. package/dist/lib/single-dashboard/Components/delete-item/delete-item.component.d.ts.map +1 -1
  136. package/dist/lib/single-dashboard/Components/delete-item/delete-item.component.js +48 -10
  137. package/dist/lib/single-dashboard/Components/delete-item/delete-item.component.js.map +1 -1
  138. package/dist/lib/single-dashboard/Components/edit-dashboard/edit-dashboard.component.d.ts +84 -6
  139. package/dist/lib/single-dashboard/Components/edit-dashboard/edit-dashboard.component.d.ts.map +1 -1
  140. package/dist/lib/single-dashboard/Components/edit-dashboard/edit-dashboard.component.js +133 -29
  141. package/dist/lib/single-dashboard/Components/edit-dashboard/edit-dashboard.component.js.map +1 -1
  142. package/dist/lib/single-dashboard/single-dashboard.component.d.ts +173 -23
  143. package/dist/lib/single-dashboard/single-dashboard.component.d.ts.map +1 -1
  144. package/dist/lib/single-dashboard/single-dashboard.component.js +383 -109
  145. package/dist/lib/single-dashboard/single-dashboard.component.js.map +1 -1
  146. package/dist/lib/single-list-detail/single-list-detail.component.d.ts +383 -55
  147. package/dist/lib/single-list-detail/single-list-detail.component.d.ts.map +1 -1
  148. package/dist/lib/single-list-detail/single-list-detail.component.js +962 -326
  149. package/dist/lib/single-list-detail/single-list-detail.component.js.map +1 -1
  150. package/dist/lib/single-query/single-query.component.d.ts +22 -2
  151. package/dist/lib/single-query/single-query.component.d.ts.map +1 -1
  152. package/dist/lib/single-query/single-query.component.js +40 -10
  153. package/dist/lib/single-query/single-query.component.js.map +1 -1
  154. package/dist/lib/single-record/single-record.component.d.ts +41 -3
  155. package/dist/lib/single-record/single-record.component.d.ts.map +1 -1
  156. package/dist/lib/single-record/single-record.component.js +71 -11
  157. package/dist/lib/single-record/single-record.component.js.map +1 -1
  158. package/dist/lib/single-search-result/single-search-result.component.d.ts +23 -3
  159. package/dist/lib/single-search-result/single-search-result.component.d.ts.map +1 -1
  160. package/dist/lib/single-search-result/single-search-result.component.js +42 -12
  161. package/dist/lib/single-search-result/single-search-result.component.js.map +1 -1
  162. package/dist/lib/system-validation/system-validation-banner.component.d.ts +12 -2
  163. package/dist/lib/system-validation/system-validation-banner.component.d.ts.map +1 -1
  164. package/dist/lib/system-validation/system-validation-banner.component.js +25 -5
  165. package/dist/lib/system-validation/system-validation-banner.component.js.map +1 -1
  166. package/dist/lib/user-menu/user-menu.types.d.ts +4 -0
  167. package/dist/lib/user-menu/user-menu.types.d.ts.map +1 -1
  168. package/dist/lib/user-menu/user-menu.types.js +10 -2
  169. package/dist/lib/user-menu/user-menu.types.js.map +1 -1
  170. package/dist/lib/user-notifications/user-notifications.component.d.ts +59 -7
  171. package/dist/lib/user-notifications/user-notifications.component.d.ts.map +1 -1
  172. package/dist/lib/user-notifications/user-notifications.component.js +151 -47
  173. package/dist/lib/user-notifications/user-notifications.component.js.map +1 -1
  174. package/dist/lib/user-profile/user-profile.component.d.ts +3 -2
  175. package/dist/lib/user-profile/user-profile.component.d.ts.map +1 -1
  176. package/dist/lib/user-profile/user-profile.component.js +5 -4
  177. package/dist/lib/user-profile/user-profile.component.js.map +1 -1
  178. package/package.json +52 -52
@@ -1 +1 @@
1
- {"version":3,"file":"record-origin-crumb.component.js","sourceRoot":"","sources":["../../../../../src/lib/shell/components/record-open/record-origin-crumb.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, inject, ChangeDetectorRef } from '@angular/core';\nimport { NavigationService, RecordSourceContext, RecordSourceHasReturnTarget } from '@memberjunction/ng-shared';\nimport { CompositeKey, Metadata } from '@memberjunction/core';\n\n/**\n * Pane-level origin crumb — the FIRST element inside a record's golden-layout\n * pane (Matt's placement call, 2026-07-30: no record-chrome refactor; the\n * crumb rides the pane). One instance per record pane, which makes it correct\n * in every layout context the region-level bar could not be:\n * - split views: each pane carries its OWN origin\n * - docked records (main layout, bar or single-resource): provenance intact\n * - edit mode / custom form headers: unaffected — the crumb sits above the\n * form entirely, in shell-owned territory\n * Rendered/positioned by the tab-container wherever record content attaches\n * (fresh loads, cache reattaches after promote/demote, single-resource).\n */\n@Component({\n selector: 'mj-record-origin-crumb',\n standalone: true,\n // The crumb sits INSIDE a GL pane: without these stoppers, clicks bubble\n // to Golden Layout's pane-focus handlers, which re-activate the pane's\n // own tab and STOMP the navigation the click just started (the app\n // segment created the destination tab and then lost the activation race).\n host: {\n '(mousedown)': '$event.stopPropagation()',\n '(click)': '$event.stopPropagation()'\n },\n template: `\n @if (Origin) {\n @if (Clickable) {\n <i class=\"fa-solid fa-arrow-left crumb-lead\" aria-hidden=\"true\"></i>\n @if (DisplayLabel) {\n <button type=\"button\" class=\"crumb-seg\" (click)=\"OnPageClick()\" title=\"Back to where you opened this record\">\n {{ DisplayLabel }}\n </button>\n } @else {\n <button type=\"button\" class=\"crumb-seg\" (click)=\"OnAppClick()\" [title]=\"'Go to ' + Origin.sourceAppName\">\n {{ Origin.sourceAppName }}\n </button>\n @if (Origin.sourceNavLabel) {\n <span class=\"crumb-sep\" aria-hidden=\"true\">›</span>\n <button type=\"button\" class=\"crumb-seg crumb-page\" (click)=\"OnPageClick()\" title=\"Back to where you opened this record\">\n {{ Origin.sourceNavLabel }}\n </button>\n }\n }\n } @else {\n <!-- Provenance only — no return target (e.g. opened by an agent) -->\n <span class=\"origin-static\">From {{ DisplayLabel || Origin.sourceLabel }}</span>\n }\n }\n `,\n styles: [`\n :host {\n display: flex;\n align-items: center;\n gap: 2px;\n flex-shrink: 0;\n height: 36px;\n /* Left indent deliberately DEEPER than the first tab's edge (~16px)\n so the crumb reads as a level BENEATH the tab — hierarchy, not a\n sibling bar. */\n padding: 0 12px 0 24px;\n /* Match the ACTIVE tab's surface so the tab flows into its pane\n (the sunken tone read as a disconnected band between them). */\n background: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n }\n :host:not(:has(*)) { display: none; }\n .crumb-lead {\n font-size: 11px;\n color: var(--mj-text-muted);\n margin-right: 6px;\n }\n /* Each breadcrumb level is its OWN link with its own hover */\n .crumb-seg {\n display: inline-flex;\n align-items: center;\n max-width: 100%;\n padding: 5px 8px;\n border: none;\n border-radius: 4px;\n background: transparent;\n font-size: 12px;\n line-height: 1;\n font-family: inherit;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: background 0.15s, color 0.15s;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n .crumb-seg:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n }\n .crumb-seg:focus-visible {\n outline: 2px solid var(--mj-border-focus);\n outline-offset: -2px;\n }\n .crumb-page { font-weight: 500; }\n .crumb-sep { color: var(--mj-text-muted); font-size: 12px; }\n .origin-static {\n padding: 5px 8px;\n font-size: 12px;\n color: var(--mj-text-muted);\n }\n `]\n})\nexport class RecordOriginCrumbComponent {\n private _origin: RecordSourceContext | null = null;\n public asyncResolvedLabel?: string;\n\n @Input()\n set Origin(value: RecordSourceContext | null) {\n this._origin = value;\n this.asyncResolvedLabel = undefined;\n this.resolveAsyncLabel();\n }\n get Origin(): RecordSourceContext | null {\n return this._origin;\n }\n\n private navigationService = inject(NavigationService);\n private cdr = inject(ChangeDetectorRef);\n\n get Clickable(): boolean {\n return RecordSourceHasReturnTarget(this.Origin);\n }\n\n get DisplayLabel(): string | undefined {\n if (this.Origin?.sourceRecordEntity && this.Origin?.sourceRecordId) {\n if (this.asyncResolvedLabel) {\n return this.asyncResolvedLabel;\n }\n const md = Metadata.Provider; // global-provider-ok: shell breadcrumb component displays navigation history using global metadata cache\n const entity = md?.EntityByName(this.Origin.sourceRecordEntity);\n const key = CompositeKey.FromURLSegment(entity, this.Origin.sourceRecordId);\n const cached = md ? md.GetCachedRecordNameOnlyIfCached(this.Origin.sourceRecordEntity, key) : undefined;\n if (cached) {\n return cached;\n }\n const fallbackEntityLabel = entity?.DisplayName || entity?.Name || this.Origin.sourceRecordEntity;\n if (this.Origin.sourceLabel && !this.Origin.sourceLabel.includes(this.Origin.sourceRecordId)) {\n return this.Origin.sourceLabel;\n }\n return fallbackEntityLabel;\n }\n return this.Origin?.sourceLabel;\n }\n\n private resolveAsyncLabel(): void {\n const md = Metadata.Provider; // global-provider-ok: shell breadcrumb component asynchronously resolves record name using global metadata provider\n if (this.Origin?.sourceRecordEntity && this.Origin?.sourceRecordId && md) {\n const entity = md.EntityByName(this.Origin.sourceRecordEntity);\n const key = CompositeKey.FromURLSegment(entity, this.Origin.sourceRecordId);\n const cached = md.GetCachedRecordNameOnlyIfCached(this.Origin.sourceRecordEntity, key);\n if (cached) {\n this.asyncResolvedLabel = cached;\n return;\n }\n const entityName = this.Origin.sourceRecordEntity;\n if (typeof md.GetEntityRecordName === 'function') {\n md.GetEntityRecordName(entityName, key).then(name => {\n if (name && this.Origin?.sourceRecordEntity === entityName) {\n this.asyncResolvedLabel = name;\n this.cdr.markForCheck();\n }\n }).catch(() => {\n // Silently ignore async name fetch error, fallback will remain active\n });\n }\n }\n }\n\n /** Page segment: full restore — the captured page, section state included */\n async OnPageClick(): Promise<void> {\n if (this.Origin) {\n await this.navigationService.ReturnToRecordSource(this.Origin);\n }\n }\n\n /** App segment: the app's LANDING page (breadcrumb level semantics) */\n async OnAppClick(): Promise<void> {\n if (this.Origin?.sourceAppId) {\n await this.navigationService.SwitchToAppHome(this.Origin.sourceAppId);\n }\n }\n}\n"]}
1
+ {"version":3,"file":"record-origin-crumb.component.js","sourceRoot":"","sources":["../../../../../src/lib/shell/components/record-open/record-origin-crumb.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, inject, ChangeDetectorRef } from '@angular/core';\nimport { NavigationService, RecordSourceContext, RecordSourceHasReturnTarget } from '@memberjunction/ng-shared';\nimport { CompositeKey, Metadata } from '@memberjunction/core';\n\n/**\n * Pane-level origin crumb — the FIRST element inside a record's golden-layout\n * pane (Matt's placement call, 2026-07-30: no record-chrome refactor; the\n * crumb rides the pane). One instance per record pane, which makes it correct\n * in every layout context the region-level bar could not be:\n * - split views: each pane carries its OWN origin\n * - docked records (main layout, bar or single-resource): provenance intact\n * - edit mode / custom form headers: unaffected — the crumb sits above the\n * form entirely, in shell-owned territory\n * Rendered/positioned by the tab-container wherever record content attaches\n * (fresh loads, cache reattaches after promote/demote, single-resource).\n */\n@Component({\n selector: 'mj-record-origin-crumb',\n standalone: true,\n // The crumb sits INSIDE a GL pane: without these stoppers, clicks bubble\n // to Golden Layout's pane-focus handlers, which re-activate the pane's\n // own tab and STOMP the navigation the click just started (the app\n // segment created the destination tab and then lost the activation race).\n host: {\n '(mousedown)': '$event.stopPropagation()',\n '(click)': '$event.stopPropagation()'\n },\n template: `\n @if (Origin) {\n @if (Clickable) {\n <i class=\"fa-solid fa-arrow-left crumb-lead\" aria-hidden=\"true\"></i>\n @if (DisplayLabel) {\n <button type=\"button\" class=\"crumb-seg\" (click)=\"OnPageClick()\" title=\"Back to where you opened this record\">\n {{ DisplayLabel }}\n </button>\n } @else {\n <button type=\"button\" class=\"crumb-seg\" (click)=\"OnAppClick()\" [title]=\"'Go to ' + Origin.sourceAppName\">\n {{ Origin.sourceAppName }}\n </button>\n @if (Origin.sourceNavLabel) {\n <span class=\"crumb-sep\" aria-hidden=\"true\">›</span>\n <button type=\"button\" class=\"crumb-seg crumb-page\" (click)=\"OnPageClick()\" title=\"Back to where you opened this record\">\n {{ Origin.sourceNavLabel }}\n </button>\n }\n }\n } @else {\n <!-- Provenance only — no return target (e.g. opened by an agent) -->\n <span class=\"origin-static\">From {{ DisplayLabel || Origin.sourceLabel }}</span>\n }\n }\n `,\n styles: [`\n :host {\n display: flex;\n align-items: center;\n gap: 2px;\n flex-shrink: 0;\n height: 36px;\n /* Left indent deliberately DEEPER than the first tab's edge (~16px)\n so the crumb reads as a level BENEATH the tab — hierarchy, not a\n sibling bar. */\n padding: 0 12px 0 24px;\n /* Match the ACTIVE tab's surface so the tab flows into its pane\n (the sunken tone read as a disconnected band between them). */\n background: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n }\n :host:not(:has(*)) { display: none; }\n .crumb-lead {\n font-size: 11px;\n color: var(--mj-text-muted);\n margin-right: 6px;\n }\n /* Each breadcrumb level is its OWN link with its own hover */\n .crumb-seg {\n display: inline-flex;\n align-items: center;\n max-width: 100%;\n padding: 5px 8px;\n border: none;\n border-radius: 4px;\n background: transparent;\n font-size: 12px;\n line-height: 1;\n font-family: inherit;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: background 0.15s, color 0.15s;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n .crumb-seg:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n }\n .crumb-seg:focus-visible {\n outline: 2px solid var(--mj-border-focus);\n outline-offset: -2px;\n }\n .crumb-page { font-weight: 500; }\n .crumb-sep { color: var(--mj-text-muted); font-size: 12px; }\n .origin-static {\n padding: 5px 8px;\n font-size: 12px;\n color: var(--mj-text-muted);\n }\n `]\n})\nexport class RecordOriginCrumbComponent {\n private _origin: RecordSourceContext | null = null;\n public asyncResolvedLabel?: string; // case-violation-ok-legacy-back-compat: an accessor cannot be optional, so a stub would turn this into a required member\n\n @Input()\n set Origin(value: RecordSourceContext | null) {\n this._origin = value;\n this.asyncResolvedLabel = undefined;\n this.resolveAsyncLabel();\n }\n get Origin(): RecordSourceContext | null {\n return this._origin;\n }\n\n private navigationService = inject(NavigationService);\n private cdr = inject(ChangeDetectorRef);\n\n get Clickable(): boolean {\n return RecordSourceHasReturnTarget(this.Origin);\n }\n\n get DisplayLabel(): string | undefined {\n if (this.Origin?.sourceRecordEntity && this.Origin?.sourceRecordId) {\n if (this.asyncResolvedLabel) {\n return this.asyncResolvedLabel;\n }\n const md = Metadata.Provider; // global-provider-ok: shell breadcrumb component displays navigation history using global metadata cache\n const entity = md?.EntityByName(this.Origin.sourceRecordEntity);\n const key = CompositeKey.FromURLSegment(entity, this.Origin.sourceRecordId);\n const cached = md ? md.GetCachedRecordNameOnlyIfCached(this.Origin.sourceRecordEntity, key) : undefined;\n if (cached) {\n return cached;\n }\n const fallbackEntityLabel = entity?.DisplayName || entity?.Name || this.Origin.sourceRecordEntity;\n if (this.Origin.sourceLabel && !this.Origin.sourceLabel.includes(this.Origin.sourceRecordId)) {\n return this.Origin.sourceLabel;\n }\n return fallbackEntityLabel;\n }\n return this.Origin?.sourceLabel;\n }\n\n private resolveAsyncLabel(): void {\n const md = Metadata.Provider; // global-provider-ok: shell breadcrumb component asynchronously resolves record name using global metadata provider\n if (this.Origin?.sourceRecordEntity && this.Origin?.sourceRecordId && md) {\n const entity = md.EntityByName(this.Origin.sourceRecordEntity);\n const key = CompositeKey.FromURLSegment(entity, this.Origin.sourceRecordId);\n const cached = md.GetCachedRecordNameOnlyIfCached(this.Origin.sourceRecordEntity, key);\n if (cached) {\n this.asyncResolvedLabel = cached;\n return;\n }\n const entityName = this.Origin.sourceRecordEntity;\n if (typeof md.GetEntityRecordName === 'function') {\n md.GetEntityRecordName(entityName, key).then(name => {\n if (name && this.Origin?.sourceRecordEntity === entityName) {\n this.asyncResolvedLabel = name;\n this.cdr.markForCheck();\n }\n }).catch(() => {\n // Silently ignore async name fetch error, fallback will remain active\n });\n }\n }\n }\n\n /** Page segment: full restore — the captured page, section state included */\n async OnPageClick(): Promise<void> {\n if (this.Origin) {\n await this.navigationService.ReturnToRecordSource(this.Origin);\n }\n }\n\n /** App segment: the app's LANDING page (breadcrumb level semantics) */\n async OnAppClick(): Promise<void> {\n if (this.Origin?.sourceAppId) {\n await this.navigationService.SwitchToAppHome(this.Origin.sourceAppId);\n }\n }\n}\n"]}
@@ -64,29 +64,52 @@ export declare class ComponentCacheManager {
64
64
  * Generate a unique cache key from resource identity.
65
65
  * This is the ONE canonical key format used by ALL cache operations.
66
66
  *
67
- * `discriminator` is appended into the recordId slot only when recordId is empty.
68
- * It exists to prevent collisions between distinct "new record" tabs that share
69
- * an empty recordId — e.g., a new MJ:Companies form and a new MJ:Employees form
70
- * would otherwise both cache at `appId::RecordResource::__no_record__` and clobber
71
- * each other. Passing the entity name as discriminator keeps them separate.
67
+ * `discriminator` is appended into the recordId slot when the recordId alone does not
68
+ * identify the resource:
69
+ * - **Empty recordId** — distinct "new record" tabs, e.g. a new MJ:Companies form and a new
70
+ * MJ:Employees form, would otherwise both cache at `appId::RecordResource::__no_record__`
71
+ * and clobber each other.
72
+ * - **The `'dynamic'` view marker** — `NavigationService.OpenDynamicView` stamps every
73
+ * dynamic view with that same recordId, so without the entity every dynamic view in an app
74
+ * shares one key. A reload from `#Accounts` to `#Contacts` would then get a cache hit on the
75
+ * Accounts component, which never has its `Data` rebound, so the tab keeps showing Accounts
76
+ * and the tab-container's entity check fires again on every emission.
77
+ *
78
+ * Passing the entity name as discriminator keeps them separate. Whatever the tab-container's
79
+ * reload check compares has to be part of this key, or a reload is handed back the component
80
+ * it just detached.
72
81
  */
73
82
  private getCacheKey;
83
+ /**
84
+ * Whether a recordId is the `'dynamic'` marker `NavigationService.OpenDynamicView` stamps on a
85
+ * dynamic view, which is not a saved-view ID. Matched the way `ViewResourceComponent` matches it
86
+ * (trimmed, case-insensitive), so the cache and the view agree on what counts as dynamic.
87
+ */
88
+ static IsDynamicViewMarker(recordId: string | null | undefined): boolean;
74
89
  /**
75
90
  * Check if a component exists in cache and is available for reuse.
76
91
  */
92
+ HasAvailableComponent(resourceType: string, recordId: string, appId: string, discriminator?: string): boolean;
93
+ /** @deprecated Use {@link HasAvailableComponent}. */
77
94
  hasAvailableComponent(resourceType: string, recordId: string, appId: string, discriminator?: string): boolean;
78
95
  /**
79
96
  * Get a cached component if available (not currently attached).
80
97
  * Lookup is by resource identity, not tab ID.
81
98
  */
99
+ GetCachedComponent(resourceType: string, recordId: string, appId: string, discriminator?: string): CachedComponentInfo | null;
100
+ /** @deprecated Use {@link GetCachedComponent}. */
82
101
  getCachedComponent(resourceType: string, recordId: string, appId: string, discriminator?: string): CachedComponentInfo | null;
83
102
  /**
84
103
  * Store a component in the cache and mark as attached.
85
104
  */
105
+ CacheComponent(componentRef: ComponentRef<BaseResourceComponent>, wrapperElement: HTMLElement, resourceData: ResourceData, tabId: string): void;
106
+ /** @deprecated Use {@link CacheComponent}. */
86
107
  cacheComponent(componentRef: ComponentRef<BaseResourceComponent>, wrapperElement: HTMLElement, resourceData: ResourceData, tabId: string): void;
87
108
  /**
88
109
  * Mark a component as attached. Lookup by resource identity.
89
110
  */
111
+ MarkAsAttached(resourceType: string, recordId: string, appId: string, tabId: string, discriminator?: string): void;
112
+ /** @deprecated Use {@link MarkAsAttached}. */
90
113
  markAsAttached(resourceType: string, recordId: string, appId: string, tabId: string, discriminator?: string): void;
91
114
  /**
92
115
  * Mark a component as detached (available for reuse). Lookup by resource identity.
@@ -94,6 +117,8 @@ export declare class ComponentCacheManager {
94
117
  * This is the ONLY way to detach a component. Both single-resource mode and
95
118
  * Golden Layout mode use this same method to ensure consistent cache behavior.
96
119
  */
120
+ MarkAsDetached(resourceType: string, recordId: string, appId: string, discriminator?: string): CachedComponentInfo | null;
121
+ /** @deprecated Use {@link MarkAsDetached}. */
97
122
  markAsDetached(resourceType: string, recordId: string, appId: string, discriminator?: string): CachedComponentInfo | null;
98
123
  /**
99
124
  * Re-key a cached component when its underlying record identity changes.
@@ -106,6 +131,8 @@ export declare class ComponentCacheManager {
106
131
  * Preserves the live component instance — only the cache key + stored identity change.
107
132
  * Returns true if a matching entry was found and re-keyed, false otherwise.
108
133
  */
134
+ RekeyComponent(resourceType: string, oldRecordId: string, newRecordId: string, appId: string, oldDiscriminator?: string, newDiscriminator?: string): boolean;
135
+ /** @deprecated Use {@link RekeyComponent}. */
109
136
  rekeyComponent(resourceType: string, oldRecordId: string, newRecordId: string, appId: string, oldDiscriminator?: string, newDiscriminator?: string): boolean;
110
137
  /**
111
138
  * Find a cached component by tab ID and detach it.
@@ -113,29 +140,39 @@ export declare class ComponentCacheManager {
113
140
  * (e.g., Golden Layout tab close events). It resolves the tab ID to
114
141
  * resource identity, then delegates to the identity-based markAsDetached.
115
142
  */
143
+ FindAndDetachByTabId(tabId: string): CachedComponentInfo | null;
144
+ /** @deprecated Use {@link FindAndDetachByTabId}. */
116
145
  findAndDetachByTabId(tabId: string): CachedComponentInfo | null;
117
146
  /**
118
147
  * Evict least-recently-used detached components when over the limit.
119
148
  * Only evicts components that are not currently attached.
120
149
  */
121
- private EvictIfNeeded;
150
+ private evictIfNeeded;
122
151
  /**
123
152
  * Get component info by tab ID (for finding what's attached to a tab).
124
153
  * Uses linear scan since tabId is metadata, not a key.
125
154
  */
155
+ GetComponentByTabId(tabId: string): CachedComponentInfo | null;
156
+ /** @deprecated Use {@link GetComponentByTabId}. */
126
157
  getComponentByTabId(tabId: string): CachedComponentInfo | null;
127
158
  /**
128
159
  * Remove and destroy a specific component from cache by resource identity.
129
160
  */
161
+ DestroyComponent(resourceType: string, recordId: string, appId: string, discriminator?: string): void;
162
+ /** @deprecated Use {@link DestroyComponent}. */
130
163
  destroyComponent(resourceType: string, recordId: string, appId: string, discriminator?: string): void;
131
164
  /**
132
165
  * Remove and destroy component by tab ID (convenience for Golden Layout tab close).
133
166
  */
167
+ DestroyComponentByTabId(tabId: string): void;
168
+ /** @deprecated Use {@link DestroyComponentByTabId}. */
134
169
  destroyComponentByTabId(tabId: string): void;
135
170
  /**
136
171
  * Clear the entire cache, destroying all components.
137
172
  * Call this on user logout or app shutdown.
138
173
  */
174
+ ClearCache(): void;
175
+ /** @deprecated Use {@link ClearCache}. */
139
176
  clearCache(): void;
140
177
  /**
141
178
  * Selectively clear cached components matching a predicate.
@@ -151,6 +188,13 @@ export declare class ComponentCacheManager {
151
188
  /**
152
189
  * Get cache statistics for debugging.
153
190
  */
191
+ GetCacheStats(): {
192
+ total: number;
193
+ attached: number;
194
+ detached: number;
195
+ byResourceType: Map<string, number>;
196
+ };
197
+ /** @deprecated Use {@link GetCacheStats}. */
154
198
  getCacheStats(): {
155
199
  total: number;
156
200
  attached: number;
@@ -1 +1 @@
1
- {"version":3,"file":"component-cache-manager.d.ts","sourceRoot":"","sources":["../../../../../src/lib/shell/components/tabs/component-cache-manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"component-cache-manager.d.ts","sourceRoot":"","sources":["../../../../../src/lib/shell/components/tabs/component-cache-manager.ts"],"names":[],"mappings":"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"}
@@ -40,30 +40,59 @@ export class ComponentCacheManager {
40
40
  * Generate a unique cache key from resource identity.
41
41
  * This is the ONE canonical key format used by ALL cache operations.
42
42
  *
43
- * `discriminator` is appended into the recordId slot only when recordId is empty.
44
- * It exists to prevent collisions between distinct "new record" tabs that share
45
- * an empty recordId — e.g., a new MJ:Companies form and a new MJ:Employees form
46
- * would otherwise both cache at `appId::RecordResource::__no_record__` and clobber
47
- * each other. Passing the entity name as discriminator keeps them separate.
43
+ * `discriminator` is appended into the recordId slot when the recordId alone does not
44
+ * identify the resource:
45
+ * - **Empty recordId** — distinct "new record" tabs, e.g. a new MJ:Companies form and a new
46
+ * MJ:Employees form, would otherwise both cache at `appId::RecordResource::__no_record__`
47
+ * and clobber each other.
48
+ * - **The `'dynamic'` view marker** — `NavigationService.OpenDynamicView` stamps every
49
+ * dynamic view with that same recordId, so without the entity every dynamic view in an app
50
+ * shares one key. A reload from `#Accounts` to `#Contacts` would then get a cache hit on the
51
+ * Accounts component, which never has its `Data` rebound, so the tab keeps showing Accounts
52
+ * and the tab-container's entity check fires again on every emission.
53
+ *
54
+ * Passing the entity name as discriminator keeps them separate. Whatever the tab-container's
55
+ * reload check compares has to be part of this key, or a reload is handed back the component
56
+ * it just detached.
48
57
  */
49
58
  getCacheKey(resourceType, recordId, appId, discriminator) {
50
- const normalizedRecordId = recordId
51
- || (discriminator ? `__new__::${discriminator}` : '__no_record__');
59
+ let normalizedRecordId;
60
+ if (!recordId) {
61
+ normalizedRecordId = discriminator ? `__new__::${discriminator}` : '__no_record__';
62
+ }
63
+ else if (discriminator && ComponentCacheManager.IsDynamicViewMarker(recordId)) {
64
+ normalizedRecordId = `dynamic::${discriminator}`;
65
+ }
66
+ else {
67
+ normalizedRecordId = recordId;
68
+ }
52
69
  return `${appId}::${resourceType}::${normalizedRecordId}`;
53
70
  }
71
+ /**
72
+ * Whether a recordId is the `'dynamic'` marker `NavigationService.OpenDynamicView` stamps on a
73
+ * dynamic view, which is not a saved-view ID. Matched the way `ViewResourceComponent` matches it
74
+ * (trimmed, case-insensitive), so the cache and the view agree on what counts as dynamic.
75
+ */
76
+ static IsDynamicViewMarker(recordId) {
77
+ return typeof recordId === 'string' && recordId.trim().toLowerCase() === 'dynamic';
78
+ }
54
79
  /**
55
80
  * Check if a component exists in cache and is available for reuse.
56
81
  */
57
- hasAvailableComponent(resourceType, recordId, appId, discriminator) {
82
+ HasAvailableComponent(resourceType, recordId, appId, discriminator) {
58
83
  const key = this.getCacheKey(resourceType, recordId, appId, discriminator);
59
84
  const info = this.cache.get(key);
60
85
  return info !== undefined && !info.isAttached;
61
86
  }
87
+ /** @deprecated Use {@link HasAvailableComponent}. */
88
+ hasAvailableComponent(resourceType, recordId, appId, discriminator) {
89
+ return this.HasAvailableComponent(resourceType, recordId, appId, discriminator);
90
+ }
62
91
  /**
63
92
  * Get a cached component if available (not currently attached).
64
93
  * Lookup is by resource identity, not tab ID.
65
94
  */
66
- getCachedComponent(resourceType, recordId, appId, discriminator) {
95
+ GetCachedComponent(resourceType, recordId, appId, discriminator) {
67
96
  const key = this.getCacheKey(resourceType, recordId, appId, discriminator);
68
97
  const info = this.cache.get(key);
69
98
  if (!info) {
@@ -75,10 +104,14 @@ export class ComponentCacheManager {
75
104
  }
76
105
  return info;
77
106
  }
107
+ /** @deprecated Use {@link GetCachedComponent}. */
108
+ getCachedComponent(resourceType, recordId, appId, discriminator) {
109
+ return this.GetCachedComponent(resourceType, recordId, appId, discriminator);
110
+ }
78
111
  /**
79
112
  * Store a component in the cache and mark as attached.
80
113
  */
81
- cacheComponent(componentRef, wrapperElement, resourceData, tabId) {
114
+ CacheComponent(componentRef, wrapperElement, resourceData, tabId) {
82
115
  // Use driverClass (the actual component class name) as the resourceType for the cache key,
83
116
  // NOT resourceData.ResourceType (which is often just "Custom" for dashboard resources).
84
117
  // This must match the lookup key used in getCachedComponent/markAsAttached/markAsDetached.
@@ -104,10 +137,14 @@ export class ComponentCacheManager {
104
137
  };
105
138
  this.cache.set(key, info);
106
139
  }
140
+ /** @deprecated Use {@link CacheComponent}. */
141
+ cacheComponent(componentRef, wrapperElement, resourceData, tabId) {
142
+ return this.CacheComponent(componentRef, wrapperElement, resourceData, tabId);
143
+ }
107
144
  /**
108
145
  * Mark a component as attached. Lookup by resource identity.
109
146
  */
110
- markAsAttached(resourceType, recordId, appId, tabId, discriminator) {
147
+ MarkAsAttached(resourceType, recordId, appId, tabId, discriminator) {
111
148
  const key = this.getCacheKey(resourceType, recordId, appId, discriminator);
112
149
  const info = this.cache.get(key);
113
150
  if (info) {
@@ -119,13 +156,17 @@ export class ComponentCacheManager {
119
156
  this.navigationService?.NotifyResourceReattached(info.componentRef.instance);
120
157
  }
121
158
  }
159
+ /** @deprecated Use {@link MarkAsAttached}. */
160
+ markAsAttached(resourceType, recordId, appId, tabId, discriminator) {
161
+ return this.MarkAsAttached(resourceType, recordId, appId, tabId, discriminator);
162
+ }
122
163
  /**
123
164
  * Mark a component as detached (available for reuse). Lookup by resource identity.
124
165
  *
125
166
  * This is the ONLY way to detach a component. Both single-resource mode and
126
167
  * Golden Layout mode use this same method to ensure consistent cache behavior.
127
168
  */
128
- markAsDetached(resourceType, recordId, appId, discriminator) {
169
+ MarkAsDetached(resourceType, recordId, appId, discriminator) {
129
170
  const key = this.getCacheKey(resourceType, recordId, appId, discriminator);
130
171
  const info = this.cache.get(key);
131
172
  if (!info)
@@ -136,9 +177,13 @@ export class ComponentCacheManager {
136
177
  // Clear this surface's agent client tools on detach so the previous app's tools aren't offered to
137
178
  // the AI agent on the next surface. NotifyResourceReattached replays them if the user returns.
138
179
  this.navigationService?.NotifyResourceDetached(info.componentRef.instance);
139
- this.EvictIfNeeded();
180
+ this.evictIfNeeded();
140
181
  return info;
141
182
  }
183
+ /** @deprecated Use {@link MarkAsDetached}. */
184
+ markAsDetached(resourceType, recordId, appId, discriminator) {
185
+ return this.MarkAsDetached(resourceType, recordId, appId, discriminator);
186
+ }
142
187
  /**
143
188
  * Re-key a cached component when its underlying record identity changes.
144
189
  *
@@ -150,7 +195,7 @@ export class ComponentCacheManager {
150
195
  * Preserves the live component instance — only the cache key + stored identity change.
151
196
  * Returns true if a matching entry was found and re-keyed, false otherwise.
152
197
  */
153
- rekeyComponent(resourceType, oldRecordId, newRecordId, appId, oldDiscriminator, newDiscriminator) {
198
+ RekeyComponent(resourceType, oldRecordId, newRecordId, appId, oldDiscriminator, newDiscriminator) {
154
199
  const oldKey = this.getCacheKey(resourceType, oldRecordId, appId, oldDiscriminator);
155
200
  const info = this.cache.get(oldKey);
156
201
  if (!info) {
@@ -172,25 +217,33 @@ export class ComponentCacheManager {
172
217
  this.cache.set(newKey, info);
173
218
  return true;
174
219
  }
220
+ /** @deprecated Use {@link RekeyComponent}. */
221
+ rekeyComponent(resourceType, oldRecordId, newRecordId, appId, oldDiscriminator, newDiscriminator) {
222
+ return this.RekeyComponent(resourceType, oldRecordId, newRecordId, appId, oldDiscriminator, newDiscriminator);
223
+ }
175
224
  /**
176
225
  * Find a cached component by tab ID and detach it.
177
226
  * This is a convenience wrapper for callers that only know the tab ID
178
227
  * (e.g., Golden Layout tab close events). It resolves the tab ID to
179
228
  * resource identity, then delegates to the identity-based markAsDetached.
180
229
  */
181
- findAndDetachByTabId(tabId) {
230
+ FindAndDetachByTabId(tabId) {
182
231
  const entry = Array.from(this.cache.entries())
183
232
  .find(([_, info]) => info.attachedToTabId === tabId);
184
233
  if (!entry)
185
234
  return null;
186
235
  const [_, info] = entry;
187
- return this.markAsDetached(info.resourceType, info.resourceRecordId, info.applicationId, info.keyDiscriminator);
236
+ return this.MarkAsDetached(info.resourceType, info.resourceRecordId, info.applicationId, info.keyDiscriminator);
237
+ }
238
+ /** @deprecated Use {@link FindAndDetachByTabId}. */
239
+ findAndDetachByTabId(tabId) {
240
+ return this.FindAndDetachByTabId(tabId);
188
241
  }
189
242
  /**
190
243
  * Evict least-recently-used detached components when over the limit.
191
244
  * Only evicts components that are not currently attached.
192
245
  */
193
- EvictIfNeeded() {
246
+ evictIfNeeded() {
194
247
  if (ComponentCacheManager.MaxDetachedComponents <= 0)
195
248
  return;
196
249
  const detached = Array.from(this.cache.entries())
@@ -209,15 +262,19 @@ export class ComponentCacheManager {
209
262
  * Get component info by tab ID (for finding what's attached to a tab).
210
263
  * Uses linear scan since tabId is metadata, not a key.
211
264
  */
212
- getComponentByTabId(tabId) {
265
+ GetComponentByTabId(tabId) {
213
266
  const entry = Array.from(this.cache.entries())
214
267
  .find(([_, info]) => info.attachedToTabId === tabId);
215
268
  return entry ? entry[1] : null;
216
269
  }
270
+ /** @deprecated Use {@link GetComponentByTabId}. */
271
+ getComponentByTabId(tabId) {
272
+ return this.GetComponentByTabId(tabId);
273
+ }
217
274
  /**
218
275
  * Remove and destroy a specific component from cache by resource identity.
219
276
  */
220
- destroyComponent(resourceType, recordId, appId, discriminator) {
277
+ DestroyComponent(resourceType, recordId, appId, discriminator) {
221
278
  const key = this.getCacheKey(resourceType, recordId, appId, discriminator);
222
279
  const info = this.cache.get(key);
223
280
  if (!info)
@@ -227,10 +284,14 @@ export class ComponentCacheManager {
227
284
  info.componentRef.destroy();
228
285
  this.cache.delete(key);
229
286
  }
287
+ /** @deprecated Use {@link DestroyComponent}. */
288
+ destroyComponent(resourceType, recordId, appId, discriminator) {
289
+ return this.DestroyComponent(resourceType, recordId, appId, discriminator);
290
+ }
230
291
  /**
231
292
  * Remove and destroy component by tab ID (convenience for Golden Layout tab close).
232
293
  */
233
- destroyComponentByTabId(tabId) {
294
+ DestroyComponentByTabId(tabId) {
234
295
  const entry = Array.from(this.cache.entries())
235
296
  .find(([_, info]) => info.attachedToTabId === tabId);
236
297
  if (!entry)
@@ -240,17 +301,25 @@ export class ComponentCacheManager {
240
301
  info.componentRef.destroy();
241
302
  this.cache.delete(key);
242
303
  }
304
+ /** @deprecated Use {@link DestroyComponentByTabId}. */
305
+ destroyComponentByTabId(tabId) {
306
+ return this.DestroyComponentByTabId(tabId);
307
+ }
243
308
  /**
244
309
  * Clear the entire cache, destroying all components.
245
310
  * Call this on user logout or app shutdown.
246
311
  */
247
- clearCache() {
312
+ ClearCache() {
248
313
  this.cache.forEach(info => {
249
314
  this.appRef.detachView(info.componentRef.hostView);
250
315
  info.componentRef.destroy();
251
316
  });
252
317
  this.cache.clear();
253
318
  }
319
+ /** @deprecated Use {@link ClearCache}. */
320
+ clearCache() {
321
+ return this.ClearCache();
322
+ }
254
323
  /**
255
324
  * Selectively clear cached components matching a predicate.
256
325
  * Components that match are destroyed; those that don't are kept.
@@ -280,7 +349,7 @@ export class ComponentCacheManager {
280
349
  /**
281
350
  * Get cache statistics for debugging.
282
351
  */
283
- getCacheStats() {
352
+ GetCacheStats() {
284
353
  const stats = {
285
354
  total: this.cache.size,
286
355
  attached: 0,
@@ -299,5 +368,9 @@ export class ComponentCacheManager {
299
368
  });
300
369
  return stats;
301
370
  }
371
+ /** @deprecated Use {@link GetCacheStats}. */
372
+ getCacheStats() {
373
+ return this.GetCacheStats();
374
+ }
302
375
  }
303
376
  //# sourceMappingURL=component-cache-manager.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"component-cache-manager.js","sourceRoot":"","sources":["../../../../../src/lib/shell/components/tabs/component-cache-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { ComponentRef, ApplicationRef } from '@angular/core';\nimport { BaseResourceComponent, NavigationService } from '@memberjunction/ng-shared';\nimport { ResourceData } from '@memberjunction/core-entities';\n\n/**\n * Metadata about a cached component\n */\nexport interface CachedComponentInfo {\n // The Angular component reference\n componentRef: ComponentRef<BaseResourceComponent>;\n\n // The wrapper DOM element (for detaching/reattaching)\n wrapperElement: HTMLElement;\n\n // Resource identity (the ONLY key used for cache operations)\n resourceType: string;\n resourceRecordId: string;\n applicationId: string;\n\n // Optional tiebreaker used when recordId is empty (e.g., \"new record\" tabs for\n // different entities all have recordId='' but must NOT share a cache entry).\n // For entity-record resources this is the entity name; null/undefined for\n // resource types that don't need disambiguation.\n keyDiscriminator?: string;\n\n // Usage tracking\n isAttached: boolean; // Currently attached to a tab/container?\n attachedToTabId: string | null; // Which tab is it attached to? (metadata only, NOT used for lookup)\n\n // Lifecycle tracking\n lastUsed: Date;\n createdAt: Date;\n\n // Resource data snapshot (for comparison)\n resourceData: ResourceData;\n\n // Saved query params from the tab config at detach time.\n // Restored to the tab config when the component is reattached,\n // so the URL reflects the component's preserved state.\n savedQueryParams?: Record<string, string>;\n\n // Agent context reported by this component via NavigationService.SetAgentContext()\n // Cached so it can be restored when the component becomes active again.\n AgentContext?: Record<string, unknown>;\n\n // Agent client tools registered by this component via NavigationService.SetAgentClientTools()\n // Cached so they can be re-registered when the component becomes active again.\n AgentClientTools?: { Name: string; Description: string; ParameterSchema: Record<string, unknown>; Handler: (params: Record<string, unknown>) => Promise<unknown> }[];\n}\n\n/**\n * Smart component cache manager that preserves component state across tab switches.\n *\n * ALL cache operations use a consistent identity key: `appId::resourceType::recordId`.\n * This key is the same regardless of whether the component is in Golden Layout (tabbed)\n * mode or Single Resource mode, ensuring components are reusable across both modes.\n *\n * The `attachedToTabId` field is metadata for debugging/display — it is NEVER used\n * as a lookup key. This prevents bugs where multiple resources sharing the same tab ID\n * (e.g., nav items within a single-resource app) interfere with each other's cache state.\n *\n * Features:\n * - Caches components by resource identity (appId + resourceType + recordId)\n * - Tracks component usage to prevent double-attachment\n * - Detaches/reattaches DOM elements without destroying Angular components\n * - LRU eviction when detached component count exceeds MaxDetachedComponents\n */\nexport class ComponentCacheManager {\n private cache = new Map<string, CachedComponentInfo>();\n\n /**\n * Maximum number of detached (not currently visible) components to keep\n * cached. When exceeded, least-recently-used detached components are\n * evicted. Set to 0 to disable eviction (legacy behavior). Default: 20.\n */\n public static MaxDetachedComponents: number = 20;\n\n /**\n * @param navigationService Optional — when provided, the cache manager keeps the AI agent's live\n * client-tool set in sync with the attached surface: it replays a reattached component's tools\n * ({@link NavigationService.NotifyResourceReattached}) and clears them on detach\n * ({@link NavigationService.NotifyResourceDetached}), since cached components don't re-run\n * `ngAfterViewInit`. Centralizing it here covers BOTH single-resource and Golden Layout paths.\n */\n constructor(private appRef: ApplicationRef, private navigationService?: NavigationService) {}\n\n /**\n * Generate a unique cache key from resource identity.\n * This is the ONE canonical key format used by ALL cache operations.\n *\n * `discriminator` is appended into the recordId slot only when recordId is empty.\n * It exists to prevent collisions between distinct \"new record\" tabs that share\n * an empty recordId — e.g., a new MJ:Companies form and a new MJ:Employees form\n * would otherwise both cache at `appId::RecordResource::__no_record__` and clobber\n * each other. Passing the entity name as discriminator keeps them separate.\n */\n private getCacheKey(resourceType: string, recordId: string, appId: string, discriminator?: string): string {\n const normalizedRecordId = recordId\n || (discriminator ? `__new__::${discriminator}` : '__no_record__');\n return `${appId}::${resourceType}::${normalizedRecordId}`;\n }\n\n /**\n * Check if a component exists in cache and is available for reuse.\n */\n hasAvailableComponent(resourceType: string, recordId: string, appId: string, discriminator?: string): boolean {\n const key = this.getCacheKey(resourceType, recordId, appId, discriminator);\n const info = this.cache.get(key);\n return info !== undefined && !info.isAttached;\n }\n\n /**\n * Get a cached component if available (not currently attached).\n * Lookup is by resource identity, not tab ID.\n */\n getCachedComponent(resourceType: string, recordId: string, appId: string, discriminator?: string): CachedComponentInfo | null {\n const key = this.getCacheKey(resourceType, recordId, appId, discriminator);\n const info = this.cache.get(key);\n\n if (!info) {\n return null;\n }\n\n // Can only reuse if not currently attached elsewhere\n if (info.isAttached) {\n return null;\n }\n\n return info;\n }\n\n /**\n * Store a component in the cache and mark as attached.\n */\n cacheComponent(\n componentRef: ComponentRef<BaseResourceComponent>,\n wrapperElement: HTMLElement,\n resourceData: ResourceData,\n tabId: string\n ): void {\n // Use driverClass (the actual component class name) as the resourceType for the cache key,\n // NOT resourceData.ResourceType (which is often just \"Custom\" for dashboard resources).\n // This must match the lookup key used in getCachedComponent/markAsAttached/markAsDetached.\n const resolvedResourceType = resourceData.Configuration?.resourceTypeDriverClass\n || resourceData.Configuration?.driverClass\n || resourceData.ResourceType;\n // Entity name discriminates between distinct \"new record\" tabs (empty recordId).\n // For non-Records resource types this is undefined, leaving the key unchanged.\n const discriminator = resourceData.Configuration?.Entity as string | undefined;\n const key = this.getCacheKey(\n resolvedResourceType,\n resourceData.ResourceRecordID || '',\n resourceData.Configuration?.applicationId || '',\n discriminator\n );\n\n const info: CachedComponentInfo = {\n componentRef,\n wrapperElement,\n resourceType: resolvedResourceType,\n resourceRecordId: resourceData.ResourceRecordID || '',\n applicationId: resourceData.Configuration?.applicationId || '',\n keyDiscriminator: discriminator,\n isAttached: true,\n attachedToTabId: tabId,\n lastUsed: new Date(),\n createdAt: new Date(),\n resourceData\n };\n\n this.cache.set(key, info);\n }\n\n /**\n * Mark a component as attached. Lookup by resource identity.\n */\n markAsAttached(resourceType: string, recordId: string, appId: string, tabId: string, discriminator?: string): void {\n const key = this.getCacheKey(resourceType, recordId, appId, discriminator);\n const info = this.cache.get(key);\n\n if (info) {\n info.isAttached = true;\n info.attachedToTabId = tabId;\n info.lastUsed = new Date();\n // Replay this surface's agent client tools to the AI agent on reattach (cached components keep\n // their instance but never re-run ngAfterViewInit, so they don't re-register on their own).\n this.navigationService?.NotifyResourceReattached(info.componentRef.instance);\n }\n }\n\n /**\n * Mark a component as detached (available for reuse). Lookup by resource identity.\n *\n * This is the ONLY way to detach a component. Both single-resource mode and\n * Golden Layout mode use this same method to ensure consistent cache behavior.\n */\n markAsDetached(resourceType: string, recordId: string, appId: string, discriminator?: string): CachedComponentInfo | null {\n const key = this.getCacheKey(resourceType, recordId, appId, discriminator);\n const info = this.cache.get(key);\n if (!info) return null;\n\n info.isAttached = false;\n info.attachedToTabId = null;\n info.lastUsed = new Date();\n // Clear this surface's agent client tools on detach so the previous app's tools aren't offered to\n // the AI agent on the next surface. NotifyResourceReattached replays them if the user returns.\n this.navigationService?.NotifyResourceDetached(info.componentRef.instance);\n this.EvictIfNeeded();\n return info;\n }\n\n /**\n * Re-key a cached component when its underlying record identity changes.\n *\n * Used when a \"new record\" component (cached at `recordId = ''`) becomes a saved record\n * (now identified by its new PK). Without re-keying, the next \"new record\" request would\n * still resolve to this same cache entry, surfacing the previously-saved record on a form\n * the user expects to be blank.\n *\n * Preserves the live component instance — only the cache key + stored identity change.\n * Returns true if a matching entry was found and re-keyed, false otherwise.\n */\n rekeyComponent(\n resourceType: string,\n oldRecordId: string,\n newRecordId: string,\n appId: string,\n oldDiscriminator?: string,\n newDiscriminator?: string\n ): boolean {\n const oldKey = this.getCacheKey(resourceType, oldRecordId, appId, oldDiscriminator);\n const info = this.cache.get(oldKey);\n if (!info) {\n return false;\n }\n\n // Once a record has a real id, the discriminator no longer matters (the id is unique).\n // Default the new discriminator to undefined unless explicitly provided.\n const newKey = this.getCacheKey(resourceType, newRecordId, appId, newDiscriminator);\n if (oldKey === newKey) {\n return false;\n }\n\n info.resourceRecordId = newRecordId;\n info.keyDiscriminator = newDiscriminator;\n info.lastUsed = new Date();\n if (info.resourceData) {\n info.resourceData.ResourceRecordID = newRecordId;\n }\n\n this.cache.delete(oldKey);\n this.cache.set(newKey, info);\n return true;\n }\n\n /**\n * Find a cached component by tab ID and detach it.\n * This is a convenience wrapper for callers that only know the tab ID\n * (e.g., Golden Layout tab close events). It resolves the tab ID to\n * resource identity, then delegates to the identity-based markAsDetached.\n */\n findAndDetachByTabId(tabId: string): CachedComponentInfo | null {\n const entry = Array.from(this.cache.entries())\n .find(([_, info]) => info.attachedToTabId === tabId);\n\n if (!entry) return null;\n\n const [_, info] = entry;\n return this.markAsDetached(info.resourceType, info.resourceRecordId, info.applicationId, info.keyDiscriminator);\n }\n\n /**\n * Evict least-recently-used detached components when over the limit.\n * Only evicts components that are not currently attached.\n */\n private EvictIfNeeded(): void {\n if (ComponentCacheManager.MaxDetachedComponents <= 0) return;\n\n const detached = Array.from(this.cache.entries())\n .filter(([_, info]) => !info.isAttached)\n .sort((a, b) => a[1].lastUsed.getTime() - b[1].lastUsed.getTime());\n\n while (detached.length > ComponentCacheManager.MaxDetachedComponents) {\n const [key, info] = detached.shift()!;\n // Drop the destroyed component's cached agent tools so the registry doesn't retain a dead ref.\n this.navigationService?.ForgetResource(info.componentRef.instance);\n this.appRef.detachView(info.componentRef.hostView);\n info.componentRef.destroy();\n this.cache.delete(key);\n }\n }\n\n /**\n * Get component info by tab ID (for finding what's attached to a tab).\n * Uses linear scan since tabId is metadata, not a key.\n */\n getComponentByTabId(tabId: string): CachedComponentInfo | null {\n const entry = Array.from(this.cache.entries())\n .find(([_, info]) => info.attachedToTabId === tabId);\n\n return entry ? entry[1] : null;\n }\n\n /**\n * Remove and destroy a specific component from cache by resource identity.\n */\n destroyComponent(resourceType: string, recordId: string, appId: string, discriminator?: string): void {\n const key = this.getCacheKey(resourceType, recordId, appId, discriminator);\n const info = this.cache.get(key);\n\n if (!info) return;\n\n this.navigationService?.ForgetResource(info.componentRef.instance);\n this.appRef.detachView(info.componentRef.hostView);\n info.componentRef.destroy();\n this.cache.delete(key);\n }\n\n /**\n * Remove and destroy component by tab ID (convenience for Golden Layout tab close).\n */\n destroyComponentByTabId(tabId: string): void {\n const entry = Array.from(this.cache.entries())\n .find(([_, info]) => info.attachedToTabId === tabId);\n\n if (!entry) return;\n\n const [key, info] = entry;\n this.appRef.detachView(info.componentRef.hostView);\n info.componentRef.destroy();\n this.cache.delete(key);\n }\n\n /**\n * Clear the entire cache, destroying all components.\n * Call this on user logout or app shutdown.\n */\n clearCache(): void {\n this.cache.forEach(info => {\n this.appRef.detachView(info.componentRef.hostView);\n info.componentRef.destroy();\n });\n this.cache.clear();\n }\n\n /**\n * Selectively clear cached components matching a predicate.\n * Components that match are destroyed; those that don't are kept.\n *\n * Use this for tenant switching: clear org-scoped components while\n * keeping system/global components alive.\n *\n * @param predicate Return true for components that should be destroyed.\n * @returns Number of components destroyed.\n */\n ClearCacheByPredicate(predicate: (info: CachedComponentInfo) => boolean): number {\n let destroyed = 0;\n const toRemove: string[] = [];\n\n this.cache.forEach((info, key) => {\n if (predicate(info)) {\n this.appRef.detachView(info.componentRef.hostView);\n info.componentRef.destroy();\n toRemove.push(key);\n destroyed++;\n }\n });\n\n for (const key of toRemove) {\n this.cache.delete(key);\n }\n\n return destroyed;\n }\n\n /**\n * Get cache statistics for debugging.\n */\n getCacheStats(): {\n total: number;\n attached: number;\n detached: number;\n byResourceType: Map<string, number>;\n } {\n const stats = {\n total: this.cache.size,\n attached: 0,\n detached: 0,\n byResourceType: new Map<string, number>()\n };\n\n this.cache.forEach(info => {\n if (info.isAttached) {\n stats.attached++;\n } else {\n stats.detached++;\n }\n\n const count = stats.byResourceType.get(info.resourceType) || 0;\n stats.byResourceType.set(info.resourceType, count + 1);\n });\n\n return stats;\n }\n}\n"]}
1
+ {"version":3,"file":"component-cache-manager.js","sourceRoot":"","sources":["../../../../../src/lib/shell/components/tabs/component-cache-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { ComponentRef, ApplicationRef } from '@angular/core';\nimport { BaseResourceComponent, NavigationService } from '@memberjunction/ng-shared';\nimport { ResourceData } from '@memberjunction/core-entities';\n\n/**\n * Metadata about a cached component\n */\nexport interface CachedComponentInfo {\n // The Angular component reference\n componentRef: ComponentRef<BaseResourceComponent>; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n\n // The wrapper DOM element (for detaching/reattaching)\n wrapperElement: HTMLElement; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n\n // Resource identity (the ONLY key used for cache operations)\n resourceType: string; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n resourceRecordId: string; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n applicationId: string; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n\n // Optional tiebreaker used when recordId is empty (e.g., \"new record\" tabs for\n // different entities all have recordId='' but must NOT share a cache entry).\n // For entity-record resources this is the entity name; null/undefined for\n // resource types that don't need disambiguation.\n keyDiscriminator?: string; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n\n // Usage tracking\n isAttached: boolean; // Currently attached to a tab/container? — case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n attachedToTabId: string | null; // Which tab is it attached to? (metadata only, NOT used for lookup) — case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n\n // Lifecycle tracking\n lastUsed: Date; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n createdAt: Date; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n\n // Resource data snapshot (for comparison)\n resourceData: ResourceData; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n\n // Saved query params from the tab config at detach time.\n // Restored to the tab config when the component is reattached,\n // so the URL reflects the component's preserved state.\n savedQueryParams?: Record<string, string>; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n\n // Agent context reported by this component via NavigationService.SetAgentContext()\n // Cached so it can be restored when the component becomes active again.\n AgentContext?: Record<string, unknown>;\n\n // Agent client tools registered by this component via NavigationService.SetAgentClientTools()\n // Cached so they can be re-registered when the component becomes active again.\n AgentClientTools?: { Name: string; Description: string; ParameterSchema: Record<string, unknown>; Handler: (params: Record<string, unknown>) => Promise<unknown> }[];\n}\n\n/**\n * Smart component cache manager that preserves component state across tab switches.\n *\n * ALL cache operations use a consistent identity key: `appId::resourceType::recordId`.\n * This key is the same regardless of whether the component is in Golden Layout (tabbed)\n * mode or Single Resource mode, ensuring components are reusable across both modes.\n *\n * The `attachedToTabId` field is metadata for debugging/display — it is NEVER used\n * as a lookup key. This prevents bugs where multiple resources sharing the same tab ID\n * (e.g., nav items within a single-resource app) interfere with each other's cache state.\n *\n * Features:\n * - Caches components by resource identity (appId + resourceType + recordId)\n * - Tracks component usage to prevent double-attachment\n * - Detaches/reattaches DOM elements without destroying Angular components\n * - LRU eviction when detached component count exceeds MaxDetachedComponents\n */\nexport class ComponentCacheManager {\n private cache = new Map<string, CachedComponentInfo>();\n\n /**\n * Maximum number of detached (not currently visible) components to keep\n * cached. When exceeded, least-recently-used detached components are\n * evicted. Set to 0 to disable eviction (legacy behavior). Default: 20.\n */\n public static MaxDetachedComponents: number = 20;\n\n /**\n * @param navigationService Optional — when provided, the cache manager keeps the AI agent's live\n * client-tool set in sync with the attached surface: it replays a reattached component's tools\n * ({@link NavigationService.NotifyResourceReattached}) and clears them on detach\n * ({@link NavigationService.NotifyResourceDetached}), since cached components don't re-run\n * `ngAfterViewInit`. Centralizing it here covers BOTH single-resource and Golden Layout paths.\n */\n constructor(private appRef: ApplicationRef, private navigationService?: NavigationService) {}\n\n /**\n * Generate a unique cache key from resource identity.\n * This is the ONE canonical key format used by ALL cache operations.\n *\n * `discriminator` is appended into the recordId slot when the recordId alone does not\n * identify the resource:\n * - **Empty recordId** — distinct \"new record\" tabs, e.g. a new MJ:Companies form and a new\n * MJ:Employees form, would otherwise both cache at `appId::RecordResource::__no_record__`\n * and clobber each other.\n * - **The `'dynamic'` view marker** — `NavigationService.OpenDynamicView` stamps every\n * dynamic view with that same recordId, so without the entity every dynamic view in an app\n * shares one key. A reload from `#Accounts` to `#Contacts` would then get a cache hit on the\n * Accounts component, which never has its `Data` rebound, so the tab keeps showing Accounts\n * and the tab-container's entity check fires again on every emission.\n *\n * Passing the entity name as discriminator keeps them separate. Whatever the tab-container's\n * reload check compares has to be part of this key, or a reload is handed back the component\n * it just detached.\n */\n private getCacheKey(resourceType: string, recordId: string, appId: string, discriminator?: string): string {\n let normalizedRecordId: string;\n if (!recordId) {\n normalizedRecordId = discriminator ? `__new__::${discriminator}` : '__no_record__';\n } else if (discriminator && ComponentCacheManager.IsDynamicViewMarker(recordId)) {\n normalizedRecordId = `dynamic::${discriminator}`;\n } else {\n normalizedRecordId = recordId;\n }\n return `${appId}::${resourceType}::${normalizedRecordId}`;\n }\n\n /**\n * Whether a recordId is the `'dynamic'` marker `NavigationService.OpenDynamicView` stamps on a\n * dynamic view, which is not a saved-view ID. Matched the way `ViewResourceComponent` matches it\n * (trimmed, case-insensitive), so the cache and the view agree on what counts as dynamic.\n */\n public static IsDynamicViewMarker(recordId: string | null | undefined): boolean {\n return typeof recordId === 'string' && recordId.trim().toLowerCase() === 'dynamic';\n }\n\n /**\n * Check if a component exists in cache and is available for reuse.\n */\n HasAvailableComponent(resourceType: string, recordId: string, appId: string, discriminator?: string): boolean {\n const key = this.getCacheKey(resourceType, recordId, appId, discriminator);\n const info = this.cache.get(key);\n return info !== undefined && !info.isAttached;\n }\n\n /** @deprecated Use {@link HasAvailableComponent}. */\n hasAvailableComponent(resourceType: string, recordId: string, appId: string, discriminator?: string): boolean {\n return this.HasAvailableComponent(resourceType, recordId, appId, discriminator);\n }\n\n /**\n * Get a cached component if available (not currently attached).\n * Lookup is by resource identity, not tab ID.\n */\n GetCachedComponent(resourceType: string, recordId: string, appId: string, discriminator?: string): CachedComponentInfo | null {\n const key = this.getCacheKey(resourceType, recordId, appId, discriminator);\n const info = this.cache.get(key);\n\n if (!info) {\n return null;\n }\n\n // Can only reuse if not currently attached elsewhere\n if (info.isAttached) {\n return null;\n }\n\n return info;\n }\n\n /** @deprecated Use {@link GetCachedComponent}. */\n getCachedComponent(resourceType: string, recordId: string, appId: string, discriminator?: string): CachedComponentInfo | null {\n return this.GetCachedComponent(resourceType, recordId, appId, discriminator);\n }\n\n /**\n * Store a component in the cache and mark as attached.\n */\n CacheComponent(\n componentRef: ComponentRef<BaseResourceComponent>,\n wrapperElement: HTMLElement,\n resourceData: ResourceData,\n tabId: string\n ): void {\n // Use driverClass (the actual component class name) as the resourceType for the cache key,\n // NOT resourceData.ResourceType (which is often just \"Custom\" for dashboard resources).\n // This must match the lookup key used in getCachedComponent/markAsAttached/markAsDetached.\n const resolvedResourceType = resourceData.Configuration?.resourceTypeDriverClass\n || resourceData.Configuration?.driverClass\n || resourceData.ResourceType;\n // Entity name discriminates between distinct \"new record\" tabs (empty recordId).\n // For non-Records resource types this is undefined, leaving the key unchanged.\n const discriminator = resourceData.Configuration?.Entity as string | undefined;\n const key = this.getCacheKey(\n resolvedResourceType,\n resourceData.ResourceRecordID || '',\n resourceData.Configuration?.applicationId || '',\n discriminator\n );\n\n const info: CachedComponentInfo = {\n componentRef,\n wrapperElement,\n resourceType: resolvedResourceType,\n resourceRecordId: resourceData.ResourceRecordID || '',\n applicationId: resourceData.Configuration?.applicationId || '',\n keyDiscriminator: discriminator,\n isAttached: true,\n attachedToTabId: tabId,\n lastUsed: new Date(),\n createdAt: new Date(),\n resourceData\n };\n\n this.cache.set(key, info);\n }\n\n /** @deprecated Use {@link CacheComponent}. */\n cacheComponent(\n componentRef: ComponentRef<BaseResourceComponent>,\n wrapperElement: HTMLElement,\n resourceData: ResourceData,\n tabId: string\n ): void {\n return this.CacheComponent(componentRef, wrapperElement, resourceData, tabId);\n }\n\n /**\n * Mark a component as attached. Lookup by resource identity.\n */\n MarkAsAttached(resourceType: string, recordId: string, appId: string, tabId: string, discriminator?: string): void {\n const key = this.getCacheKey(resourceType, recordId, appId, discriminator);\n const info = this.cache.get(key);\n\n if (info) {\n info.isAttached = true;\n info.attachedToTabId = tabId;\n info.lastUsed = new Date();\n // Replay this surface's agent client tools to the AI agent on reattach (cached components keep\n // their instance but never re-run ngAfterViewInit, so they don't re-register on their own).\n this.navigationService?.NotifyResourceReattached(info.componentRef.instance);\n }\n }\n\n /** @deprecated Use {@link MarkAsAttached}. */\n markAsAttached(resourceType: string, recordId: string, appId: string, tabId: string, discriminator?: string): void {\n return this.MarkAsAttached(resourceType, recordId, appId, tabId, discriminator);\n }\n\n /**\n * Mark a component as detached (available for reuse). Lookup by resource identity.\n *\n * This is the ONLY way to detach a component. Both single-resource mode and\n * Golden Layout mode use this same method to ensure consistent cache behavior.\n */\n MarkAsDetached(resourceType: string, recordId: string, appId: string, discriminator?: string): CachedComponentInfo | null {\n const key = this.getCacheKey(resourceType, recordId, appId, discriminator);\n const info = this.cache.get(key);\n if (!info) return null;\n\n info.isAttached = false;\n info.attachedToTabId = null;\n info.lastUsed = new Date();\n // Clear this surface's agent client tools on detach so the previous app's tools aren't offered to\n // the AI agent on the next surface. NotifyResourceReattached replays them if the user returns.\n this.navigationService?.NotifyResourceDetached(info.componentRef.instance);\n this.evictIfNeeded();\n return info;\n }\n\n /** @deprecated Use {@link MarkAsDetached}. */\n markAsDetached(resourceType: string, recordId: string, appId: string, discriminator?: string): CachedComponentInfo | null {\n return this.MarkAsDetached(resourceType, recordId, appId, discriminator);\n }\n\n /**\n * Re-key a cached component when its underlying record identity changes.\n *\n * Used when a \"new record\" component (cached at `recordId = ''`) becomes a saved record\n * (now identified by its new PK). Without re-keying, the next \"new record\" request would\n * still resolve to this same cache entry, surfacing the previously-saved record on a form\n * the user expects to be blank.\n *\n * Preserves the live component instance — only the cache key + stored identity change.\n * Returns true if a matching entry was found and re-keyed, false otherwise.\n */\n RekeyComponent(\n resourceType: string,\n oldRecordId: string,\n newRecordId: string,\n appId: string,\n oldDiscriminator?: string,\n newDiscriminator?: string\n ): boolean {\n const oldKey = this.getCacheKey(resourceType, oldRecordId, appId, oldDiscriminator);\n const info = this.cache.get(oldKey);\n if (!info) {\n return false;\n }\n\n // Once a record has a real id, the discriminator no longer matters (the id is unique).\n // Default the new discriminator to undefined unless explicitly provided.\n const newKey = this.getCacheKey(resourceType, newRecordId, appId, newDiscriminator);\n if (oldKey === newKey) {\n return false;\n }\n\n info.resourceRecordId = newRecordId;\n info.keyDiscriminator = newDiscriminator;\n info.lastUsed = new Date();\n if (info.resourceData) {\n info.resourceData.ResourceRecordID = newRecordId;\n }\n\n this.cache.delete(oldKey);\n this.cache.set(newKey, info);\n return true;\n }\n\n /** @deprecated Use {@link RekeyComponent}. */\n rekeyComponent(\n resourceType: string,\n oldRecordId: string,\n newRecordId: string,\n appId: string,\n oldDiscriminator?: string,\n newDiscriminator?: string\n ): boolean {\n return this.RekeyComponent(resourceType, oldRecordId, newRecordId, appId, oldDiscriminator, newDiscriminator);\n }\n\n /**\n * Find a cached component by tab ID and detach it.\n * This is a convenience wrapper for callers that only know the tab ID\n * (e.g., Golden Layout tab close events). It resolves the tab ID to\n * resource identity, then delegates to the identity-based markAsDetached.\n */\n FindAndDetachByTabId(tabId: string): CachedComponentInfo | null {\n const entry = Array.from(this.cache.entries())\n .find(([_, info]) => info.attachedToTabId === tabId);\n\n if (!entry) return null;\n\n const [_, info] = entry;\n return this.MarkAsDetached(info.resourceType, info.resourceRecordId, info.applicationId, info.keyDiscriminator);\n }\n\n /** @deprecated Use {@link FindAndDetachByTabId}. */\n findAndDetachByTabId(tabId: string): CachedComponentInfo | null {\n return this.FindAndDetachByTabId(tabId);\n }\n\n /**\n * Evict least-recently-used detached components when over the limit.\n * Only evicts components that are not currently attached.\n */\n private evictIfNeeded(): void {\n if (ComponentCacheManager.MaxDetachedComponents <= 0) return;\n\n const detached = Array.from(this.cache.entries())\n .filter(([_, info]) => !info.isAttached)\n .sort((a, b) => a[1].lastUsed.getTime() - b[1].lastUsed.getTime());\n\n while (detached.length > ComponentCacheManager.MaxDetachedComponents) {\n const [key, info] = detached.shift()!;\n // Drop the destroyed component's cached agent tools so the registry doesn't retain a dead ref.\n this.navigationService?.ForgetResource(info.componentRef.instance);\n this.appRef.detachView(info.componentRef.hostView);\n info.componentRef.destroy();\n this.cache.delete(key);\n }\n }\n\n /**\n * Get component info by tab ID (for finding what's attached to a tab).\n * Uses linear scan since tabId is metadata, not a key.\n */\n GetComponentByTabId(tabId: string): CachedComponentInfo | null {\n const entry = Array.from(this.cache.entries())\n .find(([_, info]) => info.attachedToTabId === tabId);\n\n return entry ? entry[1] : null;\n }\n\n /** @deprecated Use {@link GetComponentByTabId}. */\n getComponentByTabId(tabId: string): CachedComponentInfo | null {\n return this.GetComponentByTabId(tabId);\n }\n\n /**\n * Remove and destroy a specific component from cache by resource identity.\n */\n DestroyComponent(resourceType: string, recordId: string, appId: string, discriminator?: string): void {\n const key = this.getCacheKey(resourceType, recordId, appId, discriminator);\n const info = this.cache.get(key);\n\n if (!info) return;\n\n this.navigationService?.ForgetResource(info.componentRef.instance);\n this.appRef.detachView(info.componentRef.hostView);\n info.componentRef.destroy();\n this.cache.delete(key);\n }\n\n /** @deprecated Use {@link DestroyComponent}. */\n destroyComponent(resourceType: string, recordId: string, appId: string, discriminator?: string): void {\n return this.DestroyComponent(resourceType, recordId, appId, discriminator);\n }\n\n /**\n * Remove and destroy component by tab ID (convenience for Golden Layout tab close).\n */\n DestroyComponentByTabId(tabId: string): void {\n const entry = Array.from(this.cache.entries())\n .find(([_, info]) => info.attachedToTabId === tabId);\n\n if (!entry) return;\n\n const [key, info] = entry;\n this.appRef.detachView(info.componentRef.hostView);\n info.componentRef.destroy();\n this.cache.delete(key);\n }\n\n /** @deprecated Use {@link DestroyComponentByTabId}. */\n destroyComponentByTabId(tabId: string): void {\n return this.DestroyComponentByTabId(tabId);\n }\n\n /**\n * Clear the entire cache, destroying all components.\n * Call this on user logout or app shutdown.\n */\n ClearCache(): void {\n this.cache.forEach(info => {\n this.appRef.detachView(info.componentRef.hostView);\n info.componentRef.destroy();\n });\n this.cache.clear();\n }\n\n /** @deprecated Use {@link ClearCache}. */\n clearCache(): void {\n return this.ClearCache();\n }\n\n /**\n * Selectively clear cached components matching a predicate.\n * Components that match are destroyed; those that don't are kept.\n *\n * Use this for tenant switching: clear org-scoped components while\n * keeping system/global components alive.\n *\n * @param predicate Return true for components that should be destroyed.\n * @returns Number of components destroyed.\n */\n ClearCacheByPredicate(predicate: (info: CachedComponentInfo) => boolean): number {\n let destroyed = 0;\n const toRemove: string[] = [];\n\n this.cache.forEach((info, key) => {\n if (predicate(info)) {\n this.appRef.detachView(info.componentRef.hostView);\n info.componentRef.destroy();\n toRemove.push(key);\n destroyed++;\n }\n });\n\n for (const key of toRemove) {\n this.cache.delete(key);\n }\n\n return destroyed;\n }\n\n /**\n * Get cache statistics for debugging.\n */\n GetCacheStats(): {\n total: number;\n attached: number;\n detached: number;\n byResourceType: Map<string, number>;\n } {\n const stats = {\n total: this.cache.size,\n attached: 0,\n detached: 0,\n byResourceType: new Map<string, number>()\n };\n\n this.cache.forEach(info => {\n if (info.isAttached) {\n stats.attached++;\n } else {\n stats.detached++;\n }\n\n const count = stats.byResourceType.get(info.resourceType) || 0;\n stats.byResourceType.set(info.resourceType, count + 1);\n });\n\n return stats;\n }\n\n /** @deprecated Use {@link GetCacheStats}. */\n getCacheStats(): {\n total: number;\n attached: number;\n detached: number;\n byResourceType: Map<string, number>;\n } {\n return this.GetCacheStats();\n }\n}\n"]}