@memberjunction/ng-shared 6.1.0-edge.4 → 6.1.0-edge.6

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.
@@ -84,6 +84,9 @@ export declare abstract class BaseResourceComponent extends BaseNavigationCompon
84
84
  /**
85
85
  * Push query param changes to the URL. Creates a browser history entry.
86
86
  * Safe to call during OnQueryParamsChanged — auto-suppressed to prevent loops.
87
+ *
88
+ * The write is ALWAYS scoped to this component's own tab. A component that cannot
89
+ * identify its tab does not write at all — see the guard below.
87
90
  */
88
91
  protected UpdateQueryParams(params: Record<string, string | null>): void;
89
92
  /**
@@ -127,6 +130,28 @@ export declare abstract class BaseResourceComponent extends BaseNavigationCompon
127
130
  * delivers them immediately.
128
131
  */
129
132
  RebindTabId(tabId: string): void;
133
+ /**
134
+ * Hook for a host that instantiates CHILD resource components: re-home them here.
135
+ *
136
+ * A host stamps its children with its tab id when it creates them, which is a SNAPSHOT. A cache
137
+ * reattach moves the host to a different tab without recreating anything, so a child left
138
+ * holding the birth tab's id would go on reading and — worse — writing that tab's params from
139
+ * inside a tab it no longer belongs to. That is the same cross-tab corruption this class refuses
140
+ * elsewhere, just arriving by a slower route, so the stamp has to move when the host does.
141
+ *
142
+ * Default is a no-op: a component with no children has nothing to re-home.
143
+ */
144
+ protected onTabIdRebound(_tabId: string): void;
145
+ /**
146
+ * Re-homes a child this component created to `tabId`.
147
+ *
148
+ * The `ParentTabId` stamp is cleared FIRST and deliberately: `getTabId()` prefers it over the
149
+ * rebound id, so leaving the old stamp in place would make the child's own `RebindTabId` a
150
+ * no-op — it early-returns when `getTabId()` already matches — and the child would keep
151
+ * answering with the tab it was born in. Clearing it lets the rebind be the authority, and the
152
+ * child re-delivers the NEW tab's current params exactly as a fresh mount would.
153
+ */
154
+ protected rehomeChildToTab(child: BaseResourceComponent | null | undefined, tabId: string): void;
130
155
  private getQueryParamUpdateGuard;
131
156
  protected NotifyLoadComplete(): void;
132
157
  protected NotifyLoadStarted(): void;
@@ -162,6 +187,17 @@ export declare abstract class BaseResourceComponent extends BaseNavigationCompon
162
187
  * purpose. The tab-container listens for this and closes the workspace tab.
163
188
  */
164
189
  protected NotifyCloseRequested(): void;
190
+ /**
191
+ * True when this resource holds in-progress user edits that a silent
192
+ * replacement would destroy. Consulted by the shell before consuming a
193
+ * temporary record tab under the preview-tab model (see
194
+ * TabRequest.TempScope): an editing tab is never replaced — the next open
195
+ * lands in its own tab instead, so the edit survives.
196
+ *
197
+ * Default false: most resources are read-only surfaces with nothing to
198
+ * lose. Resources that host an editable form override it.
199
+ */
200
+ IsEditing(): boolean;
165
201
  abstract GetResourceDisplayName(data: ResourceData): Promise<string>;
166
202
  abstract GetResourceIconClass(data: ResourceData): Promise<string>;
167
203
  static ɵfac: i0.ɵɵFactoryDeclaration<BaseResourceComponent, never>;
@@ -1 +1 @@
1
- {"version":3,"file":"base-resource-component.d.ts","sourceRoot":"","sources":["../../src/lib/base-resource-component.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"base-resource-component.d.ts","sourceRoot":"","sources":["../../src/lib/base-resource-component.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import { ChangeDetectorRef, Directive, Input, inject } from "@angular/core";
2
2
  import { Subject } from "rxjs";
3
3
  import { filter, takeUntil } from "rxjs/operators";
4
+ import { LogError } from "@memberjunction/core";
4
5
  import { BaseNavigationComponent } from "./base-navigation-component";
5
6
  import { ResourceData } from "@memberjunction/core-entities";
6
7
  import { NavigationService } from "./navigation.service";
@@ -146,13 +147,29 @@ export class BaseResourceComponent extends BaseNavigationComponent {
146
147
  /**
147
148
  * Push query param changes to the URL. Creates a browser history entry.
148
149
  * Safe to call during OnQueryParamsChanged — auto-suppressed to prevent loops.
150
+ *
151
+ * The write is ALWAYS scoped to this component's own tab. A component that cannot
152
+ * identify its tab does not write at all — see the guard below.
149
153
  */
150
154
  UpdateQueryParams(params) {
151
155
  if (this._suppressQueryParamSync)
152
156
  return;
153
157
  const tabId = this.getTabId();
154
158
  if (!tabId) {
155
- this.navigationService.UpdateActiveTabQueryParams(params);
159
+ // Deliberately NO fallback to "the active tab". The active tab is whatever the user is
160
+ // looking at RIGHT NOW — which, for a background dashboard finishing an async load, is
161
+ // somebody else's deep link. That fallback silently rewrote the visible tab's URL from an
162
+ // invisible tab (a background Studio dashboard replacing a Review tab's ?id=…&tab=… with
163
+ // its own ?section=… twelve seconds after the user navigated). A correctly scoped write
164
+ // is already harmless from a background tab, so refusing here costs nothing and closes
165
+ // the corruption entirely.
166
+ //
167
+ // Fix the HOST, not this guard: whoever renders a resource component must give it a tab
168
+ // id — Data.Configuration.tabId (what the tab container sets) or the ParentTabId input
169
+ // (what a wrapper rendering a child dashboard sets).
170
+ LogError(`${this.constructor.name}.UpdateQueryParams: no tab id — query-param update DROPPED ` +
171
+ `(params: ${Object.keys(params).join(', ') || 'none'}). The host rendering this component must set ` +
172
+ `ParentTabId or Data.Configuration.tabId; writing to the active tab would corrupt whichever tab the user is viewing.`);
156
173
  return;
157
174
  }
158
175
  this.navigationService.UpdateTabQueryParams(tabId, params, this.getQueryParamUpdateGuard());
@@ -264,6 +281,37 @@ export class BaseResourceComponent extends BaseNavigationComponent {
264
281
  this.reboundTabId = tabId;
265
282
  this._lastDeliveredParamsKey = null;
266
283
  this.setupInitialParamDelivery();
284
+ this.onTabIdRebound(tabId);
285
+ }
286
+ /**
287
+ * Hook for a host that instantiates CHILD resource components: re-home them here.
288
+ *
289
+ * A host stamps its children with its tab id when it creates them, which is a SNAPSHOT. A cache
290
+ * reattach moves the host to a different tab without recreating anything, so a child left
291
+ * holding the birth tab's id would go on reading and — worse — writing that tab's params from
292
+ * inside a tab it no longer belongs to. That is the same cross-tab corruption this class refuses
293
+ * elsewhere, just arriving by a slower route, so the stamp has to move when the host does.
294
+ *
295
+ * Default is a no-op: a component with no children has nothing to re-home.
296
+ */
297
+ onTabIdRebound(_tabId) {
298
+ // no children by default
299
+ }
300
+ /**
301
+ * Re-homes a child this component created to `tabId`.
302
+ *
303
+ * The `ParentTabId` stamp is cleared FIRST and deliberately: `getTabId()` prefers it over the
304
+ * rebound id, so leaving the old stamp in place would make the child's own `RebindTabId` a
305
+ * no-op — it early-returns when `getTabId()` already matches — and the child would keep
306
+ * answering with the tab it was born in. Clearing it lets the rebind be the authority, and the
307
+ * child re-delivers the NEW tab's current params exactly as a fresh mount would.
308
+ */
309
+ rehomeChildToTab(child, tabId) {
310
+ if (!child) {
311
+ return;
312
+ }
313
+ child.ParentTabId = null;
314
+ child.RebindTabId(tabId);
267
315
  }
268
316
  getQueryParamUpdateGuard() {
269
317
  const config = this.Data?.Configuration || {};
@@ -349,6 +397,19 @@ export class BaseResourceComponent extends BaseNavigationComponent {
349
397
  this._resourceCloseRequestedEvent();
350
398
  }
351
399
  }
400
+ /**
401
+ * True when this resource holds in-progress user edits that a silent
402
+ * replacement would destroy. Consulted by the shell before consuming a
403
+ * temporary record tab under the preview-tab model (see
404
+ * TabRequest.TempScope): an editing tab is never replaced — the next open
405
+ * lands in its own tab instead, so the edit survives.
406
+ *
407
+ * Default false: most resources are read-only surfaces with nothing to
408
+ * lose. Resources that host an editable form override it.
409
+ */
410
+ IsEditing() {
411
+ return false;
412
+ }
352
413
  static ɵfac = /*@__PURE__*/ (() => { let ɵBaseResourceComponent_BaseFactory; return function BaseResourceComponent_Factory(__ngFactoryType__) { return (ɵBaseResourceComponent_BaseFactory || (ɵBaseResourceComponent_BaseFactory = i0.ɵɵgetInheritedFactory(BaseResourceComponent)))(__ngFactoryType__ || BaseResourceComponent); }; })();
353
414
  static ɵdir = /*@__PURE__*/ i0.ɵɵdefineDirective({ type: BaseResourceComponent, inputs: { ParentTabId: "ParentTabId" }, features: [i0.ɵɵInheritDefinitionFeature] });
354
415
  }
@@ -1 +1 @@
1
- {"version":3,"file":"base-resource-component.js","sourceRoot":"","sources":["../../src/lib/base-resource-component.ts"],"names":[],"mappings":"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","sourcesContent":["import { ChangeDetectorRef, Directive, OnInit, OnDestroy, Input, inject } from \"@angular/core\";\nimport { Subject, Subscription } from \"rxjs\";\nimport { filter, takeUntil } from \"rxjs/operators\";\nimport { BaseEntity } from \"@memberjunction/core\";\nimport { BaseNavigationComponent } from \"./base-navigation-component\";\nimport { ResourceData } from \"@memberjunction/core-entities\";\nimport { NavigationService, TabQueryParamUpdateGuard } from \"./navigation.service\";\n\n@Directive()\nexport abstract class BaseResourceComponent extends BaseNavigationComponent implements OnInit, OnDestroy {\n private _data: ResourceData = new ResourceData();\n private _suppressQueryParamSync = false;\n /** Stable key of the most recently delivered query params, used to de-duplicate\n * delivery across the reactive (initial/deep-link) and popstate paths. */\n private _lastDeliveredParamsKey: string | null = null;\n protected destroy$ = new Subject<void>();\n protected navigationService = inject(NavigationService);\n /**\n * Optional: the tab container instantiates LIGHTWEIGHT throwaway instances\n * of resource components (via runInInjectionContext with an environment\n * injector) purely to call GetResourceDisplayName for background tab\n * titles. ChangeDetectorRef only exists in node injectors, so a required\n * inject() there throws NG0201 and silently kills every background title\n * resolution — tabs keep raw \"Entity - ID|guid\" titles until opened.\n */\n private changeDetectorRef = inject(ChangeDetectorRef, { optional: true });\n\n /**\n * Tab ID for query param notification scoping. Set by resource wrappers\n * that render child dashboards, so the child knows which tab it belongs to.\n * If not set, falls back to Data.Configuration.tabId.\n */\n @Input() ParentTabId: string | null = null;\n\n /**\n * Tab id this component was RE-HOMED to by a cache reattach. The\n * component cache keys on driver+record+app — NOT tab id — so a cached\n * component can be reattached to a different tab than it was born under.\n * Without rebinding, its query-param subscription listens to the DEAD\n * birth tab forever and every delivery to the live tab is lost (the\n * \"crumb lands on the dashboard root after promote/demote\" bug).\n */\n private reboundTabId: string | null = null;\n\n /** Handle for the reactive param subscription so RebindTabId can replace it */\n private reactiveParamSub: Subscription | null = null;\n\n public get Data(): ResourceData {\n return this._data;\n }\n public set Data(value: ResourceData) {\n this._data = value;\n }\n\n private _loadComplete: boolean = false;\n public get LoadComplete(): boolean {\n return this._loadComplete;\n }\n\n /**\n * Watchdog window: if a resource component hasn't called {@link NotifyLoadComplete} within this\n * time, we log a warning naming the offending class AND fail open — forcing NotifyLoadComplete\n * so the app loading screen (which blocks on that signal) is released. Without the fail-open, a\n * resource that never signals — because it errored, hung, or has its own `ngOnInit` that\n * bypasses BaseDashboard's guarded lifecycle — would hang the whole Explorer forever. This is\n * the safety net covering EVERY BaseResourceComponent subclass, not just BaseDashboard (whose\n * own try/finally already guarantees the signal on the normal path).\n */\n private static readonly LOAD_COMPLETE_WATCHDOG_MS = 15_000;\n private _loadCompleteWatchdog: ReturnType<typeof setTimeout> | null = null;\n\n private _loadStarted: boolean = false;\n public get LoadStarted(): boolean {\n return this._loadStarted;\n }\n\n\n private _loadCompleteEvent: any = null;\n public get LoadCompleteEvent(): any {\n return this._loadCompleteEvent\n }\n public set LoadCompleteEvent(value: any) {\n this._loadCompleteEvent = value;\n }\n\n private _loadStartedEvent: any = null;\n public get LoadStartedEvent(): any {\n return this._loadStartedEvent\n }\n public set LoadStartedEvent(value: any) {\n this._loadStartedEvent = value;\n }\n\n private _resourceRecordSavedEvent: any = null;\n public get ResourceRecordSavedEvent(): any {\n return this._resourceRecordSavedEvent\n }\n public set ResourceRecordSavedEvent(value: any) {\n this._resourceRecordSavedEvent = value;\n }\n\n private _resourceCloseRequestedEvent: (() => void) | null = null;\n public get ResourceCloseRequestedEvent(): (() => void) | null {\n return this._resourceCloseRequestedEvent;\n }\n public set ResourceCloseRequestedEvent(value: (() => void) | null) {\n this._resourceCloseRequestedEvent = value;\n }\n\n private _displayNameChangedEvent: ((newName: string) => void) | null = null;\n public get DisplayNameChangedEvent(): ((newName: string) => void) | null {\n return this._displayNameChangedEvent;\n }\n public set DisplayNameChangedEvent(value: ((newName: string) => void) | null) {\n this._displayNameChangedEvent = value;\n }\n\n ngOnInit(): void {\n // Order matters: set up the popstate subscription first (a plain Subject — emits\n // nothing on subscribe), then the reactive initial-delivery subscription (backed by\n // a BehaviorSubject — emits the current params synchronously). This lets the initial\n // emission claim the 'deeplink' source before any later popstate event.\n this.setupQueryParamSubscription();\n this.setupInitialParamDelivery();\n\n // Start the NotifyLoadComplete watchdog (no-op once it fires or load completes).\n this._loadCompleteWatchdog = setTimeout(() => {\n if (!this._loadComplete) {\n // eslint-disable-next-line no-console\n console.warn(\n `[LoadComplete WATCHDOG] ${this.constructor.name} has NOT called NotifyLoadComplete() ` +\n `within ${BaseResourceComponent.LOAD_COMPLETE_WATCHDOG_MS / 1000}s — FAILING OPEN: forcing ` +\n `load-complete to release the app loading screen. This resource either errored/hung without ` +\n `signalling, or is simply still loading (a slow-but-healthy load re-signals harmlessly when ` +\n `it finishes). If you are chasing a \"stuck loading\" report, start here. ` +\n `(tabId=${this.getTabId() || 'n/a'})`\n );\n // Fail-open: the shell's loading screen blocks on NotifyLoadComplete. A subclass that\n // never calls it — one whose own ngOnInit bypasses BaseDashboard's guarded lifecycle,\n // or whose load genuinely hangs — would otherwise brick the whole Explorer. Force the\n // signal so no single resource can take down the shell. (Idempotent-safe: NotifyLoad\n // Complete is already called repeatedly on the normal path, e.g. every Refresh.)\n this.NotifyLoadComplete();\n }\n }, BaseResourceComponent.LOAD_COMPLETE_WATCHDOG_MS);\n }\n\n ngOnDestroy(): void {\n if (this._loadCompleteWatchdog) {\n clearTimeout(this._loadCompleteWatchdog);\n this._loadCompleteWatchdog = null;\n }\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n /**\n * Called by the framework when query params change from an external source\n * (browser back/forward, deep link navigation).\n * Override in subclasses to react to query param changes.\n * @param params The new query params from the URL\n * @param source 'popstate' for back/forward, 'deeplink' for external URL entry\n */\n protected OnQueryParamsChanged(params: Record<string, string>, source: 'popstate' | 'deeplink'): void {\n // Default no-op — override in subclasses\n }\n\n /**\n * Push query param changes to the URL. Creates a browser history entry.\n * Safe to call during OnQueryParamsChanged — auto-suppressed to prevent loops.\n */\n protected UpdateQueryParams(params: Record<string, string | null>): void {\n if (this._suppressQueryParamSync) return;\n const tabId = this.getTabId();\n if (!tabId) {\n this.navigationService.UpdateActiveTabQueryParams(params);\n return;\n }\n\n this.navigationService.UpdateTabQueryParams(tabId, params, this.getQueryParamUpdateGuard());\n }\n\n /**\n * Read current query params from tab configuration.\n * Use in initDashboard() / ngOnInit() to get initial URL state.\n */\n protected GetQueryParams(): Record<string, string> {\n return (this.Data?.Configuration?.['queryParams'] as Record<string, string>) || {};\n }\n\n /**\n * Internal: subscribe to NavigationService query param notifications.\n * Filters to only this component's tab to prevent cross-tab leakage.\n * This is the explicit back/forward (popstate) path.\n */\n private setupQueryParamSubscription(): void {\n this.navigationService.QueryParamChanged$\n .pipe(\n filter(event => {\n const myTabId = this.getTabId();\n return !myTabId || event.TabId === myTabId;\n }),\n takeUntil(this.destroy$)\n )\n .subscribe(event => this.deliverQueryParams(event.Params, event.Force === true));\n }\n\n /**\n * Internal: reactively deliver this tab's query params from the workspace\n * BehaviorSubject. Because the source replays the current value on subscribe and\n * emits future changes, this delivers initial deep-link params even when the\n * component mounts (e.g. from workspace restoration) before the URL params have\n * been merged into the tab configuration — closing the cold-load race that the\n * popstate-only path could not.\n */\n private setupInitialParamDelivery(): void {\n const tabId = this.getTabId();\n if (!tabId) {\n return; // No tab scope (e.g. embedded usage) — nothing to observe.\n }\n this.reactiveParamSub?.unsubscribe();\n this.reactiveParamSub = this.navigationService.ObserveTabQueryParams(tabId)\n .pipe(takeUntil(this.destroy$))\n .subscribe(params => this.deliverQueryParams(params));\n }\n\n /**\n * Internal: funnel point for both the reactive and popstate paths. De-duplicates\n * identical deliveries (the two paths overlap on back/forward) and labels the first\n * meaningful delivery as a 'deeplink', subsequent ones as 'popstate'.\n */\n private deliverQueryParams(params: Record<string, string>, force = false): void {\n const key = this.queryParamsKey(params);\n // The duplicate guard assumes delivered params == applied state, but a\n // component's state can DRIFT after a delivery (internal navigation\n // whose write-back was suppressed or lost). Explicit restores (origin\n // crumb) pass force to reach OnQueryParamsChanged regardless.\n if (key === this._lastDeliveredParamsKey && !force) {\n return; // Already delivered these exact params.\n }\n const isInitial = this._lastDeliveredParamsKey === null;\n // Don't fire an initial no-op: a component entered without deep-link params has\n // nothing to apply. Leave _lastDeliveredParamsKey null so the first real params\n // (whenever they arrive) are still treated as the deep-link entry.\n // FORCED empties bypass this too: an explicit reset (SwitchToAppHome\n // returning a drifted dashboard to its landing) must reach\n // OnQueryParamsChanged even when nothing was ever delivered.\n if (isInitial && Object.keys(params).length === 0 && !force) {\n return;\n }\n this._lastDeliveredParamsKey = key;\n const source: 'popstate' | 'deeplink' = isInitial ? 'deeplink' : 'popstate';\n // try/finally ensures the suppression flag is always cleared, and the view is always\n // refreshed, even if OnQueryParamsChanged throws.\n this._suppressQueryParamSync = true;\n try {\n this.OnQueryParamsChanged(params, source);\n } finally {\n this._suppressQueryParamSync = false;\n // Query params arrive via RxJS (workspace stream / popstate), not via a template event —\n // state a subclass changed in OnQueryParamsChanged needs an explicit re-render request\n // for dynamically-attached views (see RefreshView). Inside `finally` so a subclass\n // override that throws still renders whatever partial state it applied before throwing.\n this.RefreshView();\n }\n }\n\n /** Stable, order-independent string key for a query param record. */\n private queryParamsKey(params: Record<string, string>): string {\n return Object.keys(params)\n .sort()\n .map(k => `${k}=${params[k]}`)\n .join('&');\n }\n\n /**\n * Get this component's tab ID. Checks ParentTabId input first (set by resource\n * wrappers for child dashboards), then falls back to Data.Configuration.tabId.\n */\n public getTabId(): string {\n return this.ParentTabId || this.reboundTabId || this.Data?.Configuration?.['tabId'] as string || '';\n }\n\n /**\n * Re-home this component to a different tab (cache reattach across tab\n * ids). Replaces the reactive param subscription with one bound to the\n * new tab and resets the duplicate-delivery key so the new tab's\n * CURRENT params apply as a fresh mount would — the replay-on-subscribe\n * delivers them immediately.\n */\n public RebindTabId(tabId: string): void {\n if (this.getTabId() === tabId) {\n return;\n }\n this.reboundTabId = tabId;\n this._lastDeliveredParamsKey = null;\n this.setupInitialParamDelivery();\n }\n\n private getQueryParamUpdateGuard(): TabQueryParamUpdateGuard {\n const config = this.Data?.Configuration || {};\n return {\n resourceType: config['resourceType'] as string | undefined,\n driverClass: (config['resourceTypeDriverClass'] || config['driverClass']) as string | undefined,\n recordId: (this.Data?.ResourceRecordID || config['recordId']) as string | undefined,\n navItemName: config['navItemName'] as string | undefined,\n entity: (config['Entity'] || config['entity']) as string | undefined\n };\n }\n\n protected NotifyLoadComplete() {\n this._loadComplete = true;\n if (this._loadCompleteWatchdog) {\n clearTimeout(this._loadCompleteWatchdog);\n this._loadCompleteWatchdog = null;\n }\n if (this._loadCompleteEvent) {\n this._loadCompleteEvent();\n }\n // Load completion almost always follows async work (RunView/fetch continuations) that\n // Angular's tick will NOT pick up on its own for dynamically-attached views — re-render\n // must be requested explicitly. See RefreshView() for the full explanation.\n this.RefreshView();\n }\n\n protected NotifyLoadStarted() {\n this._loadStarted = true;\n if (this._loadStartedEvent) {\n this._loadStartedEvent();\n }\n this.RefreshView();\n }\n\n /**\n * Marks this component's view (and its ancestors) as needing change detection and schedules\n * a tick — the reliable way for a resource component to re-render after updating state from\n * async work (RunView/RunQuery continuations, timers, websocket pushes, RxJS subscriptions).\n *\n * WHY THIS EXISTS: the Explorer shell hosts resource components dynamically — `createComponent()`\n * + `ApplicationRef.attachView()` — because tab content mounts into Golden Layout / cached DOM\n * containers, not into an Angular template. Since Angular 18's change-detection scheduler\n * rework (Explorer is on Angular 21), `ApplicationRef.tick()` only refreshes attached views\n * that are FLAGGED dirty; a root-level attached view whose component mutates plain fields from\n * an async continuation is never flagged, so the view silently never re-renders — even though\n * the component uses default (non-OnPush) change detection (issue #3106).\n *\n * The framework calls this automatically from NotifyLoadStarted() / NotifyLoadComplete(), so\n * the standard load lifecycle re-renders without any subclass action. Call it yourself after\n * any LATER async state change that must reach the DOM outside those signals.\n */\n protected RefreshView(): void {\n // Null on lightweight non-view instances (background title resolution)\n this.changeDetectorRef?.markForCheck();\n }\n\n\n\n /**\n * Call this to notify the tab system that the resource's display name has changed.\n * The tab container will update the tab title and browser title accordingly.\n */\n protected NotifyDisplayNameChanged(newName: string): void {\n if (this._displayNameChangedEvent) {\n this._displayNameChangedEvent(newName);\n }\n }\n\n protected ResourceRecordSaved(resourceRecordEntity: BaseEntity) {\n // Use ToURLSegment, NOT ToString. ResourceRecordID is consumed by\n // EntityRecordResource.GetPrimaryKey → CompositeKey.LoadFromURLSegment, which\n // expects URL-segment format (e.g. \"ID|<uuid>\"). ToString produces \"ID=<uuid>\",\n // which LoadFromURLSegment treats as a raw value and double-prefixes —\n // breaking any code path that re-reads this field after save.\n this.Data.ResourceRecordID = resourceRecordEntity.PrimaryKey.ToURLSegment();\n if (this._resourceRecordSavedEvent) {\n this._resourceRecordSavedEvent(resourceRecordEntity);\n }\n }\n\n /**\n * Ask the host shell to close/dismiss this resource (typically: close the tab).\n * Called by subclasses that hosted a form that emitted a 'dismiss' navigation\n * event — most often, a brand-new record where the user clicked Discard. The\n * record was never saved, the form is empty, and leaving it open serves no\n * purpose. The tab-container listens for this and closes the workspace tab.\n */\n protected NotifyCloseRequested(): void {\n if (this._resourceCloseRequestedEvent) {\n this._resourceCloseRequestedEvent();\n }\n }\n\n abstract GetResourceDisplayName(data: ResourceData): Promise<string>\n\n abstract GetResourceIconClass(data: ResourceData): Promise<string>\n}\n"]}
1
+ {"version":3,"file":"base-resource-component.js","sourceRoot":"","sources":["../../src/lib/base-resource-component.ts"],"names":[],"mappings":"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","sourcesContent":["import { ChangeDetectorRef, Directive, OnInit, OnDestroy, Input, inject } from \"@angular/core\";\nimport { Subject, Subscription } from \"rxjs\";\nimport { filter, takeUntil } from \"rxjs/operators\";\nimport { BaseEntity, LogError } from \"@memberjunction/core\";\nimport { BaseNavigationComponent } from \"./base-navigation-component\";\nimport { ResourceData } from \"@memberjunction/core-entities\";\nimport { NavigationService, TabQueryParamUpdateGuard } from \"./navigation.service\";\n\n@Directive()\nexport abstract class BaseResourceComponent extends BaseNavigationComponent implements OnInit, OnDestroy {\n private _data: ResourceData = new ResourceData();\n private _suppressQueryParamSync = false;\n /** Stable key of the most recently delivered query params, used to de-duplicate\n * delivery across the reactive (initial/deep-link) and popstate paths. */\n private _lastDeliveredParamsKey: string | null = null;\n protected destroy$ = new Subject<void>();\n protected navigationService = inject(NavigationService);\n /**\n * Optional: the tab container instantiates LIGHTWEIGHT throwaway instances\n * of resource components (via runInInjectionContext with an environment\n * injector) purely to call GetResourceDisplayName for background tab\n * titles. ChangeDetectorRef only exists in node injectors, so a required\n * inject() there throws NG0201 and silently kills every background title\n * resolution — tabs keep raw \"Entity - ID|guid\" titles until opened.\n */\n private changeDetectorRef = inject(ChangeDetectorRef, { optional: true });\n\n /**\n * Tab ID for query param notification scoping. Set by resource wrappers\n * that render child dashboards, so the child knows which tab it belongs to.\n * If not set, falls back to Data.Configuration.tabId.\n */\n @Input() ParentTabId: string | null = null;\n\n /**\n * Tab id this component was RE-HOMED to by a cache reattach. The\n * component cache keys on driver+record+app — NOT tab id — so a cached\n * component can be reattached to a different tab than it was born under.\n * Without rebinding, its query-param subscription listens to the DEAD\n * birth tab forever and every delivery to the live tab is lost (the\n * \"crumb lands on the dashboard root after promote/demote\" bug).\n */\n private reboundTabId: string | null = null;\n\n /** Handle for the reactive param subscription so RebindTabId can replace it */\n private reactiveParamSub: Subscription | null = null;\n\n public get Data(): ResourceData {\n return this._data;\n }\n public set Data(value: ResourceData) {\n this._data = value;\n }\n\n private _loadComplete: boolean = false;\n public get LoadComplete(): boolean {\n return this._loadComplete;\n }\n\n /**\n * Watchdog window: if a resource component hasn't called {@link NotifyLoadComplete} within this\n * time, we log a warning naming the offending class AND fail open — forcing NotifyLoadComplete\n * so the app loading screen (which blocks on that signal) is released. Without the fail-open, a\n * resource that never signals — because it errored, hung, or has its own `ngOnInit` that\n * bypasses BaseDashboard's guarded lifecycle — would hang the whole Explorer forever. This is\n * the safety net covering EVERY BaseResourceComponent subclass, not just BaseDashboard (whose\n * own try/finally already guarantees the signal on the normal path).\n */\n private static readonly LOAD_COMPLETE_WATCHDOG_MS = 15_000;\n private _loadCompleteWatchdog: ReturnType<typeof setTimeout> | null = null;\n\n private _loadStarted: boolean = false;\n public get LoadStarted(): boolean {\n return this._loadStarted;\n }\n\n\n private _loadCompleteEvent: any = null;\n public get LoadCompleteEvent(): any {\n return this._loadCompleteEvent\n }\n public set LoadCompleteEvent(value: any) {\n this._loadCompleteEvent = value;\n }\n\n private _loadStartedEvent: any = null;\n public get LoadStartedEvent(): any {\n return this._loadStartedEvent\n }\n public set LoadStartedEvent(value: any) {\n this._loadStartedEvent = value;\n }\n\n private _resourceRecordSavedEvent: any = null;\n public get ResourceRecordSavedEvent(): any {\n return this._resourceRecordSavedEvent\n }\n public set ResourceRecordSavedEvent(value: any) {\n this._resourceRecordSavedEvent = value;\n }\n\n private _resourceCloseRequestedEvent: (() => void) | null = null;\n public get ResourceCloseRequestedEvent(): (() => void) | null {\n return this._resourceCloseRequestedEvent;\n }\n public set ResourceCloseRequestedEvent(value: (() => void) | null) {\n this._resourceCloseRequestedEvent = value;\n }\n\n private _displayNameChangedEvent: ((newName: string) => void) | null = null;\n public get DisplayNameChangedEvent(): ((newName: string) => void) | null {\n return this._displayNameChangedEvent;\n }\n public set DisplayNameChangedEvent(value: ((newName: string) => void) | null) {\n this._displayNameChangedEvent = value;\n }\n\n ngOnInit(): void {\n // Order matters: set up the popstate subscription first (a plain Subject — emits\n // nothing on subscribe), then the reactive initial-delivery subscription (backed by\n // a BehaviorSubject — emits the current params synchronously). This lets the initial\n // emission claim the 'deeplink' source before any later popstate event.\n this.setupQueryParamSubscription();\n this.setupInitialParamDelivery();\n\n // Start the NotifyLoadComplete watchdog (no-op once it fires or load completes).\n this._loadCompleteWatchdog = setTimeout(() => {\n if (!this._loadComplete) {\n // eslint-disable-next-line no-console\n console.warn(\n `[LoadComplete WATCHDOG] ${this.constructor.name} has NOT called NotifyLoadComplete() ` +\n `within ${BaseResourceComponent.LOAD_COMPLETE_WATCHDOG_MS / 1000}s — FAILING OPEN: forcing ` +\n `load-complete to release the app loading screen. This resource either errored/hung without ` +\n `signalling, or is simply still loading (a slow-but-healthy load re-signals harmlessly when ` +\n `it finishes). If you are chasing a \"stuck loading\" report, start here. ` +\n `(tabId=${this.getTabId() || 'n/a'})`\n );\n // Fail-open: the shell's loading screen blocks on NotifyLoadComplete. A subclass that\n // never calls it — one whose own ngOnInit bypasses BaseDashboard's guarded lifecycle,\n // or whose load genuinely hangs — would otherwise brick the whole Explorer. Force the\n // signal so no single resource can take down the shell. (Idempotent-safe: NotifyLoad\n // Complete is already called repeatedly on the normal path, e.g. every Refresh.)\n this.NotifyLoadComplete();\n }\n }, BaseResourceComponent.LOAD_COMPLETE_WATCHDOG_MS);\n }\n\n ngOnDestroy(): void {\n if (this._loadCompleteWatchdog) {\n clearTimeout(this._loadCompleteWatchdog);\n this._loadCompleteWatchdog = null;\n }\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n /**\n * Called by the framework when query params change from an external source\n * (browser back/forward, deep link navigation).\n * Override in subclasses to react to query param changes.\n * @param params The new query params from the URL\n * @param source 'popstate' for back/forward, 'deeplink' for external URL entry\n */\n protected OnQueryParamsChanged(params: Record<string, string>, source: 'popstate' | 'deeplink'): void {\n // Default no-op — override in subclasses\n }\n\n /**\n * Push query param changes to the URL. Creates a browser history entry.\n * Safe to call during OnQueryParamsChanged — auto-suppressed to prevent loops.\n *\n * The write is ALWAYS scoped to this component's own tab. A component that cannot\n * identify its tab does not write at all — see the guard below.\n */\n protected UpdateQueryParams(params: Record<string, string | null>): void {\n if (this._suppressQueryParamSync) return;\n const tabId = this.getTabId();\n if (!tabId) {\n // Deliberately NO fallback to \"the active tab\". The active tab is whatever the user is\n // looking at RIGHT NOW — which, for a background dashboard finishing an async load, is\n // somebody else's deep link. That fallback silently rewrote the visible tab's URL from an\n // invisible tab (a background Studio dashboard replacing a Review tab's ?id=…&tab=… with\n // its own ?section=… twelve seconds after the user navigated). A correctly scoped write\n // is already harmless from a background tab, so refusing here costs nothing and closes\n // the corruption entirely.\n //\n // Fix the HOST, not this guard: whoever renders a resource component must give it a tab\n // id — Data.Configuration.tabId (what the tab container sets) or the ParentTabId input\n // (what a wrapper rendering a child dashboard sets).\n LogError(\n `${this.constructor.name}.UpdateQueryParams: no tab id — query-param update DROPPED ` +\n `(params: ${Object.keys(params).join(', ') || 'none'}). The host rendering this component must set ` +\n `ParentTabId or Data.Configuration.tabId; writing to the active tab would corrupt whichever tab the user is viewing.`\n );\n return;\n }\n\n this.navigationService.UpdateTabQueryParams(tabId, params, this.getQueryParamUpdateGuard());\n }\n\n /**\n * Read current query params from tab configuration.\n * Use in initDashboard() / ngOnInit() to get initial URL state.\n */\n protected GetQueryParams(): Record<string, string> {\n return (this.Data?.Configuration?.['queryParams'] as Record<string, string>) || {};\n }\n\n /**\n * Internal: subscribe to NavigationService query param notifications.\n * Filters to only this component's tab to prevent cross-tab leakage.\n * This is the explicit back/forward (popstate) path.\n */\n private setupQueryParamSubscription(): void {\n this.navigationService.QueryParamChanged$\n .pipe(\n filter(event => {\n const myTabId = this.getTabId();\n return !myTabId || event.TabId === myTabId;\n }),\n takeUntil(this.destroy$)\n )\n .subscribe(event => this.deliverQueryParams(event.Params, event.Force === true));\n }\n\n /**\n * Internal: reactively deliver this tab's query params from the workspace\n * BehaviorSubject. Because the source replays the current value on subscribe and\n * emits future changes, this delivers initial deep-link params even when the\n * component mounts (e.g. from workspace restoration) before the URL params have\n * been merged into the tab configuration — closing the cold-load race that the\n * popstate-only path could not.\n */\n private setupInitialParamDelivery(): void {\n const tabId = this.getTabId();\n if (!tabId) {\n return; // No tab scope (e.g. embedded usage) — nothing to observe.\n }\n this.reactiveParamSub?.unsubscribe();\n this.reactiveParamSub = this.navigationService.ObserveTabQueryParams(tabId)\n .pipe(takeUntil(this.destroy$))\n .subscribe(params => this.deliverQueryParams(params));\n }\n\n /**\n * Internal: funnel point for both the reactive and popstate paths. De-duplicates\n * identical deliveries (the two paths overlap on back/forward) and labels the first\n * meaningful delivery as a 'deeplink', subsequent ones as 'popstate'.\n */\n private deliverQueryParams(params: Record<string, string>, force = false): void {\n const key = this.queryParamsKey(params);\n // The duplicate guard assumes delivered params == applied state, but a\n // component's state can DRIFT after a delivery (internal navigation\n // whose write-back was suppressed or lost). Explicit restores (origin\n // crumb) pass force to reach OnQueryParamsChanged regardless.\n if (key === this._lastDeliveredParamsKey && !force) {\n return; // Already delivered these exact params.\n }\n const isInitial = this._lastDeliveredParamsKey === null;\n // Don't fire an initial no-op: a component entered without deep-link params has\n // nothing to apply. Leave _lastDeliveredParamsKey null so the first real params\n // (whenever they arrive) are still treated as the deep-link entry.\n // FORCED empties bypass this too: an explicit reset (SwitchToAppHome\n // returning a drifted dashboard to its landing) must reach\n // OnQueryParamsChanged even when nothing was ever delivered.\n if (isInitial && Object.keys(params).length === 0 && !force) {\n return;\n }\n this._lastDeliveredParamsKey = key;\n const source: 'popstate' | 'deeplink' = isInitial ? 'deeplink' : 'popstate';\n // try/finally ensures the suppression flag is always cleared, and the view is always\n // refreshed, even if OnQueryParamsChanged throws.\n this._suppressQueryParamSync = true;\n try {\n this.OnQueryParamsChanged(params, source);\n } finally {\n this._suppressQueryParamSync = false;\n // Query params arrive via RxJS (workspace stream / popstate), not via a template event —\n // state a subclass changed in OnQueryParamsChanged needs an explicit re-render request\n // for dynamically-attached views (see RefreshView). Inside `finally` so a subclass\n // override that throws still renders whatever partial state it applied before throwing.\n this.RefreshView();\n }\n }\n\n /** Stable, order-independent string key for a query param record. */\n private queryParamsKey(params: Record<string, string>): string {\n return Object.keys(params)\n .sort()\n .map(k => `${k}=${params[k]}`)\n .join('&');\n }\n\n /**\n * Get this component's tab ID. Checks ParentTabId input first (set by resource\n * wrappers for child dashboards), then falls back to Data.Configuration.tabId.\n */\n public getTabId(): string {\n return this.ParentTabId || this.reboundTabId || this.Data?.Configuration?.['tabId'] as string || '';\n }\n\n /**\n * Re-home this component to a different tab (cache reattach across tab\n * ids). Replaces the reactive param subscription with one bound to the\n * new tab and resets the duplicate-delivery key so the new tab's\n * CURRENT params apply as a fresh mount would — the replay-on-subscribe\n * delivers them immediately.\n */\n public RebindTabId(tabId: string): void {\n if (this.getTabId() === tabId) {\n return;\n }\n this.reboundTabId = tabId;\n this._lastDeliveredParamsKey = null;\n this.setupInitialParamDelivery();\n this.onTabIdRebound(tabId);\n }\n\n /**\n * Hook for a host that instantiates CHILD resource components: re-home them here.\n *\n * A host stamps its children with its tab id when it creates them, which is a SNAPSHOT. A cache\n * reattach moves the host to a different tab without recreating anything, so a child left\n * holding the birth tab's id would go on reading and — worse — writing that tab's params from\n * inside a tab it no longer belongs to. That is the same cross-tab corruption this class refuses\n * elsewhere, just arriving by a slower route, so the stamp has to move when the host does.\n *\n * Default is a no-op: a component with no children has nothing to re-home.\n */\n protected onTabIdRebound(_tabId: string): void {\n // no children by default\n }\n\n /**\n * Re-homes a child this component created to `tabId`.\n *\n * The `ParentTabId` stamp is cleared FIRST and deliberately: `getTabId()` prefers it over the\n * rebound id, so leaving the old stamp in place would make the child's own `RebindTabId` a\n * no-op — it early-returns when `getTabId()` already matches — and the child would keep\n * answering with the tab it was born in. Clearing it lets the rebind be the authority, and the\n * child re-delivers the NEW tab's current params exactly as a fresh mount would.\n */\n protected rehomeChildToTab(child: BaseResourceComponent | null | undefined, tabId: string): void {\n if (!child) {\n return;\n }\n child.ParentTabId = null;\n child.RebindTabId(tabId);\n }\n\n private getQueryParamUpdateGuard(): TabQueryParamUpdateGuard {\n const config = this.Data?.Configuration || {};\n return {\n resourceType: config['resourceType'] as string | undefined,\n driverClass: (config['resourceTypeDriverClass'] || config['driverClass']) as string | undefined,\n recordId: (this.Data?.ResourceRecordID || config['recordId']) as string | undefined,\n navItemName: config['navItemName'] as string | undefined,\n entity: (config['Entity'] || config['entity']) as string | undefined\n };\n }\n\n protected NotifyLoadComplete() {\n this._loadComplete = true;\n if (this._loadCompleteWatchdog) {\n clearTimeout(this._loadCompleteWatchdog);\n this._loadCompleteWatchdog = null;\n }\n if (this._loadCompleteEvent) {\n this._loadCompleteEvent();\n }\n // Load completion almost always follows async work (RunView/fetch continuations) that\n // Angular's tick will NOT pick up on its own for dynamically-attached views — re-render\n // must be requested explicitly. See RefreshView() for the full explanation.\n this.RefreshView();\n }\n\n protected NotifyLoadStarted() {\n this._loadStarted = true;\n if (this._loadStartedEvent) {\n this._loadStartedEvent();\n }\n this.RefreshView();\n }\n\n /**\n * Marks this component's view (and its ancestors) as needing change detection and schedules\n * a tick — the reliable way for a resource component to re-render after updating state from\n * async work (RunView/RunQuery continuations, timers, websocket pushes, RxJS subscriptions).\n *\n * WHY THIS EXISTS: the Explorer shell hosts resource components dynamically — `createComponent()`\n * + `ApplicationRef.attachView()` — because tab content mounts into Golden Layout / cached DOM\n * containers, not into an Angular template. Since Angular 18's change-detection scheduler\n * rework (Explorer is on Angular 21), `ApplicationRef.tick()` only refreshes attached views\n * that are FLAGGED dirty; a root-level attached view whose component mutates plain fields from\n * an async continuation is never flagged, so the view silently never re-renders — even though\n * the component uses default (non-OnPush) change detection (issue #3106).\n *\n * The framework calls this automatically from NotifyLoadStarted() / NotifyLoadComplete(), so\n * the standard load lifecycle re-renders without any subclass action. Call it yourself after\n * any LATER async state change that must reach the DOM outside those signals.\n */\n protected RefreshView(): void {\n // Null on lightweight non-view instances (background title resolution)\n this.changeDetectorRef?.markForCheck();\n }\n\n\n\n /**\n * Call this to notify the tab system that the resource's display name has changed.\n * The tab container will update the tab title and browser title accordingly.\n */\n protected NotifyDisplayNameChanged(newName: string): void {\n if (this._displayNameChangedEvent) {\n this._displayNameChangedEvent(newName);\n }\n }\n\n protected ResourceRecordSaved(resourceRecordEntity: BaseEntity) {\n // Use ToURLSegment, NOT ToString. ResourceRecordID is consumed by\n // EntityRecordResource.GetPrimaryKey → CompositeKey.LoadFromURLSegment, which\n // expects URL-segment format (e.g. \"ID|<uuid>\"). ToString produces \"ID=<uuid>\",\n // which LoadFromURLSegment treats as a raw value and double-prefixes —\n // breaking any code path that re-reads this field after save.\n this.Data.ResourceRecordID = resourceRecordEntity.PrimaryKey.ToURLSegment();\n if (this._resourceRecordSavedEvent) {\n this._resourceRecordSavedEvent(resourceRecordEntity);\n }\n }\n\n /**\n * Ask the host shell to close/dismiss this resource (typically: close the tab).\n * Called by subclasses that hosted a form that emitted a 'dismiss' navigation\n * event — most often, a brand-new record where the user clicked Discard. The\n * record was never saved, the form is empty, and leaving it open serves no\n * purpose. The tab-container listens for this and closes the workspace tab.\n */\n protected NotifyCloseRequested(): void {\n if (this._resourceCloseRequestedEvent) {\n this._resourceCloseRequestedEvent();\n }\n }\n\n /**\n * True when this resource holds in-progress user edits that a silent\n * replacement would destroy. Consulted by the shell before consuming a\n * temporary record tab under the preview-tab model (see\n * TabRequest.TempScope): an editing tab is never replaced — the next open\n * lands in its own tab instead, so the edit survives.\n *\n * Default false: most resources are read-only surfaces with nothing to\n * lose. Resources that host an editable form override it.\n */\n public IsEditing(): boolean {\n return false;\n }\n\n abstract GetResourceDisplayName(data: ResourceData): Promise<string>\n\n abstract GetResourceIconClass(data: ResourceData): Promise<string>\n}\n"]}
@@ -374,6 +374,18 @@ export declare class NavigationService implements OnDestroy {
374
374
  * This updates the tab's configuration and triggers a URL sync via the shell's
375
375
  * workspace configuration subscription.
376
376
  *
377
+ * 🚨 NEVER CALL THIS FROM A RESOURCE COMPONENT OR DASHBOARD. "The active tab" is whatever the
378
+ * user is looking at right now, which is not necessarily the tab the caller lives in. A component
379
+ * whose async work completes while it sits in a BACKGROUND tab will rewrite — and destroy — the
380
+ * deep link of the tab the user is actually viewing. Components use
381
+ * {@link BaseResourceComponent.UpdateQueryParams}, which routes to {@link UpdateTabQueryParams}
382
+ * with their own tab id and refuses to write when they have none.
383
+ *
384
+ * This remains only for shell-level navigation code that is, by construction, acting on behalf of
385
+ * the user's current tab (nothing in this repo does today).
386
+ *
387
+ * @deprecated Prefer {@link UpdateTabQueryParams} with an explicit tab id.
388
+ *
377
389
  * Use this instead of directly calling router.navigate() to ensure proper
378
390
  * URL management that respects app-scoped routes.
379
391
  *
@@ -1 +1 @@
1
- {"version":3,"file":"navigation.service.d.ts","sourceRoot":"","sources":["../../src/lib/navigation.service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"navigation.service.d.ts","sourceRoot":"","sources":["../../src/lib/navigation.service.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { Injectable } from '@angular/core';
2
- import { IsRecordTabsStyle, RECORDS_RESOURCE_TYPE, IsRecordsTabConfiguration } from './record-open-style';
2
+ import { IsRecordTabsStyle, RECORDS_RESOURCE_TYPE, IsRecordsTabConfiguration, GetRecordSourceContext, TruncateRecordOriginChain } from './record-open-style';
3
3
  import { CompositeKey } from '@memberjunction/core';
4
4
  import { fromEvent, BehaviorSubject, Subject } from 'rxjs';
5
5
  import { map, distinctUntilChanged } from 'rxjs/operators';
@@ -385,7 +385,13 @@ export class NavigationService {
385
385
  return existing.id;
386
386
  }
387
387
  }
388
- let forceNew = tabsMode || this.shouldForceNewTab(options);
388
+ // Records style no longer forces a new tab unconditionally: a plain click
389
+ // consumes the records region's temporary tab (preview-tab behavior), and
390
+ // only shift — or an explicit forceNewTab, which is what finally makes
391
+ // single-record's "Open in New Tab" do something — adds a second tab.
392
+ // The region scoping below is what keeps a nav click from ever consuming
393
+ // a record, which is the protection the old unconditional force provided.
394
+ let forceNew = this.shouldForceNewTab(options);
389
395
  const request = {
390
396
  ApplicationId: appId,
391
397
  Title: `${entityName} - ${recordId}`,
@@ -397,10 +403,12 @@ export class NavigationService {
397
403
  },
398
404
  ResourceRecordId: recordId,
399
405
  IsPinned: options?.pinTab || false,
400
- // Records style: opening a record must not pin the nav tab (see
401
- // TabRequest.PreservePinState) a pinned nav tab forces the main tab
402
- // bar visible on every nav page.
403
- PreservePinState: tabsMode
406
+ // Records style: this open belongs to the RECORDS temp-tab pool, so both
407
+ // consumption and the pin cascade stay inside the region and the nav
408
+ // tab's temp status is untouched (a pinned nav tab would force the main
409
+ // tab bar visible on every nav page). Classic style keeps the single
410
+ // 'main' pool.
411
+ TempScope: tabsMode ? 'records' : 'main'
404
412
  };
405
413
  // Handle transition from single-resource mode
406
414
  forceNew = this.handleSingleResourceModeTransition(forceNew, request);
@@ -459,6 +467,9 @@ export class NavigationService {
459
467
  sourceQueryParams: undefined,
460
468
  sourceRecordEntity: undefined,
461
469
  sourceRecordId: undefined,
470
+ // Clear the ancestor chain too — this is an explicit re-capture, and a
471
+ // surviving chain would hand the crumb an origin from a previous open.
472
+ sourceParentOrigin: undefined,
462
473
  ...this.resolveSourceContext(options)
463
474
  });
464
475
  }
@@ -488,6 +499,17 @@ export class NavigationService {
488
499
  if (src.sourceQueryParams && Object.keys(src.sourceQueryParams).length > 0) {
489
500
  context['sourceQueryParams'] = src.sourceQueryParams;
490
501
  }
502
+ if (src.sourceRecordEntity)
503
+ context['sourceRecordEntity'] = src.sourceRecordEntity;
504
+ if (src.sourceRecordId)
505
+ context['sourceRecordId'] = src.sourceRecordId;
506
+ // An explicit origin can carry an ancestor chain (ReturnToRecordSource
507
+ // replays one). Dropping it here would re-lose the entry point on the
508
+ // very navigation that exists to restore it.
509
+ const chained = TruncateRecordOriginChain(src.sourceParentOrigin);
510
+ if (chained) {
511
+ context['sourceParentOrigin'] = chained;
512
+ }
491
513
  return context;
492
514
  }
493
515
  return this.captureSourceContext();
@@ -523,6 +545,15 @@ export class NavigationService {
523
545
  context['sourceLabel'] = activeTab.title;
524
546
  context['sourceRecordEntity'] = parentEntity;
525
547
  context['sourceRecordId'] = parentRecordId;
548
+ // Carry the parent's OWN origin forward. Preview-tab replacement
549
+ // consumes the parent's tab, so returning to it later re-opens rather
550
+ // than reactivates — and a re-open would recapture the CHILD as the
551
+ // origin. Keeping the chain is what lets the return restore the real
552
+ // entry point instead of pointing the two records at each other.
553
+ const parentOrigin = TruncateRecordOriginChain(GetRecordSourceContext(activeTab.configuration));
554
+ if (parentOrigin) {
555
+ context['sourceParentOrigin'] = parentOrigin;
556
+ }
526
557
  }
527
558
  return context;
528
559
  }
@@ -606,7 +637,14 @@ export class NavigationService {
606
637
  }
607
638
  const parentKey = new CompositeKey();
608
639
  parentKey.SimpleLoadFromURLSegment(origin.sourceRecordId);
609
- this.OpenEntityRecord(origin.sourceRecordEntity, parentKey);
640
+ // RESTORE the parent's origin; do not let the re-open capture a fresh
641
+ // one. Standing on the child at this moment, a capture would make the
642
+ // child the parent's origin — the two records would point at each other
643
+ // and the real entry point would be unreachable. 'none' when we have no
644
+ // chain: a missing crumb beats a circular one.
645
+ this.OpenEntityRecord(origin.sourceRecordEntity, parentKey, {
646
+ recordSource: origin.sourceParentOrigin ?? 'none'
647
+ });
610
648
  return;
611
649
  }
612
650
  if (!origin.sourceAppId) {
@@ -836,6 +874,9 @@ export class NavigationService {
836
874
  const activeApp = this.appManager.GetActiveApp();
837
875
  const appId = tabsMode && activeApp ? activeApp.ID : this.getDefaultApplicationId();
838
876
  const appColor = tabsMode && activeApp ? activeApp.GetColor() : this.getDefaultAppColor();
877
+ // A NEW record keeps forcing its own tab under the records style: it is
878
+ // unsaved work from the moment it opens, so it must never land on top of
879
+ // a record the user is reading.
839
880
  let forceNew = tabsMode || this.shouldForceNewTab(options);
840
881
  const request = {
841
882
  ApplicationId: appId,
@@ -849,8 +890,12 @@ export class NavigationService {
849
890
  ...this.resolveSourceContext(options)
850
891
  },
851
892
  ResourceRecordId: '', // Empty for new records
852
- IsPinned: options?.pinTab || false,
853
- PreservePinState: tabsMode
893
+ // Pinned under the records style so the region's preview replacement can
894
+ // never consume it: an unsaved new record is exactly the tab that must
895
+ // not vanish when the user clicks the next row in a grid. (VS Code holds
896
+ // the same line — an untitled buffer is never a preview tab.)
897
+ IsPinned: options?.pinTab || tabsMode,
898
+ TempScope: tabsMode ? 'records' : 'main'
854
899
  };
855
900
  // Handle transition from single-resource mode
856
901
  forceNew = this.handleSingleResourceModeTransition(forceNew, request);
@@ -1047,6 +1092,18 @@ export class NavigationService {
1047
1092
  * This updates the tab's configuration and triggers a URL sync via the shell's
1048
1093
  * workspace configuration subscription.
1049
1094
  *
1095
+ * 🚨 NEVER CALL THIS FROM A RESOURCE COMPONENT OR DASHBOARD. "The active tab" is whatever the
1096
+ * user is looking at right now, which is not necessarily the tab the caller lives in. A component
1097
+ * whose async work completes while it sits in a BACKGROUND tab will rewrite — and destroy — the
1098
+ * deep link of the tab the user is actually viewing. Components use
1099
+ * {@link BaseResourceComponent.UpdateQueryParams}, which routes to {@link UpdateTabQueryParams}
1100
+ * with their own tab id and refuses to write when they have none.
1101
+ *
1102
+ * This remains only for shell-level navigation code that is, by construction, acting on behalf of
1103
+ * the user's current tab (nothing in this repo does today).
1104
+ *
1105
+ * @deprecated Prefer {@link UpdateTabQueryParams} with an explicit tab id.
1106
+ *
1050
1107
  * Use this instead of directly calling router.navigate() to ensure proper
1051
1108
  * URL management that respects app-scoped routes.
1052
1109
  *
@@ -1 +1 @@
1
- {"version":3,"file":"navigation.service.js","sourceRoot":"","sources":["../../src/lib/navigation.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,eAAe,CAAC;AAGtD,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,yBAAyB,EAAuB,MAAM,qBAAqB,CAAC;AAC/H,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,OAAO,EAA4B,MAAM,MAAM,CAAC;AAErF,OAAO,EAAE,GAAG,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;;;AA+CpD;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,YAAY,CAAC;AAE1C;;GAEG;AACH,MAAM,iBAAiB,GAAG,SAAS,CAAC,CAAC,2BAA2B;AAEhE;;;GAGG;AAIH,MAAM,OAAO,iBAAiB;IAclB;IACA;IAdF,eAAe,GAAG,KAAK,CAAC;IACxB,aAAa,GAAmB,EAAE,CAAC;IAEnC,kBAAkB,GAAG,IAAI,OAAO,EAAyB,CAAC;IAClE,yFAAyF;IAClF,kBAAkB,GAAG,IAAI,CAAC,kBAAkB,CAAC,YAAY,EAAE,CAAC;IAEnE,6EAA6E;IACrE,UAAU,GAA8B,SAAS,CAAC;IAC1D,4BAA4B;IACpB,aAAa,GAAkB,IAAI,CAAC;IAE5C,YACU,gBAAuC,EACvC,UAA8B;QAD9B,qBAAgB,GAAhB,gBAAgB,CAAuB;QACvC,eAAU,GAAV,UAAU,CAAoB;QAEtC,IAAI,CAAC,4BAA4B,EAAE,CAAC;IACtC,CAAC;IAED;;;;OAIG;IACH,IAAI,gBAAgB;QAClB,OAAO,iBAAiB,CAAC;IAC3B,CAAC;IAED;;;;;;OAMG;IACK,uBAAuB;QAC7B,oBAAoB;QACpB,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;YAClC,IAAI,IAAI,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;gBAC7B,OAAO,IAAI,CAAC,UAAU,CAAC;YACzB,CAAC;YACD,uCAAuC;YACvC,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,YAAY,EAAE,CAAC;YACjD,IAAI,SAAS,EAAE,CAAC;gBACd,OAAO,SAAS,CAAC,EAAE,CAAC;YACtB,CAAC;YACD,OAAO,aAAa,CAAC;QACvB,CAAC;QAED,iCAAiC;QACjC,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QACrD,IAAI,OAAO,EAAE,CAAC;YACZ,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,EAAE,CAAC;YAC7B,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;YACxC,OAAO,OAAO,CAAC,EAAE,CAAC;QACpB,CAAC;QAED,oCAAoC;QACpC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QAEvB,oCAAoC;QACpC,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,YAAY,EAAE,CAAC;QACjD,IAAI,SAAS,EAAE,CAAC;YACd,OAAO,SAAS,CAAC,EAAE,CAAC;QACtB,CAAC;QAED,8BAA8B;QAC9B,OAAO,aAAa,CAAC;IACvB,CAAC;IAED;;;OAGG;IACK,kBAAkB;QACxB,4BAA4B;QAC5B,IAAI,CAAC,uBAAuB,EAAE,CAAC;QAE/B,oCAAoC;QACpC,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,OAAO,IAAI,CAAC,aAAa,CAAC;QAC5B,CAAC;QAED,mBAAmB;QACnB,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,YAAY,EAAE,CAAC;QACjD,IAAI,SAAS,EAAE,CAAC;YACd,OAAO,SAAS,CAAC,QAAQ,EAAE,CAAC;QAC9B,CAAC;QAED,6BAA6B;QAC7B,OAAO,iBAAiB,CAAC;IAC3B,CAAC;IAED;;;OAGG;IACI,iBAAiB;QACtB,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC5B,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;IAC5B,CAAC;IAED,+CAA+C;IAC/C,+BAA+B;IAC/B,+CAA+C;IAE/C;;;;OAIG;IACa,oBAAoB,GAAG,IAAI,OAAO,EAAsB,CAAC;IAEzE,kGAAkG;IAC1F,iBAAiB,GAAwD,EAAE,CAAC;IAEpF;;;;;;;;OAQG;IACc,4BAA4B,GAAG,IAAI,GAAG,EAA8E,CAAC;IAEtI;;;;;;;;;;;;;;;;;;;OAmBG;IACa,mBAAmB,GAAG,IAAI,eAAe,CAA4B,IAAI,CAAC,CAAC;IAE3F;;;;;;OAMG;IACI,yBAAyB,CAAC,QAAmC;QAClE,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC1C,CAAC;IAED;;;;;;;;;;OAUG;IACI,eAAe,CAAC,MAA6B,EAAE,OAAgC;QACpF,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC,CAAC;IAC5E,CAAC;IAED;;;;;;;;;OASG;IACI,mBAAmB,CAAC,MAA6B,EAAE,KAKxD;QACA,IAAI,CAAC,iBAAiB,GAAG,KAAK,CAAC;QAC/B,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,KAAK,EAAE,CAAC,CAAC;IAC9E,CAAC;IAED;;;;;;OAMG;IACI,wBAAwB,CAAC,MAA6B;QAC3D,MAAM,KAAK,GAAG,IAAI,CAAC,4BAA4B,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC5D,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,OAAO;QACT,CAAC;QACD,IAAI,CAAC,iBAAiB,GAAG,KAAK,CAAC;QAC/B,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,KAAK,EAAE,CAAC,CAAC;IAC9E,CAAC;IAED;;;;;;OAMG;IACI,sBAAsB,CAAC,MAA6B;QACzD,IAAI,CAAC,4BAA4B,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC;QACtE,IAAI,CAAC,iBAAiB,GAAG,EAAE,CAAC;QAC5B,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,EAAE,EAAE,CAAC,CAAC;IAC3E,CAAC;IAED;;;OAGG;IACI,cAAc,CAAC,MAA6B;QACjD,IAAI,CAAC,4BAA4B,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACnD,CAAC;IAED,WAAW;QACT,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;IACvD,CAAC;IAED;;OAEG;IACK,4BAA4B;QAClC,iFAAiF;QACjF,iCAAiC;QACjC,iFAAiF;QACjF,sFAAsF;QACtF,2EAA2E;QAC3E,iCAAiC;QACjC,IAAI,CAAC,aAAa,CAAC,IAAI,CACrB,SAAS,CAAa,QAAQ,EAAE,WAAW,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE;YAChF,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC,QAAQ,CAAC;QACxC,CAAC,CAAC,CACH,CAAC;IACJ,CAAC;IAED;;OAEG;IACK,cAAc;QACpB,OAAO,IAAI,CAAC,eAAe,CAAC;IAC9B,CAAC;IAED;;OAEG;IACK,iBAAiB,CAAC,OAA2B;QACnD,6CAA6C;QAC7C,IAAI,OAAO,EAAE,WAAW,KAAK,SAAS,EAAE,CAAC;YACvC,OAAO,OAAO,CAAC,WAAW,CAAC;QAC7B,CAAC;QAED,4CAA4C;QAC5C,OAAO,IAAI,CAAC,cAAc,EAAE,CAAC;IAC/B,CAAC;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACK,kCAAkC,CAAC,QAAiB,EAAE,WAAuB;QACnF,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED;;OAEG;IACK,cAAc,CAAC,GAAQ,EAAE,OAAmB;QAClD,uCAAuC;QACvC,IAAI,GAAG,CAAC,aAAa,KAAK,OAAO,CAAC,aAAa,EAAE,CAAC;YAChD,OAAO,KAAK,CAAC;QACf,CAAC;QAED,6DAA6D;QAC7D,IAAI,OAAO,CAAC,aAAa,EAAE,YAAY,EAAE,CAAC;YACxC,MAAM,eAAe,GAAG,OAAO,CAAC,gBAAgB,IAAI,EAAE,CAAC;YACvD,MAAM,WAAW,GAAG,GAAG,CAAC,gBAAgB,IAAI,EAAE,CAAC;YAC/C,OAAO,GAAG,CAAC,aAAa,EAAE,YAAY,KAAK,OAAO,CAAC,aAAa,CAAC,YAAY;gBACtE,WAAW,KAAK,eAAe,CAAC;QACzC,CAAC;QAED,qDAAqD;QACrD,IAAI,OAAO,CAAC,aAAa,EAAE,OAAO,IAAI,OAAO,CAAC,aAAa,EAAE,WAAW,EAAE,CAAC;YACzE,OAAO,GAAG,CAAC,aAAa,EAAE,OAAO,KAAK,OAAO,CAAC,aAAa,CAAC,OAAO;gBAC5D,GAAG,CAAC,aAAa,EAAE,WAAW,KAAK,OAAO,CAAC,aAAa,CAAC,WAAW,CAAC;QAC9E,CAAC;QAED,+BAA+B;QAC/B,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;OAEG;IACI,WAAW,CAAC,KAAa,EAAE,OAAgB,EAAE,QAAgB,EAAE,OAA2B;QAC/F,IAAI,QAAQ,GAAG,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAE/C,+BAA+B;QAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QAC9C,MAAM,OAAO,GAAG,GAAG,EAAE,IAAI,IAAI,EAAE,CAAC;QAEhC,wFAAwF;QACxF,qFAAqF;QACrF,gFAAgF;QAChF,wDAAwD;QACxD,MAAM,SAAS,GAAI,OAA0B,CAAC,SAAS,KAAK,IAAI,CAAC;QAEjE,MAAM,OAAO,GAAe;YAC1B,aAAa,EAAE,KAAK;YACpB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,gBAAgB,EAAE,OAAO,CAAC,QAAQ,IAAI,EAAE,EAAG,sDAAsD;YACjG,aAAa,EAAE;gBACb,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,YAAY,EAAE,OAAO,CAAC,YAAY;gBAClC,WAAW,EAAE,OAAO,CAAC,WAAW,EAAG,oDAAoD;gBACvF,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,OAAO,EAAE,OAAO,EAAG,kCAAkC;gBACrD,KAAK,EAAE,KAAK;gBACZ,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,EAAG,gCAAgC;gBACvF,GAAG,CAAC,OAAO,CAAC,aAAa,IAAI,EAAE,CAAC;aACjC;YACD,QAAQ,EAAE,OAAO,EAAE,MAAM,IAAI,KAAK;SACnC,CAAC;QAEF,8CAA8C;QAC9C,QAAQ,GAAG,IAAI,CAAC,kCAAkC,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAEtE,IAAI,KAAa,CAAC;QAClB,IAAI,QAAQ,EAAE,CAAC;YACb,0BAA0B;YAC1B,KAAK,GAAG,IAAI,CAAC,gBAAgB,CAAC,aAAa,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACjE,CAAC;aAAM,CAAC;YACN,yDAAyD;YACzD,KAAK,GAAG,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC3D,CAAC;QAED,mEAAmE;QACnE,IAAI,OAAO,EAAE,WAAW,EAAE,CAAC;YACzB,IAAI,CAAC,qBAAqB,CAAC,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;QACzD,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;;;;;;;OAUG;IACI,gBAAgB,CACrB,UAAkB,EAClB,UAAwB,EACxB,OAA2B;QAE3B,MAAM,QAAQ,GAAG,iBAAiB,EAAE,CAAC;QAErC,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,YAAY,EAAE,CAAC;QACjD,MAAM,KAAK,GAAG,QAAQ,IAAI,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,uBAAuB,EAAE,CAAC;QACpF,MAAM,QAAQ,GAAG,QAAQ,IAAI,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAE1F,MAAM,QAAQ,GAAG,UAAU,CAAC,YAAY,EAAE,CAAC;QAE3C,sEAAsE;QACtE,kDAAkD;QAClD,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,QAAQ,GAAG,IAAI,CAAC,iBAAiB,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;YAC9D,IAAI,QAAQ,EAAE,CAAC;gBACb,iEAAiE;gBACjE,gEAAgE;gBAChE,mEAAmE;gBACnE,8DAA8D;gBAC9D,IAAI,CAAC,oBAAoB,CAAC,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;gBAChD,iEAAiE;gBACjE,oEAAoE;gBACpE,kEAAkE;gBAClE,4DAA4D;gBAC5D,IAAI,CAAC,gBAAgB,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;gBAChD,IAAI,CAAC,sBAAsB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;gBACzC,OAAO,QAAQ,CAAC,EAAE,CAAC;YACrB,CAAC;QACH,CAAC;QAED,IAAI,QAAQ,GAAG,QAAQ,IAAI,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAE3D,MAAM,OAAO,GAAe;YAC1B,aAAa,EAAE,KAAK;YACpB,KAAK,EAAE,GAAG,UAAU,MAAM,QAAQ,EAAE;YACpC,aAAa,EAAE;gBACb,YAAY,EAAE,qBAAqB;gBACnC,MAAM,EAAE,UAAU,EAAG,wEAAwE;gBAC7F,QAAQ,EAAE,QAAQ,EAAG,wFAAwF;gBAC7G,GAAG,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC;aACtC;YACD,gBAAgB,EAAE,QAAQ;YAC1B,QAAQ,EAAE,OAAO,EAAE,MAAM,IAAI,KAAK;YAClC,gEAAgE;YAChE,sEAAsE;YACtE,iCAAiC;YACjC,gBAAgB,EAAE,QAAQ;SAC3B,CAAC;QAEF,8CAA8C;QAC9C,QAAQ,GAAG,IAAI,CAAC,kCAAkC,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAEtE,IAAI,KAAa,CAAC;QAClB,IAAI,QAAQ,EAAE,CAAC;YACb,KAAK,GAAG,IAAI,CAAC,gBAAgB,CAAC,aAAa,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACjE,CAAC;aAAM,CAAC;YACN,KAAK,GAAG,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC3D,CAAC;QAED,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC;QACrC,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;;;;;OAQG;IACK,sBAAsB,CAAC,KAAa;QAC1C,UAAU,CAAC,GAAG,EAAE;YACd,MAAM,MAAM,GAAG,IAAI,CAAC,gBAAgB,CAAC,gBAAgB,EAAE,CAAC;YACxD,IAAI,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,IAAI,MAAM,CAAC,WAAW,KAAK,KAAK,EAAE,CAAC;gBAC3E,IAAI,CAAC,gBAAgB,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;YAC5C,CAAC;QACH,CAAC,EAAE,CAAC,CAAC,CAAC;IACR,CAAC;IAED,+EAA+E;IACvE,iBAAiB,CAAC,UAAkB,EAAE,QAAgB;QAC5D,MAAM,MAAM,GAAG,IAAI,CAAC,gBAAgB,CAAC,gBAAgB,EAAE,CAAC;QACxD,MAAM,KAAK,GAAG,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAClC,CAAC,CAAC,gBAAgB,KAAK,QAAQ;YAC/B,CAAC,CAAC,aAAa,EAAE,CAAC,cAAc,CAAC,KAAK,qBAAqB;YAC3D,CAAC,CAAC,aAAa,EAAE,CAAC,QAAQ,CAAC,KAAK,UAAU,CAC3C,CAAC;QACF,OAAO,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACzC,CAAC;IAED;;;;;;OAMG;IACK,oBAAoB,CAAC,KAAa,EAAE,OAA2B;QACrE,IAAI,CAAC,gBAAgB,CAAC,sBAAsB,CAAC,KAAK,EAAE;YAClD,WAAW,EAAE,SAAS;YACtB,aAAa,EAAE,SAAS;YACxB,cAAc,EAAE,SAAS;YACzB,iBAAiB,EAAE,SAAS;YAC5B,WAAW,EAAE,SAAS;YACtB,WAAW,EAAE,SAAS;YACtB,iBAAiB,EAAE,SAAS;YAC5B,kBAAkB,EAAE,SAAS;YAC7B,cAAc,EAAE,SAAS;YACzB,GAAG,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC;SACtC,CAAC,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACK,oBAAoB,CAAC,OAA2B;QACtD,IAAI,OAAO,EAAE,YAAY,KAAK,MAAM,EAAE,CAAC;YACrC,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,IAAI,OAAO,EAAE,YAAY,EAAE,CAAC;YAC1B,MAAM,GAAG,GAAG,OAAO,CAAC,YAAY,CAAC;YACjC,MAAM,OAAO,GAA4B,EAAE,CAAC;YAC5C,IAAI,GAAG,CAAC,WAAW;gBAAE,OAAO,CAAC,aAAa,CAAC,GAAG,GAAG,CAAC,WAAW,CAAC;YAC9D,IAAI,GAAG,CAAC,aAAa;gBAAE,OAAO,CAAC,eAAe,CAAC,GAAG,GAAG,CAAC,aAAa,CAAC;YACpE,IAAI,GAAG,CAAC,cAAc;gBAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,GAAG,CAAC,cAAc,CAAC;YACvE,IAAI,GAAG,CAAC,WAAW;gBAAE,OAAO,CAAC,aAAa,CAAC,GAAG,GAAG,CAAC,WAAW,CAAC;YAC9D,IAAI,GAAG,CAAC,WAAW;gBAAE,OAAO,CAAC,aAAa,CAAC,GAAG,GAAG,CAAC,WAAW,CAAC;YAC9D,IAAI,GAAG,CAAC,iBAAiB,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC3E,OAAO,CAAC,mBAAmB,CAAC,GAAG,GAAG,CAAC,iBAAiB,CAAC;YACvD,CAAC;YACD,OAAO,OAAO,CAAC;QACjB,CAAC;QACD,OAAO,IAAI,CAAC,oBAAoB,EAAE,CAAC;IACrC,CAAC;IAED;;;;;OAKG;IACK,oBAAoB;QAC1B,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,YAAY,EAAE,CAAC;QACjD,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,MAAM,OAAO,GAA4B;YACvC,WAAW,EAAE,SAAS,CAAC,EAAE;YACzB,aAAa,EAAE,SAAS,CAAC,IAAI;SAC9B,CAAC;QACF,MAAM,MAAM,GAAG,IAAI,CAAC,gBAAgB,CAAC,gBAAgB,EAAE,CAAC;QACxD,MAAM,SAAS,GAAG,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,WAAW,CAAC,CAAC;QACtE,IAAI,CAAC,SAAS,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,aAAa,EAAE,SAAS,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;YACzF,OAAO,OAAO,CAAC;QACjB,CAAC;QACD,IAAI,yBAAyB,CAAC,SAAS,CAAC,aAAa,CAAC,EAAE,CAAC;YACvD,qEAAqE;YACrE,oEAAoE;YACpE,kEAAkE;YAClE,MAAM,YAAY,GAAG,SAAS,CAAC,aAAa,EAAE,CAAC,QAAQ,CAAC,CAAC;YACzD,MAAM,cAAc,GAAG,SAAS,CAAC,gBAAgB,IAAI,SAAS,CAAC,aAAa,EAAE,CAAC,UAAU,CAAC,CAAC;YAC3F,IAAI,OAAO,YAAY,KAAK,QAAQ,IAAI,OAAO,cAAc,KAAK,QAAQ,IAAI,cAAc,EAAE,CAAC;gBAC7F,OAAO,CAAC,aAAa,CAAC,GAAG,SAAS,CAAC,EAAE,CAAC;gBACtC,OAAO,CAAC,aAAa,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC;gBACzC,OAAO,CAAC,oBAAoB,CAAC,GAAG,YAAY,CAAC;gBAC7C,OAAO,CAAC,gBAAgB,CAAC,GAAG,cAAc,CAAC;YAC7C,CAAC;YACD,OAAO,OAAO,CAAC;QACjB,CAAC;QACD,OAAO,CAAC,gBAAgB,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC;QAC5C,oEAAoE;QACpE,uEAAuE;QACvE,6DAA6D;QAC7D,MAAM,WAAW,GAAG,SAAS,CAAC,aAAa,EAAE,CAAC,aAAa,CAAC,CAAC;QAC7D,IAAI,OAAO,WAAW,KAAK,QAAQ,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9D,OAAO,CAAC,mBAAmB,CAAC,GAAG,WAAW,CAAC;QAC7C,CAAC;QACD,qEAAqE;QACrE,qEAAqE;QACrE,qBAAqB;QACrB,OAAO,CAAC,aAAa,CAAC,GAAG,SAAS,CAAC,EAAE,CAAC;QACtC,sEAAsE;QACtE,sEAAsE;QACtE,qEAAqE;QACrE,+BAA+B;QAC/B,MAAM,WAAW,GAAG,SAAS,CAAC,aAAa,EAAE,CAAC,aAAa,CAAC,CAAC;QAC7D,IAAI,WAAW,IAAI,OAAO,WAAW,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1F,OAAO,CAAC,mBAAmB,CAAC,GAAG,EAAE,GAAI,WAAsC,EAAE,CAAC;QAChF,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,eAAe,CAAC,KAAa;QACjC,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC;QAC5E,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO;QACT,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,WAAW,EAAE,CAAC;QACzC,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC;QAC5D,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;QAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,gBAAgB,CAAC,cAAc,EAAE,CAAC;QACrD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO;QACT,CAAC;QACD,MAAM,GAAG,GAAG,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAChD,MAAM,OAAO,GAAG,CAAC,GAAG,EAAE,aAAa,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,CAA2B,CAAC;QACtF,MAAM,KAAK,GAAkC,EAAE,CAAC;QAChD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YACvC,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;QACpB,CAAC;QACD,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAClC,IAAI,CAAC,qBAAqB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAC3C,CAAC;QACD,IAAI,CAAC,wBAAwB,CAAC,KAAK,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC;IACjD,CAAC;IAED;;;;;;;;OAQG;IACI,KAAK,CAAC,oBAAoB,CAAC,MAA2B;QAC3D,yEAAyE;QACzE,yEAAyE;QACzE,qEAAqE;QACrE,uEAAuE;QACvE,oCAAoC;QACpC,IAAI,MAAM,CAAC,kBAAkB,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;YACvD,MAAM,MAAM,GAAG,IAAI,CAAC,gBAAgB,CAAC,gBAAgB,EAAE,CAAC;YACxD,MAAM,SAAS,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YACvG,MAAM,WAAW,GAAG,CAAC,CAAC,SAAS;gBAC7B,yBAAyB,CAAC,SAAS,CAAC,aAAa,CAAC;gBAClD,SAAS,CAAC,aAAa,EAAE,CAAC,QAAQ,CAAC,KAAK,MAAM,CAAC,kBAAkB;gBACjE,SAAS,CAAC,gBAAgB,KAAK,MAAM,CAAC,cAAc,CAAC;YACvD,IAAI,SAAS,IAAI,WAAW,EAAE,CAAC;gBAC7B,IAAI,CAAC,gBAAgB,CAAC,YAAY,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;gBACjD,OAAO;YACT,CAAC;YACD,MAAM,SAAS,GAAG,IAAI,YAAY,EAAE,CAAC;YACrC,SAAS,CAAC,wBAAwB,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;YAC1D,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,kBAAkB,EAAE,SAAS,CAAC,CAAC;YAC5D,OAAO;QACT,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YACxB,OAAO;QACT,CAAC;QACD,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;YACvB,MAAM,MAAM,GAAG,IAAI,CAAC,gBAAgB,CAAC,gBAAgB,EAAE,CAAC;YACxD,MAAM,SAAS,GAAG,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,WAAW,CAAC,CAAC;YACtE,gEAAgE;YAChE,oEAAoE;YACpE,qEAAqE;YACrE,oEAAoE;YACpE,+DAA+D;YAC/D,mEAAmE;YACnE,mEAAmE;YACnE,oEAAoE;YACpE,MAAM,cAAc,GAAG,SAAS,EAAE,aAAa,EAAE,CAAC,aAAa,CAAC,CAAC;YACjE,MAAM,QAAQ,GAAG,MAAM,CAAC,iBAAiB,IAAI,OAAO,cAAc,KAAK,QAAQ;gBAC7E,CAAC,CAAC,cAAc,KAAK,MAAM,CAAC,iBAAiB;gBAC7C,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,cAAc,IAAI,SAAS,EAAE,KAAK,KAAK,MAAM,CAAC,cAAc,CAAC,CAAC;YAC3E,MAAM,eAAe,GAAG,CAAC,CAAC,SAAS;gBACjC,CAAC,CAAC,MAAM,CAAC,WAAW,IAAI,UAAU,CAAC,SAAS,CAAC,aAAa,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;gBAChF,QAAQ,CAAC;YACX,IAAI,SAAS,IAAI,eAAe,EAAE,CAAC;gBACjC,qEAAqE;gBACrE,oEAAoE;gBACpE,4DAA4D;gBAC5D,oEAAoE;gBACpE,+DAA+D;gBAC/D,mEAAmE;gBACnE,MAAM,aAAa,GAAG,CAAC,SAAS,CAAC,aAAa,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,CAA2B,CAAC;gBACjG,MAAM,OAAO,GAAkC,EAAE,CAAC;gBAClD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC;oBAC7C,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;gBACtB,CAAC;gBACD,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC;gBACvD,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACpC,IAAI,CAAC,qBAAqB,CAAC,SAAS,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;gBACpD,CAAC;gBACD,mEAAmE;gBACnE,kEAAkE;gBAClE,iEAAiE;gBACjE,qDAAqD;gBACrD,IAAI,MAAM,CAAC,iBAAiB,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACjF,IAAI,CAAC,wBAAwB,CAAC,SAAS,CAAC,EAAE,EAAE,MAAM,CAAC,iBAAiB,EAAE,IAAI,CAAC,CAAC;gBAC9E,CAAC;gBACD,iEAAiE;gBACjE,gEAAgE;gBAChE,+DAA+D;gBAC/D,MAAM,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;gBAC5D,IAAI,CAAC,gBAAgB,CAAC,YAAY,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;gBACjD,OAAO;YACT,CAAC;QACH,CAAC;QACD,wEAAwE;QACxE,wEAAwE;QACxE,4DAA4D;QAC5D,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,iBAAiB,IAAI,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,iBAAiB,CAAC,CAAC;QACxH,uEAAuE;QACvE,iCAAiC;QACjC,IAAI,MAAM,CAAC,iBAAiB,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACjF,MAAM,WAAW,GAAG,IAAI,CAAC,gBAAgB,CAAC,cAAc,EAAE,CAAC;YAC3D,IAAI,WAAW,EAAE,CAAC;gBAChB,IAAI,CAAC,wBAAwB,CAAC,WAAW,EAAE,MAAM,CAAC,iBAAiB,EAAE,IAAI,CAAC,CAAC;YAC7E,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;OAGG;IACI,QAAQ,CACb,MAAc,EACd,QAAgB,EAChB,OAA2B;QAE3B,MAAM,KAAK,GAAG,IAAI,CAAC,uBAAuB,EAAE,CAAC;QAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC3C,IAAI,QAAQ,GAAG,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAE/C,MAAM,OAAO,GAAe;YAC1B,aAAa,EAAE,KAAK;YACpB,KAAK,EAAE,QAAQ;YACf,aAAa,EAAE;gBACb,YAAY,EAAE,gBAAgB;gBAC9B,MAAM;gBACN,QAAQ,EAAE,MAAM,CAAE,wFAAwF;aAC3G;YACD,gBAAgB,EAAE,MAAM;YACxB,QAAQ,EAAE,OAAO,EAAE,MAAM,IAAI,KAAK;SACnC,CAAC;QAEF,8CAA8C;QAC9C,QAAQ,GAAG,IAAI,CAAC,kCAAkC,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAEtE,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO,IAAI,CAAC,gBAAgB,CAAC,aAAa,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAChE,CAAC;aAAM,CAAC;YACN,OAAO,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC;IAED;;;OAGG;IACI,aAAa,CAClB,WAAmB,EACnB,aAAqB,EACrB,OAA2B;QAE3B,MAAM,KAAK,GAAG,IAAI,CAAC,uBAAuB,EAAE,CAAC;QAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC3C,IAAI,QAAQ,GAAG,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAE/C,MAAM,OAAO,GAAe;YAC1B,aAAa,EAAE,KAAK;YACpB,KAAK,EAAE,aAAa;YACpB,aAAa,EAAE;gBACb,YAAY,EAAE,YAAY;gBAC1B,WAAW;gBACX,QAAQ,EAAE,WAAW,CAAE,wFAAwF;aAChH;YACD,gBAAgB,EAAE,WAAW;YAC7B,QAAQ,EAAE,OAAO,EAAE,MAAM,IAAI,KAAK;SACnC,CAAC;QAEF,8CAA8C;QAC9C,QAAQ,GAAG,IAAI,CAAC,kCAAkC,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAEtE,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO,IAAI,CAAC,gBAAgB,CAAC,aAAa,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAChE,CAAC;aAAM,CAAC;YACN,OAAO,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC;IAED;;;;OAIG;IACI,YAAY,CACjB,UAAkB,EAClB,YAAqB,EACrB,OAA2B;QAE3B,MAAM,KAAK,GAAG,IAAI,CAAC,uBAAuB,EAAE,CAAC;QAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC3C,IAAI,QAAQ,GAAG,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAE/C,MAAM,OAAO,GAAe;YAC1B,aAAa,EAAE,KAAK;YACpB,KAAK,EAAE,YAAY,IAAI,cAAc,UAAU,EAAE;YACjD,aAAa,EAAE;gBACb,YAAY,EAAE,WAAW;gBACzB,UAAU;gBACV,QAAQ,EAAE,UAAU,CAAE,wFAAwF;aAC/G;YACD,gBAAgB,EAAE,UAAU;YAC5B,QAAQ,EAAE,OAAO,EAAE,MAAM,IAAI,KAAK;SACnC,CAAC;QAEF,8CAA8C;QAC9C,QAAQ,GAAG,IAAI,CAAC,kCAAkC,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAEtE,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO,IAAI,CAAC,gBAAgB,CAAC,aAAa,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAChE,CAAC;aAAM,CAAC;YACN,OAAO,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC;IAED;;;;OAIG;IACI,eAAe,CACpB,UAAkB,EAClB,WAAoB,EACpB,OAA2B;QAE3B,MAAM,KAAK,GAAG,IAAI,CAAC,uBAAuB,EAAE,CAAC;QAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC3C,IAAI,QAAQ,GAAG,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAE/C,MAAM,YAAY,GAAG,WAAW,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC;QACtD,MAAM,OAAO,GAAe;YAC1B,aAAa,EAAE,KAAK;YACpB,KAAK,EAAE,GAAG,UAAU,GAAG,YAAY,EAAE;YACrC,aAAa,EAAE;gBACb,YAAY,EAAE,gBAAgB;gBAC9B,MAAM,EAAE,UAAU;gBAClB,WAAW,EAAE,WAAW;gBACxB,SAAS,EAAE,IAAI;gBACf,QAAQ,EAAE,SAAS,CAAE,mCAAmC;aACzD;YACD,gBAAgB,EAAE,SAAS;YAC3B,QAAQ,EAAE,OAAO,EAAE,MAAM,IAAI,KAAK;SACnC,CAAC;QAEF,8CAA8C;QAC9C,QAAQ,GAAG,IAAI,CAAC,kCAAkC,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAEtE,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO,IAAI,CAAC,gBAAgB,CAAC,aAAa,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAChE,CAAC;aAAM,CAAC;YACN,OAAO,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC;IAED;;;OAGG;IACI,SAAS,CACd,OAAe,EACf,SAAiB,EACjB,OAA2B;QAE3B,MAAM,KAAK,GAAG,IAAI,CAAC,uBAAuB,EAAE,CAAC;QAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC3C,IAAI,QAAQ,GAAG,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAE/C,MAAM,OAAO,GAAe;YAC1B,aAAa,EAAE,KAAK;YACpB,KAAK,EAAE,SAAS;YAChB,aAAa,EAAE;gBACb,YAAY,EAAE,SAAS;gBACvB,OAAO;gBACP,QAAQ,EAAE,OAAO,CAAE,wFAAwF;aAC5G;YACD,gBAAgB,EAAE,OAAO;YACzB,QAAQ,EAAE,OAAO,EAAE,MAAM,IAAI,KAAK;SACnC,CAAC;QAEF,8CAA8C;QAC9C,QAAQ,GAAG,IAAI,CAAC,kCAAkC,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAEtE,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO,IAAI,CAAC,gBAAgB,CAAC,aAAa,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAChE,CAAC;aAAM,CAAC;YACN,OAAO,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACI,mBAAmB,CACxB,UAAkB,EAClB,OAA2B;QAE3B,kEAAkE;QAClE,uEAAuE;QACvE,sEAAsE;QACtE,6DAA6D;QAC7D,oEAAoE;QACpE,6CAA6C;QAC7C,MAAM,QAAQ,GAAG,iBAAiB,EAAE,CAAC;QACrC,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,YAAY,EAAE,CAAC;QACjD,MAAM,KAAK,GAAG,QAAQ,IAAI,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,uBAAuB,EAAE,CAAC;QACpF,MAAM,QAAQ,GAAG,QAAQ,IAAI,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAE1F,IAAI,QAAQ,GAAG,QAAQ,IAAI,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAE3D,MAAM,OAAO,GAAe;YAC1B,aAAa,EAAE,KAAK;YACpB,KAAK,EAAE,OAAO,UAAU,EAAE;YAC1B,aAAa,EAAE;gBACb,YAAY,EAAE,qBAAqB;gBACnC,MAAM,EAAE,UAAU,EAAG,wEAAwE;gBAC7F,QAAQ,EAAE,EAAE,EAAS,sCAAsC;gBAC3D,KAAK,EAAE,IAAI,EAAU,wCAAwC;gBAC7D,eAAe,EAAE,OAAO,EAAE,eAAe,EAAG,0CAA0C;gBACtF,GAAG,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC;aACtC;YACD,gBAAgB,EAAE,EAAE,EAAG,wBAAwB;YAC/C,QAAQ,EAAE,OAAO,EAAE,MAAM,IAAI,KAAK;YAClC,gBAAgB,EAAE,QAAQ;SAC3B,CAAC;QAEF,8CAA8C;QAC9C,QAAQ,GAAG,IAAI,CAAC,kCAAkC,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAEtE,IAAI,KAAa,CAAC;QAClB,IAAI,QAAQ,EAAE,CAAC;YACb,KAAK,GAAG,IAAI,CAAC,gBAAgB,CAAC,aAAa,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACjE,CAAC;aAAM,CAAC;YACN,KAAK,GAAG,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC3D,CAAC;QAED,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC;QACrC,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;;;;OAOG;IACI,UAAU,CACf,KAAa,EACb,aAA8D,EAC9D,OAA2B;QAE3B,MAAM,KAAK,GAAG,IAAI,CAAC,uBAAuB,EAAE,CAAC;QAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC3C,IAAI,QAAQ,GAAG,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAE/C,MAAM,MAAM,GAA4B;YACtC,YAAY,EAAE,gBAAgB;YAC9B,KAAK,EAAE,KAAK;YACZ,WAAW,EAAE,KAAK;YAClB,QAAQ,EAAE,UAAU,KAAK,EAAE;SAC5B,CAAC;QACF,IAAI,aAAa,EAAE,YAAY,IAAI,IAAI,EAAE,CAAC;YACxC,MAAM,CAAC,cAAc,CAAC,GAAG,aAAa,CAAC,YAAY,CAAC;QACtD,CAAC;QACD,IAAI,aAAa,EAAE,QAAQ,IAAI,aAAa,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACjE,MAAM,CAAC,UAAU,CAAC,GAAG,aAAa,CAAC,QAAQ,CAAC;QAC9C,CAAC;QAED,MAAM,OAAO,GAAe;YAC1B,aAAa,EAAE,KAAK;YACpB,KAAK,EAAE,WAAW,KAAK,EAAE;YACzB,aAAa,EAAE,MAAM;YACrB,gBAAgB,EAAE,UAAU,KAAK,EAAE;YACnC,QAAQ,EAAE,KAAK;SAChB,CAAC;QAEF,8CAA8C;QAC9C,QAAQ,GAAG,IAAI,CAAC,kCAAkC,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAEtE,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO,IAAI,CAAC,gBAAgB,CAAC,aAAa,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAChE,CAAC;aAAM,CAAC;YACN,OAAO,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC;IAED;;;;;;;;;;;OAWG;IACI,KAAK,CAAC,iBAAiB,CAC5B,WAAmB,EACnB,aAAuC,EACvC,KAAc,EACd,OAA2B;QAE3B,2CAA2C;QAC3C,MAAM,WAAW,GAAG,KAAK,IAAI,IAAI,CAAC,UAAU,CAAC,YAAY,EAAE,EAAE,EAAE,CAAC;QAChE,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;QACpD,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO,IAAI,CAAC;QACd,CAAC;QAED,4BAA4B;QAC5B,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,WAAW,EAAE,CAAC;QACzC,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,KAAK,WAAW,CAAC,CAAC;QAClE,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,IAAI,CAAC;QACd,CAAC;QAED,yDAAyD;QACzD,MAAM,aAAa,GAAY;YAC7B,GAAG,OAAO;YACV,aAAa,EAAE;gBACb,GAAG,CAAC,OAAO,CAAC,aAAa,IAAI,EAAE,CAAC;gBAChC,GAAG,CAAC,aAAa,IAAI,EAAE,CAAC;aACzB;SACF,CAAC;QAEF,2BAA2B;QAC3B,OAAO,IAAI,CAAC,WAAW,CAAC,WAAW,EAAE,aAAa,EAAE,GAAG,CAAC,QAAQ,EAAE,EAAE,OAAO,CAAC,CAAC;IAC/E,CAAC;IAED;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,WAAW,CAAC,KAAa,EAAE,WAAoB,EAAE,WAA2C;QAChG,MAAM,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;QAE1C,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC;QAC5E,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO;QACT,CAAC;QAED,wEAAwE;QACxE,sEAAsE;QACtE,sEAAsE;QACtE,uEAAuE;QACvE,sEAAsE;QACtE,gEAAgE;QAChE,sEAAsE;QACtE,mEAAmE;QACnE,kCAAkC;QAClC,MAAM,OAAO,GAAG,iBAAiB,EAAE;YACjC,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,yBAAyB,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC;YAClG,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QAE5C,sCAAsC;QACtC,IAAI,WAAW,EAAE,CAAC;YAChB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,WAAW,EAAE,CAAC;YACzC,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,KAAK,WAAW,CAAC,CAAC;YAClE,IAAI,OAAO,EAAE,CAAC;gBACZ,mEAAmE;gBACnE,kEAAkE;gBAClE,mEAAmE;gBACnE,mEAAmE;gBACnE,+DAA+D;gBAC/D,iEAAiE;gBACjE,0BAA0B;gBAC1B,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;oBACrC,MAAM,MAAM,GAAG,GAAG,CAAC,aAAa,EAAE,CAAC,aAAa,CAAC,CAAC;oBAClD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;wBACpD,OAAO,MAAM,KAAK,OAAO,CAAC,KAAK,CAAC;oBAClC,CAAC;oBACD,OAAO,GAAG,CAAC,KAAK,KAAK,OAAO,CAAC,KAAK;wBAChC,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;gBACxE,CAAC,CAAC,CAAC;gBAEH,IAAI,WAAW,EAAE,CAAC;oBAChB,yBAAyB;oBACzB,IAAI,CAAC,gBAAgB,CAAC,YAAY,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;gBACrD,CAAC;qBAAM,CAAC;oBACN,iCAAiC;oBACjC,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;gBACnD,CAAC;gBACD,mFAAmF;gBACnF,iFAAiF;gBACjF,IAAI,WAAW,IAAI,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACvD,MAAM,WAAW,GAAG,IAAI,CAAC,gBAAgB,CAAC,cAAc,EAAE,CAAC;oBAC3D,IAAI,WAAW,EAAE,CAAC;wBAChB,IAAI,CAAC,qBAAqB,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;oBACvD,CAAC;gBACH,CAAC;gBACD,OAAO;YACT,CAAC;YACD,uDAAuD;QACzD,CAAC;QAED,6DAA6D;QAC7D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,qBAAqB;YACrB,MAAM,UAAU,GAAG,MAAM,GAAG,CAAC,gBAAgB,EAAE,CAAC;YAChD,IAAI,UAAU,EAAE,CAAC;gBACf,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;YAC5D,CAAC;QACH,CAAC;aAAM,CAAC;YACN,mEAAmE;YACnE,MAAM,MAAM,GAAG,IAAI,CAAC,gBAAgB,CAAC,gBAAgB,EAAE,CAAC;YACxD,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,EAAE,WAAW,CAAC,CAAC;YACrE,IAAI,CAAC,YAAY,EAAE,CAAC;gBAClB,kDAAkD;gBAClD,IAAI,CAAC,gBAAgB,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACpD,CAAC;QACH,CAAC;QAED,4EAA4E;QAC5E,+EAA+E;QAC/E,oFAAoF;QACpF,IAAI,WAAW,IAAI,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvD,MAAM,WAAW,GAAG,IAAI,CAAC,gBAAgB,CAAC,cAAc,EAAE,CAAC;YAC3D,IAAI,WAAW,EAAE,CAAC;gBAChB,IAAI,CAAC,qBAAqB,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;YACvD,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;;;;;;;;;;;;;;OAgBG;IACH,0BAA0B,CAAC,WAA0C;QACnE,MAAM,WAAW,GAAG,IAAI,CAAC,gBAAgB,CAAC,cAAc,EAAE,CAAC;QAC3D,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,OAAO,CAAC,IAAI,CAAC,6DAA6D,CAAC,CAAC;YAC5E,OAAO;QACT,CAAC;QAED,IAAI,CAAC,qBAAqB,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;IACvD,CAAC;IAED;;;;;OAKG;IACH,oBAAoB,CAClB,KAAa,EACb,WAA0C,EAC1C,KAAgC;QAEhC,MAAM,GAAG,GAAG,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAChD,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO,CAAC,KAAK,CAAC,2FAA2F,EAAE,KAAK,CAAC,CAAC;YAClH,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,yBAAyB,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,CAAC;YACzD,OAAO,CAAC,KAAK,CAAC,kGAAkG,EAAE;gBAChH,KAAK;gBACL,KAAK;gBACL,OAAO,EAAE;oBACP,YAAY,EAAE,GAAG,CAAC,aAAa,EAAE,CAAC,cAAc,CAAC;oBACjD,WAAW,EAAE,GAAG,CAAC,aAAa,EAAE,CAAC,yBAAyB,CAAC,IAAI,GAAG,CAAC,aAAa,EAAE,CAAC,aAAa,CAAC;oBACjG,QAAQ,EAAE,GAAG,CAAC,gBAAgB,IAAI,GAAG,CAAC,aAAa,EAAE,CAAC,UAAU,CAAC;oBACjE,WAAW,EAAE,GAAG,CAAC,aAAa,EAAE,CAAC,aAAa,CAAC;oBAC/C,MAAM,EAAE,GAAG,CAAC,aAAa,EAAE,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,aAAa,EAAE,CAAC,QAAQ,CAAC;iBACvE;aACF,CAAC,CAAC;YACH,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,CAAC,qBAAqB,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;QAC/C,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;OAIG;IACH,wBAAwB,CAAC,KAAa,EAAE,MAA8B,EAAE,KAAK,GAAG,KAAK;QACnF,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/E,CAAC;IAED;;;;;;;;;;;;;OAaG;IACI,qBAAqB,CAAC,KAAa;QACxC,OAAO,IAAI,CAAC,gBAAgB,CAAC,aAAa,CAAC,IAAI,CAC7C,GAAG,CAAC,MAAM,CAAC,EAAE;YACX,MAAM,GAAG,GAAG,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,CAAC;YACpD,OAAO,CAAC,GAAG,EAAE,aAAa,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,CAA2B,CAAC;QAC/E,CAAC,CAAC,EACF,oBAAoB,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAC9D,CAAC;IACJ,CAAC;IAEO,kBAAkB,CAAC,CAAyB,EAAE,CAAyB;QAC7E,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC7B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC7B,IAAI,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,EAAE,CAAC;YAClC,OAAO,KAAK,CAAC;QACf,CAAC;QACD,OAAO,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/C,CAAC;IAEO,yBAAyB,CAAC,GAA2E,EAAE,KAA+B;QAC5I,MAAM,MAAM,GAAG,GAAG,CAAC,aAAa,IAAI,EAAE,CAAC;QACvC,MAAM,OAAO,GAAG,CAAC,QAAmC,EAAE,MAAe,EAAE,SAAS,GAAG,KAAK,EAAW,EAAE;YACnG,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;gBAChD,OAAO,IAAI,CAAC;YACd,CAAC;YACD,MAAM,YAAY,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;YACtC,MAAM,UAAU,GAAG,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACxD,OAAO,SAAS;gBACd,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,UAAU,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE;gBACvE,CAAC,CAAC,YAAY,KAAK,UAAU,CAAC;QAClC,CAAC,CAAC;QAEF,MAAM,kBAAkB,GAAG,CAAC,MAAM,CAAC,yBAAyB,CAAC,IAAI,MAAM,CAAC,aAAa,CAAC,CAAuB,CAAC;QAC9G,MAAM,eAAe,GAAG,GAAG,CAAC,gBAAgB,IAAK,MAAM,CAAC,UAAU,CAAwB,IAAI,EAAE,CAAC;QACjG,MAAM,aAAa,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAuB,CAAC;QAEnF,oFAAoF;QACpF,qFAAqF;QACrF,OAAO,OAAO,CAAC,KAAK,CAAC,YAAY,EAAE,MAAM,CAAC,cAAc,CAAC,EAAE,IAAI,CAAC;YAC9D,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,kBAAkB,CAAC;YAC9C,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,eAAe,CAAC;YACxC,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC;YACjD,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,aAAa,EAAE,IAAI,CAAC,CAAC;IAC/C,CAAC;IAED;;;OAGG;IACK,qBAAqB,CAAC,KAAa,EAAE,WAA0C;QACrF,MAAM,GAAG,GAAG,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAChD,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO,CAAC,IAAI,CAAC,yDAAyD,EAAE,KAAK,CAAC,CAAC;YAC/E,OAAO;QACT,CAAC;QAED,kDAAkD;QAClD,MAAM,mBAAmB,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,CAAkC,CAAC;QAExG,8BAA8B;QAC9B,MAAM,iBAAiB,GAA2B,EAAE,CAAC;QAErD,+CAA+C;QAC/C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC,EAAE,CAAC;YAC/D,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;gBACnB,iBAAiB,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;YACjC,CAAC;QACH,CAAC;QAED,uCAAuC;QACvC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;YACvD,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;gBACnB,OAAO,iBAAiB,CAAC,GAAG,CAAC,CAAC;YAChC,CAAC;iBAAM,CAAC;gBACN,iBAAiB,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;YACjC,CAAC;QACH,CAAC;QAED,+BAA+B;QAC/B,IAAI,CAAC,gBAAgB,CAAC,sBAAsB,CAAC,KAAK,EAAE;YAClD,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,SAAS;SACvF,CAAC,CAAC;IACL,CAAC;2GA3yCU,iBAAiB;gEAAjB,iBAAiB,WAAjB,iBAAiB,mBAFhB,MAAM;;iFAEP,iBAAiB;cAH7B,UAAU;eAAC;gBACV,UAAU,EAAE,MAAM;aACnB","sourcesContent":["import { Injectable, OnDestroy } from '@angular/core';\nimport { WorkspaceStateManager, NavItem, DynamicNavItem, TabRequest, ApplicationManager } from '@memberjunction/ng-base-application';\nimport { NavigationOptions } from './navigation.interfaces';\nimport { IsRecordTabsStyle, RECORDS_RESOURCE_TYPE, IsRecordsTabConfiguration, RecordSourceContext } from './record-open-style';\nimport { CompositeKey } from '@memberjunction/core';\nimport { fromEvent, BehaviorSubject, Subject, Subscription, Observable } from 'rxjs';\nimport type { AppContextSnapshot } from '@memberjunction/ai-core-plus';\nimport { map, distinctUntilChanged } from 'rxjs/operators';\nimport { UUIDsEqual } from '@memberjunction/global';\nimport { BaseResourceComponent } from './base-resource-component';\n\n/**\n * Event emitted when query params change on a tab (e.g., from browser back/forward).\n * Includes the tab ID so that only the component in the affected tab reacts,\n * preventing cross-tab leakage in multi-tab scenarios.\n */\nexport interface QueryParamChangeEvent {\n TabId: string;\n Params: Record<string, string>;\n /**\n * Bypass the receiving component's duplicate-delivery guard. Set by\n * explicit restore navigations (e.g. the origin crumb): the component's\n * state may have DRIFTED from the last-delivered params, in which case a\n * restore to those same params would otherwise be dropped as a duplicate.\n */\n Force?: boolean;\n}\n\n/**\n * Event emitted when a resource component reports its agent context or tools.\n * The shell (which owns the ComponentCacheManager) subscribes to these events\n * and updates the cache + active AppContextSnapshot accordingly.\n */\nexport interface AgentContextUpdate {\n /** The component instance that reported the update */\n Caller: BaseResourceComponent;\n /** Dashboard-specific context for the agent (undefined = no change) */\n AgentContext?: Record<string, unknown>;\n /** Client tools available from this dashboard (undefined = no change) */\n AgentClientTools?: Array<{\n Name: string;\n Description: string;\n ParameterSchema: Record<string, unknown>;\n Handler: (params: Record<string, unknown>) => Promise<unknown>;\n }>;\n}\n\nexport interface TabQueryParamUpdateGuard {\n resourceType?: string | null;\n driverClass?: string | null;\n recordId?: string | null;\n navItemName?: string | null;\n entity?: string | null;\n}\n\n/**\n * System application ID for non-app-specific resources (fallback only)\n * Uses double underscore prefix to indicate system-level resource\n * @deprecated Prefer using NavigationService.getDefaultApplicationId() instead\n */\nexport const SYSTEM_APP_ID = '__explorer';\n\n/**\n * Neutral color for fallback when no app is available\n */\nconst NEUTRAL_APP_COLOR = '#9E9E9E'; // Material Design Gray 500\n\n/**\n * Centralized navigation service that handles all navigation operations\n * with automatic shift-key detection for power user workflows\n */\n@Injectable({\n providedIn: 'root'\n})\nexport class NavigationService implements OnDestroy {\n private shiftKeyPressed = false;\n private subscriptions: Subscription[] = [];\n\n private queryParamChanged$ = new Subject<QueryParamChangeEvent>();\n /** Observable that emits when query params change on a tab (back/forward navigation). */\n public QueryParamChanged$ = this.queryParamChanged$.asObservable();\n\n /** Cached Home app ID (null means not found, undefined means not checked) */\n private _homeAppId: string | null | undefined = undefined;\n /** Cached Home app color */\n private _homeAppColor: string | null = null;\n\n constructor(\n private workspaceManager: WorkspaceStateManager,\n private appManager: ApplicationManager\n ) {\n this.setupGlobalShiftKeyDetection();\n }\n\n /**\n * Get the neutral color used for system-wide resources (entities, views, dashboards)\n * Returns a light neutral gray\n * @deprecated Use getDefaultAppColor() for better UX with Home app integration\n */\n get ExplorerAppColor(): string {\n return NEUTRAL_APP_COLOR;\n }\n\n /**\n * Gets the default application ID for orphan resources.\n * Priority: Home app > Active app > SYSTEM_APP_ID\n *\n * This ensures orphan resources (entity records, dashboards, views opened directly)\n * are grouped under the Home app instead of being orphaned in the tab system.\n */\n private getDefaultApplicationId(): string {\n // Check cache first\n if (this._homeAppId !== undefined) {\n if (this._homeAppId !== null) {\n return this._homeAppId;\n }\n // Home app not found, check active app\n const activeApp = this.appManager.GetActiveApp();\n if (activeApp) {\n return activeApp.ID;\n }\n return SYSTEM_APP_ID;\n }\n\n // First time - look for Home app\n const homeApp = this.appManager.GetAppByName('Home');\n if (homeApp) {\n this._homeAppId = homeApp.ID;\n this._homeAppColor = homeApp.GetColor();\n return homeApp.ID;\n }\n\n // Cache that Home app doesn't exist\n this._homeAppId = null;\n\n // Fall back to currently active app\n const activeApp = this.appManager.GetActiveApp();\n if (activeApp) {\n return activeApp.ID;\n }\n\n // Last resort - system app ID\n return SYSTEM_APP_ID;\n }\n\n /**\n * Gets the default app color for orphan resources.\n * Returns Home app color if available, otherwise neutral gray.\n */\n private getDefaultAppColor(): string {\n // Ensure cache is populated\n this.getDefaultApplicationId();\n\n // If Home app exists, use its color\n if (this._homeAppColor) {\n return this._homeAppColor;\n }\n\n // Check active app\n const activeApp = this.appManager.GetActiveApp();\n if (activeApp) {\n return activeApp.GetColor();\n }\n\n // Fall back to neutral color\n return NEUTRAL_APP_COLOR;\n }\n\n /**\n * Clears the cached Home app info.\n * Call this if apps are reloaded or user logs out.\n */\n public clearHomeAppCache(): void {\n this._homeAppId = undefined;\n this._homeAppColor = null;\n }\n\n // ════════════════════════════════════════════\n // Agent Context & Client Tools\n // ════════════════════════════════════════════\n\n /**\n * Observable stream of agent context updates from resource components.\n * The shell subscribes to this to update the ComponentCacheManager and\n * push changes to the chat overlay's AppContextSnapshot.DashboardContext.\n */\n public readonly AgentContextUpdated$ = new Subject<AgentContextUpdate>();\n\n /** The client tools currently surfaced to the agent (the most recent SetAgentClientTools set). */\n private currentAgentTools: NonNullable<AgentContextUpdate['AgentClientTools']> = [];\n\n /**\n * Tools captured for each cached resource component at the moment it was DETACHED, keyed by that\n * component (the one the cache manager tracks). Replayed on reattach. Captured at detach time (vs.\n * keyed by the registering component) so it works even when a resource WRAPPER component is what's\n * cached/reattached while an INNER child component is what actually called SetAgentClientTools\n * (e.g. Data Explorer's resource wrapper hosting its dashboard) — keying by the registerer would\n * miss on reattach. This keeps the agent's live tool set a function of the CURRENTLY attached\n * surface, fixing the staleness where a previous app's tools lingered after navigation.\n */\n private readonly agentToolsByDetachedResource = new Map<BaseResourceComponent, NonNullable<AgentContextUpdate['AgentClientTools']>>();\n\n /**\n * Latest `AppContextSnapshot` published by the Explorer app shell.\n *\n * Why: any embedded `<mj-conversation-chat-area>` instance outside the\n * floating chat overlay (Form Builder cockpit, future domain dashboards\n * that pop their own AI pane) needs to feed the SAME context the overlay\n * does so the agent sees what app + view + dashboard state the user is\n * looking at. Without this, the agent only sees the embedder's narrow\n * `AdditionalContext` slice and treats the user as if they have no app\n * context at all — which is the bug we just fixed.\n *\n * `MJExplorerAppComponent` is the canonical publisher (it owns the\n * snapshot construction); consumers SUBSCRIBE and bind the value to\n * their chat-area's `[appContext]`. Non-Explorer apps (custom MJ apps\n * that don't include explorer-app at all) build their own snapshot via\n * `BuildAppContextSnapshot()` in `@memberjunction/ai-core-plus`.\n *\n * Initial value is `null`; the publisher emits the first real snapshot\n * after the active app + nav state resolve on bootstrap.\n */\n public readonly AppContextSnapshot$ = new BehaviorSubject<AppContextSnapshot | null>(null);\n\n /**\n * Push a fresh AppContextSnapshot. Called by MJExplorerAppComponent\n * after each (a) app/tab change, (b) `handleAgentContextUpdate`\n * merging in `AdditionalContext` from a dashboard. Idempotent — no\n * de-duplication; embedders should treat the stream as \"the latest\n * value is canonical.\"\n */\n public PublishAppContextSnapshot(snapshot: AppContextSnapshot | null): void {\n this.AppContextSnapshot$.next(snapshot);\n }\n\n /**\n * Report the current agent-visible state from a resource component.\n * Call this whenever the dashboard's internal state changes (tab switch,\n * filter change, pipeline status change, drill-down, etc.).\n *\n * @param caller - Pass `this` from the calling component. Used to match\n * against the ComponentCacheManager to identify which cached component\n * this update belongs to.\n * @param context - Key-value pairs representing dashboard state the agent\n * should know about. Each dashboard defines its own shape.\n */\n public SetAgentContext(caller: BaseResourceComponent, context: Record<string, unknown>): void {\n this.AgentContextUpdated$.next({ Caller: caller, AgentContext: context });\n }\n\n /**\n * Register the client tools available from a resource component.\n * Call this on component init and whenever the available tools change.\n * Tools are automatically unregistered when the component becomes inactive\n * (tab switch) and re-registered when it becomes active again.\n *\n * @param caller - Pass `this` from the calling component.\n * @param tools - Array of tool definitions with Name, Description,\n * ParameterSchema (JSON Schema), and Handler function.\n */\n public SetAgentClientTools(caller: BaseResourceComponent, tools: Array<{\n Name: string;\n Description: string;\n ParameterSchema: Record<string, unknown>;\n Handler: (params: Record<string, unknown>) => Promise<unknown>;\n }>): void {\n this.currentAgentTools = tools;\n this.AgentContextUpdated$.next({ Caller: caller, AgentClientTools: tools });\n }\n\n /**\n * Re-publish a cached resource component's tools when its tab is re-focused. Cached components keep\n * their Angular instance but do NOT re-run `ngAfterViewInit`, so they never re-register on reattach\n * — the shell calls this so the just-reactivated surface's tools become the agent's active set\n * again. Replays the set captured for this component at its last detach; no-op (lets a fresh\n * component register itself) when none was captured (e.g. a component's very first attach).\n */\n public NotifyResourceReattached(caller: BaseResourceComponent): void {\n const tools = this.agentToolsByDetachedResource.get(caller);\n if (tools === undefined) {\n return;\n }\n this.currentAgentTools = tools;\n this.AgentContextUpdated$.next({ Caller: caller, AgentClientTools: tools });\n }\n\n /**\n * Capture + clear the active client tools when a resource component's tab is detached (navigated\n * away from), so the previous surface's tools aren't offered to the agent on the next surface. We\n * snapshot whatever tools are CURRENTLY active and key them by the detaching component, so\n * {@link NotifyResourceReattached} can replay them — robust to a wrapper component being the one\n * cached/reattached while an inner child actually registered the tools (e.g. Data Explorer).\n */\n public NotifyResourceDetached(caller: BaseResourceComponent): void {\n this.agentToolsByDetachedResource.set(caller, this.currentAgentTools);\n this.currentAgentTools = [];\n this.AgentContextUpdated$.next({ Caller: caller, AgentClientTools: [] });\n }\n\n /**\n * Drop a destroyed component's captured tools (e.g. on LRU eviction), so the map doesn't retain\n * references to dead component instances.\n */\n public ForgetResource(caller: BaseResourceComponent): void {\n this.agentToolsByDetachedResource.delete(caller);\n }\n\n ngOnDestroy(): void {\n this.subscriptions.forEach(sub => sub.unsubscribe());\n }\n\n /**\n * Set up global keyboard event listeners to track shift key state\n */\n private setupGlobalShiftKeyDetection(): void {\n // Track shift key via mousedown events (capture phase) instead of keydown/keyup.\n // This is more reliable because:\n // 1. MouseEvent.shiftKey always reflects the actual modifier state at click time\n // 2. No risk of \"stuck\" state from missed keyup events (focus loss, tab switch, etc.)\n // 3. Navigation is always triggered by a click, so the shift state is read\n // at exactly the right moment\n this.subscriptions.push(\n fromEvent<MouseEvent>(document, 'mousedown', { capture: true }).subscribe(event => {\n this.shiftKeyPressed = event.shiftKey;\n })\n );\n }\n\n /**\n * Get current shift key state\n */\n private isShiftPressed(): boolean {\n return this.shiftKeyPressed;\n }\n\n /**\n * Determine if a new tab should be forced based on options and shift key state\n */\n private shouldForceNewTab(options?: NavigationOptions): boolean {\n // If forceNewTab is explicitly set, use that\n if (options?.forceNewTab !== undefined) {\n return options.forceNewTab;\n }\n\n // Otherwise, use global shift key detection\n return this.isShiftPressed();\n }\n\n /**\n * Returns whether the caller should use OpenTabForced (force-new path) or\n * OpenTab (replace-temp path).\n *\n * Rule: only honor an explicit force-new request — from the user via\n * shift+click, or from the caller via `options.forceNewTab`. We deliberately\n * do NOT apply heuristics that auto-switch the workspace out of\n * single-resource mode on cross-resource navigation. A previous version of\n * this method tried to do that (\"force new if single-resource + different\n * resource\") and it caused a regression: every plain hyperlink click on a\n * record opened a new tab and dropped the user into multi-tab mode, even\n * though they didn't ask for it. That violated the principle that mode\n * transitions are user-driven (shift) or explicitly requested (options).\n *\n * If a particular caller really needs the parent context preserved when\n * creating/navigating to a child resource (e.g. \"+New\" on a related-entity\n * grid inside an open record), the caller should pass `forceNewTab: true`\n * in `NavigationOptions`. That keeps intent explicit at the call site\n * instead of buried in a global heuristic.\n */\n private handleSingleResourceModeTransition(forceNew: boolean, _newRequest: TabRequest): boolean {\n return forceNew;\n }\n\n /**\n * Check if a tab request matches an existing tab's resource\n */\n private isSameResource(tab: any, request: TabRequest): boolean {\n // Different apps = different resources\n if (tab.applicationId !== request.ApplicationId) {\n return false;\n }\n\n // For resource-based tabs, compare resourceType and recordId\n if (request.Configuration?.resourceType) {\n const requestRecordId = request.ResourceRecordId || '';\n const tabRecordId = tab.resourceRecordId || '';\n return tab.configuration?.resourceType === request.Configuration.resourceType &&\n tabRecordId === requestRecordId;\n }\n\n // For app nav items, compare appName and navItemName\n if (request.Configuration?.appName && request.Configuration?.navItemName) {\n return tab.configuration?.appName === request.Configuration.appName &&\n tab.configuration?.navItemName === request.Configuration.navItemName;\n }\n\n // Fallback to basic comparison\n return false;\n }\n\n /**\n * Open a navigation item within an app\n */\n public OpenNavItem(appId: string, navItem: NavItem, appColor: string, options?: NavigationOptions): string {\n let forceNew = this.shouldForceNewTab(options);\n\n // Get the app to find its name\n const app = this.appManager.GetAppById(appId);\n const appName = app?.Name || '';\n\n // Dynamic nav items (e.g. orphan entity records) carry their original tab Configuration\n // and should NOT get navItemName stamped on them — that would cause buildResourceUrl\n // to produce a nav-item-style URL like /app/home/<label> instead of the correct\n // resource-type URL like /app/home/record/Entity/ID|...\n const isDynamic = (navItem as DynamicNavItem).isDynamic === true;\n\n const request: TabRequest = {\n ApplicationId: appId,\n Title: navItem.Label,\n ResourceRecordId: navItem.RecordID || '', // Also store at top level for consistent tab matching\n Configuration: {\n route: navItem.Route,\n resourceType: navItem.ResourceType,\n driverClass: navItem.DriverClass, // Pass through DriverClass for Custom resource type\n recordId: navItem.RecordID,\n appName: appName, // Store app name for URL building\n appId: appId,\n ...(isDynamic ? {} : { navItemName: navItem.Label }), // Only set for static nav items\n ...(navItem.Configuration || {})\n },\n IsPinned: options?.pinTab || false\n };\n\n // Handle transition from single-resource mode\n forceNew = this.handleSingleResourceModeTransition(forceNew, request);\n\n let tabId: string;\n if (forceNew) {\n // Always create a new tab\n tabId = this.workspaceManager.OpenTabForced(request, appColor);\n } else {\n // Use existing OpenTab logic (may replace temporary tab)\n tabId = this.workspaceManager.OpenTab(request, appColor);\n }\n\n // Apply query params to the newly opened/activated tab if provided\n if (options?.queryParams) {\n this.applyQueryParamsToTab(tabId, options.queryParams);\n }\n\n return tabId;\n }\n\n /**\n * Open an entity record view. Behavior forks on the deployment's\n * record-open style (see record-open-style.ts):\n * - 'records' (default): the record ALWAYS opens as its own tab, assigned\n * to the app the user is standing in (no Home reassignment, no nav flip).\n * Re-opening a record that's already open focuses its existing tab,\n * browser-style. The shell's Records pill + strip are the visible UI.\n * - 'classic': the original Home-first assignment and tab semantics.\n * Both styles capture the source-app trail into the tab configuration for\n * context UI and diagnostics.\n */\n public OpenEntityRecord(\n entityName: string,\n recordPkey: CompositeKey,\n options?: NavigationOptions\n ): string {\n const tabsMode = IsRecordTabsStyle();\n\n const activeApp = this.appManager.GetActiveApp();\n const appId = tabsMode && activeApp ? activeApp.ID : this.getDefaultApplicationId();\n const appColor = tabsMode && activeApp ? activeApp.GetColor() : this.getDefaultAppColor();\n\n const recordId = recordPkey.ToURLSegment();\n\n // Browser-style dedup: if this exact record is already open in a tab,\n // focus that tab instead of opening a second copy\n if (tabsMode) {\n const existing = this.findOpenRecordTab(entityName, recordId);\n if (existing) {\n // RE-CAPTURE the origin: re-opening means \"I came from somewhere\n // NEW\" — the crumb must point at where the user is standing for\n // THIS open, not wherever they were when the tab was first created\n // (possibly days ago, possibly under an older origin schema).\n this.refreshSourceContext(existing.id, options);\n // The activation assert applies to RE-opens too — the same-click\n // stale-stomp that motivated it for fresh opens is equally possible\n // here, and without it the symptom is maddening: opening a record\n // works the FIRST time and silently fails every time after.\n this.workspaceManager.SetActiveTab(existing.id);\n this.assertRecordActivation(existing.id);\n return existing.id;\n }\n }\n\n let forceNew = tabsMode || this.shouldForceNewTab(options);\n\n const request: TabRequest = {\n ApplicationId: appId,\n Title: `${entityName} - ${recordId}`,\n Configuration: {\n resourceType: RECORDS_RESOURCE_TYPE,\n Entity: entityName, // Must use 'Entity' (capital E) - expected by record-resource.component\n recordId: recordId, // Also needed in Configuration for tab-container.component to populate ResourceRecordID\n ...this.resolveSourceContext(options)\n },\n ResourceRecordId: recordId,\n IsPinned: options?.pinTab || false,\n // Records style: opening a record must not pin the nav tab (see\n // TabRequest.PreservePinState) — a pinned nav tab forces the main tab\n // bar visible on every nav page.\n PreservePinState: tabsMode\n };\n\n // Handle transition from single-resource mode\n forceNew = this.handleSingleResourceModeTransition(forceNew, request);\n\n let tabId: string;\n if (forceNew) {\n tabId = this.workspaceManager.OpenTabForced(request, appColor);\n } else {\n tabId = this.workspaceManager.OpenTab(request, appColor);\n }\n\n if (tabsMode) {\n this.assertRecordActivation(tabId);\n }\n\n return tabId;\n }\n\n /**\n * Opening a record means the record WINS the activation race — assert it\n * after the synchronous open flow settles. Surfaces that both emit a\n * record-open AND run their own internal navigation in the same click\n * (e.g. a grid row's select handler updating query params from a config\n * snapshot captured before the open) can otherwise stomp the activation\n * with stale state, leaving the tab open but never shown. Skipped when the\n * tab was closed in the meantime.\n */\n private assertRecordActivation(tabId: string): void {\n setTimeout(() => {\n const config = this.workspaceManager.GetConfiguration();\n if (config?.tabs.some(t => t.id === tabId) && config.activeTabId !== tabId) {\n this.workspaceManager.SetActiveTab(tabId);\n }\n }, 0);\n }\n\n /** Find an already-open tab showing this exact record (records-style dedup) */\n private findOpenRecordTab(entityName: string, recordId: string): { id: string } | null {\n const config = this.workspaceManager.GetConfiguration();\n const match = config?.tabs.find(t =>\n t.resourceRecordId === recordId &&\n t.configuration?.['resourceType'] === RECORDS_RESOURCE_TYPE &&\n t.configuration?.['Entity'] === entityName\n );\n return match ? { id: match.id } : null;\n }\n\n /**\n * Overwrite a record tab's origin with a freshly-resolved one (the dedup\n * re-open path). All source keys are explicitly cleared first — a shallow\n * config merge would otherwise let fields from the OLD origin shape leak\n * through (e.g. a parent-record origin's sourceRecordEntity surviving a\n * re-open from a nav page, or pre-schema captures keeping stale labels).\n */\n private refreshSourceContext(tabId: string, options?: NavigationOptions): void {\n this.workspaceManager.UpdateTabConfiguration(tabId, {\n sourceAppId: undefined,\n sourceAppName: undefined,\n sourceNavLabel: undefined,\n sourceNavItemName: undefined,\n sourceTabId: undefined,\n sourceLabel: undefined,\n sourceQueryParams: undefined,\n sourceRecordEntity: undefined,\n sourceRecordId: undefined,\n ...this.resolveSourceContext(options)\n });\n }\n\n /**\n * Resolve the source context to stamp into a record tab's configuration.\n * Callers that are workspace tabs omit options.recordSource and get the\n * active app/tab snapshot; overlay surfaces pass their own context;\n * 'none' means no origin (no crumb).\n */\n private resolveSourceContext(options?: NavigationOptions): Record<string, unknown> {\n if (options?.recordSource === 'none') {\n return {};\n }\n if (options?.recordSource) {\n const src = options.recordSource;\n const context: Record<string, unknown> = {};\n if (src.sourceAppId) context['sourceAppId'] = src.sourceAppId;\n if (src.sourceAppName) context['sourceAppName'] = src.sourceAppName;\n if (src.sourceNavLabel) context['sourceNavLabel'] = src.sourceNavLabel;\n if (src.sourceTabId) context['sourceTabId'] = src.sourceTabId;\n if (src.sourceLabel) context['sourceLabel'] = src.sourceLabel;\n if (src.sourceQueryParams && Object.keys(src.sourceQueryParams).length > 0) {\n context['sourceQueryParams'] = src.sourceQueryParams;\n }\n return context;\n }\n return this.captureSourceContext();\n }\n\n /**\n * Snapshot of where the user was when the record was\n * opened — the source app and (when the active tab belongs to that app and\n * isn't itself a record) the nav page they were on. Rendered by the\n * breadcrumb strip as \"App › Page › Record\".\n */\n private captureSourceContext(): Record<string, unknown> {\n const activeApp = this.appManager.GetActiveApp();\n if (!activeApp) {\n return {};\n }\n const context: Record<string, unknown> = {\n sourceAppId: activeApp.ID,\n sourceAppName: activeApp.Name\n };\n const config = this.workspaceManager.GetConfiguration();\n const activeTab = config?.tabs.find(t => t.id === config.activeTabId);\n if (!activeTab || !UUIDsEqual(activeTab.applicationId, activeApp.ID) || !activeTab.title) {\n return context;\n }\n if (IsRecordsTabConfiguration(activeTab.configuration)) {\n // Opened from INSIDE another record → the crumb points at the PARENT\n // record (Matt's record→record design call). Identity, not just the\n // tab id, so the return survives tab closure (re-open via dedup).\n const parentEntity = activeTab.configuration?.['Entity'];\n const parentRecordId = activeTab.resourceRecordId || activeTab.configuration?.['recordId'];\n if (typeof parentEntity === 'string' && typeof parentRecordId === 'string' && parentRecordId) {\n context['sourceTabId'] = activeTab.id;\n context['sourceLabel'] = activeTab.title;\n context['sourceRecordEntity'] = parentEntity;\n context['sourceRecordId'] = parentRecordId;\n }\n return context;\n }\n context['sourceNavLabel'] = activeTab.title;\n // Canonical nav identity alongside the display title: titles mutate\n // (Data Explorer renames its tab to the drilled entity), so the return\n // path verifies/resolves by navItemName and shows the label.\n const navItemName = activeTab.configuration?.['navItemName'];\n if (typeof navItemName === 'string' && navItemName.length > 0) {\n context['sourceNavItemName'] = navItemName;\n }\n // The exact tab, not just its label — the origin crumb's return path\n // prefers reactivating this tab (state intact) over re-resolving the\n // nav item by label.\n context['sourceTabId'] = activeTab.id;\n // Snapshot the tab's query params too: dashboards keep their DRILL-IN\n // state there (e.g. Data Explorer's entity section), and it can drift\n // or be stomped between open and return. The crumb restores the page\n // AS IT WAS, not just the tab.\n const queryParams = activeTab.configuration?.['queryParams'];\n if (queryParams && typeof queryParams === 'object' && Object.keys(queryParams).length > 0) {\n context['sourceQueryParams'] = { ...(queryParams as Record<string, string>) };\n }\n return context;\n }\n\n /**\n * Switch to an app's LANDING page — its default nav item in its BASE\n * state (drifted section params cleared, reset force-delivered so the\n * dashboard actually returns to its landing even if its state diverged).\n * The origin crumb's APP segment uses this: classic breadcrumb semantics,\n * each level goes to that level's home.\n */\n async SwitchToAppHome(appId: string): Promise<void> {\n const app = this.appManager.GetAllApps().find(a => UUIDsEqual(a.ID, appId));\n if (!app) {\n return;\n }\n const navItems = await app.GetNavItems();\n const home = navItems.find(i => i.isDefault) ?? navItems[0];\n await this.SwitchToApp(appId, home?.Label);\n const tabId = this.workspaceManager.GetActiveTabId();\n if (!tabId) {\n return;\n }\n const tab = this.workspaceManager.GetTab(tabId);\n const current = (tab?.configuration?.['queryParams'] || {}) as Record<string, string>;\n const clear: Record<string, string | null> = {};\n for (const key of Object.keys(current)) {\n clear[key] = null;\n }\n if (Object.keys(clear).length > 0) {\n this.applyQueryParamsToTab(tabId, clear);\n }\n this.NotifyQueryParamsChanged(tabId, {}, true);\n }\n\n /**\n * Take the user back to where they were when a record was opened (the\n * origin crumb's click path). Fidelity ladder:\n * 1. The exact source tab, if it's still open — reactivate it with all its\n * state (query params, scroll, cached component) intact.\n * 2. Otherwise re-resolve by app + nav label via SwitchToApp, which reuses\n * a matching open tab or opens the nav item fresh.\n * No-ops when the context has no sourceAppId (capture was best-effort).\n */\n public async ReturnToRecordSource(origin: RecordSourceContext): Promise<void> {\n // Parent-RECORD origin (opened from inside another record): activate the\n // parent's tab while it still shows that record, else RE-OPEN the parent\n // — OpenEntityRecord's dedup makes the re-open land on (or recreate)\n // exactly that record. No app flip either way: record tabs live in the\n // records region regardless of app.\n if (origin.sourceRecordEntity && origin.sourceRecordId) {\n const config = this.workspaceManager.GetConfiguration();\n const sourceTab = origin.sourceTabId ? config?.tabs.find(t => t.id === origin.sourceTabId) : undefined;\n const stillParent = !!sourceTab &&\n IsRecordsTabConfiguration(sourceTab.configuration) &&\n sourceTab.configuration?.['Entity'] === origin.sourceRecordEntity &&\n sourceTab.resourceRecordId === origin.sourceRecordId;\n if (sourceTab && stillParent) {\n this.workspaceManager.SetActiveTab(sourceTab.id);\n return;\n }\n const parentKey = new CompositeKey();\n parentKey.SimpleLoadFromURLSegment(origin.sourceRecordId);\n this.OpenEntityRecord(origin.sourceRecordEntity, parentKey);\n return;\n }\n if (!origin.sourceAppId) {\n return;\n }\n if (origin.sourceTabId) {\n const config = this.workspaceManager.GetConfiguration();\n const sourceTab = config?.tabs.find(t => t.id === origin.sourceTabId);\n // Tab ids are NOT stable identities for nav pages: the temp-tab\n // replacement model (WorkspaceStateManager.OpenTab) reuses the same\n // tab id when the user navigates elsewhere — the captured id can now\n // hold a DIFFERENT page in a DIFFERENT app. Trust it only while the\n // tab still shows the captured page; otherwise fall through to\n // re-resolving by app + nav item below. Prefer navItemName for the\n // page check — TITLES MUTATE (Data Explorer renames its tab to the\n // drilled entity), and a mutated title must not disqualify the tab.\n const tabNavItemName = sourceTab?.configuration?.['navItemName'];\n const samePage = origin.sourceNavItemName && typeof tabNavItemName === 'string'\n ? tabNavItemName === origin.sourceNavItemName\n : (!origin.sourceNavLabel || sourceTab?.title === origin.sourceNavLabel);\n const stillSourcePage = !!sourceTab &&\n (!origin.sourceAppId || UUIDsEqual(sourceTab.applicationId, origin.sourceAppId)) &&\n samePage;\n if (sourceTab && stillSourcePage) {\n // Restore the tab's query params to the capture-time snapshot BEFORE\n // activating, so a cached resource component reattaches against the\n // right config (see SwitchToApp's queryParams doc). REPLACE\n // semantics: null out params the tab has gained since capture, then\n // lay the snapshot over — \"back\" means the page as it was, and\n // drill-in state (e.g. Data Explorer's entity section) lives here.\n const currentParams = (sourceTab.configuration?.['queryParams'] || {}) as Record<string, string>;\n const restore: Record<string, string | null> = {};\n for (const key of Object.keys(currentParams)) {\n restore[key] = null;\n }\n Object.assign(restore, origin.sourceQueryParams ?? {});\n if (Object.keys(restore).length > 0) {\n this.applyQueryParamsToTab(sourceTab.id, restore);\n }\n // FORCE delivery: the component's state may have drifted while its\n // last-delivered params already equal the snapshot — the reactive\n // path would drop the restore as a duplicate (crumb lands on the\n // dashboard's home instead of the captured section).\n if (origin.sourceQueryParams && Object.keys(origin.sourceQueryParams).length > 0) {\n this.NotifyQueryParamsChanged(sourceTab.id, origin.sourceQueryParams, true);\n }\n // Set the app context first so the shell's nav renders the right\n // app the moment the tab activates (activation alone would also\n // flip it, but explicit sequencing avoids a visible two-step).\n await this.appManager.SetActiveApp(sourceTab.applicationId);\n this.workspaceManager.SetActiveTab(sourceTab.id);\n return;\n }\n }\n // Re-resolve by the CANONICAL nav-item name when we have it — the label\n // is a display title that may have mutated into something SwitchToApp's\n // nav-item lookup can't match (e.g. a drilled entity name).\n await this.SwitchToApp(origin.sourceAppId, origin.sourceNavItemName ?? origin.sourceNavLabel, origin.sourceQueryParams);\n // Same drift-proofing as the exact-tab path: force the delivery so the\n // restore can't be deduped away.\n if (origin.sourceQueryParams && Object.keys(origin.sourceQueryParams).length > 0) {\n const targetTabId = this.workspaceManager.GetActiveTabId();\n if (targetTabId) {\n this.NotifyQueryParamsChanged(targetTabId, origin.sourceQueryParams, true);\n }\n }\n }\n\n /**\n * Open a view\n * Uses Home app if available, otherwise falls back to active app or system app\n */\n public OpenView(\n viewId: string,\n viewName: string,\n options?: NavigationOptions\n ): string {\n const appId = this.getDefaultApplicationId();\n const appColor = this.getDefaultAppColor();\n let forceNew = this.shouldForceNewTab(options);\n\n const request: TabRequest = {\n ApplicationId: appId,\n Title: viewName,\n Configuration: {\n resourceType: 'MJ: User Views',\n viewId,\n recordId: viewId // Also needed in Configuration for tab-container.component to populate ResourceRecordID\n },\n ResourceRecordId: viewId,\n IsPinned: options?.pinTab || false\n };\n\n // Handle transition from single-resource mode\n forceNew = this.handleSingleResourceModeTransition(forceNew, request);\n\n if (forceNew) {\n return this.workspaceManager.OpenTabForced(request, appColor);\n } else {\n return this.workspaceManager.OpenTab(request, appColor);\n }\n }\n\n /**\n * Open a dashboard\n * Uses Home app if available, otherwise falls back to active app or system app\n */\n public OpenDashboard(\n dashboardId: string,\n dashboardName: string,\n options?: NavigationOptions\n ): string {\n const appId = this.getDefaultApplicationId();\n const appColor = this.getDefaultAppColor();\n let forceNew = this.shouldForceNewTab(options);\n\n const request: TabRequest = {\n ApplicationId: appId,\n Title: dashboardName,\n Configuration: {\n resourceType: 'Dashboards',\n dashboardId,\n recordId: dashboardId // Also needed in Configuration for tab-container.component to populate ResourceRecordID\n },\n ResourceRecordId: dashboardId,\n IsPinned: options?.pinTab || false\n };\n\n // Handle transition from single-resource mode\n forceNew = this.handleSingleResourceModeTransition(forceNew, request);\n\n if (forceNew) {\n return this.workspaceManager.OpenTabForced(request, appColor);\n } else {\n return this.workspaceManager.OpenTab(request, appColor);\n }\n }\n\n /**\n * Open an artifact\n * Artifacts are versioned content containers (reports, dashboards, UI components, etc.)\n * Uses Home app if available, otherwise falls back to active app or system app\n */\n public OpenArtifact(\n artifactId: string,\n artifactName?: string,\n options?: NavigationOptions\n ): string {\n const appId = this.getDefaultApplicationId();\n const appColor = this.getDefaultAppColor();\n let forceNew = this.shouldForceNewTab(options);\n\n const request: TabRequest = {\n ApplicationId: appId,\n Title: artifactName || `Artifact - ${artifactId}`,\n Configuration: {\n resourceType: 'Artifacts',\n artifactId,\n recordId: artifactId // Also needed in Configuration for tab-container.component to populate ResourceRecordID\n },\n ResourceRecordId: artifactId,\n IsPinned: options?.pinTab || false\n };\n\n // Handle transition from single-resource mode\n forceNew = this.handleSingleResourceModeTransition(forceNew, request);\n\n if (forceNew) {\n return this.workspaceManager.OpenTabForced(request, appColor);\n } else {\n return this.workspaceManager.OpenTab(request, appColor);\n }\n }\n\n /**\n * Open a dynamic view\n * Dynamic views are entity-based views with custom filters, not saved views\n * Uses Home app if available, otherwise falls back to active app or system app\n */\n public OpenDynamicView(\n entityName: string,\n extraFilter?: string,\n options?: NavigationOptions\n ): string {\n const appId = this.getDefaultApplicationId();\n const appColor = this.getDefaultAppColor();\n let forceNew = this.shouldForceNewTab(options);\n\n const filterSuffix = extraFilter ? ' (Filtered)' : '';\n const request: TabRequest = {\n ApplicationId: appId,\n Title: `${entityName}${filterSuffix}`,\n Configuration: {\n resourceType: 'MJ: User Views',\n Entity: entityName,\n ExtraFilter: extraFilter,\n isDynamic: true,\n recordId: 'dynamic' // Special marker for dynamic views\n },\n ResourceRecordId: 'dynamic',\n IsPinned: options?.pinTab || false\n };\n\n // Handle transition from single-resource mode\n forceNew = this.handleSingleResourceModeTransition(forceNew, request);\n\n if (forceNew) {\n return this.workspaceManager.OpenTabForced(request, appColor);\n } else {\n return this.workspaceManager.OpenTab(request, appColor);\n }\n }\n\n /**\n * Open a query\n * Uses Home app if available, otherwise falls back to active app or system app\n */\n public OpenQuery(\n queryId: string,\n queryName: string,\n options?: NavigationOptions\n ): string {\n const appId = this.getDefaultApplicationId();\n const appColor = this.getDefaultAppColor();\n let forceNew = this.shouldForceNewTab(options);\n\n const request: TabRequest = {\n ApplicationId: appId,\n Title: queryName,\n Configuration: {\n resourceType: 'Queries',\n queryId,\n recordId: queryId // Also needed in Configuration for tab-container.component to populate ResourceRecordID\n },\n ResourceRecordId: queryId,\n IsPinned: options?.pinTab || false\n };\n\n // Handle transition from single-resource mode\n forceNew = this.handleSingleResourceModeTransition(forceNew, request);\n\n if (forceNew) {\n return this.workspaceManager.OpenTabForced(request, appColor);\n } else {\n return this.workspaceManager.OpenTab(request, appColor);\n }\n }\n\n /**\n * Open a new entity record creation form\n * Uses Home app if available, otherwise falls back to active app or system app\n * @param entityName The name of the entity to create a new record for\n * @param options Navigation options including optional newRecordValues for pre-populating fields\n */\n public OpenNewEntityRecord(\n entityName: string,\n options?: NavigationOptions\n ): string {\n // Records style: new-record tabs are RECORD tabs and get the same\n // treatment as OpenEntityRecord — assigned to the ACTIVE app, always a\n // new tab, nav pins preserved, origin captured. Without the forceNew,\n // the non-forced OpenTab consumed the user's nav page as the\n // replaceable temp tab and CONVERTED it into a records-region tab —\n // silently destroying the page they were on.\n const tabsMode = IsRecordTabsStyle();\n const activeApp = this.appManager.GetActiveApp();\n const appId = tabsMode && activeApp ? activeApp.ID : this.getDefaultApplicationId();\n const appColor = tabsMode && activeApp ? activeApp.GetColor() : this.getDefaultAppColor();\n\n let forceNew = tabsMode || this.shouldForceNewTab(options);\n\n const request: TabRequest = {\n ApplicationId: appId,\n Title: `New ${entityName}`,\n Configuration: {\n resourceType: RECORDS_RESOURCE_TYPE,\n Entity: entityName, // Must use 'Entity' (capital E) - expected by record-resource.component\n recordId: '', // Empty recordId indicates new record\n isNew: true, // Flag to indicate this is a new record\n NewRecordValues: options?.newRecordValues, // Pass through initial values if provided\n ...this.resolveSourceContext(options)\n },\n ResourceRecordId: '', // Empty for new records\n IsPinned: options?.pinTab || false,\n PreservePinState: tabsMode\n };\n\n // Handle transition from single-resource mode\n forceNew = this.handleSingleResourceModeTransition(forceNew, request);\n\n let tabId: string;\n if (forceNew) {\n tabId = this.workspaceManager.OpenTabForced(request, appColor);\n } else {\n tabId = this.workspaceManager.OpenTab(request, appColor);\n }\n\n if (tabsMode) {\n this.assertRecordActivation(tabId);\n }\n\n return tabId;\n }\n\n /**\n * Open a universal search results tab for the given query.\n * This is the primary way to open search results from anywhere in the application.\n *\n * @param query The search query text\n * @param searchOptions Optional search-specific options (e.g., minRelevance, scopeIDs)\n * @param options Navigation options\n */\n public OpenSearch(\n query: string,\n searchOptions?: { minRelevance?: number; scopeIDs?: string[] },\n options?: NavigationOptions\n ): string {\n const appId = this.getDefaultApplicationId();\n const appColor = this.getDefaultAppColor();\n let forceNew = this.shouldForceNewTab(options);\n\n const config: Record<string, unknown> = {\n resourceType: 'Search Results',\n Query: query,\n SearchInput: query,\n recordId: `search-${query}`\n };\n if (searchOptions?.minRelevance != null) {\n config['MinRelevance'] = searchOptions.minRelevance;\n }\n if (searchOptions?.scopeIDs && searchOptions.scopeIDs.length > 0) {\n config['ScopeIDs'] = searchOptions.scopeIDs;\n }\n\n const request: TabRequest = {\n ApplicationId: appId,\n Title: `Search: ${query}`,\n Configuration: config,\n ResourceRecordId: `search-${query}`,\n IsPinned: false\n };\n\n // Handle transition from single-resource mode\n forceNew = this.handleSingleResourceModeTransition(forceNew, request);\n\n if (forceNew) {\n return this.workspaceManager.OpenTabForced(request, appColor);\n } else {\n return this.workspaceManager.OpenTab(request, appColor);\n }\n }\n\n /**\n * Navigate to a nav item by name within the current or specified application.\n * Allows passing additional configuration parameters to merge with the nav item's config.\n * This is useful for cross-resource navigation where a component needs to navigate\n * to another nav item with specific parameters (e.g., navigate to Conversations with a specific conversationId).\n *\n * @param navItemName The label/name of the nav item to navigate to\n * @param configuration Additional configuration to merge (e.g., conversationId, artifactId)\n * @param appId Optional app ID (defaults to current active app)\n * @param options Navigation options\n * @returns The tab ID if successful, null if nav item not found\n */\n public async OpenNavItemByName(\n navItemName: string,\n configuration?: Record<string, unknown>,\n appId?: string,\n options?: NavigationOptions\n ): Promise<string | null> {\n // Get app (use provided or current active)\n const targetAppId = appId || this.appManager.GetActiveApp()?.ID;\n if (!targetAppId) {\n return null;\n }\n\n const app = this.appManager.GetAppById(targetAppId);\n if (!app) {\n return null;\n }\n\n // Find the nav item by name\n const navItems = await app.GetNavItems();\n const navItem = navItems.find(item => item.Label === navItemName);\n if (!navItem) {\n return null;\n }\n\n // Create a merged nav item with additional configuration\n const mergedNavItem: NavItem = {\n ...navItem,\n Configuration: {\n ...(navItem.Configuration || {}),\n ...(configuration || {})\n }\n };\n\n // Use existing OpenNavItem\n return this.OpenNavItem(targetAppId, mergedNavItem, app.GetColor(), options);\n }\n\n /**\n * Switch to an application by ID.\n * This sets the app as active and either opens a specific nav item or creates a default tab.\n * If the requested nav item already has an open tab, switches to that tab instead of creating a new one.\n * @param appId The application ID to switch to\n * @param navItemName Optional name of a nav item to open within the app. If provided, opens that nav item.\n * @param queryParams Optional query params to apply to the target tab. Applied SYNCHRONOUSLY once the\n * target tab is active — critical when navigating (e.g. from a Home pin) to an\n * app whose resource component is cached: the params must be in the tab config\n * BEFORE the tab-container reattaches the cached component, otherwise the cache\n * restores its own (stale) saved params and the navigation intent is lost.\n */\n async SwitchToApp(appId: string, navItemName?: string, queryParams?: Record<string, string | null>): Promise<void> {\n await this.appManager.SetActiveApp(appId);\n\n const app = this.appManager.GetAllApps().find(a => UUIDsEqual(a.ID, appId));\n if (!app) {\n return;\n }\n\n // Records style: record tabs are ASSIGNED to apps but live in their own\n // region — switching to an app must surface its NAV pages, so records\n // don't count here. Without this filter, the active record tab (which\n // belongs to the target app) satisfied the \"already on this app's tab\"\n // check and the whole switch silently no-oped — the dead origin-crumb\n // click when the crumb only knew the app. Records DOCKED to the\n // workspace are excluded too (identity check, not region membership):\n // switch-to-app must land on a page, never a record. Classic style\n // keeps records as ordinary tabs.\n const appTabs = IsRecordTabsStyle()\n ? this.workspaceManager.GetAppTabs(appId).filter(t => !IsRecordsTabConfiguration(t.configuration))\n : this.workspaceManager.GetAppTabs(appId);\n\n // If a specific nav item is requested\n if (navItemName) {\n const navItems = await app.GetNavItems();\n const navItem = navItems.find(item => item.Label === navItemName);\n if (navItem) {\n // Check if there's already a tab for this nav item. Identity check\n // uses configuration.navItemName when the tab carries it — TITLES\n // LIE: same-driver nav switches reuse the live tab and can leave a\n // stale title behind (e.g. Data Explorer's tab titled \"Data\" while\n // its content and navItemName say \"Dashboards\"). Matching that\n // chimera by title activated the WRONG page instead of reopening\n // the requested nav item.\n const existingTab = appTabs.find(tab => {\n const tabNav = tab.configuration?.['navItemName'];\n if (typeof tabNav === 'string' && tabNav.length > 0) {\n return tabNav === navItem.Label;\n }\n return tab.title === navItem.Label ||\n (tab.configuration?.['route'] === navItem.Route && !!navItem.Route);\n });\n\n if (existingTab) {\n // Switch to existing tab\n this.workspaceManager.SetActiveTab(existingTab.id);\n } else {\n // Open new tab for this nav item\n this.OpenNavItem(appId, navItem, app.GetColor());\n }\n // Apply the requested query params to whichever tab is now active — synchronously,\n // so they're present before the (possibly cached) resource component reattaches.\n if (queryParams && Object.keys(queryParams).length > 0) {\n const targetTabId = this.workspaceManager.GetActiveTabId();\n if (targetTabId) {\n this.applyQueryParamsToTab(targetTabId, queryParams);\n }\n }\n return;\n }\n // Nav item not found, fall through to default behavior\n }\n\n // No specific nav item requested - check if app has any tabs\n if (appTabs.length === 0) {\n // Create default tab\n const tabRequest = await app.CreateDefaultTab();\n if (tabRequest) {\n this.workspaceManager.OpenTab(tabRequest, app.GetColor());\n }\n } else {\n // App has tabs - switch to the first one (or active one if exists)\n const config = this.workspaceManager.GetConfiguration();\n const activeAppTab = appTabs.find(t => t.id === config?.activeTabId);\n if (!activeAppTab) {\n // No active tab for this app, switch to first tab\n this.workspaceManager.SetActiveTab(appTabs[0].id);\n }\n }\n\n // Apply requested query params on the DEFAULT path too (previously only the\n // navItemName branch did) — e.g. the omnibar's '@agent' → Chat pre-addressing.\n // Synchronous, so params are present before a cached resource component reattaches.\n if (queryParams && Object.keys(queryParams).length > 0) {\n const targetTabId = this.workspaceManager.GetActiveTabId();\n if (targetTabId) {\n this.applyQueryParamsToTab(targetTabId, queryParams);\n }\n }\n }\n\n /**\n * Update the query params for the currently active tab.\n * This updates the tab's configuration and triggers a URL sync via the shell's\n * workspace configuration subscription.\n *\n * Use this instead of directly calling router.navigate() to ensure proper\n * URL management that respects app-scoped routes.\n *\n * @param queryParams Object containing query param key-value pairs.\n * Use null values to remove a query param.\n * @example\n * // Add or update query params\n * navigationService.UpdateActiveTabQueryParams({ category: 'abc123', dashboard: 'xyz789' });\n *\n * // Remove a query param\n * navigationService.UpdateActiveTabQueryParams({ category: null });\n */\n UpdateActiveTabQueryParams(queryParams: Record<string, string | null>): void {\n const activeTabId = this.workspaceManager.GetActiveTabId();\n if (!activeTabId) {\n console.warn('NavigationService.UpdateActiveTabQueryParams: No active tab');\n return;\n }\n\n this.applyQueryParamsToTab(activeTabId, queryParams);\n }\n\n /**\n * Update query params for a specific tab, optionally only if the tab still hosts\n * the expected resource identity. This matters in single-resource mode where a\n * tab ID can be reused for a different resource while the previous component is\n * detached but still alive in the cache.\n */\n UpdateTabQueryParams(\n tabId: string,\n queryParams: Record<string, string | null>,\n guard?: TabQueryParamUpdateGuard\n ): boolean {\n const tab = this.workspaceManager.GetTab(tabId);\n if (!tab) {\n console.debug('NavigationService.UpdateTabQueryParams: Tab not found; ignoring stale query-param update:', tabId);\n return false;\n }\n\n if (guard && !this.tabMatchesQueryParamGuard(tab, guard)) {\n console.debug('NavigationService.UpdateTabQueryParams: Tab identity changed; ignoring stale query-param update:', {\n tabId,\n guard,\n current: {\n resourceType: tab.configuration?.['resourceType'],\n driverClass: tab.configuration?.['resourceTypeDriverClass'] || tab.configuration?.['driverClass'],\n recordId: tab.resourceRecordId || tab.configuration?.['recordId'],\n navItemName: tab.configuration?.['navItemName'],\n entity: tab.configuration?.['Entity'] || tab.configuration?.['entity']\n }\n });\n return false;\n }\n\n this.applyQueryParamsToTab(tabId, queryParams);\n return true;\n }\n\n /**\n * Notify subscribers that query params changed on a specific tab.\n * Called by the shell when back/forward navigation changes query params on the active tab.\n * The notification includes the tab ID so only the component in that tab reacts.\n */\n NotifyQueryParamsChanged(tabId: string, params: Record<string, string>, force = false): void {\n this.queryParamChanged$.next({ TabId: tabId, Params: params, Force: force });\n }\n\n /**\n * Reactively observe the query params for a specific tab.\n *\n * Backed by the workspace BehaviorSubject, so a subscriber receives the current\n * params *immediately* on subscribe AND every subsequent change — including the\n * deep-link params that the ResourceResolver merges into the tab configuration on\n * a cold/direct URL load.\n *\n * This is the race-free counterpart to {@link NotifyQueryParamsChanged} (a plain\n * Subject that drops events fired before a component has subscribed). A resource\n * component that mounts from workspace restoration can subscribe here and still\n * pick up its initial deep-link state regardless of whether the params landed in\n * the tab config before or after it mounted.\n */\n public ObserveTabQueryParams(tabId: string): Observable<Record<string, string>> {\n return this.workspaceManager.Configuration.pipe(\n map(config => {\n const tab = config?.tabs?.find(t => t.id === tabId);\n return (tab?.configuration?.['queryParams'] || {}) as Record<string, string>;\n }),\n distinctUntilChanged((a, b) => this.shallowParamsEqual(a, b))\n );\n }\n\n private shallowParamsEqual(a: Record<string, string>, b: Record<string, string>): boolean {\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n if (keysA.length !== keysB.length) {\n return false;\n }\n return keysA.every(key => a[key] === b[key]);\n }\n\n private tabMatchesQueryParamGuard(tab: { resourceRecordId?: string; configuration?: Record<string, unknown> }, guard: TabQueryParamUpdateGuard): boolean {\n const config = tab.configuration || {};\n const matches = (expected: string | null | undefined, actual: unknown, normalize = false): boolean => {\n if (expected === undefined || expected === null) {\n return true;\n }\n const expectedText = String(expected);\n const actualText = actual == null ? '' : String(actual);\n return normalize\n ? expectedText.trim().toLowerCase() === actualText.trim().toLowerCase()\n : expectedText === actualText;\n };\n\n const currentDriverClass = (config['resourceTypeDriverClass'] || config['driverClass']) as string | undefined;\n const currentRecordId = tab.resourceRecordId || (config['recordId'] as string | undefined) || '';\n const currentEntity = (config['Entity'] || config['entity']) as string | undefined;\n\n // Resource type and entity names can vary by casing/metadata spelling; class names,\n // record IDs, and nav labels are canonical tab identity fields and stay exact-match.\n return matches(guard.resourceType, config['resourceType'], true) &&\n matches(guard.driverClass, currentDriverClass) &&\n matches(guard.recordId, currentRecordId) &&\n matches(guard.navItemName, config['navItemName']) &&\n matches(guard.entity, currentEntity, true);\n }\n\n /**\n * Apply query params to a specific tab by ID.\n * Merges with any existing query params on the tab. Use null values to remove params.\n */\n private applyQueryParamsToTab(tabId: string, queryParams: Record<string, string | null>): void {\n const tab = this.workspaceManager.GetTab(tabId);\n if (!tab) {\n console.warn('NavigationService.applyQueryParamsToTab: Tab not found:', tabId);\n return;\n }\n\n // Get existing queryParams from tab configuration\n const existingQueryParams = (tab.configuration?.['queryParams'] || {}) as Record<string, string | null>;\n\n // Merge with new query params\n const mergedQueryParams: Record<string, string> = {};\n\n // Start with existing params (excluding nulls)\n for (const [key, value] of Object.entries(existingQueryParams)) {\n if (value !== null) {\n mergedQueryParams[key] = value;\n }\n }\n\n // Apply new params (null means remove)\n for (const [key, value] of Object.entries(queryParams)) {\n if (value === null) {\n delete mergedQueryParams[key];\n } else {\n mergedQueryParams[key] = value;\n }\n }\n\n // Update the tab configuration\n this.workspaceManager.UpdateTabConfiguration(tabId, {\n queryParams: Object.keys(mergedQueryParams).length > 0 ? mergedQueryParams : undefined\n });\n }\n}\n"]}
1
+ {"version":3,"file":"navigation.service.js","sourceRoot":"","sources":["../../src/lib/navigation.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable, OnDestroy } from '@angular/core';\nimport { WorkspaceStateManager, NavItem, DynamicNavItem, TabRequest, ApplicationManager } from '@memberjunction/ng-base-application';\nimport { NavigationOptions } from './navigation.interfaces';\nimport { IsRecordTabsStyle, RECORDS_RESOURCE_TYPE, IsRecordsTabConfiguration, RecordSourceContext, GetRecordSourceContext, TruncateRecordOriginChain } from './record-open-style';\nimport { CompositeKey } from '@memberjunction/core';\nimport { fromEvent, BehaviorSubject, Subject, Subscription, Observable } from 'rxjs';\nimport type { AppContextSnapshot } from '@memberjunction/ai-core-plus';\nimport { map, distinctUntilChanged } from 'rxjs/operators';\nimport { UUIDsEqual } from '@memberjunction/global';\nimport { BaseResourceComponent } from './base-resource-component';\n\n/**\n * Event emitted when query params change on a tab (e.g., from browser back/forward).\n * Includes the tab ID so that only the component in the affected tab reacts,\n * preventing cross-tab leakage in multi-tab scenarios.\n */\nexport interface QueryParamChangeEvent {\n TabId: string;\n Params: Record<string, string>;\n /**\n * Bypass the receiving component's duplicate-delivery guard. Set by\n * explicit restore navigations (e.g. the origin crumb): the component's\n * state may have DRIFTED from the last-delivered params, in which case a\n * restore to those same params would otherwise be dropped as a duplicate.\n */\n Force?: boolean;\n}\n\n/**\n * Event emitted when a resource component reports its agent context or tools.\n * The shell (which owns the ComponentCacheManager) subscribes to these events\n * and updates the cache + active AppContextSnapshot accordingly.\n */\nexport interface AgentContextUpdate {\n /** The component instance that reported the update */\n Caller: BaseResourceComponent;\n /** Dashboard-specific context for the agent (undefined = no change) */\n AgentContext?: Record<string, unknown>;\n /** Client tools available from this dashboard (undefined = no change) */\n AgentClientTools?: Array<{\n Name: string;\n Description: string;\n ParameterSchema: Record<string, unknown>;\n Handler: (params: Record<string, unknown>) => Promise<unknown>;\n }>;\n}\n\nexport interface TabQueryParamUpdateGuard {\n resourceType?: string | null;\n driverClass?: string | null;\n recordId?: string | null;\n navItemName?: string | null;\n entity?: string | null;\n}\n\n/**\n * System application ID for non-app-specific resources (fallback only)\n * Uses double underscore prefix to indicate system-level resource\n * @deprecated Prefer using NavigationService.getDefaultApplicationId() instead\n */\nexport const SYSTEM_APP_ID = '__explorer';\n\n/**\n * Neutral color for fallback when no app is available\n */\nconst NEUTRAL_APP_COLOR = '#9E9E9E'; // Material Design Gray 500\n\n/**\n * Centralized navigation service that handles all navigation operations\n * with automatic shift-key detection for power user workflows\n */\n@Injectable({\n providedIn: 'root'\n})\nexport class NavigationService implements OnDestroy {\n private shiftKeyPressed = false;\n private subscriptions: Subscription[] = [];\n\n private queryParamChanged$ = new Subject<QueryParamChangeEvent>();\n /** Observable that emits when query params change on a tab (back/forward navigation). */\n public QueryParamChanged$ = this.queryParamChanged$.asObservable();\n\n /** Cached Home app ID (null means not found, undefined means not checked) */\n private _homeAppId: string | null | undefined = undefined;\n /** Cached Home app color */\n private _homeAppColor: string | null = null;\n\n constructor(\n private workspaceManager: WorkspaceStateManager,\n private appManager: ApplicationManager\n ) {\n this.setupGlobalShiftKeyDetection();\n }\n\n /**\n * Get the neutral color used for system-wide resources (entities, views, dashboards)\n * Returns a light neutral gray\n * @deprecated Use getDefaultAppColor() for better UX with Home app integration\n */\n get ExplorerAppColor(): string {\n return NEUTRAL_APP_COLOR;\n }\n\n /**\n * Gets the default application ID for orphan resources.\n * Priority: Home app > Active app > SYSTEM_APP_ID\n *\n * This ensures orphan resources (entity records, dashboards, views opened directly)\n * are grouped under the Home app instead of being orphaned in the tab system.\n */\n private getDefaultApplicationId(): string {\n // Check cache first\n if (this._homeAppId !== undefined) {\n if (this._homeAppId !== null) {\n return this._homeAppId;\n }\n // Home app not found, check active app\n const activeApp = this.appManager.GetActiveApp();\n if (activeApp) {\n return activeApp.ID;\n }\n return SYSTEM_APP_ID;\n }\n\n // First time - look for Home app\n const homeApp = this.appManager.GetAppByName('Home');\n if (homeApp) {\n this._homeAppId = homeApp.ID;\n this._homeAppColor = homeApp.GetColor();\n return homeApp.ID;\n }\n\n // Cache that Home app doesn't exist\n this._homeAppId = null;\n\n // Fall back to currently active app\n const activeApp = this.appManager.GetActiveApp();\n if (activeApp) {\n return activeApp.ID;\n }\n\n // Last resort - system app ID\n return SYSTEM_APP_ID;\n }\n\n /**\n * Gets the default app color for orphan resources.\n * Returns Home app color if available, otherwise neutral gray.\n */\n private getDefaultAppColor(): string {\n // Ensure cache is populated\n this.getDefaultApplicationId();\n\n // If Home app exists, use its color\n if (this._homeAppColor) {\n return this._homeAppColor;\n }\n\n // Check active app\n const activeApp = this.appManager.GetActiveApp();\n if (activeApp) {\n return activeApp.GetColor();\n }\n\n // Fall back to neutral color\n return NEUTRAL_APP_COLOR;\n }\n\n /**\n * Clears the cached Home app info.\n * Call this if apps are reloaded or user logs out.\n */\n public clearHomeAppCache(): void {\n this._homeAppId = undefined;\n this._homeAppColor = null;\n }\n\n // ════════════════════════════════════════════\n // Agent Context & Client Tools\n // ════════════════════════════════════════════\n\n /**\n * Observable stream of agent context updates from resource components.\n * The shell subscribes to this to update the ComponentCacheManager and\n * push changes to the chat overlay's AppContextSnapshot.DashboardContext.\n */\n public readonly AgentContextUpdated$ = new Subject<AgentContextUpdate>();\n\n /** The client tools currently surfaced to the agent (the most recent SetAgentClientTools set). */\n private currentAgentTools: NonNullable<AgentContextUpdate['AgentClientTools']> = [];\n\n /**\n * Tools captured for each cached resource component at the moment it was DETACHED, keyed by that\n * component (the one the cache manager tracks). Replayed on reattach. Captured at detach time (vs.\n * keyed by the registering component) so it works even when a resource WRAPPER component is what's\n * cached/reattached while an INNER child component is what actually called SetAgentClientTools\n * (e.g. Data Explorer's resource wrapper hosting its dashboard) — keying by the registerer would\n * miss on reattach. This keeps the agent's live tool set a function of the CURRENTLY attached\n * surface, fixing the staleness where a previous app's tools lingered after navigation.\n */\n private readonly agentToolsByDetachedResource = new Map<BaseResourceComponent, NonNullable<AgentContextUpdate['AgentClientTools']>>();\n\n /**\n * Latest `AppContextSnapshot` published by the Explorer app shell.\n *\n * Why: any embedded `<mj-conversation-chat-area>` instance outside the\n * floating chat overlay (Form Builder cockpit, future domain dashboards\n * that pop their own AI pane) needs to feed the SAME context the overlay\n * does so the agent sees what app + view + dashboard state the user is\n * looking at. Without this, the agent only sees the embedder's narrow\n * `AdditionalContext` slice and treats the user as if they have no app\n * context at all — which is the bug we just fixed.\n *\n * `MJExplorerAppComponent` is the canonical publisher (it owns the\n * snapshot construction); consumers SUBSCRIBE and bind the value to\n * their chat-area's `[appContext]`. Non-Explorer apps (custom MJ apps\n * that don't include explorer-app at all) build their own snapshot via\n * `BuildAppContextSnapshot()` in `@memberjunction/ai-core-plus`.\n *\n * Initial value is `null`; the publisher emits the first real snapshot\n * after the active app + nav state resolve on bootstrap.\n */\n public readonly AppContextSnapshot$ = new BehaviorSubject<AppContextSnapshot | null>(null);\n\n /**\n * Push a fresh AppContextSnapshot. Called by MJExplorerAppComponent\n * after each (a) app/tab change, (b) `handleAgentContextUpdate`\n * merging in `AdditionalContext` from a dashboard. Idempotent — no\n * de-duplication; embedders should treat the stream as \"the latest\n * value is canonical.\"\n */\n public PublishAppContextSnapshot(snapshot: AppContextSnapshot | null): void {\n this.AppContextSnapshot$.next(snapshot);\n }\n\n /**\n * Report the current agent-visible state from a resource component.\n * Call this whenever the dashboard's internal state changes (tab switch,\n * filter change, pipeline status change, drill-down, etc.).\n *\n * @param caller - Pass `this` from the calling component. Used to match\n * against the ComponentCacheManager to identify which cached component\n * this update belongs to.\n * @param context - Key-value pairs representing dashboard state the agent\n * should know about. Each dashboard defines its own shape.\n */\n public SetAgentContext(caller: BaseResourceComponent, context: Record<string, unknown>): void {\n this.AgentContextUpdated$.next({ Caller: caller, AgentContext: context });\n }\n\n /**\n * Register the client tools available from a resource component.\n * Call this on component init and whenever the available tools change.\n * Tools are automatically unregistered when the component becomes inactive\n * (tab switch) and re-registered when it becomes active again.\n *\n * @param caller - Pass `this` from the calling component.\n * @param tools - Array of tool definitions with Name, Description,\n * ParameterSchema (JSON Schema), and Handler function.\n */\n public SetAgentClientTools(caller: BaseResourceComponent, tools: Array<{\n Name: string;\n Description: string;\n ParameterSchema: Record<string, unknown>;\n Handler: (params: Record<string, unknown>) => Promise<unknown>;\n }>): void {\n this.currentAgentTools = tools;\n this.AgentContextUpdated$.next({ Caller: caller, AgentClientTools: tools });\n }\n\n /**\n * Re-publish a cached resource component's tools when its tab is re-focused. Cached components keep\n * their Angular instance but do NOT re-run `ngAfterViewInit`, so they never re-register on reattach\n * — the shell calls this so the just-reactivated surface's tools become the agent's active set\n * again. Replays the set captured for this component at its last detach; no-op (lets a fresh\n * component register itself) when none was captured (e.g. a component's very first attach).\n */\n public NotifyResourceReattached(caller: BaseResourceComponent): void {\n const tools = this.agentToolsByDetachedResource.get(caller);\n if (tools === undefined) {\n return;\n }\n this.currentAgentTools = tools;\n this.AgentContextUpdated$.next({ Caller: caller, AgentClientTools: tools });\n }\n\n /**\n * Capture + clear the active client tools when a resource component's tab is detached (navigated\n * away from), so the previous surface's tools aren't offered to the agent on the next surface. We\n * snapshot whatever tools are CURRENTLY active and key them by the detaching component, so\n * {@link NotifyResourceReattached} can replay them — robust to a wrapper component being the one\n * cached/reattached while an inner child actually registered the tools (e.g. Data Explorer).\n */\n public NotifyResourceDetached(caller: BaseResourceComponent): void {\n this.agentToolsByDetachedResource.set(caller, this.currentAgentTools);\n this.currentAgentTools = [];\n this.AgentContextUpdated$.next({ Caller: caller, AgentClientTools: [] });\n }\n\n /**\n * Drop a destroyed component's captured tools (e.g. on LRU eviction), so the map doesn't retain\n * references to dead component instances.\n */\n public ForgetResource(caller: BaseResourceComponent): void {\n this.agentToolsByDetachedResource.delete(caller);\n }\n\n ngOnDestroy(): void {\n this.subscriptions.forEach(sub => sub.unsubscribe());\n }\n\n /**\n * Set up global keyboard event listeners to track shift key state\n */\n private setupGlobalShiftKeyDetection(): void {\n // Track shift key via mousedown events (capture phase) instead of keydown/keyup.\n // This is more reliable because:\n // 1. MouseEvent.shiftKey always reflects the actual modifier state at click time\n // 2. No risk of \"stuck\" state from missed keyup events (focus loss, tab switch, etc.)\n // 3. Navigation is always triggered by a click, so the shift state is read\n // at exactly the right moment\n this.subscriptions.push(\n fromEvent<MouseEvent>(document, 'mousedown', { capture: true }).subscribe(event => {\n this.shiftKeyPressed = event.shiftKey;\n })\n );\n }\n\n /**\n * Get current shift key state\n */\n private isShiftPressed(): boolean {\n return this.shiftKeyPressed;\n }\n\n /**\n * Determine if a new tab should be forced based on options and shift key state\n */\n private shouldForceNewTab(options?: NavigationOptions): boolean {\n // If forceNewTab is explicitly set, use that\n if (options?.forceNewTab !== undefined) {\n return options.forceNewTab;\n }\n\n // Otherwise, use global shift key detection\n return this.isShiftPressed();\n }\n\n /**\n * Returns whether the caller should use OpenTabForced (force-new path) or\n * OpenTab (replace-temp path).\n *\n * Rule: only honor an explicit force-new request — from the user via\n * shift+click, or from the caller via `options.forceNewTab`. We deliberately\n * do NOT apply heuristics that auto-switch the workspace out of\n * single-resource mode on cross-resource navigation. A previous version of\n * this method tried to do that (\"force new if single-resource + different\n * resource\") and it caused a regression: every plain hyperlink click on a\n * record opened a new tab and dropped the user into multi-tab mode, even\n * though they didn't ask for it. That violated the principle that mode\n * transitions are user-driven (shift) or explicitly requested (options).\n *\n * If a particular caller really needs the parent context preserved when\n * creating/navigating to a child resource (e.g. \"+New\" on a related-entity\n * grid inside an open record), the caller should pass `forceNewTab: true`\n * in `NavigationOptions`. That keeps intent explicit at the call site\n * instead of buried in a global heuristic.\n */\n private handleSingleResourceModeTransition(forceNew: boolean, _newRequest: TabRequest): boolean {\n return forceNew;\n }\n\n /**\n * Check if a tab request matches an existing tab's resource\n */\n private isSameResource(tab: any, request: TabRequest): boolean {\n // Different apps = different resources\n if (tab.applicationId !== request.ApplicationId) {\n return false;\n }\n\n // For resource-based tabs, compare resourceType and recordId\n if (request.Configuration?.resourceType) {\n const requestRecordId = request.ResourceRecordId || '';\n const tabRecordId = tab.resourceRecordId || '';\n return tab.configuration?.resourceType === request.Configuration.resourceType &&\n tabRecordId === requestRecordId;\n }\n\n // For app nav items, compare appName and navItemName\n if (request.Configuration?.appName && request.Configuration?.navItemName) {\n return tab.configuration?.appName === request.Configuration.appName &&\n tab.configuration?.navItemName === request.Configuration.navItemName;\n }\n\n // Fallback to basic comparison\n return false;\n }\n\n /**\n * Open a navigation item within an app\n */\n public OpenNavItem(appId: string, navItem: NavItem, appColor: string, options?: NavigationOptions): string {\n let forceNew = this.shouldForceNewTab(options);\n\n // Get the app to find its name\n const app = this.appManager.GetAppById(appId);\n const appName = app?.Name || '';\n\n // Dynamic nav items (e.g. orphan entity records) carry their original tab Configuration\n // and should NOT get navItemName stamped on them — that would cause buildResourceUrl\n // to produce a nav-item-style URL like /app/home/<label> instead of the correct\n // resource-type URL like /app/home/record/Entity/ID|...\n const isDynamic = (navItem as DynamicNavItem).isDynamic === true;\n\n const request: TabRequest = {\n ApplicationId: appId,\n Title: navItem.Label,\n ResourceRecordId: navItem.RecordID || '', // Also store at top level for consistent tab matching\n Configuration: {\n route: navItem.Route,\n resourceType: navItem.ResourceType,\n driverClass: navItem.DriverClass, // Pass through DriverClass for Custom resource type\n recordId: navItem.RecordID,\n appName: appName, // Store app name for URL building\n appId: appId,\n ...(isDynamic ? {} : { navItemName: navItem.Label }), // Only set for static nav items\n ...(navItem.Configuration || {})\n },\n IsPinned: options?.pinTab || false\n };\n\n // Handle transition from single-resource mode\n forceNew = this.handleSingleResourceModeTransition(forceNew, request);\n\n let tabId: string;\n if (forceNew) {\n // Always create a new tab\n tabId = this.workspaceManager.OpenTabForced(request, appColor);\n } else {\n // Use existing OpenTab logic (may replace temporary tab)\n tabId = this.workspaceManager.OpenTab(request, appColor);\n }\n\n // Apply query params to the newly opened/activated tab if provided\n if (options?.queryParams) {\n this.applyQueryParamsToTab(tabId, options.queryParams);\n }\n\n return tabId;\n }\n\n /**\n * Open an entity record view. Behavior forks on the deployment's\n * record-open style (see record-open-style.ts):\n * - 'records' (default): the record ALWAYS opens as its own tab, assigned\n * to the app the user is standing in (no Home reassignment, no nav flip).\n * Re-opening a record that's already open focuses its existing tab,\n * browser-style. The shell's Records pill + strip are the visible UI.\n * - 'classic': the original Home-first assignment and tab semantics.\n * Both styles capture the source-app trail into the tab configuration for\n * context UI and diagnostics.\n */\n public OpenEntityRecord(\n entityName: string,\n recordPkey: CompositeKey,\n options?: NavigationOptions\n ): string {\n const tabsMode = IsRecordTabsStyle();\n\n const activeApp = this.appManager.GetActiveApp();\n const appId = tabsMode && activeApp ? activeApp.ID : this.getDefaultApplicationId();\n const appColor = tabsMode && activeApp ? activeApp.GetColor() : this.getDefaultAppColor();\n\n const recordId = recordPkey.ToURLSegment();\n\n // Browser-style dedup: if this exact record is already open in a tab,\n // focus that tab instead of opening a second copy\n if (tabsMode) {\n const existing = this.findOpenRecordTab(entityName, recordId);\n if (existing) {\n // RE-CAPTURE the origin: re-opening means \"I came from somewhere\n // NEW\" — the crumb must point at where the user is standing for\n // THIS open, not wherever they were when the tab was first created\n // (possibly days ago, possibly under an older origin schema).\n this.refreshSourceContext(existing.id, options);\n // The activation assert applies to RE-opens too — the same-click\n // stale-stomp that motivated it for fresh opens is equally possible\n // here, and without it the symptom is maddening: opening a record\n // works the FIRST time and silently fails every time after.\n this.workspaceManager.SetActiveTab(existing.id);\n this.assertRecordActivation(existing.id);\n return existing.id;\n }\n }\n\n // Records style no longer forces a new tab unconditionally: a plain click\n // consumes the records region's temporary tab (preview-tab behavior), and\n // only shift — or an explicit forceNewTab, which is what finally makes\n // single-record's \"Open in New Tab\" do something — adds a second tab.\n // The region scoping below is what keeps a nav click from ever consuming\n // a record, which is the protection the old unconditional force provided.\n let forceNew = this.shouldForceNewTab(options);\n\n const request: TabRequest = {\n ApplicationId: appId,\n Title: `${entityName} - ${recordId}`,\n Configuration: {\n resourceType: RECORDS_RESOURCE_TYPE,\n Entity: entityName, // Must use 'Entity' (capital E) - expected by record-resource.component\n recordId: recordId, // Also needed in Configuration for tab-container.component to populate ResourceRecordID\n ...this.resolveSourceContext(options)\n },\n ResourceRecordId: recordId,\n IsPinned: options?.pinTab || false,\n // Records style: this open belongs to the RECORDS temp-tab pool, so both\n // consumption and the pin cascade stay inside the region and the nav\n // tab's temp status is untouched (a pinned nav tab would force the main\n // tab bar visible on every nav page). Classic style keeps the single\n // 'main' pool.\n TempScope: tabsMode ? 'records' : 'main'\n };\n\n // Handle transition from single-resource mode\n forceNew = this.handleSingleResourceModeTransition(forceNew, request);\n\n let tabId: string;\n if (forceNew) {\n tabId = this.workspaceManager.OpenTabForced(request, appColor);\n } else {\n tabId = this.workspaceManager.OpenTab(request, appColor);\n }\n\n if (tabsMode) {\n this.assertRecordActivation(tabId);\n }\n\n return tabId;\n }\n\n /**\n * Opening a record means the record WINS the activation race — assert it\n * after the synchronous open flow settles. Surfaces that both emit a\n * record-open AND run their own internal navigation in the same click\n * (e.g. a grid row's select handler updating query params from a config\n * snapshot captured before the open) can otherwise stomp the activation\n * with stale state, leaving the tab open but never shown. Skipped when the\n * tab was closed in the meantime.\n */\n private assertRecordActivation(tabId: string): void {\n setTimeout(() => {\n const config = this.workspaceManager.GetConfiguration();\n if (config?.tabs.some(t => t.id === tabId) && config.activeTabId !== tabId) {\n this.workspaceManager.SetActiveTab(tabId);\n }\n }, 0);\n }\n\n /** Find an already-open tab showing this exact record (records-style dedup) */\n private findOpenRecordTab(entityName: string, recordId: string): { id: string } | null {\n const config = this.workspaceManager.GetConfiguration();\n const match = config?.tabs.find(t =>\n t.resourceRecordId === recordId &&\n t.configuration?.['resourceType'] === RECORDS_RESOURCE_TYPE &&\n t.configuration?.['Entity'] === entityName\n );\n return match ? { id: match.id } : null;\n }\n\n /**\n * Overwrite a record tab's origin with a freshly-resolved one (the dedup\n * re-open path). All source keys are explicitly cleared first — a shallow\n * config merge would otherwise let fields from the OLD origin shape leak\n * through (e.g. a parent-record origin's sourceRecordEntity surviving a\n * re-open from a nav page, or pre-schema captures keeping stale labels).\n */\n private refreshSourceContext(tabId: string, options?: NavigationOptions): void {\n this.workspaceManager.UpdateTabConfiguration(tabId, {\n sourceAppId: undefined,\n sourceAppName: undefined,\n sourceNavLabel: undefined,\n sourceNavItemName: undefined,\n sourceTabId: undefined,\n sourceLabel: undefined,\n sourceQueryParams: undefined,\n sourceRecordEntity: undefined,\n sourceRecordId: undefined,\n // Clear the ancestor chain too — this is an explicit re-capture, and a\n // surviving chain would hand the crumb an origin from a previous open.\n sourceParentOrigin: undefined,\n ...this.resolveSourceContext(options)\n });\n }\n\n /**\n * Resolve the source context to stamp into a record tab's configuration.\n * Callers that are workspace tabs omit options.recordSource and get the\n * active app/tab snapshot; overlay surfaces pass their own context;\n * 'none' means no origin (no crumb).\n */\n private resolveSourceContext(options?: NavigationOptions): Record<string, unknown> {\n if (options?.recordSource === 'none') {\n return {};\n }\n if (options?.recordSource) {\n const src = options.recordSource;\n const context: Record<string, unknown> = {};\n if (src.sourceAppId) context['sourceAppId'] = src.sourceAppId;\n if (src.sourceAppName) context['sourceAppName'] = src.sourceAppName;\n if (src.sourceNavLabel) context['sourceNavLabel'] = src.sourceNavLabel;\n if (src.sourceTabId) context['sourceTabId'] = src.sourceTabId;\n if (src.sourceLabel) context['sourceLabel'] = src.sourceLabel;\n if (src.sourceQueryParams && Object.keys(src.sourceQueryParams).length > 0) {\n context['sourceQueryParams'] = src.sourceQueryParams;\n }\n if (src.sourceRecordEntity) context['sourceRecordEntity'] = src.sourceRecordEntity;\n if (src.sourceRecordId) context['sourceRecordId'] = src.sourceRecordId;\n // An explicit origin can carry an ancestor chain (ReturnToRecordSource\n // replays one). Dropping it here would re-lose the entry point on the\n // very navigation that exists to restore it.\n const chained = TruncateRecordOriginChain(src.sourceParentOrigin);\n if (chained) {\n context['sourceParentOrigin'] = chained;\n }\n return context;\n }\n return this.captureSourceContext();\n }\n\n /**\n * Snapshot of where the user was when the record was\n * opened — the source app and (when the active tab belongs to that app and\n * isn't itself a record) the nav page they were on. Rendered by the\n * breadcrumb strip as \"App › Page › Record\".\n */\n private captureSourceContext(): Record<string, unknown> {\n const activeApp = this.appManager.GetActiveApp();\n if (!activeApp) {\n return {};\n }\n const context: Record<string, unknown> = {\n sourceAppId: activeApp.ID,\n sourceAppName: activeApp.Name\n };\n const config = this.workspaceManager.GetConfiguration();\n const activeTab = config?.tabs.find(t => t.id === config.activeTabId);\n if (!activeTab || !UUIDsEqual(activeTab.applicationId, activeApp.ID) || !activeTab.title) {\n return context;\n }\n if (IsRecordsTabConfiguration(activeTab.configuration)) {\n // Opened from INSIDE another record → the crumb points at the PARENT\n // record (Matt's record→record design call). Identity, not just the\n // tab id, so the return survives tab closure (re-open via dedup).\n const parentEntity = activeTab.configuration?.['Entity'];\n const parentRecordId = activeTab.resourceRecordId || activeTab.configuration?.['recordId'];\n if (typeof parentEntity === 'string' && typeof parentRecordId === 'string' && parentRecordId) {\n context['sourceTabId'] = activeTab.id;\n context['sourceLabel'] = activeTab.title;\n context['sourceRecordEntity'] = parentEntity;\n context['sourceRecordId'] = parentRecordId;\n // Carry the parent's OWN origin forward. Preview-tab replacement\n // consumes the parent's tab, so returning to it later re-opens rather\n // than reactivates — and a re-open would recapture the CHILD as the\n // origin. Keeping the chain is what lets the return restore the real\n // entry point instead of pointing the two records at each other.\n const parentOrigin = TruncateRecordOriginChain(GetRecordSourceContext(activeTab.configuration));\n if (parentOrigin) {\n context['sourceParentOrigin'] = parentOrigin;\n }\n }\n return context;\n }\n context['sourceNavLabel'] = activeTab.title;\n // Canonical nav identity alongside the display title: titles mutate\n // (Data Explorer renames its tab to the drilled entity), so the return\n // path verifies/resolves by navItemName and shows the label.\n const navItemName = activeTab.configuration?.['navItemName'];\n if (typeof navItemName === 'string' && navItemName.length > 0) {\n context['sourceNavItemName'] = navItemName;\n }\n // The exact tab, not just its label — the origin crumb's return path\n // prefers reactivating this tab (state intact) over re-resolving the\n // nav item by label.\n context['sourceTabId'] = activeTab.id;\n // Snapshot the tab's query params too: dashboards keep their DRILL-IN\n // state there (e.g. Data Explorer's entity section), and it can drift\n // or be stomped between open and return. The crumb restores the page\n // AS IT WAS, not just the tab.\n const queryParams = activeTab.configuration?.['queryParams'];\n if (queryParams && typeof queryParams === 'object' && Object.keys(queryParams).length > 0) {\n context['sourceQueryParams'] = { ...(queryParams as Record<string, string>) };\n }\n return context;\n }\n\n /**\n * Switch to an app's LANDING page — its default nav item in its BASE\n * state (drifted section params cleared, reset force-delivered so the\n * dashboard actually returns to its landing even if its state diverged).\n * The origin crumb's APP segment uses this: classic breadcrumb semantics,\n * each level goes to that level's home.\n */\n async SwitchToAppHome(appId: string): Promise<void> {\n const app = this.appManager.GetAllApps().find(a => UUIDsEqual(a.ID, appId));\n if (!app) {\n return;\n }\n const navItems = await app.GetNavItems();\n const home = navItems.find(i => i.isDefault) ?? navItems[0];\n await this.SwitchToApp(appId, home?.Label);\n const tabId = this.workspaceManager.GetActiveTabId();\n if (!tabId) {\n return;\n }\n const tab = this.workspaceManager.GetTab(tabId);\n const current = (tab?.configuration?.['queryParams'] || {}) as Record<string, string>;\n const clear: Record<string, string | null> = {};\n for (const key of Object.keys(current)) {\n clear[key] = null;\n }\n if (Object.keys(clear).length > 0) {\n this.applyQueryParamsToTab(tabId, clear);\n }\n this.NotifyQueryParamsChanged(tabId, {}, true);\n }\n\n /**\n * Take the user back to where they were when a record was opened (the\n * origin crumb's click path). Fidelity ladder:\n * 1. The exact source tab, if it's still open — reactivate it with all its\n * state (query params, scroll, cached component) intact.\n * 2. Otherwise re-resolve by app + nav label via SwitchToApp, which reuses\n * a matching open tab or opens the nav item fresh.\n * No-ops when the context has no sourceAppId (capture was best-effort).\n */\n public async ReturnToRecordSource(origin: RecordSourceContext): Promise<void> {\n // Parent-RECORD origin (opened from inside another record): activate the\n // parent's tab while it still shows that record, else RE-OPEN the parent\n // — OpenEntityRecord's dedup makes the re-open land on (or recreate)\n // exactly that record. No app flip either way: record tabs live in the\n // records region regardless of app.\n if (origin.sourceRecordEntity && origin.sourceRecordId) {\n const config = this.workspaceManager.GetConfiguration();\n const sourceTab = origin.sourceTabId ? config?.tabs.find(t => t.id === origin.sourceTabId) : undefined;\n const stillParent = !!sourceTab &&\n IsRecordsTabConfiguration(sourceTab.configuration) &&\n sourceTab.configuration?.['Entity'] === origin.sourceRecordEntity &&\n sourceTab.resourceRecordId === origin.sourceRecordId;\n if (sourceTab && stillParent) {\n this.workspaceManager.SetActiveTab(sourceTab.id);\n return;\n }\n const parentKey = new CompositeKey();\n parentKey.SimpleLoadFromURLSegment(origin.sourceRecordId);\n // RESTORE the parent's origin; do not let the re-open capture a fresh\n // one. Standing on the child at this moment, a capture would make the\n // child the parent's origin — the two records would point at each other\n // and the real entry point would be unreachable. 'none' when we have no\n // chain: a missing crumb beats a circular one.\n this.OpenEntityRecord(origin.sourceRecordEntity, parentKey, {\n recordSource: origin.sourceParentOrigin ?? 'none'\n });\n return;\n }\n if (!origin.sourceAppId) {\n return;\n }\n if (origin.sourceTabId) {\n const config = this.workspaceManager.GetConfiguration();\n const sourceTab = config?.tabs.find(t => t.id === origin.sourceTabId);\n // Tab ids are NOT stable identities for nav pages: the temp-tab\n // replacement model (WorkspaceStateManager.OpenTab) reuses the same\n // tab id when the user navigates elsewhere — the captured id can now\n // hold a DIFFERENT page in a DIFFERENT app. Trust it only while the\n // tab still shows the captured page; otherwise fall through to\n // re-resolving by app + nav item below. Prefer navItemName for the\n // page check — TITLES MUTATE (Data Explorer renames its tab to the\n // drilled entity), and a mutated title must not disqualify the tab.\n const tabNavItemName = sourceTab?.configuration?.['navItemName'];\n const samePage = origin.sourceNavItemName && typeof tabNavItemName === 'string'\n ? tabNavItemName === origin.sourceNavItemName\n : (!origin.sourceNavLabel || sourceTab?.title === origin.sourceNavLabel);\n const stillSourcePage = !!sourceTab &&\n (!origin.sourceAppId || UUIDsEqual(sourceTab.applicationId, origin.sourceAppId)) &&\n samePage;\n if (sourceTab && stillSourcePage) {\n // Restore the tab's query params to the capture-time snapshot BEFORE\n // activating, so a cached resource component reattaches against the\n // right config (see SwitchToApp's queryParams doc). REPLACE\n // semantics: null out params the tab has gained since capture, then\n // lay the snapshot over — \"back\" means the page as it was, and\n // drill-in state (e.g. Data Explorer's entity section) lives here.\n const currentParams = (sourceTab.configuration?.['queryParams'] || {}) as Record<string, string>;\n const restore: Record<string, string | null> = {};\n for (const key of Object.keys(currentParams)) {\n restore[key] = null;\n }\n Object.assign(restore, origin.sourceQueryParams ?? {});\n if (Object.keys(restore).length > 0) {\n this.applyQueryParamsToTab(sourceTab.id, restore);\n }\n // FORCE delivery: the component's state may have drifted while its\n // last-delivered params already equal the snapshot — the reactive\n // path would drop the restore as a duplicate (crumb lands on the\n // dashboard's home instead of the captured section).\n if (origin.sourceQueryParams && Object.keys(origin.sourceQueryParams).length > 0) {\n this.NotifyQueryParamsChanged(sourceTab.id, origin.sourceQueryParams, true);\n }\n // Set the app context first so the shell's nav renders the right\n // app the moment the tab activates (activation alone would also\n // flip it, but explicit sequencing avoids a visible two-step).\n await this.appManager.SetActiveApp(sourceTab.applicationId);\n this.workspaceManager.SetActiveTab(sourceTab.id);\n return;\n }\n }\n // Re-resolve by the CANONICAL nav-item name when we have it — the label\n // is a display title that may have mutated into something SwitchToApp's\n // nav-item lookup can't match (e.g. a drilled entity name).\n await this.SwitchToApp(origin.sourceAppId, origin.sourceNavItemName ?? origin.sourceNavLabel, origin.sourceQueryParams);\n // Same drift-proofing as the exact-tab path: force the delivery so the\n // restore can't be deduped away.\n if (origin.sourceQueryParams && Object.keys(origin.sourceQueryParams).length > 0) {\n const targetTabId = this.workspaceManager.GetActiveTabId();\n if (targetTabId) {\n this.NotifyQueryParamsChanged(targetTabId, origin.sourceQueryParams, true);\n }\n }\n }\n\n /**\n * Open a view\n * Uses Home app if available, otherwise falls back to active app or system app\n */\n public OpenView(\n viewId: string,\n viewName: string,\n options?: NavigationOptions\n ): string {\n const appId = this.getDefaultApplicationId();\n const appColor = this.getDefaultAppColor();\n let forceNew = this.shouldForceNewTab(options);\n\n const request: TabRequest = {\n ApplicationId: appId,\n Title: viewName,\n Configuration: {\n resourceType: 'MJ: User Views',\n viewId,\n recordId: viewId // Also needed in Configuration for tab-container.component to populate ResourceRecordID\n },\n ResourceRecordId: viewId,\n IsPinned: options?.pinTab || false\n };\n\n // Handle transition from single-resource mode\n forceNew = this.handleSingleResourceModeTransition(forceNew, request);\n\n if (forceNew) {\n return this.workspaceManager.OpenTabForced(request, appColor);\n } else {\n return this.workspaceManager.OpenTab(request, appColor);\n }\n }\n\n /**\n * Open a dashboard\n * Uses Home app if available, otherwise falls back to active app or system app\n */\n public OpenDashboard(\n dashboardId: string,\n dashboardName: string,\n options?: NavigationOptions\n ): string {\n const appId = this.getDefaultApplicationId();\n const appColor = this.getDefaultAppColor();\n let forceNew = this.shouldForceNewTab(options);\n\n const request: TabRequest = {\n ApplicationId: appId,\n Title: dashboardName,\n Configuration: {\n resourceType: 'Dashboards',\n dashboardId,\n recordId: dashboardId // Also needed in Configuration for tab-container.component to populate ResourceRecordID\n },\n ResourceRecordId: dashboardId,\n IsPinned: options?.pinTab || false\n };\n\n // Handle transition from single-resource mode\n forceNew = this.handleSingleResourceModeTransition(forceNew, request);\n\n if (forceNew) {\n return this.workspaceManager.OpenTabForced(request, appColor);\n } else {\n return this.workspaceManager.OpenTab(request, appColor);\n }\n }\n\n /**\n * Open an artifact\n * Artifacts are versioned content containers (reports, dashboards, UI components, etc.)\n * Uses Home app if available, otherwise falls back to active app or system app\n */\n public OpenArtifact(\n artifactId: string,\n artifactName?: string,\n options?: NavigationOptions\n ): string {\n const appId = this.getDefaultApplicationId();\n const appColor = this.getDefaultAppColor();\n let forceNew = this.shouldForceNewTab(options);\n\n const request: TabRequest = {\n ApplicationId: appId,\n Title: artifactName || `Artifact - ${artifactId}`,\n Configuration: {\n resourceType: 'Artifacts',\n artifactId,\n recordId: artifactId // Also needed in Configuration for tab-container.component to populate ResourceRecordID\n },\n ResourceRecordId: artifactId,\n IsPinned: options?.pinTab || false\n };\n\n // Handle transition from single-resource mode\n forceNew = this.handleSingleResourceModeTransition(forceNew, request);\n\n if (forceNew) {\n return this.workspaceManager.OpenTabForced(request, appColor);\n } else {\n return this.workspaceManager.OpenTab(request, appColor);\n }\n }\n\n /**\n * Open a dynamic view\n * Dynamic views are entity-based views with custom filters, not saved views\n * Uses Home app if available, otherwise falls back to active app or system app\n */\n public OpenDynamicView(\n entityName: string,\n extraFilter?: string,\n options?: NavigationOptions\n ): string {\n const appId = this.getDefaultApplicationId();\n const appColor = this.getDefaultAppColor();\n let forceNew = this.shouldForceNewTab(options);\n\n const filterSuffix = extraFilter ? ' (Filtered)' : '';\n const request: TabRequest = {\n ApplicationId: appId,\n Title: `${entityName}${filterSuffix}`,\n Configuration: {\n resourceType: 'MJ: User Views',\n Entity: entityName,\n ExtraFilter: extraFilter,\n isDynamic: true,\n recordId: 'dynamic' // Special marker for dynamic views\n },\n ResourceRecordId: 'dynamic',\n IsPinned: options?.pinTab || false\n };\n\n // Handle transition from single-resource mode\n forceNew = this.handleSingleResourceModeTransition(forceNew, request);\n\n if (forceNew) {\n return this.workspaceManager.OpenTabForced(request, appColor);\n } else {\n return this.workspaceManager.OpenTab(request, appColor);\n }\n }\n\n /**\n * Open a query\n * Uses Home app if available, otherwise falls back to active app or system app\n */\n public OpenQuery(\n queryId: string,\n queryName: string,\n options?: NavigationOptions\n ): string {\n const appId = this.getDefaultApplicationId();\n const appColor = this.getDefaultAppColor();\n let forceNew = this.shouldForceNewTab(options);\n\n const request: TabRequest = {\n ApplicationId: appId,\n Title: queryName,\n Configuration: {\n resourceType: 'Queries',\n queryId,\n recordId: queryId // Also needed in Configuration for tab-container.component to populate ResourceRecordID\n },\n ResourceRecordId: queryId,\n IsPinned: options?.pinTab || false\n };\n\n // Handle transition from single-resource mode\n forceNew = this.handleSingleResourceModeTransition(forceNew, request);\n\n if (forceNew) {\n return this.workspaceManager.OpenTabForced(request, appColor);\n } else {\n return this.workspaceManager.OpenTab(request, appColor);\n }\n }\n\n /**\n * Open a new entity record creation form\n * Uses Home app if available, otherwise falls back to active app or system app\n * @param entityName The name of the entity to create a new record for\n * @param options Navigation options including optional newRecordValues for pre-populating fields\n */\n public OpenNewEntityRecord(\n entityName: string,\n options?: NavigationOptions\n ): string {\n // Records style: new-record tabs are RECORD tabs and get the same\n // treatment as OpenEntityRecord — assigned to the ACTIVE app, always a\n // new tab, nav pins preserved, origin captured. Without the forceNew,\n // the non-forced OpenTab consumed the user's nav page as the\n // replaceable temp tab and CONVERTED it into a records-region tab —\n // silently destroying the page they were on.\n const tabsMode = IsRecordTabsStyle();\n const activeApp = this.appManager.GetActiveApp();\n const appId = tabsMode && activeApp ? activeApp.ID : this.getDefaultApplicationId();\n const appColor = tabsMode && activeApp ? activeApp.GetColor() : this.getDefaultAppColor();\n\n // A NEW record keeps forcing its own tab under the records style: it is\n // unsaved work from the moment it opens, so it must never land on top of\n // a record the user is reading.\n let forceNew = tabsMode || this.shouldForceNewTab(options);\n\n const request: TabRequest = {\n ApplicationId: appId,\n Title: `New ${entityName}`,\n Configuration: {\n resourceType: RECORDS_RESOURCE_TYPE,\n Entity: entityName, // Must use 'Entity' (capital E) - expected by record-resource.component\n recordId: '', // Empty recordId indicates new record\n isNew: true, // Flag to indicate this is a new record\n NewRecordValues: options?.newRecordValues, // Pass through initial values if provided\n ...this.resolveSourceContext(options)\n },\n ResourceRecordId: '', // Empty for new records\n // Pinned under the records style so the region's preview replacement can\n // never consume it: an unsaved new record is exactly the tab that must\n // not vanish when the user clicks the next row in a grid. (VS Code holds\n // the same line — an untitled buffer is never a preview tab.)\n IsPinned: options?.pinTab || tabsMode,\n TempScope: tabsMode ? 'records' : 'main'\n };\n\n // Handle transition from single-resource mode\n forceNew = this.handleSingleResourceModeTransition(forceNew, request);\n\n let tabId: string;\n if (forceNew) {\n tabId = this.workspaceManager.OpenTabForced(request, appColor);\n } else {\n tabId = this.workspaceManager.OpenTab(request, appColor);\n }\n\n if (tabsMode) {\n this.assertRecordActivation(tabId);\n }\n\n return tabId;\n }\n\n /**\n * Open a universal search results tab for the given query.\n * This is the primary way to open search results from anywhere in the application.\n *\n * @param query The search query text\n * @param searchOptions Optional search-specific options (e.g., minRelevance, scopeIDs)\n * @param options Navigation options\n */\n public OpenSearch(\n query: string,\n searchOptions?: { minRelevance?: number; scopeIDs?: string[] },\n options?: NavigationOptions\n ): string {\n const appId = this.getDefaultApplicationId();\n const appColor = this.getDefaultAppColor();\n let forceNew = this.shouldForceNewTab(options);\n\n const config: Record<string, unknown> = {\n resourceType: 'Search Results',\n Query: query,\n SearchInput: query,\n recordId: `search-${query}`\n };\n if (searchOptions?.minRelevance != null) {\n config['MinRelevance'] = searchOptions.minRelevance;\n }\n if (searchOptions?.scopeIDs && searchOptions.scopeIDs.length > 0) {\n config['ScopeIDs'] = searchOptions.scopeIDs;\n }\n\n const request: TabRequest = {\n ApplicationId: appId,\n Title: `Search: ${query}`,\n Configuration: config,\n ResourceRecordId: `search-${query}`,\n IsPinned: false\n };\n\n // Handle transition from single-resource mode\n forceNew = this.handleSingleResourceModeTransition(forceNew, request);\n\n if (forceNew) {\n return this.workspaceManager.OpenTabForced(request, appColor);\n } else {\n return this.workspaceManager.OpenTab(request, appColor);\n }\n }\n\n /**\n * Navigate to a nav item by name within the current or specified application.\n * Allows passing additional configuration parameters to merge with the nav item's config.\n * This is useful for cross-resource navigation where a component needs to navigate\n * to another nav item with specific parameters (e.g., navigate to Conversations with a specific conversationId).\n *\n * @param navItemName The label/name of the nav item to navigate to\n * @param configuration Additional configuration to merge (e.g., conversationId, artifactId)\n * @param appId Optional app ID (defaults to current active app)\n * @param options Navigation options\n * @returns The tab ID if successful, null if nav item not found\n */\n public async OpenNavItemByName(\n navItemName: string,\n configuration?: Record<string, unknown>,\n appId?: string,\n options?: NavigationOptions\n ): Promise<string | null> {\n // Get app (use provided or current active)\n const targetAppId = appId || this.appManager.GetActiveApp()?.ID;\n if (!targetAppId) {\n return null;\n }\n\n const app = this.appManager.GetAppById(targetAppId);\n if (!app) {\n return null;\n }\n\n // Find the nav item by name\n const navItems = await app.GetNavItems();\n const navItem = navItems.find(item => item.Label === navItemName);\n if (!navItem) {\n return null;\n }\n\n // Create a merged nav item with additional configuration\n const mergedNavItem: NavItem = {\n ...navItem,\n Configuration: {\n ...(navItem.Configuration || {}),\n ...(configuration || {})\n }\n };\n\n // Use existing OpenNavItem\n return this.OpenNavItem(targetAppId, mergedNavItem, app.GetColor(), options);\n }\n\n /**\n * Switch to an application by ID.\n * This sets the app as active and either opens a specific nav item or creates a default tab.\n * If the requested nav item already has an open tab, switches to that tab instead of creating a new one.\n * @param appId The application ID to switch to\n * @param navItemName Optional name of a nav item to open within the app. If provided, opens that nav item.\n * @param queryParams Optional query params to apply to the target tab. Applied SYNCHRONOUSLY once the\n * target tab is active — critical when navigating (e.g. from a Home pin) to an\n * app whose resource component is cached: the params must be in the tab config\n * BEFORE the tab-container reattaches the cached component, otherwise the cache\n * restores its own (stale) saved params and the navigation intent is lost.\n */\n async SwitchToApp(appId: string, navItemName?: string, queryParams?: Record<string, string | null>): Promise<void> {\n await this.appManager.SetActiveApp(appId);\n\n const app = this.appManager.GetAllApps().find(a => UUIDsEqual(a.ID, appId));\n if (!app) {\n return;\n }\n\n // Records style: record tabs are ASSIGNED to apps but live in their own\n // region — switching to an app must surface its NAV pages, so records\n // don't count here. Without this filter, the active record tab (which\n // belongs to the target app) satisfied the \"already on this app's tab\"\n // check and the whole switch silently no-oped — the dead origin-crumb\n // click when the crumb only knew the app. Records DOCKED to the\n // workspace are excluded too (identity check, not region membership):\n // switch-to-app must land on a page, never a record. Classic style\n // keeps records as ordinary tabs.\n const appTabs = IsRecordTabsStyle()\n ? this.workspaceManager.GetAppTabs(appId).filter(t => !IsRecordsTabConfiguration(t.configuration))\n : this.workspaceManager.GetAppTabs(appId);\n\n // If a specific nav item is requested\n if (navItemName) {\n const navItems = await app.GetNavItems();\n const navItem = navItems.find(item => item.Label === navItemName);\n if (navItem) {\n // Check if there's already a tab for this nav item. Identity check\n // uses configuration.navItemName when the tab carries it — TITLES\n // LIE: same-driver nav switches reuse the live tab and can leave a\n // stale title behind (e.g. Data Explorer's tab titled \"Data\" while\n // its content and navItemName say \"Dashboards\"). Matching that\n // chimera by title activated the WRONG page instead of reopening\n // the requested nav item.\n const existingTab = appTabs.find(tab => {\n const tabNav = tab.configuration?.['navItemName'];\n if (typeof tabNav === 'string' && tabNav.length > 0) {\n return tabNav === navItem.Label;\n }\n return tab.title === navItem.Label ||\n (tab.configuration?.['route'] === navItem.Route && !!navItem.Route);\n });\n\n if (existingTab) {\n // Switch to existing tab\n this.workspaceManager.SetActiveTab(existingTab.id);\n } else {\n // Open new tab for this nav item\n this.OpenNavItem(appId, navItem, app.GetColor());\n }\n // Apply the requested query params to whichever tab is now active — synchronously,\n // so they're present before the (possibly cached) resource component reattaches.\n if (queryParams && Object.keys(queryParams).length > 0) {\n const targetTabId = this.workspaceManager.GetActiveTabId();\n if (targetTabId) {\n this.applyQueryParamsToTab(targetTabId, queryParams);\n }\n }\n return;\n }\n // Nav item not found, fall through to default behavior\n }\n\n // No specific nav item requested - check if app has any tabs\n if (appTabs.length === 0) {\n // Create default tab\n const tabRequest = await app.CreateDefaultTab();\n if (tabRequest) {\n this.workspaceManager.OpenTab(tabRequest, app.GetColor());\n }\n } else {\n // App has tabs - switch to the first one (or active one if exists)\n const config = this.workspaceManager.GetConfiguration();\n const activeAppTab = appTabs.find(t => t.id === config?.activeTabId);\n if (!activeAppTab) {\n // No active tab for this app, switch to first tab\n this.workspaceManager.SetActiveTab(appTabs[0].id);\n }\n }\n\n // Apply requested query params on the DEFAULT path too (previously only the\n // navItemName branch did) — e.g. the omnibar's '@agent' → Chat pre-addressing.\n // Synchronous, so params are present before a cached resource component reattaches.\n if (queryParams && Object.keys(queryParams).length > 0) {\n const targetTabId = this.workspaceManager.GetActiveTabId();\n if (targetTabId) {\n this.applyQueryParamsToTab(targetTabId, queryParams);\n }\n }\n }\n\n /**\n * Update the query params for the currently active tab.\n * This updates the tab's configuration and triggers a URL sync via the shell's\n * workspace configuration subscription.\n *\n * 🚨 NEVER CALL THIS FROM A RESOURCE COMPONENT OR DASHBOARD. \"The active tab\" is whatever the\n * user is looking at right now, which is not necessarily the tab the caller lives in. A component\n * whose async work completes while it sits in a BACKGROUND tab will rewrite — and destroy — the\n * deep link of the tab the user is actually viewing. Components use\n * {@link BaseResourceComponent.UpdateQueryParams}, which routes to {@link UpdateTabQueryParams}\n * with their own tab id and refuses to write when they have none.\n *\n * This remains only for shell-level navigation code that is, by construction, acting on behalf of\n * the user's current tab (nothing in this repo does today).\n *\n * @deprecated Prefer {@link UpdateTabQueryParams} with an explicit tab id.\n *\n * Use this instead of directly calling router.navigate() to ensure proper\n * URL management that respects app-scoped routes.\n *\n * @param queryParams Object containing query param key-value pairs.\n * Use null values to remove a query param.\n * @example\n * // Add or update query params\n * navigationService.UpdateActiveTabQueryParams({ category: 'abc123', dashboard: 'xyz789' });\n *\n * // Remove a query param\n * navigationService.UpdateActiveTabQueryParams({ category: null });\n */\n UpdateActiveTabQueryParams(queryParams: Record<string, string | null>): void {\n const activeTabId = this.workspaceManager.GetActiveTabId();\n if (!activeTabId) {\n console.warn('NavigationService.UpdateActiveTabQueryParams: No active tab');\n return;\n }\n\n this.applyQueryParamsToTab(activeTabId, queryParams);\n }\n\n /**\n * Update query params for a specific tab, optionally only if the tab still hosts\n * the expected resource identity. This matters in single-resource mode where a\n * tab ID can be reused for a different resource while the previous component is\n * detached but still alive in the cache.\n */\n UpdateTabQueryParams(\n tabId: string,\n queryParams: Record<string, string | null>,\n guard?: TabQueryParamUpdateGuard\n ): boolean {\n const tab = this.workspaceManager.GetTab(tabId);\n if (!tab) {\n console.debug('NavigationService.UpdateTabQueryParams: Tab not found; ignoring stale query-param update:', tabId);\n return false;\n }\n\n if (guard && !this.tabMatchesQueryParamGuard(tab, guard)) {\n console.debug('NavigationService.UpdateTabQueryParams: Tab identity changed; ignoring stale query-param update:', {\n tabId,\n guard,\n current: {\n resourceType: tab.configuration?.['resourceType'],\n driverClass: tab.configuration?.['resourceTypeDriverClass'] || tab.configuration?.['driverClass'],\n recordId: tab.resourceRecordId || tab.configuration?.['recordId'],\n navItemName: tab.configuration?.['navItemName'],\n entity: tab.configuration?.['Entity'] || tab.configuration?.['entity']\n }\n });\n return false;\n }\n\n this.applyQueryParamsToTab(tabId, queryParams);\n return true;\n }\n\n /**\n * Notify subscribers that query params changed on a specific tab.\n * Called by the shell when back/forward navigation changes query params on the active tab.\n * The notification includes the tab ID so only the component in that tab reacts.\n */\n NotifyQueryParamsChanged(tabId: string, params: Record<string, string>, force = false): void {\n this.queryParamChanged$.next({ TabId: tabId, Params: params, Force: force });\n }\n\n /**\n * Reactively observe the query params for a specific tab.\n *\n * Backed by the workspace BehaviorSubject, so a subscriber receives the current\n * params *immediately* on subscribe AND every subsequent change — including the\n * deep-link params that the ResourceResolver merges into the tab configuration on\n * a cold/direct URL load.\n *\n * This is the race-free counterpart to {@link NotifyQueryParamsChanged} (a plain\n * Subject that drops events fired before a component has subscribed). A resource\n * component that mounts from workspace restoration can subscribe here and still\n * pick up its initial deep-link state regardless of whether the params landed in\n * the tab config before or after it mounted.\n */\n public ObserveTabQueryParams(tabId: string): Observable<Record<string, string>> {\n return this.workspaceManager.Configuration.pipe(\n map(config => {\n const tab = config?.tabs?.find(t => t.id === tabId);\n return (tab?.configuration?.['queryParams'] || {}) as Record<string, string>;\n }),\n distinctUntilChanged((a, b) => this.shallowParamsEqual(a, b))\n );\n }\n\n private shallowParamsEqual(a: Record<string, string>, b: Record<string, string>): boolean {\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n if (keysA.length !== keysB.length) {\n return false;\n }\n return keysA.every(key => a[key] === b[key]);\n }\n\n private tabMatchesQueryParamGuard(tab: { resourceRecordId?: string; configuration?: Record<string, unknown> }, guard: TabQueryParamUpdateGuard): boolean {\n const config = tab.configuration || {};\n const matches = (expected: string | null | undefined, actual: unknown, normalize = false): boolean => {\n if (expected === undefined || expected === null) {\n return true;\n }\n const expectedText = String(expected);\n const actualText = actual == null ? '' : String(actual);\n return normalize\n ? expectedText.trim().toLowerCase() === actualText.trim().toLowerCase()\n : expectedText === actualText;\n };\n\n const currentDriverClass = (config['resourceTypeDriverClass'] || config['driverClass']) as string | undefined;\n const currentRecordId = tab.resourceRecordId || (config['recordId'] as string | undefined) || '';\n const currentEntity = (config['Entity'] || config['entity']) as string | undefined;\n\n // Resource type and entity names can vary by casing/metadata spelling; class names,\n // record IDs, and nav labels are canonical tab identity fields and stay exact-match.\n return matches(guard.resourceType, config['resourceType'], true) &&\n matches(guard.driverClass, currentDriverClass) &&\n matches(guard.recordId, currentRecordId) &&\n matches(guard.navItemName, config['navItemName']) &&\n matches(guard.entity, currentEntity, true);\n }\n\n /**\n * Apply query params to a specific tab by ID.\n * Merges with any existing query params on the tab. Use null values to remove params.\n */\n private applyQueryParamsToTab(tabId: string, queryParams: Record<string, string | null>): void {\n const tab = this.workspaceManager.GetTab(tabId);\n if (!tab) {\n console.warn('NavigationService.applyQueryParamsToTab: Tab not found:', tabId);\n return;\n }\n\n // Get existing queryParams from tab configuration\n const existingQueryParams = (tab.configuration?.['queryParams'] || {}) as Record<string, string | null>;\n\n // Merge with new query params\n const mergedQueryParams: Record<string, string> = {};\n\n // Start with existing params (excluding nulls)\n for (const [key, value] of Object.entries(existingQueryParams)) {\n if (value !== null) {\n mergedQueryParams[key] = value;\n }\n }\n\n // Apply new params (null means remove)\n for (const [key, value] of Object.entries(queryParams)) {\n if (value === null) {\n delete mergedQueryParams[key];\n } else {\n mergedQueryParams[key] = value;\n }\n }\n\n // Update the tab configuration\n this.workspaceManager.UpdateTabConfiguration(tabId, {\n queryParams: Object.keys(mergedQueryParams).length > 0 ? mergedQueryParams : undefined\n });\n }\n}\n"]}
@@ -70,7 +70,32 @@ export interface RecordSourceContext {
70
70
  */
71
71
  sourceRecordEntity?: string;
72
72
  sourceRecordId?: string;
73
+ /**
74
+ * The PARENT record's own origin, carried forward so a return can restore
75
+ * it instead of recapturing.
76
+ *
77
+ * Without this the crumb ping-pongs. Under the preview-tab model an
78
+ * in-record link CONSUMES the parent's tab, so by the time the user clicks
79
+ * the crumb back, the parent's tab no longer holds the parent — the
80
+ * reactivate-the-tab fast path in ReturnToRecordSource misses and it
81
+ * re-opens the parent, capturing wherever the user is standing (the CHILD)
82
+ * as the new origin. The two records then point at each other and the
83
+ * entry point ("Data Explorer › Data") is lost for good.
84
+ *
85
+ * Nesting makes this unwind at any depth: Data › A › B › C returns to B
86
+ * carrying A, then to A carrying Data. Truncated at
87
+ * {@link MAX_RECORD_ORIGIN_DEPTH} — it rides in the tab configuration and
88
+ * is persisted with the workspace.
89
+ */
90
+ sourceParentOrigin?: RecordSourceContext;
73
91
  }
92
+ /**
93
+ * How many ancestors a record origin chain keeps. Deep enough for real
94
+ * drill-downs, shallow enough that the persisted workspace config stays small.
95
+ */
96
+ export declare const MAX_RECORD_ORIGIN_DEPTH = 5;
97
+ /** Drop ancestors beyond MAX_RECORD_ORIGIN_DEPTH so the chain cannot grow without bound. */
98
+ export declare function TruncateRecordOriginChain(origin: RecordSourceContext | null | undefined, depth?: number): RecordSourceContext | undefined;
74
99
  /** True when the origin has somewhere to GO back to (crumb is clickable) */
75
100
  export declare function RecordSourceHasReturnTarget(origin: RecordSourceContext | null): boolean;
76
101
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"record-open-style.d.ts","sourceRoot":"","sources":["../../src/lib/record-open-style.ts"],"names":[],"mappings":"AAuBA,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,SAAS,CAAC;AAEpD;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,YAAY,CAAC;AAE/C,iFAAiF;AACjF,wBAAgB,yBAAyB,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,CAE5G;AAED;;;;;GAKG;AACH,eAAO,MAAM,8BAA8B,4BAA4B,CAAC;AAExE,8EAA8E;AAC9E,wBAAgB,yBAAyB,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,CAE5G;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,CAErG;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,mBAAmB;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qEAAqE;IACrE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,+EAA+E;IAC/E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4EAA4E;IAC5E,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3C;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,4EAA4E;AAC5E,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI,GAAG,OAAO,CAEvF;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,IAAI,GAAG,mBAAmB,GAAG,IAAI,CAiC5H;AAgBD,kEAAkE;AAClE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAE/D;AAED,iFAAiF;AACjF,wBAAgB,kBAAkB,IAAI,eAAe,CAEpD;AAED,gEAAgE;AAChE,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C"}
1
+ {"version":3,"file":"record-open-style.d.ts","sourceRoot":"","sources":["../../src/lib/record-open-style.ts"],"names":[],"mappings":"AAuBA,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,SAAS,CAAC;AAEpD;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,YAAY,CAAC;AAE/C,iFAAiF;AACjF,wBAAgB,yBAAyB,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,CAE5G;AAED;;;;;GAKG;AACH,eAAO,MAAM,8BAA8B,4BAA4B,CAAC;AAExE,8EAA8E;AAC9E,wBAAgB,yBAAyB,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,CAE5G;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,CAErG;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,mBAAmB;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qEAAqE;IACrE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,+EAA+E;IAC/E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4EAA4E;IAC5E,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3C;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;;;;;;;;;;;;OAgBG;IACH,kBAAkB,CAAC,EAAE,mBAAmB,CAAC;CAC1C;AAED;;;GAGG;AACH,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAEzC,4FAA4F;AAC5F,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,mBAAmB,GAAG,IAAI,GAAG,SAAS,EAC9C,KAAK,GAAE,MAAgC,GACtC,mBAAmB,GAAG,SAAS,CAUjC;AAED,4EAA4E;AAC5E,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI,GAAG,OAAO,CAEvF;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,IAAI,GAAG,mBAAmB,GAAG,IAAI,CA0C5H;AAgBD,kEAAkE;AAClE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAE/D;AAED,iFAAiF;AACjF,wBAAgB,kBAAkB,IAAI,eAAe,CAEpD;AAED,gEAAgE;AAChE,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C"}
@@ -54,6 +54,23 @@ export function IsRecordDockedToWorkspace(configuration) {
54
54
  export function IsRecordsRegionTab(configuration) {
55
55
  return IsRecordsTabConfiguration(configuration) && !IsRecordDockedToWorkspace(configuration);
56
56
  }
57
+ /**
58
+ * How many ancestors a record origin chain keeps. Deep enough for real
59
+ * drill-downs, shallow enough that the persisted workspace config stays small.
60
+ */
61
+ export const MAX_RECORD_ORIGIN_DEPTH = 5;
62
+ /** Drop ancestors beyond MAX_RECORD_ORIGIN_DEPTH so the chain cannot grow without bound. */
63
+ export function TruncateRecordOriginChain(origin, depth = MAX_RECORD_ORIGIN_DEPTH) {
64
+ if (!origin) {
65
+ return undefined;
66
+ }
67
+ if (depth <= 1) {
68
+ const { sourceParentOrigin: _dropped, ...rest } = origin;
69
+ return rest;
70
+ }
71
+ const parent = TruncateRecordOriginChain(origin.sourceParentOrigin, depth - 1);
72
+ return parent ? { ...origin, sourceParentOrigin: parent } : { ...origin, sourceParentOrigin: undefined };
73
+ }
57
74
  /** True when the origin has somewhere to GO back to (crumb is clickable) */
58
75
  export function RecordSourceHasReturnTarget(origin) {
59
76
  return !!(origin && (origin.sourceTabId || origin.sourceAppId));
@@ -83,6 +100,15 @@ export function GetRecordSourceContext(configuration) {
83
100
  sourceRecordEntity: readString('sourceRecordEntity'),
84
101
  sourceRecordId: readString('sourceRecordId')
85
102
  };
103
+ // The ancestor chain round-trips as a nested object on the same
104
+ // configuration bag, so recurse through the same reader.
105
+ const rawParent = configuration?.['sourceParentOrigin'];
106
+ if (rawParent && typeof rawParent === 'object' && !Array.isArray(rawParent)) {
107
+ const parent = GetRecordSourceContext(rawParent);
108
+ if (parent) {
109
+ context.sourceParentOrigin = parent;
110
+ }
111
+ }
86
112
  const rawParams = configuration?.['sourceQueryParams'];
87
113
  if (rawParams && typeof rawParams === 'object' && !Array.isArray(rawParams)) {
88
114
  const params = {};
@@ -1 +1 @@
1
- {"version":3,"file":"record-open-style.js","sourceRoot":"","sources":["../../src/lib/record-open-style.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Record-open style — how Explorer treats records opened from within apps.\n *\n * - 'records' (DEFAULT): records are a GLOBAL surface. Opening a record keeps\n * you in the app you're standing in (assigned there, no Home reassignment),\n * always opens as its own tab (re-open focuses the existing one), and the\n * shell renders a persistent count-badged \"Records\" pill in the header nav\n * plus a record-only tab strip while a record is being viewed. The\n * invariant: any open record is at most 2 clicks away from anywhere — the\n * pill resumes the last-viewed record (1 click), the strip reaches the rest.\n * - 'classic': the previous behavior — record opens are reassigned to the\n * Home app (which surfaces them as dynamic nav items) and multi-tab UI is\n * the Golden Layout header. Downstream escape hatch, not the product\n * direction.\n *\n * The value is resolved from the `Shell.RecordOpen.Style` instance config by\n * the shell (see ShellComponent.chromeFlags) and pushed here so that\n * NavigationService — which runs in this package, below the shell — forks on\n * the same value. Module-level state (not a service) because the style is a\n * per-deployment constant, not per-component state.\n */\nimport { GetGlobalObjectStore } from '@memberjunction/global';\n\nexport type RecordOpenStyle = 'records' | 'classic';\n\n/**\n * The workspace tab resourceType identifying entity-record tabs — the tabs\n * the records-style model routes into the separate records layout region.\n * ALWAYS compare through this constant: a typo'd literal would silently leak\n * record tabs into the wrong layout with no compile error.\n */\nexport const RECORDS_RESOURCE_TYPE = 'Records';\n\n/** True when a workspace tab's configuration marks it as an entity-record tab */\nexport function IsRecordsTabConfiguration(configuration: Record<string, unknown> | undefined | null): boolean {\n return configuration?.['resourceType'] === RECORDS_RESOURCE_TYPE;\n}\n\n/**\n * Tab-configuration key marking a record tab the user has promoted (\"docked\")\n * out of the records region into the MAIN workspace layout (\"Move to\n * Workspace\"). Persisted with the tab configuration. ALWAYS read through\n * IsRecordDockedToWorkspace.\n */\nexport const RECORD_DOCKED_TO_WORKSPACE_KEY = 'recordDockedToWorkspace';\n\n/** True when a record tab has been promoted into the main workspace layout */\nexport function IsRecordDockedToWorkspace(configuration: Record<string, unknown> | undefined | null): boolean {\n return configuration?.[RECORD_DOCKED_TO_WORKSPACE_KEY] === true;\n}\n\n/**\n * True when a tab LIVES in the records region: it is a record tab that has\n * NOT been docked to the workspace. This is the REGION-MEMBERSHIP predicate —\n * layout routing, region visibility, the Records pill, and the shell's\n * app-flip guard all fork on it. Record IDENTITY checks (open dedup,\n * record→record origin capture, SwitchToApp's \"never land on a record\"\n * filter) must keep using IsRecordsTabConfiguration: a docked record is\n * still a record, it just lives in the main layout.\n */\nexport function IsRecordsRegionTab(configuration: Record<string, unknown> | undefined | null): boolean {\n return IsRecordsTabConfiguration(configuration) && !IsRecordDockedToWorkspace(configuration);\n}\n\n/**\n * Where the user was standing when a record was opened — captured by\n * NavigationService.OpenEntityRecord into the record tab's configuration.\n * Powers the origin crumb in the records region (\"← App › Page\"), which takes\n * the user straight back to that page. Origin is PROVENANCE, not location:\n * records are a global surface with no canonical parent, so this trail is a\n * convenience affordance (\"return to where I came from\"), never an ancestry\n * claim. All fields optional because capture is best-effort (e.g. no active\n * app at open time, or the source tab has since been closed).\n */\nexport interface RecordSourceContext {\n sourceAppId?: string;\n sourceAppName?: string;\n /** Display title of the source tab at capture (what the user saw) */\n sourceNavLabel?: string;\n /**\n * Canonical nav-item identity of the source tab (its configuration\n * `navItemName`). Titles MUTATE — dashboards rename their tab as the user\n * drills (Data Explorer sets it to the selected entity) — so identity\n * checks and nav re-resolution use THIS; the label is display-only.\n * Absent for dynamic nav items, which never get navItemName stamped.\n */\n sourceNavItemName?: string;\n /** The exact workspace tab the user was on — highest-fidelity return target */\n sourceTabId?: string;\n /**\n * Display label for NON-TAB origins (overlays, dialogs, agent actions —\n * e.g. 'Conversation', 'Search'). When present, the crumb renders this\n * label instead of \"App › Page\". Surfaces that aren't workspace tabs MUST\n * set this (via NavigationOptions.recordSource) instead of letting\n * captureSourceContext blame whatever tab sits behind the overlay.\n */\n sourceLabel?: string;\n /** Query params to apply when returning via the app + nav-label fallback */\n sourceQueryParams?: Record<string, string>;\n /**\n * Set when the record was opened from INSIDE another record (Matt's\n * record→record call, 2026-07-28: the crumb points at the PARENT record).\n * Identity — not the tab id — is what the return path verifies and, when\n * the parent's tab has been closed, re-opens.\n */\n sourceRecordEntity?: string;\n sourceRecordId?: string;\n}\n\n/** True when the origin has somewhere to GO back to (crumb is clickable) */\nexport function RecordSourceHasReturnTarget(origin: RecordSourceContext | null): boolean {\n return !!(origin && (origin.sourceTabId || origin.sourceAppId));\n}\n\n/**\n * Extract the source context from a record tab's configuration. Returns null\n * when there is no usable origin (no sourceAppId), so callers can gate the\n * crumb UI on a single truthiness check.\n */\nexport function GetRecordSourceContext(configuration: Record<string, unknown> | undefined | null): RecordSourceContext | null {\n const readString = (key: string): string | undefined => {\n const value = configuration?.[key];\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n };\n const appId = readString('sourceAppId');\n const label = readString('sourceLabel');\n if (!appId && !label) {\n return null;\n }\n const context: RecordSourceContext = {\n sourceAppId: appId,\n sourceAppName: readString('sourceAppName'),\n sourceNavLabel: readString('sourceNavLabel'),\n sourceNavItemName: readString('sourceNavItemName'),\n sourceTabId: readString('sourceTabId'),\n sourceLabel: label,\n sourceRecordEntity: readString('sourceRecordEntity'),\n sourceRecordId: readString('sourceRecordId')\n };\n const rawParams = configuration?.['sourceQueryParams'];\n if (rawParams && typeof rawParams === 'object' && !Array.isArray(rawParams)) {\n const params: Record<string, string> = {};\n for (const [key, value] of Object.entries(rawParams as Record<string, unknown>)) {\n if (typeof value === 'string') {\n params[key] = value;\n }\n }\n if (Object.keys(params).length > 0) {\n context.sourceQueryParams = params;\n }\n }\n return context;\n}\n\n/**\n * The resolved style lives in the GLOBAL OBJECT STORE, not a module-level\n * variable: ESBuild/Vite code-splitting can duplicate this module across\n * chunks, and a lazy chunk's copy would silently keep the default while the\n * shell wrote 'classic' into another copy — the exact split-brain the\n * BaseSingleton rule exists to prevent. The store guarantees one value per\n * page regardless of how many module copies load.\n */\nconst RECORD_OPEN_STYLE_STORE_KEY = '__mj_record_open_style';\n\nfunction styleStore(): Record<string, RecordOpenStyle | undefined> {\n return GetGlobalObjectStore() as Record<string, RecordOpenStyle | undefined>;\n}\n\n/** Set by the shell once instance config resolves. Idempotent. */\nexport function SetRecordOpenStyle(style: RecordOpenStyle): void {\n styleStore()[RECORD_OPEN_STYLE_STORE_KEY] = style;\n}\n\n/** The deployment's record-open style ('records' unless configured 'classic') */\nexport function GetRecordOpenStyle(): RecordOpenStyle {\n return styleStore()[RECORD_OPEN_STYLE_STORE_KEY] ?? 'records';\n}\n\n/** True when records-as-tabs is active (the 'records' style) */\nexport function IsRecordTabsStyle(): boolean {\n return GetRecordOpenStyle() === 'records';\n}\n"]}
1
+ {"version":3,"file":"record-open-style.js","sourceRoot":"","sources":["../../src/lib/record-open-style.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Record-open style — how Explorer treats records opened from within apps.\n *\n * - 'records' (DEFAULT): records are a GLOBAL surface. Opening a record keeps\n * you in the app you're standing in (assigned there, no Home reassignment),\n * always opens as its own tab (re-open focuses the existing one), and the\n * shell renders a persistent count-badged \"Records\" pill in the header nav\n * plus a record-only tab strip while a record is being viewed. The\n * invariant: any open record is at most 2 clicks away from anywhere — the\n * pill resumes the last-viewed record (1 click), the strip reaches the rest.\n * - 'classic': the previous behavior — record opens are reassigned to the\n * Home app (which surfaces them as dynamic nav items) and multi-tab UI is\n * the Golden Layout header. Downstream escape hatch, not the product\n * direction.\n *\n * The value is resolved from the `Shell.RecordOpen.Style` instance config by\n * the shell (see ShellComponent.chromeFlags) and pushed here so that\n * NavigationService — which runs in this package, below the shell — forks on\n * the same value. Module-level state (not a service) because the style is a\n * per-deployment constant, not per-component state.\n */\nimport { GetGlobalObjectStore } from '@memberjunction/global';\n\nexport type RecordOpenStyle = 'records' | 'classic';\n\n/**\n * The workspace tab resourceType identifying entity-record tabs — the tabs\n * the records-style model routes into the separate records layout region.\n * ALWAYS compare through this constant: a typo'd literal would silently leak\n * record tabs into the wrong layout with no compile error.\n */\nexport const RECORDS_RESOURCE_TYPE = 'Records';\n\n/** True when a workspace tab's configuration marks it as an entity-record tab */\nexport function IsRecordsTabConfiguration(configuration: Record<string, unknown> | undefined | null): boolean {\n return configuration?.['resourceType'] === RECORDS_RESOURCE_TYPE;\n}\n\n/**\n * Tab-configuration key marking a record tab the user has promoted (\"docked\")\n * out of the records region into the MAIN workspace layout (\"Move to\n * Workspace\"). Persisted with the tab configuration. ALWAYS read through\n * IsRecordDockedToWorkspace.\n */\nexport const RECORD_DOCKED_TO_WORKSPACE_KEY = 'recordDockedToWorkspace';\n\n/** True when a record tab has been promoted into the main workspace layout */\nexport function IsRecordDockedToWorkspace(configuration: Record<string, unknown> | undefined | null): boolean {\n return configuration?.[RECORD_DOCKED_TO_WORKSPACE_KEY] === true;\n}\n\n/**\n * True when a tab LIVES in the records region: it is a record tab that has\n * NOT been docked to the workspace. This is the REGION-MEMBERSHIP predicate —\n * layout routing, region visibility, the Records pill, and the shell's\n * app-flip guard all fork on it. Record IDENTITY checks (open dedup,\n * record→record origin capture, SwitchToApp's \"never land on a record\"\n * filter) must keep using IsRecordsTabConfiguration: a docked record is\n * still a record, it just lives in the main layout.\n */\nexport function IsRecordsRegionTab(configuration: Record<string, unknown> | undefined | null): boolean {\n return IsRecordsTabConfiguration(configuration) && !IsRecordDockedToWorkspace(configuration);\n}\n\n/**\n * Where the user was standing when a record was opened — captured by\n * NavigationService.OpenEntityRecord into the record tab's configuration.\n * Powers the origin crumb in the records region (\"← App › Page\"), which takes\n * the user straight back to that page. Origin is PROVENANCE, not location:\n * records are a global surface with no canonical parent, so this trail is a\n * convenience affordance (\"return to where I came from\"), never an ancestry\n * claim. All fields optional because capture is best-effort (e.g. no active\n * app at open time, or the source tab has since been closed).\n */\nexport interface RecordSourceContext {\n sourceAppId?: string;\n sourceAppName?: string;\n /** Display title of the source tab at capture (what the user saw) */\n sourceNavLabel?: string;\n /**\n * Canonical nav-item identity of the source tab (its configuration\n * `navItemName`). Titles MUTATE — dashboards rename their tab as the user\n * drills (Data Explorer sets it to the selected entity) — so identity\n * checks and nav re-resolution use THIS; the label is display-only.\n * Absent for dynamic nav items, which never get navItemName stamped.\n */\n sourceNavItemName?: string;\n /** The exact workspace tab the user was on — highest-fidelity return target */\n sourceTabId?: string;\n /**\n * Display label for NON-TAB origins (overlays, dialogs, agent actions —\n * e.g. 'Conversation', 'Search'). When present, the crumb renders this\n * label instead of \"App › Page\". Surfaces that aren't workspace tabs MUST\n * set this (via NavigationOptions.recordSource) instead of letting\n * captureSourceContext blame whatever tab sits behind the overlay.\n */\n sourceLabel?: string;\n /** Query params to apply when returning via the app + nav-label fallback */\n sourceQueryParams?: Record<string, string>;\n /**\n * Set when the record was opened from INSIDE another record (Matt's\n * record→record call, 2026-07-28: the crumb points at the PARENT record).\n * Identity — not the tab id — is what the return path verifies and, when\n * the parent's tab has been closed, re-opens.\n */\n sourceRecordEntity?: string;\n sourceRecordId?: string;\n /**\n * The PARENT record's own origin, carried forward so a return can restore\n * it instead of recapturing.\n *\n * Without this the crumb ping-pongs. Under the preview-tab model an\n * in-record link CONSUMES the parent's tab, so by the time the user clicks\n * the crumb back, the parent's tab no longer holds the parent — the\n * reactivate-the-tab fast path in ReturnToRecordSource misses and it\n * re-opens the parent, capturing wherever the user is standing (the CHILD)\n * as the new origin. The two records then point at each other and the\n * entry point (\"Data Explorer › Data\") is lost for good.\n *\n * Nesting makes this unwind at any depth: Data › A › B › C returns to B\n * carrying A, then to A carrying Data. Truncated at\n * {@link MAX_RECORD_ORIGIN_DEPTH} — it rides in the tab configuration and\n * is persisted with the workspace.\n */\n sourceParentOrigin?: RecordSourceContext;\n}\n\n/**\n * How many ancestors a record origin chain keeps. Deep enough for real\n * drill-downs, shallow enough that the persisted workspace config stays small.\n */\nexport const MAX_RECORD_ORIGIN_DEPTH = 5;\n\n/** Drop ancestors beyond MAX_RECORD_ORIGIN_DEPTH so the chain cannot grow without bound. */\nexport function TruncateRecordOriginChain(\n origin: RecordSourceContext | null | undefined,\n depth: number = MAX_RECORD_ORIGIN_DEPTH\n): RecordSourceContext | undefined {\n if (!origin) {\n return undefined;\n }\n if (depth <= 1) {\n const { sourceParentOrigin: _dropped, ...rest } = origin;\n return rest;\n }\n const parent = TruncateRecordOriginChain(origin.sourceParentOrigin, depth - 1);\n return parent ? { ...origin, sourceParentOrigin: parent } : { ...origin, sourceParentOrigin: undefined };\n}\n\n/** True when the origin has somewhere to GO back to (crumb is clickable) */\nexport function RecordSourceHasReturnTarget(origin: RecordSourceContext | null): boolean {\n return !!(origin && (origin.sourceTabId || origin.sourceAppId));\n}\n\n/**\n * Extract the source context from a record tab's configuration. Returns null\n * when there is no usable origin (no sourceAppId), so callers can gate the\n * crumb UI on a single truthiness check.\n */\nexport function GetRecordSourceContext(configuration: Record<string, unknown> | undefined | null): RecordSourceContext | null {\n const readString = (key: string): string | undefined => {\n const value = configuration?.[key];\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n };\n const appId = readString('sourceAppId');\n const label = readString('sourceLabel');\n if (!appId && !label) {\n return null;\n }\n const context: RecordSourceContext = {\n sourceAppId: appId,\n sourceAppName: readString('sourceAppName'),\n sourceNavLabel: readString('sourceNavLabel'),\n sourceNavItemName: readString('sourceNavItemName'),\n sourceTabId: readString('sourceTabId'),\n sourceLabel: label,\n sourceRecordEntity: readString('sourceRecordEntity'),\n sourceRecordId: readString('sourceRecordId')\n };\n // The ancestor chain round-trips as a nested object on the same\n // configuration bag, so recurse through the same reader.\n const rawParent = configuration?.['sourceParentOrigin'];\n if (rawParent && typeof rawParent === 'object' && !Array.isArray(rawParent)) {\n const parent = GetRecordSourceContext(rawParent as Record<string, unknown>);\n if (parent) {\n context.sourceParentOrigin = parent;\n }\n }\n const rawParams = configuration?.['sourceQueryParams'];\n if (rawParams && typeof rawParams === 'object' && !Array.isArray(rawParams)) {\n const params: Record<string, string> = {};\n for (const [key, value] of Object.entries(rawParams as Record<string, unknown>)) {\n if (typeof value === 'string') {\n params[key] = value;\n }\n }\n if (Object.keys(params).length > 0) {\n context.sourceQueryParams = params;\n }\n }\n return context;\n}\n\n/**\n * The resolved style lives in the GLOBAL OBJECT STORE, not a module-level\n * variable: ESBuild/Vite code-splitting can duplicate this module across\n * chunks, and a lazy chunk's copy would silently keep the default while the\n * shell wrote 'classic' into another copy — the exact split-brain the\n * BaseSingleton rule exists to prevent. The store guarantees one value per\n * page regardless of how many module copies load.\n */\nconst RECORD_OPEN_STYLE_STORE_KEY = '__mj_record_open_style';\n\nfunction styleStore(): Record<string, RecordOpenStyle | undefined> {\n return GetGlobalObjectStore() as Record<string, RecordOpenStyle | undefined>;\n}\n\n/** Set by the shell once instance config resolves. Idempotent. */\nexport function SetRecordOpenStyle(style: RecordOpenStyle): void {\n styleStore()[RECORD_OPEN_STYLE_STORE_KEY] = style;\n}\n\n/** The deployment's record-open style ('records' unless configured 'classic') */\nexport function GetRecordOpenStyle(): RecordOpenStyle {\n return styleStore()[RECORD_OPEN_STYLE_STORE_KEY] ?? 'records';\n}\n\n/** True when records-as-tabs is active (the 'records' style) */\nexport function IsRecordTabsStyle(): boolean {\n return GetRecordOpenStyle() === 'records';\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memberjunction/ng-shared",
3
- "version": "6.1.0-edge.4",
3
+ "version": "6.1.0-edge.6",
4
4
  "description": "MemberJunction: MJ Explorer Angular Shared Package - utility functions and other reusable elements used across other MJ Angular packages within the MJ Explorer App - do not use outside of MJ Explorer.",
5
5
  "main": "./dist/public-api.js",
6
6
  "typings": "./dist/public-api.d.ts",
@@ -14,28 +14,28 @@
14
14
  "author": "",
15
15
  "license": "BUSL-1.1",
16
16
  "devDependencies": {
17
- "@angular/compiler": "21.1.3",
18
- "@angular/compiler-cli": "21.1.3"
17
+ "@angular/compiler": "21.2.22",
18
+ "@angular/compiler-cli": "21.2.22"
19
19
  },
20
20
  "peerDependencies": {
21
- "@angular/common": "^21.1.3",
22
- "@angular/core": "^21.1.3",
23
- "@angular/router": "^21.1.3",
21
+ "@angular/common": "^21.2.22",
22
+ "@angular/core": "^21.2.22",
23
+ "@angular/platform-browser": "^21.2.22",
24
+ "@angular/router": "^21.2.22",
24
25
  "rxjs": "^7.8.2"
25
26
  },
26
27
  "dependencies": {
27
- "@angular/platform-browser": "21.1.3",
28
- "@memberjunction/ai-core-plus": "6.1.0-edge.4",
29
- "@memberjunction/ai-engine-base": "6.1.0-edge.4",
30
- "@memberjunction/core": "6.1.0-edge.4",
31
- "@memberjunction/core-entities": "6.1.0-edge.4",
32
- "@memberjunction/entity-communications-base": "6.1.0-edge.4",
33
- "@memberjunction/global": "6.1.0-edge.4",
34
- "@memberjunction/graphql-dataprovider": "6.1.0-edge.4",
35
- "@memberjunction/ng-base-application": "6.1.0-edge.4",
36
- "@memberjunction/ng-base-types": "6.1.0-edge.4",
37
- "@memberjunction/ng-notifications": "6.1.0-edge.4",
38
- "@memberjunction/ng-shared-generic": "6.1.0-edge.4",
28
+ "@memberjunction/ai-core-plus": "6.1.0-edge.6",
29
+ "@memberjunction/ai-engine-base": "6.1.0-edge.6",
30
+ "@memberjunction/core": "6.1.0-edge.6",
31
+ "@memberjunction/core-entities": "6.1.0-edge.6",
32
+ "@memberjunction/entity-communications-base": "6.1.0-edge.6",
33
+ "@memberjunction/global": "6.1.0-edge.6",
34
+ "@memberjunction/graphql-dataprovider": "6.1.0-edge.6",
35
+ "@memberjunction/ng-base-application": "6.1.0-edge.6",
36
+ "@memberjunction/ng-base-types": "6.1.0-edge.6",
37
+ "@memberjunction/ng-notifications": "6.1.0-edge.6",
38
+ "@memberjunction/ng-shared-generic": "6.1.0-edge.6",
39
39
  "html-to-image": "^1.11.11",
40
40
  "tslib": "^2.8.1"
41
41
  },