From 40c716b17af6157b928074f8860c98cc69036c07 Mon Sep 17 00:00:00 2001 From: erKam Date: Mon, 12 Oct 2026 22:07:25 +0000 Subject: [PATCH 1/3] feat(ui): add one timeline, a popup splitter and pop-out, and a terminals dock Adds a Timeline tab that merges router, HTTP, NgRx, change detection, forms and signals events with toggleable layers, a resizable docked popup with pop-out into its own window, and an opt-in hub Terminals dock that runs only the project's detected build and test commands. Records which devframe plugins were adopted. Refs #342 --- app/src/__tests__/popout-window.test.ts | 45 + app/src/__tests__/timeline.test.ts | 484 ++++++++ app/src/app.ts | 22 + app/src/pages/tab-icon.ts | 6 + app/src/pages/timeline-data.ts | 634 +++++++++++ app/src/pages/timeline.ts | 1000 +++++++++++++++++ app/src/shell-prefs.ts | 40 + app/src/types/tab.types.ts | 1 + apps/docs/src/content/agents/tools.md | 10 +- .../getting-started/command-palette.md | 7 +- .../content/getting-started/configuration.md | 10 + .../src/content/getting-started/express.md | 1 + .../content/getting-started/popup-and-hub.md | 45 +- .../src/content/getting-started/terminals.md | 90 ++ apps/docs/src/content/getting-started/vite.md | 17 +- apps/docs/src/content/inspectors/timeline.md | 96 ++ apps/docs/src/content/security.md | 9 + apps/docs/src/ngmd.config.ts | 2 + packages/devtools/package.json | 2 + .../src/__tests__/hub-terminals.test.ts | 304 +++++ .../src/__tests__/panel-shell.test.ts | 6 + packages/devtools/src/__tests__/popup.test.ts | 210 +++- .../src/__tests__/terminal-commands.test.ts | 153 +++ .../src/__tests__/terminal-runner.test.ts | 332 ++++++ .../src/__tests__/vite-terminals.test.ts | 44 + packages/devtools/src/config.ts | 27 +- packages/devtools/src/hub-terminals-dock.ts | 132 +++ packages/devtools/src/hub-terminals.ts | 189 ++++ packages/devtools/src/hub.ts | 31 +- packages/devtools/src/popup.ts | 346 +++++- packages/devtools/src/terminal-commands.ts | 159 +++ packages/devtools/src/terminal-runner.ts | 394 +++++++ packages/devtools/src/vite.ts | 10 + pnpm-lock.yaml | 37 + 34 files changed, 4856 insertions(+), 39 deletions(-) create mode 100644 app/src/__tests__/popout-window.test.ts create mode 100644 app/src/__tests__/timeline.test.ts create mode 100644 app/src/pages/timeline-data.ts create mode 100644 app/src/pages/timeline.ts create mode 100644 apps/docs/src/content/getting-started/terminals.md create mode 100644 apps/docs/src/content/inspectors/timeline.md create mode 100644 packages/devtools/src/__tests__/hub-terminals.test.ts create mode 100644 packages/devtools/src/__tests__/terminal-commands.test.ts create mode 100644 packages/devtools/src/__tests__/terminal-runner.test.ts create mode 100644 packages/devtools/src/__tests__/vite-terminals.test.ts create mode 100644 packages/devtools/src/hub-terminals-dock.ts create mode 100644 packages/devtools/src/hub-terminals.ts create mode 100644 packages/devtools/src/terminal-commands.ts create mode 100644 packages/devtools/src/terminal-runner.ts diff --git a/app/src/__tests__/popout-window.test.ts b/app/src/__tests__/popout-window.test.ts new file mode 100644 index 00000000..51ad4f65 --- /dev/null +++ b/app/src/__tests__/popout-window.test.ts @@ -0,0 +1,45 @@ +import { describe, expect, it, vi } from 'vitest'; +import { popoutOpener, returnFromPopout } from '../shell-prefs'; + +/** A stand-in for a window, with the fields the helpers read. */ +function fakeWindow(name: string, opener: unknown, referrer = '') { + const win = { name, opener, document: { referrer } } as unknown as Window & { + top: Window; + }; + Object.defineProperty(win, 'top', { value: win }); + return win; +} + +describe('the pop-out window', () => { + it('knows the app page only in the window the popup opened', () => { + const opener = { postMessage: vi.fn(), location: { origin: 'http://localhost:4200' } }; + expect(popoutOpener(fakeWindow('pangular-inspector-k3x9', opener))).toBe(opener); + expect(popoutOpener(fakeWindow('other', opener))).toBeNull(); + expect(popoutOpener(fakeWindow('pangular-inspector-k3x9', null))).toBeNull(); + }); + + it('asks the popup to take the panel back, on the app page origin', () => { + const opener = { postMessage: vi.fn(), location: { origin: 'http://localhost:4200' } }; + expect(returnFromPopout(fakeWindow('pangular-inspector-k3x9', opener))).toBe(true); + expect(opener.postMessage).toHaveBeenCalledWith( + { type: 'pangular:popup-return' }, + 'http://localhost:4200', + ); + }); + + it('falls back to the referrer when the app page is on another origin', () => { + const opener = { postMessage: vi.fn() }; + Object.defineProperty(opener, 'location', { + get: () => { + throw new Error('cross origin'); + }, + }); + const win = fakeWindow('pangular-inspector-k3x9', opener, 'http://app.test/trips'); + expect(returnFromPopout(win)).toBe(true); + expect(opener.postMessage).toHaveBeenCalledWith( + { type: 'pangular:popup-return' }, + 'http://app.test', + ); + expect(returnFromPopout(fakeWindow('pangular-inspector-k3x9', opener))).toBe(false); + }); +}); diff --git a/app/src/__tests__/timeline.test.ts b/app/src/__tests__/timeline.test.ts new file mode 100644 index 00000000..8258d0e3 --- /dev/null +++ b/app/src/__tests__/timeline.test.ts @@ -0,0 +1,484 @@ +import { TestBed, type ComponentFixture } from '@angular/core/testing'; +import type { DevframeRpcClient } from 'devframe/client'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { + ALL_PAGES, + binTimeline, + extentOf, + filterTimeline, + inRange, + mergeTimeline, + nearestEvent, + panRange, + rangeBetween, + storedHiddenLayers, + timelinePages, + zoomRange, + type TimelineLayer, + type TimelineSources, +} from '../pages/timeline-data'; + +vi.mock('devframe/client', () => ({ connectDevframe: () => new Promise(() => {}) })); + +const { Timeline, TIMELINE_LIST_CAP } = await import('../pages/timeline'); + +const ALL_LAYERS = new Set(['router', 'http', 'ngrx', 'cd', 'forms', 'signals']); + +/** One page of each source, with events spread over a few seconds. */ +function sources(): TimelineSources { + return { + router: { + pages: [ + { + pageId: 'p1', + reportedAt: 0, + snapshot: { url: '/trips' }, + navigations: [ + { + id: 1, + url: '/trips', + trigger: 'imperative', + startedAt: 1_000, + endedAt: 1_040, + outcome: 'succeeded', + }, + { + id: 2, + url: '/admin', + trigger: 'imperative', + startedAt: 5_000, + endedAt: 5_010, + outcome: 'failed', + }, + { + id: 3, + url: '/probe', + trigger: 'x', + startedAt: 5_500, + outcome: 'pending', + probe: true, + }, + { + id: 4, + url: '/early', + trigger: 'x', + startedAt: 600, + outcome: 'succeeded', + beforeConnect: true, + }, + ], + }, + ], + }, + http: { + serverCalls: [ + { + id: 's1', + url: 'http://localhost/api/trips', + method: 'GET', + status: 200, + durationMs: 30, + side: 'server', + at: 500, + pageUrl: '/trips', + }, + ], + pages: [ + { + pageId: 'p1', + url: '/trips', + initialUrl: '/trips', + title: 'Trips', + calls: [ + { + id: 'c1', + url: '/api/seats?trip=42', + method: 'POST', + status: 503, + durationMs: 120, + side: 'client', + at: 2_000, + }, + ], + }, + { + pageId: 'p2', + url: '/home', + title: 'Home', + calls: [ + { id: 'c1', url: '/api/me', method: 'GET', status: 200, durationMs: 5, at: 2_500 }, + ], + }, + ], + }, + ngrx: { + pages: [ + { + pageId: 'p1', + url: '/trips', + title: 'Trips', + log: [ + { + seq: 1, + source: 'store', + storeId: 'store', + type: '[Trips] Load', + timestamp: 3_000, + diff: [{ path: 'trips.loading', op: 'change' }], + restorable: true, + }, + ], + }, + ], + }, + cd: { + pages: { + p1: { + pageId: 'p1', + cycles: [{ id: 7, at: 3_500, ms: 22, passes: 1, checks: 12, components: [] }], + }, + }, + }, + forms: { + events: [{ formId: 'f1@p1', path: 'email', type: 'value', timestamp: 4_000, origin: 'user' }], + }, + signals: { + pages: { + p1: { + pageId: 'p1', + nodes: [{ id: 'n1', label: 'count', kind: 'signal' }], + history: { + n1: [ + { epoch: 1, value: 0, at: 100, source: 'initial' }, + { epoch: 2, value: 1, at: 4_500, source: 'write' }, + ], + }, + }, + }, + }, + }; +} + +describe('timeline data', () => { + it('merges every source on one time axis, oldest first', () => { + const events = mergeTimeline(sources()); + expect(events.map((event) => event.layer)).toEqual([ + 'http', + 'router', + 'http', + 'http', + 'ngrx', + 'cd', + 'forms', + 'signals', + 'router', + ]); + expect(events.map((event) => event.at)).toEqual( + [...events.map((e) => e.at)].sort((a, b) => a - b), + ); + // Probes, navigations without a real start and a signal's initial value are not events. + expect(events.some((event) => event.title.includes('/probe'))).toBe(false); + expect(events.some((event) => event.title.includes('/early'))).toBe(false); + expect(events.filter((event) => event.layer === 'signals')).toHaveLength(1); + }); + + it('gives every event a unique key, a tone for problems and a link to its tab', () => { + const events = mergeTimeline(sources()); + expect(new Set(events.map((event) => event.key)).size).toBe(events.length); + const failed = events.find((event) => event.title === 'Navigation to /admin')!; + expect(failed.tone).toBe('danger'); + expect(failed.link).toEqual({ tab: 'routes', page: 'p1' }); + const post = events.find((event) => event.title.startsWith('POST'))!; + expect(post.title).toBe('POST /api/seats?trip=42'); + expect(post.tone).toBe('danger'); + expect(events.find((event) => event.layer === 'cd')!.tone).toBe('warn'); + // Forms can select the form itself through the deep link. + expect(events.find((event) => event.layer === 'forms')!.link).toEqual({ + tab: 'forms', + id: 'f1@p1', + page: 'p1', + }); + }); + + it('gives a server call to the page its SSR request rendered', () => { + const server = mergeTimeline(sources()).find((event) => event.key.startsWith('http:server'))!; + expect(server.pageId).toBe('p1'); + expect(server.title).toBe('GET /api/trips'); + }); + + it('keeps keys unique when a source repeats an id', () => { + const repeated = { + ngrx: { + pages: [ + { + pageId: 'p1', + log: [ + { seq: 1, storeId: 's', type: 'a', timestamp: 1, diff: [] }, + { seq: 1, storeId: 's', type: 'b', timestamp: 2, diff: [] }, + ], + }, + ], + }, + }; + const keys = mergeTimeline(repeated).map((event) => event.key); + expect(new Set(keys).size).toBe(2); + }); + + it('ignores sources it cannot read', () => { + expect( + mergeTimeline({ router: 'nope', http: { pages: 3 }, cd: null, forms: { events: {} } }), + ).toEqual([]); + }); + + it('filters by layer, page and text', () => { + const events = mergeTimeline(sources()); + const only = (layers: TimelineLayer[], pageId = ALL_PAGES, query = '') => + filterTimeline(events, { layers: new Set(layers), pageId, query }); + expect(only(['http']).length).toBe(3); + expect(only(['http'], 'p2').map((event) => event.title)).toEqual(['GET /api/me']); + expect(only([...ALL_LAYERS], ALL_PAGES, 'SEATS').map((event) => event.layer)).toEqual(['http']); + expect(only([...ALL_LAYERS], 'p1').every((event) => event.pageId === 'p1')).toBe(true); + }); + + it('names pages from the sources that know their URL or title', () => { + const merged = mergeTimeline(sources()); + expect(timelinePages(sources(), merged)).toEqual([ + { pageId: 'p1', label: 'Trips' }, + { pageId: 'p2', label: 'Home' }, + ]); + }); + + it('zooms, pans and selects a range inside the events', () => { + const events = mergeTimeline(sources()); + const bounds = extentOf(events)!; + expect(bounds).toEqual({ start: 500, end: 5_010 }); + const zoomed = zoomRange(bounds, 0.5, bounds)!; + expect(zoomed.end - zoomed.start).toBeCloseTo((5_010 - 500) / 2); + expect(zoomRange(zoomed, 4, bounds)).toBeNull(); + const earlier = panRange(zoomed, -10, bounds); + expect(earlier.start).toBe(bounds.start); + const picked = rangeBetween(bounds, 0.6, 0.2)!; + expect(picked.start).toBeLessThan(picked.end); + expect(inRange(events, picked).every((e) => e.at >= picked.start && e.at <= picked.end)).toBe( + true, + ); + expect(rangeBetween(bounds, 0.5, 0.5)).toBeNull(); + }); + + it('draws a few thousand events as at most one mark per slice and layer', () => { + const many = { + ngrx: { + pages: [ + { + pageId: 'p1', + log: Array.from({ length: 5_000 }, (_, i) => ({ + seq: i, + storeId: 's', + type: 'tick', + timestamp: i, + diff: [], + })), + }, + ], + }, + }; + const events = mergeTimeline(many); + const bins = binTimeline(events, extentOf(events)!, 240); + expect(bins.length).toBeLessThanOrEqual(240); + expect(bins.reduce((sum, bin) => sum + bin.count, 0)).toBe(5_000); + }); + + it('marks a slice with the worst tone of its events, and finds the nearest event', () => { + const events = mergeTimeline(sources()); + const bins = binTimeline(events, { start: 0, end: 10_000 }, 1); + expect(bins.find((bin) => bin.layer === 'router')!.tone).toBe('danger'); + expect(nearestEvent(events, 'router', 4_000)!.title).toBe('Navigation to /admin'); + }); +}); + +function fakeClient(states: Record, configs: Record = {}) { + const rpc = { + call: () => Promise.resolve([]), + sharedState: (name: string) => + name in states + ? Promise.resolve({ value: () => states[name], on: () => () => {} }) + : Promise.reject(new Error('no state')), + }; + return { + connectionMeta: { configs }, + scope: () => ({ rpc }), + } as unknown as DevframeRpcClient; +} + +function wire(source: TimelineSources) { + return { + router: source.router, + http: source.http, + 'ngrx-store': source.ngrx, + 'change-detection': source.cd, + forms: source.forms, + 'signal-graph': source.signals, + }; +} + +async function settle(fixture: ComponentFixture) { + for (let i = 0; i < 4; i++) { + await new Promise((resolve) => setTimeout(resolve)); + await fixture.whenStable(); + } +} + +const host = (fixture: ComponentFixture) => fixture.nativeElement as HTMLElement; +const text = (fixture: ComponentFixture) => + (host(fixture).textContent ?? '').replace(/\s+/g, ' '); + +async function mount(states: Record) { + const fixture = TestBed.createComponent(Timeline); + fixture.componentRef.setInput('rpc', fakeClient(states)); + document.body.append(host(fixture)); + await settle(fixture); + return fixture; +} + +function layerButton(fixture: ComponentFixture, label: string): HTMLButtonElement { + const found = [...host(fixture).querySelectorAll('button.layer')].find( + (button) => (button.textContent ?? '').trim().startsWith(label), + ); + if (!found) throw new Error(`No layer ${label}`); + return found; +} + +const rows = (fixture: ComponentFixture) => [ + ...host(fixture).querySelectorAll('button.row'), +]; + +beforeEach(() => localStorage.clear()); +afterEach(() => { + document.body.innerHTML = ''; +}); + +describe('the Timeline tab', () => { + it('says where events come from when there are none', async () => { + const fixture = await mount(wire({})); + expect(text(fixture)).toContain('Nothing on the timeline yet.'); + }); + + it('offers Retry when the server sends no state at all', async () => { + const fixture = await mount({}); + expect(host(fixture).querySelector('[role="alert"]')?.textContent).toContain( + "Can't read the timeline", + ); + }); + + it('lists every event newest first, with an accessible chart summary', async () => { + const fixture = await mount(wire(sources())); + const titles = rows(fixture).map((row) => row.querySelector('.title')!.textContent); + expect(titles[0]).toBe('Navigation to /admin'); + expect(titles.at(-1)).toBe('GET /api/trips'); + expect(text(fixture)).toContain('9 of 9 events'); + const chart = host(fixture).querySelector('[role="img"]')!; + expect(chart.getAttribute('aria-label')).toContain('Router 2'); + expect(host(fixture).querySelectorAll('h2')[0].textContent).toBe('Time axis'); + }); + + it('turns a layer off and remembers it', async () => { + const fixture = await mount(wire(sources())); + const http = layerButton(fixture, 'HTTP'); + expect(http.getAttribute('aria-pressed')).toBe('true'); + http.click(); + await settle(fixture); + expect(layerButton(fixture, 'HTTP').getAttribute('aria-pressed')).toBe('false'); + expect(rows(fixture).some((row) => row.textContent!.includes('POST'))).toBe(false); + expect(text(fixture)).toContain('6 of 9 events'); + expect(storedHiddenLayers()).toEqual(['http']); + + const again = await mount(wire(sources())); + expect(layerButton(again, 'HTTP').getAttribute('aria-pressed')).toBe('false'); + }); + + it('hides the layer of an inspector that is turned off', async () => { + const fixture = TestBed.createComponent(Timeline); + fixture.componentRef.setInput( + 'rpc', + fakeClient(wire(sources()), { pangular: { inspectors: { ngrx: false } } }), + ); + await settle(fixture); + const labels = [...host(fixture).querySelectorAll('button.layer')].map((b) => + b.textContent!.trim(), + ); + expect(labels.some((label) => label.startsWith('NgRx'))).toBe(false); + }); + + it('moves through the list with the arrow keys and opens an event in its tab', async () => { + const fixture = await mount(wire(sources())); + const opened: unknown[] = []; + fixture.componentInstance.open.subscribe((link) => opened.push(link)); + const [first, second] = rows(fixture); + first.focus(); + first.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true })); + expect(document.activeElement).toBe(second); + second.click(); + await settle(fixture); + expect(second.getAttribute('aria-current')).toBe('true'); + const open = [...host(fixture).querySelectorAll('button')].find((b) => + b.textContent!.includes('Open in'), + )!; + expect(open.textContent!.trim()).toBe('Open in Signals'); + open.click(); + expect(opened).toEqual([{ tab: 'signals', page: 'p1' }]); + }); + + it('filters by text, says when nothing matches, and clears the filters', async () => { + const fixture = await mount(wire(sources())); + const search = host(fixture).querySelector('input[type="search"]')!; + search.value = 'no such thing'; + search.dispatchEvent(new Event('input')); + await settle(fixture); + expect(text(fixture)).toContain('No event matches the filters.'); + const clear = [...host(fixture).querySelectorAll('button')].find( + (b) => b.textContent!.trim() === 'Clear filters', + )!; + clear.click(); + await settle(fixture); + expect(rows(fixture)).toHaveLength(9); + }); + + it('zooms in from the toolbar and back out to everything', async () => { + const fixture = await mount(wire(sources())); + const button = (name: string) => + [...host(fixture).querySelectorAll('.zoom button')].find( + (b) => b.textContent!.trim() === name, + )!; + expect(button('Show all').disabled).toBe(true); + button('Zoom in').click(); + await settle(fixture); + expect(button('Show all').disabled).toBe(false); + expect(rows(fixture).length).toBeLessThan(9); + button('Show all').click(); + await settle(fixture); + expect(rows(fixture)).toHaveLength(9); + }); + + it('filters to one page', async () => { + const fixture = await mount(wire(sources())); + fixture.componentInstance.pageId.set('p2'); + await settle(fixture); + expect(rows(fixture).map((row) => row.querySelector('.title')!.textContent)).toEqual([ + 'GET /api/me', + ]); + }); + + it('renders a capped list for thousands of events and says so', async () => { + const log = Array.from({ length: 3_000 }, (_, i) => ({ + seq: i, + storeId: 's', + type: `tick ${i}`, + timestamp: i * 10, + diff: [], + })); + const fixture = await mount(wire({ ngrx: { pages: [{ pageId: 'p1', log }] } })); + expect(rows(fixture)).toHaveLength(TIMELINE_LIST_CAP); + expect(text(fixture)).toContain(`Showing the newest ${TIMELINE_LIST_CAP} of 3000 events`); + expect(host(fixture).querySelectorAll('rect.mark').length).toBeLessThanOrEqual(240); + }); +}); diff --git a/app/src/app.ts b/app/src/app.ts index 033d2b80..6f7dfb40 100644 --- a/app/src/app.ts +++ b/app/src/app.ts @@ -31,6 +31,7 @@ import { NetworkInspector } from './pages/network-inspector'; import { ComingSoon, type ComingSoonInfo } from './pages/coming-soon'; import { CapacitorInspector } from './pages/capacitor-inspector'; import { ErrorsInspector } from './pages/errors-inspector'; +import { Timeline } from './pages/timeline'; import { capacitorPages } from './pages/capacitor-types'; import { TabIcon } from './pages/tab-icon'; import { styleHubRail } from './hub-rail-style'; @@ -67,6 +68,8 @@ import { arrangeTabs, inPopup, isMacPlatform, + popoutOpener, + returnFromPopout, storePassive, storeTabLayout, storedPassive, @@ -208,6 +211,7 @@ function readView(): View | null { NetworkInspector, CapacitorInspector, ErrorsInspector, + Timeline, ComingSoon, TabIcon, Settings, @@ -499,6 +503,9 @@ function readView(): View | null { @case ('dashboard') { } + @case ('timeline') { + + } @case ('components') { { if (command.id.startsWith('tab:')) return available.has(command.id.slice(4)); + if (command.id === 'popout:return') return this.poppedOut; if (command.popupOnly) return this.popup; if (command.id === 'pick-component' || command.id.startsWith('cd-')) return components; return true; @@ -1439,10 +1449,22 @@ export class App implements OnInit, OnDestroy { if (theme === 'system' || theme === 'light' || theme === 'dark') this.setTheme(theme); return; } + if (id === 'dock:reset-size') { + this.postToPopup({ type: 'pangular:popup-dock-size' }); + return; + } if (id.startsWith('dock:')) { this.postToPopup({ type: 'pangular:popup-dock', mode: id.slice(5) }); return; } + if (id === 'popout') { + this.postToPopup({ type: 'pangular:popup-popout' }); + return; + } + if (id === 'popout:return') { + returnFromPopout(); + return; + } const kinds: Record = { 'pick-component': 'pick', 'cd-record-start': 'cd-start', diff --git a/app/src/pages/tab-icon.ts b/app/src/pages/tab-icon.ts index 50079c0d..1594a061 100644 --- a/app/src/pages/tab-icon.ts +++ b/app/src/pages/tab-icon.ts @@ -21,6 +21,12 @@ import { Component, input } from '@angular/core'; } + @case ('timeline') { + + + + + } @case ('components') { diff --git a/app/src/pages/timeline-data.ts b/app/src/pages/timeline-data.ts new file mode 100644 index 00000000..6a870520 --- /dev/null +++ b/app/src/pages/timeline-data.ts @@ -0,0 +1,634 @@ +import type { + CdReport, + PangularInspector, + PanelLink, + ResolvedPangularConfig, +} from '@pangular-inspector/devtools/config'; +import type { FormEvent } from './forms-types'; +import { pageOf } from './forms-types'; +import type { NavigationRecord, RouterPage } from './router-types'; +import type { NgrxLogEntry, NgrxPage } from './store-types'; + +/** One source of events on the timeline. Each has its own lane and toggle. */ +export type TimelineLayer = 'router' | 'http' | 'ngrx' | 'cd' | 'forms' | 'signals'; + +export interface TimelineLayerInfo { + id: TimelineLayer; + label: string; + /** The tab that shows the event in full. */ + tab: string; + tabLabel: string; + /** The inspector that collects it, so a layer goes away with its inspector. */ + inspector: PangularInspector; + /** The shared state the panel reads it from. */ + state: string; +} + +export const TIMELINE_LAYERS: readonly TimelineLayerInfo[] = [ + { + id: 'router', + label: 'Router', + tab: 'routes', + tabLabel: 'Routes', + inspector: 'router', + state: 'router', + }, + { + id: 'http', + label: 'HTTP', + tab: 'network', + tabLabel: 'SSR & HTTP', + inspector: 'http', + state: 'http', + }, + { + id: 'ngrx', + label: 'NgRx', + tab: 'store', + tabLabel: 'Store', + inspector: 'ngrx', + state: 'ngrx-store', + }, + { + id: 'cd', + label: 'Change detection', + tab: 'components', + tabLabel: 'Components', + inspector: 'components', + state: 'change-detection', + }, + { + id: 'forms', + label: 'Forms', + tab: 'forms', + tabLabel: 'Forms', + inspector: 'forms', + state: 'forms', + }, + { + id: 'signals', + label: 'Signals', + tab: 'signals', + tabLabel: 'Signals', + inspector: 'signals', + state: 'signal-graph', + }, +]; + +export const LAYER_INFO = Object.fromEntries( + TIMELINE_LAYERS.map((layer) => [layer.id, layer]), +) as Record; + +/** The layers whose inspector is on under `config`. */ +export function enabledLayers(config: ResolvedPangularConfig): TimelineLayerInfo[] { + return TIMELINE_LAYERS.filter((layer) => config.inspectors[layer.inspector]); +} + +export type TimelineTone = 'warn' | 'danger'; + +export interface TimelineEvent { + /** Unique over every layer, stable across updates. */ + key: string; + layer: TimelineLayer; + /** The page that reported it, or `''` for a server call no page claims. */ + pageId: string; + /** Start, in ms since the epoch. */ + at: number; + /** How long it took, when the source measures it. */ + ms?: number; + title: string; + detail: string; + tone?: TimelineTone; + /** Where the event shows in full: its tab, and the item when that tab can select one. */ + link: PanelLink; +} + +/** The latest value of each shared state, as the server sent it. */ +export type TimelineSources = Partial>; + +interface HttpCallLike { + id?: string; + url?: string; + method?: string; + status?: number; + durationMs?: number; + side?: string; + cacheHit?: boolean; + faulted?: boolean; + mocked?: boolean; + cancelled?: boolean; + error?: string; + pageUrl?: string; + at?: number; +} + +interface HttpPageLike { + pageId?: string; + url?: string; + initialUrl?: string; + title?: string; + calls?: HttpCallLike[]; +} + +interface SignalChangeLike { + epoch?: number; + value?: unknown; + at?: number; + source?: string; +} + +interface SignalGraphLike { + pageId?: string; + nodes?: { id?: string; label?: string; kind?: string }[]; + history?: Record; +} + +function list(value: unknown): T[] { + return Array.isArray(value) ? (value as T[]) : []; +} + +function record(value: unknown): Record { + return value && typeof value === 'object' && !Array.isArray(value) + ? (value as Record) + : {}; +} + +function pagesOf(state: unknown): T[] { + const pages = record(state)['pages']; + if (Array.isArray(pages)) return pages as T[]; + return Object.values(record(pages)) as T[]; +} + +function finite(value: unknown): value is number { + return typeof value === 'number' && Number.isFinite(value); +} + +/** A URL without its origin, so lists stay short. */ +export function pathOf(url: string): string { + try { + const parsed = new URL(url, 'http://x'); + return url.startsWith('/') || parsed.host === 'x' + ? `${parsed.pathname}${parsed.search}` + : url.replace(/^[a-z]+:\/\/[^/]+/i, '') || url; + } catch { + return url; + } +} + +function ms(value: number): string { + return value < 10 ? `${Math.round(value * 10) / 10} ms` : `${Math.round(value)} ms`; +} + +function preview(value: unknown): string { + let text: string; + try { + text = typeof value === 'string' ? JSON.stringify(value) : (JSON.stringify(value) ?? ''); + } catch { + text = String(value); + } + return text.length > 80 ? `${text.slice(0, 79)}…` : text; +} + +function routerEvents(state: unknown): TimelineEvent[] { + const events: TimelineEvent[] = []; + for (const page of pagesOf(state)) { + const pageId = typeof page?.pageId === 'string' ? page.pageId : ''; + for (const nav of list(page?.navigations)) { + // A navigation from before the overlay connected has no real start time, + // which the Routes tab doesn't show either. + if (!finite(nav?.startedAt) || nav.probe || nav.beforeConnect) continue; + const took = finite(nav.endedAt) ? Math.max(0, nav.endedAt - nav.startedAt) : undefined; + const to = nav.finalUrl && nav.finalUrl !== nav.url ? ` to ${nav.finalUrl}` : ''; + events.push({ + key: `router:${pageId}:${nav.id}`, + layer: 'router', + pageId, + at: nav.startedAt, + ...(took !== undefined ? { ms: took } : {}), + title: `Navigation to ${nav.url}`, + detail: [`${nav.outcome}${to}`, nav.trigger, took !== undefined ? ms(took) : ''] + .filter(Boolean) + .join(' · '), + ...(nav.outcome === 'failed' + ? { tone: 'danger' as const } + : nav.outcome === 'cancelled' || nav.outcome === 'redirected' + ? { tone: 'warn' as const } + : {}), + link: { tab: 'routes', ...(pageId ? { page: pageId } : {}) }, + }); + } + } + return events; +} + +function httpTone(call: HttpCallLike): TimelineTone | undefined { + const status = call.status ?? 0; + if (call.error || status >= 500 || (status === 0 && !call.cancelled)) return 'danger'; + if (status >= 400 || call.faulted || call.cancelled) return 'warn'; + return undefined; +} + +function httpEvent(call: HttpCallLike, pageId: string): TimelineEvent | null { + if (!finite(call?.at)) return null; + const side = call.side === 'server' ? 'SSR' : 'client'; + const status = call.status ? String(call.status) : call.cancelled ? 'cancelled' : 'no response'; + const took = finite(call.durationMs) ? call.durationMs : undefined; + const tone = httpTone(call); + return { + key: `http:${call.side === 'server' ? 'server' : pageId}:${call.id ?? call.at}`, + layer: 'http', + pageId, + at: call.at, + ...(took !== undefined ? { ms: took } : {}), + title: `${call.method ?? 'GET'} ${pathOf(call.url ?? '')}`, + detail: [ + status, + side, + took !== undefined ? ms(took) : '', + call.cacheHit ? 'from cache' : '', + call.mocked ? 'mocked' : call.faulted ? 'fault rule' : '', + ] + .filter(Boolean) + .join(' · '), + ...(tone ? { tone } : {}), + link: { tab: 'network', ...(pageId ? { page: pageId } : {}) }, + }; +} + +function httpEvents(state: unknown): TimelineEvent[] { + const events: TimelineEvent[] = []; + const pages = pagesOf(state); + for (const page of pages) { + const pageId = typeof page?.pageId === 'string' ? page.pageId : ''; + for (const call of list(page?.calls)) { + const event = httpEvent(call, pageId); + if (event) events.push(event); + } + } + // A server call belongs to the page its SSR request rendered, when one says so. + for (const call of list(record(state)['serverCalls'])) { + const owner = call?.pageUrl + ? pages.find((page) => page?.initialUrl && page.initialUrl === call.pageUrl) + : undefined; + const event = httpEvent({ ...call, side: 'server' }, owner?.pageId ?? ''); + if (event) events.push(event); + } + return events; +} + +function ngrxEvents(state: unknown): TimelineEvent[] { + const events: TimelineEvent[] = []; + for (const page of pagesOf(state)) { + const pageId = typeof page?.pageId === 'string' ? page.pageId : ''; + for (const entry of list(page?.log)) { + if (!finite(entry?.timestamp)) continue; + const changes = list(entry.diff).length + (entry.diffTruncated ?? 0); + const took = finite(entry.durationMs) ? entry.durationMs : undefined; + events.push({ + key: `ngrx:${pageId}:${entry.seq}`, + layer: 'ngrx', + pageId, + at: entry.timestamp, + ...(took !== undefined ? { ms: took } : {}), + title: entry.source === 'event' ? (entry.eventType ?? entry.type) : entry.type, + detail: [ + entry.storeId, + entry.source === 'event' + ? 'event' + : changes + ? `${changes} ${changes === 1 ? 'change' : 'changes'}` + : 'no change', + took !== undefined ? ms(took) : '', + ] + .filter(Boolean) + .join(' · '), + link: { tab: 'store', ...(pageId ? { page: pageId } : {}) }, + }); + } + } + return events; +} + +/** A change detection cycle past one frame (16 ms) is marked slow. */ +const SLOW_CYCLE_MS = 16; + +function cdEvents(state: unknown): TimelineEvent[] { + const events: TimelineEvent[] = []; + for (const page of pagesOf(state)) { + const pageId = typeof page?.pageId === 'string' ? page.pageId : ''; + for (const cycle of list(page?.cycles)) { + if (!finite(cycle?.at)) continue; + const took = finite(cycle.ms) ? cycle.ms : 0; + events.push({ + key: `cd:${pageId}:${cycle.id}`, + layer: 'cd', + pageId, + at: cycle.at, + ms: took, + title: `Change detection cycle ${cycle.id}`, + detail: [ + ms(took), + `${cycle.checks} ${cycle.checks === 1 ? 'check' : 'checks'}`, + cycle.trigger ? `after ${cycle.trigger}` : '', + ] + .filter(Boolean) + .join(' · '), + ...(took > SLOW_CYCLE_MS ? { tone: 'warn' as const } : {}), + link: { tab: 'components', ...(pageId ? { page: pageId } : {}) }, + }); + } + } + return events; +} + +function formsEvents(state: unknown): TimelineEvent[] { + return list(record(state)['events']).flatMap((event, index) => { + if (!finite(event?.timestamp) || typeof event.formId !== 'string') return []; + const pageId = pageOf(event.formId); + const detail = [event.detail, event.origin, event.outcome].filter(Boolean).join(' · '); + return [ + { + key: `forms:${event.formId}:${event.seq ?? `i${index}`}`, + layer: 'forms' as const, + pageId, + at: event.timestamp, + ...(finite(event.ms) ? { ms: event.ms } : {}), + title: `${event.type} ${event.path || 'form'}`, + detail, + ...(event.type === 'submit' && event.outcome === 'invalid' + ? { tone: 'warn' as const } + : {}), + link: { tab: 'forms', id: event.formId, ...(pageId ? { page: pageId } : {}) }, + }, + ]; + }); +} + +const SIGNAL_SOURCE: Record = { write: 'set', sample: 'sampled' }; + +function signalEvents(state: unknown): TimelineEvent[] { + const events: TimelineEvent[] = []; + const pages = record(record(state)['pages']); + // Older servers send only the newest graph. + const graphs: [string, unknown][] = Object.keys(pages).length + ? Object.entries(pages) + : [['', record(state)['graph']]]; + for (const [key, value] of graphs) { + const graph = record(value) as SignalGraphLike; + const pageId = typeof graph.pageId === 'string' ? graph.pageId : String(key); + const names = new Map( + list<{ id?: string; label?: string; kind?: string }>(graph.nodes).map((node) => [ + node?.id, + node?.label || node?.kind || 'signal', + ]), + ); + for (const [nodeId, changes] of Object.entries(record(graph.history))) { + for (const change of list(changes)) { + // The first value is not a change; it only says where history starts. + if (!finite(change?.at) || change.source === 'initial') continue; + const name = names.get(nodeId) ?? nodeId; + events.push({ + key: `signals:${pageId}:${nodeId}:${change.epoch ?? change.at}`, + layer: 'signals', + pageId, + at: change.at, + title: `${name} ${SIGNAL_SOURCE[change.source ?? ''] ?? 'changed'}`, + detail: preview(change.value), + link: { tab: 'signals', ...(pageId ? { page: pageId } : {}) }, + }); + } + } + } + return events; +} + +const READERS: Record TimelineEvent[]> = { + router: routerEvents, + http: httpEvents, + ngrx: ngrxEvents, + cd: cdEvents, + forms: formsEvents, + signals: signalEvents, +}; + +/** + * Every event the sources hold, on one time axis, oldest first. Events at the + * same time keep the layer order, so the list doesn't reshuffle on an update. + */ +export function mergeTimeline(sources: TimelineSources): TimelineEvent[] { + const events: TimelineEvent[] = []; + for (const layer of TIMELINE_LAYERS) { + const state = sources[layer.id]; + if (state !== undefined && state !== null) events.push(...READERS[layer.id](state)); + } + const order = new Map(TIMELINE_LAYERS.map((layer, index) => [layer.id, index])); + events.sort( + (a, b) => + a.at - b.at || + order.get(a.layer)! - order.get(b.layer)! || + (a.key < b.key ? -1 : a.key > b.key ? 1 : 0), + ); + // A source that repeats an id must not break the list, which tracks by key. + const seen = new Set(); + for (const event of events) { + let key = event.key; + for (let n = 2; seen.has(key); n++) key = `${event.key}#${n}`; + event.key = key; + seen.add(key); + } + return events; +} + +export interface TimelinePage { + pageId: string; + label: string; +} + +/** The pages any source reports, labelled by the best name a source gives. */ +export function timelinePages(sources: TimelineSources, events: TimelineEvent[]): TimelinePage[] { + const labels = new Map(); + const name = (pageId: unknown, label: unknown) => { + if (typeof pageId !== 'string' || !pageId) return; + if (typeof label === 'string' && label && !labels.get(pageId)) labels.set(pageId, label); + else if (!labels.has(pageId)) labels.set(pageId, ''); + }; + for (const page of pagesOf(sources.http)) + name(page?.pageId, page?.title || page?.url); + for (const page of pagesOf(sources.router)) name(page?.pageId, page?.snapshot?.url); + for (const page of pagesOf(sources.ngrx)) name(page?.pageId, page?.title || page?.url); + for (const event of events) name(event.pageId, ''); + return [...labels].map(([pageId, label]) => ({ pageId, label: label || pageId })); +} + +export const ALL_PAGES = 'all'; + +export interface TimeRange { + start: number; + end: number; +} + +export interface TimelineFilter { + layers: ReadonlySet; + /** A `pageId`, or `ALL_PAGES`. */ + pageId: string; + query: string; +} + +/** The events on the visible layers and the picked page that match the query. */ +export function filterTimeline(events: TimelineEvent[], filter: TimelineFilter): TimelineEvent[] { + const query = filter.query.trim().toLowerCase(); + return events.filter( + (event) => + filter.layers.has(event.layer) && + (filter.pageId === ALL_PAGES || event.pageId === filter.pageId) && + (!query || + event.title.toLowerCase().includes(query) || + event.detail.toLowerCase().includes(query)), + ); +} + +/** The events that start inside `range`; all of them without one. */ +export function inRange(events: TimelineEvent[], range: TimeRange | null): TimelineEvent[] { + if (!range) return events; + return events.filter((event) => event.at >= range.start && event.at <= range.end); +} + +/** The time the events cover, from the first start to the last end. */ +export function extentOf(events: TimelineEvent[]): TimeRange | null { + if (!events.length) return null; + let start = Infinity; + let end = -Infinity; + for (const event of events) { + start = Math.min(start, event.at); + end = Math.max(end, event.at + (event.ms ?? 0)); + } + // A single instant still needs some width to draw. + return end - start < MIN_SPAN + ? { start: start - MIN_SPAN / 2, end: start + MIN_SPAN / 2 } + : { start, end }; +} + +/** The narrowest range zooming goes to, in ms. */ +export const MIN_SPAN = 20; + +/** Zooms around the middle of `range` by `factor` (below 1 zooms in), inside `bounds`. */ +export function zoomRange(range: TimeRange, factor: number, bounds: TimeRange): TimeRange | null { + const span = Math.max(MIN_SPAN, (range.end - range.start) * factor); + if (span >= bounds.end - bounds.start) return null; + const middle = (range.start + range.end) / 2; + return clampRange({ start: middle - span / 2, end: middle + span / 2 }, bounds); +} + +/** Moves `range` by a share of its width (negative is earlier), inside `bounds`. */ +export function panRange(range: TimeRange, share: number, bounds: TimeRange): TimeRange { + const shift = (range.end - range.start) * share; + return clampRange({ start: range.start + shift, end: range.end + shift }, bounds); +} + +function clampRange(range: TimeRange, bounds: TimeRange): TimeRange { + const span = range.end - range.start; + if (range.start < bounds.start) return { start: bounds.start, end: bounds.start + span }; + if (range.end > bounds.end) return { start: bounds.end - span, end: bounds.end }; + return range; +} + +/** The range between two points of the chart, as shares of its width (0 to 1). */ +export function rangeBetween(view: TimeRange, from: number, to: number): TimeRange | null { + const [a, b] = from < to ? [from, to] : [to, from]; + const clamp = (share: number) => Math.min(1, Math.max(0, share)); + const span = view.end - view.start; + const start = view.start + clamp(a) * span; + const end = view.start + clamp(b) * span; + return end - start < MIN_SPAN ? null : { start, end }; +} + +export interface TimelineBin { + layer: TimelineLayer; + /** Index of the first slot the bin covers. */ + slot: number; + count: number; + tone?: TimelineTone; +} + +/** + * Groups the events of each layer into `slots` equal slices of `view`, so the + * chart draws at most one mark per slice and layer however many events there + * are. A bin takes the worst tone of its events. + */ +export function binTimeline( + events: TimelineEvent[], + view: TimeRange, + slots: number, +): TimelineBin[] { + const span = Math.max(1, view.end - view.start); + const bins = new Map(); + for (const event of events) { + if (event.at < view.start || event.at > view.end) continue; + const slot = Math.min(slots - 1, Math.floor(((event.at - view.start) / span) * slots)); + const id = `${event.layer}:${slot}`; + let bin = bins.get(id); + if (!bin) { + bin = { layer: event.layer, slot, count: 0 }; + bins.set(id, bin); + } + bin.count++; + if (event.tone === 'danger' || (event.tone === 'warn' && bin.tone !== 'danger')) { + bin.tone = event.tone; + } + } + return [...bins.values()]; +} + +/** The event of `layer` that starts closest to `at`. */ +export function nearestEvent( + events: TimelineEvent[], + layer: TimelineLayer, + at: number, +): TimelineEvent | null { + let best: TimelineEvent | null = null; + for (const event of events) { + if (event.layer !== layer) continue; + if (!best || Math.abs(event.at - at) < Math.abs(best.at - at)) best = event; + } + return best; +} + +const HIDDEN_KEY = 'pangular-timeline-hidden-layers'; + +/** The layers the viewer turned off, kept across reloads. */ +export function storedHiddenLayers(): TimelineLayer[] { + try { + const value: unknown = JSON.parse(localStorage.getItem(HIDDEN_KEY) ?? '[]'); + return Array.isArray(value) + ? value.filter((layer): layer is TimelineLayer => + TIMELINE_LAYERS.some((info) => info.id === layer), + ) + : []; + } catch { + return []; + } +} + +export function storeHiddenLayers(layers: readonly TimelineLayer[]) { + try { + localStorage.setItem(HIDDEN_KEY, JSON.stringify(layers)); + } catch { + // Blocked storage only forgets the layers on the next reload. + } +} + +/** A wall clock time with milliseconds, which events close together need. */ +export function clockTime(at: number): string { + const date = new Date(at); + const pad = (value: number, size = 2) => String(value).padStart(size, '0'); + return `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}.${pad(date.getMilliseconds(), 3)}`; +} + +/** A span of time in the unit that reads best. */ +export function spanText(ms: number): string { + if (ms < 1000) return `${Math.round(ms)} ms`; + if (ms < 60_000) return `${Math.round(ms / 100) / 10} s`; + const minutes = Math.floor(ms / 60_000); + const seconds = Math.round((ms % 60_000) / 1000); + return seconds ? `${minutes} min ${seconds} s` : `${minutes} min`; +} diff --git a/app/src/pages/timeline.ts b/app/src/pages/timeline.ts new file mode 100644 index 00000000..cf02b282 --- /dev/null +++ b/app/src/pages/timeline.ts @@ -0,0 +1,1000 @@ +import { + Component, + DestroyRef, + ElementRef, + computed, + effect, + inject, + input, + linkedSignal, + output, + signal, + viewChild, +} from '@angular/core'; +import type { PanelLink } from '@pangular-inspector/devtools/config'; +import type { DevframeRpcClient } from 'devframe/client'; +import { panelConfig } from '../devtools-config'; +import { hostPageId } from '../page-id'; +import { RovingList } from '../ui/roving-list'; +import { Select, type SelectOption } from '../ui/select'; +import { + ALL_PAGES, + LAYER_INFO, + TIMELINE_LAYERS, + binTimeline, + clockTime, + enabledLayers, + extentOf, + filterTimeline, + inRange, + mergeTimeline, + nearestEvent, + panRange, + rangeBetween, + spanText, + storeHiddenLayers, + storedHiddenLayers, + timelinePages, + zoomRange, + type TimeRange, + type TimelineEvent, + type TimelineLayer, + type TimelineSources, +} from './timeline-data'; + +/** How many rows the list renders, newest first. The chart always draws every event. */ +export const TIMELINE_LIST_CAP = 200; +/** Slices of the chart's width; each layer draws at most one mark per slice. */ +const SLOTS = 240; +/** Width of the chart's drawing, in its own units. */ +const CHART_WIDTH = 1000; +/** A pointer that moves less than this many pixels clicks rather than selects a range. */ +const DRAG_THRESHOLD = 4; + +@Component({ + selector: 'app-timeline', + imports: [Select, RovingList], + template: ` +

+ Navigations, HTTP calls, NgRx actions, change detection cycles, form events and signal changes + from the live page on one time axis. Turn layers on and off, drag across the chart to look at + a range, and open an event in its own tab. +

+ @if (loading()) { +

Loading the timeline…

+ } @else if (failed()) { + + } @else { +
+ + @if (pageOptions().length > 2) { + + } + {{ countText() }} +
+
+
+ @for (layer of layers(); track layer.id) { + + } +
+
+ + + + + +
+
+ + @if (!events().length) { +
+

Nothing on the timeline yet.

+

+ Use the app with the overlay loaded. Navigations, HTTP calls, NgRx actions and form + events show up as they happen. Record change detection on the Components tab, and pick a + component on the Signals tab to follow its signals. +

+
+ } @else if (!shown().length) { +
+

No event matches the filters{{ range() ? ' in this range' : '' }}.

+ +
+ } @else { +
+
+

Time axis

+ {{ viewText() }} +
+
+ + +
+ +

+ Drag across the chart to look at a range, and click a mark to select the nearest event. + The list below holds the same events. +

+
+ +
+
+

Events

+ @if (shown().length > listed().length) { +

+ Showing the newest {{ listed().length }} of {{ shown().length }} events. Zoom in or + filter to see older ones. +

+ } +
    + @for (event of listed(); track event.key) { +
  1. + +
  2. + } +
+
+ +
+ } + } + `, + styles: ` + @use 'mixins' as m; + + :host { + display: block; + font-size: 13px; + } + h2 { + margin: 0; + @include m.label; + } + .intro { + margin: 0 0 12px; + color: var(--text-2); + line-height: 1.5; + } + .muted { + color: var(--text-2); + } + .toolbar { + position: sticky; + top: 0; + z-index: 2; + display: flex; + flex-wrap: wrap; + gap: 8px 12px; + align-items: center; + margin: 0 0 8px; + padding: 8px; + background: color-mix(in srgb, var(--surface) 85%, transparent); + backdrop-filter: blur(10px); + border: 1px solid var(--border); + border-radius: var(--radius); + } + .search { + position: relative; + flex: 1 1 200px; + min-width: 0; + } + .search-icon { + position: absolute; + top: 50%; + left: 12px; + width: 14px; + height: 14px; + transform: translateY(-50%); + fill: none; + stroke: var(--text-3); + stroke-width: 2.2; + stroke-linecap: round; + pointer-events: none; + } + input[type='search'] { + width: 100%; + height: var(--control-h); + padding: 0 12px 0 34px; + background: var(--bg); + border: 1px solid var(--border-strong); + border-radius: var(--radius-sm); + color: var(--text); + font-size: 13px; + } + input[type='search']:focus { + @include m.field-focus; + } + .page-select { + flex: 0 1 200px; + min-width: 0; + } + .count { + margin-left: auto; + color: var(--text-2); + font-size: 12px; + font-variant-numeric: tabular-nums; + } + .controls { + display: flex; + flex-wrap: wrap; + gap: 8px 16px; + align-items: center; + justify-content: space-between; + margin: 0 0 12px; + } + .layers, + .zoom { + display: flex; + flex-wrap: wrap; + gap: 6px; + } + .layer { + display: inline-flex; + gap: 6px; + align-items: center; + height: var(--control-h); + padding: 0 10px; + border: 1px solid var(--border); + border-radius: 99px; + background: var(--surface); + color: var(--text-2); + font: inherit; + cursor: pointer; + transition: background-color 150ms var(--ease); + } + .layer:hover { + background: var(--surface-2); + } + .layer[aria-pressed='true'] { + border-color: var(--accent-line); + background: var(--accent-soft); + color: var(--text-strong); + } + .layer:focus-visible { + @include m.focus-ring; + } + .check { + width: 12px; + height: 12px; + border: 1.5px solid var(--border-strong); + border-radius: 3px; + } + .layer[aria-pressed='true'] .check { + border-color: var(--accent); + background: var(--accent); + box-shadow: inset 0 0 0 2px var(--surface); + } + .badge { + color: var(--text-2); + font-variant-numeric: tabular-nums; + } + .small, + .primary { + height: var(--control-h); + padding: 0 12px; + border: 1px solid var(--border-strong); + border-radius: var(--radius-sm); + background: var(--surface-2); + color: var(--text); + font: inherit; + cursor: pointer; + } + .small:disabled { + color: var(--text-3); + cursor: default; + } + .primary { + border-color: var(--accent); + background: var(--accent); + color: var(--accent-ink); + font-weight: 600; + } + .primary:hover { + background: var(--accent-hover); + } + .small:focus-visible, + .primary:focus-visible { + @include m.focus-ring; + } + .state { + margin: 0 0 12px; + color: var(--text-2); + } + .state p { + margin: 0 0 8px; + } + .chart-card { + @include m.panel; + margin: 0 0 12px; + padding: 10px 12px; + } + .chart-head { + display: flex; + flex-wrap: wrap; + gap: 4px 12px; + align-items: baseline; + justify-content: space-between; + margin: 0 0 8px; + font-size: 12px; + } + .chart { + display: grid; + grid-template-columns: auto minmax(0, 1fr); + gap: 8px; + } + .lane-labels { + margin: 0; + padding: 0; + list-style: none; + } + .lane-labels li { + height: 24px; + color: var(--text-2); + font-size: 11.5px; + line-height: 24px; + white-space: nowrap; + } + .tracks { + position: relative; + min-width: 0; + border-radius: var(--radius-sm); + background: var(--bg); + cursor: crosshair; + touch-action: none; + user-select: none; + } + .tracks svg { + display: block; + width: 100%; + height: 100%; + } + .lane-line { + stroke: var(--border); + stroke-width: 1; + vector-effect: non-scaling-stroke; + } + /* Plain events are neutral, so warnings stand out in every accent. */ + .mark { + fill: var(--text-2); + } + .mark[data-tone='warn'] { + fill: var(--warn); + } + .mark[data-tone='danger'] { + fill: var(--danger); + } + .selected-mark { + fill: none; + stroke: var(--text-strong); + stroke-width: 2; + vector-effect: non-scaling-stroke; + } + .brush { + position: absolute; + top: 0; + bottom: 0; + border: 1px solid var(--accent-line); + background: var(--accent-soft); + pointer-events: none; + } + .axis { + display: flex; + justify-content: space-between; + margin: 4px 0 0; + color: var(--text-2); + font-size: 11.5px; + font-variant-numeric: tabular-nums; + } + .hint { + margin: 6px 0 0; + color: var(--text-2); + font-size: 12px; + } + .split { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(240px, 320px); + gap: 12px; + align-items: start; + } + @media (max-width: 880px) { + .split { + grid-template-columns: minmax(0, 1fr); + } + } + .events h2, + .detail-panel h2 { + margin: 0 0 8px; + } + .note { + margin: 0 0 8px; + padding: 8px 12px; + border: 1px solid var(--border); + border-radius: var(--radius-sm); + background: var(--surface); + color: var(--text-2); + line-height: 1.5; + } + .rows { + display: grid; + margin: 0; + padding: 0; + list-style: none; + border: 1px solid var(--border); + border-radius: var(--radius); + overflow: hidden; + } + .rows li + li { + border-top: 1px solid var(--border); + } + .row { + display: flex; + flex-wrap: wrap; + gap: 2px 10px; + align-items: baseline; + width: 100%; + min-width: 0; + padding: 6px 10px; + border: 0; + background: var(--surface); + color: var(--text); + font: inherit; + text-align: left; + cursor: pointer; + } + .row:hover { + background: var(--surface-2); + } + .row.selected { + background: var(--accent-soft); + box-shadow: inset 3px 0 0 var(--accent); + } + .row:focus-visible { + @include m.focus-ring(-2px); + } + .when { + color: var(--text-2); + font-size: 12px; + font-variant-numeric: tabular-nums; + } + .chip { + display: inline-flex; + align-items: center; + padding: 0 8px; + border: 1px solid transparent; + border-radius: 99px; + font-size: 11px; + font-weight: 600; + border-color: var(--border); + background: var(--surface-2); + color: var(--text-2); + } + .chip[data-tone='warn'] { + @include m.tint(warn); + } + .chip[data-tone='danger'] { + @include m.tint(danger); + } + .title { + flex: 1 1 200px; + min-width: 0; + color: var(--text-strong); + font-family: var(--font-mono); + font-size: 12px; + overflow-wrap: anywhere; + } + .detail, + .page { + color: var(--text-2); + font-size: 12px; + overflow-wrap: anywhere; + } + .detail-panel { + @include m.panel; + position: sticky; + top: 64px; + display: grid; + gap: 8px; + min-width: 0; + padding: 12px; + } + .detail-title { + margin: 0; + color: var(--text-strong); + font-family: var(--font-mono); + font-size: 12.5px; + overflow-wrap: anywhere; + } + dl { + display: grid; + grid-template-columns: auto minmax(0, 1fr); + gap: 4px 12px; + margin: 0; + } + dt { + color: var(--text-2); + } + dd { + margin: 0; + overflow-wrap: anywhere; + } + .mono { + font-family: var(--font-mono); + font-size: 12px; + } + .detail-panel p { + margin: 0; + } + @media (prefers-reduced-motion: reduce) { + .layer { + transition: none; + } + } + `, +}) +export class Timeline { + readonly rpc = input(null); + /** Opens an event in its own tab, through the panel's deep links. */ + readonly open = output(); + + private readonly destroyRef = inject(DestroyRef); + private readonly tracks = viewChild>('tracks'); + private readonly host = inject>(ElementRef); + private readonly hostPageId = hostPageId(); + private cleanups: (() => void)[] = []; + private loads = 0; + + protected readonly allPages = ALL_PAGES; + protected readonly clock = clockTime; + protected readonly span = spanText; + protected readonly laneHeight = 24; + protected readonly chartWidth = CHART_WIDTH; + protected readonly slotWidth = CHART_WIDTH / SLOTS; + + readonly loading = signal(true); + readonly failed = signal(false); + readonly sources = signal({}); + readonly query = signal(''); + readonly hidden = signal(storedHiddenLayers()); + /** The range the viewer zoomed into, or `null` for everything, which follows new events. */ + readonly range = signal(null); + readonly selectedKey = signal(null); + /** The range being dragged on the chart, as shares of its width. */ + readonly brush = signal<{ from: number; to: number; id: number } | null>(null); + + /** The layers of the inspectors that are on. */ + readonly layers = computed(() => enabledLayers(panelConfig(this.rpc()))); + readonly events = computed(() => { + const on = new Set(this.layers().map((layer) => layer.id)); + return mergeTimeline(this.sources()).filter((event) => on.has(event.layer)); + }); + readonly pages = computed(() => timelinePages(this.sources(), this.events())); + /** Keeps the picked page while it reports, else this tab's page, else all pages. */ + readonly pageId = linkedSignal({ + source: () => this.pages().map((page) => page.pageId), + computation: (pages, previous) => { + if (previous && (previous.value === ALL_PAGES || pages.includes(previous.value))) { + return previous.value; + } + return this.hostPageId && pages.includes(this.hostPageId) ? this.hostPageId : ALL_PAGES; + }, + }); + readonly pageOptions = computed(() => [ + { value: ALL_PAGES, label: 'All pages' }, + ...this.pages().map((page) => ({ + value: page.pageId, + label: page.label, + ...(page.label !== page.pageId ? { hint: page.pageId } : {}), + })), + ]); + readonly showPage = computed(() => this.pageId() === ALL_PAGES && this.pages().length > 1); + + /** Events on the picked page that match the query, on every layer. */ + private readonly matching = computed(() => + filterTimeline(this.events(), { + layers: new Set(TIMELINE_LAYERS.map((layer) => layer.id)), + pageId: this.pageId(), + query: this.query(), + }), + ); + readonly layerCounts = computed(() => { + const counts = Object.fromEntries(TIMELINE_LAYERS.map((layer) => [layer.id, 0])) as Record< + TimelineLayer, + number + >; + for (const event of inRange(this.matching(), this.range())) counts[event.layer]++; + return counts; + }); + /** The events on the visible layers, before the range. */ + private readonly visible = computed(() => { + const hidden = new Set(this.hidden()); + return this.matching().filter((event) => !hidden.has(event.layer)); + }); + readonly lanes = computed(() => { + const hidden = new Set(this.hidden()); + return this.layers().filter((layer) => !hidden.has(layer.id)); + }); + readonly laneIndex = computed( + () => + Object.fromEntries(this.lanes().map((layer, index) => [layer.id, index])) as Record< + TimelineLayer, + number + >, + ); + readonly bounds = computed(() => extentOf(this.visible())); + readonly view = computed(() => this.range() ?? this.bounds()); + readonly shown = computed(() => inRange(this.visible(), this.range())); + readonly listed = computed(() => this.shown().slice(-TIMELINE_LIST_CAP).reverse()); + readonly bins = computed(() => { + const view = this.view(); + return view ? binTimeline(this.shown(), view, SLOTS) : []; + }); + private readonly maxBin = computed(() => + this.bins().reduce((max, bin) => Math.max(max, bin.count), 1), + ); + readonly selected = computed( + () => this.events().find((event) => event.key === this.selectedKey()) ?? null, + ); + readonly selectedMark = computed(() => { + const event = this.selected(); + const view = this.view(); + const lane = event ? this.laneIndex()[event.layer] : undefined; + if (!event || !view || lane === undefined || event.at < view.start || event.at > view.end) { + return null; + } + const span = Math.max(1, view.end - view.start); + const slot = Math.min(SLOTS - 1, Math.floor(((event.at - view.start) / span) * SLOTS)); + return { x: slot * this.slotWidth, y: lane * this.laneHeight }; + }); + readonly brushBox = computed(() => { + const brush = this.brush(); + if (!brush) return null; + const from = Math.min(brush.from, brush.to); + const to = Math.max(brush.from, brush.to); + return { left: from * 100, width: (to - from) * 100 }; + }); + + readonly countText = computed(() => { + const total = this.events().length; + return `${this.shown().length} of ${total} ${total === 1 ? 'event' : 'events'}`; + }); + readonly viewText = computed(() => { + const view = this.view(); + if (!view) return ''; + const span = spanText(view.end - view.start); + return this.range() ? `Range of ${span}` : `All ${span}`; + }); + readonly chartLabel = computed(() => { + const view = this.view(); + if (!view) return 'Timeline'; + const counts = this.layerCounts(); + const parts = this.lanes().map((layer) => `${layer.label} ${counts[layer.id]}`); + return ( + `Timeline from ${clockTime(view.start)} to ${clockTime(view.end)}: ` + + `${parts.join(', ')}. The Events list holds the same events.` + ); + }); + readonly canZoomIn = computed(() => { + const view = this.view(); + return !!view && view.end - view.start > 40; + }); + + constructor() { + effect(() => { + const client = this.rpc(); + if (client) void this.load(client); + }); + this.destroyRef.onDestroy(() => this.stop()); + } + + layerLabel(layer: TimelineLayer): string { + return LAYER_INFO[layer].label; + } + + tabLabel(layer: TimelineLayer): string { + return LAYER_INFO[layer].tabLabel; + } + + pageLabel(pageId: string): string { + if (!pageId) return 'Server'; + return this.pages().find((page) => page.pageId === pageId)?.label ?? pageId; + } + + binOpacity(count: number): number { + // Denser slices read darker, but one event stays clearly visible. + return ( + Math.round( + (0.55 + 0.45 * Math.min(1, Math.log2(count + 1) / Math.log2(this.maxBin() + 1))) * 100, + ) / 100 + ); + } + + toggleLayer(layer: TimelineLayer) { + const hidden = this.hidden().includes(layer) + ? this.hidden().filter((other) => other !== layer) + : [...this.hidden(), layer]; + this.hidden.set(hidden); + storeHiddenLayers(hidden); + } + + clearFilters() { + this.query.set(''); + this.range.set(null); + this.hidden.set([]); + storeHiddenLayers([]); + this.pageId.set(ALL_PAGES); + } + + zoom(factor: number) { + const view = this.view(); + const bounds = this.bounds(); + if (!view || !bounds) return; + this.range.set(zoomRange(view, factor, bounds)); + } + + canPan(direction: -1 | 1): boolean { + const range = this.range(); + const bounds = this.bounds(); + if (!range || !bounds) return false; + return direction < 0 ? range.start > bounds.start : range.end < bounds.end; + } + + pan(direction: -1 | 1) { + const range = this.range(); + const bounds = this.bounds(); + if (range && bounds) this.range.set(panRange(range, direction * 0.5, bounds)); + } + + select(event: TimelineEvent) { + this.selectedKey.set(event.key); + } + + openEvent(event: TimelineEvent) { + this.open.emit(event.link); + } + + /** Where a pointer is across the chart, from 0 at the left edge to 1 at the right. */ + private share(event: PointerEvent): number { + const box = this.tracks()?.nativeElement.getBoundingClientRect(); + if (!box || box.width <= 0) return 0; + return Math.min(1, Math.max(0, (event.clientX - box.left) / box.width)); + } + + onPointerDown(event: PointerEvent) { + if (event.pointerType === 'mouse' && event.button !== 0) return; + const at = this.share(event); + this.brush.set({ from: at, to: at, id: event.pointerId }); + this.downX = event.clientX; + try { + (event.currentTarget as Element).setPointerCapture(event.pointerId); + } catch { + // the pointer is gone; the drag ends with it + } + event.preventDefault(); + } + + onPointerMove(event: PointerEvent) { + const brush = this.brush(); + if (!brush || brush.id !== event.pointerId) return; + this.brush.set({ ...brush, to: this.share(event) }); + } + + onPointerUp(event: PointerEvent) { + const brush = this.brush(); + const view = this.view(); + this.brush.set(null); + if (!brush || brush.id !== event.pointerId || !view) return; + if (Math.abs(event.clientX - this.downX) < DRAG_THRESHOLD) { + this.pick(event, view); + return; + } + const range = rangeBetween(view, brush.from, this.share(event)); + if (range) this.range.set(range); + } + + private downX = 0; + + /** Selects the event nearest to a click, on the lane under the pointer. */ + private pick(event: PointerEvent, view: TimeRange) { + const box = this.tracks()?.nativeElement.getBoundingClientRect(); + const lanes = this.lanes(); + if (!box || !lanes.length) return; + const lane = + lanes[ + Math.min( + lanes.length - 1, + Math.max(0, Math.floor((event.clientY - box.top) / this.laneHeight)), + ) + ]; + const at = view.start + this.share(event) * (view.end - view.start); + const found = nearestEvent(this.shown(), lane.id, at); + if (!found) return; + this.selectedKey.set(found.key); + queueMicrotask(() => { + const row = this.host.nativeElement.querySelector( + `button.row[data-key="${CSS.escape(found.key)}"]`, + ); + row?.scrollIntoView?.({ block: 'nearest' }); + }); + } + + retry() { + const client = this.rpc(); + if (client) void this.load(client); + } + + private stop() { + for (const cleanup of this.cleanups.splice(0)) cleanup(); + } + + private async load(client: DevframeRpcClient) { + this.stop(); + const load = ++this.loads; + this.loading.set(true); + this.failed.set(false); + const rpc = client.scope('pangular').rpc; + const results = await Promise.allSettled( + TIMELINE_LAYERS.map(async (layer) => { + const state = await rpc.sharedState(layer.state); + if (this.destroyRef.destroyed || load !== this.loads) return; + const apply = (value: unknown) => + this.sources.update((sources) => ({ ...sources, [layer.id]: value })); + apply(state.value()); + this.cleanups.push(state.on('updated', apply)); + }), + ); + if (this.destroyRef.destroyed || load !== this.loads) return; + this.failed.set(results.every((result) => result.status === 'rejected')); + this.loading.set(false); + } +} diff --git a/app/src/shell-prefs.ts b/app/src/shell-prefs.ts index c0c274a5..38723b4a 100644 --- a/app/src/shell-prefs.ts +++ b/app/src/shell-prefs.ts @@ -185,3 +185,43 @@ export function inPopup(win: Window = window): boolean { } return false; } + +/** The prefix of the window name the popup gives the panel it pops out (`popup.ts`). */ +const POPOUT_NAME = 'pangular-inspector-'; + +/** + * The app page's window when the panel runs in the window the popup popped + * it out into, or `null` elsewhere. A hub dock inside that window counts too. + */ +export function popoutOpener(win: Window = window): Window | null { + try { + const top = win.top ?? win; + if (!top.name.startsWith(POPOUT_NAME)) return null; + return top.opener && top.opener !== top ? (top.opener as Window) : null; + } catch { + return null; + } +} + +/** Asks the popup on the app's page to take the panel back from its own window. */ +export function returnFromPopout(win: Window = window): boolean { + const opener = popoutOpener(win); + if (!opener) return false; + let origin: string | undefined; + try { + origin = opener.location.origin; + } catch { + try { + origin = new URL(win.document.referrer).origin; + } catch { + origin = undefined; + } + } + if (!origin || origin === 'null') return false; + try { + opener.postMessage({ type: 'pangular:popup-return' }, origin); + return true; + } catch { + return false; + } +} diff --git a/app/src/types/tab.types.ts b/app/src/types/tab.types.ts index 340cad06..8f35e260 100644 --- a/app/src/types/tab.types.ts +++ b/app/src/types/tab.types.ts @@ -1,5 +1,6 @@ export type Tab = | 'dashboard' + | 'timeline' | 'components' | 'pipes' | 'routes' diff --git a/apps/docs/src/content/agents/tools.md b/apps/docs/src/content/agents/tools.md index fc37c233..babc4225 100644 --- a/apps/docs/src/content/agents/tools.md +++ b/apps/docs/src/content/agents/tools.md @@ -49,11 +49,11 @@ The URL comes from the HTTP or router report. A page that sends neither (no rout Gives the user a link that opens the panel on one tab with one item selected, and switches the panels that are open to it. Reads: page. Use it to point the user at what you found. -| Argument | Required | What it is | -| -------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `tab` | Yes | The tab: `dashboard`, `components`, `routes`, `signals`, `injectors`, `store`, `forms`, `pipes`, `network` (SSR & HTTP), `errors`, `analog` or `capacitor`. Only tabs that are on count. | -| `id` | No | The item to select: a component id on `components`, the id of the component whose element injector to show on `injectors`, a form id on `forms`. Other tabs ignore it. | -| `page` | No | The page the item belongs to, from `list-pages`. | +| Argument | Required | What it is | +| -------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `tab` | Yes | The tab: `dashboard`, `timeline`, `components`, `routes`, `signals`, `injectors`, `store`, `forms`, `pipes`, `network` (SSR & HTTP), `errors`, `analog` or `capacitor`. Only tabs that are on count. | +| `id` | No | The item to select: a component id on `components`, the id of the component whose element injector to show on `injectors`, a form id on `forms`. Other tabs ignore it. | +| `page` | No | The page the item belongs to, from `list-pages`. | The answer is a full URL, such as `http://localhost:4200/__devframes/pangular/#tab=components&id=12&page=k3x9`, when the hub serves the panel and a page has reported its address. Otherwise it is the hash to add to the panel's address. Each panel that is open and not in a background tab opens the link at once. The tool is `read`: it changes what the panel shows, not the app. See [Deep links](../getting-started/popup-and-hub.md#deep-links). diff --git a/apps/docs/src/content/getting-started/command-palette.md b/apps/docs/src/content/getting-started/command-palette.md index 191b402d..919820db 100644 --- a/apps/docs/src/content/getting-started/command-palette.md +++ b/apps/docs/src/content/getting-started/command-palette.md @@ -32,6 +32,9 @@ Use Cmd on macOS and Ctrl elsewhere. The palette button in | **Clear change detection recording** | Opens **Components** and clears the recorded cycles. | | **Theme: follow the system**, **Theme: light**, **Theme: dark** | Sets the panel theme, like the [Settings](./settings.md#theme) page. | | **Popup: float**, **Popup: dock to the bottom**, **Popup: dock to the right** | Changes the [dock mode](./popup-and-hub.md#dock-modes). Only in the popup's own palette. | +| **Popup: reset the docked size** | Puts a [resized](./popup-and-hub.md#resize-a-docked-panel) bottom or right dock back to 40%. | +| **Popup: open in its own window** | Opens the panel in [its own window](./popup-and-hub.md#open-in-its-own-window) and closes it here. | +| **Popup: back into the page** | Closes that window and opens the panel on the page again. Only in the window's palette. | The **Components** commands need the Components inspector on and a page connected. Without a live page the tab shows why, and the command does nothing. @@ -52,7 +55,9 @@ The palette is a modal dialog. The filter box is a combobox that controls the li In the hub, the Pangular commands are in the hub's command palette, in the **Pangular Inspector** category. **Pangular: open a tab** groups the tab commands. A **Go to** or **Components** command first opens the **All tools** dock, so the panel is on screen. -The server registers the commands through Devframe's command registry when the hub starts, and each one asks the open panels to run it. A panel in a background browser tab ignores it. If you have the hub open in two windows, both run the command. The dock mode commands stay out of the hub's palette, because the hub has its own. +The server registers the commands through Devframe's command registry when the hub starts, and each one asks the open panels to run it. A panel in a background browser tab ignores it. If you have the hub open in two windows, both run the command. The **Popup** commands stay out of the hub's palette, because the hub has its own dock modes. + +With the [terminals dock](./terminals.md) on, the hub's palette also has **Pangular: run <command>** and **Pangular: stop <command>** for the build and test commands, such as **Pangular: run pnpm run build**. The server runs these itself, so they work without a panel open. They are not in the panel's own palette. ## Where to next diff --git a/apps/docs/src/content/getting-started/configuration.md b/apps/docs/src/content/getting-started/configuration.md index cec10c52..74f5cff2 100644 --- a/apps/docs/src/content/getting-started/configuration.md +++ b/apps/docs/src/content/getting-started/configuration.md @@ -208,6 +208,16 @@ const devtools = initPangularHub({ The overlay reads the option from the server when it connects, so the default works until then. With `false`, [passive mode](./settings.md#passive-mode) has no effect and the floating button always shows. +### Terminals + +`terminals` is an option of the Express hub and the Vite plugin only, next to the access options. It is not part of `PangularConfig`, so `createPangular()` and the CLI config file don't take it. + +| Option | Default | What it does | +| ----------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `terminals` | `false` | `true` adds a **Terminals** dock to the hub that runs the project's build and test commands. It stays off in production, with `allowedOrigins: false` and with `allowLan`. See [Terminals](./terminals.md). | + +It is off by default because it starts processes on your machine. A value other than `true` or `false` prints a warning and keeps it off. + ## Check the active configuration The server checks the options when it starts. An unknown key, a value of the wrong type or a limit outside its bounds prints one `[pangular]` warning in the terminal that lists each problem and the value used instead. An unknown key names the closest known key: diff --git a/apps/docs/src/content/getting-started/express.md b/apps/docs/src/content/getting-started/express.md index 27edf6f4..82cc147f 100644 --- a/apps/docs/src/content/getting-started/express.md +++ b/apps/docs/src/content/getting-started/express.md @@ -103,6 +103,7 @@ With `ws: {sidecar: true}`, the WebSocket runs on its own port, picked automatic | `auth` | on | `false` turns off the one-time code. `{tokens: [...]}` keeps it on and trusts clients that send one of the [pairing tokens](../security.md#pairing-tokens). | | `allowedOrigins` | loopback origins and the Chrome extension | Extra origins allowed to reach the hub, such as a tunnel. `false` turns the origin check and the request gate off. | | `allowLan` | `false` | Answer other machines, such as a phone on Wi-Fi. Needs the one-time code on. | +| `terminals` | `false` | Add a dock that runs the project's build and test commands. See [Terminals](./terminals.md). | | `mcp` | a bearer token | Mounts the MCP endpoint at `__mcp` and asks for a bearer token. With `auth: false` the default is `'auto'`: it mounts once agent tools exist and asks for no token. See [Send a token](../agents/mcp-server.md#send-a-token). | The hub also takes the devtools options, such as `inspectors`, `agent`, `actions`, `redaction` and `limits`. See [Configuration](./configuration.md). Pass `browserDistFolder`, the folder that holds `index.csr.html`, to let the **Force Client render** override serve it. See [SSR overrides](../inspectors/ssr-http.md#ssr-overrides). diff --git a/apps/docs/src/content/getting-started/popup-and-hub.md b/apps/docs/src/content/getting-started/popup-and-hub.md index dcce8708..e15e4b37 100644 --- a/apps/docs/src/content/getting-started/popup-and-hub.md +++ b/apps/docs/src/content/getting-started/popup-and-hub.md @@ -1,6 +1,6 @@ --- title: Popup and hub -description: The floating button, the panel and its dock modes, the hub rail and deep links. +description: The floating button, the panel and its dock modes, the splitter, the pop-out window, the hub rail and deep links. --- @@ -74,14 +74,41 @@ The values above are the defaults. A free panel on top of your app. Drag it by the toolbar with a mouse, finger or pen, and resize it from the corner. - Full width, 40% of the viewport height. Resize it vertically. + Full width, 40% of the viewport height until you resize it. Drag the splitter on its top edge. - 40% of the viewport width, full height. Resize it horizontally. + 40% of the viewport width until you resize it, full height. Drag the splitter on its left edge. -You can drag only the floating panel. Switch modes from the buttons in the panel toolbar. The floating panel always stays inside the window: when the window shrinks, the panel moves and shrinks to fit, and grows back to its saved size when there is room again. +You can drag only the floating panel. Switch modes from the buttons in the panel toolbar, or from the [command palette](./command-palette.md). The floating panel always stays inside the window: when the window shrinks, the panel moves and shrinks to fit, and grows back to its saved size when there is room again. + +### Resize a docked panel + +A docked panel has a splitter on the edge that faces your app. Drag it with a mouse, finger or pen, or focus it with Tab and use the keyboard: + +| Key | Bottom dock | Right dock | +| ---------------------------------- | --------------------------------- | --------------------------------- | +| ↑ and ↓ | Taller and shorter by 16 pixels | Nothing | +| ← and → | Nothing | Wider and narrower by 16 pixels | +| Shift with an arrow key | 64 pixels at a time | 64 pixels at a time | +| Home and End | The smallest and the largest size | The smallest and the largest size | + +A bottom dock is at least 120 pixels high and a right dock at least 280 pixels wide. Both leave 48 pixels of the window for your app. Double-click the splitter, or run **Popup: reset the docked size** from the command palette, to go back to 40%. The bottom and right docks each keep their own size, and a saved size shrinks to fit a smaller window. + +Screen readers announce the splitter as a separator named **Resize the panel**, with its current, smallest and largest size in pixels. + +### Open in its own window + +The **Open in its own window** button in the panel toolbar, or **Popup: open in its own window** in the command palette, opens the panel in a separate browser window and closes it on the page. Use it to keep the devtools on a second screen. + +The window shows the full panel without the hub rail, and its address carries the page's id (`?pageId=`), so it keeps showing this browser tab after a reload of the app. If you passed `src` to `createDevtoolsPopup`, the window opens that address instead. Each tab gets its own window, and popping out again reuses it. + +To bring the panel back into the page, click the floating button (its name is **Bring Pangular Inspector back into the page** while the window is open), press the popup shortcut, or run **Popup: back into the page** from the command palette in the window. Each closes the window and opens the panel on the page. Closing the window yourself also gives the button its usual job back. + +If the browser blocks the window, the panel stays on the page and says so. Allow pop-ups for your app's address and try again. + +### Top layer The button and the panel sit in the browser top layer, like a ``, a popover or a CDK overlay (MatDialog, MatMenu, MatSelect). When one of those opens, the button and the panel move back above it. A modal `` makes the rest of the page inert, so they stay under it until it closes. @@ -96,6 +123,7 @@ The button and the panel sit in the browser top layer, like a ``, a popo | Move through a long list | ↑, ↓, Home and End in the SSR & HTTP timeline, the Store changes and events, and the Signals list. Each list is one Tab stop | | Close the panel | Escape, outside a search box with text | | Move the floating panel | Drag its toolbar | +| Resize a docked panel | Drag the splitter, or focus it and use the arrow keys, Home and End. See [Resize a docked panel](#resize-a-docked-panel) | | Reset the panel position | Double-click its toolbar | | Move the button | Drag it, or focus it and use the arrow keys | | Move the button further | Hold Shift with the arrow keys | @@ -105,7 +133,7 @@ In a search box that has text, Escape clears the box and leaves the p ### Saved layout -The panel saves its position, size, dock mode and [passive mode](./settings.md#passive-mode) in `localStorage` under `pangular-popup`, so it keeps its layout across reloads. Clear that key to reset it. +The panel saves its position, size, dock mode, the size of each docked side and [passive mode](./settings.md#passive-mode) in `localStorage` under `pangular-popup`, so it keeps its layout across reloads. Clear that key to reset it. ### Passive mode @@ -124,7 +152,7 @@ When the page's server mounts the hub (`/__devframes/`), the button opens the wh | Dock | Shows | | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | | All tools | The full panel, with every tab in one tab bar (`/__devframes/pangular/`) | -| Angular | Dashboard, Components, Routes, Signals, Injectors, Forms, Pipes, and SSR & HTTP | +| Angular | Dashboard, Timeline, Components, Routes, Signals, Injectors, Forms, Pipes, SSR & HTTP, and Errors | | NgRx | The Store tab | | Analog | The Analog tab, or a notice in apps that do not use Analog | | Angular Native | Components, Signals, Injectors and Store for a connected [Angular Native](./angular-native.md) app, or a setup notice when none is connected | @@ -167,6 +195,10 @@ A dock shows how many items it has that you have not seen yet: NgRx writes on ** Messages follow the config. An inspector that is off posts nothing (forms errors need `inspectors.forms`, failed navigations `inspectors.router`, hydration messages `inspectors.http`), and an agent tool turned off with `agent.tools`, `agent.readOnly` or `actions` posts nothing either. See [Configuration](./configuration.md). +### Terminals dock + +With `terminals: true`, the hub also has a **Terminals** dock that runs your project's build and test commands and shows their output. See [Terminals](./terminals.md). + ### Full-page viewer The full-page viewer is at `/__devframes/` on the same server. The hub is built on [`@devframes/hub`](https://github.com/devframes/devframe), so other devframe tools can join the same rail. @@ -184,6 +216,7 @@ The URL hash selects a tab. Open `/__devframes/pangular/#tab=signals` to land on | Tab | Hash | | ---------- | ----------------- | | Dashboard | `#tab=dashboard` | +| Timeline | `#tab=timeline` | | Components | `#tab=components` | | Routes | `#tab=routes` | | Signals | `#tab=signals` | diff --git a/apps/docs/src/content/getting-started/terminals.md b/apps/docs/src/content/getting-started/terminals.md new file mode 100644 index 00000000..f5cebc5e --- /dev/null +++ b/apps/docs/src/content/getting-started/terminals.md @@ -0,0 +1,90 @@ +--- +title: Terminals +description: Run your project's build and test commands from the hub and read their output in a terminals dock. +--- + + + Turn on terminals to run your project's build and test commands from the hub, and read their output in a Terminals dock next to the Pangular docks. + + +# Terminals + +The terminals dock runs two commands of your project, build and test, on the server that mounts the hub. It shows their output as it arrives, and lets you stop and restart them. It is off by default. + +## Turn it on + +Pass `terminals: true` to the Express hub or the Vite plugin: + +```ts {6} +// src/server.ts +import {initPangularHub} from '@pangular-inspector/devtools/hub'; + +const devtools = initPangularHub({ + ws: false, + terminals: true, +}); +``` + +```ts {9} +// vite.config.ts +import analog from '@analogjs/platform'; +import pangular from '@pangular-inspector/devtools/vite'; +import {defineConfig} from 'vite'; + +export default defineConfig({ + plugins: [analog(), pangular({terminals: true})], +}); +``` + +`createPangular()`, the standalone CLI and the [Chrome extension](./chrome-extension.md) have no terminals: the dock needs the [hub](./popup-and-hub.md#the-hub). `terminals` is not part of the shared options or the CLI config file. See [Configuration](./configuration.md#terminals). + +## The commands + +The hub looks for the commands once, when it starts, in the folder it runs in (`cwd`): + +| Command | Runs | +| ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Build | The `build` script of `package.json`, such as `pnpm run build`. Without one, `ng build` when `angular.json` has a `build` target and `@angular/cli` is installed. | +| Test | The `test` script of `package.json`. Without one, `ng test` when `angular.json` has a `test` target and `@angular/cli` is installed. | + +The commands run with your project's package manager: the `packageManager` field of `package.json`, else the nearest lockfile in the project or up to five folders above it (`pnpm-lock.yaml`, `yarn.lock`, `bun.lock`, `package-lock.json`), else npm. `ng` runs through it too, such as `pnpm exec ng build`. The `test` script that `npm init` writes ("no test specified") doesn't count. + +If the project has neither command, the hub prints a `[pangular]` warning and adds no dock. The dock runs these commands and nothing else. It has no shell, and a request for any other command, arguments, working directory or environment variables is refused. + +## Run, stop and restart + +| Where | How | +| ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- | +| The **Terminals** dock | Pick the command among the dock's presets. Select a session to see its output, and use its stop and restart buttons. | +| The hub's [command palette](./command-palette.md#in-the-hub) | **Pangular: run <command>** runs it, or runs it again, and opens the dock on its output. **Pangular: stop <command>** stops it. | + +Each command has one session in the dock. Running it again stops a run in progress first, then starts over in the same session, under the output of the earlier run. A run ends with `[exited with code ]`, or `[stopped]` when you stopped it. A command that exits with another code than 0 shows as failed. + +The output is read-only. JWTs, bearer tokens, passwords in URLs, `Authorization` values and well-known API key shapes are masked line by line before they reach the dock, like the rest of the [redacted values](../security.md#what-is-redacted). + +Stopping a command sends `SIGTERM` to its process group, so the CLI under the package manager stops too, then `SIGKILL` after 3 seconds if they are still running. On Windows the hub ends the process tree with `taskkill`. The commands also stop when the hub closes and when the server process exits or gets `SIGINT`, `SIGTERM` or `SIGHUP`. + +## When the dock stays off + +`terminals: true` adds the dock only when all of these hold. Otherwise the hub prints a `[pangular] The terminals dock is off` warning with the reason. + +| Condition | Why | +| ------------------------------ | ------------------------------------------------------------------------------------- | +| `NODE_ENV` is not `production` | The commands are for development. The Vite plugin runs only with `vite dev`. | +| The origin check is on | With `allowedOrigins: false`, any web page could reach the hub and start a command. | +| `allowLan` is off | Only clients on this machine may run commands. With `allowLan`, other machines could. | +| A command was found | See [The commands](#the-commands). | + +The dock goes through the same request gate and one-time code as the rest of the hub. See [Access and redaction](../security.md#terminals). + + + Agents can't run the commands: the terminal functions are not MCP tools. + + +## Where to next + + + + + + diff --git a/apps/docs/src/content/getting-started/vite.md b/apps/docs/src/content/getting-started/vite.md index 3fe13b49..cc0777e8 100644 --- a/apps/docs/src/content/getting-started/vite.md +++ b/apps/docs/src/content/getting-started/vite.md @@ -93,14 +93,15 @@ pangular({ }); ``` -| Option | Default | What it does | -| ---------------- | -------------------------------- | ------------------------------------------------------------------------ | -| `base` | `'/__devframes/'` | Where the hub is mounted. | -| `apiPrefix` | Analog's `apiPrefix`, or `'api'` | The prefix of your server routes, used to classify API calls. | -| `allowedOrigins` | none | Extra exact origins allowed to reach the devtools, for example a tunnel. | -| `auth` | on | Whether the devtools ask for the one-time code, and its pairing tokens. | -| `allowLan` | `false` | Answer other machines, such as a phone on Wi-Fi, from listed origins. | -| `overlay` | `true` | Add the overlay to the pages the dev server serves. | +| Option | Default | What it does | +| ---------------- | -------------------------------- | ---------------------------------------------------------------------------------- | +| `base` | `'/__devframes/'` | Where the hub is mounted. | +| `apiPrefix` | Analog's `apiPrefix`, or `'api'` | The prefix of your server routes, used to classify API calls. | +| `allowedOrigins` | none | Extra exact origins allowed to reach the devtools, for example a tunnel. | +| `auth` | on | Whether the devtools ask for the one-time code, and its pairing tokens. | +| `allowLan` | `false` | Answer other machines, such as a phone on Wi-Fi, from listed origins. | +| `overlay` | `true` | Add the overlay to the pages the dev server serves. | +| `terminals` | `false` | Add a dock that runs the build and test commands. See [Terminals](./terminals.md). | The plugin also takes the devtools options, such as `inspectors`, `agent`, `actions`, `redaction` and `limits`. See [Configuration](./configuration.md). diff --git a/apps/docs/src/content/inspectors/timeline.md b/apps/docs/src/content/inspectors/timeline.md new file mode 100644 index 00000000..050813d8 --- /dev/null +++ b/apps/docs/src/content/inspectors/timeline.md @@ -0,0 +1,96 @@ +--- +title: Timeline +description: Navigations, HTTP calls, NgRx actions, change detection cycles, form events and signal changes of the running page on one time axis. +--- + + + See what happened on the page in order, across the router, HTTP, NgRx, change detection, forms and signals, and jump to any event in its own tab. + + +# Timeline + +The Timeline tab puts the events that other tabs already record on one time axis. Each source is a layer with its own lane. Turn layers on and off, pick a range, and open any event in the tab that shows it in full. + +## What it shows + +### Layers + +| Layer | Events | Opens in | Turned off with | +| ---------------- | -------------------------------------------------------------------------- | ---------------------------------------------- | ----------------------- | +| Router | Navigations, with their outcome, trigger and duration | [Routes](./router.md) | `inspectors.router` | +| HTTP | Client and SSR HTTP calls, with their status, side and duration | [SSR & HTTP](./ssr-http.md) | `inspectors.http` | +| NgRx | Store actions, signal store changes and events, with the store and changes | [Store](./ngrx-store.md) | `inspectors.ngrx` | +| Change detection | Recorded change detection cycles, with their time, checks and trigger | [Components](./components.md#change-detection) | `inspectors.components` | +| Forms | Form events such as value, status, submit and reset, with their origin | [Forms](./forms.md), with the form selected | `inspectors.forms` | +| Signals | Value changes of the signals in the graph the Signals tab shows | [Signals](./signals.md#value-history) | `inspectors.signals` | + +A layer button shows how many events of that layer are in the range. Press it to hide or show the layer. The panel remembers the hidden layers in `localStorage` under `pangular-timeline-hidden-layers`. A layer whose inspector is off in the [configuration](../getting-started/configuration.md) is not listed. + +Failed navigations and HTTP calls that fail or answer 500 or more are marked as errors. Cancelled or redirected navigations, HTTP answers from 400, fault rules and change detection cycles over 16 ms are marked as warnings. + +### Time axis + +The chart has one lane per layer that is shown. It groups events that fall close together into one mark, so a few thousand events still draw quickly. A mark is darker when it holds more events, and it takes the colour of the worst event in it. + +### Events + +The **Events** list holds the same events as the chart, newest first, with their time to the millisecond, layer, name and detail. It renders the newest 200 events in the range and says how many it left out. Zoom in or filter to see older ones. + +Select an event to see it under **Selected event**. **Open in** opens it in its own tab through a [deep link](../getting-started/popup-and-hub.md#deep-links). A form event selects its form on the Forms tab. Other events open their tab on the same page. + +## Use it + +### Pick a range + +| Action | How | +| ------------------------ | --------------------------------------------------------------------------- | +| Look at a range | Drag across the chart | +| Zoom in or out | **Zoom in** halves the range around its middle, **Zoom out** doubles it | +| Move the range | **Earlier** and **Later** move it by half its width | +| Go back to every event | **Show all** | +| Select the nearest event | Click a lane on the chart | +| Filter by name or detail | Type in the search box. Escape clears it | +| Look at one page | Pick it in **Page**, or **All pages**. The panel of a tab starts on its tab | + +Without a range, the chart grows with each event that arrives. + +### Keyboard + +The **Events** list is one Tab stop. ↑, ↓, Home and End move through it, and Enter selects an event. Every chart action has a button, and screen readers get a summary of the chart with the count of each layer. The list is the text version of the chart. + +## Where the data comes from + +The tab reads the shared state the other tabs read: `router`, `http`, `ngrx-store`, `change-detection`, `forms` and `signal-graph`. It adds no collection of its own, so each layer holds what its tab keeps: + +| Layer | What it needs | Limit | +| ---------------- | ----------------------------------------------------------------------------------------------------------------------- | --------------------- | +| Router | The app's router | `limits.navigations` | +| HTTP | [HTTP setup](../getting-started/express.md) for SSR calls | `limits.httpCalls` | +| NgRx | NgRx in the app | `limits.changeLog` | +| Change detection | A recording, started with **Record** on the [Components](./components.md#change-detection) tab or the palette | `limits.cdCycles` | +| Forms | Forms in the app | `limits.formTimeline` | +| Signals | The component whose graph the [Signals](./signals.md) tab shows. A signal without a `debugName` has sampled values only | 50 changes per signal | + +Raise the limits in the [configuration](../getting-started/configuration.md#limits) to keep more. Times come from the page's clock, and SSR HTTP calls from the server's clock. + +## Limits and gotchas + + + + Use the app with the overlay loaded. Navigations, HTTP calls, NgRx actions and form events show up as they happen. Change detection needs a recording, and signals need a component picked on the Signals tab. + + + A server call belongs to a page when the SSR request that rendered the page made it. Other server calls have no page. + + + Each layer starts when its collector starts. Navigations that started before the overlay connected have no real start time, so they are not on the axis. The Routes tab still lists them. + + + +## Where to next + + + + + + diff --git a/apps/docs/src/content/security.md b/apps/docs/src/content/security.md index 57967ebe..251a38fa 100644 --- a/apps/docs/src/content/security.md +++ b/apps/docs/src/content/security.md @@ -170,6 +170,15 @@ The panel and the overlay can ask the server to open a source file in your edito The request names a file and a line, never a command. The editor comes from the server's environment. Agent tools can't open files. +### Terminals + +The [terminals dock](./getting-started/terminals.md) is off unless you pass `terminals: true`. When it is on, it starts processes on your machine, so the hub adds it only in development, with the origin check on and without `allowLan`. Its calls go through the same gate and one-time code as every other call. + +- It runs the `build` and `test` commands the hub found in the project when it started, and nothing else. A request names one of them, never a command line. +- A request with a command, arguments, a working directory, environment variables or an interactive mode is refused, and so is a request for a shell. +- The output is read-only and masked like other text before it reaches the dock. +- Agent tools can't start, stop or read the commands. + ### Page ownership Each tab, Angular Native app or Capacitor app that reports to the server has a page id. The first connection that reports a page owns it. A report or a `forget` for that page id from any other connection is ignored, so one client can't overwrite or remove the data of another tab. The answer to a request sent to a page, such as a navigation, a form action, an NgRx dispatch or a component pick, is taken only from the connection that owns that page. diff --git a/apps/docs/src/ngmd.config.ts b/apps/docs/src/ngmd.config.ts index 5779cdac..91e3d621 100644 --- a/apps/docs/src/ngmd.config.ts +++ b/apps/docs/src/ngmd.config.ts @@ -184,6 +184,7 @@ const config: NgmdConfig = { {label: 'Configuration', href: '/getting-started/configuration', status: 'new'}, {label: 'Popup and hub', href: '/getting-started/popup-and-hub', status: 'new'}, {label: 'Command palette', href: '/getting-started/command-palette', status: 'new'}, + {label: 'Terminals', href: '/getting-started/terminals', status: 'new'}, {label: 'Settings', href: '/getting-started/settings', status: 'new'}, {label: 'Browser overlay', href: '/getting-started/overlay', status: 'updated'}, {label: 'Chrome extension', href: '/getting-started/chrome-extension'}, @@ -195,6 +196,7 @@ const config: NgmdConfig = { label: 'Inspectors', items: [ {label: 'Dashboard', href: '/inspectors/dashboard'}, + {label: 'Timeline', href: '/inspectors/timeline', status: 'new'}, {label: 'Components', href: '/inspectors/components'}, {label: 'Injectors', href: '/inspectors/injectors', status: 'updated'}, {label: 'Signals', href: '/inspectors/signals'}, diff --git a/packages/devtools/package.json b/packages/devtools/package.json index 341bbdea..0b9175f3 100644 --- a/packages/devtools/package.json +++ b/packages/devtools/package.json @@ -86,6 +86,8 @@ "@devframes/hub-ui": "^1.2.3", "@devframes/plugin-messages": "^1.2.3", "@devframes/plugin-messages--assets": "^1.2.3", + "@devframes/plugin-terminals": "^1.2.3", + "@devframes/plugin-terminals--assets": "^1.2.3", "@valibot/to-json-schema": "^1.8.0", "cac": "^7.0.0", "devframe": "^1.2.3", diff --git a/packages/devtools/src/__tests__/hub-terminals.test.ts b/packages/devtools/src/__tests__/hub-terminals.test.ts new file mode 100644 index 00000000..8a119725 --- /dev/null +++ b/packages/devtools/src/__tests__/hub-terminals.test.ts @@ -0,0 +1,304 @@ +import { EventEmitter } from 'node:events'; +import type { ChildProcess } from 'node:child_process'; +import { afterEach, describe, expect, it, vi } from 'vitest'; +import { initPangularHub } from '../hub.ts'; +import { + TERMINALS_DOCK_ID, + TERMINALS_SPAWN_RPC, + dockRpcFunctions, + guardSpawnRequest, +} from '../hub-terminals-dock.ts'; +import { + hubTerminals, + registerTerminalCommands, + setupHubTerminals, + terminalsEnabled, + terminalsRefusal, +} from '../hub-terminals.ts'; +import type { TerminalCommand } from '../terminal-commands.ts'; +import { TerminalRunner, terminalSessionId } from '../terminal-runner.ts'; +import { makeProject } from './analog-fixture.ts'; + +const BUILD: TerminalCommand = { + id: 'build', + title: 'pnpm run build', + command: 'pnpm', + args: ['run', 'build'], + source: 'script', +}; +const TEST: TerminalCommand = { + id: 'test', + title: 'pnpm run test', + command: 'pnpm', + args: ['run', 'test'], + source: 'script', +}; + +const PROJECT = { + 'package.json': JSON.stringify({ scripts: { build: 'ng build', test: 'ng test' } }), + 'pnpm-lock.yaml': '', +}; + +const hubs: { close: () => Promise }[] = []; + +afterEach(async () => { + vi.restoreAllMocks(); + for (const hub of hubs.splice(0)) await hub.close(); + delete (globalThis as { __PANGULAR_HUB__?: unknown }).__PANGULAR_HUB__; +}); + +async function boot(options: Parameters[0] = {}) { + const hub = initPangularHub({ + cwd: makeProject(PROJECT), + ws: false, + auth: false, + ...options, + }); + hubs.push(hub); + await hub.ready; + return { hub, ctx: await hub.context }; +} + +function fakeSpawner() { + const children: (EventEmitter & { stdout: EventEmitter; stderr: EventEmitter })[] = []; + const spawn = vi.fn(() => { + const child = Object.assign(new EventEmitter(), { + stdout: new EventEmitter(), + stderr: new EventEmitter(), + pid: 4000 + children.length, + }); + children.push(child); + return child as unknown as ChildProcess; + }); + const kill = vi.fn(); + return { spawn, kill, children }; +} + +describe('terminals option', () => { + it('is off unless it is true', () => { + const warn = vi.fn(); + expect(terminalsEnabled(undefined, {}, warn)).toBe(false); + expect(terminalsEnabled(false, {}, warn)).toBe(false); + expect(warn).not.toHaveBeenCalled(); + expect(terminalsEnabled('yes', {}, warn)).toBe(false); + expect(warn).toHaveBeenCalledWith( + '[pangular] Ignoring terminals: "yes". Pass true to turn it on.', + ); + expect(terminalsEnabled(true, { env: {} }, warn)).toBe(true); + }); + + it('stays off in production, without the origin check and with allowLan', () => { + expect(terminalsRefusal({ env: { NODE_ENV: 'production' } })).toBe('NODE_ENV is production'); + expect(terminalsRefusal({ env: {}, originCheckOff: true })).toContain( + 'allowedOrigins is false', + ); + expect(terminalsRefusal({ env: {}, remote: true })).toContain('allowLan'); + const warn = vi.fn(); + expect(terminalsEnabled(true, { env: {}, remote: true }, warn)).toBe(false); + expect(warn).toHaveBeenCalledWith( + '[pangular] The terminals dock is off: allowLan lets other machines reach the hub.', + ); + }); +}); + +describe('terminals dock guard', () => { + it('lets a detected preset through and nothing else', () => { + const commands = [BUILD]; + expect(guardSpawnRequest({ presetId: 'build', cols: 120, rows: 30 }, commands)).toBe('build'); + expect(guardSpawnRequest({ presetId: 'build', mode: 'readonly' }, commands)).toBe('build'); + // No preset means the default shell in the plugin. + expect(() => guardSpawnRequest({}, commands)).toThrow('build and test commands'); + expect(() => guardSpawnRequest(undefined, commands)).toThrow('build and test commands'); + expect(() => guardSpawnRequest({ mode: 'interactive' }, commands)).toThrow('read-only'); + expect(() => guardSpawnRequest({ presetId: 'test' }, commands)).toThrow('Pick one of them'); + expect(() => guardSpawnRequest({ command: 'bash' }, commands)).toThrow('command cannot be set'); + expect(() => + guardSpawnRequest({ presetId: 'build', args: ['--', 'rm', '-rf', '/'] }, commands), + ).toThrow('args cannot be set'); + expect(() => guardSpawnRequest({ presetId: 'build', cwd: '/' }, commands)).toThrow( + 'cwd cannot be set', + ); + expect(() => + guardSpawnRequest({ presetId: 'build', env: { NODE_OPTIONS: '--require x' } }, commands), + ).toThrow('env cannot be set'); + }); + + it('registers the plugin functions without agent tools, and spawns through the runner', () => { + const start = vi.fn(() => terminalSessionId('build')); + const functions = dockRpcFunctions({ commands: [BUILD], start }); + expect(functions.length).toBeGreaterThan(5); + expect(functions.filter((fn) => 'agent' in fn && fn.agent)).toEqual([]); + const spawn = functions.find((fn) => fn.name === TERMINALS_SPAWN_RPC)!; + const { handler } = (spawn.setup as (ctx: unknown) => { handler: (req: unknown) => unknown })( + {}, + ); + expect(handler({ presetId: 'build' })).toMatchObject({ + id: 'pangular:terminal:build', + mode: 'readonly', + channel: 'devframe:terminals', + }); + expect(start).toHaveBeenCalledWith('build'); + expect(() => handler({ command: 'sh', args: ['-c', 'id'] })).toThrow(); + expect(start).toHaveBeenCalledTimes(1); + }); +}); + +describe('terminals palette commands', () => { + it('adds run and stop for each detected command', () => { + const host = { + sessions: new Map(), + register: vi.fn(), + update: vi.fn(), + remove: vi.fn(), + }; + const { spawn, kill } = fakeSpawner(); + const runner = new TerminalRunner(host as never, [BUILD, TEST], { + cwd: '/work', + spawn, + kill, + hookProcessExit: false, + }); + const registered: { id: string; title: string; handler: () => void }[] = []; + const activate = vi.fn(); + const ids = registerTerminalCommands( + { commands: { register: (command) => registered.push(command) }, docks: { activate } }, + runner, + ); + expect(ids).toEqual([ + 'pangular:terminal:run-build', + 'pangular:terminal:stop-build', + 'pangular:terminal:run-test', + 'pangular:terminal:stop-test', + ]); + expect(registered[0].title).toBe('Pangular: run pnpm run build'); + registered[0].handler(); + expect(spawn).toHaveBeenCalledTimes(1); + expect(activate).toHaveBeenCalledWith(TERMINALS_DOCK_ID, { + sessionId: 'pangular:terminal:build', + }); + registered[1].handler(); + expect(kill).toHaveBeenCalledWith(expect.anything(), 'SIGTERM'); + runner.dispose(); + }); + + it('registers nothing outside a hub', () => { + const runner = new TerminalRunner({ sessions: new Map() } as never, [BUILD], { + cwd: '/work', + hookProcessExit: false, + }); + expect(registerTerminalCommands({}, runner)).toEqual([]); + }); +}); + +describe('terminals in the hub', () => { + it('adds no terminals dock by default', async () => { + const { ctx } = await boot(); + expect(ctx.docks.views.has(TERMINALS_DOCK_ID)).toBe(false); + expect(ctx.rpc.has(TERMINALS_SPAWN_RPC)).toBe(false); + }); + + it('adds the dock with the detected commands as presets, and refuses a shell', async () => { + const { hub, ctx } = await boot({ terminals: true }); + const dock = ctx.docks.views.get(TERMINALS_DOCK_ID); + expect(dock && 'url' in dock ? dock.url : undefined).toBe(`/__devframes/${TERMINALS_DOCK_ID}/`); + const presets = (await ctx.rpc.invokeLocal( + 'devframes:plugin:terminals:presets' as never, + ...([] as never), + )) as { + id: string; + title: string; + mode: string; + }[]; + expect(presets.map((p) => [p.id, p.title, p.mode])).toEqual([ + ['build', 'pnpm run build', 'readonly'], + ['test', 'pnpm run test', 'readonly'], + ]); + const spawnShell = ctx.rpc.invokeLocal(TERMINALS_SPAWN_RPC as never, ...([{}] as never)); + await expect(spawnShell).rejects.toThrow('build and test commands'); + const spawnCommand = ctx.rpc.invokeLocal( + TERMINALS_SPAWN_RPC as never, + ...([{ command: process.env['SHELL'] ?? 'bash' }] as never), + ); + await expect(spawnCommand).rejects.toThrow('command cannot be set'); + expect(ctx.terminals.sessions.size).toBe(0); + const panel = await hub.handler( + new Request(`http://localhost/__devframes/${TERMINALS_DOCK_ID}/`), + ); + expect(panel.status).toBe(200); + }); + + it('keeps the terminal functions out of the MCP tools', async () => { + const { hub } = await boot({ terminals: true, mcp: { authorization: 'secret-token' } }); + const response = await hub.handler( + new Request('http://localhost/__devframes/__mcp', { + method: 'POST', + headers: { + 'content-type': 'application/json', + accept: 'application/json, text/event-stream', + origin: 'http://localhost:4000', + authorization: 'Bearer secret-token', + }, + body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'tools/list', params: {} }), + }), + ); + const text = await response.text(); + expect(text).toContain('pangular_'); + expect(text).not.toContain('terminals'); + }); + + it('turns the dock off when other machines can reach the hub', async () => { + const warn = vi.spyOn(console, 'warn').mockImplementation(() => undefined); + const { ctx } = await boot({ terminals: true, auth: true, allowLan: true }); + expect(ctx.docks.views.has(TERMINALS_DOCK_ID)).toBe(false); + expect(warn).toHaveBeenCalledWith( + '[pangular] The terminals dock is off: allowLan lets other machines reach the hub.', + ); + }); + + it('runs a preset from the dock as a hub session, stops it from the hub, and stops it on close', async () => { + const { ctx } = await boot(); + const handle = hubTerminals(); + const { spawn, kill, children } = fakeSpawner(); + const runner = await setupHubTerminals(ctx as never, '/__devframes/', handle, { + commands: [BUILD, TEST], + runner: { spawn, kill, hookProcessExit: false }, + }); + expect(runner).toBeDefined(); + const info = (await ctx.rpc.invokeLocal( + TERMINALS_SPAWN_RPC as never, + ...([{ presetId: 'build' }] as never), + )) as { id: string }; + expect(info.id).toBe('pangular:terminal:build'); + expect(spawn).toHaveBeenCalledWith(BUILD, expect.any(String)); + expect(ctx.terminals.sessions.get(info.id)?.status).toBe('running'); + // The dock lists hub sessions next to its own. + const list = (await ctx.rpc.invokeLocal( + 'devframes:plugin:terminals:list' as never, + ...([] as never), + )) as { + id: string; + channel?: string; + }[]; + expect(list).toContainEqual( + expect.objectContaining({ id: info.id, channel: 'devframe:terminals' }), + ); + await ctx.rpc.invokeLocal('hub:terminals:terminate' as never, ...([info.id] as never)); + expect(kill).toHaveBeenCalledWith(children[0], 'SIGTERM'); + await ctx.rpc.invokeLocal('hub:terminals:restart' as never, ...([info.id] as never)); + expect(spawn).toHaveBeenCalledTimes(2); + handle.dispose(); + expect(kill).toHaveBeenCalledWith(children[1], 'SIGTERM'); + expect(ctx.terminals.sessions.has(info.id)).toBe(false); + }); + + it('says so and adds nothing when the project has no build or test command', async () => { + const { ctx } = await boot(); + const warn = vi.fn(); + const runner = await setupHubTerminals(ctx as never, '/__devframes/', hubTerminals(), { + commands: [], + warn, + }); + expect(runner).toBeUndefined(); + expect(warn).toHaveBeenCalledWith(expect.stringContaining('no build or test script')); + }); +}); diff --git a/packages/devtools/src/__tests__/panel-shell.test.ts b/packages/devtools/src/__tests__/panel-shell.test.ts index 10f3661c..19c3137b 100644 --- a/packages/devtools/src/__tests__/panel-shell.test.ts +++ b/packages/devtools/src/__tests__/panel-shell.test.ts @@ -115,8 +115,12 @@ describe('panel commands', () => { 'dock:float', 'dock:bottom', 'dock:right', + 'dock:reset-size', + 'popout', + 'popout:return', ]), ); + expect(ids).toContain('tab:timeline'); const off = panelCommands(resolvePangularConfig({ inspectors: { components: false } })); expect(off.map((command) => command.id)).not.toContain('tab:components'); expect(off.map((command) => command.id)).not.toContain('pick-component'); @@ -133,6 +137,8 @@ describe('panel commands', () => { ); expect(ids).toContain('pangular:pick-component'); expect(ids).not.toContain('pangular:dock:float'); + expect(ids).not.toContain('pangular:popout'); + expect(ids).not.toContain('pangular:popout:return'); const inputs = register.mock.calls.map(([input]) => input); const tabs = inputs.find((input) => input.id === 'pangular:tabs'); expect(tabs.children.map((child: { id: string }) => child.id)).toContain( diff --git a/packages/devtools/src/__tests__/popup.test.ts b/packages/devtools/src/__tests__/popup.test.ts index 2acfc240..b63a84f2 100644 --- a/packages/devtools/src/__tests__/popup.test.ts +++ b/packages/devtools/src/__tests__/popup.test.ts @@ -282,7 +282,9 @@ describe.sequential('devtools popup', () => { expect(missing.querySelector('a')!.getAttribute('href')).toMatch(/^https:/); expect(missing.querySelector('h2')!.id).toBe(missing.getAttribute('aria-labelledby')); expect(missing.querySelector('a')!.textContent).toContain('opens in a new tab'); - expect(shadow.querySelector('[role="status"]')!.textContent).toBe('No devtools server found'); + expect(shadow.querySelector('.sr-only[role="status"]')!.textContent).toBe( + 'No devtools server found', + ); expect(frame().hidden).toBe(true); expect(frame().getAttribute('src')).toBeNull(); }); @@ -410,7 +412,7 @@ describe.sequential('devtools popup', () => { expect(stored().docked).toBe('float'); }); - it('draws no resize grip on a docked panel, whose size is fixed', async () => { + it('draws no resize grip on a docked panel, which the splitter resizes', async () => { await loadPopup(true); const sheet = document .getElementById('pangular-popup-root')! @@ -654,4 +656,208 @@ describe.sequential('devtools popup', () => { expect(panel.classList.contains('open')).toBe(true); }); }); + describe('splitter', () => { + const splitter = () => + document + .getElementById('pangular-popup-root')! + .shadowRoot!.querySelector('.splitter') as HTMLElement; + const key = (k: string, init: KeyboardEventInit = {}) => + splitter().dispatchEvent(new KeyboardEvent('keydown', { key: k, bubbles: true, ...init })); + const docked = async (mode: 'bottom' | 'right') => { + await loadPopup(true); + parts().fab.click(); + parts().dock(mode).click(); + }; + + it('is a focusable separator on the docked edge only', async () => { + await loadPopup(true); + parts().fab.click(); + expect(splitter().hidden).toBe(true); + parts().dock('bottom').click(); + const el = splitter(); + expect(el.hidden).toBe(false); + expect(el.getAttribute('role')).toBe('separator'); + expect(el.tabIndex).toBe(0); + expect(el.getAttribute('aria-label')).toBe('Resize the panel'); + expect(el.getAttribute('aria-orientation')).toBe('horizontal'); + expect(el.getAttribute('aria-controls')).toBe(parts().panel.id); + // jsdom's window is 1024 by 768: 40% of its height, inside 120 and 768 - 48. + expect(el.getAttribute('aria-valuenow')).toBe(String(Math.round(768 * 0.4))); + expect(el.getAttribute('aria-valuemin')).toBe('120'); + expect(el.getAttribute('aria-valuemax')).toBe('720'); + expect(el.getAttribute('aria-valuetext')).toBe('307 pixels high'); + parts().dock('right').click(); + expect(el.getAttribute('aria-orientation')).toBe('vertical'); + expect(el.getAttribute('aria-valuemin')).toBe('280'); + expect(el.getAttribute('aria-valuemax')).toBe('976'); + }); + + it('resizes a bottom dock with Up, Down, Home and End, and remembers the size', async () => { + await docked('bottom'); + key('ArrowUp'); + expect(splitter().getAttribute('aria-valuenow')).toBe('323'); + expect(parts().panel.style.getPropertyValue('--_dock-size')).toBe('323px'); + key('ArrowDown', { shiftKey: true }); + expect(splitter().getAttribute('aria-valuenow')).toBe('259'); + // The other axis does nothing on a horizontal splitter. + key('ArrowLeft'); + expect(splitter().getAttribute('aria-valuenow')).toBe('259'); + key('Home'); + expect(splitter().getAttribute('aria-valuenow')).toBe('120'); + key('ArrowDown'); + expect(splitter().getAttribute('aria-valuenow')).toBe('120'); + key('End'); + expect(splitter().getAttribute('aria-valuenow')).toBe('720'); + expect(stored().dockSize).toEqual({ bottom: 720 }); + + await loadPopup(true); + parts().fab.click(); + expect(splitter().getAttribute('aria-valuenow')).toBe('720'); + }); + + it('keeps a size for each side, and Left grows a right dock', async () => { + await docked('right'); + key('ArrowLeft'); + expect(splitter().getAttribute('aria-valuenow')).toBe(String(Math.round(1024 * 0.4) + 16)); + parts().dock('bottom').click(); + key('ArrowUp'); + expect(stored().dockSize).toEqual({ right: 426, bottom: 323 }); + }); + + it('resizes by dragging and goes back to the default on a double click', async () => { + await docked('right'); + const { panel } = parts(); + splitter().dispatchEvent(pointer('pointerdown', 600, 300, 'mouse')); + expect(panel.classList.contains('resizing')).toBe(true); + splitter().dispatchEvent(pointer('pointermove', 500, 300, 'mouse')); + expect(splitter().getAttribute('aria-valuenow')).toBe('524'); + // Dragging past the window edge stops at the largest size. + splitter().dispatchEvent(pointer('pointermove', -50, 300, 'mouse')); + expect(splitter().getAttribute('aria-valuenow')).toBe('976'); + splitter().dispatchEvent(pointer('pointerup', -50, 300, 'mouse')); + expect(panel.classList.contains('resizing')).toBe(false); + expect(stored().dockSize).toEqual({ right: 976 }); + splitter().dispatchEvent(new MouseEvent('dblclick', { bubbles: true })); + expect(stored().dockSize).toBeUndefined(); + expect(splitter().getAttribute('aria-valuenow')).toBe('410'); + }); + + it('keeps a saved size inside a smaller window', async () => { + localStorage.setItem( + 'pangular-popup', + JSON.stringify({ docked: 'bottom', dockSize: { bottom: 5000, right: 'x' } }), + ); + await loadPopup(true); + parts().fab.click(); + expect(splitter().getAttribute('aria-valuenow')).toBe('720'); + // A size that is not a number is dropped, so that side opens at the default. + parts().dock('right').click(); + expect(splitter().getAttribute('aria-valuenow')).toBe('410'); + }); + }); + + describe('pop-out window', () => { + const notice = () => + document.getElementById('pangular-popup-root')!.shadowRoot!.querySelector('.notice')!; + const popoutButton = () => + document + .getElementById('pangular-popup-root')! + .shadowRoot!.querySelector('.popout-btn') as HTMLButtonElement; + const fakeWindow = () => { + const win = { closed: false, close: vi.fn(), focus: vi.fn(), name: '' }; + win.close.mockImplementation(() => { + win.closed = true; + }); + Object.defineProperty(win, 'parent', { get: () => win }); + return win; + }; + + afterEach(() => { + vi.restoreAllMocks(); + sessionStorage.clear(); + }); + + it('opens the full panel in its own window on this page id, and closes it here', async () => { + sessionStorage.setItem('pangular-page-id', 'k3x9'); + const win = fakeWindow(); + const open = vi.spyOn(window, 'open').mockReturnValue(win as unknown as Window); + await loadPopup(true); + await openedSrc(); + const { fab, panel } = parts(); + expect(popoutButton().getAttribute('aria-label')).toBe( + 'Open Pangular Inspector in its own window', + ); + popoutButton().click(); + await vi.waitFor(() => expect(open).toHaveBeenCalled()); + const [url, name, features] = open.mock.calls[0]; + const base = `${location.origin}/__devframes/pangular/`; + expect(url).toBe(`${base}?baseURL=${encodeURIComponent(base)}&pageId=k3x9`); + expect(name).toBe('pangular-inspector-k3x9'); + expect(features).toMatch(/^popup,width=\d+,height=\d+$/); + expect(panel.classList.contains('open')).toBe(false); + expect(fab.getAttribute('aria-label')).toBe('Bring Pangular Inspector back into the page'); + + // The launcher is the way back: it closes the window and opens the panel here. + fab.click(); + expect(win.close).toHaveBeenCalled(); + expect(panel.classList.contains('open')).toBe(true); + expect(fab.getAttribute('aria-label')).toBe('Toggle Pangular Inspector'); + }); + + it('says so and stays in the page when the browser blocks the window', async () => { + vi.spyOn(window, 'open').mockReturnValue(null); + await loadPopup(true); + await openedSrc(); + popoutButton().click(); + await vi.waitFor(() => expect(notice().textContent).toContain('blocked the new window')); + expect(notice().getAttribute('role')).toBe('status'); + expect(parts().panel.classList.contains('open')).toBe(true); + }); + + it('takes the panel back when the window asks, and only that window', async () => { + const win = fakeWindow(); + vi.spyOn(window, 'open').mockReturnValue(win as unknown as Window); + await loadPopup(true); + await openedSrc(); + popoutButton().click(); + const { panel } = parts(); + await vi.waitFor(() => expect(panel.classList.contains('open')).toBe(false)); + const send = (source: unknown, origin = location.origin) => + window.dispatchEvent( + new MessageEvent('message', { + data: { type: 'pangular:popup-return' }, + source: source as Window, + origin, + }), + ); + send(window); + send(win, 'https://evil.test'); + expect(panel.classList.contains('open')).toBe(false); + send(win); + expect(panel.classList.contains('open')).toBe(true); + expect(win.close).toHaveBeenCalled(); + }); + + it('notices the window was closed by hand', async () => { + vi.useFakeTimers({ toFake: ['setInterval', 'clearInterval'] }); + try { + const win = fakeWindow(); + vi.spyOn(window, 'open').mockReturnValue(win as unknown as Window); + await loadPopup(true); + await openedSrc(); + popoutButton().click(); + const { fab } = parts(); + await vi.waitFor(() => + expect(fab.getAttribute('aria-label')).toBe( + 'Bring Pangular Inspector back into the page', + ), + ); + win.closed = true; + vi.advanceTimersByTime(1000); + expect(fab.getAttribute('aria-label')).toBe('Toggle Pangular Inspector'); + } finally { + vi.useRealTimers(); + } + }); + }); }); diff --git a/packages/devtools/src/__tests__/terminal-commands.test.ts b/packages/devtools/src/__tests__/terminal-commands.test.ts new file mode 100644 index 00000000..e73c305c --- /dev/null +++ b/packages/devtools/src/__tests__/terminal-commands.test.ts @@ -0,0 +1,153 @@ +import { join } from 'node:path'; +import { describe, expect, it } from 'vitest'; +import { + detectTerminalCommands, + isTerminalCommandId, + packageManagerFor, +} from '../terminal-commands.ts'; +import { makeProject } from './analog-fixture.ts'; + +/** A fake file system: paths relative to `/work/app`. */ +function files(entries: Record) { + const map = new Map( + Object.entries(entries).map(([path, value]) => [ + join('/work/app', path), + typeof value === 'string' ? value : JSON.stringify(value), + ]), + ); + return (path: string) => map.get(path); +} + +const ANGULAR_JSON = { + projects: { shop: { architect: { build: {}, serve: {}, test: {} } } }, +}; + +describe('terminal command detection', () => { + it('runs the build and test scripts with the package manager of the lockfile', () => { + const read = files({ + 'package.json': { scripts: { build: 'ng build', test: 'ng test', start: 'ng serve' } }, + 'pnpm-lock.yaml': '', + }); + expect(detectTerminalCommands('/work/app', read)).toEqual([ + { + id: 'build', + title: 'pnpm run build', + command: 'pnpm', + args: ['run', 'build'], + source: 'script', + }, + { + id: 'test', + title: 'pnpm run test', + command: 'pnpm', + args: ['run', 'test'], + source: 'script', + }, + ]); + }); + + it('only ever offers build and test, whatever other scripts the project has', () => { + const read = files({ + 'package.json': { scripts: { deploy: 'rm -rf /', start: 'ng serve', lint: 'eslint .' } }, + }); + expect(detectTerminalCommands('/work/app', read)).toEqual([]); + }); + + it('falls back to ng build and ng test when angular.json has the targets', () => { + const read = files({ + 'package.json': { devDependencies: { '@angular/cli': '^22.0.0' } }, + 'angular.json': ANGULAR_JSON, + 'package-lock.json': '{}', + }); + expect(detectTerminalCommands('/work/app', read)).toEqual([ + { + id: 'build', + title: 'ng build', + command: 'npm', + args: ['exec', '--', 'ng', 'build'], + source: 'angular-cli', + }, + { + id: 'test', + title: 'ng test', + command: 'npm', + args: ['exec', '--', 'ng', 'test'], + source: 'angular-cli', + }, + ]); + }); + + it('runs ng through each package manager', () => { + const pkg = { devDependencies: { '@angular/cli': '^22.0.0' } }; + const argsFor = (lockfile: string) => + detectTerminalCommands( + '/work/app', + files({ 'package.json': pkg, 'angular.json': ANGULAR_JSON, [lockfile]: '' }), + )[0]; + expect(argsFor('pnpm-lock.yaml')).toMatchObject({ + command: 'pnpm', + args: ['exec', 'ng', 'build'], + }); + expect(argsFor('yarn.lock')).toMatchObject({ command: 'yarn', args: ['ng', 'build'] }); + expect(argsFor('bun.lock')).toMatchObject({ command: 'bun', args: ['run', 'ng', 'build'] }); + }); + + it('prefers a script over the Angular CLI target and skips the npm placeholder test script', () => { + const read = files({ + 'package.json': { + scripts: { + build: 'ng build --configuration production', + test: 'echo "Error: no test specified" && exit 1', + }, + devDependencies: { '@angular/cli': '^22.0.0' }, + }, + 'angular.json': { projects: { shop: { targets: { build: {} } } } }, + }); + expect(detectTerminalCommands('/work/app', read)).toEqual([ + expect.objectContaining({ id: 'build', source: 'script', args: ['run', 'build'] }), + ]); + }); + + it('needs the Angular CLI installed for ng commands', () => { + const read = files({ 'package.json': {}, 'angular.json': ANGULAR_JSON }); + expect(detectTerminalCommands('/work/app', read)).toEqual([]); + }); + + it('finds nothing without a package.json, or with one that is not JSON', () => { + expect(detectTerminalCommands('/work/app', files({}))).toEqual([]); + expect(detectTerminalCommands('/work/app', files({ 'package.json': '{oops' }))).toEqual([]); + }); + + it('reads the package manager from packageManager, then the nearest lockfile above the project', () => { + expect( + packageManagerFor('/work/app', files({ 'package.json': { packageManager: 'yarn@4.5.0' } })), + ).toBe('yarn'); + const workspace = (path: string) => + path === '/work/pnpm-lock.yaml' ? '' : path === '/work/app/package.json' ? '{}' : undefined; + expect(packageManagerFor('/work/app', workspace)).toBe('pnpm'); + expect(packageManagerFor('/work/app', files({ 'package.json': {} }))).toBe('npm'); + }); + + it('reads a real project folder', () => { + const cwd = makeProject({ + 'package.json': JSON.stringify({ scripts: { test: 'vitest run' } }), + 'yarn.lock': '', + }); + expect(detectTerminalCommands(cwd)).toEqual([ + { + id: 'test', + title: 'yarn run test', + command: 'yarn', + args: ['run', 'test'], + source: 'script', + }, + ]); + }); + + it('knows its command ids', () => { + expect(isTerminalCommandId('build')).toBe(true); + expect(isTerminalCommandId('test')).toBe(true); + expect(isTerminalCommandId('bash')).toBe(false); + expect(isTerminalCommandId(undefined)).toBe(false); + }); +}); diff --git a/packages/devtools/src/__tests__/terminal-runner.test.ts b/packages/devtools/src/__tests__/terminal-runner.test.ts new file mode 100644 index 00000000..5543b8be --- /dev/null +++ b/packages/devtools/src/__tests__/terminal-runner.test.ts @@ -0,0 +1,332 @@ +import { EventEmitter } from 'node:events'; +import { writeFileSync } from 'node:fs'; +import { join } from 'node:path'; +import type { ChildProcess } from 'node:child_process'; +import { afterEach, describe, expect, it, vi } from 'vitest'; +import type { TerminalCommand } from '../terminal-commands.ts'; +import { + KILL_GRACE_MS, + TerminalRunner, + spawnCommand, + terminalSessionId, + type TerminalSessionEntry, +} from '../terminal-runner.ts'; +import { makeProject } from './analog-fixture.ts'; + +const BUILD: TerminalCommand = { + id: 'build', + title: 'pnpm run build', + command: 'pnpm', + args: ['run', 'build'], + source: 'script', +}; +const TEST: TerminalCommand = { + id: 'test', + title: 'ng test', + command: 'pnpm', + args: ['exec', 'ng', 'test'], + source: 'angular-cli', +}; + +/** A stand-in for `ctx.terminals` that reads each session's stream like the hub does. */ +function fakeHost() { + const sessions = new Map(); + const output = new Map(); + const readers = new Map>(); + return { + sessions, + output: (id: string) => output.get(id) ?? '', + register: vi.fn((session: TerminalSessionEntry) => { + sessions.set(session.id, session); + output.set(session.id, ''); + const reader = session.stream.getReader(); + readers.set(session.id, reader); + void (async () => { + for (;;) { + const { done, value } = await reader.read(); + if (done) break; + output.set(session.id, (output.get(session.id) ?? '') + value); + } + })().catch(() => undefined); + return session; + }), + update: vi.fn((patch: Partial & { id: string }) => { + Object.assign(sessions.get(patch.id)!, patch); + }), + // Like the hub: dispose the bound stream, then forget the session. + remove: vi.fn((session: { id: string }) => { + void readers.get(session.id)?.cancel('removed'); + sessions.delete(session.id); + }), + }; +} + +class FakeChild extends EventEmitter { + stdout = new EventEmitter(); + stderr = new EventEmitter(); + constructor(readonly pid: number) { + super(); + } + print(text: string, stream: 'stdout' | 'stderr' = 'stdout') { + this[stream].emit('data', Buffer.from(text)); + } + exit(code: number | null, signal: NodeJS.Signals | null = null) { + this.emit('close', code, signal); + } +} + +function setup(commands: TerminalCommand[] = [BUILD, TEST]) { + const host = fakeHost(); + const children: FakeChild[] = []; + const spawn = vi.fn((_command: TerminalCommand, _cwd: string) => { + const child = new FakeChild(1000 + children.length); + children.push(child); + return child as unknown as ChildProcess; + }); + const kill = vi.fn((child: ChildProcess, signal: NodeJS.Signals) => { + if (signal === 'SIGKILL') (child as unknown as FakeChild).exit(null, 'SIGKILL'); + }); + const runner = new TerminalRunner(host, commands, { + cwd: '/work/app', + spawn, + kill, + hookProcessExit: false, + }); + return { host, runner, spawn, kill, children }; +} + +const flush = () => new Promise((resolve) => setTimeout(resolve, 0)); + +afterEach(() => { + vi.useRealTimers(); +}); + +describe('terminal runner', () => { + it('runs a detected command as a hub terminal session and streams its output', async () => { + const { host, runner, spawn, children } = setup(); + const id = runner.start('build'); + expect(id).toBe(terminalSessionId('build')); + expect(spawn).toHaveBeenCalledWith(BUILD, '/work/app'); + const entry = host.sessions.get(id)!; + expect(entry).toMatchObject({ + title: 'pnpm run build', + status: 'running', + interactive: false, + restartable: true, + }); + children[0].print('Building...\n'); + children[0].print('warning\n', 'stderr'); + children[0].exit(0); + await flush(); + expect(host.output(id)).toContain('$ pnpm run build'); + expect(host.output(id)).toContain('Building...\nwarning\n'); + expect(host.output(id)).toContain('[exited with code 0]'); + expect(entry.status).toBe('stopped'); + expect(runner.isRunning('build')).toBe(false); + }); + + it('marks a failed run as an error', async () => { + const { host, runner, children } = setup(); + const id = runner.start('test'); + children[0].exit(1); + await flush(); + expect(host.sessions.get(id)!.status).toBe('error'); + expect(host.output(id)).toContain('[exited with code 1]'); + }); + + it('refuses any command it did not detect and never spawns for it', () => { + const { runner, spawn } = setup([BUILD]); + expect(() => runner.start('test')).toThrow('No test command was found in this project.'); + expect(() => runner.start('rm -rf /' as never)).toThrow('was found in this project'); + expect(spawn).not.toHaveBeenCalled(); + expect(runner.commands.map((c) => c.id)).toEqual(['build']); + }); + + it('masks bearer tokens and JWTs in the output, also across chunks', async () => { + const { host, runner, children } = setup(); + const id = runner.start('test'); + const jwt = + 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4ifQ.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U'; + children[0].print('Authorization: Bearer abc.def'); + children[0].print('ghi123\n'); + children[0].print(`token ${jwt.slice(0, 40)}`); + children[0].print(`${jwt.slice(40)}\n`); + children[0].exit(0); + await flush(); + const text = host.output(id); + expect(text).not.toContain('abc.defghi123'); + expect(text).not.toContain(jwt); + expect(text).toContain('Bearer'); + }); + + it('shows a partial line after a short wait, for progress output', async () => { + vi.useFakeTimers(); + const { host, runner, children } = setup(); + const id = runner.start('build'); + children[0].print('Building 42%'); + await vi.advanceTimersByTimeAsync(200); + expect(host.output(id)).toContain('Building 42%'); + }); + + it('stops a run with SIGTERM, then SIGKILL after the grace period, and keeps the output', async () => { + vi.useFakeTimers(); + const { host, runner, kill, children } = setup(); + const id = runner.start('build'); + children[0].print('compiling\n'); + runner.stop('build'); + expect(kill).toHaveBeenCalledWith(children[0], 'SIGTERM'); + expect(kill).not.toHaveBeenCalledWith(children[0], 'SIGKILL'); + await vi.advanceTimersByTimeAsync(KILL_GRACE_MS); + expect(kill).toHaveBeenCalledWith(children[0], 'SIGKILL'); + expect(host.sessions.get(id)!.status).toBe('stopped'); + expect(host.output(id)).toContain('compiling'); + expect(host.output(id)).toContain('[stopped]'); + }); + + it('does not force-kill a run that exits after SIGTERM', async () => { + vi.useFakeTimers(); + const { runner, kill, children } = setup(); + runner.start('build'); + runner.stop('build'); + children[0].exit(null, 'SIGTERM'); + await vi.advanceTimersByTimeAsync(KILL_GRACE_MS * 2); + expect(kill).toHaveBeenCalledTimes(1); + }); + + it('restarts in the same session: stops the running command, then runs it again', async () => { + const { host, runner, spawn, kill, children } = setup(); + const id = runner.start('build'); + expect(runner.start('build')).toBe(id); + expect(kill).toHaveBeenCalledWith(children[0], 'SIGTERM'); + expect(spawn).toHaveBeenCalledTimes(2); + expect(host.register).toHaveBeenCalledTimes(1); + // Output from the old run no longer reaches the session. + children[0].print('old\n'); + children[1].print('new\n'); + children[0].exit(null, 'SIGTERM'); + await flush(); + expect(host.output(id)).toContain('[restarting]'); + expect(host.output(id)).not.toContain('old'); + expect(host.output(id)).toContain('new'); + expect(host.sessions.get(id)!.status).toBe('running'); + }); + + it('runs again after a run ended, through the session the hub restarts', async () => { + const { host, runner, spawn, children } = setup(); + const id = runner.start('build'); + children[0].exit(0); + await flush(); + const entry = host.sessions.get(id)!; + // `hub:terminals:restart` and `hub:terminals:terminate` call these. + await entry.restart(); + expect(spawn).toHaveBeenCalledTimes(2); + expect(entry.status).toBe('running'); + await entry.terminate(); + children[1].exit(null, 'SIGTERM'); + await flush(); + expect(entry.status).toBe('stopped'); + }); + + it('stops the command when the hub removes its session, and starts a fresh session next time', async () => { + const { host, runner, kill, children } = setup(); + const id = runner.start('build'); + host.remove(host.sessions.get(id)!); + await flush(); + expect(kill).toHaveBeenCalledWith(children[0], 'SIGTERM'); + runner.start('build'); + expect(host.register).toHaveBeenCalledTimes(2); + expect(host.sessions.has(id)).toBe(true); + }); + + it('stops every command and removes the sessions on dispose', async () => { + const { host, runner, kill, children } = setup(); + runner.start('build'); + runner.start('test'); + runner.dispose(); + expect(kill).toHaveBeenCalledWith(children[0], 'SIGTERM'); + expect(kill).toHaveBeenCalledWith(children[1], 'SIGTERM'); + expect(host.sessions.size).toBe(0); + expect(() => runner.start('build')).toThrow('closed'); + }); + + it('reports a command that cannot start', async () => { + const { host } = setup(); + const runner = new TerminalRunner(host, [BUILD], { + cwd: '/work/app', + spawn: () => { + throw new Error('spawn pnpm ENOENT'); + }, + hookProcessExit: false, + }); + const id = runner.start('build'); + await flush(); + expect(host.sessions.get(id)!.status).toBe('error'); + expect(host.output(id)).toContain('could not start: spawn pnpm ENOENT'); + }); + + it('hooks process exit while a command runs, and unhooks when it ends', () => { + const host = fakeHost(); + const child = new FakeChild(7); + const kill = vi.fn(); + const before = process.listeners('exit'); + const runner = new TerminalRunner(host, [BUILD], { + cwd: '/work/app', + spawn: () => child as unknown as ChildProcess, + kill, + }); + runner.start('build'); + const added = process.listeners('exit').filter((listener) => !before.includes(listener)); + expect(added).toHaveLength(1); + expect(process.listenerCount('SIGINT')).toBeGreaterThan(0); + // Only the runner's own listener, not the test runner's. + (added[0] as (code: number) => void)(0); + expect(kill).toHaveBeenCalledWith(child, 'SIGTERM'); + child.exit(0); + expect(process.listeners('exit')).toEqual(before); + runner.dispose(); + }); +}); + +describe.skipIf(process.platform === 'win32')('terminal runner with real processes', () => { + it('kills the command and the processes it started when the runner is disposed', async () => { + const dir = makeProject({}); + const script = join(dir, 'parent.mjs'); + writeFileSync( + script, + [ + "import { spawn } from 'node:child_process';", + "const child = spawn(process.execPath, ['-e', 'setInterval(() => {}, 1000)'], { stdio: 'ignore' });", + 'console.log(`pids ${process.pid} ${child.pid}`);', + 'setInterval(() => {}, 1000);', + ].join('\n'), + ); + const host = fakeHost(); + const command = { ...BUILD, command: process.execPath as never, args: [script] }; + const runner = new TerminalRunner(host, [command], { + cwd: dir, + spawn: spawnCommand, + hookProcessExit: false, + }); + const id = runner.start('build'); + let pids: number[] = []; + await vi.waitFor( + () => { + const match = /pids (\d+) (\d+)/.exec(host.output(id)); + expect(match).not.toBeNull(); + pids = [Number(match![1]), Number(match![2])]; + }, + { timeout: 5000 }, + ); + const alive = (pid: number) => { + try { + process.kill(pid, 0); + return true; + } catch { + return false; + } + }; + expect(pids.every(alive)).toBe(true); + runner.dispose(); + await vi.waitFor(() => expect(pids.filter(alive)).toEqual([]), { timeout: 5000 }); + }); +}); diff --git a/packages/devtools/src/__tests__/vite-terminals.test.ts b/packages/devtools/src/__tests__/vite-terminals.test.ts new file mode 100644 index 00000000..52449055 --- /dev/null +++ b/packages/devtools/src/__tests__/vite-terminals.test.ts @@ -0,0 +1,44 @@ +import { describe, expect, it, vi } from 'vitest'; + +const hubOptions = vi.hoisted(() => [] as Record[]); + +vi.mock('../hub.ts', () => ({ + PANGULAR_HUB_BASE: '/__devframes/', + startPangularHub: (options: Record) => { + hubOptions.push(options); + return { nodeMiddleware: vi.fn(), close: vi.fn() }; + }, +})); + +const { default: pangularVite } = await import('../vite.ts'); + +function hubTerminals(options: Parameters[0]) { + hubOptions.length = 0; + const warn = vi.fn(); + const plugin = pangularVite({ apiPrefix: 'api', ...options }); + (plugin.configureServer as (server: unknown) => void)({ + config: { root: process.cwd(), server: {}, logger: { warn, info: vi.fn() } }, + middlewares: { use: vi.fn() }, + httpServer: null, + }); + return { terminals: hubOptions[0]?.['terminals'], warn }; +} + +describe('Vite plugin terminals option', () => { + it('is off by default and on with terminals: true', () => { + expect(hubTerminals({}).terminals).toBe(false); + expect(hubTerminals({ terminals: true }).terminals).toBe(true); + }); + + it('stays off when other machines can reach the dev server', () => { + const { terminals, warn } = hubTerminals({ + terminals: true, + allowLan: true, + allowedOrigins: ['http://192.168.1.20:5173'], + }); + expect(terminals).toBe(false); + expect(warn).toHaveBeenCalledWith( + '[pangular] The terminals dock is off: allowLan lets other machines reach the hub.', + ); + }); +}); diff --git a/packages/devtools/src/config.ts b/packages/devtools/src/config.ts index 95db1102..25360a01 100644 --- a/packages/devtools/src/config.ts +++ b/packages/devtools/src/config.ts @@ -312,6 +312,7 @@ export function hotkeyLabel(hotkey: string, mac: boolean): string { */ export const PANEL_TABS = [ { id: 'dashboard', label: 'Dashboard' }, + { id: 'timeline', label: 'Timeline' }, { id: 'analog', label: 'Analog', inspector: 'analog' }, { id: 'components', label: 'Components', inspector: 'components' }, { id: 'routes', label: 'Routes', inspector: 'router' }, @@ -386,7 +387,10 @@ export interface PanelCommand { group: PanelCommandGroup; /** Extra words the palette filter matches. */ keywords?: string; - /** Only the popup has dock modes, so the hub leaves these out. */ + /** + * Only the popup has dock modes and a pop-out window, so the hub leaves + * these out. `popout:return` shows in the pop-out window instead. + */ popupOnly?: boolean; } @@ -438,6 +442,27 @@ const PANEL_ACTIONS: (PanelCommand & { inspector?: PangularInspector })[] = [ keywords: 'dock', popupOnly: true, }, + { + id: 'dock:reset-size', + title: 'Popup: reset the docked size', + group: 'Popup', + keywords: 'splitter resize height width', + popupOnly: true, + }, + { + id: 'popout', + title: 'Popup: open in its own window', + group: 'Popup', + keywords: 'pop out detach new window', + popupOnly: true, + }, + { + id: 'popout:return', + title: 'Popup: back into the page', + group: 'Popup', + keywords: 'pop in attach dock window', + popupOnly: true, + }, ]; /** Every panel command under `config`: one per enabled tab, then the actions. */ diff --git a/packages/devtools/src/hub-terminals-dock.ts b/packages/devtools/src/hub-terminals-dock.ts new file mode 100644 index 00000000..916dd8ad --- /dev/null +++ b/packages/devtools/src/hub-terminals-dock.ts @@ -0,0 +1,132 @@ +import createTerminalsDevframe from '@devframes/plugin-terminals'; +import { PLUGIN_ID } from '@devframes/plugin-terminals/constants'; +import { TerminalManager, setTerminalManager } from '@devframes/plugin-terminals/node'; +import { serverFunctions } from '@devframes/plugin-terminals/rpc'; +import { defineDevframe, type DevframeDefinition } from 'devframe'; +import { + isTerminalCommandId, + type TerminalCommand, + type TerminalCommandId, +} from './terminal-commands.ts'; + +/** The dock id of the terminals devframe in the hub rail. */ +export const TERMINALS_DOCK_ID = PLUGIN_ID; + +/** The plugin's spawn RPC, which Pangular replaces with `guardedSpawn`. */ +export const TERMINALS_SPAWN_RPC = 'devframes:plugin:terminals:spawn'; + +/** The hub's own terminal stream, where the runner's sessions write. */ +const HUB_TERMINAL_STREAM = 'devframe:terminals'; + +/** What the dock needs from the runner. */ +export interface TerminalsDockRunner { + readonly commands: readonly TerminalCommand[]; + start: (id: TerminalCommandId) => string; +} + +const ALLOWED_SPAWN_KEYS = new Set(['presetId', 'cols', 'rows', 'title', 'mode']); + +/** + * Reads a spawn request from the terminals dock and returns the command id it + * names, or throws. Only `{presetId}` for a detected command passes, with the + * terminal size and title the dock adds. A command, arguments, a working + * directory, environment variables or an interactive mode are refused, and so + * is a request without a preset, which would start a shell. + */ +export function guardSpawnRequest( + request: unknown, + commands: readonly TerminalCommand[], +): TerminalCommandId { + const input = + request && typeof request === 'object' && !Array.isArray(request) + ? (request as Record) + : {}; + const extra = Object.keys(input).filter( + (key) => !ALLOWED_SPAWN_KEYS.has(key) && input[key] !== undefined, + ); + if (extra.length) { + throw new Error( + `Pangular Inspector only runs the project's build and test commands; ${extra.join(', ')} cannot be set.`, + ); + } + if (input['mode'] !== undefined && input['mode'] !== 'readonly') { + throw new Error('Pangular Inspector terminals are read-only.'); + } + const id = input['presetId']; + if (!isTerminalCommandId(id) || !commands.some((command) => command.id === id)) { + throw new Error( + "Pangular Inspector only runs the project's build and test commands. Pick one of them.", + ); + } + return id; +} + +type RpcDefinition = (typeof serverFunctions)[number]; + +/** + * The plugin's RPC functions as the dock registers them: without `agent`, so + * none of them becomes an MCP tool, and with the spawn handler replaced by one + * that starts a detected command through the runner and nothing else. + */ +export function dockRpcFunctions(runner: TerminalsDockRunner): RpcDefinition[] { + return serverFunctions.map((fn) => { + const { agent: _agent, ...rest } = fn as RpcDefinition & { agent?: unknown }; + if (fn.name !== TERMINALS_SPAWN_RPC) return rest as RpcDefinition; + return { + ...rest, + setup: () => ({ + handler: (request: unknown) => { + const id = guardSpawnRequest(request, runner.commands); + const command = runner.commands.find((c) => c.id === id)!; + const sessionId = runner.start(id); + // The shape of a hub session in the plugin's own list, so the dock focuses it. + return { + id: sessionId, + title: command.title, + mode: 'readonly' as const, + status: 'running' as const, + backend: 'pipe' as const, + command: '', + args: [], + cwd: '', + cols: 80, + rows: 24, + createdAt: 0, + channel: HUB_TERMINAL_STREAM, + }; + }, + }), + } as unknown as RpcDefinition; + }); +} + +/** + * The terminals dock: `@devframes/plugin-terminals` for its panel (xterm + * views, stop and restart buttons, the session list), with Pangular's setup. + * The plugin would start a shell or any command a client sends, so the setup + * registers the plugin's RPC functions through `dockRpcFunctions` instead: + * its presets are the detected commands, and its spawn RPC only starts those, + * through the runner, as hub sessions. + */ +export function createTerminalsDock(runner: TerminalsDockRunner): DevframeDefinition { + const plugin = createTerminalsDevframe(); + return defineDevframe({ + ...plugin, + async setup(ctx) { + const manager = new TerminalManager(ctx, { + presets: runner.commands.map((command) => ({ + id: command.id, + title: command.title, + command: command.command, + args: command.args, + mode: 'readonly', + icon: command.id === 'test' ? 'ph:flask-duotone' : 'ph:hammer-duotone', + })), + allowArbitraryCommands: false, + }); + setTerminalManager(ctx, manager); + await manager.init(); + for (const fn of dockRpcFunctions(runner)) ctx.rpc.register(fn as never); + }, + }); +} diff --git a/packages/devtools/src/hub-terminals.ts b/packages/devtools/src/hub-terminals.ts new file mode 100644 index 00000000..a511c63d --- /dev/null +++ b/packages/devtools/src/hub-terminals.ts @@ -0,0 +1,189 @@ +import type { InitHubOptions } from '@devframes/hub/initiate'; +import { PLUGIN_ID as TERMINALS_DOCK_ID } from '@devframes/plugin-terminals/constants'; +import type { DevframeDefinition } from 'devframe'; +import { detectTerminalCommands, type TerminalCommand } from './terminal-commands.ts'; +import { + TerminalRunner, + type TerminalRunnerOptions, + type TerminalsHostLike, +} from './terminal-runner.ts'; + +/** Why the hub runs no terminals although `terminals: true` was passed, or `undefined`. */ +export function terminalsRefusal(context: { + env?: Record; + /** The hub's origin check is off (`allowedOrigins: false`). */ + originCheckOff?: boolean; + /** Other machines can reach the hub (`allowLan`). */ + remote?: boolean; +}): string | undefined { + if ((context.env ?? process.env)['NODE_ENV'] === 'production') { + return 'NODE_ENV is production'; + } + if (context.originCheckOff) return 'allowedOrigins is false, so the origin check is off'; + if (context.remote) return 'allowLan lets other machines reach the hub'; + return undefined; +} + +/** + * Whether the hub runs the terminals dock: only when `terminals` is `true`, + * in development, behind the hub's origin check and for this machine only. + * Any other value keeps it off; a value that is not a boolean, or `true` in a + * setup that refuses it, says so once through `warn`. + */ +export function terminalsEnabled( + terminals: unknown, + context: Parameters[0], + warn: (message: string) => void = console.warn, +): boolean { + if (terminals === undefined || terminals === false) return false; + if (terminals !== true) { + warn(`[pangular] Ignoring terminals: ${JSON.stringify(terminals)}. Pass true to turn it on.`); + return false; + } + const refusal = terminalsRefusal(context); + if (refusal) { + warn(`[pangular] The terminals dock is off: ${refusal}.`); + return false; + } + return true; +} + +interface HubTerminalsContext { + cwd?: string; + terminals?: unknown; + commands?: { register: (command: TerminalPaletteCommand) => unknown }; + docks?: { activate?: (dockId: string, params?: Record) => void }; + install: (devframe: DevframeDefinition, options?: { base?: string }) => Promise; +} + +interface TerminalPaletteCommand { + id: string; + title: string; + category: string; + description?: string; + handler: () => void; +} + +function isTerminalsHost(value: unknown): value is TerminalsHostLike { + const host = value as Partial | null; + return ( + !!host && + host.sessions instanceof Map && + typeof host.register === 'function' && + typeof host.update === 'function' && + typeof host.remove === 'function' + ); +} + +const CATEGORY = 'Pangular Inspector'; + +/** The hub palette commands for the detected commands: run (or run again) and stop each one. */ +export function registerTerminalCommands( + ctx: Pick, + runner: TerminalRunner, +): string[] { + if (typeof ctx.commands?.register !== 'function') return []; + const ids: string[] = []; + for (const command of runner.commands) { + const run = { + id: `pangular:terminal:run-${command.id}`, + title: `Pangular: run ${command.title}`, + category: CATEGORY, + description: `Runs the project's ${command.id} command in the terminals dock, or runs it again.`, + handler: () => { + const sessionId = runner.start(command.id); + ctx.docks?.activate?.(TERMINALS_DOCK_ID, { sessionId }); + }, + }; + const stop = { + id: `pangular:terminal:stop-${command.id}`, + title: `Pangular: stop ${command.title}`, + category: CATEGORY, + handler: () => runner.stop(command.id), + }; + ctx.commands.register(run); + ctx.commands.register(stop); + ids.push(run.id, stop.id); + } + return ids; +} + +/** Holds the runner the hub started, so closing the hub stops its commands. */ +export interface HubTerminals { + runner?: TerminalRunner; + dispose: () => void; +} + +export function hubTerminals(): HubTerminals { + const handle: HubTerminals = { + dispose: () => { + handle.runner?.dispose(); + handle.runner = undefined; + }, + }; + return handle; +} + +export interface SetupTerminalsOptions { + /** Defaults to `ctx.cwd`, then the process directory. */ + cwd?: string; + /** Defaults to `detectTerminalCommands(cwd)`. */ + commands?: TerminalCommand[]; + runner?: Omit; + warn?: (message: string) => void; +} + +/** + * Starts the terminals in a hub context: detects the build and test commands, + * runs them as `ctx.terminals` sessions, installs the terminals dock under the + * hub base, and adds the run and stop commands to the hub palette. Does + * nothing outside a hub or when the project has neither command. + */ +export async function setupHubTerminals( + ctx: HubTerminalsContext, + hubBase: string, + handle: HubTerminals, + options: SetupTerminalsOptions = {}, +): Promise { + const warn = options.warn ?? console.warn; + if (!isTerminalsHost(ctx.terminals)) return undefined; + const cwd = options.cwd ?? ctx.cwd ?? process.cwd(); + const commands = options.commands ?? detectTerminalCommands(cwd); + if (!commands.length) { + warn( + `[pangular] The terminals dock is off: no build or test script in package.json and no Angular CLI target in angular.json under ${cwd}.`, + ); + return undefined; + } + handle.runner?.dispose(); + const runner = new TerminalRunner(ctx.terminals, commands, { ...options.runner, cwd }); + handle.runner = runner; + // Loaded here, so a hub without terminals never loads the plugin. + const { createTerminalsDock } = await import('./hub-terminals-dock.ts'); + await ctx.install(createTerminalsDock(runner), { base: `${hubBase}${TERMINALS_DOCK_ID}/` }); + registerTerminalCommands(ctx, runner); + return runner; +} + +/** + * Adds the terminals to the hub before the app's own `configure` runs, when + * `enabled`. `handle` keeps the runner so the hub's `close()` stops it. + */ +export function withTerminals>( + options: T, + hubBase: string, + enabled: boolean, + handle: HubTerminals, +): T { + if (!enabled) return options; + const { configure } = options; + return { + ...options, + configure: async (ctx) => { + await setupHubTerminals(ctx as unknown as HubTerminalsContext, hubBase, handle, { + cwd: options.cwd, + }); + await configure?.(ctx); + }, + }; +} diff --git a/packages/devtools/src/hub.ts b/packages/devtools/src/hub.ts index f0c02ede..11a5b7f8 100644 --- a/packages/devtools/src/hub.ts +++ b/packages/devtools/src/hub.ts @@ -9,6 +9,7 @@ import type { WsOriginRegistry } from 'devframe/rpc/transports/ws-server'; import { isAllowedOrigin } from 'devframe/utils/origin'; import { createPangular } from './devframe.ts'; import { withMessagesPanel } from './hub-messages-panel.ts'; +import { hubTerminals, terminalsEnabled, withTerminals } from './hub-terminals.ts'; import { blockedOriginReporter, isAllowedHubRequest, @@ -51,6 +52,13 @@ export type PangularHubOptions = Partial = {}, gate?: HubOriginPolicy, ): PangularHub { - const { browserDistFolder, auth: authOption, ...hubOptions } = options; + const { browserDistFolder, auth: authOption, terminals: terminalsOn, ...hubOptions } = options; const { config, rest: picked } = pickPangularConfig(hubOptions); // Behind a gate, a shared `server` is attached through the gated `attach` // below, so its upgrades pass the same checks as `handleUpgrade`. const { server: sharedServer, ...hubRest } = picked; - const rest = withMessagesPanel(hubRest, normalizeHubBase(hubRest.base ?? PANGULAR_HUB_BASE)); + const hubBase = normalizeHubBase(hubRest.base ?? PANGULAR_HUB_BASE); + const terminals = hubTerminals(); + const rest = withTerminals( + withMessagesPanel(hubRest, hubBase), + hubBase, + terminalsOn === true, + terminals, + ); const authTokens = isAuthTokens(authOption) ? authTokensFrom(authOption.tokens) : []; const auth = isAuthTokens(authOption) ? undefined : authOption; const hub = initHub({ @@ -201,6 +216,7 @@ export function startPangularHub( close: () => { if (hubs.get(hub.base) === own) hubs.delete(hub.base); detachShared?.(); + terminals.dispose(); // Stop the HTTP registry timers and SSR capture this hub installed, as Vite does. return (closing ??= hub.context .then(releaseServerState, () => undefined) @@ -335,7 +351,7 @@ function disabledHub(base: string): PangularHub { * `Host`, and the origin check. Off in production unless `enabled` is `true`. */ export function initPangularHub(options: PangularHubOptions = {}): PangularHub { - const { enabled, allowLan, ...hubOptions } = options; + const { enabled, allowLan, terminals, ...hubOptions } = options; const base = normalizeHubBase(hubOptions.base ?? PANGULAR_HUB_BASE); if (!hubEnabled(enabled)) return disabledHub(base); const authOn = hubOptions.auth !== false; @@ -345,9 +361,14 @@ export function initPangularHub(options: PangularHubOptions = {}): PangularHub { ); } // `allowedOrigins: false` turns the gate off; startPangularHub prints that warning. + const remote = allowLan === true && authOn; const policy = hubOptions.allowedOrigins === false ? undefined - : hubGatePolicy(hubAllowedOrigins(hubOptions.allowedOrigins), allowLan === true && authOn); - return startPangularHub(hubOptions, policy); + : hubGatePolicy(hubAllowedOrigins(hubOptions.allowedOrigins), remote); + const terminalsOn = terminalsEnabled(terminals, { + originCheckOff: hubOptions.allowedOrigins === false, + remote, + }); + return startPangularHub({ ...hubOptions, terminals: terminalsOn }, policy); } diff --git a/packages/devtools/src/popup.ts b/packages/devtools/src/popup.ts index 26a539ca..1d925e66 100644 --- a/packages/devtools/src/popup.ts +++ b/packages/devtools/src/popup.ts @@ -21,6 +21,45 @@ interface PopupState { theme?: 'light' | 'dark'; /** Passive mode: the launcher stays hidden until the shortcut opens the panel. */ passive?: boolean; + /** The size in px the splitter left each docked side at; the default is 40% of the window. */ + dockSize?: Partial>; +} + +type DockSide = 'bottom' | 'right'; + +/** The smallest a docked panel gets, in px, so its toolbar and some content stay usable. */ +const DOCK_MIN: Record = { bottom: 120, right: 280 }; +/** Room a docked panel always leaves for the app, in px. */ +const DOCK_GAP = 48; +/** How far an arrow key moves the splitter, and with Shift held. */ +const DOCK_STEP = 16; +const DOCK_STEP_LARGE = 64; + +function windowSpan(side: DockSide): number { + return side === 'bottom' ? window.innerHeight : window.innerWidth; +} + +/** The sizes a docked side can take in the current window. */ +function dockLimits(side: DockSide): { min: number; max: number } { + const max = Math.max(0, windowSpan(side) - DOCK_GAP); + return { min: Math.min(DOCK_MIN[side], max), max }; +} + +/** A docked size kept inside the window, the default when there is none. */ +function clampDockSize(side: DockSide, size: number | undefined): number { + const { min, max } = dockLimits(side); + const wanted = size ?? windowSpan(side) * 0.4; + return Math.round(Math.min(max, Math.max(min, wanted))); +} + +function dockSizes(value: unknown): PopupState['dockSize'] | undefined { + if (!value || typeof value !== 'object') return undefined; + const sizes: Partial> = {}; + for (const side of ['bottom', 'right'] as const) { + const size = (value as Record)[side]; + if (typeof size === 'number' && Number.isFinite(size) && size > 0) sizes[side] = size; + } + return Object.keys(sizes).length ? sizes : undefined; } const DEFAULT_STATE: PopupState = { x: 16, y: 16, width: 720, height: 480, docked: 'float' }; @@ -49,6 +88,7 @@ function loadState(): PopupState { : {}), ...(saved.theme === 'light' || saved.theme === 'dark' ? { theme: saved.theme } : {}), ...(saved.passive === true ? { passive: true } : {}), + ...(dockSizes(saved.dockSize) ? { dockSize: dockSizes(saved.dockSize) } : {}), }; } catch { return { ...DEFAULT_STATE }; @@ -105,16 +145,38 @@ async function servesJson(url: string): Promise { } } -function panelUrl(base: string): string { - let pageId = ''; +/** The id this page claimed, or `''` when the overlay has not claimed one yet. */ +function currentPageId(): string { try { - pageId = sessionStorage.getItem('pangular-page-id') ?? ''; + return sessionStorage.getItem('pangular-page-id') ?? ''; } catch { // Storage can be blocked; the panel then shows the latest page. + return ''; } +} + +function panelUrl(base: string): string { + const pageId = currentPageId(); return `${base}?baseURL=${encodeURIComponent(base)}&pageId=${encodeURIComponent(pageId)}`; } +/** + * What the pop-out window loads. A hub's full panel opens alone with this + * page's id in its URL, so the window keeps following this tab. A panel URL + * carries the id already, and a `src` given to the popup is used as it is. + */ +function popoutUrl(src: string): string { + if (explicitSrc !== undefined) return src; + const url = new URL(src, location.href); + if (url.searchParams.has('pageId')) return url.href; + return panelUrl(new URL('pangular/', url).href); +} + +/** The pop-out window's name, one per page, so opening it again reuses the window. */ +function popoutName(): string { + return `pangular-inspector-${currentPageId() || 'panel'}`; +} + /** The hub that serves a devtools frame, when the frame sits at `pangular/`. */ function hubOf(base: string): string | undefined { const url = new URL(base, location.href); @@ -266,6 +328,14 @@ export function createDevtoolsPopup(options: { src?: string; hotkey?: string | f saveState(state); } + const popoutBtn = document.createElement('button'); + popoutBtn.type = 'button'; + popoutBtn.classList.add('popout-btn'); + popoutBtn.textContent = '⧉'; + popoutBtn.title = 'Open in its own window'; + popoutBtn.setAttribute('aria-label', 'Open Pangular Inspector in its own window'); + popoutBtn.addEventListener('click', () => void popOut()); + const closeBtn = document.createElement('button'); closeBtn.type = 'button'; closeBtn.classList.add('close-btn'); @@ -275,7 +345,7 @@ export function createDevtoolsPopup(options: { src?: string; hotkey?: string | f closeBtn.addEventListener('click', togglePanel); toolbar.title = 'Drag to move. Double click to reset the position.'; - toolbar.append(title, dockGroup, closeBtn); + toolbar.append(title, dockGroup, popoutBtn, closeBtn); // Iframe const iframe = document.createElement('iframe'); @@ -321,7 +391,24 @@ export function createDevtoolsPopup(options: { src?: string; hotkey?: string | f missingActions.append(retryBtn, setupLink); missing.append(missingTitle, missingHint, missingActions); - panel.append(toolbar, iframe, missing, missingStatus); + // The splitter sits on the docked edge. It is a focusable separator, so the + // size can be set with the keyboard as well as by dragging. + panel.id = 'pangular-panel'; + const splitter = document.createElement('div'); + splitter.classList.add('splitter'); + splitter.setAttribute('role', 'separator'); + splitter.setAttribute('aria-label', 'Resize the panel'); + splitter.setAttribute('aria-controls', panel.id); + splitter.title = 'Drag or use the arrow keys to resize. Double click to reset the size.'; + splitter.tabIndex = 0; + splitter.hidden = true; + + // Says why the panel stayed in the page when the browser blocked the window. + const notice = document.createElement('p'); + notice.classList.add('notice'); + notice.setAttribute('role', 'status'); + + panel.append(splitter, toolbar, notice, iframe, missing, missingStatus); // Styles const style = document.createElement('style'); @@ -402,7 +489,7 @@ export function createDevtoolsPopup(options: { src?: string; hotkey?: string | f transform: scale(1.06); } /* The shadow root cannot inherit the page's focus styles. */ - .dock-btn:focus-visible, .close-btn:focus-visible { + .dock-btn:focus-visible, .close-btn:focus-visible, .popout-btn:focus-visible { outline: 2px solid var(--_text-str); outline-offset: 2px; } @@ -459,7 +546,7 @@ export function createDevtoolsPopup(options: { src?: string; hotkey?: string | f bottom: 0 !important; top: auto !important; width: 100% !important; - height: 40vh !important; + height: var(--_dock-size, 40vh) !important; border-radius: 10px 10px 0 0; resize: none; } @@ -468,11 +555,43 @@ export function createDevtoolsPopup(options: { src?: string; hotkey?: string | f right: 0 !important; bottom: 0 !important; left: auto !important; - width: 40vw !important; + width: var(--_dock-size, 40vw) !important; height: 100% !important; border-radius: 10px 0 0 10px; resize: none; } + .splitter { + position: absolute; + z-index: 1; + background: transparent; + touch-action: none; + transition: background 150ms ease; + } + .splitter[hidden] { display: none; } + .dock-bottom .splitter { top: 0; left: 0; right: 0; height: 6px; cursor: ns-resize; } + .dock-right .splitter { top: 0; bottom: 0; left: 0; width: 6px; cursor: ew-resize; } + .splitter:hover, .resizing .splitter { background: var(--_accent); } + .splitter:focus-visible { + background: var(--_accent); + outline: 2px solid var(--_text-str); + outline-offset: -2px; + } + /* The frame would take the pointer while it crosses it. */ + .resizing .frame { pointer-events: none; } + @media (prefers-reduced-motion: reduce) { + .splitter { transition: none; } + } + .notice { + margin: 0; + padding: 6px 12px; + font-family: system-ui, sans-serif; + font-size: 12px; + line-height: 1.4; + color: var(--_text); + background: var(--_surface); + border-bottom: 1px solid var(--_border); + } + .notice:empty { display: none; } .toolbar { cursor: grab; display: flex; @@ -497,7 +616,7 @@ export function createDevtoolsPopup(options: { src?: string; hotkey?: string | f display: flex; gap: 2px; } - .dock-btn, .close-btn { + .dock-btn, .close-btn, .popout-btn { border: none; background: transparent; color: var(--_text-dim); @@ -507,7 +626,7 @@ export function createDevtoolsPopup(options: { src?: string; hotkey?: string | f border-radius: 4px; line-height: 1; } - .dock-btn:hover, .close-btn:hover { background: var(--_hover-bg); color: var(--_hover-fg); } + .dock-btn:hover, .close-btn:hover, .popout-btn:hover { background: var(--_hover-bg); color: var(--_hover-fg); } .dock-btn.active { color: var(--_accent); } .close-btn { font-size: 13px; } .frame { @@ -745,9 +864,178 @@ export function createDevtoolsPopup(options: { src?: string; hotkey?: string | f panel.style.width = ''; panel.style.height = ''; } + applySplitter(); + } + + /** Sizes a docked panel from the saved size, and gives the splitter its range. */ + function applySplitter() { + const side = state.docked; + splitter.hidden = side === 'float'; + if (side === 'float') { + panel.style.removeProperty('--_dock-size'); + return; + } + const { min, max } = dockLimits(side); + const size = clampDockSize(side, state.dockSize?.[side]); + panel.style.setProperty('--_dock-size', `${size}px`); + splitter.setAttribute('aria-orientation', side === 'bottom' ? 'horizontal' : 'vertical'); + splitter.setAttribute('aria-valuemin', String(min)); + splitter.setAttribute('aria-valuemax', String(max)); + splitter.setAttribute('aria-valuenow', String(size)); + splitter.setAttribute( + 'aria-valuetext', + `${size} pixels ${side === 'bottom' ? 'high' : 'wide'}`, + ); + } + + /** Sets the size of the docked side, inside the window; `undefined` goes back to the default. */ + function setDockSize(size: number | undefined) { + const side = state.docked; + if (side === 'float') return; + const sizes = { ...state.dockSize }; + if (size === undefined) delete sizes[side]; + else sizes[side] = clampDockSize(side, size); + state.dockSize = Object.keys(sizes).length ? sizes : undefined; + applySplitter(); + } + + // Dragging the splitter. Pointer capture keeps the drag going when the + // pointer crosses the frame or leaves the window. + let resize: { id: number } | null = null; + splitter.addEventListener('pointerdown', (e) => { + if (state.docked === 'float' || resize) return; + if (e.pointerType === 'mouse' && e.button !== 0) return; + resize = { id: e.pointerId }; + panel.classList.add('resizing'); + try { + splitter.setPointerCapture(e.pointerId); + } catch { + // the pointer is already gone; the move events still reach the splitter + } + e.preventDefault(); + }); + splitter.addEventListener('pointermove', (e) => { + if (!resize || e.pointerId !== resize.id) return; + setDockSize( + state.docked === 'bottom' ? window.innerHeight - e.clientY : window.innerWidth - e.clientX, + ); + }); + const endResize = (e: PointerEvent) => { + if (!resize || e.pointerId !== resize.id) return; + resize = null; + panel.classList.remove('resizing'); + saveState(state); + }; + splitter.addEventListener('pointerup', endResize); + splitter.addEventListener('pointercancel', endResize); + splitter.addEventListener('lostpointercapture', endResize); + + // The arrow keys along the splitter's axis move it, Shift moves it further, + // and Home and End go to the smallest and largest size. + splitter.addEventListener('keydown', (e) => { + const side = state.docked; + if (side === 'float') return; + const step = e.shiftKey ? DOCK_STEP_LARGE : DOCK_STEP; + const size = clampDockSize(side, state.dockSize?.[side]); + const { min, max } = dockLimits(side); + const grow = side === 'bottom' ? 'ArrowUp' : 'ArrowLeft'; + const shrink = side === 'bottom' ? 'ArrowDown' : 'ArrowRight'; + const next: Record = { + [grow]: size + step, + [shrink]: size - step, + Home: min, + End: max, + }; + if (!Object.hasOwn(next, e.key)) return; + e.preventDefault(); + setDockSize(next[e.key]); + saveState(state); + }); + + splitter.addEventListener('dblclick', () => { + setDockSize(undefined); + saveState(state); + }); + + /** The window the panel was popped out into, while it is open. */ + let outside: Window | null = null; + /** The origin of the panel in that window, which its messages must come from. */ + let outsideOrigin = ''; + let outsideWatch: ReturnType | undefined; + + /** Labels the launcher for what a click does: open the panel, or bring it back. */ + function applyOutside() { + const label = outside + ? 'Bring Pangular Inspector back into the page' + : 'Toggle Pangular Inspector'; + fab.setAttribute('aria-label', label); + fab.title = outside ? label : 'Pangular Inspector'; + fab.classList.toggle('outside', !!outside); + } + + /** Forgets the pop-out window, closing it unless it is already closed. */ + function forgetOutside(close: boolean) { + clearInterval(outsideWatch); + outsideWatch = undefined; + const win = outside; + outside = null; + if (close && win && !win.closed) { + try { + win.close(); + } catch { + // a window the page can no longer reach + } + } + applyOutside(); + } + + /** + * Opens the panel in its own browser window, still on this page's id, and + * closes it here. The launcher, the shortcut or the panel's **Back into the + * page** command bring it back. + */ + async function popOut() { + notice.textContent = ''; + // The frame's address is known once the panel loaded, which keeps the + // call inside the click, as a pop-up blocker wants. + const src = iframe.src && !iframe.hidden ? iframe.src : await resolveTarget(); + if (src === null) { + notice.textContent = 'No devtools server found, so there is nothing to open.'; + return; + } + const width = Math.min(1200, Math.max(480, Math.round(window.outerWidth * 0.6) || 960)); + const height = Math.min(900, Math.max(360, Math.round(window.outerHeight * 0.8) || 720)); + const url = popoutUrl(src); + let win: Window | null = null; + try { + win = window.open(url, popoutName(), `popup,width=${width},height=${height}`); + } catch { + win = null; + } + if (!win) { + notice.textContent = + 'The browser blocked the new window. Allow pop-ups for this site and try again.'; + return; + } + forgetOutside(false); + outside = win; + outsideOrigin = new URL(url, location.href).origin; + applyOutside(); + // Closing the window by hand brings back the launcher's usual job. + outsideWatch = setInterval(() => { + if (outside?.closed) forgetOutside(false); + }, 1000); + if (isOpen) togglePanel(); + try { + win.focus(); + } catch { + // the browser decides which window has focus + } } function togglePanel() { + // Opening the panel here while it is popped out brings it back from the window. + if (!isOpen && outside) forgetOutside(true); isOpen = !isOpen; fab.classList.toggle('open', isOpen); fab.setAttribute('aria-expanded', String(isOpen)); @@ -990,7 +1278,11 @@ export function createDevtoolsPopup(options: { src?: string; hotkey?: string | f passive?: unknown; } | null; if (msg?.type === 'pangular:theme-change') applyPopupTheme(msg.theme); - else if (msg?.type === 'pangular:popup-dock') { + else if (msg?.type === 'pangular:popup-popout') void popOut(); + else if (msg?.type === 'pangular:popup-dock-size' && state.docked !== 'float') { + setDockSize(undefined); + saveState(state); + } else if (msg?.type === 'pangular:popup-dock') { if (msg.mode === 'float' || msg.mode === 'bottom' || msg.mode === 'right') setDock(msg.mode); } else if (msg?.type === 'pangular:popup-close') { // The panel heard the shortcut inside a frame the popup can't listen to. @@ -1003,6 +1295,36 @@ export function createDevtoolsPopup(options: { src?: string; hotkey?: string | f }; window.addEventListener('message', onThemeMessage); + /** The pop-out window a message came from, itself or through a frame inside it. */ + const fromOutside = (e: MessageEvent): Window | null => { + if (!e.source) return null; + let w = e.source as Window; + try { + for (let depth = 0; depth < 10; depth++) { + if (outside && w === outside) return e.origin === outsideOrigin ? w : null; + // After a reload of this page the window is no longer known by + // reference, but it still has the name this page gave it. Its name + // can only be read on this page's origin. + if (!outside && w.name === popoutName()) return e.origin === location.origin ? w : null; + if (w === w.parent) return null; + w = w.parent; + } + } catch { + // a window on another origin, whose name can't be read + } + return null; + }; + const onOutsideMessage = (e: MessageEvent) => { + const type = (e.data as { type?: unknown } | null)?.type; + if (type !== 'pangular:popup-return') return; + const win = fromOutside(e); + if (!win) return; + outside = win; + forgetOutside(true); + if (!isOpen) togglePanel(); + }; + window.addEventListener('message', onOutsideMessage); + applyLauncher(); applyPassive(); // Keep it reachable when the window changes size. @@ -1028,6 +1350,8 @@ export function createDevtoolsPopup(options: { src?: string; hotkey?: string | f toggle: togglePanel, destroy: () => { window.removeEventListener('message', onThemeMessage); + window.removeEventListener('message', onOutsideMessage); + clearInterval(outsideWatch); window.removeEventListener('keydown', onHotkey, true); rehotkey = undefined; window.removeEventListener('resize', applyLauncher); diff --git a/packages/devtools/src/terminal-commands.ts b/packages/devtools/src/terminal-commands.ts new file mode 100644 index 00000000..052454ba --- /dev/null +++ b/packages/devtools/src/terminal-commands.ts @@ -0,0 +1,159 @@ +import { existsSync, readFileSync } from 'node:fs'; +import { dirname, join } from 'node:path'; + +/** The commands the terminals dock can run. Nothing else is ever spawned. */ +export const TERMINAL_COMMAND_IDS = ['build', 'test'] as const; + +export type TerminalCommandId = (typeof TERMINAL_COMMAND_IDS)[number]; + +export type PackageManager = 'pnpm' | 'yarn' | 'npm' | 'bun'; + +/** One fixed command, found in the project when the hub starts. */ +export interface TerminalCommand { + id: TerminalCommandId; + /** What the dock and the palette show, such as `pnpm run build` or `ng test`. */ + title: string; + command: PackageManager; + args: string[]; + /** A `package.json` script, or an Angular CLI target in `angular.json`. */ + source: 'script' | 'angular-cli'; +} + +/** Reads a text file, or `undefined` when it is missing. Tests pass a fake. */ +export type ReadText = (path: string) => string | undefined; + +const readText: ReadText = (path) => { + try { + return existsSync(path) ? readFileSync(path, 'utf8') : undefined; + } catch { + return undefined; + } +}; + +function json(text: string | undefined): Record | undefined { + if (!text) return undefined; + try { + const value: unknown = JSON.parse(text); + return value && typeof value === 'object' && !Array.isArray(value) + ? (value as Record) + : undefined; + } catch { + return undefined; + } +} + +function record(value: unknown): Record { + return value && typeof value === 'object' && !Array.isArray(value) + ? (value as Record) + : {}; +} + +const LOCKFILES: [file: string, manager: PackageManager][] = [ + ['pnpm-lock.yaml', 'pnpm'], + ['yarn.lock', 'yarn'], + ['bun.lock', 'bun'], + ['bun.lockb', 'bun'], + ['package-lock.json', 'npm'], +]; + +/** How many folders above the project the lockfile search goes, for workspaces. */ +const MAX_PARENTS = 5; + +/** + * The project's package manager: `packageManager` in `package.json`, then the + * nearest lockfile in the project or up to five folders above it, then npm. + */ +export function packageManagerFor(cwd: string, read: ReadText = readText): PackageManager { + let dir = cwd; + for (let level = 0; level <= MAX_PARENTS; level++) { + const declared = json(read(join(dir, 'package.json')))?.['packageManager']; + if (typeof declared === 'string') { + const name = declared.split('@')[0]; + if (name === 'pnpm' || name === 'yarn' || name === 'npm' || name === 'bun') return name; + } + for (const [file, manager] of LOCKFILES) { + if (read(join(dir, file)) !== undefined) return manager; + } + const parent = dirname(dir); + if (parent === dir) break; + dir = parent; + } + return 'npm'; +} + +/** `npm init` writes this test script; it only fails, so it doesn't count. */ +const PLACEHOLDER_TEST = /no test specified/i; + +function scriptFor(pkg: Record, id: TerminalCommandId): string | undefined { + const script = record(pkg['scripts'])[id]; + if (typeof script !== 'string' || !script.trim()) return undefined; + if (id === 'test' && PLACEHOLDER_TEST.test(script)) return undefined; + return script; +} + +function hasAngularCli(pkg: Record): boolean { + return ['dependencies', 'devDependencies'].some( + (field) => typeof record(pkg[field])['@angular/cli'] === 'string', + ); +} + +/** Whether a project in `angular.json` has the target, under `architect` or `targets`. */ +function hasAngularTarget(angular: Record, id: TerminalCommandId): boolean { + return Object.values(record(angular['projects'])).some((project) => { + const p = record(project); + return id in record(p['architect']) || id in record(p['targets']); + }); +} + +/** `ng ` through the package manager, so the project's own CLI runs. */ +function ngArgs(manager: PackageManager, id: TerminalCommandId): string[] { + switch (manager) { + case 'pnpm': + return ['exec', 'ng', id]; + case 'npm': + return ['exec', '--', 'ng', id]; + case 'yarn': + return ['ng', id]; + case 'bun': + return ['run', 'ng', id]; + } +} + +/** + * The build and test commands of the project in `cwd`, found once when the hub + * starts: the `build` and `test` scripts of `package.json`, run with the + * project's package manager, or else `ng build` and `ng test` when + * `angular.json` has the target and `@angular/cli` is installed. The list + * holds at most one command per id, and the dock runs only these. + */ +export function detectTerminalCommands(cwd: string, read: ReadText = readText): TerminalCommand[] { + const pkg = json(read(join(cwd, 'package.json'))); + if (!pkg) return []; + const manager = packageManagerFor(cwd, read); + const angular = hasAngularCli(pkg) ? json(read(join(cwd, 'angular.json'))) : undefined; + const commands: TerminalCommand[] = []; + for (const id of TERMINAL_COMMAND_IDS) { + if (scriptFor(pkg, id)) { + commands.push({ + id, + title: `${manager} run ${id}`, + command: manager, + args: ['run', id], + source: 'script', + }); + } else if (angular && hasAngularTarget(angular, id)) { + commands.push({ + id, + title: `ng ${id}`, + command: manager, + args: ngArgs(manager, id), + source: 'angular-cli', + }); + } + } + return commands; +} + +export function isTerminalCommandId(value: unknown): value is TerminalCommandId { + return typeof value === 'string' && (TERMINAL_COMMAND_IDS as readonly string[]).includes(value); +} diff --git a/packages/devtools/src/terminal-runner.ts b/packages/devtools/src/terminal-runner.ts new file mode 100644 index 00000000..03ef6cd9 --- /dev/null +++ b/packages/devtools/src/terminal-runner.ts @@ -0,0 +1,394 @@ +import { spawn as nodeSpawn, spawnSync, type ChildProcess } from 'node:child_process'; +import { redactText } from './text-redact.ts'; +import type { TerminalCommand, TerminalCommandId } from './terminal-commands.ts'; + +/** + * The part of the hub's `ctx.terminals` the runner uses. Sessions registered + * here show in the hub's terminals dock, and the hub's own + * `hub:terminals:terminate`, `restart` and `remove` RPCs drive them through + * the session's `terminate()` and `restart()`. + */ +export interface TerminalsHostLike { + readonly sessions: Map; + register: (session: TerminalSessionEntry) => unknown; + update: (patch: Partial & { id: string }) => void; + remove: (session: { id: string }) => void; +} + +export type TerminalSessionStatus = 'running' | 'stopped' | 'error'; + +/** The session object handed to `ctx.terminals.register()`. */ +export interface TerminalSessionEntry { + id: string; + title: string; + description?: string; + status: TerminalSessionStatus; + icon?: string; + interactive: false; + restartable: true; + stream: ReadableStream; + terminate: () => Promise; + restart: () => Promise; +} + +/** Starts a command. Tests pass a fake that returns an `EventEmitter` child. */ +export type SpawnCommand = (command: TerminalCommand, cwd: string) => ChildProcess; + +/** Stops a child and everything it started. Tests pass a fake. */ +export type KillTree = (child: ChildProcess, signal: NodeJS.Signals) => void; + +const WINDOWS = process.platform === 'win32'; + +/** + * Runs a fixed command with no shell on POSIX, in its own process group so a + * stop reaches the CLI under the package manager too. Windows runs the + * `pnpm.cmd` style shims through `cmd.exe`; the arguments are fixed, never + * user input. + */ +export const spawnCommand: SpawnCommand = (command, cwd) => + nodeSpawn(command.command, command.args, { + cwd, + env: { ...process.env, FORCE_COLOR: '1' }, + stdio: ['ignore', 'pipe', 'pipe'], + detached: !WINDOWS, + shell: WINDOWS, + windowsHide: true, + }); + +/** Kills the child's process group (POSIX) or tree (`taskkill /T` on Windows). */ +export const killTree: KillTree = (child, signal) => { + const pid = child.pid; + if (pid === undefined) return; + try { + if (WINDOWS) { + spawnSync('taskkill', ['/pid', String(pid), '/T', '/F'], { windowsHide: true }); + } else { + process.kill(-pid, signal); + } + } catch { + try { + child.kill(signal); + } catch { + // Already gone. + } + } +}; + +/** How long a stopped command gets to exit after SIGTERM before SIGKILL. */ +export const KILL_GRACE_MS = 3000; +/** How long a partial line waits for its end before it is shown, for progress output. */ +const FLUSH_MS = 100; +/** A partial line longer than this is shown without waiting for its end. */ +const MAX_PENDING = 8192; + +interface Run { + child: ChildProcess; + exited: boolean; + /** Set when the runner stopped it, so the exit reads as stopped, not failed. */ + stopping: boolean; + killTimer?: ReturnType; +} + +interface Session { + command: TerminalCommand; + entry: TerminalSessionEntry; + controller?: ReadableStreamDefaultController; + closed: boolean; + run?: Run; + pending: string; + flushTimer?: ReturnType; +} + +export interface TerminalRunnerOptions { + cwd: string; + spawn?: SpawnCommand; + kill?: KillTree; + /** Masks secrets in the output, line by line. Defaults to `redactText`. */ + redact?: (text: string) => string; + /** Hooks process signals so the commands die with the server. Off in tests. */ + hookProcessExit?: boolean; +} + +/** The hub session id of a command, such as `pangular:terminal:build`. */ +export function terminalSessionId(id: TerminalCommandId): string { + return `pangular:terminal:${id}`; +} + +const EXIT_SIGNALS: NodeJS.Signals[] = ['SIGINT', 'SIGTERM', 'SIGHUP']; + +/** + * Runs the project's detected build and test commands as hub terminal + * sessions. Only the commands it was created with can run: `start()` takes a + * command id, never a command line. Output is masked like the rest of the + * devtools before it reaches the dock. Each session keeps one output stream + * for its whole life, so a run that ended can be started again in place, and + * `dispose()` stops every command when the hub closes. + */ +export class TerminalRunner { + readonly #host: TerminalsHostLike; + readonly #commands: Map; + readonly #sessions = new Map(); + readonly #options: Required> & { + hookProcessExit: boolean; + }; + #hooked = false; + #disposed = false; + + constructor( + host: TerminalsHostLike, + commands: readonly TerminalCommand[], + options: TerminalRunnerOptions, + ) { + this.#host = host; + this.#commands = new Map(commands.map((command) => [command.id, command])); + this.#options = { + cwd: options.cwd, + spawn: options.spawn ?? spawnCommand, + kill: options.kill ?? killTree, + redact: options.redact ?? ((text) => redactText(text)), + hookProcessExit: options.hookProcessExit ?? true, + }; + } + + /** The commands this runner may run. */ + get commands(): TerminalCommand[] { + return [...this.#commands.values()]; + } + + /** Whether the command is running now. */ + isRunning(id: TerminalCommandId): boolean { + const run = this.#sessions.get(id)?.run; + return !!run && !run.exited; + } + + /** + * Runs the command in its session, creating the session the first time. A + * run in progress is stopped first, so this is also the restart. Returns the + * hub session id. + */ + start(id: TerminalCommandId): string { + if (this.#disposed) throw new Error('The terminals are closed.'); + const command = this.#commands.get(id); + if (!command) throw new Error(`No ${String(id)} command was found in this project.`); + let session = this.#sessions.get(id); + if (!session || session.closed) session = this.#open(command); + this.#launch(session); + return session.entry.id; + } + + /** Stops the command and keeps its session and output. */ + stop(id: TerminalCommandId): void { + const session = this.#sessions.get(id); + if (session) this.#stopRun(session); + } + + /** Stops every command and drops their sessions from the hub. */ + dispose(): void { + if (this.#disposed) return; + this.#disposed = true; + for (const session of this.#sessions.values()) { + this.#stopRun(session); + this.#close(session); + if (this.#host.sessions.has(session.entry.id)) this.#host.remove(session.entry); + } + this.#sessions.clear(); + this.#unhook(); + } + + #open(command: TerminalCommand): Session { + const session: Session = { + command, + closed: false, + pending: '', + entry: undefined as unknown as TerminalSessionEntry, + }; + const stream = new ReadableStream({ + start: (controller) => { + session.controller = controller; + }, + // The hub cancels the stream when the session is removed from the dock. + cancel: () => { + session.closed = true; + this.#stopRun(session); + if (this.#sessions.get(command.id) === session) this.#sessions.delete(command.id); + }, + }); + session.entry = { + id: terminalSessionId(command.id), + title: command.title, + description: `Pangular Inspector · ${command.source === 'script' ? 'package.json script' : 'Angular CLI'}`, + status: 'stopped', + icon: command.id === 'test' ? 'ph:flask-duotone' : 'ph:hammer-duotone', + interactive: false, + restartable: true, + stream, + terminate: async () => this.stop(command.id), + restart: async () => { + this.start(command.id); + }, + }; + this.#sessions.set(command.id, session); + const stale = this.#host.sessions.get(session.entry.id); + if (stale) this.#host.remove(stale as { id: string }); + this.#host.register(session.entry); + return session; + } + + #launch(session: Session): void { + if (session.run && !session.run.exited) { + this.#write(session, '\r\n\x1b[2m[restarting]\x1b[0m\r\n'); + this.#stopRun(session); + } else if (session.run) { + this.#write(session, '\r\n'); + } + const { command } = session; + this.#write(session, `\x1b[2m$ ${command.command} ${command.args.join(' ')}\x1b[0m\r\n`); + let child: ChildProcess; + try { + child = this.#options.spawn(command, this.#options.cwd); + } catch (error) { + this.#write(session, `\x1b[31m[could not start: ${errorText(error)}]\x1b[0m\r\n`); + this.#setStatus(session, 'error'); + return; + } + const run: Run = { child, exited: false, stopping: false }; + session.run = run; + this.#setStatus(session, 'running'); + this.#hook(); + const onData = (chunk: Buffer | string) => { + if (session.run === run) this.#output(session, chunk.toString()); + }; + child.stdout?.on('data', onData); + child.stderr?.on('data', onData); + child.once('error', (error) => { + if (run.exited) return; + run.exited = true; + this.#settle(run); + if (session.run !== run) return; + this.#flush(session); + this.#write(session, `\x1b[31m[could not start: ${errorText(error)}]\x1b[0m\r\n`); + this.#setStatus(session, 'error'); + }); + child.once('close', (code: number | null, signal: NodeJS.Signals | null) => { + if (run.exited) return; + run.exited = true; + this.#settle(run); + if (session.run !== run) return; + this.#flush(session); + const stopped = run.stopping || (code === null && signal !== null); + this.#write( + session, + stopped + ? '\r\n\x1b[2m[stopped]\x1b[0m\r\n' + : `\r\n\x1b[2m[exited with code ${code ?? 0}]\x1b[0m\r\n`, + ); + this.#setStatus(session, !stopped && code !== 0 ? 'error' : 'stopped'); + }); + } + + #stopRun(session: Session): void { + const run = session.run; + if (!run || run.exited || run.stopping) return; + run.stopping = true; + this.#options.kill(run.child, 'SIGTERM'); + run.killTimer = setTimeout(() => { + if (!run.exited) this.#options.kill(run.child, 'SIGKILL'); + }, KILL_GRACE_MS); + run.killTimer.unref?.(); + } + + #settle(run: Run): void { + if (run.killTimer) clearTimeout(run.killTimer); + if (![...this.#sessions.values()].some((s) => s.run && !s.run.exited)) this.#unhook(); + } + + /** Masks complete lines; a partial line waits briefly for the rest. */ + #output(session: Session, text: string): void { + const all = session.pending + text; + const end = Math.max(all.lastIndexOf('\n'), all.lastIndexOf('\r')); + if (end >= 0) { + this.#write(session, this.#options.redact(all.slice(0, end + 1))); + session.pending = all.slice(end + 1); + } else { + session.pending = all; + } + if (session.pending.length > MAX_PENDING) { + this.#flush(session); + } else if (session.pending && !session.flushTimer) { + session.flushTimer = setTimeout(() => this.#flush(session), FLUSH_MS); + session.flushTimer.unref?.(); + } + } + + #flush(session: Session): void { + if (session.flushTimer) clearTimeout(session.flushTimer); + session.flushTimer = undefined; + if (!session.pending) return; + const text = session.pending; + session.pending = ''; + this.#write(session, this.#options.redact(text)); + } + + #write(session: Session, text: string): void { + if (session.closed || !text) return; + try { + session.controller?.enqueue(text); + } catch { + session.closed = true; + } + } + + #close(session: Session): void { + this.#flush(session); + if (session.closed) return; + session.closed = true; + try { + session.controller?.close(); + } catch { + // Already closed or cancelled. + } + } + + #setStatus(session: Session, status: TerminalSessionStatus): void { + session.entry.status = status; + if (this.#host.sessions.get(session.entry.id) === session.entry) { + this.#host.update({ id: session.entry.id, status }); + } + } + + /** Stops the running commands when the server process exits or is interrupted. */ + readonly #onExit = () => { + for (const session of this.#sessions.values()) { + const run = session.run; + if (run && !run.exited) this.#options.kill(run.child, 'SIGTERM'); + } + }; + + /** + * On a signal, stops the commands, then lets the signal end the server as + * it would have: re-raised when nothing else listens for it. + */ + readonly #onSignal = (signal: NodeJS.Signals) => { + this.#onExit(); + this.#unhook(); + if (process.listenerCount(signal) === 0) process.kill(process.pid, signal); + }; + + #hook(): void { + if (this.#hooked || !this.#options.hookProcessExit) return; + this.#hooked = true; + process.on('exit', this.#onExit); + for (const signal of EXIT_SIGNALS) process.on(signal, this.#onSignal); + } + + #unhook(): void { + if (!this.#hooked) return; + this.#hooked = false; + process.off('exit', this.#onExit); + for (const signal of EXIT_SIGNALS) process.off(signal, this.#onSignal); + } +} + +function errorText(error: unknown): string { + return error instanceof Error ? error.message : String(error); +} diff --git a/packages/devtools/src/vite.ts b/packages/devtools/src/vite.ts index 8d49450c..36da776c 100644 --- a/packages/devtools/src/vite.ts +++ b/packages/devtools/src/vite.ts @@ -22,6 +22,7 @@ import { } from './hub-gate.ts'; import { pickPangularConfig, resolvePangularConfig, type PangularConfig } from './config.ts'; import { overlayTags } from './vite-overlay.ts'; +import { terminalsEnabled } from './hub-terminals.ts'; export type { PangularConfig } from './config.ts'; @@ -45,6 +46,12 @@ export interface PangularViteOptions extends PangularConfig { * `false` to load it yourself, for example with `initOverlay` options. */ overlay?: boolean; + /** + * Add a terminals dock that runs the project's own build and test commands + * (its `build` and `test` scripts, or `ng build` and `ng test`). Off by + * default. Ignored with `allowLan`. + */ + terminals?: boolean; } export { @@ -146,6 +153,9 @@ export default function pangularVite(options: PangularViteOptions = {}): Plugin ...(httpServer ? {} : { ws: { sidecar: true } }), auth: isAuthTokens(options.auth) ? options.auth : hubAuthFor(options.auth), allowedOrigins: hubOriginRegistryFor(policy), + terminals: terminalsEnabled(options.terminals, { remote: policy.lan }, (message) => + logger?.warn(message), + ), }); httpServer?.on('upgrade', hubUpgradeListener(base, policy, devtools.handleUpgrade, blocked)); server.middlewares.use(devtools.nodeMiddleware); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5dc994a8..d3c6fd78 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -369,6 +369,12 @@ importers: '@devframes/plugin-messages--assets': specifier: ^1.2.3 version: 1.2.3 + '@devframes/plugin-terminals': + specifier: ^1.2.3 + version: 1.2.3(@devframes/hub-ui@1.2.3(@devframes/hub@1.2.3(patch_hash=47cc90f584a849c60796c501e52048208c6eeca320859012f9f9acf454c7929b)(@devframes/agentic@1.2.3)(crossws@0.4.12(srvx@1.0.5))(devframe@1.2.3))(devframe@1.2.3))(@devframes/hub@1.2.3(patch_hash=47cc90f584a849c60796c501e52048208c6eeca320859012f9f9acf454c7929b)(@devframes/agentic@1.2.3)(crossws@0.4.12(srvx@1.0.5))(devframe@1.2.3))(@devframes/plugin-terminals--assets@1.2.3)(devframe@1.2.3)(vite@8.3.0(@types/node@24.13.6)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.105.0)(terser@5.51.2)(yaml@2.9.0)) + '@devframes/plugin-terminals--assets': + specifier: ^1.2.3 + version: 1.2.3 '@valibot/to-json-schema': specifier: ^1.8.0 version: 1.8.0(valibot@1.5.0(typescript@6.0.3)) @@ -1505,6 +1511,22 @@ packages: vite: optional: true + '@devframes/plugin-terminals--assets@1.2.3': + resolution: {integrity: sha512-mOPl+0f48NFuXkjHXddsKZvMXBnlwqHXc8A42NQrE0fyP3PyX5Nwhcl1UnTeMhy1qXfnK05CqLWVwYOsSUspZg==} + + '@devframes/plugin-terminals@1.2.3': + resolution: {integrity: sha512-y9Yla9XMkLwleYVkBlAam7ekPfgNT/vKAQ9UAIBCYGXSprcR19ZrbAco9liwoopTNsjKmd2bathFs4R5TndHeg==} + hasBin: true + peerDependencies: + '@devframes/plugin-terminals--assets': 1.2.3 + devframe: 1.2.3 + vite: ^8.0.0 + peerDependenciesMeta: + '@devframes/plugin-terminals--assets': + optional: true + vite: + optional: true + '@devframes/service-open@1.2.3': resolution: {integrity: sha512-x71jVlxkERpLnScjyhE/EuL7QvDsaxjoWRffOqp6ntwhCzQvOyWsJOPTHKE3PYrAH1kAbhZj7pBDse7jcMbyzg==} peerDependencies: @@ -9034,6 +9056,21 @@ snapshots: '@devframes/plugin-messages--assets': 1.2.3 vite: 8.3.0(@types/node@24.13.6)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.105.0)(terser@5.51.2)(yaml@2.9.0) + '@devframes/plugin-terminals--assets@1.2.3': {} + + '@devframes/plugin-terminals@1.2.3(@devframes/hub-ui@1.2.3(@devframes/hub@1.2.3(patch_hash=47cc90f584a849c60796c501e52048208c6eeca320859012f9f9acf454c7929b)(@devframes/agentic@1.2.3)(crossws@0.4.12(srvx@1.0.5))(devframe@1.2.3))(devframe@1.2.3))(@devframes/hub@1.2.3(patch_hash=47cc90f584a849c60796c501e52048208c6eeca320859012f9f9acf454c7929b)(@devframes/agentic@1.2.3)(crossws@0.4.12(srvx@1.0.5))(devframe@1.2.3))(@devframes/plugin-terminals--assets@1.2.3)(devframe@1.2.3)(vite@8.3.0(@types/node@24.13.6)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.105.0)(terser@5.51.2)(yaml@2.9.0))': + dependencies: + '@devframes/vite': 1.2.3(@devframes/hub-ui@1.2.3(@devframes/hub@1.2.3(patch_hash=47cc90f584a849c60796c501e52048208c6eeca320859012f9f9acf454c7929b)(@devframes/agentic@1.2.3)(crossws@0.4.12(srvx@1.0.5))(devframe@1.2.3))(devframe@1.2.3))(@devframes/hub@1.2.3(patch_hash=47cc90f584a849c60796c501e52048208c6eeca320859012f9f9acf454c7929b)(@devframes/agentic@1.2.3)(crossws@0.4.12(srvx@1.0.5))(devframe@1.2.3))(devframe@1.2.3)(vite@8.3.0(@types/node@24.13.6)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.105.0)(terser@5.51.2)(yaml@2.9.0)) + cac: 7.0.0 + devframe: 1.2.3(patch_hash=877e107b3b640e28241731b2173f869f4b34ad5ab62106d024b98fede65d82bd)(@devframes/agentic@1.2.3)(cac@7.0.0)(srvx@1.0.5) + zigpty: 0.2.1 + optionalDependencies: + '@devframes/plugin-terminals--assets': 1.2.3 + vite: 8.3.0(@types/node@24.13.6)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.105.0)(terser@5.51.2)(yaml@2.9.0) + transitivePeerDependencies: + - '@devframes/hub' + - '@devframes/hub-ui' + '@devframes/service-open@1.2.3(devframe@1.2.3)': dependencies: devframe: 1.2.3(patch_hash=877e107b3b640e28241731b2173f869f4b34ad5ab62106d024b98fede65d82bd)(@devframes/agentic@1.2.3)(cac@7.0.0)(srvx@1.0.5) From f5dfc6230405b2c0d6b94ba8195fdc4397e0cb7a Mon Sep 17 00:00:00 2001 From: erKam Date: Mon, 12 Oct 2026 22:07:29 +0000 Subject: [PATCH 2/3] build(extension): rebuild the panel bundle Rebuilds extension/ui for the timeline and the pop-out window. --- ...=> browser-agent-rpc-BXhoSh1z-DUQ-j6wr.js} | 2 +- .../{index-qtNa3SVJ.js => index-BPzeAf9j.js} | 571 ++++++++++++++++-- extension/ui/index.html | 2 +- 3 files changed, 521 insertions(+), 54 deletions(-) rename extension/ui/assets/{browser-agent-rpc-BXhoSh1z-DTULcebV.js => browser-agent-rpc-BXhoSh1z-DUQ-j6wr.js} (93%) rename extension/ui/assets/{index-qtNa3SVJ.js => index-BPzeAf9j.js} (81%) diff --git a/extension/ui/assets/browser-agent-rpc-BXhoSh1z-DTULcebV.js b/extension/ui/assets/browser-agent-rpc-BXhoSh1z-DUQ-j6wr.js similarity index 93% rename from extension/ui/assets/browser-agent-rpc-BXhoSh1z-DTULcebV.js rename to extension/ui/assets/browser-agent-rpc-BXhoSh1z-DUQ-j6wr.js index 9377ec72..aca5df3f 100644 --- a/extension/ui/assets/browser-agent-rpc-BXhoSh1z-DTULcebV.js +++ b/extension/ui/assets/browser-agent-rpc-BXhoSh1z-DUQ-j6wr.js @@ -1 +1 @@ -import{t as e}from"./index-qtNa3SVJ.js";var t=Symbol.for(`devframe:browser-agent-registry`),{tools:n,listeners:r}=globalThis[t]??={tools:new Map,listeners:new Set};function i(){return[...n.values()]}function a(e){return r.add(e),()=>r.delete(e)}var o=`devframe:client-id`,s;function c(t=globalThis.window){try{let n=t?.sessionStorage;if(n){let t=n.getItem(o);return t||(t=e(),n.setItem(o,t)),t}}catch{}return s??=e(),s}function l(e){e.client.register({name:`devframe:agent:invoke-client-tool`,type:`action`,jsonSerializable:!0,handler:async(e,t)=>{let n=i().find(t=>t.id===e);if(!n)throw Error(`[devframe/agent] browser tool "${e}" not found`);return await n.invoke(t)}});let t=!1,n=!1,r=0,o=()=>{t||n||(t=!0,queueMicrotask(async()=>{if(t=!1,n)return;let a=i().map(({invoke:e,...t})=>t);(a.length!==0||r!==0)&&(r=a.length,await e.callOptional(`devframe:agent:sync-client-tools`,c(),a).catch(()=>{}))}))},s=a(o),l=e.events.on(`connection:status`,e=>{e===`connected`&&o()});return o(),()=>{n=!0,s(),l()}}export{l as setupBrowserAgentRpcBridge}; \ No newline at end of file +import{t as e}from"./index-BPzeAf9j.js";var t=Symbol.for(`devframe:browser-agent-registry`),{tools:n,listeners:r}=globalThis[t]??={tools:new Map,listeners:new Set};function i(){return[...n.values()]}function a(e){return r.add(e),()=>r.delete(e)}var o=`devframe:client-id`,s;function c(t=globalThis.window){try{let n=t?.sessionStorage;if(n){let t=n.getItem(o);return t||(t=e(),n.setItem(o,t)),t}}catch{}return s??=e(),s}function l(e){e.client.register({name:`devframe:agent:invoke-client-tool`,type:`action`,jsonSerializable:!0,handler:async(e,t)=>{let n=i().find(t=>t.id===e);if(!n)throw Error(`[devframe/agent] browser tool "${e}" not found`);return await n.invoke(t)}});let t=!1,n=!1,r=0,o=()=>{t||n||(t=!0,queueMicrotask(async()=>{if(t=!1,n)return;let a=i().map(({invoke:e,...t})=>t);(a.length!==0||r!==0)&&(r=a.length,await e.callOptional(`devframe:agent:sync-client-tools`,c(),a).catch(()=>{}))}))},s=a(o),l=e.events.on(`connection:status`,e=>{e===`connected`&&o()});return o(),()=>{n=!0,s(),l()}}export{l as setupBrowserAgentRpcBridge}; \ No newline at end of file diff --git a/extension/ui/assets/index-qtNa3SVJ.js b/extension/ui/assets/index-BPzeAf9j.js similarity index 81% rename from extension/ui/assets/index-qtNa3SVJ.js rename to extension/ui/assets/index-BPzeAf9j.js index 3ff4646b..f8fbdce9 100644 --- a/extension/ui/assets/index-qtNa3SVJ.js +++ b/extension/ui/assets/index-BPzeAf9j.js @@ -5,10 +5,10 @@ `):``,this.name=`UnsubscriptionError`,this.errors=t}});function hr(e,t){if(e){var n=e.indexOf(t);0<=n&&e.splice(n,1)}}var gr=function(){function e(e){this.initialTeardown=e,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var e,t,n,r,i;if(!this.closed){this.closed=!0;var a=this._parentage;if(a){if(this._parentage=null,Array.isArray(a))try{for(var o=lr(a),s=o.next();!s.done;s=o.next())s.value.remove(this)}catch(t){e={error:t}}finally{try{s&&!s.done&&(t=o.return)&&t.call(o)}finally{if(e)throw e.error}}else a.remove(this)}var c=this.initialTeardown;if(fr(c))try{c()}catch(e){i=e instanceof mr?e.errors:[e]}var l=this._finalizers;if(l){this._finalizers=null;try{for(var u=lr(l),d=u.next();!d.done;d=u.next()){var f=d.value;try{yr(f)}catch(e){i??=[],e instanceof mr?i=dr(dr([],ur(i)),ur(e.errors)):i.push(e)}}}catch(e){n={error:e}}finally{try{d&&!d.done&&(r=u.return)&&r.call(u)}finally{if(n)throw n.error}}}if(i)throw new mr(i)}},e.prototype.add=function(t){if(t&&t!==this){if(this.closed)yr(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=this._finalizers??[]).push(t)}}},e.prototype._hasParent=function(e){var t=this._parentage;return t===e||Array.isArray(t)&&t.includes(e)},e.prototype._addParent=function(e){var t=this._parentage;this._parentage=Array.isArray(t)?(t.push(e),t):t?[t,e]:e},e.prototype._removeParent=function(e){var t=this._parentage;t===e?this._parentage=null:Array.isArray(t)&&hr(t,e)},e.prototype.remove=function(t){var n=this._finalizers;n&&hr(n,t),t instanceof e&&t._removeParent(this)},e.EMPTY=(function(){var t=new e;return t.closed=!0,t})(),e}(),_r=gr.EMPTY;function vr(e){return e instanceof gr||e&&`closed`in e&&fr(e.remove)&&fr(e.add)&&fr(e.unsubscribe)}function yr(e){fr(e)?e():e.unsubscribe()}var br={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1},xr={setTimeout:function(e,t){var n=[...arguments].slice(2),r=xr.delegate;return r?.setTimeout?r.setTimeout.apply(r,dr([e,t],ur(n))):setTimeout.apply(void 0,dr([e,t],ur(n)))},clearTimeout:function(e){return(xr.delegate?.clearTimeout||clearTimeout)(e)},delegate:void 0};function Sr(e){xr.setTimeout(function(){var t=br.onUnhandledError;if(t)t(e);else throw e})}function Cr(){}var wr=(function(){return Dr(`C`,void 0,void 0)})();function Tr(e){return Dr(`E`,void 0,e)}function Er(e){return Dr(`N`,e,void 0)}function Dr(e,t,n){return{kind:e,value:t,error:n}}var Or=null;function kr(e){if(br.useDeprecatedSynchronousErrorHandling){var t=!Or;if(t&&(Or={errorThrown:!1,error:null}),e(),t){var n=Or,r=n.errorThrown,i=n.error;if(Or=null,r)throw i}}else e()}function Ar(e){br.useDeprecatedSynchronousErrorHandling&&Or&&(Or.errorThrown=!0,Or.error=e)}var jr=function(e){cr(t,e);function t(t){var n=e.call(this)||this;return n.isStopped=!1,t?(n.destination=t,vr(t)&&t.add(n)):n.destination=zr,n}return t.create=function(e,t,n){return new Fr(e,t,n)},t.prototype.next=function(e){this.isStopped?Rr(Er(e),this):this._next(e)},t.prototype.error=function(e){this.isStopped?Rr(Tr(e),this):(this.isStopped=!0,this._error(e))},t.prototype.complete=function(){this.isStopped?Rr(wr,this):(this.isStopped=!0,this._complete())},t.prototype.unsubscribe=function(){this.closed||(this.isStopped=!0,e.prototype.unsubscribe.call(this),this.destination=null)},t.prototype._next=function(e){this.destination.next(e)},t.prototype._error=function(e){try{this.destination.error(e)}finally{this.unsubscribe()}},t.prototype._complete=function(){try{this.destination.complete()}finally{this.unsubscribe()}},t}(gr),Mr=Function.prototype.bind;function Nr(e,t){return Mr.call(e,t)}var Pr=function(){function e(e){this.partialObserver=e}return e.prototype.next=function(e){var t=this.partialObserver;if(t.next)try{t.next(e)}catch(e){Ir(e)}},e.prototype.error=function(e){var t=this.partialObserver;if(t.error)try{t.error(e)}catch(e){Ir(e)}else Ir(e)},e.prototype.complete=function(){var e=this.partialObserver;if(e.complete)try{e.complete()}catch(e){Ir(e)}},e}(),Fr=function(e){cr(t,e);function t(t,n,r){var i=e.call(this)||this,a;if(fr(t)||!t)a={next:t??void 0,error:n??void 0,complete:r??void 0};else{var o;i&&br.useDeprecatedNextContext?(o=Object.create(t),o.unsubscribe=function(){return i.unsubscribe()},a={next:t.next&&Nr(t.next,o),error:t.error&&Nr(t.error,o),complete:t.complete&&Nr(t.complete,o)}):a=t}return i.destination=new Pr(a),i}return t}(jr);function Ir(e){br.useDeprecatedSynchronousErrorHandling?Ar(e):Sr(e)}function Lr(e){throw e}function Rr(e,t){var n=br.onStoppedNotification;n&&xr.setTimeout(function(){return n(e,t)})}var zr={closed:!0,next:Cr,error:Lr,complete:Cr},Br=(function(){return typeof Symbol==`function`&&Symbol.observable||`@@observable`})();function Vr(e){return e}function Hr(e){return e.length===0?Vr:e.length===1?e[0]:function(t){return e.reduce(function(e,t){return t(e)},t)}}var Ur=function(){function e(e){e&&(this._subscribe=e)}return e.prototype.lift=function(t){var n=new e;return n.source=this,n.operator=t,n},e.prototype.subscribe=function(e,t,n){var r=this,i=Kr(e)?e:new Fr(e,t,n);return kr(function(){var e=r,t=e.operator,n=e.source;i.add(t?t.call(i,n):n?r._subscribe(i):r._trySubscribe(i))}),i},e.prototype._trySubscribe=function(e){try{return this._subscribe(e)}catch(t){e.error(t)}},e.prototype.forEach=function(e,t){var n=this;return t=Wr(t),new t(function(t,r){var i=new Fr({next:function(t){try{e(t)}catch(e){r(e),i.unsubscribe()}},error:r,complete:t});n.subscribe(i)})},e.prototype._subscribe=function(e){return this.source?.subscribe(e)},e.prototype[Br]=function(){return this},e.prototype.pipe=function(){return Hr([...arguments])(this)},e.prototype.toPromise=function(e){var t=this;return e=Wr(e),new e(function(e,n){var r;t.subscribe(function(e){return r=e},function(e){return n(e)},function(){return e(r)})})},e.create=function(t){return new e(t)},e}();function Wr(e){return e??br.Promise??Promise}function Gr(e){return e&&fr(e.next)&&fr(e.error)&&fr(e.complete)}function Kr(e){return e&&e instanceof jr||Gr(e)&&vr(e)}function qr(e){return fr(e?.lift)}function Jr(e){return function(t){if(qr(t))return t.lift(function(t){try{return e(t,this)}catch(e){this.error(e)}});throw TypeError(`Unable to lift unknown Observable type`)}}function Yr(e,t,n,r,i){return new Xr(e,t,n,r,i)}var Xr=function(e){cr(t,e);function t(t,n,r,i,a,o){var s=e.call(this,t)||this;return s.onFinalize=a,s.shouldUnsubscribe=o,s._next=n?function(e){try{n(e)}catch(e){t.error(e)}}:e.prototype._next,s._error=i?function(e){try{i(e)}catch(e){t.error(e)}finally{this.unsubscribe()}}:e.prototype._error,s._complete=r?function(){try{r()}catch(e){t.error(e)}finally{this.unsubscribe()}}:e.prototype._complete,s}return t.prototype.unsubscribe=function(){var t;if(!this.shouldUnsubscribe||this.shouldUnsubscribe()){var n=this.closed;e.prototype.unsubscribe.call(this),!n&&((t=this.onFinalize)==null||t.call(this))}},t}(jr),Zr=pr(function(e){return function(){e(this),this.name=`ObjectUnsubscribedError`,this.message=`object unsubscribed`}}),Qr=function(e){cr(t,e);function t(){var t=e.call(this)||this;return t.closed=!1,t.currentObservers=null,t.observers=[],t.isStopped=!1,t.hasError=!1,t.thrownError=null,t}return t.prototype.lift=function(e){var t=new $r(this,this);return t.operator=e,t},t.prototype._throwIfClosed=function(){if(this.closed)throw new Zr},t.prototype.next=function(e){var t=this;kr(function(){var n,r;if(t._throwIfClosed(),!t.isStopped){t.currentObservers||=Array.from(t.observers);try{for(var i=lr(t.currentObservers),a=i.next();!a.done;a=i.next())a.value.next(e)}catch(e){n={error:e}}finally{try{a&&!a.done&&(r=i.return)&&r.call(i)}finally{if(n)throw n.error}}}})},t.prototype.error=function(e){var t=this;kr(function(){if(t._throwIfClosed(),!t.isStopped){t.hasError=t.isStopped=!0,t.thrownError=e;for(var n=t.observers;n.length;)n.shift().error(e)}})},t.prototype.complete=function(){var e=this;kr(function(){if(e._throwIfClosed(),!e.isStopped){e.isStopped=!0;for(var t=e.observers;t.length;)t.shift().complete()}})},t.prototype.unsubscribe=function(){this.isStopped=this.closed=!0,this.observers=this.currentObservers=null},Object.defineProperty(t.prototype,"observed",{get:function(){return this.observers?.length>0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(t){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,t)},t.prototype._subscribe=function(e){return this._throwIfClosed(),this._checkFinalizedStatuses(e),this._innerSubscribe(e)},t.prototype._innerSubscribe=function(e){var t=this,n=this,r=n.hasError,i=n.isStopped,a=n.observers;return r||i?_r:(this.currentObservers=null,a.push(e),new gr(function(){t.currentObservers=null,hr(a,e)}))},t.prototype._checkFinalizedStatuses=function(e){var t=this,n=t.hasError,r=t.thrownError,i=t.isStopped;n?e.error(r):i&&e.complete()},t.prototype.asObservable=function(){var e=new Ur;return e.source=this,e},t.create=function(e,t){return new $r(e,t)},t}(Ur),$r=function(e){cr(t,e);function t(t,n){var r=e.call(this)||this;return r.destination=t,r.source=n,r}return t.prototype.next=function(e){var t,n;(n=(t=this.destination)?.next)==null||n.call(t,e)},t.prototype.error=function(e){var t,n;(n=(t=this.destination)?.error)==null||n.call(t,e)},t.prototype.complete=function(){var e,t;(t=(e=this.destination)?.complete)==null||t.call(e)},t.prototype._subscribe=function(e){return this.source?.subscribe(e)??_r},t}(Qr),ei=function(e){cr(t,e);function t(t){var n=e.call(this)||this;return n._value=t,n}return Object.defineProperty(t.prototype,"value",{get:function(){return this.getValue()},enumerable:!1,configurable:!0}),t.prototype._subscribe=function(t){var n=e.prototype._subscribe.call(this,t);return!n.closed&&t.next(this._value),n},t.prototype.getValue=function(){var e=this,t=e.hasError,n=e.thrownError,r=e._value;if(t)throw n;return this._throwIfClosed(),r},t.prototype.next=function(t){e.prototype.next.call(this,this._value=t)},t}(Qr);function ti(e,t){return Jr(function(n,r){var i=0;n.subscribe(Yr(r,function(n){r.next(e.call(t,n,i++))}))})}var ni=`https://angular.dev/best-practices/security#preventing-cross-site-scripting-xss`,ri=class extends Error{code;constructor(e,t){super(ai(e,t)),this.code=e}};function ii(e){return`NG0${Math.abs(e)}`}function ai(e,t){return`${ii(e)}${t?`: `+t:``}`}function oi(e){for(let t in e)if(e[t]===oi)return t;throw Error(``)}function si(e){if(typeof e==`string`)return e;if(Array.isArray(e))return`[${e.map(si).join(`, `)}]`;if(e==null)return``+e;let t=e.overriddenName||e.name;if(t)return`${t}`;let n=e.toString();if(n==null)return``+n;let r=n.indexOf(` `);return r>=0?n.slice(0,r):n}function ci(e,t){return e?t?`${e} ${t}`:e:t||``}var li=oi({__forward_ref__:oi});function ui(e){return e.__forward_ref__=ui,e}function di(e){return fi(e)?e():e}function fi(e){return typeof e==`function`&&Object.hasOwn(e,li)&&e.__forward_ref__===ui}function pi(e){return{token:e.token,providedIn:e.providedIn||null,factory:e.factory,value:void 0}}function mi(e){return hi(e,vi)}function hi(e,t){return Object.hasOwn(e,t)&&e[t]||null}function gi(e){return(e?.[vi]??null)||null}function _i(e){return e&&Object.hasOwn(e,yi)?e[yi]:null}var vi=oi({ɵprov:oi}),yi=oi({ɵinj:oi}),bi=class{_desc;ngMetadataName=`InjectionToken`;ɵprov;constructor(e,t){this._desc=e,this.ɵprov=void 0,typeof t==`number`?this.__NG_ELEMENT_ID__=t:t!==void 0&&(this.ɵprov=pi({token:this,providedIn:t.providedIn||`root`,factory:t.factory}))}get multi(){return this}toString(){return`InjectionToken ${this._desc}`}};function xi(e){return e&&!!e.ɵproviders}var Si=oi({ɵcmp:oi}),Ci=oi({ɵdir:oi}),wi=oi({ɵpipe:oi}),Ti=oi({ɵfac:oi}),Ei=oi({__NG_ELEMENT_ID__:oi}),Di=oi({__NG_ENV_ID__:oi});function Oi(e){return ji(e,`@Component`),e[Si]||null}function ki(e){return ji(e,`@Directive`),e[Ci]||null}function Ai(e){return ji(e,`@Pipe`),e[wi]||null}function ji(e,t){if(e==null)throw new ri(-919,!1)}function Mi(e){return typeof e==`string`?e:e==null?``:String(e)}var Ni=oi({ngErrorCode:oi}),Pi=oi({ngErrorMessage:oi}),Fi=oi({ngTokenPath:oi});function Ii(e,t){return Ri(``,-200,t)}function Li(e,t){throw new ri(-201,!1)}function Ri(e,t,n){let r=new ri(t,e);return r[Ni]=t,r[Pi]=e,n&&(r[Fi]=n),r}function zi(e){return e[Ni]}var Bi;function Vi(){return Bi}function Hi(e){let t=Bi;return Bi=e,t}function Ui(e,t,n){let r=mi(e);if(r&&r.providedIn==`root`)return r.value===void 0?r.value=r.factory():r.value;if(n&8)return null;if(t!==void 0)return t;Li(e,``)}var Wi=globalThis,Gi={},Ki=`__NG_DI_FLAG__`,qi=class{injector;constructor(e){this.injector=e}retrieve(e,t){let n=Xi(t)||0;try{return this.injector.get(e,n&8?null:Gi,n)}catch(e){if(tr(e))return e;throw e}}};function Ji(e,t=0){let n=Qn();if(n===void 0)throw new ri(-203,!1);if(n===null)return Ui(e,void 0,t);{let r=Zi(t),i=n.retrieve(e,r);if(tr(i)){if(r.optional)return null;throw i}return i}}function Yi(e,t=0){return(Vi()||Ji)(di(e),t)}function w(e,t){return Yi(e,Xi(t))}function Xi(e){return e===void 0||typeof e==`number`?e:0|(e.optional&&8)|(e.host&&1)|(e.self&&2)|(e.skipSelf&&4)}function Zi(e){return{optional:!!(e&8),host:!!(e&1),self:!!(e&2),skipSelf:!!(e&4)}}function Qi(e){let t=[];for(let n=0;nArray.isArray(e)?ra(e,t):t(e))}function ia(e,t,n){t>=e.length?e.push(n):e.splice(t,0,n)}function aa(e,t){return t>=e.length-1?e.pop():e.splice(t,1)[0]}function oa(e,t){let n=[];for(let r=0;rt;){let t=i-2;e[i]=e[t],i--}e[t]=n,e[t+1]=r}}function ca(e,t,n){let r=ua(e,t);return r>=0?e[r|1]=n:(r=~r,sa(e,r,t,n)),r}function la(e,t){let n=ua(e,t);if(n>=0)return e[n|1]}function ua(e,t){return da(e,t,1)}function da(e,t,n){let r=0,i=e.length>>n;for(;i!==r;){let a=r+(i-r>>1),o=e[a<t?i=a:r=a+1}return~(i<{n.push(e)};return ra(t,e=>{let t=e;xa(t,a,[],r)&&(i||=[],i.push(t))}),i!==void 0&&ba(i,a),n}function ba(e,t){for(let n=0;n{t(e,r)})}}function xa(e,t,n,r){if(e=di(e),!e)return!1;let i=null,a=_i(e),o=!a&&Oi(e);if(!a&&!o){let t=e.ngModule;if(a=_i(t),a)i=t;else return!1}else if(o&&!o.standalone)return!1;else i=e;let s=r.has(i);if(o){if(s)return!1;if(r.add(i),o.dependencies){let e=typeof o.dependencies==`function`?o.dependencies():o.dependencies;for(let i of e)xa(i,t,n,r)}}else if(a){if(a.imports!=null&&!s){r.add(i);let e;try{ra(a.imports,i=>{xa(i,t,n,r)&&(e||=[],e.push(i))})}finally{}e!==void 0&&ba(e,t)}if(!s){let e=ea(i)||(()=>new i);t({provide:i,useFactory:e,deps:pa},i),t({provide:ga,useValue:i,multi:!0},i),t({provide:ma,useValue:()=>Yi(i),multi:!0},i)}let o=a.providers;if(o!=null&&!s){let n=e;Sa(o,e=>{t(e,n)})}}else return!1;return i!==e&&e.providers!==void 0}function Sa(e,t){for(let n of e)xi(n)&&(n=n.ɵproviders),Array.isArray(n)?Sa(n,t):t(n)}var Ca=oi({provide:String,useValue:oi});function wa(e){return typeof e==`object`&&!!e&&Ca in e}function Ta(e){return!!(e&&e.useExisting)}function Ea(e){return!!(e&&e.useFactory)}function Da(e){return typeof e==`function`}var Oa=new bi(``),ka={},Aa={},ja=void 0;function Ma(){return ja===void 0&&(ja=new _a),ja}var Na=class{},Pa=class extends Na{parent;source;scopes;records=new Map;_ngOnDestroyHooks=new Set;_onDestroyHooks=[];get destroyed(){return this._destroyed}_destroyed=!1;injectorDefTypes;constructor(e,t,n,r){super(),this.parent=t,this.source=n,this.scopes=r,Wa(e,e=>this.processProvider(e)),this.records.set(ha,Ba(void 0,this)),r.has(`environment`)&&this.records.set(Na,Ba(void 0,this));let i=this.records.get(Oa);i!=null&&typeof i.value==`string`&&this.scopes.add(i.value),this.injectorDefTypes=new Set(this.get(ga,pa,{self:!0}))}retrieve(e,t){let n=Xi(t)||0;try{return this.get(e,Gi,n)}catch(e){if(tr(e))return e;throw e}}destroy(){za(this),this._destroyed=!0;let e=C(null);try{for(let e of this._ngOnDestroyHooks)e.ngOnDestroy();let e=this._onDestroyHooks;this._onDestroyHooks=[];for(let t of e)t()}finally{this.records.clear(),this._ngOnDestroyHooks.clear(),this.injectorDefTypes.clear(),C(e)}}onDestroy(e){return za(this),this._onDestroyHooks.push(e),()=>this.removeOnDestroy(e)}runInContext(e){za(this);let t=$n(this),n=Hi(void 0);try{return e()}finally{$n(t),Hi(n)}}get(e,t=Gi,n){if(za(this),Object.hasOwn(e,Di))return e[Di](this);let r=Xi(n),i=$n(this),a=Hi(void 0);try{if(!(r&4)){let t=this.records.get(e);if(t===void 0){let n=Ua(e)&&mi(e);t=n&&this.injectableDefInScope(n)?Ba(Fa(e),ka):null,this.records.set(e,t)}if(t!=null)return this.hydrate(e,t,r)}let n=r&2?Ma():this.parent;return t=r&8&&t===Gi?null:t,n.get(e,t)}catch(e){let t=zi(e);throw t===-200||t===-201?new ri(t,null):e}finally{Hi(a),$n(i)}}resolveInjectorInitializers(){let e=C(null),t=$n(this),n=Hi(void 0);try{let e=this.get(ma,pa,{self:!0});for(let t of e)t()}finally{$n(t),Hi(n),C(e)}}toString(){return`R3Injector[...]`}processProvider(e){e=di(e);let t=Da(e)?e:di(e&&e.provide),n=La(e);if(!Da(e)&&e.multi===!0){let n=this.records.get(t);n||(n=Ba(void 0,ka,!0),n.factory=()=>Qi(n.multi),this.records.set(t,n)),t=e,n.multi.push(e)}this.records.set(t,n)}hydrate(e,t,n){let r=C(null);try{if(t.value===Aa)throw Ii(``);return t.value===ka&&(t.value=Aa,t.value=t.factory(void 0,n)),typeof t.value==`object`&&t.value&&Ha(t.value)&&this._ngOnDestroyHooks.add(t.value),t.value}finally{C(r)}}injectableDefInScope(e){if(!e.providedIn)return!1;let t=di(e.providedIn);return typeof t==`string`?t===`any`||this.scopes.has(t):this.injectorDefTypes.has(t)}removeOnDestroy(e){let t=this._onDestroyHooks.indexOf(e);t!==-1&&this._onDestroyHooks.splice(t,1)}};function Fa(e){let t=mi(e),n=t===null?ea(e):t.factory;if(n!==null)return n;if(e instanceof bi)throw new ri(-204,!1);if(e instanceof Function)return Ia(e);throw new ri(-204,!1)}function Ia(e){if(e.length>0)throw new ri(-204,!1);let t=gi(e);return t===null?()=>new e:()=>t.factory(e)}function La(e){return wa(e)?Ba(void 0,e.useValue):Ba(Ra(e),ka)}function Ra(e,t,n){let r;if(Da(e)){let t=di(e);return ea(t)||Fa(t)}if(wa(e))r=()=>di(e.useValue);else if(Ea(e))r=()=>e.useFactory(...Qi(e.deps||[]));else if(Ta(e))r=(t,n)=>Yi(di(e.useExisting),n!==void 0&&n&8?8:void 0);else{let t=di(e&&(e.useClass||e.provide));if(Va(e))r=()=>new t(...Qi(e.deps));else return ea(t)||Fa(t)}return r}function za(e){if(e.destroyed)throw new ri(-205,!1)}function Ba(e,t,n=!1){return{factory:e,value:t,multi:n?[]:void 0}}function Va(e){return!!e.deps}function Ha(e){return typeof e==`object`&&!!e&&typeof e.ngOnDestroy==`function`}function Ua(e){return typeof e==`function`||typeof e==`object`&&e.ngMetadataName===`InjectionToken`}function Wa(e,t){for(let n of e)Array.isArray(n)?Wa(n,t):n&&xi(n)?Wa(n.ɵproviders,t):t(n)}function Ga(e,t){let n;e instanceof Pa?(za(e),n=e):n=new qi(e);let r=$n(n),i=Hi(void 0);try{return t()}finally{$n(r),Hi(i)}}function Ka(){return Vi()!==void 0||Qn()!=null}var qa=1;function Ja(e){return Array.isArray(e)&&typeof e[qa]==`object`}function Ya(e){return Array.isArray(e)&&e[qa]===!0}function Xa(e){return!!(e.flags&4)}function Za(e){return e.componentOffset>-1}function Qa(e){return(e.flags&1)==1}function $a(e){return!!e.template}function eo(e){return!!(e[2]&512)}function to(e){return(e[2]&256)==256}var no=(function(e){return e[e.NONE=0]=`NONE`,e[e.HTML=1]=`HTML`,e[e.STYLE=2]=`STYLE`,e[e.SCRIPT=3]=`SCRIPT`,e[e.URL=4]=`URL`,e[e.RESOURCE_URL=5]=`RESOURCE_URL`,e[e.ATTRIBUTE_NO_BINDING=6]=`ATTRIBUTE_NO_BINDING`,e})(no||{}),ro=`math`;function io(e){for(;Array.isArray(e);)e=e[0];return e}function ao(e,t){return io(t[e])}function oo(e,t){return io(t[e.index])}function so(e,t){return e.data[t]}function co(e,t){return e[t]}function lo(e,t,n,r){n>=e.data.length&&(e.data[n]=null,e.blueprint[n]=null),t[n]=r}function uo(e,t){let n=t[e];return Ja(n)?n:n[0]}function fo(e){return(e[2]&128)==128}function po(e){return Ya(e[3])}function mo(e,t){return t==null?null:e[t]}function ho(e){e[17]=0}function go(e){e[2]&1024||(e[2]|=1024,fo(e)&&bo(e))}function _o(e,t){for(;e>0;)t=t[14],e--;return t}function vo(e){return!!(e[2]&9216||e[24]?.dirty)}function yo(e){e[10].changeDetectionScheduler?.notify(8),e[2]&64&&(e[2]|=1024),vo(e)&&bo(e)}function bo(e){e[10].changeDetectionScheduler?.notify(0);let t=Co(e);for(;t!==null&&!(t[2]&8192||(t[2]|=8192,!fo(t)));)t=Co(t)}function xo(e,t){if(to(e))throw new ri(911,!1);e[21]===null&&(e[21]=[]),e[21].push(t)}function So(e,t){if(e[21]===null)return;let n=e[21].indexOf(t);n!==-1&&e[21].splice(n,1)}function Co(e){let t=e[3];return Ya(t)?t[3]:t}function wo(e){return e[7]??=[]}function To(e){return e.cleanup??=[]}function Eo(e,t,n,r){let i=wo(t);i.push(n),e.firstCreatePass&&To(e).push(r,i.length-1)}var Do={lFrame:cs(null),bindingsEnabled:!0,skipHydrationRootTNode:null},Oo=!1;function ko(){return Do.lFrame.elementDepthCount}function Ao(){Do.lFrame.elementDepthCount++}function jo(){Do.lFrame.elementDepthCount--}function Mo(){return Do.bindingsEnabled}function No(){return Do.skipHydrationRootTNode!==null}function Po(e){return Do.skipHydrationRootTNode===e}function Fo(){Do.skipHydrationRootTNode=null}function T(){return Do.lFrame.lView}function Io(){return Do.lFrame.tView}function E(e){return Do.lFrame.contextLView=e,e[8]}function D(e){return Do.lFrame.contextLView=null,e}function Lo(){let e=Ro();for(;e!==null&&e.type===64;)e=e.parent;return e}function Ro(){return Do.lFrame.currentTNode}function zo(){let e=Do.lFrame,t=e.currentTNode;return e.isParent?t:t.parent}function Bo(e,t){let n=Do.lFrame;n.currentTNode=e,n.isParent=t}function Vo(){return Do.lFrame.isParent}function Ho(){Do.lFrame.isParent=!1}function Uo(){return Do.lFrame.contextLView}function Wo(){return Oo}function Go(e){let t=Oo;return Oo=e,t}function Ko(){let e=Do.lFrame,t=e.bindingRootIndex;return t===-1&&(t=e.bindingRootIndex=e.tView.bindingStartIndex),t}function qo(){return Do.lFrame.bindingIndex}function Jo(e){return Do.lFrame.bindingIndex=e}function Yo(){return Do.lFrame.bindingIndex++}function Xo(e){let t=Do.lFrame,n=t.bindingIndex;return t.bindingIndex+=e,n}function Zo(){return Do.lFrame.inI18n}function Qo(e,t){let n=Do.lFrame;n.bindingIndex=n.bindingRootIndex=e,es(t)}function $o(){return Do.lFrame.currentDirectiveIndex}function es(e){Do.lFrame.currentDirectiveIndex=e}function ts(e){let t=Do.lFrame.currentDirectiveIndex;return t===-1?null:e[t]}function ns(){return Do.lFrame.currentQueryIndex}function rs(e){Do.lFrame.currentQueryIndex=e}function is(e){let t=e[1];return t.type===2?t.declTNode:t.type===1?e[5]:null}function as(e,t,n){if(n&4){let r=t,i=e;for(;r=r.parent,r===null&&!(n&1)&&(r=is(i),!(r===null||(i=i[14],r.type&10))););if(r===null)return!1;t=r,e=i}let r=Do.lFrame=ss();return r.currentTNode=t,r.lView=e,!0}function os(e){let t=ss(),n=e[1];Do.lFrame=t,t.currentTNode=n.firstChild,t.lView=e,t.tView=n,t.contextLView=e,t.bindingIndex=n.bindingStartIndex,t.inI18n=!1}function ss(){let e=Do.lFrame,t=e===null?null:e.child;return t===null?cs(e):t}function cs(e){let t={currentTNode:null,isParent:!0,lView:null,tView:null,selectedIndex:-1,contextLView:null,elementDepthCount:0,currentNamespace:null,currentDirectiveIndex:-1,bindingRootIndex:-1,bindingIndex:-1,currentQueryIndex:0,parent:e,child:null,inI18n:!1};return e!==null&&(e.child=t),t}function ls(){let e=Do.lFrame;return Do.lFrame=e.parent,e.currentTNode=null,e.lView=null,e}var us=ls;function ds(){let e=ls();e.isParent=!0,e.tView=null,e.selectedIndex=-1,e.contextLView=null,e.elementDepthCount=0,e.currentDirectiveIndex=-1,e.currentNamespace=null,e.bindingRootIndex=-1,e.bindingIndex=-1,e.currentQueryIndex=0}function fs(e){return(Do.lFrame.contextLView=_o(e,Do.lFrame.contextLView))[8]}function ps(){return Do.lFrame.selectedIndex}function ms(e){Do.lFrame.selectedIndex=e}function hs(){let e=Do.lFrame;return so(e.tView,e.selectedIndex)}function O(){Do.lFrame.currentNamespace=`svg`}function gs(){_s()}function _s(){Do.lFrame.currentNamespace=null}function vs(){return Do.lFrame.currentNamespace}var ys=!0;function bs(){return ys}function xs(e){ys=e}function Ss(e,t=null,n=null,r){let i=Cs(e,t,n,r);return i.resolveInjectorInitializers(),i}function Cs(e,t=null,n=null,r,i=new Set){return new Pa([n||pa,va(e)],t||Ma(),null,i)}var ws=class e{static THROW_IF_NOT_FOUND=Gi;static NULL=new _a;static create(e,t){if(Array.isArray(e))return Ss({name:``},t,e,``);{let t=e.name??``;return Ss({name:t},e.parent,e.providers,t)}}static ɵprov=pi({token:e,providedIn:`any`,factory:()=>Yi(ha)});static __NG_ELEMENT_ID__=-1},Ts=new bi(``),Es=class{static __NG_ELEMENT_ID__=Os;static __NG_ENV_ID__=e=>e},Ds=class extends Es{_lView;constructor(e){super(),this._lView=e}get destroyed(){return to(this._lView)}onDestroy(e){let t=this._lView;return xo(t,e),()=>So(t,e)}};function Os(){return new Ds(T())}var ks=new bi(``),As=(()=>{class e{taskId=0;pendingTasks=new Set;destroyed=!1;pendingTask=new ei(!1);debugTaskTracker=w(ks,{optional:!0});get hasPendingTasks(){return!this.destroyed&&this.pendingTask.value}get hasPendingTasksObservable(){return this.destroyed?new Ur(e=>{e.next(!1),e.complete()}):this.pendingTask}add(){!this.hasPendingTasks&&!this.destroyed&&this.pendingTask.next(!0);let e=this.taskId++;return this.pendingTasks.add(e),this.debugTaskTracker?.add(e),e}has(e){return this.pendingTasks.has(e)}remove(e){this.pendingTasks.delete(e),this.debugTaskTracker?.remove(e),this.pendingTasks.size===0&&this.hasPendingTasks&&this.pendingTask.next(!1)}ngOnDestroy(){this.pendingTasks.clear(),this.hasPendingTasks&&this.pendingTask.next(!1),this.destroyed=!0,this.pendingTask.unsubscribe()}static ɵprov=pi({token:e,providedIn:`root`,factory:()=>new e})}return e})(),js=class extends Qr{__isAsync;destroyRef=void 0;pendingTasks=void 0;constructor(e=!1){super(),this.__isAsync=e,Ka()&&(this.destroyRef=w(Es,{optional:!0})??void 0,this.pendingTasks=w(As,{optional:!0})??void 0)}emit(e){let t=C(null);try{super.next(e)}finally{C(t)}}subscribe(e,t,n){let r=e,i=t||(()=>null),a=n;if(e&&typeof e==`object`){let t=e;r=t.next?.bind(t),i=t.error?.bind(t),a=t.complete?.bind(t)}this.__isAsync&&(i=this.wrapInTimeout(i),r&&=this.wrapInTimeout(r),a&&=this.wrapInTimeout(a));let o=super.subscribe({next:r,error:i,complete:a});return e instanceof gr&&e.add(o),o}wrapInTimeout(e){return t=>{let n=this.pendingTasks?.add();setTimeout(()=>{try{e(t)}finally{n!==void 0&&this.pendingTasks?.remove(n)}})}}};function Ms(...e){}function Ns(e){let t,n;function r(){e=Ms;try{n!==void 0&&typeof cancelAnimationFrame==`function`&&cancelAnimationFrame(n),t!==void 0&&clearTimeout(t)}catch{}}return t=setTimeout(()=>{e(),r()}),typeof requestAnimationFrame==`function`&&(n=requestAnimationFrame(()=>{e(),r()})),()=>r()}function Ps(e){return queueMicrotask(()=>e()),()=>{e=Ms}}var Fs=`isAngularZone`,Is=`isAngularZone_ID`,Ls=0,Rs=class e{hasPendingMacrotasks=!1;hasPendingMicrotasks=!1;isStable=!0;onUnstable=new js(!1);onMicrotaskEmpty=new js(!1);onStable=new js(!1);onError=new js(!1);constructor(e){let{enableLongStackTrace:t=!1,shouldCoalesceEventChangeDetection:n=!1,shouldCoalesceRunChangeDetection:r=!1,scheduleInRootZone:i=!1}=e;if(typeof Zone>`u`)throw new ri(908,!1);Zone.assertZonePatched();let a=this;a._nesting=0,a._outer=a._inner=Zone.current,Zone.TaskTrackingZoneSpec&&(a._inner=a._inner.fork(new Zone.TaskTrackingZoneSpec)),t&&Zone.longStackTraceZoneSpec&&(a._inner=a._inner.fork(Zone.longStackTraceZoneSpec)),a.shouldCoalesceEventChangeDetection=!r&&n,a.shouldCoalesceRunChangeDetection=r,a.callbackScheduled=!1,a.scheduleInRootZone=i,Hs(a)}static isInAngularZone(){return typeof Zone<`u`&&Zone.current.get(Fs)===!0}static assertInAngularZone(){if(!e.isInAngularZone())throw new ri(909,!1)}static assertNotInAngularZone(){if(e.isInAngularZone())throw new ri(909,!1)}run(e,t,n){return this._inner.run(e,t,n)}runTask(e,t,n,r){let i=this._inner,a=i.scheduleEventTask(`NgZoneEvent: `+r,e,zs,Ms,Ms);try{return i.runTask(a,t,n)}finally{i.cancelTask(a)}}runGuarded(e,t,n){return this._inner.runGuarded(e,t,n)}runOutsideAngular(e){return this._outer.run(e)}},zs={};function Bs(e){if(e._nesting==0&&!e.hasPendingMicrotasks&&!e.isStable)try{e._nesting++,e.onMicrotaskEmpty.emit(null)}finally{if(e._nesting--,!e.hasPendingMicrotasks)try{e.runOutsideAngular(()=>e.onStable.emit(null))}finally{e.isStable=!0}}}function Vs(e){if(e.isCheckStableRunning||e.callbackScheduled)return;e.callbackScheduled=!0;function t(){Ns(()=>{e.callbackScheduled=!1,Us(e),e.isCheckStableRunning=!0,Bs(e),e.isCheckStableRunning=!1})}e.scheduleInRootZone?Zone.root.run(()=>{t()}):e._outer.run(()=>{t()}),Us(e)}function Hs(e){let t=()=>{Vs(e)},n=Ls++;e._inner=e._inner.fork({name:`angular`,properties:{[Fs]:!0,[Is]:n,[Is+n]:!0},onInvokeTask:(n,r,i,a,o,s)=>{if(qs(s))return n.invokeTask(i,a,o,s);try{return Ws(e),n.invokeTask(i,a,o,s)}finally{(e.shouldCoalesceEventChangeDetection&&a.type===`eventTask`||e.shouldCoalesceRunChangeDetection)&&t(),Gs(e)}},onInvoke:(n,r,i,a,o,s,c)=>{try{return Ws(e),n.invoke(i,a,o,s,c)}finally{e.shouldCoalesceRunChangeDetection&&!e.callbackScheduled&&!Js(s)&&t(),Gs(e)}},onHasTask:(t,n,r,i)=>{t.hasTask(r,i),n===r&&(i.change==`microTask`?(e._hasPendingMicrotasks=i.microTask,Us(e),Bs(e)):i.change==`macroTask`&&(e.hasPendingMacrotasks=i.macroTask))},onHandleError:(t,n,r,i)=>(t.handleError(r,i),e.runOutsideAngular(()=>e.onError.emit(i)),!1)})}function Us(e){e.hasPendingMicrotasks=!!(e._hasPendingMicrotasks||(e.shouldCoalesceEventChangeDetection||e.shouldCoalesceRunChangeDetection)&&e.callbackScheduled===!0)}function Ws(e){e._nesting++,e.isStable&&(e.isStable=!1,e.onUnstable.emit(null))}function Gs(e){e._nesting--,Bs(e)}var Ks=class{hasPendingMicrotasks=!1;hasPendingMacrotasks=!1;isStable=!0;onUnstable=new js;onMicrotaskEmpty=new js;onStable=new js;onError=new js;run(e,t,n){return e.apply(t,n)}runGuarded(e,t,n){return e.apply(t,n)}runOutsideAngular(e){return e()}runTask(e,t,n,r){return e.apply(t,n)}};function qs(e){return Ys(e,`__ignore_ng_zone__`)}function Js(e){return Ys(e,`__scheduler_tick__`)}function Ys(e,t){return!Array.isArray(e)||e.length!==1?!1:e[0]?.data?.[t]===!0}var Xs=class{_console=console;handleError(e){this._console.error(`ERROR`,e)}},Zs=new bi(``,{factory:()=>{let e=w(Rs),t=w(Na),n;return r=>{e.runOutsideAngular(()=>{t.destroyed&&!n?setTimeout(()=>{throw r}):(n??=t.get(Xs),n.handleError(r))})}}}),Qs={provide:ma,useValue:()=>{w(Xs,{optional:!0})},multi:!0};function k(e,t){let[n,r,i]=Un(e,t?.equal),a=n;return a[fn],a.set=r,a.update=i,a.asReadonly=$s.bind(a),a}function $s(){let e=this[fn];if(e.readonlyFn===void 0){let t=()=>this();t[fn]=e,e.readonlyFn=t}return e.readonlyFn}var ec=new bi(``,{factory:()=>tc}),tc=`ng`,nc=new bi(``),rc=new bi(``,{providedIn:`platform`,factory:()=>`unknown`}),ic=new bi(``,{factory:()=>w(Ts).body?.querySelector(`[ngCspNonce]`)?.getAttribute(`ngCspNonce`)||null}),ac=(()=>{class e{view;node;constructor(e,t){this.view=e,this.node=t}static __NG_ELEMENT_ID__=oc}return e})();function oc(){return new ac(T(),Lo())}var sc=class{},cc=new bi(``,{factory:()=>!0}),lc=new bi(``),uc=(()=>{class e{static ɵprov=pi({token:e,providedIn:`root`,factory:()=>new dc})}return e})(),dc=class{dirtyEffectCount=0;queues=new Map;add(e){this.enqueue(e),this.schedule(e)}schedule(e){e.dirty&&this.dirtyEffectCount++}remove(e){let t=e.zone,n=this.queues.get(t);n.has(e)&&(n.delete(e),e.dirty&&this.dirtyEffectCount--)}enqueue(e){let t=e.zone;this.queues.has(t)||this.queues.set(t,new Set);let n=this.queues.get(t);n.has(e)||n.add(e)}flush(){for(;this.dirtyEffectCount>0;){let e=!1;for(let[t,n]of this.queues)e||=t===null?this.flushQueue(n):t.run(()=>this.flushQueue(n));e||(this.dirtyEffectCount=0)}}flushQueue(e){let t=!1;for(let n of e)n.dirty&&(this.dirtyEffectCount--,t=!0,n.run());return t}},fc=class{[fn];constructor(e){this[fn]=e}destroy(){this[fn].destroy()}};function pc(e,t){let n=t?.injector??w(ws),r=t?.manualCleanup===!0?null:n.get(Es),i,a=n.get(ac,null,{optional:!0}),o=n.get(sc);return a===null?i=vc(e,n.get(uc),o):(i=_c(a.view,o,e),r instanceof Ds&&r._lView===a.view&&(r=null)),i.injector=n,r!==null&&(i.onDestroyFns=[r.onDestroy(()=>i.destroy())]),new fc(i)}var mc={...Yn,cleanupFns:void 0,zone:null,onDestroyFns:null,run(){let e=Go(!1);try{Xn(this)}finally{Go(e)}},cleanup(){if(!this.cleanupFns?.length)return;let e=C(null);try{for(;this.cleanupFns.length;)this.cleanupFns.pop()()}finally{this.cleanupFns=[],C(e)}}},hc={...mc,consumerMarkedDirty(){this.scheduler.schedule(this),this.notifier.notify(12)},destroy(){if(Dn(this),this.onDestroyFns!==null)for(let e of this.onDestroyFns)e();this.cleanup(),this.scheduler.remove(this)}},gc={...mc,consumerMarkedDirty(){this.view[2]|=8192,bo(this.view),this.notifier.notify(13)},destroy(){if(Dn(this),this.onDestroyFns!==null)for(let e of this.onDestroyFns)e();this.cleanup(),this.view[23]?.delete(this)}};function _c(e,t,n){let r=Object.create(gc);return r.view=e,r.zone=typeof Zone<`u`?Zone.current:null,r.notifier=t,r.fn=yc(r,n),e[23]??=new Set,e[23].add(r),r.consumerMarkedDirty(r),r}function vc(e,t,n){let r=Object.create(hc);return r.fn=yc(r,e),r.scheduler=t,r.notifier=n,r.zone=typeof Zone<`u`?Zone.current:null,r.scheduler.add(r),r.notifier.notify(12),r}function yc(e,t){return()=>{t(t=>(e.cleanupFns??=[]).push(t))}}function bc(e){return typeof e==`function`&&e[fn]!==void 0}function xc(e){return bc(e)&&typeof e.set==`function`}var Sc=Symbol(`InputSignalNode#UNSET`),Cc={...qn,transformFn:void 0,applyValueToInputSignal(e,t){Gn(e,t)}};function wc(e){return{toString:e}.toString()}var Tc=(function(e){return e[e.TemplateCreateStart=0]=`TemplateCreateStart`,e[e.TemplateCreateEnd=1]=`TemplateCreateEnd`,e[e.TemplateUpdateStart=2]=`TemplateUpdateStart`,e[e.TemplateUpdateEnd=3]=`TemplateUpdateEnd`,e[e.LifecycleHookStart=4]=`LifecycleHookStart`,e[e.LifecycleHookEnd=5]=`LifecycleHookEnd`,e[e.OutputStart=6]=`OutputStart`,e[e.OutputEnd=7]=`OutputEnd`,e[e.BootstrapApplicationStart=8]=`BootstrapApplicationStart`,e[e.BootstrapApplicationEnd=9]=`BootstrapApplicationEnd`,e[e.BootstrapComponentStart=10]=`BootstrapComponentStart`,e[e.BootstrapComponentEnd=11]=`BootstrapComponentEnd`,e[e.ChangeDetectionStart=12]=`ChangeDetectionStart`,e[e.ChangeDetectionEnd=13]=`ChangeDetectionEnd`,e[e.ChangeDetectionSyncStart=14]=`ChangeDetectionSyncStart`,e[e.ChangeDetectionSyncEnd=15]=`ChangeDetectionSyncEnd`,e[e.AfterRenderHooksStart=16]=`AfterRenderHooksStart`,e[e.AfterRenderHooksEnd=17]=`AfterRenderHooksEnd`,e[e.ComponentStart=18]=`ComponentStart`,e[e.ComponentEnd=19]=`ComponentEnd`,e[e.DeferBlockStateStart=20]=`DeferBlockStateStart`,e[e.DeferBlockStateEnd=21]=`DeferBlockStateEnd`,e[e.DynamicComponentStart=22]=`DynamicComponentStart`,e[e.DynamicComponentEnd=23]=`DynamicComponentEnd`,e[e.HostBindingsUpdateStart=24]=`HostBindingsUpdateStart`,e[e.HostBindingsUpdateEnd=25]=`HostBindingsUpdateEnd`,e})(Tc||{}),Ec=class{previousValue;currentValue;firstChange;constructor(e,t,n){this.previousValue=e,this.currentValue=t,this.firstChange=n}isFirstChange(){return this.firstChange}};function Dc(e,t,n,r){t===null?e[n]=r:t.applyValueToInputSignal(t,r)}var Oc=null,kc=(()=>{Oc=jc;let e=()=>jc;return e.ngInherit=!0,e})();function Ac(){return Oc}function jc(e){return e.type.prototype.ngOnChanges&&(e.setInput=Nc),Mc}function Mc(){let e=Fc(this),t=e?.current;if(t){let n=e.previous;if(n===fa)e.previous=t;else for(let e in t)n[e]=t[e];e.current=null,this.ngOnChanges(t)}}function Nc(e,t,n,r,i){let a=this.declaredInputs[r],o=Fc(e)||Ic(e,{previous:fa,current:null}),s=o.current||={},c=o.previous,l=c[a];s[a]=new Ec(l&&l.currentValue,n,c===fa),Dc(e,t,i,n)}var Pc=`__ngSimpleChanges__`;function Fc(e){return Object.hasOwn(e,Pc)&&e[Pc]||null}function Ic(e,t){return e[Pc]=t}var Lc=[],Rc=function(e,t=null,n){for(let r=0;r=r)break}else t[c]<0&&(e[17]+=65536),(s>14>16&&(e[2]&3)===t&&(e[2]+=16384,Gc(o,a)):Gc(o,a)}var qc=-1,Jc=class{factory;name;injectImpl;resolving=!1;canSeeViewProviders;multi;componentProviders;index;providerFactory;constructor(e,t,n,r){this.factory=e,this.name=r,this.canSeeViewProviders=t,this.injectImpl=n}};function Yc(e){return!!(e.flags&8)}function Xc(e){return!!(e.flags&16)}function Zc(e,t,n){let r=0;for(;rt){o=a-1;break}}}for(;a>16}function al(e,t){let n=il(e),r=t;for(;n>0;)r=r[14],n--;return r}var ol=!0;function sl(e){let t=ol;return ol=e,t}var cl=255,ll=5,ul=0,dl={};function fl(e,t,n){let r;typeof n==`string`?r=n.charCodeAt(0)||0:Object.hasOwn(n,Ei)&&(r=n[Ei]),r??=n[Ei]=ul++;let i=r&cl,a=1<>ll)]|=a}function pl(e,t){let n=hl(e,t);if(n!==-1)return n;let r=t[1];r.firstCreatePass&&(e.injectorIndex=t.length,ml(r.data,e),ml(t,null),ml(r.blueprint,null));let i=gl(e,t),a=e.injectorIndex;if(nl(i)){let e=rl(i),n=al(i,t),r=n[1].data;for(let i=0;i<8;i++)t[a+i]=n[e+i]|r[e+i]}return t[a+8]=i,a}function ml(e,t){e.push(0,0,0,0,0,0,0,0,t)}function hl(e,t){return e.injectorIndex===-1||e.parent&&e.parent.injectorIndex===e.injectorIndex||t[e.injectorIndex+8]===null?-1:e.injectorIndex}function gl(e,t){if(e.parent&&e.parent.injectorIndex!==-1)return e.parent.injectorIndex;let n=0,r=null,i=t;for(;i!==null;){if(r=jl(i),r===null)return qc;if(n++,i=i[14],r.injectorIndex!==-1)return r.injectorIndex|n<<16}return qc}function _l(e,t,n){fl(e,t,n)}function vl(e,t,n){if(n&8||e!==void 0)return e;Li(t,`NodeInjector`)}function yl(e,t,n,r){if(n&8&&r===void 0&&(r=null),!(n&3)){let i=e[9],a=Hi(void 0);try{return i?i.get(t,r,n&8):Ui(t,r,n&8)}finally{Hi(a)}}return vl(r,t,n)}function bl(e,t,n,r=0,i){if(e!==null){if(t[2]&2048&&!(r&2)){let i=Al(e,t,n,r,dl);if(i!==dl)return i}let i=xl(e,t,n,r,dl);if(i!==dl)return i}return yl(t,n,r,i)}function xl(e,t,n,r,i){let a=Tl(n);if(typeof a==`function`){if(!as(t,e,r))return r&1?vl(i,n,r):yl(t,n,r,i);try{let e;if(e=a(r),e==null&&!(r&8))Li(n);else return e}finally{us()}}else if(typeof a==`number`){let i=null,o=hl(e,t),s=qc,c=r&1?t[15][5]:null;for((o===-1||r&4)&&(s=o===-1?gl(e,t):t[o+8],s===qc||!Dl(r,!1)?o=-1:(i=t[1],o=rl(s),t=al(s,t)));o!==-1;){let e=t[1];if(El(a,o,e.data)){let e=Sl(o,t,n,i,r,c);if(e!==dl)return e}s=t[o+8],s!==qc&&Dl(r,t[1].data[o+8]===c)&&El(a,o,t)?(i=e,o=rl(s),t=al(s,t)):o=-1}}return i}function Sl(e,t,n,r,i,a){let o=t[1],s=o.data[e+8],c=Cl(s,o,n,r==null?Za(s)&&ol:r!=o&&!!(s.type&3),i&1&&a===s);return c===null?dl:wl(t,o,c,s,i)}function Cl(e,t,n,r,i){let a=e.providerIndexes,o=t.data,s=a&1048575,c=e.directiveStart,l=e.directiveEnd,u=a>>20,d=r?s:s+u,f=i?s+u:l;for(let e=d;e=c&&t.type===n)return e}if(i){let e=o[c];if(e&&$a(e)&&e.type===n)return c}return null}function wl(e,t,n,r,i){let a=e[n],o=t.data;if(a instanceof Jc){let s=a;if(s.resolving)throw Ii(``);let c=sl(s.canSeeViewProviders);s.resolving=!0,o[n].type||o[n];let l=s.injectImpl?Hi(s.injectImpl):null;as(e,r,0);try{a=e[n]=s.factory(void 0,i,o,e,r),t.firstCreatePass&&n>=r.directiveStart&&zc(n,o[n],t)}finally{l!==null&&Hi(l),sl(c),s.resolving=!1,us()}}return a}function Tl(e){if(typeof e==`string`)return e.charCodeAt(0)||0;let t=Object.hasOwn(e,Ei)?e[Ei]:void 0;return typeof t==`number`?t>=0?t&cl:kl:t}function El(e,t,n){let r=1<>ll)]&r)}function Dl(e,t){return!(e&2)&&!(e&1&&t)}var Ol=class{_tNode;_lView;constructor(e,t){this._tNode=e,this._lView=t}get(e,t,n){return bl(this._tNode,this._lView,e,Xi(n),t)}};function kl(){return new Ol(Lo(),T())}function Al(e,t,n,r,i){let a=e,o=t;for(;a!==null&&o!==null&&o[2]&2048&&!eo(o);){let e=xl(a,o,n,r|2,dl);if(e!==dl)return e;r&=-5;let t=a.parent;if(!t){let e=o[20];if(e){let t=e.get(n,dl,r);if(t!==dl)return t}t=jl(o),o=o[14]}a=t}return i}function jl(e){let t=e[1],n=t.type;return n===2?t.declTNode:n===1?e[5]:null}function Ml(e){return{token:e.token,providedIn:e.autoProvided===!1?null:`root`,factory:e.factory,value:void 0}}function Nl(){return Pl(Lo(),T())}function Pl(e,t){return new Fl(oo(e,t))}var Fl=(()=>{class e{nativeElement;constructor(e){this.nativeElement=e}static __NG_ELEMENT_ID__=Nl}return e})();function Il(e){return e instanceof Fl?e.nativeElement:e}function Ll(){return this._results[Symbol.iterator]()}var Rl=class{_emitDistinctChangesOnly;dirty=!0;_onDirty=void 0;_results=[];_changesDetected=!1;_changes=void 0;length=0;first=void 0;last=void 0;get changes(){return this._changes??=new Qr}constructor(e=!1){this._emitDistinctChangesOnly=e}get(e){return this._results[e]}map(e){return this._results.map(e)}filter(e){return this._results.filter(e)}find(e){return this._results.find(e)}reduce(e,t){return this._results.reduce(e,t)}forEach(e){this._results.forEach(e)}some(e){return this._results.some(e)}toArray(){return this._results.slice()}toString(){return this._results.toString()}reset(e,t){this.dirty=!1;let n=na(e);(this._changesDetected=!ta(this._results,n,t))&&(this._results=n,this.length=n.length,this.last=n[this.length-1],this.first=n[0])}notifyOnChanges(){this._changes!==void 0&&(this._changesDetected||!this._emitDistinctChangesOnly)&&this._changes.next(this)}onDirty(e){this._onDirty=e}setDirty(){this.dirty=!0,this._onDirty?.()}destroy(){this._changes!==void 0&&(this._changes.complete(),this._changes.unsubscribe())}[Symbol.iterator]=Ll};function zl(e){return(e.flags&128)==128}var Bl=(function(e){return e[e.OnPush=0]=`OnPush`,e[e.Eager=1]=`Eager`,e[e.Default=1]=`Default`,e})(Bl||{}),Vl=new Map,Hl=0;function Ul(){return Hl++}function Wl(e){Vl.set(e[19],e)}function Gl(e){Vl.delete(e[19])}var Kl=`__ngContext__`;function ql(e,t){Ja(t)?(e[Kl]=t[19],Wl(t)):e[Kl]=t}function Jl(e){return Xl(e[12])}function Yl(e){return Xl(e[4])}function Xl(e){for(;e!==null&&!Ya(e);)e=e[4];return e}var Zl=void 0;function Ql(e){Zl=e}function $l(){if(Zl!==void 0)return Zl;if(typeof document<`u`)return document;throw new ri(210,!1)}var eu=!1,tu=new bi(``,{factory:()=>eu}),nu=new WeakMap;function ru(e,t){if(typeof e!=`object`||!e)return;let n=nu.get(e);n||(n=new WeakSet,nu.set(e,n)),n.add(t)}function iu(e){return(e.flags&32)==32}var au=()=>null;function ou(e,t,n=!1){return au(e,t,n)}function su(e,t){let n=e.contentQueries;if(n!==null){let r=C(null);try{for(let r=0;r|^->||--!>|)/g,yu=`​$1​`;function bu(e){return e.replace(_u,e=>e.replace(vu,yu))}function xu(e,t){return e.createText(t)}function Su(e,t,n){e.setValue(t,n)}function Cu(e,t){return e.createComment(bu(t))}function wu(e,t,n){return e.createElement(t,n)}function Tu(e,t,n,r,i){e.insertBefore(t,n,r,i)}function Eu(e,t,n){e.appendChild(t,n)}function Du(e,t,n,r,i){r===null?Eu(e,t,n):Tu(e,t,n,r,i)}function Ou(e,t,n,r){e.removeChild(null,t,n,r)}function ku(e,t,n){e.setAttribute(t,`style`,n)}function Au(e,t,n){n===``?e.removeAttribute(t,`class`):e.setAttribute(t,`class`,n)}function ju(e,t,n){let{mergedAttrs:r,classes:i,styles:a}=n;r!==null&&Zc(e,t,r),i!==null&&Au(e,t,i),a!==null&&ku(e,t,a)}function Mu(e){let t=Nu();return t?t.sanitize(no.URL,e)||``:pu(e,`URL`)?fu(e):gu(Mi(e))}function Nu(){let e=T();return e&&e[10].sanitizer}function Pu(e){return e.ownerDocument.defaultView}function Fu(e){return e.ownerDocument}function Iu(e,t,n){let r=e.length;for(;;){let i=e.indexOf(t,n);if(i===-1)return i;if(i===0||e.charCodeAt(i-1)<=32){let n=t.length;if(i+n===r||e.charCodeAt(i+n)<=32)return i}n=i+1}}var Lu=`ng-template`;function Ru(e,t,n,r){let i=0;if(r){for(;i-1){let e;for(;++ia?``:i[u+1].toLowerCase(),r&2&&l!==e){if(Hu(r))return!1;o=!0}}}}}return Hu(r)||o}function Hu(e){return!(e&1)}function Uu(e,t,n,r){if(t===null)return-1;let i=0;if(r||!n){let n=!1;for(;i-1)for(n++;n0?`="`+t+`"`:``)+`]`}else r&8?i+=`.`+o:r&4&&(i+=` `+o)}else i!==``&&!Hu(o)&&(t+=Yu(a,i),i=``),r=o,a||=!Hu(r);n++}return i!==``&&(t+=Yu(a,i)),t}function Zu(e){return e.map(Xu).join(`,`)}function Qu(e){let t=[],n=[],r=1,i=2;for(;r!1});function id(e){if(!e)return 0;let t=e.toLowerCase().indexOf(`ms`)>-1?1:1e3;return parseFloat(e)*t}function ad(e,t){return e.getPropertyValue(t).split(`,`).map(e=>e.trim())}function od(e){let t=ad(e,`transition-property`),n=ad(e,`transition-duration`),r=ad(e,`transition-delay`),i={propertyName:``,duration:0,animationName:void 0};for(let e=0;ei.duration&&(i.propertyName=t[e],i.duration=a)}return i}function sd(e){let t=ad(e,`animation-name`),n=ad(e,`animation-delay`),r=ad(e,`animation-duration`),i=ad(e,`animation-iteration-count`),a={animationName:``,propertyName:void 0,duration:0};for(let e=0;ea.duration&&s!==`infinite`&&(a.animationName=t[e],a.duration=o)}return a}function cd(e,t){return e!==void 0&&e.duration>t.duration}function ld(e){return(e.animationName!=null||e.propertyName!=null)&&e.duration>0}function ud(e){let t=e.effect?.getTiming();if(t===void 0)return;let n=typeof t.duration==`number`?t.duration:0,r=(t.delay??0)+n,i=e.playbackRate;return i!==void 0&&i!==0&&i!==1&&(r/=Math.abs(i)),r}function dd(e,t){let n=getComputedStyle(e),r=sd(n),i=od(n),a=r.duration>i.duration?r:i;cd(t.get(e),a)||ld(a)&&t.set(e,a)}function fd(e,t,n){if(!n)return;let r=e.getAnimations();return r.length===0?dd(e,t):pd(e,t,r)}function pd(e,t,n){let r={animationName:void 0,propertyName:void 0,duration:0};for(let e of n){if(e.effect?.getTiming()?.iterations===1/0)continue;let t=ud(e)??0,n,i;e.animationName?i=e.animationName:n=e.transitionProperty,t>=r.duration&&(r={animationName:i,propertyName:n,duration:t})}cd(t.get(e),r)||ld(r)&&t.set(e,r)}var md=new Set,hd=!1,gd=1,_d=typeof document<`u`&&typeof document?.documentElement?.getAnimations==`function`;function vd(e){return e[9].get(rd,hd)}function yd(e,t,n){let r=xd.get(e);if(r){for(let e of t)r.classList.push(e);for(let e of n)r.cleanupFns.push(e)}else xd.set(e,{classList:t,cleanupFns:n})}function bd(e){let t=xd.get(e);if(t){for(let e of t.cleanupFns)e();xd.delete(e)}Sd.delete(e)}var xd=new WeakMap,Sd=new WeakMap,Cd=new WeakMap;function wd(e){return e?e[14]??e:null}var Td=new WeakSet;function Ed(e,t,n){let r=Cd.get(e);if(!r||r.length===0)return;let i=t.parentNode,a=t.previousSibling,o=wd(n);for(let e=r.length-1;e>=0;e--){let{el:n,declarationView:s}=r[e],c=n.parentNode;n===t?(r.splice(e,1),Td.add(n),n.dispatchEvent(new CustomEvent(`animationend`,{detail:{cancel:!0}}))):(a&&n===a||c&&i&&c!==i&&(o===null||s===null||o===s))&&(r.splice(e,1),n.dispatchEvent(new CustomEvent(`animationend`,{detail:{cancel:!0}})),n.parentNode?.removeChild(n))}}function Dd(e,t,n){let r=wd(n),i=Cd.get(e);i?i.some(e=>e.el===t)||i.push({el:t,declarationView:r}):Cd.set(e,[{el:t,declarationView:r}])}function Od(e){let t=e[26]??={};return t.enter??=new Map}function kd(e){let t=typeof e==`function`?e():e,n=Array.isArray(t)?t:null;return typeof t==`string`&&(n=t.trim().split(/\s+/).filter(e=>e)),n}function Ad(e){return e.composedPath?e.composedPath()[0]:e.target}function jd(e,t){let n=Sd.get(t);if(n===void 0)return!0;if(t!==Ad(e))return!1;let r=e.animation;if(r){let e=ud(r);if(e!==void 0&&e+gd{class e{impl=null;execute(){this.impl?.execute()}static ɵprov=pi({token:e,providedIn:`root`,factory:()=>new e})}return e})(),Rd=[0,1,2,3],zd=(()=>{class e{ngZone=w(Rs);scheduler=w(sc);errorHandler=w(Xs,{optional:!0});sequences=new Set;deferredRegistrations=new Set;executing=!1;constructor(){w(Pd,{optional:!0})}execute(){let e=this.sequences.size>0;e&&Rc(Tc.AfterRenderHooksStart),this.executing=!0;for(let e of Rd)for(let t of this.sequences)if(!t.erroredOrDestroyed&&t.hooks[e])try{t.pipelinedValue=this.ngZone.runOutsideAngular(()=>this.maybeTrace(()=>{let n=t.hooks[e];return n(t.pipelinedValue)},t.snapshot))}catch(e){t.erroredOrDestroyed=!0,this.errorHandler?.handleError(e)}this.executing=!1;for(let e of this.sequences)e.afterRun(),e.once&&(this.sequences.delete(e),e.destroy());for(let e of this.deferredRegistrations)this.sequences.add(e);this.deferredRegistrations.size>0&&this.scheduler.notify(7),this.deferredRegistrations.clear(),e&&Rc(Tc.AfterRenderHooksEnd)}register(e){let{view:t}=e;t===void 0?this.executing?this.deferredRegistrations.add(e):this.addSequence(e):((t[25]??=[]).push(e),bo(t),t[2]|=8192)}addSequence(e){this.sequences.add(e),this.scheduler.notify(7)}unregister(e){this.executing&&this.sequences.has(e)?(e.erroredOrDestroyed=!0,e.pipelinedValue=void 0,e.once=!0):(this.sequences.delete(e),this.deferredRegistrations.delete(e))}maybeTrace(e,t){return t?t.run(Nd.AFTER_NEXT_RENDER,e):e()}static ɵprov=pi({token:e,providedIn:`root`,factory:()=>new e})}return e})(),Bd=class{impl;hooks;view;once;snapshot;erroredOrDestroyed=!1;pipelinedValue=void 0;unregisterOnDestroy;constructor(e,t,n,r,i,a=null){this.impl=e,this.hooks=t,this.view=n,this.once=r,this.snapshot=a,this.unregisterOnDestroy=i?.onDestroy(()=>this.destroy())}afterRun(){this.erroredOrDestroyed=!1,this.pipelinedValue=void 0,this.snapshot?.dispose(),this.snapshot=null}destroy(){this.impl.unregister(this),this.unregisterOnDestroy?.();let e=this.view?.[25];e&&(this.view[25]=e.filter(e=>e!==this))}};function Vd(e,t){let n=t?.injector??w(ws);return Id(`NgAfterNextRender`),Ud(e,n,t,!0)}function Hd(e){return e instanceof Function?[void 0,void 0,e,void 0]:[e.earlyRead,e.write,e.mixedReadWrite,e.read]}function Ud(e,t,n,r){let i=t.get(Ld);i.impl??=t.get(zd);let a=t.get(Pd,null,{optional:!0}),o=n?.manualCleanup===!0?null:t.get(Es),s=t.get(ac,null,{optional:!0}),c=new Bd(i.impl,Hd(e),s?.view,r,o,a?.snapshot(null));return i.impl.register(c),c}var Wd=new bi(``,{factory:()=>{let e=w(Na),t=new Set;return e.onDestroy(()=>t.clear()),{queue:t,isScheduled:!1,scheduler:null,injector:e}}});function Gd(e,t,n){let r=e.get(Wd);if(Array.isArray(t))for(let e of t)r.queue.add(e),n?.detachedLeaveAnimationFns?.push(e);else r.queue.add(t),n?.detachedLeaveAnimationFns?.push(t);r.scheduler&&r.scheduler(e)}function Kd(e,t){let n=e.get(Wd);if(Array.isArray(t))for(let e of t)n.queue.delete(e);else n.queue.delete(t)}function qd(e,t){let n=e.get(Wd);if(t.detachedLeaveAnimationFns){for(let e of t.detachedLeaveAnimationFns)n.queue.delete(e);t.detachedLeaveAnimationFns=void 0}}function Jd(e){let t=e.get(Wd);t.isScheduled||=(Vd(()=>{t.isScheduled=!1;for(let e of t.queue)e();t.queue.clear()},{injector:t.injector}),!0)}function Yd(e){let t=e.get(Wd);t.scheduler=Jd,t.scheduler(e)}function Xd(e,t){for(let[n,r]of t)Gd(e,r.animateFns)}function Zd(e,t,n,r){let i=e?.[26]?.enter;t!==null&&i&&i.has(n.index)&&Xd(r,i)}function Qd(e,t,n,r){try{n.get(ha)}catch{return r(!1)}let i=e?.[26];i?.enter?.has(t.index)&&Kd(n,i.enter.get(t.index).animateFns);let a=$d(e,t,i);if(a.size===0){let n=!1;if(e){let r=[];tf(e,t,r),n=r.length>0}if(!n)return r(!1)}e&&md.add(e[19]),Gd(n,()=>ef(e,t,i||void 0,a,r),i||void 0)}function $d(e,t,n){let r=new Map,i=n?.leave;if(i&&i.has(t.index)&&r.set(t.index,i.get(t.index)),e&&i)for(let[n,a]of i){if(r.has(n))continue;let i=e[1].data[n].parent;for(;i;){if(i===t){r.set(n,a);break}i=i.parent}}return r}function ef(e,t,n,r,i){let a=[];if(n&&n.leave)for(let[e]of r){if(!n.leave.has(e))continue;let t=n.leave.get(e);for(let e of t.animateFns){let{promise:t}=e();a.push(t)}n.detachedLeaveAnimationFns=void 0}if(e&&tf(e,t,a),a.length>0){let t=n||e?.[26];if(t){let n=t.running;n&&a.push(n),t.running=Promise.allSettled(a),rf(e,t.running,i)}else Promise.allSettled(a).then(()=>{e&&md.delete(e[19]),i(!0)})}else e&&md.delete(e[19]),i(!1)}function tf(e,t,n){if(t.type&12){let r=e[t.index];if(Ya(r))for(let e=10;e{e[26]?.running===t&&(e[26].running=void 0,md.delete(e[19])),n(!0)})}function af(e,t,n,r,i,a,o,s){if(i!=null){let c,l=!1;Ya(i)?c=i:Ja(i)&&(l=!0,i=i[0]);let u=io(i);e===0&&r!==null?(Zd(s,r,a,n),o==null?Eu(t,r,u):Tu(t,r,u,o||null,!0)):e===1&&r!==null?(Zd(s,r,a,n),Tu(t,r,u,o||null,!0),Ed(a,u,s)):e===2?(s?.[26]?.leave?.has(a.index)&&Dd(a,u,s),Td.delete(u),Qd(s,a,n,e=>{if(Td.has(u)){Td.delete(u);return}Ou(t,u,l,e)})):e===3&&(Td.delete(u),Qd(s,a,n,()=>{t.destroyNode(u)})),c!=null&&kf(t,e,n,c,a,r,o)}}function of(e,t){cf(e,t),t[0]=null,t[5]=null}function sf(e,t,n,r,i,a){r[0]=i,r[5]=t,Tf(e,r,n,1,i,a)}function cf(e,t){t[10].changeDetectionScheduler?.notify(9),Tf(e,t,t[11],2,null,null)}function lf(e){let t=e[12];if(!t)return ff(e[1],e);for(;t;){let n=null;if(Ja(t))n=t[12];else{let e=t[10];e&&(n=e)}if(!n){for(;t&&!t[4]&&t!==e;)Ja(t)&&ff(t[1],t),t=t[3];t===null&&(t=e),Ja(t)&&ff(t[1],t),n=t&&t[4]}t=n}}function uf(e,t){let n=e[9],r=n.indexOf(t);n.splice(r,1)}function df(e,t){if(to(t))return;let n=t[11];n.destroyNode&&Tf(e,t,n,3,null,null),lf(t)}function ff(e,t){if(to(t))return;let n=C(null);try{t[2]&=-129,t[2]|=256,t[24]&&Dn(t[24]),mf(e,t),pf(e,t),t[1].type===1&&t[11].destroy();let n=t[16];if(n!==null&&Ya(t[3])){n!==t[3]&&uf(n,t);let r=t[18];r!==null&&r.detachView(e)}Gl(t)}finally{C(n)}}function pf(e,t){let n=e.cleanup,r=t[7];if(n!==null)for(let e=0;e=0?r[t]():r[-t].unsubscribe(),e+=2}else{let t=r[n[e+1]];n[e].call(t)}r!==null&&(t[7]=null);let i=t[21];if(i!==null){t[21]=null;for(let e=0;e27&&zf(e,t,27,!1),Rc(o?Tc.TemplateUpdateStart:Tc.TemplateCreateStart,i,n),n(r,i)}finally{ms(a),Rc(o?Tc.TemplateUpdateEnd:Tc.TemplateCreateEnd,i,n)}}function Uf(e,t,n){Xf(e,t,n),(n.flags&64)==64&&Zf(e,t,n)}function Wf(e,t,n=oo){let r=t.localNames;if(r!==null){let i=t.index+1;for(let a=0;a{bo(e.lView)},consumerOnSignalRead(){this.lView[24]=this}};function xp(e){let t=e[24]??Object.create(Sp);return t.lView=e,t}var Sp={...mn,consumerIsAlwaysLive:!0,kind:`template`,consumerMarkedDirty:e=>{let t=Co(e.lView);for(;t&&!Cp(t[1]);)t=Co(t);t&&go(t)},consumerOnSignalRead(){this.lView[24]=this}};function Cp(e){return e.type!==2}function wp(e){if(e[23]===null)return;let t=!0;for(;t;){let n=!1;for(let t of e[23])if(t.dirty&&(n=!0,t.zone===null||Zone.current===t.zone?t.run():t.zone.run(()=>t.run()),e[23]===null))return;t=n&&!!(e[2]&8192)}}var Tp=100;function Ep(e,t=0){let n=e[10].rendererFactory;n.begin?.();try{Dp(e,t)}finally{n.end?.()}}function Dp(e,t){let n=Wo();try{Go(!0),Np(e,t);let n=0;for(;vo(e);){if(n===Tp)throw new ri(103,!1);n++,Np(e,1)}}finally{Go(n)}}function Op(e,t,n,r){if(to(t))return;let i=t[2];os(t);let a=!0,o=null,s=null;Cp(e)?(s=_p(t),o=Sn(s)):pn()===null?(a=!1,s=xp(t),o=Sn(s)):t[24]&&=(Dn(t[24]),null);try{ho(t),Jo(e.bindingStartIndex),n!==null&&Hf(e,t,n,2,r);let a=(i&3)==3;if(a){let n=e.preOrderCheckHooks;n!==null&&Vc(t,n,null)}else{let n=e.preOrderHooks;n!==null&&Hc(t,n,0,null),Uc(t,0)}if(Ap(t),wp(t),kp(t,0),e.contentQueries!==null&&su(e,t),a){let n=e.contentCheckHooks;n!==null&&Vc(t,n)}else{let n=e.contentHooks;n!==null&&Hc(t,n,1),Uc(t,1)}Fp(e,t);let o=e.components;o!==null&&Pp(t,o,0);let s=e.viewQuery;if(s!==null&&cu(2,s,r),a){let n=e.viewCheckHooks;n!==null&&Vc(t,n)}else{let n=e.viewHooks;n!==null&&Hc(t,n,2),Uc(t,2)}if(e.firstUpdatePass===!0&&(e.firstUpdatePass=!1),t[22]){for(let e of t[22])e();t[22]=null}hp(t),t[2]&=-73}catch(e){throw bo(t),e}finally{s!==null&&(wn(s,o),a&&yp(s)),ds()}}function kp(e,t){for(let n=Jl(e);n!==null;n=Yl(n))for(let e=10;e0&&(e[n-1][4]=r[4]);let a=aa(e,10+t);of(r[1],r);let o=a[18];o!==null&&o.detachView(a[1]),r[3]=null,r[4]=null,r[2]&=-129}return r}function Hp(e,t,n,r){let i=10+r,a=n.length;r>0&&(n[i-1][4]=t),r-1&&(Vp(e,n),aa(t,n))}this._attachedToViewContainer=!1}df(this._lView[1],this._lView)}onDestroy(e){xo(this._lView,e)}markForCheck(){Ip(this._cdRefInjectingView||this._lView,4)}detach(){this._lView[2]&=-129}reattach(){yo(this._lView),this._lView[2]|=128}detectChanges(){this._lView[2]|=1024,Ep(this._lView)}checkNoChanges(){}attachToViewContainerRef(){if(this._appRef)throw new ri(902,!1);this._attachedToViewContainer=!0}detachFromAppRef(){this._appRef=null;let e=eo(this._lView),t=this._lView[16];t!==null&&!e&&uf(t,this._lView),cf(this._lView[1],this._lView)}attachToAppRef(e){if(this._attachedToViewContainer)throw new ri(902,!1);this._appRef=e;let t=eo(this._lView),n=this._lView[16];n!==null&&!t&&Up(n,this._lView),yo(this._lView)}},Gp=(()=>{class e{_declarationLView;_declarationTContainer;elementRef;static __NG_ELEMENT_ID__=Kp;constructor(e,t,n){this._declarationLView=e,this._declarationTContainer=t,this.elementRef=n}get ssrId(){return this._declarationTContainer.tView?.ssrId||null}createEmbeddedView(e,t){return this.createEmbeddedViewImpl(e,t)}createEmbeddedViewImpl(e,t,n){return new Wp(dp(this._declarationLView,this._declarationTContainer,e,{embeddedViewInjector:t,dehydratedView:n}))}}return e})();function Kp(){return qp(Lo(),T())}function qp(e,t){return e.type&4?new Gp(t,e,Pl(e,t)):null}function Jp(e,t,n,r,i){let a=e.data[t];if(a===null)a=Yp(e,t,n,r,i),Zo()&&(a.flags|=32);else if(a.type&64){a.type=n,a.value=r,a.attrs=i;let e=zo();a.injectorIndex=e===null?-1:e.injectorIndex}return Bo(a,!0),a}function Yp(e,t,n,r,i){let a=Ro(),o=Vo(),s=o?a:a&&a.parent,c=e.data[t]=Zp(e,s,n,t,r,i);return Xp(e,c,a,o),c}function Xp(e,t,n,r){e.firstChild===null&&(e.firstChild=t),n!==null&&(r?n.child==null&&t.parent!==null&&(n.child=t):n.next===null&&(n.next=t,t.prev=n))}function Zp(e,t,n,r,i,a){let o=t?t.injectorIndex:-1,s=0;return No()&&(s|=128),{type:n,index:r,insertBeforeIndex:null,injectorIndex:o,directiveStart:-1,directiveEnd:-1,directiveStylingLast:-1,componentOffset:-1,controlDirectiveIndex:-1,customControlIndex:-1,propertyBindings:null,flags:s,providerIndexes:0,value:i,namespace:vs(),attrs:a,mergedAttrs:null,localNames:null,initialInputs:null,inputs:null,hostDirectiveInputs:null,outputs:null,hostDirectiveOutputs:null,directiveToIndex:null,tView:null,next:null,prev:null,projectionNext:null,child:null,parent:t,projection:null,styles:null,stylesWithoutHost:null,residualStyles:void 0,classes:null,classesWithoutHost:null,residualClasses:void 0,classBindings:0,styleBindings:0}}function Qp(e){let t=e[6]??[],n=e[3][11],r=[];for(let e of t)e.data.di===void 0?$p(e,n):r.push(e);e[6]=r}function $p(e,t){let n=0,r=e.firstChild;if(r){let i=e.data.r;for(;nnull,tm=()=>null;function nm(e,t){return em(e,t)}function rm(e,t,n){return tm(e,t,n)}var im=class{},am=class{},om=(()=>{class e{static ɵprov=pi({token:e,providedIn:`root`,factory:()=>null})}return e})();function sm(e){return e.debugInfo?.className||e.type.name||null}var cm={},lm=class{injector;parentInjector;constructor(e,t){this.injector=e,this.parentInjector=t}get(e,t,n){let r=this.injector.get(e,cm,n);return r!==cm||t===cm?r:this.parentInjector.get(e,t,n)}};function um(e,t,n){return e[t]=n}function dm(e,t){return e[t]}function fm(e,t,n){if(n===$u)return!1;let r=e[t];return!Object.is(r,n)&&(e[t]=n,!0)}function pm(e,t,n,r){let i=fm(e,t,n);return fm(e,t+1,r)||i}function mm(e,t,n,r,i){let a=pm(e,t,n,r);return fm(e,t+2,i)||a}function hm(e,t,n,r,i,a){let o=pm(e,t,n,r);return pm(e,t+2,i,a)||o}function gm(e,t,n){return function r(i){let a=r.__ngNativeEl__;a!==void 0&&ru(i,a),Ip(Za(e)?uo(e.index,t):t,5);let o=t[8],s=_m(t,o,n,i),c=r.__ngNextListenerFn__;for(;c;)s=_m(t,o,c,i)&&s,c=c.__ngNextListenerFn__;return s}}function _m(e,t,n,r){let i=C(null);try{return Rc(Tc.OutputStart,t,n),n(r)!==!1}catch(t){return ap(e,t),!1}finally{Rc(Tc.OutputEnd,t,n),C(i)}}function vm(e,t,n,r,i,a,o,s){let c=Qa(e),l=!1,u=null;if(!r&&c&&(u=bm(t,n,a,e.index)),u!==null){let e=u.__ngLastListenerFn__||u;e.__ngNextListenerFn__=o,u.__ngLastListenerFn__=o,l=!0}else{let o=oo(e,n),c=r?r(o):o;r||(s.__ngNativeEl__=o);let l=i.listen(c,a,s);ym(a)||xm(r?t=>r(io(t[e.index])):e.index,t,n,a,s,l,!1)}return l}function ym(e){return e.startsWith(`animation`)||e.startsWith(`transition`)}function bm(e,t,n,r){let i=e.cleanup;if(i!=null)for(let e=0;er?n[r]:null}typeof a==`string`&&(e+=2)}return null}function xm(e,t,n,r,i,a,o){let s=t.firstCreatePass?To(t):null,c=wo(n),l=c.length;c.push(i,a),s&&s.push(r,e,l,(l+1)*(o?-1:1))}function Sm(e,t,n,r,i,a){let o=t[n],s=t[1],c=o[s.data[n].outputs[r]].subscribe(a);xm(e.index,s,t,i,a,c,!0)}var Cm=Symbol(`BINDING`),wm=new bi(``);function Tm(e,t,n){let r=n?e.styles:null,i=n?e.classes:null,a=0;if(t!==null)for(let e=0;e0&&(n.directiveToIndex=new Map);for(let c=0;c0;){let n=e[--t];if(typeof n==`number`&&n<0)return n}return 0}function zm(e,t,n){if(n){if(t.exportAs)for(let r=0;r{let[n,r,i]=e[t],a={propName:n,templateName:t,isSignal:(r&Bf.SignalBased)!==0};return i&&(a.transform=i),a})}function qm(e){return Object.keys(e).map(t=>({propName:e[t],templateName:t}))}function Jm(e,t,n){let r=t instanceof Na?t:t?.injector;return r&&e.getStandaloneInjector!==null&&(r=e.getStandaloneInjector(r)||r),r?new lm(n,r):n}function Ym(e){let t=e.get(am,null);if(t===null)throw new ri(407,!1);return{rendererFactory:t,sanitizer:e.get(om,null),changeDetectionScheduler:e.get(sc,null),ngReflect:!1,tracingService:e.get(Pd,null,{optional:!0})}}function Xm(e,t,n){let r=Qm(e);return wu(t,r,r===`svg`?`svg`:r===`math`?ro:n)}function Zm(e){if((e&&`localName`in e&&typeof e.localName==`string`?e.localName:e?.tagName)?.toLowerCase()===`script`)throw new ri(905,!1)}function Qm(e){return(e.selectors[0][0]||`div`).toLowerCase()}var $m=class{componentDef;ngModule;selector;componentType;ngContentSelectors;isBoundToModule;cachedInputs=null;cachedOutputs=null;get inputs(){return this.cachedInputs??=Km(this.componentDef.inputs),this.cachedInputs}get outputs(){return this.cachedOutputs??=qm(this.componentDef.outputs),this.cachedOutputs}constructor(e,t){this.componentDef=e,this.ngModule=t,this.componentType=e.type,this.selector=Zu(e.selectors),this.ngContentSelectors=e.ngContentSelectors??[],this.isBoundToModule=!!t}create(e,t,n,r,i,a,o){Rc(Tc.DynamicComponentStart);let s=C(null);try{let s=this.componentDef,c=Jm(s,r||this.ngModule,e),l=Ym(c),u=l.tracingService;return u&&u.componentCreate?u.componentCreate(sm(s),()=>this.createComponentRef(l,c,t,n,i,a,o)):this.createComponentRef(l,c,t,n,i,a,o)}finally{C(s)}}createComponentRef(e,t,n,r,i,a,o){let s=this.componentDef,c=eh(r,s,a,i),l=e.rendererFactory.createRenderer(null,s),u=r?Gf(l,r,s.encapsulation,t):Xm(s,l,o??null);Zm(u);let d=t.get(wm,null),f=th(u,()=>t.get(Ts,null)??$l());d&&d.addHost(f);let p=a?.some(rh)||i?.some(e=>typeof e!=`function`&&e.bindings.some(rh)),m=Pf(null,c,null,512|If(s),null,null,e,l,t,null,ou(u,t,!0));d&&Wm&&f instanceof ShadowRoot&&xo(m,()=>{d.removeHost(f)}),m[27]=u,os(m);let h=null;try{let e=Vm(27,m,2,`#host`,()=>c.directiveRegistry,!0,0);ju(l,u,e),ql(u,m),Uf(c,m,e),lu(c,e,m),Hm(c,e),n!==void 0&&ah(e,this.ngContentSelectors,n),h=uo(e.index,m),m[8]=h[8],lp(c,m,null)}catch(e){throw h!==null&&Gl(h),Gl(m),e}finally{Rc(Tc.DynamicComponentEnd),ds()}return new ih(this.componentType,m,!!p)}};function eh(e,t,n,r){let i=e?[`ng-version`,`22.1.7`]:Qu(t.selectors[0]),a=null,o=null,s=0;if(n)for(let e of n)s+=e[Cm].requiredVars,e.create&&(e.targetIdx=0,(a??=[]).push(e)),e.update&&(e.targetIdx=0,(o??=[]).push(e));if(r)for(let e=0;e{if(n&1&&e)for(let t of e)t.create();if(n&2&&t)for(let e of t)e.update()}}function rh(e){let t=e[Cm].kind;return t===`input`||t===`twoWay`}var ih=class extends im{_rootLView;_hasInputBindings;instance;hostView;changeDetectorRef;componentType;location;previousInputValues=null;_tNode;constructor(e,t,n){super(),this._rootLView=t,this._hasInputBindings=n,this._tNode=so(t[1],27),this.location=Pl(this._tNode,t),this.instance=uo(this._tNode.index,t)[8],this.hostView=this.changeDetectorRef=new Wp(t,void 0),this.componentType=e}setInput(e,t){this._hasInputBindings;let n=this._tNode;if(this.previousInputValues??=new Map,this.previousInputValues.has(e)&&Object.is(this.previousInputValues.get(e),t))return;let r=this._rootLView;op(n,r[1],r,e,t),this.previousInputValues.set(e,t),Ip(uo(n.index,r),1)}get injector(){return new Ol(this._tNode,this._rootLView)}destroy(){this.hostView.destroy()}onDestroy(e){this.hostView.onDestroy(e)}};function ah(e,t,n){let r=e.projection=[];for(let e=0;e{class e{static __NG_ELEMENT_ID__=sh}return e})();function sh(){return dh(Lo(),T())}var ch=class e extends oh{_lContainer;_hostTNode;_hostLView;constructor(e,t,n){super(),this._lContainer=e,this._hostTNode=t,this._hostLView=n}get element(){return Pl(this._hostTNode,this._hostLView)}get injector(){return new Ol(this._hostTNode,this._hostLView)}get parentInjector(){let e=gl(this._hostTNode,this._hostLView);if(nl(e)){let t=al(e,this._hostLView),n=rl(e),r=t[1].data[n+8];return new Ol(r,t)}return new Ol(null,this._hostLView)}clear(){for(;this.length>0;)this.remove(this.length-1)}get(e){let t=lh(this._lContainer);return t!==null&&t[e]||null}get length(){return this._lContainer.length-10}createEmbeddedView(e,t,n){let r,i;typeof n==`number`?r=n:n!=null&&(r=n.index,i=n.injector);let a=nm(this._lContainer,e.ssrId),o=e.createEmbeddedViewImpl(t||{},i,a);return this.insertImpl(o,r,fp(this._hostTNode,a)),o}createComponent(e,t,n,r,i,a,o){let s,c=t||{};s=c.index,n=c.injector,r=c.projectableNodes,i=c.environmentInjector||c.ngModuleRef,a=c.directives,o=c.bindings;let l=new $m(Oi(e)),u=n||this.parentInjector;if(!i&&l.ngModule==null){let e=this.parentInjector.get(Na,null);e&&(i=e)}let d=Oi(l.componentType??{}),f=nm(this._lContainer,d?.id??null),p=f?.firstChild??null,m=l.create(u,r,p,i,a,o,this._getHostElementNamespace());return this.insertImpl(m.hostView,s,fp(this._hostTNode,f)),m}_getHostElementNamespace(){if(this._hostTNode.type&2){let e=this._hostTNode.parent??this._hostLView[5];return e!==null&&e.type&2&&typeof e.value==`string`&&e.value.toLowerCase()===`foreignobject`?null:e?.namespace??null}return this._hostTNode.namespace}insert(e,t){return this.insertImpl(e,t,!0)}insertImpl(t,n,r){let i=t._lView;if(po(i)){let n=this.indexOf(t);if(n!==-1)this.detach(n);else{let n=i[3],r=new e(n,n[5],n[3]);r.detach(r.indexOf(t))}}let a=this._adjustIndex(n),o=this._lContainer;return zp(o,i,a,r),t.attachToViewContainerRef(),ia(uh(o),a,t),t}move(e,t){return this.insert(e,t)}indexOf(e){let t=lh(this._lContainer);return t===null?-1:t.indexOf(e)}remove(e){let t=this._adjustIndex(e,-1),n=Vp(this._lContainer,t);n&&(aa(uh(this._lContainer),t),df(n[1],n))}detach(e){let t=this._adjustIndex(e,-1),n=Vp(this._lContainer,t);return n&&aa(uh(this._lContainer),t)!=null?new Wp(n):null}_adjustIndex(e,t=0){return e??this.length+t}};function lh(e){return e[8]}function uh(e){return e[8]||=[]}function dh(e,t){let n,r=t[e.index];return Ya(r)?n=r:(n=Lp(r,t,null,e),t[e.index]=n,Rf(t,n)),ph(n,t,e,r),new ch(n,e,t)}function fh(e,t){let n=e[11],r=n.createComment(``),i=oo(t,e);return Tu(n,n.parentNode(i),r,n.nextSibling(i),!1),r}var ph=gh,mh=()=>!1;function hh(e,t,n){return mh(e,t,n)}function gh(e,t,n,r){if(e[7])return;let i;i=n.type&8?io(r):fh(t,n),e[7]=i}var _h=class e{queryList;matches=null;constructor(e){this.queryList=e}clone(){return new e(this.queryList)}setDirty(){this.queryList.setDirty()}},vh=class e{queries;constructor(e=[]){this.queries=e}createEmbeddedView(t){let n=t.queries;if(n!==null){let r=t.contentQueries===null?n.length:t.contentQueries[0],i=[];for(let e=0;e0)r.push(o[e/2]);else{let i=a[e+1],o=t[-n];for(let e=10;ee.trim())}function Mh(e,t,n){e.queries===null&&(e.queries=new bh),e.queries.track(new xh(t,n))}function Nh(e,t){return e.queries.getByIndex(t)}function Ph(e,t){let n=e[1],r=Nh(n,t);return r.crossesNgTemplate?Dh(n,e,t,[]):Eh(n,e,r,t)}function Fh(e,t,n){let r,i=Nn(()=>{r._dirtyCounter();let n=zh(r,e);if(t&&n===void 0)throw new ri(-951,!1);return n});return r=i[fn],r._dirtyCounter=k(0),r._flatValue=void 0,i}function Ih(e){return Fh(!0,!1,e)}function Lh(e){return Fh(!0,!0,e)}function Rh(e,t){let n=e[fn];n._lView=T(),n._queryIndex=t,n._queryList=Oh(n._lView,t),n._queryList.onDirty(()=>n._dirtyCounter.update(e=>e+1))}function zh(e,t){let n=e._lView,r=e._queryIndex;if(n===void 0||r===void 0||n[2]&4)return t?void 0:pa;let i=Oh(n,r),a=Ph(n,r);return i.reset(a,Il),t?i.first:i._changesDetected||e._flatValue===void 0?e._flatValue=i.toArray():e._flatValue}function Bh(e){return!!e&&typeof e.then==`function`}function Vh(e){return!!e&&typeof e.subscribe==`function`}var Hh=class{},Uh=class extends Hh{injector;instance=null;constructor(e){super();let t=new Pa([...e.providers,{provide:Hh,useValue:this}],e.parent||Ma(),e.debugName,new Set([`environment`]));this.injector=t,e.runEnvironmentInitializers&&t.resolveInjectorInitializers()}destroy(){this.injector.destroy()}onDestroy(e){this.injector.onDestroy(e)}};function Wh(e,t,n=null){return new Uh({providers:e,parent:t,debugName:n,runEnvironmentInitializers:!0}).injector}var Gh=(()=>{class e{_injector;cachedInjectors=new Map;constructor(e){this._injector=e}getOrCreateStandaloneInjector(e){if(!e.standalone)return null;if(!this.cachedInjectors.has(e)){let t=ya(!1,e.type),n=t.length>0?Wh([t],this._injector,``):null;this.cachedInjectors.set(e,n)}return this.cachedInjectors.get(e)}ngOnDestroy(){try{for(let e of this.cachedInjectors.values())e!==null&&e.destroy()}finally{this.cachedInjectors.clear()}}static ɵprov=pi({token:e,providedIn:`environment`,factory:()=>new e(Yi(Na))})}return e})();function Kh(e){return wc(()=>{let t=Qh(e),n={...t,decls:e.decls,vars:e.vars,template:e.template,consts:e.consts||null,ngContentSelectors:e.ngContentSelectors,onPush:e.changeDetection!==Bl.Eager,directiveDefs:null,pipeDefs:null,dependencies:t.standalone&&e.dependencies||null,getStandaloneInjector:t.standalone?e=>e.get(Gh).getOrCreateStandaloneInjector(n):null,getExternalStyles:null,signals:e.signals??!1,data:e.data||{},encapsulation:e.encapsulation||uu.Emulated,styles:e.styles||pa,_:null,schemas:e.schemas||null,tView:null,id:``};t.standalone&&Id(`NgStandalone`),$h(n);let r=e.dependencies;return n.directiveDefs=eg(r,qh),n.pipeDefs=eg(r,Ai),n.id=tg(n),n})}function qh(e){return Oi(e)||ki(e)}function Jh(e,t){if(e==null)return fa;let n={};for(let r in e)if(Object.hasOwn(e,r)){let i=e[r],a,o,s,c;Array.isArray(i)?(s=i[0],a=i[1],o=i[2]??a,c=i[3]||null):(a=i,o=i,s=Bf.None,c=null),n[a]=[r,s,c],t[a]=o}return n}function Yh(e){if(e==null)return fa;let t={};for(let n in e)Object.hasOwn(e,n)&&(t[e[n]]=n);return t}function Xh(e){return wc(()=>{let t=Qh(e);return $h(t),t})}function Zh(e){return{type:e.type,name:e.name,factory:null,pure:e.pure!==!1,standalone:e.standalone??!0,onDestroy:e.type.prototype.ngOnDestroy||null}}function Qh(e){let t={};return{type:e.type,providersResolver:null,viewProvidersResolver:null,factory:null,hostBindings:e.hostBindings||null,hostVars:e.hostVars||0,hostAttrs:e.hostAttrs||null,contentQueries:e.contentQueries||null,declaredInputs:t,inputConfig:e.inputs||fa,exportAs:e.exportAs||null,standalone:e.standalone??!0,signals:e.signals===!0,selectors:e.selectors||pa,viewQuery:e.viewQuery||null,features:e.features||null,setInput:null,resolveHostDirectives:null,hostDirectives:null,controlDef:null,signalFormsInputPresence:null,inputs:Jh(e.inputs,t),outputs:Yh(e.outputs),debugInfo:null}}function $h(e){e.features?.forEach(t=>t(e))}function eg(e,t){return e?()=>{let n=typeof e==`function`?e():e,r=[];for(let e of n){let n=t(e);n!==null&&r.push(n)}return r}:null}function tg(e){let t=0,n=typeof e.consts==`function`?``:e.consts,r=[e.selectors,e.ngContentSelectors,e.hostVars,e.hostAttrs,n,e.vars,e.decls,e.encapsulation,e.standalone,e.signals,e.exportAs,JSON.stringify(e.inputs),JSON.stringify(e.outputs),Object.getOwnPropertyNames(e.type.prototype),!!e.contentQueries,!!e.viewQuery];for(let e of r.join(`|`))t=Math.imul(31,t)+e.charCodeAt(0)<<0;return t+=2147483648,`c`+t}var ng=new bi(``),rg=(()=>{class e{resolve;reject;initialized=!1;done=!1;donePromise=new Promise((e,t)=>{this.resolve=e,this.reject=t});appInits=w(ng,{optional:!0})??[];injector=w(ws);constructor(){}runInitializers(){if(this.initialized)return;let e=[];for(let t of this.appInits){let n=Ga(this.injector,t);if(Bh(n))e.push(n);else if(Vh(n)){let t=new Promise((e,t)=>{n.subscribe({complete:e,error:t})});e.push(t)}}let t=()=>{this.done=!0,this.resolve()};Promise.all(e).then(()=>{t()}).catch(e=>{this.reject(e)}),e.length===0&&t(),this.initialized=!0}static ɵfac=function(t){return new(t||e)};static ɵprov=Ml({token:e,factory:e.ɵfac})}return e})();function ig(e,t,n,r,i,a,o,s){if(n.firstCreatePass){e.mergedAttrs=el(e.mergedAttrs,e.attrs);let t=e.tView=jf(2,e,i,a,o,n.directiveRegistry,n.pipeRegistry,null,n.schemas,n.consts,null);n.queries!==null&&(n.queries.template(n,e),t.queries=n.queries.embeddedTView(e))}s&&(e.flags|=s),Bo(e,!1);let c=cg(n,t,e,r);bs()&&bf(n,t,c,e),ql(c,t);let l=Lp(c,t,c,e);t[r+27]=l,Rf(t,l),hh(l,e,t)}function ag(e,t,n,r,i,a,o,s,c,l,u){let d=n+27,f;return t.firstCreatePass?(f=Jp(t,d,4,o||null,s||null),Mo()&&Dm(t,e,f,mo(t.consts,l),$f),Bc(t,f)):f=t.data[d],ig(f,e,t,n,r,i,a,c),Qa(f)&&Uf(t,e,f),l!=null&&Wf(e,f,u),f}function og(e,t,n,r,i,a,o,s,c,l,u){let d=n+27,f;if(t.firstCreatePass){if(f=Jp(t,d,4,o||null,s||null),l!=null){let e=mo(t.consts,l);f.localNames=[];for(let t=0;t{throw new ri(600,``)})}var pg=10,mg=(()=>{class e{_runningTick=!1;_destroyed=!1;_destroyListeners=[];_views=[];internalErrorHandler=w(Zs);afterRenderManager=w(Ld);zonelessEnabled=w(cc);rootEffectScheduler=w(uc);dirtyFlags=0;tracingSnapshot=null;allTestViews=new Set;autoDetectTestViews=new Set;includeAllTestViews=!1;afterTick=new Qr;get allViews(){return[...(this.includeAllTestViews?this.allTestViews:this.autoDetectTestViews).keys(),...this._views]}get destroyed(){return this._destroyed}componentTypes=[];components=[];internalPendingTask=w(As);get isStable(){return this.internalPendingTask.hasPendingTasksObservable.pipe(ti(e=>!e))}constructor(){w(Pd,{optional:!0})}whenStable(){let e;return new Promise(t=>{e=this.isStable.subscribe({next:e=>{e&&t()}})}).finally(()=>{e.unsubscribe()})}_injector=w(Na);_rendererFactory=null;get injector(){return this._injector}bootstrap(e,t){return this.bootstrapImpl(e,t)}bootstrapImpl(e,t,n=ws.NULL){return this._injector.get(Rs).run(()=>{if(Rc(Tc.BootstrapComponentStart),!this._injector.get(rg).done)throw new ri(405,``);let r=Oi(e),i=this._injector.get(Hh),a=new $m(r,i);this.componentTypes.push(e);let{hostElement:o,directives:s,bindings:c}=hg(t),l=o||a.selector,u=a.create(n,[],l,i.injector,s,c),d=u.location.nativeElement,f=u.injector.get(ug,null);return f?.registerApplication(d),u.onDestroy(()=>{this.detachView(u.hostView),gg(this.components,u),f?.unregisterApplication(d)}),this._loadComponent(u),Rc(Tc.BootstrapComponentEnd,u),u})}tick(){this.zonelessEnabled||(this.dirtyFlags|=1),this._tick()}_tick(){Rc(Tc.ChangeDetectionStart),this.tracingSnapshot===null?this.tickImpl():this.tracingSnapshot.run(Nd.CHANGE_DETECTION,this.tickImpl)}tickImpl=()=>{if(this._runningTick)throw Rc(Tc.ChangeDetectionEnd),new ri(101,!1);let e=C(null);try{this._runningTick=!0,this.synchronize()}finally{this._runningTick=!1,this.tracingSnapshot?.dispose(),this.tracingSnapshot=null,C(e),this.afterTick.next(),Rc(Tc.ChangeDetectionEnd)}};synchronize(){this._rendererFactory===null&&!this._injector.destroyed&&(this._rendererFactory=this._injector.get(am,null,{optional:!0}));let e=0;for(;this.dirtyFlags!==0&&e++vo(e))){this.dirtyFlags|=2;return}this.dirtyFlags&=-8}attachView(e){let t=e;this._views.push(t),t.attachToAppRef(this)}detachView(e){let t=e;gg(this._views,t),t.detachFromAppRef()}_loadComponent(e){this.attachView(e.hostView);try{this.tick()}catch(e){this.internalErrorHandler(e)}this.components.push(e),this._injector.get(dg,[]).forEach(t=>t(e))}ngOnDestroy(){if(!this._destroyed)try{this._destroyListeners.forEach(e=>e()),this._views.slice().forEach(e=>e.destroy())}finally{this._destroyed=!0,this._views=[],this._destroyListeners=[]}}onDestroy(e){return this._destroyListeners.push(e),()=>gg(this._destroyListeners,e)}destroy(){if(this._destroyed)throw new ri(406,!1);let e=this._injector;e.destroy&&!e.destroyed&&e.destroy()}get viewCount(){return this._views.length}static ɵfac=function(t){return new(t||e)};static ɵprov=Ml({token:e,factory:e.ɵfac})}return e})();function hg(e){return e===void 0||typeof e==`string`||e instanceof Element?{hostElement:e}:e}function gg(e,t){let n=e.indexOf(t);n>-1&&e.splice(n,1)}function j(e,t,n,r){let i=T();return fm(i,Yo(),t)&&(Io(),ep(hs(),i,e,t,n,r)),j}function _g(e){if(Id(`NgAnimateEnter`),!_d)return _g;let t=T();if(vd(t))return _g;let n=Lo(),r=t[9].get(Rs);return Md(Od(t),n,()=>vg(t,n,e,r)),Yd(t[9]),Xd(t[9],Od(t)),_g}function vg(e,t,n,r){let i=oo(t,e),a=e[11],o=kd(n),s=[],c=!1,l=e=>{if(Ad(e)!==i)return;let t=e instanceof AnimationEvent?`animationend`:`transitionend`;r.runOutsideAngular(()=>{a.listen(i,t,u)})},u=e=>{Ad(e)===i&&(jd(e,i)&&(c=!0),yg(e,i,a))};if(o&&o.length>0){r.runOutsideAngular(()=>{s.push(a.listen(i,`animationstart`,l)),s.push(a.listen(i,`transitionstart`,l))}),yd(i,o,s);for(let e of o)a.addClass(i,e);r.runOutsideAngular(()=>{requestAnimationFrame(()=>{if(!c&&(fd(i,Sd,_d),!Sd.has(i))){for(let e of o)a.removeClass(i,e);bd(i)}})})}}function yg(e,t,n){let r=xd.get(t);if(Ad(e)===t&&r&&jd(e,t)){e.stopPropagation();for(let e of r.classList)n.removeClass(t,e);bd(t)}}var bg=class{destroy(e){}updateValue(e,t){}swap(e,t){let n=Math.min(e,t),r=Math.max(e,t),i=this.detach(r);if(r-n>1){let e=this.detach(n);this.attach(n,i),this.attach(r,e)}else this.attach(n,i)}move(e,t){this.attach(t,this.detach(e))}};function xg(e,t,n,r,i){return e===n&&Object.is(t,r)?1:Object.is(i(e,t),i(n,r))?-1:0}function Sg(e,t,n,r){let i,a,o=0,s=e.length-1;if(Array.isArray(t)){C(r);let c=t.length-1;for(C(null);o<=s&&o<=c;){let r=e.at(o),l=t[o],u=xg(o,r,o,l,n);if(u!==0){u<0&&e.updateValue(o,l),o++;continue}let d=e.at(s),f=t[c],p=xg(s,d,c,f,n);if(p!==0){p<0&&e.updateValue(s,f),s--,c--;continue}let m=n(o,r),h=n(s,d),g=n(o,l);if(Object.is(g,h)){let t=n(c,f);Object.is(t,m)?(e.swap(o,s),e.updateValue(s,f),c--,s--):e.move(s,o),e.updateValue(o,l),o++;continue}if(i??=new Eg,a??=Tg(e,o,s,n),Cg(e,i,o,g))e.updateValue(o,l),o++,s++;else if(a.has(g))i.set(m,e.detach(o)),s--;else{let n=e.create(o,t[o]);e.attach(o,n),o++,s++}}for(;o<=c;)wg(e,i,n,o,t[o]),o++}else if(t!=null){C(r);let c=t[Symbol.iterator]();C(null);let l=c.next();for(;!l.done&&o<=s;){let t=e.at(o),r=l.value,u=xg(o,t,o,r,n);if(u!==0)u<0&&e.updateValue(o,r),o++,l=c.next();else{i??=new Eg,a??=Tg(e,o,s,n);let u=n(o,r);if(Cg(e,i,o,u))e.updateValue(o,r),o++,s++,l=c.next();else if(!a.has(u))e.attach(o,e.create(o,r)),o++,s++,l=c.next();else{let r=n(o,t);i.set(r,e.detach(o)),s--}}}for(;!l.done;)wg(e,i,n,e.length,l.value),l=c.next()}for(;o<=s;)e.destroy(e.detach(s--));i?.forEach(t=>{e.destroy(t)})}function Cg(e,t,n,r){return t!==void 0&&t.has(r)?(e.attach(n,t.get(r)),t.delete(r),!0):!1}function wg(e,t,n,r,i){if(Cg(e,t,r,n(r,i)))e.updateValue(r,i);else{let t=e.create(r,i);e.attach(r,t)}}function Tg(e,t,n,r){let i=new Set;for(let a=t;a<=n;a++)i.add(r(a,e.at(a)));return i}var Eg=class{kvMap=new Map;_vMap=void 0;has(e){return this.kvMap.has(e)}delete(e){if(!this.has(e))return!1;let t=this.kvMap.get(e);return this._vMap!==void 0&&this._vMap.has(t)?(this.kvMap.set(e,this._vMap.get(t)),this._vMap.delete(t)):this.kvMap.delete(e),!0}get(e){return this.kvMap.get(e)}set(e,t){if(this.kvMap.has(e)){let n=this.kvMap.get(e);this._vMap===void 0&&(this._vMap=new Map);let r=this._vMap;for(;r.has(n);)n=r.get(n);r.set(n,t)}else this.kvMap.set(e,t)}forEach(e){for(let[t,n]of this.kvMap)if(e(n,t),this._vMap!==void 0){let r=this._vMap;for(;r.has(n);)n=r.get(n),e(n,t)}}};function M(e,t,n,r,i,a,o,s){Id(`NgControlFlow`);let c=T(),l=Io();return og(c,l,e,t,n,r,i,mo(l.consts,a),256,o,s),Dg}function Dg(e,t,n,r,i,a,o,s){Id(`NgControlFlow`);let c=T(),l=Io();return og(c,l,e,t,n,r,i,mo(l.consts,a),512,o,s),Dg}function N(e,t){Id(`NgControlFlow`);let n=T(),r=Yo(),i=n[r]===$u?-1:n[r],a=i===-1?void 0:Ng(n,27+i);if(fm(n,r,e)){let r=C(null);try{if(a!==void 0&&Bp(a,0),e!==-1){let r=27+e,i=Ng(n,r),a=Rg(n[1],r),o=rm(i,a,n);zp(i,dp(n,a,t,{dehydratedView:o}),0,fp(a,o))}}finally{C(r)}}else if(a!==void 0){let e=Rp(a,0);e!==void 0&&(e[8]=t)}}var Og=class{lContainer;$implicit;$index;constructor(e,t,n){this.lContainer=e,this.$implicit=t,this.$index=n}get $count(){return this.lContainer.length-10}};function kg(e){return e}function Ag(e,t){return t}var jg=class{hasEmptyBlock;trackByFn;liveCollection;constructor(e,t,n){this.hasEmptyBlock=e,this.trackByFn=t,this.liveCollection=n}};function P(e,t,n,r,i,a,o,s,c,l,u,d,f){Id(`NgControlFlow`);let p=T(),m=Io(),h=c!==void 0,g=T(),_=new jg(h,s?o.bind(g[15][8]):o);g[27+e]=_,og(p,m,e+1,t,n,r,i,mo(m.consts,a),256),h&&og(p,m,e+2,c,l,u,d,mo(m.consts,f),512)}var Mg=class extends bg{lContainer;hostLView;templateTNode;operationsCounter=void 0;needsIndexUpdate=!1;constructor(e,t,n){super(),this.lContainer=e,this.hostLView=t,this.templateTNode=n}get length(){return this.lContainer.length-10}at(e){return this.getLView(e)[8].$implicit}attach(e,t){let n=t[6];this.needsIndexUpdate||=e!==this.length,zp(this.lContainer,t,e,fp(this.templateTNode,n)),Pg(this.lContainer,e)}detach(e){return this.needsIndexUpdate||=e!==this.length-1,Fg(this.lContainer,e),Ig(this.lContainer,e)}create(e,t){let n=nm(this.lContainer,this.templateTNode.tView.ssrId);return dp(this.hostLView,this.templateTNode,new Og(this.lContainer,t,e),{dehydratedView:n})}destroy(e){df(e[1],e)}updateValue(e,t){this.getLView(e)[8].$implicit=t}reset(){this.needsIndexUpdate=!1}updateIndexes(){if(this.needsIndexUpdate)for(let e=0;e0){let e=n[9];qd(e,r),md.delete(n[19]),r.detachedLeaveAnimationFns=void 0}}function Fg(e,t){if(e.length<=10)return;let n=e[10+t],r=n?n[26]:void 0;r&&r.leave&&r.leave.size>0&&(r.detachedLeaveAnimationFns=[])}function Ig(e,t){return Vp(e,t)}function Lg(e,t){return Rp(e,t)}function Rg(e,t){return so(e,t)}function I(e,t,n){let r=T();return fm(r,Yo(),t)&&(Io(),qf(hs(),r,e,t,r[11],n)),I}function zg(e,t,n,r,i){op(t,e,n,i?`class`:`style`,r)}function L(e,t,n,r){let i=T(),a=i[1],o=e+27,s=a.firstCreatePass?Vm(o,i,2,t,$f,Mo(),n,r):a.data[o];if(Za(s)){let n=i[10].tracingService;if(n&&n.componentCreate){let o=a.data[s.directiveStart+s.componentOffset];return n.componentCreate(sm(o),()=>(Bg(e,t,i,s,r),L))}}return Bg(e,t,i,s,r),L}function Bg(e,t,n,r,i){if(rp(r,n,e,t,Hg),Qa(r)){let e=n[1];Uf(e,n,r),lu(e,r,n)}i!=null&&Wf(n,r)}function R(){let e=Io(),t=ip(Lo());return e.firstCreatePass&&Hm(e,t),Po(t)&&Fo(),jo(),t.classesWithoutHost!=null&&Yc(t)&&zg(e,t,T(),t.classesWithoutHost,!0),t.stylesWithoutHost!=null&&Xc(t)&&zg(e,t,T(),t.stylesWithoutHost,!1),R}function z(e,t,n,r){return L(e,t,n,r),R(),z}function B(e,t,n,r){let i=T(),a=i[1],o=e+27,s=a.firstCreatePass?Um(o,a,2,t,n,r):a.data[o];return rp(s,i,e,t,Hg),r!=null&&Wf(i,s),B}function V(){return Po(ip(Lo()))&&Fo(),jo(),V}function Vg(e,t,n,r){return B(e,t,n,r),V(),Vg}var Hg=(e,t,n,r,i)=>(xs(!0),wu(t[11],r,vs()));function Ug(e,t,n){let r=T(),i=r[1],a=e+27,o=i.firstCreatePass?Vm(a,r,8,`ng-container`,$f,Mo(),t,n):i.data[a];if(rp(o,r,e,`ng-container`,Kg),Qa(o)){let e=r[1];Uf(e,r,o),lu(e,o,r)}return n!=null&&Wf(r,o),Ug}function Wg(){let e=Io(),t=ip(Lo());return e.firstCreatePass&&Hm(e,t),Wg}function Gg(e,t,n){return Ug(e,t,n),Wg(),Gg}var Kg=(e,t,n,r,i)=>(xs(!0),Cu(t[11],``));function H(){return T()}function qg(e,t,n){let r=T();return fm(r,Yo(),t)&&(Io(),Jf(hs(),r,e,t,r[11],n)),qg}var Jg=void 0;function Yg(e){let t=Math.floor(Math.abs(e)),n=e.toString().replace(/^[^.]*\.?/,``).length;return t===1&&n===0?1:5}var Xg=[`en`,[[`a`,`p`],[`AM`,`PM`]],[[`AM`,`PM`]],[[`S`,`M`,`T`,`W`,`T`,`F`,`S`],[`Sun`,`Mon`,`Tue`,`Wed`,`Thu`,`Fri`,`Sat`],[`Sunday`,`Monday`,`Tuesday`,`Wednesday`,`Thursday`,`Friday`,`Saturday`],[`Su`,`Mo`,`Tu`,`We`,`Th`,`Fr`,`Sa`]],Jg,[[`J`,`F`,`M`,`A`,`M`,`J`,`J`,`A`,`S`,`O`,`N`,`D`],[`Jan`,`Feb`,`Mar`,`Apr`,`May`,`Jun`,`Jul`,`Aug`,`Sep`,`Oct`,`Nov`,`Dec`],[`January`,`February`,`March`,`April`,`May`,`June`,`July`,`August`,`September`,`October`,`November`,`December`]],Jg,[[`B`,`A`],[`BC`,`AD`],[`Before Christ`,`Anno Domini`]],0,[6,0],[`M/d/yy`,`MMM d, y`,`MMMM d, y`,`EEEE, MMMM d, y`],[`h:mm a`,`h:mm:ss a`,`h:mm:ss a z`,`h:mm:ss a zzzz`],[`{1}, {0}`,Jg,Jg,Jg],[`.`,`,`,`;`,`%`,`+`,`-`,`E`,`×`,`‰`,`∞`,`NaN`,`:`],[`#,##0.###`,`#,##0%`,`¤#,##0.00`,`#E0`],`USD`,`$`,`US Dollar`,{},`ltr`,Yg],Zg=Object.create(null);function Qg(e){let t=t_(e),n=$g(t);if(n)return n;let r=t.split(`-`)[0];if(n=$g(r),n)return n;if(r===`en`)return Xg;throw new ri(701,!1)}function $g(e){if(!(e in Zg)){let t=Wi.ng&&Wi.ng.common&&Wi.ng.common.locales&&Wi.ng.common.locales[e];return t!==void 0&&(Zg[e]=t),t}return Zg[e]}var e_={LocaleId:0,DayPeriodsFormat:1,DayPeriodsStandalone:2,DaysFormat:3,DaysStandalone:4,MonthsFormat:5,MonthsStandalone:6,Eras:7,FirstDayOfWeek:8,WeekendRange:9,DateFormat:10,TimeFormat:11,DateTimeFormat:12,NumberSymbols:13,NumberFormats:14,CurrencyCode:15,CurrencySymbol:16,CurrencyName:17,Currencies:18,Directionality:19,PluralCase:20,ExtraData:21};function t_(e){return e.toLowerCase().replace(/_/g,`-`)}var n_=`en-US`;function r_(e){typeof e==`string`&&e.toLowerCase().replace(/_/g,`-`)}function U(e,t,n){let r=T(),i=Io(),a=Lo();return i_(i,r,r[11],a,e,t,n),U}function W(e,t,n){let r=T(),i=Io(),a=Lo();return(a.type&3||n)&&vm(a,i,r,n,r[11],e,t,gm(a,r,t)),W}function i_(e,t,n,r,i,a,o){let s=!0,c=null;if((r.type&3||o)&&(c??=gm(r,t,a),vm(r,e,t,o,n,i,a,c)&&(s=!1)),s){let e=r.outputs?.[i],n=r.hostDirectiveOutputs?.[i];if(n&&n.length)for(let e=0;e>17&32767}function m_(e){return(e&2)==2}function h_(e,t){return e&131071|t<<17}function g_(e){return e|2}function __(e){return(e&131068)>>2}function v_(e,t){return e&-131069|t<<2}function y_(e){return(e&1)==1}function b_(e){return e|1}function x_(e,t,n,r,i,a){let o=a?t.classBindings:t.styleBindings,s=p_(o),c=__(o);e[r]=n;let l=!1,u;if(Array.isArray(n)){let e=n;u=e[1],(u===null||ua(e,u)>0)&&(l=!0)}else u=n;if(i){if(c!==0){let t=p_(e[s+1]);e[r+1]=f_(t,s),t!==0&&(e[t+1]=v_(e[t+1],r)),e[s+1]=h_(e[s+1],r)}else e[r+1]=f_(s,0),s!==0&&(e[s+1]=v_(e[s+1],r)),s=r}else e[r+1]=f_(c,0),s===0?s=r:e[c+1]=v_(e[c+1],r),c=r;l&&(e[r+1]=g_(e[r+1])),C_(e,u,r,!0),C_(e,u,r,!1),S_(t,u,e,r,a),o=f_(s,c),a?t.classBindings=o:t.styleBindings=o}function S_(e,t,n,r,i){let a=i?e.residualClasses:e.residualStyles;a!=null&&typeof t==`string`&&ua(a,t)>=0&&(n[r+1]=b_(n[r+1]))}function C_(e,t,n,r){let i=e[n+1],a=t===null,o=r?p_(i):__(i),s=!1;for(;o!==0&&(s===!1||a);){let n=e[o],i=e[o+1];w_(n,t)&&(s=!0,e[o+1]=r?b_(i):g_(i)),o=r?p_(i):__(i)}s&&(e[n+1]=r?g_(i):b_(i))}function w_(e,t){return e===null||t==null||(Array.isArray(e)?e[1]:e)===t?!0:Array.isArray(e)&&typeof t==`string`?ua(e,t)>=0:!1}var T_={textEnd:0,key:0,keyEnd:0,value:0,valueEnd:0};function E_(e){return e.substring(T_.key,T_.keyEnd)}function D_(e){return k_(e),O_(e,A_(e,0,T_.textEnd))}function O_(e,t){let n=T_.textEnd;return n===t?-1:(t=T_.keyEnd=j_(e,T_.key=t,n),A_(e,t,n))}function k_(e){T_.key=0,T_.keyEnd=0,T_.value=0,T_.valueEnd=0,T_.textEnd=e.length}function A_(e,t,n){for(;t32;)t++;return t}function K(e,t,n){return P_(e,t,n,!1),K}function q(e,t){return P_(e,t,null,!0),q}function M_(e){F_(G_,N_,e,!0)}function N_(e,t){for(let n=D_(t);n>=0;n=O_(t,n))ca(e,E_(t),!0)}function P_(e,t,n,r){let i=T(),a=Io(),o=Xo(2);if(a.firstUpdatePass&&L_(a,e,o,r),t!==$u&&fm(i,o,t)){let s=a.data[ps()];q_(a,s,i,i[11],e,i[o+1]=X_(t,n),r,o)}}function F_(e,t,n,r){let i=Io(),a=Xo(2);i.firstUpdatePass&&L_(i,null,a,r);let o=T();if(n!==$u&&fm(o,a,n)){let s=i.data[ps()];if(Z_(s,r)&&!I_(i,a)){let e=r?s.classesWithoutHost:s.stylesWithoutHost;e!==null&&(n=ci(e,n||``)),zg(i,s,o,n,r)}else K_(i,s,o,o[11],o[a+1],o[a+1]=W_(e,t,n),r,a)}}function I_(e,t){return t>=e.expandoStartIndex}function L_(e,t,n,r){let i=e.data;if(i[n+1]===null){let a=i[ps()],o=I_(e,n);Z_(a,r)&&t===null&&!o&&(t=!1),t=R_(i,a,t,r),x_(i,a,t,n,o,r)}}function R_(e,t,n,r){let i=ts(e),a=r?t.residualClasses:t.residualStyles;if(i===null)(r?t.classBindings:t.styleBindings)===0&&(n=H_(null,e,t,n,r),n=U_(n,t.attrs,r),a=null);else{let o=t.directiveStylingLast;if(o===-1||e[o]!==i){if(n=H_(i,e,t,n,r),a===null){let n=z_(e,t,r);n!==void 0&&Array.isArray(n)&&(n=H_(null,e,t,n[1],r),n=U_(n,t.attrs,r),B_(e,t,r,n))}else a=V_(e,t,r)}}return a!==void 0&&(r?t.residualClasses=a:t.residualStyles=a),n}function z_(e,t,n){let r=n?t.classBindings:t.styleBindings;if(__(r)!==0)return e[p_(r)]}function B_(e,t,n,r){let i=n?t.classBindings:t.styleBindings;e[p_(i)]=r}function V_(e,t,n){let r,i=t.directiveEnd;for(let a=1+t.directiveStylingLast;a0;){let t=e[i],a=Array.isArray(t),c=a?t[1]:t,l=c===null,u=n[i+1];u===$u&&(u=l?pa:void 0);let d=l?la(u,r):c===r?u:void 0;if(a&&!Y_(d)&&(d=la(t,r)),Y_(d)&&(s=d,o))return s;let f=e[i+1];i=o?p_(f):__(f)}if(t!==null){let e=a?t.residualClasses:t.residualStyles;e!=null&&(s=la(e,r))}return s}function Y_(e){return e!==void 0}function X_(e,t){return e==null||e===``||(typeof t==`string`?e=fu(e)+t:typeof e==`object`&&(e=si(fu(e)))),e}function Z_(e,t){return!!(e.flags&(t?8:16))}function J(e,t=``){let n=T(),r=Io(),i=e+27,a=r.firstCreatePass?Jp(r,i,1,t,null):r.data[i],o=Q_(r,n,a,t);n[i]=o,bs()&&bf(r,n,o,a),Bo(a,!1)}var Q_=(e,t,n,r)=>(xs(!0),xu(t[11],r));function $_(e,t,n,r=``){return fm(e,Yo(),n)?t+Mi(n)+r:$u}function ev(e,t,n,r,i,a=``){let o=pm(e,qo(),n,i);return Xo(2),o?t+Mi(n)+r+Mi(i)+a:$u}function tv(e,t,n,r,i,a,o,s=``){let c=mm(e,qo(),n,i,o);return Xo(3),c?t+Mi(n)+r+Mi(i)+a+Mi(o)+s:$u}function nv(e,t,n,r,i,a,o,s,c,l=``){let u=hm(e,qo(),n,i,o,c);return Xo(4),u?t+Mi(n)+r+Mi(i)+a+Mi(o)+s+Mi(c)+l:$u}function Y(e){return X(``,e),Y}function X(e,t,n){let r=T(),i=$_(r,e,t,n);return i!==$u&&av(r,ps(),i),X}function Z(e,t,n,r,i){let a=T(),o=ev(a,e,t,n,r,i);return o!==$u&&av(a,ps(),o),Z}function rv(e,t,n,r,i,a,o){let s=T(),c=tv(s,e,t,n,r,i,a,o);return c!==$u&&av(s,ps(),c),rv}function iv(e,t,n,r,i,a,o,s,c){let l=T(),u=nv(l,e,t,n,r,i,a,o,s,c);return u!==$u&&av(l,ps(),u),iv}function av(e,t,n){let r=ao(t,e);Su(e[11],r,n)}function ov(e,t,n){xc(t)&&(t=t());let r=T();return fm(r,Yo(),t)&&(Io(),qf(hs(),r,e,t,r[11],n)),ov}function sv(e,t){let n=xc(e);return n&&e.set(t),n}function cv(e,t){let n=T(),r=Io(),i=Lo();return i_(r,n,n[11],i,e,t),cv}var lv={};function uv(e){Id(`NgLet`);let t=Io(),n=T(),r=e+27;return Bo(Jp(t,r,128,null,null),!1),lo(t,n,r,lv),uv}function dv(e){return lo(Io(),T(),ps(),e),e}function fv(e){let t=co(Uo(),27+e);if(t===lv)throw new ri(314,!1);return t}function pv(e,t,n=``){return $_(T(),e,t,n)}function mv(e,t){let n=Ko()+e,r=T();return r[n]===$u?um(r,n,t()):dm(r,n)}function hv(e,t,n){return vv(T(),Ko(),e,t,n)}function gv(e,t,n,r,i){return bv(T(),Ko(),e,t,n,r,i)}function _v(e,t){let n=e[t];return n===$u?void 0:n}function vv(e,t,n,r,i,a){let o=t+n;return fm(e,o,i)?um(e,o+1,a?r.call(a,i):r(i)):_v(e,o+1)}function yv(e,t,n,r,i,a,o){let s=t+n;return pm(e,s,i,a)?um(e,s+2,o?r.call(o,i,a):r(i,a)):_v(e,s+2)}function bv(e,t,n,r,i,a,o,s){let c=t+n;return mm(e,c,i,a,o)?um(e,c+3,s?r.call(s,i,a,o):r(i,a,o)):_v(e,c+3)}function xv(e,t){let n=Io(),r,i=e+27;n.firstCreatePass?(r=Sv(t,n.pipeRegistry),n.data[i]=r,r.onDestroy&&(n.destroyHooks??=[]).push(i,r.onDestroy)):r=n.data[i];let a=r.factory||(r.factory=ea(r.type,!0)),o=Hi(Em);try{let e=sl(!1),t=a();return sl(e),lo(n,T(),i,t),t}finally{Hi(o)}}function Sv(e,t){if(t)for(let n=t.length-1;n>=0;n--){let r=t[n];if(e===r.name)return r}}function Cv(e,t,n){let r=e+27,i=T(),a=co(i,r);return Tv(i,r)?vv(i,Ko(),t,a.transform,n,a):a.transform(n)}function wv(e,t,n,r){let i=e+27,a=T(),o=co(a,i);return Tv(a,i)?yv(a,Ko(),t,o.transform,n,r,o):o.transform(n,r)}function Tv(e,t){return e[1].data[t].pure}function Ev(e,t){return qp(e,t)}var Dv=(()=>{class e{applicationErrorHandler=w(Zs);appRef=w(mg);taskService=w(As);ngZone=w(Rs);zonelessEnabled=w(cc);tracing=w(Pd,{optional:!0});zoneIsDefined=typeof Zone<`u`&&!!Zone.root.run;schedulerTickApplyArgs=[{data:{__scheduler_tick__:!0}}];subscriptions=new gr;angularZoneId=this.zoneIsDefined?this.ngZone._inner?.get(Is):null;scheduleInRootZone=!this.zonelessEnabled&&this.zoneIsDefined&&(w(lc,{optional:!0})??!1);cancelScheduledCallback=null;useMicrotaskScheduler=!1;runningTick=!1;pendingRenderTaskId=null;constructor(){this.subscriptions.add(this.appRef.afterTick.subscribe(()=>{let e=this.taskService.add();if(!this.runningTick&&(this.cleanup(),!this.zonelessEnabled||this.appRef.includeAllTestViews)){this.taskService.remove(e);return}this.switchToMicrotaskScheduler(),this.taskService.remove(e)})),this.subscriptions.add(this.ngZone.onUnstable.subscribe(()=>{this.runningTick||this.cleanup()}))}switchToMicrotaskScheduler(){this.ngZone.runOutsideAngular(()=>{let e=this.taskService.add();this.useMicrotaskScheduler=!0,queueMicrotask(()=>{this.useMicrotaskScheduler=!1,this.taskService.remove(e)})})}notify(e){if(!this.zonelessEnabled&&e===5)return;switch(e){case 0:case 2:this.appRef.dirtyFlags|=2;break;case 3:case 4:case 5:case 1:this.appRef.dirtyFlags|=4;break;case 6:this.appRef.dirtyFlags|=2;break;case 12:this.appRef.dirtyFlags|=16;break;case 13:this.appRef.dirtyFlags|=2;break;case 11:break;default:this.appRef.dirtyFlags|=8}if(this.appRef.tracingSnapshot=this.tracing?.snapshot(this.appRef.tracingSnapshot)??null,!this.shouldScheduleTick())return;let t=this.useMicrotaskScheduler?Ps:Ns;this.pendingRenderTaskId=this.taskService.add(),this.cancelScheduledCallback=this.scheduleInRootZone?Zone.root.run(()=>t(()=>this.tick())):this.ngZone.runOutsideAngular(()=>t(()=>this.tick()))}shouldScheduleTick(){return!(this.appRef.destroyed||this.pendingRenderTaskId!==null||this.runningTick||this.appRef._runningTick||!this.zonelessEnabled&&this.zoneIsDefined&&Zone.current.get(`isAngularZone_ID`+this.angularZoneId))}tick(){if(this.runningTick||this.appRef.destroyed)return;if(this.appRef.dirtyFlags===0){this.cleanup();return}!this.zonelessEnabled&&this.appRef.dirtyFlags&7&&(this.appRef.dirtyFlags|=1);let e=this.taskService.add();try{this.ngZone.run(()=>{this.runningTick=!0,this.appRef._tick()},void 0,this.schedulerTickApplyArgs)}catch(e){this.applicationErrorHandler(e)}finally{this.taskService.remove(e),this.cleanup()}}ngOnDestroy(){this.subscriptions.unsubscribe(),this.cleanup()}cleanup(){if(this.runningTick=!1,this.cancelScheduledCallback?.(),this.cancelScheduledCallback=null,this.pendingRenderTaskId!==null){let e=this.pendingRenderTaskId;this.pendingRenderTaskId=null,this.taskService.remove(e)}}static ɵfac=function(t){return new(t||e)};static ɵprov=Ml({token:e,factory:e.ɵfac})}return e})();function Ov(){return[{provide:sc,useExisting:Dv},{provide:Rs,useClass:Ks},{provide:cc,useValue:!0}]}function kv(){return typeof $localize<`u`&&$localize.locale||`en-US`}var Av=new bi(``,{factory:()=>w(Av,{optional:!0,skipSelf:!0})||kv()}),jv=class{destroyed=!1;listeners=null;errorHandler=w(Xs,{optional:!0});isEmitting=!1;hasNullListeners=!1;destroyRef=w(Es);constructor(){this.destroyRef.onDestroy(()=>{this.destroyed=!0,this.listeners=null})}subscribe(e){if(this.destroyed)throw new ri(953,!1);return(this.listeners??=[]).push(e),{unsubscribe:()=>{let t=this.listeners?this.listeners.indexOf(e):-1;t>-1&&(this.isEmitting?(this.hasNullListeners=!0,this.listeners[t]=null):this.listeners.splice(t,1))}}}emit(e){if(this.destroyed){console.warn(ai(953,!1));return}if(this.listeners===null)return;this.isEmitting=!0;let t=C(null);try{for(let t of this.listeners)try{t!==null&&t(e)}catch(e){this.errorHandler?.handleError(e)}}finally{this.hasNullListeners&&(this.hasNullListeners=!1,this.listeners&&Mv(this.listeners)),C(t),this.isEmitting=!1}}};function Mv(e){let t=e.length-1;for(;t>-1;)e[t]===null&&e.splice(t,1),t--}function Q(e,t){return Nn(e,t?.equal)}function Nv(e){return or(e)}(class e extends Error{_brand;constructor(e){super(e)}static IDLE=new e(`IDLE`);static LOADING=new e(`LOADING`)});var Pv=e=>e;function Fv(e,t){return typeof e==`function`?Iv(nr(e,Pv,t?.equal),t?.debugName,t?.set):Iv(nr(e.source,e.computation,e.equal),e.debugName,e.set)}function Iv(e,t,n){let r=e[fn],i=e;if(n!==void 0){let t=e=>rr(r,e);i.set=e=>n(e,t),i.update=r=>n(r(Nv(e)),t)}else i.set=e=>rr(r,e),i.update=e=>ir(r,e);return i.asReadonly=$s.bind(e),i}function Lv(e,t){let n=Object.create(Cc);n.value=e,n.transformFn=t?.transform;function r(){if(hn(n),n.value===Sc)throw new ri(-950,null);return n.value}return r[fn]=n,r}function Rv(e){return new jv}function zv(e,t){return Lv(e,t)}function Bv(e){return Lv(Sc,e)}var $=(zv.required=Bv,zv);function Vv(e,t){let n=Object.create(Cc),r=new jv;n.value=e;function i(){return hn(n),Hv(n.value),n.value}return i[fn]=n,i.asReadonly=$s.bind(i),i.set=e=>{n.equal(n.value,e)||(Gn(n,e),r.emit(e))},i.update=e=>{Hv(n.value),i.set(e(n.value))},i.subscribe=r.subscribe.bind(r),i.destroyRef=r.destroyRef,i}function Hv(e){if(e===Sc)throw new ri(952,!1)}function Uv(e,t){return Vv(e,t)}function Wv(e){return Vv(Sc,e)}var Gv=(Uv.required=Wv,Uv);function Kv(e,t){return Ih(t)}function qv(e,t){return Lh(t)}var Jv=(Kv.required=qv,Kv),Yv=new bi(``),Xv=new bi(``);function Zv(e){return!e.moduleRef}function Qv(e){let t=Zv(e)?e.r3Injector:e.moduleRef.injector,n=t.get(Rs);return n.run(()=>{Zv(e)?e.r3Injector.resolveInjectorInitializers():e.moduleRef.resolveInjectorInitializers();let r=t.get(Zs),i;if(n.runOutsideAngular(()=>{i=n.onError.subscribe({next:r})}),Zv(e)){let n=()=>t.destroy(),r=e.platformInjector.get(Yv);r.add(n),t.onDestroy(()=>{i.unsubscribe(),r.delete(n)})}else{let t=()=>e.moduleRef.destroy(),n=e.platformInjector.get(Yv);n.add(t),e.moduleRef.onDestroy(()=>{gg(e.allPlatformModules,e.moduleRef),i.unsubscribe(),n.delete(t)})}return ey(r,n,()=>{let n=t.get(As),r=n.add(),i=t.get(rg);return i.runInitializers(),i.donePromise.then(()=>{if(r_(t.get(Av,n_)||`en-US`),!t.get(Xv,!0))return Zv(e)?t.get(mg):(e.allPlatformModules.push(e.moduleRef),e.moduleRef);if(Zv(e)){let n=t.get(mg);return e.rootComponent!==void 0&&n.bootstrap(e.rootComponent),n}return $v?.(e.moduleRef,e.allPlatformModules),e.moduleRef}).finally(()=>void n.remove(r))})})}var $v;function ey(e,t,n){try{let r=n();return Bh(r)?r.catch(n=>{throw t.runOutsideAngular(()=>e(n)),n}):r}catch(n){throw t.runOutsideAngular(()=>e(n)),n}}var ty=null;function ny(e=[],t){return ws.create({name:t,providers:[{provide:Oa,useValue:`platform`},{provide:Yv,useValue:new Set([()=>ty=null])},...e]})}function ry(e=[]){if(ty)return ty;let t=ny(e);return ty=t,fg(),iy(t),t}function iy(e){let t=e.get(nc,null);Ga(e,()=>{t?.forEach(e=>e())})}function ay(e){let{rootComponent:t,appProviders:n,platformProviders:r,platformRef:i}=e;Rc(Tc.BootstrapApplicationStart);try{let e=i?.injector??ry(r);return Qv({r3Injector:new Uh({providers:[Ov(),Qs,...n||[]],parent:e,debugName:``,runEnvironmentInitializers:!1}).injector,platformInjector:e,rootComponent:t})}catch(e){return Promise.reject(e)}finally{Rc(Tc.BootstrapApplicationEnd)}}var oy=Symbol(`NOT_SET`),sy=new Set,cy={...qn,kind:`afterRenderEffectPhase`,consumerIsAlwaysLive:!0,consumerAllowSignalWrites:!0,value:oy,cleanup:null,consumerMarkedDirty(){if(this.sequence.impl.executing){if(this.sequence.lastPhase===null||this.sequence.lastPhase(hn(r),r.value),r.signal[fn]=r,r.registerCleanupFn=e=>(r.cleanup??=new Set).add(e),this.nodes[e]=r,this.hooks[e]=e=>r.phaseFn(e)}}afterRun(){super.afterRun(),this.lastPhase=null}destroy(){if(this.onDestroyFns!==null)for(let e of this.onDestroyFns)e();super.destroy();for(let e of this.nodes)if(e)try{for(let t of e.cleanup??sy)t()}finally{Dn(e)}}};function uy(e,t){let n=t?.injector??w(ws),r=n.get(sc),i=n.get(Ld),a=n.get(Pd,null,{optional:!0});i.impl??=n.get(zd);let o=e;typeof o==`function`&&(o={mixedReadWrite:e});let s=n.get(ac,null,{optional:!0}),c=new ly(i.impl,[o.earlyRead,o.write,o.mixedReadWrite,o.read],s?.view,r,n,a?.snapshot(null));return i.impl.register(c),c}var dy=null;function fy(){return dy}function py(e){dy??=e}var my=class{},hy=(function(e){return e[e.Format=0]=`Format`,e[e.Standalone=1]=`Standalone`,e})(hy||{}),gy=(function(e){return e[e.Narrow=0]=`Narrow`,e[e.Abbreviated=1]=`Abbreviated`,e[e.Wide=2]=`Wide`,e[e.Short=3]=`Short`,e})(gy||{}),_y=(function(e){return e[e.Short=0]=`Short`,e[e.Medium=1]=`Medium`,e[e.Long=2]=`Long`,e[e.Full=3]=`Full`,e})(_y||{}),vy={Decimal:0,Group:1,List:2,PercentSign:3,PlusSign:4,MinusSign:5,Exponential:6,SuperscriptingExponent:7,PerMille:8,Infinity:9,NaN:10,TimeSeparator:11,CurrencyDecimal:12,CurrencyGroup:13};function yy(e){return Qg(e)[e_.LocaleId]}function by(e,t,n){let r=Qg(e);return jy(jy([r[e_.DayPeriodsFormat],r[e_.DayPeriodsStandalone]],t),n)}function xy(e,t,n){let r=Qg(e);return jy(jy([r[e_.DaysFormat],r[e_.DaysStandalone]],t),n)}function Sy(e,t,n){let r=Qg(e);return jy(jy([r[e_.MonthsFormat],r[e_.MonthsStandalone]],t),n)}function Cy(e,t){let n=Qg(e)[e_.Eras];return jy(n,t)}function wy(e,t){return jy(Qg(e)[e_.DateFormat],t)}function Ty(e,t){return jy(Qg(e)[e_.TimeFormat],t)}function Ey(e,t){let n=Qg(e)[e_.DateTimeFormat];return jy(n,t)}function Dy(e,t){let n=Qg(e),r=n[e_.NumberSymbols][t];if(r===void 0){if(t===vy.CurrencyDecimal)return n[e_.NumberSymbols][vy.Decimal];if(t===vy.CurrencyGroup)return n[e_.NumberSymbols][vy.Group]}return r}function Oy(e){if(!e[e_.ExtraData])throw new ri(2303,!1)}function ky(e){let t=Qg(e);return Oy(t),(t[e_.ExtraData][2]||[]).map(e=>typeof e==`string`?My(e):[My(e[0]),My(e[1])])}function Ay(e,t,n){let r=Qg(e);return Oy(r),jy(jy([r[e_.ExtraData][0],r[e_.ExtraData][1]],t)||[],n)||[]}function jy(e,t){for(let n=t;n>-1;n--)if(e[n]!==void 0)return e[n];throw new ri(2304,!1)}function My(e){let[t,n]=e.split(`:`);return{hours:+t,minutes:+n}}var Ny=/^(\d{4,})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/,Py=Object.create(null),Fy=/((?:[^BEGHLMOSWYZabcdhmswyz']+)|(?:'(?:[^']|'')*')|(?:G{1,5}|y{1,4}|Y{1,4}|M{1,5}|L{1,5}|w{1,2}|W{1}|d{1,2}|E{1,6}|c{1,6}|a{1,5}|b{1,5}|B{1,5}|h{1,2}|H{1,2}|m{1,2}|s{1,2}|S{1,3}|z{1,4}|Z{1,5}|O{1,4}))([\s\S]*)/,Iy=256;function Ly(e,t,n,r){let i=ob(e);Ry(t),t=By(n,t)||t;let a=[],o;for(;t;)if(o=Fy.exec(t),o){a=a.concat(o.slice(1));let e=a.pop();if(!e)break;t=e}else{a.push(t);break}let s=i.getTimezoneOffset();r&&(s=rb(r,s),i=ab(i,r));let c=``;return a.forEach(e=>{let t=nb(e);c+=t?t(i,n,s):e===`''`?`'`:e.replace(/(^'|'$)/g,``).replace(/''/g,`'`)}),c}function Ry(e){if(e.length>Iy)throw new ri(2300,!1)}function zy(e,t,n){let r=new Date(0);return r.setFullYear(e,t,n),r.setHours(0,0,0),r}function By(e,t){let n=yy(e);if(Py[n]??=Object.create(null),Py[n][t])return Py[n][t];let r=``;switch(t){case`shortDate`:r=wy(e,_y.Short);break;case`mediumDate`:r=wy(e,_y.Medium);break;case`longDate`:r=wy(e,_y.Long);break;case`fullDate`:r=wy(e,_y.Full);break;case`shortTime`:r=Ty(e,_y.Short);break;case`mediumTime`:r=Ty(e,_y.Medium);break;case`longTime`:r=Ty(e,_y.Long);break;case`fullTime`:r=Ty(e,_y.Full);break;case`short`:let t=By(e,`shortTime`),n=By(e,`shortDate`);r=Vy(Ey(e,_y.Short),[t,n]);break;case`medium`:let i=By(e,`mediumTime`),a=By(e,`mediumDate`);r=Vy(Ey(e,_y.Medium),[i,a]);break;case`long`:let o=By(e,`longTime`),s=By(e,`longDate`);r=Vy(Ey(e,_y.Long),[o,s]);break;case`full`:let c=By(e,`fullTime`),l=By(e,`fullDate`);r=Vy(Ey(e,_y.Full),[c,l])}return r&&(Py[n][t]=r),r}function Vy(e,t){return t&&(e=e.replace(/\{([^}]+)}/g,function(e,n){return Object.hasOwn(t,n)?t[n]:e})),e}function Hy(e,t,n=`-`,r,i){let a=``;(e<0||i&&e<=0)&&(i?e=-e+1:(e=-e,a=n));let o=String(e);for(;o.length0||s>-n)&&(s+=n),e===3)s===0&&n===-12&&(s=12);else if(e===6)return Uy(s,t);let c=Dy(o,vy.MinusSign);return Hy(s,t,c,r,i)}}function Gy(e,t){switch(e){case 0:return t.getFullYear();case 1:return t.getMonth();case 2:return t.getDate();case 3:return t.getHours();case 4:return t.getMinutes();case 5:return t.getSeconds();case 6:return t.getMilliseconds();case 7:return t.getDay();default:throw new ri(2301,!1)}}function Ky(e,t,n=hy.Format,r=!1){return function(i,a){return qy(i,a,e,t,n,r)}}function qy(e,t,n,r,i,a){switch(n){case 2:return Sy(t,i,r)[e.getMonth()];case 1:return xy(t,i,r)[e.getDay()];case 0:let n=e.getHours(),o=e.getMinutes();if(a){let e=ky(t),a=Ay(t,i,r),s=e.findIndex(e=>{if(Array.isArray(e)){let[t,r]=e,i=n>=t.hours&&o>=t.minutes,a=n0?Math.floor(i/60):Math.ceil(i/60);switch(e){case 0:return(i>=0?`+`:``)+Hy(o,2,a)+Hy(Math.abs(i%60),2,a);case 1:return`GMT`+(i>=0?`+`:``)+Hy(o,1,a);case 2:return`GMT`+(i>=0?`+`:``)+Hy(o,2,a)+`:`+Hy(Math.abs(i%60),2,a);case 3:return r===0?`Z`:(i>=0?`+`:``)+Hy(o,2,a)+`:`+Hy(Math.abs(i%60),2,a);default:throw new ri(2310,!1)}}}var Yy=0,Xy=4;function Zy(e){let t=zy(e,Yy,1).getDay();return zy(e,0,1+(t<=Xy?Xy:11)-t)}function Qy(e){let t=e.getDay(),n=t===0?-3:Xy-t;return zy(e.getFullYear(),e.getMonth(),e.getDate()+n)}function $y(e,t=!1){return function(n,r){let i;if(t){let e=new Date(n.getFullYear(),n.getMonth(),1).getDay()-1,t=n.getDate();i=1+Math.floor((t+e)/7)}else{let e=Qy(n),t=Zy(e.getFullYear()),r=e.getTime()-t.getTime();i=1+Math.round(r/6048e5)}return Hy(i,e,Dy(r,vy.MinusSign))}}function eb(e,t=!1){return function(n,r){return Hy(Qy(n).getFullYear(),e,Dy(r,vy.MinusSign),t)}}var tb=Object.create(null);function nb(e){if(tb[e])return tb[e];let t;switch(e){case`G`:case`GG`:case`GGG`:t=Ky(3,gy.Abbreviated);break;case`GGGG`:t=Ky(3,gy.Wide);break;case`GGGGG`:t=Ky(3,gy.Narrow);break;case`y`:t=Wy(0,1,0,!1,!0);break;case`yy`:t=Wy(0,2,0,!0,!0);break;case`yyy`:t=Wy(0,3,0,!1,!0);break;case`yyyy`:t=Wy(0,4,0,!1,!0);break;case`Y`:t=eb(1);break;case`YY`:t=eb(2,!0);break;case`YYY`:t=eb(3);break;case`YYYY`:t=eb(4);break;case`M`:case`L`:t=Wy(1,1,1);break;case`MM`:case`LL`:t=Wy(1,2,1);break;case`MMM`:t=Ky(2,gy.Abbreviated);break;case`MMMM`:t=Ky(2,gy.Wide);break;case`MMMMM`:t=Ky(2,gy.Narrow);break;case`LLL`:t=Ky(2,gy.Abbreviated,hy.Standalone);break;case`LLLL`:t=Ky(2,gy.Wide,hy.Standalone);break;case`LLLLL`:t=Ky(2,gy.Narrow,hy.Standalone);break;case`w`:t=$y(1);break;case`ww`:t=$y(2);break;case`W`:t=$y(1,!0);break;case`d`:t=Wy(2,1);break;case`dd`:t=Wy(2,2);break;case`c`:case`cc`:t=Wy(7,1);break;case`ccc`:t=Ky(1,gy.Abbreviated,hy.Standalone);break;case`cccc`:t=Ky(1,gy.Wide,hy.Standalone);break;case`ccccc`:t=Ky(1,gy.Narrow,hy.Standalone);break;case`cccccc`:t=Ky(1,gy.Short,hy.Standalone);break;case`E`:case`EE`:case`EEE`:t=Ky(1,gy.Abbreviated);break;case`EEEE`:t=Ky(1,gy.Wide);break;case`EEEEE`:t=Ky(1,gy.Narrow);break;case`EEEEEE`:t=Ky(1,gy.Short);break;case`a`:case`aa`:case`aaa`:t=Ky(0,gy.Abbreviated);break;case`aaaa`:t=Ky(0,gy.Wide);break;case`aaaaa`:t=Ky(0,gy.Narrow);break;case`b`:case`bb`:case`bbb`:t=Ky(0,gy.Abbreviated,hy.Standalone,!0);break;case`bbbb`:t=Ky(0,gy.Wide,hy.Standalone,!0);break;case`bbbbb`:t=Ky(0,gy.Narrow,hy.Standalone,!0);break;case`B`:case`BB`:case`BBB`:t=Ky(0,gy.Abbreviated,hy.Format,!0);break;case`BBBB`:t=Ky(0,gy.Wide,hy.Format,!0);break;case`BBBBB`:t=Ky(0,gy.Narrow,hy.Format,!0);break;case`h`:t=Wy(3,1,-12);break;case`hh`:t=Wy(3,2,-12);break;case`H`:t=Wy(3,1);break;case`HH`:t=Wy(3,2);break;case`m`:t=Wy(4,1);break;case`mm`:t=Wy(4,2);break;case`s`:t=Wy(5,1);break;case`ss`:t=Wy(5,2);break;case`S`:t=Wy(6,1);break;case`SS`:t=Wy(6,2);break;case`SSS`:t=Wy(6,3);break;case`Z`:case`ZZ`:case`ZZZ`:t=Jy(0);break;case`ZZZZZ`:t=Jy(3);break;case`O`:case`OO`:case`OOO`:case`z`:case`zz`:case`zzz`:t=Jy(1);break;case`OOOO`:case`ZZZZ`:case`zzzz`:t=Jy(2);break;default:return null}return tb[e]=t,t}function rb(e,t){e=e.replace(/:/g,``);let n=Date.parse(`Jan 01, 1970 00:00:00 `+e)/6e4;return isNaN(n)?t:n}function ib(e,t){return e=new Date(e.getTime()),e.setMinutes(e.getMinutes()+t),e}function ab(e,t,n){let r=e.getTimezoneOffset();return ib(e,-1*(rb(t,r)-r))}function ob(e){if(cb(e))return e;if(typeof e==`number`&&!isNaN(e))return new Date(e);if(typeof e==`string`){if(e=e.trim(),/^(\d{4}(-\d{1,2}(-\d{1,2})?)?)$/.test(e)){let[t,n=1,r=1]=e.split(`-`).map(e=>+e);return zy(t,n-1,r)}let t=parseFloat(e);if(!isNaN(e-t))return new Date(t);let n;if(n=e.match(Ny))return sb(n)}let t=new Date(e);if(!cb(t))throw new ri(2311,!1);return t}function sb(e){let t=new Date(0),n=0,r=0,i=e[8]?t.setUTCFullYear:t.setFullYear,a=e[8]?t.setUTCHours:t.setHours;e[9]&&(n=Number(e[9]+e[10]),r=Number(e[9]+e[11])),i.call(t,Number(e[1]),Number(e[2])-1,Number(e[3]));let o=Number(e[4]||0)-n,s=Number(e[5]||0)-r,c=Number(e[6]||0),l=Math.floor(parseFloat(`0.`+(e[7]||0))*1e3);return a.call(t,o,s,c,l),t}function cb(e){return e instanceof Date&&!isNaN(e.valueOf())}var lb=(()=>{class e{_viewContainerRef;_viewRef=null;ngTemplateOutletContext=null;ngTemplateOutlet=null;ngTemplateOutletInjector=null;injector=w(ws);constructor(e){this._viewContainerRef=e}ngOnChanges(e){if(this._shouldRecreateView(e)){let e=this._viewContainerRef;if(this._viewRef&&e.remove(e.indexOf(this._viewRef)),!this.ngTemplateOutlet){this._viewRef=null;return}let t=this._createContextForwardProxy();this._viewRef=e.createEmbeddedView(this.ngTemplateOutlet,t,{injector:this._getInjector()})}}_getInjector(){return this.ngTemplateOutletInjector===`outlet`?this.injector:this.ngTemplateOutletInjector??void 0}_shouldRecreateView(e){return!!e.ngTemplateOutlet||!!e.ngTemplateOutletInjector}_createContextForwardProxy(){return new Proxy({},{set:(e,t,n)=>this.ngTemplateOutletContext?Reflect.set(this.ngTemplateOutletContext,t,n):!1,get:(e,t,n)=>{if(this.ngTemplateOutletContext)return Reflect.get(this.ngTemplateOutletContext,t,n)}})}static ɵfac=function(t){return new(t||e)(Em(oh))};static ɵdir=Xh({type:e,selectors:[[``,`ngTemplateOutlet`,``]],inputs:{ngTemplateOutletContext:`ngTemplateOutletContext`,ngTemplateOutlet:`ngTemplateOutlet`,ngTemplateOutletInjector:`ngTemplateOutletInjector`},features:[kc]})}return e})();function ub(e,t){return new ri(2100,!1)}var db=`mediumDate`,fb=new bi(``),pb=new bi(``),mb=(()=>{class e{locale;defaultTimezone;defaultOptions;constructor(e,t,n){this.locale=e,this.defaultTimezone=t,this.defaultOptions=n}transform(t,n,r,i){if(t==null||t===``||t!==t)return null;try{let e=n??this.defaultOptions?.dateFormat??db,a=r??this.defaultOptions?.timezone??this.defaultTimezone??void 0;return Ly(t,e,i||this.locale,a)}catch(t){throw ub(e,t.message)}}static ɵfac=function(t){return new(t||e)(Em(Av,16),Em(fb,24),Em(pb,24))};static ɵpipe=Zh({name:`date`,type:e,pure:!0})}return e})(),hb=(()=>{class e{transform(e){return JSON.stringify(e,null,2)}static ɵfac=function(t){return new(t||e)};static ɵpipe=Zh({name:`json`,type:e,pure:!1})}return e})();function gb(e,t){t=encodeURIComponent(t);for(let n of e.split(`;`)){let e=n.indexOf(`=`),[r,i]=e==-1?[n,``]:[n.slice(0,e),n.slice(e+1)];if(r.trim()!==t)continue;let a=i;try{a=decodeURIComponent(i)}catch{}return a.length>1&&a[0]===`"`&&a[a.length-1]===`"`&&(a=a.slice(1,-1)),a}return null}var _b=`browser`,vb=class{_doc;constructor(e){this._doc=e}manager},yb=(()=>{class e extends vb{constructor(e){super(e)}supports(e){return!0}addEventListener(e,t,n,r){return e.addEventListener(t,n,r),()=>this.removeEventListener(e,t,n,r)}removeEventListener(e,t,n,r){return e.removeEventListener(t,n,r)}static ɵfac=function(t){return new(t||e)(Yi(Ts))};static ɵprov=pi({token:e,factory:e.ɵfac})}return e})(),bb=new bi(``),xb=(()=>{class e{_zone;_plugins;_eventNameToPlugin=new Map;constructor(e,t){this._zone=t,e.forEach(e=>{e.manager=this});let n=e.filter(e=>!(e instanceof yb));this._plugins=n.slice().reverse();let r=e.find(e=>e instanceof yb);r&&this._plugins.push(r)}addEventListener(e,t,n,r){return this._findPluginFor(t).addEventListener(e,t,n,r)}getZone(){return this._zone}_findPluginFor(e){let t=this._eventNameToPlugin.get(e);if(t)return t;if(t=this._plugins.find(t=>t.supports(e)),!t)throw new ri(-5101,!1);return this._eventNameToPlugin.set(e,t),t}static ɵfac=function(t){return new(t||e)(Yi(bb),Yi(Rs))};static ɵprov=pi({token:e,factory:e.ɵfac})}return e})(),Sb=`ng-app-id`;function Cb(e){for(let t of e)t.remove()}function wb(e,t){let n=t.createElement(`style`);return n.textContent=e,n}function Tb(e,t,n,r){let i=e.head?.querySelectorAll(`style[${Sb}="${t}"],link[${Sb}="${t}"]`);if(!i||i.length===0)return!1;for(let e of i)e.removeAttribute(Sb),e instanceof HTMLLinkElement?r.set(e.href.slice(e.href.lastIndexOf(`/`)+1),{usage:0,elements:[e]}):e.textContent&&n.set(e.textContent,{usage:0,elements:[e]});return!0}function Eb(e,t){let n=t.createElement(`link`);return n.setAttribute(`rel`,`stylesheet`),n.setAttribute(`href`,e),n}var Db=(()=>{class e{doc;appId;nonce;inline=new Map;external=new Map;hosts=new Set;constructor(e,t,n,r={}){this.doc=e,this.appId=t,this.nonce=n,Tb(e,t,this.inline,this.external)&&this.hosts.add(e.head)}addStyles(e,t){for(let t of e)this.addUsage(t,this.inline,wb);t?.forEach(e=>this.addUsage(e,this.external,Eb))}removeStyles(e,t){for(let t of e)this.removeUsage(t,this.inline);t?.forEach(e=>this.removeUsage(e,this.external))}addUsage(e,t,n){let r=t.get(e);r?r.usage++:t.set(e,{usage:1,elements:[...this.hosts].map(t=>this.addElement(t,n(e,this.doc)))})}removeUsage(e,t){let n=t.get(e);n&&(n.usage--,n.usage<=0&&(Cb(n.elements),t.delete(e)))}ngOnDestroy(){for(let[,{elements:e}]of[...this.inline,...this.external])Cb(e);this.hosts.clear()}addHost(e){if(!this.hosts.has(e)){this.hosts.add(e);for(let[t,{elements:n}]of this.inline)n.push(this.addElement(e,wb(t,this.doc)));for(let[t,{elements:n}]of this.external)n.push(this.addElement(e,Eb(t,this.doc)))}}removeHost(e){this.hosts.delete(e);for(let t of[...this.inline.values(),...this.external.values()]){let n=[];for(let r of t.elements)r.parentNode===e?r.remove():n.push(r);t.elements=n}}addElement(e,t){return this.nonce&&t.setAttribute(`nonce`,this.nonce),e.appendChild(t)}static ɵfac=function(t){return new(t||e)(Yi(Ts),Yi(ec),Yi(ic,8),Yi(rc))};static ɵprov=pi({token:e,factory:e.ɵfac})}return e})(),Ob={svg:`http://www.w3.org/2000/svg`,xhtml:`http://www.w3.org/1999/xhtml`,xlink:`http://www.w3.org/1999/xlink`,xml:`http://www.w3.org/XML/1998/namespace`,xmlns:`http://www.w3.org/2000/xmlns/`,math:`http://www.w3.org/1998/Math/MathML`},kb=/%COMP%/g,Ab=`%COMP%`,jb=`_nghost-${Ab}`,Mb=`_ngcontent-${Ab}`,Nb=!0,Pb=new bi(``,{factory:()=>Nb}),Fb=new bi(``);function Ib(e){return Mb.replace(kb,e)}function Lb(e){return jb.replace(kb,e)}function Rb(e,t){return t.map(t=>t.replace(kb,e))}var zb=(()=>{class e{eventManager;sharedStylesHost;appId;removeStylesOnCompDestroy;doc;ngZone;nonce;tracingService;rendererByCompId=new Map;defaultRenderer;cssVarNamespace;constructor(e,t,n,r,i,a,o=null,s=null,c=null){this.eventManager=e,this.sharedStylesHost=t,this.appId=n,this.removeStylesOnCompDestroy=r,this.doc=i,this.ngZone=a,this.nonce=o,this.tracingService=s,this.cssVarNamespace=c??``,this.defaultRenderer=new Bb(e,i,a,this.tracingService,this.cssVarNamespace)}createRenderer(e,t){if(!e||!t)return this.defaultRenderer;let n=this.getOrCreateRenderer(e,t);return n instanceof Wb?n.applyToHost(e):n instanceof Ub&&n.applyStyles(),n}getOrCreateRenderer(e,t){let n=this.rendererByCompId,r=n.get(t.id);if(!r){let i=this.doc,a=this.ngZone,o=this.eventManager,s=this.sharedStylesHost,c=this.removeStylesOnCompDestroy,l=this.tracingService;switch(t.encapsulation){case uu.Emulated:r=new Wb(o,s,t,this.appId,c,i,a,l,this.cssVarNamespace);break;case uu.ShadowDom:return new Hb(o,e,t,i,a,this.nonce,l,this.cssVarNamespace,s);case uu.ExperimentalIsolatedShadowDom:return new Hb(o,e,t,i,a,this.nonce,l,this.cssVarNamespace);default:r=new Ub(o,s,t,c,i,a,l,this.cssVarNamespace)}n.set(t.id,r)}return r}ngOnDestroy(){this.rendererByCompId.clear()}componentReplaced(e){this.rendererByCompId.delete(e)}static ɵfac=function(t){return new(t||e)(Yi(xb),Yi(wm),Yi(ec),Yi(Pb),Yi(Ts),Yi(Rs),Yi(ic),Yi(Pd,8),Yi(Fb,8))};static ɵprov=pi({token:e,factory:e.ɵfac})}return e})(),Bb=class{eventManager;doc;ngZone;tracingService;cssVarNamespace;data=Object.create(null);throwOnSyntheticProps=!0;constructor(e,t,n,r,i=``){this.eventManager=e,this.doc=t,this.ngZone=n,this.tracingService=r,this.cssVarNamespace=i}destroy(){}destroyNode=null;createElement(e,t){return t?this.doc.createElementNS(Ob[t]||t,e):this.doc.createElement(e)}createComment(e){return this.doc.createComment(e)}createText(e){return this.doc.createTextNode(e)}appendChild(e,t){(Vb(e)?e.content:e).appendChild(t)}insertBefore(e,t,n){if(e){let r=Vb(e)?e.content:e;if(n!=null&&n.parentNode!==r)throw new ri(-5106,!1);r.insertBefore(t,n)}}removeChild(e,t){t.remove()}selectRootElement(e,t){let n=typeof e==`string`?this.doc.querySelector(e):e;if(!n)throw new ri(-5104,!1);return t||(n.textContent=``),n}parentNode(e){return e.parentNode}nextSibling(e){return e.nextSibling}setAttribute(e,t,n,r){if(r){t=r+`:`+t;let i=Ob[r];i?e.setAttributeNS(i,t,n):e.setAttribute(t,n)}else e.setAttribute(t,n)}removeAttribute(e,t,n){if(n){let r=Ob[n];r?e.removeAttributeNS(r,t):e.removeAttribute(`${n}:${t}`)}else e.removeAttribute(t)}addClass(e,t){e.classList.add(t)}removeClass(e,t){e.classList.remove(t)}setStyle(e,t,n,r){let i=t.startsWith(`--`);i&&(t=t.replace(`%NS%`,this.cssVarNamespace)),i||r&(ed.DashCase|ed.Important)?e.style.setProperty(t,n,r&ed.Important?`important`:``):e.style[t]=n}removeStyle(e,t,n){let r=t.startsWith(`--`);r&&(t=t.replace(`%NS%`,this.cssVarNamespace)),r||n&ed.DashCase?e.style.removeProperty(t):e.style[t]=``}setProperty(e,t,n){e!=null&&(e[t]=n)}setValue(e,t){e.nodeValue=t}listen(e,t,n,r){if(typeof e==`string`&&(e=fy().getGlobalEventTarget(this.doc,e),!e))throw new ri(-5102,!1);let i=this.decoratePreventDefault(n);return this.tracingService?.wrapEventListener&&(i=this.tracingService.wrapEventListener(e,t,i)),this.eventManager.addEventListener(e,t,i,r)}decoratePreventDefault(e){return t=>{if(t===`__ngUnwrap__`)return e;e(t)===!1&&t.preventDefault()}}};function Vb(e){return e.tagName===`TEMPLATE`&&e.content!==void 0}var Hb=class extends Bb{hostEl;sharedStylesHost;shadowRoot;constructor(e,t,n,r,i,a,o,s,c){super(e,r,i,o,s),this.hostEl=t,this.sharedStylesHost=c,this.shadowRoot=t.attachShadow({mode:`open`}),this.sharedStylesHost&&this.sharedStylesHost.addHost(this.shadowRoot);let l=n.styles;l=Rb(n.id,l).map(e=>e.replace(/%NS%/g,s));for(let e of l){let t=document.createElement(`style`);a&&t.setAttribute(`nonce`,a),t.textContent=e,this.shadowRoot.appendChild(t)}let u=n.getExternalStyles?.();if(u)for(let e of u){let t=Eb(e,r);a&&t.setAttribute(`nonce`,a),this.shadowRoot.appendChild(t)}}nodeOrShadowRoot(e){return e===this.hostEl?this.shadowRoot:e}appendChild(e,t){return super.appendChild(this.nodeOrShadowRoot(e),t)}insertBefore(e,t,n){return super.insertBefore(this.nodeOrShadowRoot(e),t,n)}removeChild(e,t){return super.removeChild(null,t)}parentNode(e){return this.nodeOrShadowRoot(super.parentNode(this.nodeOrShadowRoot(e)))}destroy(){this.sharedStylesHost&&this.sharedStylesHost.removeHost(this.shadowRoot)}},Ub=class extends Bb{sharedStylesHost;removeStylesOnCompDestroy;styles;styleUrls;constructor(e,t,n,r,i,a,o,s,c){super(e,i,a,o,s),this.sharedStylesHost=t,this.removeStylesOnCompDestroy=r;let l=n.styles,u=c?Rb(c,l):l;this.styles=u.map(e=>e.replace(/%NS%/g,s)),this.styleUrls=n.getExternalStyles?.(c)}applyStyles(){this.sharedStylesHost.addStyles(this.styles,this.styleUrls)}destroy(){this.removeStylesOnCompDestroy&&md.size===0&&this.sharedStylesHost.removeStyles(this.styles,this.styleUrls)}},Wb=class extends Ub{contentAttr;hostAttr;constructor(e,t,n,r,i,a,o,s,c){let l=r+`-`+n.id;super(e,t,n,i,a,o,s,c,l),this.contentAttr=Ib(l),this.hostAttr=Lb(l)}applyToHost(e){this.applyStyles(),this.setAttribute(e,this.hostAttr,``)}createElement(e,t){let n=super.createElement(e,t);return super.setAttribute(n,this.contentAttr,``),n}},Gb=class e extends my{supportsDOMEvents=!0;static makeCurrent(){py(new e)}onAndCancel(e,t,n,r){return e.addEventListener(t,n,r),()=>{e.removeEventListener(t,n,r)}}dispatchEvent(e,t){e.dispatchEvent(t)}remove(e){e.remove()}createElement(e,t){return t||=this.getDefaultDocument(),t.createElement(e)}createHtmlDocument(){return document.implementation.createHTMLDocument(`fakeTitle`)}getDefaultDocument(){return document}isElementNode(e){return e.nodeType===Node.ELEMENT_NODE}isShadowRoot(e){return e instanceof DocumentFragment}getGlobalEventTarget(e,t){return t===`window`?window:t===`document`?e:t===`body`?e.body:null}getBaseHref(e){let t=qb();return t==null?null:Jb(t)}resetBaseElement(){Kb=null}getUserAgent(){return window.navigator.userAgent}getCookie(e){return gb(document.cookie,e)}},Kb=null;function qb(){return Kb||=document.head.querySelector(`base`),Kb?Kb.getAttribute(`href`):null}function Jb(e){return new URL(e,document.baseURI).pathname}var Yb=[`alt`,`control`,`meta`,`shift`],Xb={"\b":`Backspace`," ":`Tab`,"":`Delete`,"\x1B":`Escape`,Del:`Delete`,Esc:`Escape`,Left:`ArrowLeft`,Right:`ArrowRight`,Up:`ArrowUp`,Down:`ArrowDown`,Menu:`ContextMenu`,Scroll:`ScrollLock`,Win:`OS`},Zb={alt:e=>e.altKey,control:e=>e.ctrlKey,meta:e=>e.metaKey,shift:e=>e.shiftKey},Qb=(()=>{class e extends vb{constructor(e){super(e)}supports(t){return e.parseEventName(t)!=null}addEventListener(t,n,r,i){let a=e.parseEventName(n),o=e.eventCallback(a.fullKey,r,this.manager.getZone());return this.manager.getZone().runOutsideAngular(()=>fy().onAndCancel(t,a.domEventName,o,i))}static parseEventName(t){let n=t.toLowerCase().split(`.`),r=n.shift();if(n.length===0||r!==`keydown`&&r!==`keyup`)return null;let i=e._normalizeKey(n.pop()),a=``,o=n.indexOf(`code`);if(o>-1&&(n.splice(o,1),a=`code.`),Yb.forEach(e=>{let t=n.indexOf(e);t>-1&&(n.splice(t,1),a+=e+`.`)}),a+=i,n.length!=0||i.length===0)return null;let s={};return s.domEventName=r,s.fullKey=a,s}static matchEventFullKeyCode(e,t){let n=Xb[e.key]||e.key,r=``;return t.indexOf(`code.`)>-1&&(n=e.code,r=`code.`),n==null||!n?!1:(n=n.toLowerCase(),n===` `?n=`space`:n===`.`&&(n=`dot`),Yb.forEach(t=>{if(t!==n){let n=Zb[t];n(e)&&(r+=t+`.`)}}),r+=n,r===t)}static eventCallback(t,n,r){return i=>{e.matchEventFullKeyCode(i,t)&&r.runGuarded(()=>n(i))}}static _normalizeKey(e){return e===`esc`?`escape`:e}static ɵfac=function(t){return new(t||e)(Yi(Ts))};static ɵprov=pi({token:e,factory:e.ɵfac})}return e})();async function $b(e,t,n){return ay({rootComponent:e,...ex(t,n)})}function ex(e,t){return{platformRef:t?.platformRef,appProviders:[...ax,...e?.providers??[]],platformProviders:ix}}function tx(){Gb.makeCurrent()}function nx(){return new Xs}function rx(){return Ql(document),document}var ix=[{provide:rc,useValue:_b},{provide:nc,useValue:tx,multi:!0},{provide:Ts,useFactory:rx}],ax=[{provide:Oa,useValue:`root`},{provide:Xs,useFactory:nx},{provide:bb,useClass:yb,multi:!0},{provide:bb,useClass:Qb,multi:!0},zb,{provide:wm,useClass:Db},{provide:Db,useExisting:wm},xb,{provide:am,useExisting:zb},[]];function ox(e,t){let n=`\x1B[${e}m`,r=`\x1B[${t}m`;return((e,...t)=>{if(Array.isArray(e)&&`raw`in e){let i=e,a=``;for(let e=0;e{let r=new ux({code:i,why:cx(a.why,e),fix:cx(a.fix,e),docs:o,cause:e.cause,sources:e.sources,data:cx(a.data,e)},s);for(let e of t)e(r,n);return r};n[i]=s}return n}function px(e){return t=>{let n=`${e.bold(e.red(`[${t.name}]`))} ${t.message}`,r=[];return t.fix&&r.push(`${e.dim(`fix:`)} ${t.fix}`),t.sources?.length&&r.push(`${e.dim(`sources:`)} ${t.sources.join(`, `)}`),t.docs&&r.push(`${e.dim(`see:`)} ${e.cyan(t.docs)}`),r.length===0?n:[n,...r.map((t,n)=>`${e.dim(n{e=n,t=r}),resolve:e,reject:t}}var yx=Math.random.bind(Math),bx=`useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict`;function xx(e=21){let t=``,n=e;for(;n--;)t+=bx[yx()*64|0];return t}var Sx=6e4,Cx=e=>e,wx=Cx,{clearTimeout:Tx,setTimeout:Ex}=globalThis;function Dx(e,t){let{post:n,on:r,off:i=()=>{},eventNames:a=[],serialize:o=Cx,deserialize:s=wx,resolver:c,bind:l=`rpc`,timeout:u=Sx,proxify:d=!0}=t,f=!1,p=new Map,m,h;async function g(e,r,i,a){if(f)throw Error(`[birpc] rpc is closed, cannot call "${e}"`);let s={m:e,a:r,t:`q`};a&&(s.o=!0);let c=async e=>n(o(e));if(i){await c(s);return}if(m)try{await m}finally{m=void 0}let{promise:l,resolve:d,reject:g}=vx(),_=xx();s.i=_;let ee;async function te(n=s){return u>=0&&(ee=Ex(()=>{try{if(t.onTimeoutError?.call(h,e,r)!==!0)throw Error(`[birpc] timeout on calling "${e}"`)}catch(e){g(e)}p.delete(_)},u),typeof ee==`object`&&(ee=ee.unref?.())),p.set(_,{resolve:d,reject:g,timeoutId:ee,method:e}),await c(n),l}try{t.onRequest?await t.onRequest.call(h,s,te,d):await te()}catch(e){if(t.onGeneralError?.call(h,e)!==!0)throw e;return}finally{Tx(ee),p.delete(_)}return l}let _={$call:(e,...t)=>g(e,t,!1),$callOptional:(e,...t)=>g(e,t,!1,!0),$callEvent:(e,...t)=>g(e,t,!0),$callRaw:e=>g(e.method,e.args,e.event,e.optional),$rejectPendingCalls:te,get $closed(){return f},get $meta(){return t.meta},$close:ee,$functions:e};h=d?new Proxy({},{get(t,n){if(Object.hasOwn(_,n))return _[n];if(n===`then`&&!a.includes(`then`)&&!(`then`in e))return;let r=(...e)=>g(n,e,!0);if(a.includes(n))return r.asEvent=r,r;let i=(...e)=>g(n,e,!1);return i.asEvent=r,i}}):_;function ee(e){f=!0,p.forEach(({reject:t,method:n})=>{let r=Error(`[birpc] rpc is closed, cannot call "${n}"`);if(e)return e.cause??=r,t(e);t(r)}),p.clear(),i(ne)}function te(e){let t=Array.from(p.values()).map(({method:t,reject:n})=>e?e({method:t,reject:n}):n(Error(`[birpc]: rejected pending call "${t}".`)));return p.clear(),t}async function ne(r,...i){let a;try{a=s(r)}catch(e){if(t.onGeneralError?.call(h,e)!==!0)throw e;return}if(a.t===`q`){let{m:r,a:s,o:u}=a,d,f,p=await(c?c.call(h,r,e[r]):e[r]);if(u&&(p||=()=>void 0),!p)f=Error(`[birpc] function "${r}" not found`);else try{d=await p.apply(l===`rpc`?h:e,s)}catch(e){f=e}if(a.i){if(f&&t.onFunctionError&&t.onFunctionError.call(h,f,r,s)===!0)return;if(!f)try{await n(o({t:`s`,i:a.i,r:d}),...i);return}catch(e){if(f=e,t.onGeneralError?.call(h,e,r,s)!==!0)throw e}try{await n(o({t:`s`,i:a.i,e:f}),...i)}catch(e){if(t.onGeneralError?.call(h,e,r,s)!==!0)throw e}}}else{let{i:e,r:t,e:n}=a,r=p.get(e);r&&(Tx(r.timeoutId),n?r.reject(n):r.resolve(t)),p.delete(e)}}return m=r(ne),h}function Ox(e,t){return t.safety?t.safety:e===`static`||e===`query`||e==null?`read`:`action`}var kx=Object.freeze({type:`object`,additionalProperties:!0});function Ax(e){let t=e[`~standard`];if(t.jsonSchema)try{return t.jsonSchema.input({target:`draft-2020-12`})}catch{return kx}return kx}function jx(e){if(!e||e.length===0)return{type:`object`,properties:{}};let t={},n=[];for(let r=0;rn[`arg${t}`]);if(`arg0`in n){let e=[];for(;`arg${e.length}`in n;)e.push(n[`arg${e.length}`]);return e}return Object.keys(n).length===0?[]:void 0}function Nx(e,t){return Mx(e,t)??[e]}function Px(e){return typeof e==`string`?`'${e}'`:new Rx().serialize(e)}var Fx=` _-,;:!?.'"()[]{}@*/\\&#%\`^+<=>|~$0123456789abcdefghijklmnopqrstuvwxyz`,Ix=(function(){let e=new Uint8Array(128);for(let t=0;t<69;t++)e[Fx.charCodeAt(t)]=t+1;for(let t=65;t<=90;t++)e[t]=e[t+32];return e})();function Lx(e,t){if(e===t)return 0;let n=Math.min(e.length,t.length),r=0;for(let i=0;ia?-1:1)}return e.length===t.length?r:e.lengththis.compare(e[0],t[0])),r=`${e}{`;for(let e=0;ethis.compare(e,t)))}`}$Map(e){return this.serializeObjectEntries(`Map`,e.entries())}}for(let t of[`Error`,`RegExp`,`URL`])e.prototype[`$`+t]=function(e){return`${t}(${e})`};for(let t of[`Int8Array`,`Uint8Array`,`Uint8ClampedArray`,`Int16Array`,`Uint16Array`,`Int32Array`,`Uint32Array`,`Float32Array`,`Float64Array`])e.prototype[`$`+t]=function(e){return`${t}[${e.join(`,`)}]`};for(let t of[`BigInt64Array`,`BigUint64Array`])e.prototype[`$`+t]=function(e){return`${t}[${e.join(`n,`)}${e.length>0?`n`:``}]`};return e})(),zx=[1779033703,-1150833019,1013904242,-1521486534,1359893119,-1694144372,528734635,1541459225],Bx=[1116352408,1899447441,-1245643825,-373957723,961987163,1508970993,-1841331548,-1424204075,-670586216,310598401,607225278,1426881987,1925078388,-2132889090,-1680079193,-1046744716,-459576895,-272742522,264347078,604807628,770255983,1249150122,1555081692,1996064986,-1740746414,-1473132947,-1341970488,-1084653625,-958395405,-710438585,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,-2117940946,-1838011259,-1564481375,-1474664885,-1035236496,-949202525,-778901479,-694614492,-200395387,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,-2067236844,-1933114872,-1866530822,-1538233109,-1090935817,-965641998],Vx=`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_`,Hx=[],Ux=class{_data=new Wx;_hash=new Wx([...zx]);_nDataBytes=0;_minBufferSize=0;finalize(e){e&&this._append(e);let t=this._nDataBytes*8,n=this._data.sigBytes*8;return this._data.words[n>>>5]|=128<<24-n%32,this._data.words[(n+64>>>9<<4)+14]=Math.floor(t/4294967296),this._data.words[(n+64>>>9<<4)+15]=t,this._data.sigBytes=this._data.words.length*4,this._process(),this._hash}_doProcessBlock(e,t){let n=this._hash.words,r=n[0],i=n[1],a=n[2],o=n[3],s=n[4],c=n[5],l=n[6],u=n[7];for(let n=0;n<64;n++){if(n<16)Hx[n]=e[t+n]|0;else{let e=Hx[n-15],t=(e<<25|e>>>7)^(e<<14|e>>>18)^e>>>3,r=Hx[n-2],i=(r<<15|r>>>17)^(r<<13|r>>>19)^r>>>10;Hx[n]=t+Hx[n-7]+i+Hx[n-16]}let d=s&c^~s&l,f=r&i^r&a^i&a,p=(r<<30|r>>>2)^(r<<19|r>>>13)^(r<<10|r>>>22),m=(s<<26|s>>>6)^(s<<21|s>>>11)^(s<<7|s>>>25),h=u+m+d+Bx[n]+Hx[n],g=p+f;u=l,l=c,c=s,s=o+h|0,o=a,a=i,i=r,r=h+g|0}n[0]=n[0]+r|0,n[1]=n[1]+i|0,n[2]=n[2]+a|0,n[3]=n[3]+o|0,n[4]=n[4]+s|0,n[5]=n[5]+c|0,n[6]=n[6]+l|0,n[7]=n[7]+u|0}_append(e){typeof e==`string`&&(e=Wx.fromUtf8(e)),this._data.concat(e),this._nDataBytes+=e.sigBytes}_process(e){let t,n=this._data.sigBytes/64;n=e?Math.ceil(n):Math.max((n|0)-this._minBufferSize,0);let r=n*16,i=Math.min(r*4,this._data.sigBytes);if(r){for(let e=0;e>>2]|=(n.charCodeAt(e)&255)<<24-e%4*8;return new e(i,r)}toBase64(){let e=[];for(let t=0;t>>2]>>>24-t%4*8&255,r=this.words[t+1>>>2]>>>24-(t+1)%4*8&255,i=this.words[t+2>>>2]>>>24-(t+2)%4*8&255,a=n<<16|r<<8|i;for(let n=0;n<4&&t*8+n*6>>6*(3-n)&63))}return e.join(``)}concat(e){if(this.words[this.sigBytes>>>2]&=4294967295<<32-this.sigBytes%4*8,this.words.length=Math.ceil(this.sigBytes/4),this.sigBytes%4)for(let t=0;t>>2]>>>24-t%4*8&255;this.words[this.sigBytes+t>>>2]|=n<<24-(this.sigBytes+t)%4*8}else for(let t=0;t>>2]=e.words[t>>>2];this.sigBytes+=e.sigBytes}};function Gx(e){return new Ux().finalize(e).toBase64()}function Kx(e){return Gx(Px(e))}function qx(e){return Kx(e)}function Jx(){let e={};function t(t,...n){let r=e[t]||[];for(let e=0,t=r.length;e{e[t]=e[t]?.filter(e=>n!==e)}}function i(e,t){let n=r(e,((...e)=>(n(),t(...e))));return n}return{_listeners:e,emit:t,emitOnce:n,on:r,once:i}}var Yx=/^[\w+.-]{2,}:\/\//;function Xx(e){return e.endsWith(`/`)?e:`${e}/`}function Zx(e){return(e.endsWith(`/`)?e.slice(0,-1):e)||`/`}function Qx(e,...t){let n=e;for(let e of t)e&&e!==`/`&&(n=n?Xx(n)+e.replace(/^\.?\//,``):e);return n}function $x(e,t){if(!t||t===`/`||Yx.test(e))return e;let n=Zx(t);return e.startsWith(n)?e:Qx(n,e)}function eS(e,t){let n=e.match(Yx);return t+(n?e.slice(n[0].length):e)}var tS=`useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict`;function nS(e=21){let t=``,n=e;for(;n--;)t+=tS[Math.random()*64|0];return t}var rS=Symbol.for(`immer-nothing`),iS=Symbol.for(`immer-draftable`),aS=Symbol.for(`immer-state`),oS=[function(e){return`The plugin for '${e}' has not been loaded into Immer. To enable the plugin, import and call \`enable${e}()\` when initializing your application.`},function(e){return`produce can only be called on things that are draftable: plain objects, arrays, Map, Set or classes that are marked with '[immerable]: true'. Got '${e}'`},`This object has been frozen and should not be mutated`,function(e){return`Cannot use a proxy that has been revoked. Did you pass an object from inside an immer function to an async process? `+e},`An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft.`,`Immer forbids circular references`,"The first or second argument to `produce` must be a function","The third argument to `produce` must be a function or undefined","First argument to `createDraft` must be a plain object, an array, or an immerable object","First argument to `finishDraft` must be a draft returned by `createDraft`",function(e){return`'current' expects a draft, got: ${e}`},`Object.defineProperty() cannot be used on an Immer draft`,`Object.setPrototypeOf() cannot be used on an Immer draft`,`Immer only supports deleting array indices`,`Immer only supports setting array indices and the 'length' property`,function(e){return`'original' expects a draft, got: ${e}`}];function sS(e,...t){{let n=oS[e],r=jS(n)?n.apply(null,t):n;throw Error(`[Immer] ${r}`)}}var cS=Object,lS=cS.getPrototypeOf,uS=`constructor`,dS=`prototype`,fS=`configurable`,pS=`enumerable`,mS=`writable`,hS=`value`,gS=e=>!!e&&!!e[aS];function _S(e){return e?bS(e)||DS(e)||!!e[iS]||!!e[uS]?.[iS]||OS(e)||kS(e):!1}var vS=cS[dS][uS].toString(),yS=new WeakMap;function bS(e){if(!e||!AS(e))return!1;let t=lS(e);if(t===null||t===cS[dS])return!0;let n=cS.hasOwnProperty.call(t,uS)&&t[uS];if(n===Object)return!0;if(!jS(n))return!1;let r=yS.get(n);return r===void 0&&(r=Function.toString.call(n),yS.set(n,r)),r===vS}function xS(e,t,n=!0){SS(e)===0?(n?Reflect.ownKeys(e):cS.keys(e)).forEach(n=>{t(n,e[n],e)}):e.forEach((n,r)=>t(r,n,e))}function SS(e){let t=e[aS];return t?t.type_:DS(e)?1:OS(e)?2:kS(e)?3:0}var CS=(e,t,n=SS(e))=>n===2?e.has(t):cS[dS].hasOwnProperty.call(e,t),wS=(e,t,n=SS(e))=>n===2?e.get(t):e[t],TS=(e,t,n,r=SS(e))=>{r===2?e.set(t,n):r===3?e.add(n):e[t]=n};function ES(e,t){return e===t?e!==0||1/e==1/t:e!==e&&t!==t}var DS=Array.isArray,OS=e=>e instanceof Map,kS=e=>e instanceof Set,AS=e=>typeof e==`object`,jS=e=>typeof e==`function`,MS=e=>typeof e==`boolean`;function NS(e){let t=+e;return Number.isInteger(t)&&String(t)===e}var PS=e=>AS(e)?e?.[aS]:null,FS=e=>e.copy_||e.base_,IS=e=>e.modified_?e.copy_:e.base_;function LS(e,t){if(OS(e))return new Map(e);if(kS(e))return new Set(e);if(DS(e))return Array[dS].slice.call(e);let n=bS(e);if(t===!0||t===`class_only`&&!n){let t=cS.getOwnPropertyDescriptors(e);delete t[aS];let n=Reflect.ownKeys(t);for(let r=0;r1&&cS.defineProperties(e,{set:BS,add:BS,clear:BS,delete:BS}),cS.freeze(e),t&&xS(e,(e,t)=>{RS(t,!0)},!1),e)}function zS(){sS(2)}var BS={[hS]:zS};function VS(e){return e===null||!AS(e)||cS.isFrozen(e)}var HS=`MapSet`,US=`Patches`,WS=`ArrayMethods`,GS={};function KS(e){let t=GS[e];return t||sS(0,e),t}var qS=e=>!!GS[e];function JS(e,t){GS[e]||(GS[e]=t)}var YS,XS=()=>YS,ZS=(e,t)=>({drafts_:[],parent_:e,immer_:t,canAutoFreeze_:!0,unfinalizedDrafts_:0,handledSet_:new Set,processedForPatches_:new Set,mapSetPlugin_:qS(HS)?KS(HS):void 0,arrayMethodsPlugin_:qS(WS)?KS(WS):void 0});function QS(e,t){t&&(e.patchPlugin_=KS(US),e.patches_=[],e.inversePatches_=[],e.patchListener_=t)}function $S(e){eC(e),e.drafts_.forEach(nC),e.drafts_=null}function eC(e){e===YS&&(YS=e.parent_)}var tC=e=>YS=ZS(YS,e);function nC(e){let t=e[aS];t.type_===0||t.type_===1?t.revoke_():t.revoked_=!0}function rC(e,t){t.unfinalizedDrafts_=t.drafts_.length;let n=t.drafts_[0];if(e!==void 0&&e!==n){n[aS].modified_&&($S(t),sS(4)),_S(e)&&(e=iC(t,e));let{patchPlugin_:r}=t;r&&r.generateReplacementPatches_(n[aS].base_,e,t)}else e=iC(t,n);return aC(t,e,!0),$S(t),t.patches_&&t.patchListener_(t.patches_,t.inversePatches_),e===rS?void 0:e}function iC(e,t){if(VS(t))return t;let n=t[aS];if(!n)return pC(t,e.handledSet_,e);if(!sC(n,e))return t;if(!n.modified_)return n.base_;if(!n.finalized_){let{callbacks_:t}=n;if(t)for(;t.length>0;)t.pop()(e);dC(n,e)}return n.copy_}function aC(e,t,n=!1){!e.parent_&&e.immer_.autoFreeze_&&e.canAutoFreeze_&&RS(t,n)}function oC(e){e.finalized_=!0,e.scope_.unfinalizedDrafts_--}var sC=(e,t)=>e.scope_===t,cC=[];function lC(e,t,n,r){let i=FS(e),a=e.type_;if(r!==void 0&&wS(i,r,a)===t){TS(i,r,n,a);return}if(!e.draftLocations_){let t=e.draftLocations_=new Map;xS(i,(e,n)=>{if(gS(n)){let r=t.get(n)||[];r.push(e),t.set(n,r)}})}let o=e.draftLocations_.get(t)??cC;for(let e of o)TS(i,e,n,a)}function uC(e,t,n){e.callbacks_.push(function(r){let i=t;if(!i||!sC(i,r))return;r.mapSetPlugin_?.fixSetContents(i);let a=IS(i);lC(e,i.draft_??i,a,n),dC(i,r)})}function dC(e,t){if(e.modified_&&!e.finalized_&&(e.type_===3||e.type_===1&&e.allIndicesReassigned_||(e.assigned_?.size??0)>0)){let{patchPlugin_:n}=t;if(n){let r=n.getPath(e);r&&n.generatePatches_(e,r,t)}oC(e)}}function fC(e,t,n){let{scope_:r}=e;if(gS(n)){let i=n[aS];sC(i,r)&&i.callbacks_.push(function(){SC(e),lC(e,n,IS(i),t)})}else _S(n)&&e.callbacks_.push(function(){let i=FS(e);e.type_===3?i.has(n)&&pC(n,r.handledSet_,r):wS(i,t,e.type_)===n&&r.drafts_.length>1&&(e.assigned_.get(t)??!1)===!0&&e.copy_&&pC(wS(e.copy_,t,e.type_),r.handledSet_,r)})}function pC(e,t,n){return!n.immer_.autoFreeze_&&n.unfinalizedDrafts_<1||gS(e)||t.has(e)||!_S(e)||VS(e)?e:(t.add(e),xS(e,(r,i)=>{if(gS(i)){let t=i[aS];sC(t,n)&&(TS(e,r,IS(t),e.type_),oC(t))}else _S(i)&&pC(i,t,n)}),e)}function mC(e,t){let n=DS(e),r={type_:+!!n,scope_:t?t.scope_:XS(),modified_:!1,finalized_:!1,assigned_:void 0,parent_:t,base_:e,draft_:null,copy_:null,revoke_:null,isManual_:!1,callbacks_:void 0},i=r,a=hC;n&&(i=[r],a=gC);let{revoke:o,proxy:s}=Proxy.revocable(i,a);return r.draft_=s,r.revoke_=o,[s,r]}var hC={get(e,t){if(t===aS)return e;let n=e.scope_.arrayMethodsPlugin_,r=e.type_===1&&typeof t==`string`;if(r&&n?.isArrayOperationMethod(t))return n.createMethodInterceptor(e,t);let i=FS(e);if(!CS(i,t,e.type_))return yC(e,i,t);let a=i[t];if(e.finalized_||!_S(a)||r&&e.operationMethod&&n?.isMutatingArrayMethod(e.operationMethod)&&NS(t))return a;if(a===_C(e.base_,t)||vC(e,t,a)){SC(e);let n=e.type_===1?+t:t,r=wC(e.scope_,a,e,n);return e.copy_[n]=r}return a},has(e,t){return t in FS(e)},ownKeys(e){return Reflect.ownKeys(FS(e))},set(e,t,n){let r=bC(FS(e),t);if(r?.set)return r.set.call(e.draft_,n),!0;if(!e.modified_){let r=_C(FS(e),t),i=r?.[aS];if(i&&i.base_===n)return e.copy_[t]=n,e.assigned_.delete(t),!0;if(ES(n,r)&&(n!==void 0||CS(e.base_,t,e.type_)))return!0;SC(e),xC(e)}return e.copy_[t]===n&&(n!==void 0||CS(e.copy_,t,e.type_))||Number.isNaN(n)&&Number.isNaN(e.copy_[t])?!0:(e.copy_[t]=n,e.assigned_.set(t,!0),fC(e,t,n),!0)},deleteProperty(e,t){return SC(e),_C(e.base_,t)!==void 0||t in e.base_?(e.assigned_.set(t,!1),xC(e)):e.assigned_.delete(t),e.copy_&&delete e.copy_[t],!0},getOwnPropertyDescriptor(e,t){let n=FS(e),r=Reflect.getOwnPropertyDescriptor(n,t);return r&&{[mS]:!0,[fS]:e.type_!==1||t!==`length`,[pS]:r[pS],[hS]:n[t]}},defineProperty(){sS(11)},getPrototypeOf(e){return lS(e.base_)},setPrototypeOf(){sS(12)}},gC={};for(let e in hC){let t=hC[e];gC[e]=function(){let e=arguments;return e[0]=e[0][0],t.apply(this,e)}}gC.deleteProperty=function(e,t){return isNaN(parseInt(t))&&sS(13),gC.set.call(this,e,t,void 0)},gC.set=function(e,t,n){return t!==`length`&&isNaN(parseInt(t))&&sS(14),hC.set.call(this,e[0],t,n,e[0])};function _C(e,t){let n=e[aS];return(n?FS(n):e)[t]}function vC(e,t,n){return e.type_!==1||!e.allIndicesReassigned_||e.assigned_?.get(t)||!_S(n)||n[aS]?!1:e.baseRefs_.has(n)}function yC(e,t,n){let r=bC(t,n);return r?hS in r?r[hS]:r.get?.call(e.draft_):void 0}function bC(e,t){if(!(t in e))return;let n=lS(e);for(;n;){let e=Object.getOwnPropertyDescriptor(n,t);if(e)return e;n=lS(n)}}function xC(e){e.modified_||(e.modified_=!0,e.parent_&&xC(e.parent_))}function SC(e){e.copy_||=(e.assigned_=new Map,LS(e.base_,e.scope_.immer_.useStrictShallowCopy_))}var CC=class{constructor(e){this.autoFreeze_=!0,this.useStrictShallowCopy_=!1,this.useStrictIteration_=!1,this.produce=(e,t,n)=>{if(jS(e)&&!jS(t)){let n=t;t=e;let r=this;return function(e=n,...i){return r.produce(e,e=>t.call(this,e,...i))}}jS(t)||sS(6),n!==void 0&&!jS(n)&&sS(7);let r;if(_S(e)){let i=tC(this),a=wC(i,e,void 0),o=!0;try{r=t(a),o=!1}finally{o?$S(i):eC(i)}return QS(i,n),rC(r,i)}if(!e||!AS(e)){if(r=t(e),r===void 0&&(r=e),r===rS&&(r=void 0),this.autoFreeze_&&RS(r,!0),n){let t=[],i=[];KS(US).generateReplacementPatches_(e,r,{patches_:t,inversePatches_:i}),n(t,i)}return r}sS(1,e)},this.produceWithPatches=(e,t)=>{if(jS(e))return(t,...n)=>this.produceWithPatches(t,t=>e(t,...n));let n,r;return[this.produce(e,t,(e,t)=>{n=e,r=t}),n,r]},MS(e?.autoFreeze)&&this.setAutoFreeze(e.autoFreeze),MS(e?.useStrictShallowCopy)&&this.setUseStrictShallowCopy(e.useStrictShallowCopy),MS(e?.useStrictIteration)&&this.setUseStrictIteration(e.useStrictIteration)}createDraft(e){_S(e)||sS(8),gS(e)&&(e=TC(e));let t=tC(this),n=wC(t,e,void 0);return n[aS].isManual_=!0,eC(t),n}finishDraft(e,t){let n=e&&e[aS];(!n||!n.isManual_)&&sS(9);let{scope_:r}=n;return QS(r,t),rC(void 0,r)}setAutoFreeze(e){this.autoFreeze_=e}setUseStrictShallowCopy(e){this.useStrictShallowCopy_=e}setUseStrictIteration(e){this.useStrictIteration_=e}shouldUseStrictIteration(){return this.useStrictIteration_}applyPatches(e,t){let n;for(n=t.length-1;n>=0;n--){let r=t[n];if(r.path.length===0&&r.op===`replace`){e=r.value;break}}n>-1&&(t=t.slice(n+1));let r=KS(US).applyPatches_;return gS(e)?r(e,t):this.produce(e,e=>r(e,t))}};function wC(e,t,n,r){let[i,a]=OS(t)?KS(HS).proxyMap_(t,n):kS(t)?KS(HS).proxySet_(t,n):mC(t,n);return(n?.scope_??XS()).drafts_.push(i),a.callbacks_=n?.callbacks_??[],a.key_=r,n&&r!==void 0?uC(n,a,r):a.callbacks_.push(function(e){e.mapSetPlugin_?.fixSetContents(a);let{patchPlugin_:t}=e;a.modified_&&t&&t.generatePatches_(a,[],e)}),i}function TC(e){return gS(e)||sS(10,e),EC(e)}function EC(e){if(!_S(e)||VS(e))return e;let t=e[aS],n,r=!0;if(t){if(!t.modified_)return t.base_;t.finalized_=!0,n=LS(e,t.scope_.immer_.useStrictShallowCopy_),r=t.scope_.immer_.shouldUseStrictIteration()}else n=LS(e,!0);return xS(n,(e,t)=>{TS(n,e,EC(t))},r),t&&(t.finalized_=!1),n}function DC(){oS.push(`Sets cannot have "replace" patches.`,function(e){return`Unsupported patch operation: `+e},function(e){return`Cannot apply patch, path doesn't resolve: `+e},`Patching reserved attributes like __proto__, prototype and constructor is not allowed`);function e(n,r=[]){if(n.key_!==void 0){let e=n.parent_.copy_??n.parent_.base_,t=PS(wS(e,n.key_)),i=wS(e,n.key_);if(i===void 0||i!==n.draft_&&i!==n.base_&&i!==n.copy_||t!=null&&t.base_!==n.base_)return null;let a=n.parent_.type_===3,o;if(a){let e=n.parent_;o=Array.from(e.drafts_.keys()).indexOf(n.key_)}else o=n.key_;if(!(a&&e.size>o||CS(e,o)))return null;r.push(o)}if(n.parent_)return e(n.parent_,r);r.reverse();try{t(n.copy_,r)}catch{return null}return r}function t(e,t){let n=e;for(let e=0;e{let u=wS(o,e,c),f=wS(s,e,c),p=l?CS(o,e)?n:`add`:r;if(u===f&&p===n)return;let m=t.concat(e);i.push(p===r?{op:p,path:m}:{op:p,path:m,value:d(f)}),a.push(p===`add`?{op:r,path:m}:p===r?{op:`add`,path:m,value:d(u)}:{op:n,path:m,value:d(u)})})}function s(e,t,n,i){let{base_:a,copy_:o}=e,s=0;a.forEach(e=>{if(!o.has(e)){let a=t.concat([s]);n.push({op:r,path:a,value:e}),i.unshift({op:`add`,path:a,value:e})}s++}),s=0,o.forEach(e=>{if(!a.has(e)){let a=t.concat([s]);n.push({op:`add`,path:a,value:e}),i.unshift({op:r,path:a,value:e})}s++})}function c(e,t,r){let{patches_:i,inversePatches_:a}=r;i.push({op:n,path:[],value:t===rS?void 0:t}),a.push({op:n,path:[],value:e})}function l(e,t){return t.forEach(t=>{let{path:i,op:a}=t,o=e;for(let e=0;e[e,u(t)]));if(kS(e))return new Set(Array.from(e).map(u));let t=Object.create(lS(e));for(let n in e)t[n]=u(e[n]);return CS(e,iS)&&(t[iS]=e[iS]),t}function d(e){return gS(e)?u(e):e}JS(US,{applyPatches_:l,generatePatches_:i,generateReplacementPatches_:c,getPath:e})}globalThis.Iterator?.from;var OC=new CC,kC=OC.produce,AC=OC.produceWithPatches.bind(OC),jC=OC.applyPatches.bind(OC),MC=1e3;function NC(e,t){if(e.add(t),e.size>MC){let t=e.values().next().value;t!==void 0&&e.delete(t)}}function PC(e){let{enablePatches:t=!1}=e;t&&DC();let n=Jx(),r=e.initialValue,i=new Set;return{on:n.on,value:()=>r,patch:(e,t=nS())=>{i.has(t)||(DC(),r=jC(r,e),NC(i,t),n.emit(`updated`,r,void 0,t))},mutate:(e,a=nS())=>{if(!i.has(a)){if(NC(i,a),t){let[t,i]=AC(r,e);if(t===r)return;r=t,n.emit(`updated`,r,i,a)}else{let t=kC(r,e);if(t===r)return;r=t,n.emit(`updated`,r,void 0,a)}}},syncIds:i}}var FC=typeof self==`object`?self:globalThis,IC=new Set([`Error`,`EvalError`,`RangeError`,`ReferenceError`,`SyntaxError`,`TypeError`,`URIError`,`AggregateError`]),LC=new Set([`Boolean`,`Number`,`String`,`Int8Array`,`Uint8Array`,`Uint8ClampedArray`,`Int16Array`,`Uint16Array`,`Int32Array`,`Uint32Array`,`Float16Array`,`Float32Array`,`Float64Array`,`BigInt64Array`,`BigUint64Array`]);function RC(e,t){let n=(t,n)=>(e.set(n,t),t),r=i=>{if(e.has(i))return e.get(i);let[a,o]=t[i];switch(a){case 0:case-1:return n(o,i);case 1:{let e=n([],i);for(let t of o)e.push(r(t));return e}case 2:{let e=n({},i);for(let[t,n]of o)e[r(t)]=r(n);return e}case 3:return n(new Date(o),i);case 4:{let{source:e,flags:t}=o;return n(new RegExp(e,t),i)}case 5:{let e=n(new Map,i);for(let[t,n]of o)e.set(r(t),r(n));return e}case 6:{let e=n(new Set,i);for(let t of o)e.add(r(t));return e}case 7:{let{name:e,message:t}=o,r=IC.has(e)?FC[e]:void 0;return n(new(r??FC.Error)(t),i)}case 8:return n(BigInt(o),i);case`BigInt`:return n(Object(BigInt(o)),i);case`ArrayBuffer`:return n(new Uint8Array(o).buffer,o);case`DataView`:{let{buffer:e}=new Uint8Array(o);return n(new DataView(e),o)}}if(typeof a==`string`&&LC.has(a))return n(new FC[a](o),i);throw TypeError(`unable to deserialize unsafe or unknown type: ${String(a)}`)};return r}function zC(e){return RC(new Map,e)(0)}var BC=``,{toString:VC}={},{keys:HC}=Object;function UC(e){let t=typeof e;if(t!==`object`||!e)return[0,t];let n=VC.call(e).slice(8,-1);switch(n){case`Array`:return[1,BC];case`Object`:return[2,BC];case`Date`:return[3,BC];case`RegExp`:return[4,BC];case`Map`:return[5,BC];case`Set`:return[6,BC];case`DataView`:return[1,n]}return n.includes(`Array`)?[1,n]:n.includes(`Error`)?[7,n]:[2,n]}function WC([e,t]){return e===0&&(t===`function`||t===`symbol`)}function GC(e,t,n,r){let i=(e,t)=>{let i=r.push(e)-1;return n.set(t,i),i},a=r=>{if(n.has(r))return n.get(r);let[o,s]=UC(r);switch(o){case 0:{let t=r;switch(s){case`bigint`:o=8,t=r.toString();break;case`function`:case`symbol`:if(e)throw TypeError(`unable to serialize ${s}`);t=null;break;case`undefined`:return i([-1],r)}return i([o,t],r)}case 1:{if(s){let e=r;return s===`DataView`?e=new Uint8Array(r.buffer):s===`ArrayBuffer`&&(e=new Uint8Array(r)),i([s,[...e]],r)}let e=[],t=i([o,e],r);for(let t of r)e.push(a(t));return t}case 2:{if(s)switch(s){case`BigInt`:return i([s,r.toString()],r);case`Boolean`:case`Number`:case`String`:return i([s,r.valueOf()],r)}if(t&&`toJSON`in r)return a(r.toJSON());let n=[],c=i([o,n],r);for(let t of HC(r))(e||!WC(UC(r[t])))&&n.push([a(t),a(r[t])]);return c}case 3:return i([o,r.toISOString()],r);case 4:{let{source:e,flags:t}=r;return i([o,{source:e,flags:t}],r)}case 5:{let t=[],n=i([o,t],r);for(let[n,i]of r)(e||!(WC(UC(n))||WC(UC(i))))&&t.push([a(n),a(i)]);return n}case 6:{let t=[],n=i([o,t],r);for(let n of r)(e||!WC(UC(n)))&&t.push(a(n));return n}}let{message:c}=r;return i([o,{name:s,message:c}],r)};return a}function KC(e,t={}){let n=[];return GC(!(t.json||t.lossy),!!t.json,new Map,n)(e),n}var{parse:qC,stringify:JC}=JSON,YC={json:!0,lossy:!0};function XC(e){return zC(qC(e))}function ZC(e){return JC(KC(e,YC))}function QC(e){return zC(e)}function $C(e){return ZC(e)}function ew(e){return XC(e)}var tw=256,nw=class extends Error{name=`StreamClosedError`};function rw(e={}){let t=e.id??nS(),n=Math.max(0,e.replayWindow??0),r=Jx(),i=new AbortController,a=[],o=!1,s=0;function c(e){if(o)throw new nw(`Cannot write to a closed stream "${t}"`);s+=1,n>0&&(a.push({seq:s,chunk:e}),a.length>n&&(a.length-n===1?a.shift():a.splice(0,a.length-n))),r.emit(`chunk`,s,e)}function l(e){if(o)return;o=!0;let t=aw(e);i.abort(e),r.emit(`end`,t)}function u(){o||(o=!0,i.signal.aborted||i.abort(`stream closed`),r.emit(`end`,void 0))}function d(e){o||i.signal.aborted||i.abort(e??`aborted`)}let f=new WritableStream({write(e){c(e)},close(){u()},abort(e){l(e)}});return{id:t,signal:i.signal,get closed(){return o},get lastSeq(){return s},write:c,error:l,close:u,abort:d,writable:f,events:r,buffer:a}}function iw(e={}){let t=e.id??nS(),n=Math.max(1,e.highWaterMark??tw),r=[],i=0,a=!1,o=!1,s,c,l,u;function d(){if(c){if(r.length>0){let e=r.shift(),t=c;c=void 0,t.resolve({value:e,done:!1});return}if(a){let e=c;if(c=void 0,s){let t=Error(s.message);t.name=s.name,e.reject(t)}else e.resolve({value:void 0,done:!0})}}}function f(){if(l){for(;r.length>0;){let e=r.shift();try{l.enqueue(e)}catch{break}}if(a&&l){try{if(s){let e=Error(s.message);e.name=s.name,l.error(e)}else l.close()}catch{}l=void 0}}}function p(t,s){if(!(a||o)&&!(t<=i)){if(i=t,r.push(s),r.length>n){let t=r.length-n;r.splice(0,t),e.onOverflow?.(t)}d(),u&&f()}}function m(e){a||(a=!0,s=e,d(),u&&f())}function h(){o||a||(o=!0,e.onCancel?.(),m(void 0))}function g(){return u||(u=new ReadableStream({start(e){l=e,f()},cancel(){h()}}),u)}return{id:t,get cancelled(){return o},get done(){return a},get lastSeenSeq(){return i},get readable(){return g()},cancel:h,_push:p,_end:m,[Symbol.asyncIterator](){return{next(){if(r.length>0)return Promise.resolve({value:r.shift(),done:!1});if(a){if(s){let e=Error(s.message);return e.name=s.name,Promise.reject(e)}return Promise.resolve({value:void 0,done:!0})}return new Promise((e,t)=>{c={resolve:e,reject:t}})},return(){return h(),Promise.resolve({value:void 0,done:!0})}}}}}function aw(e){if(e instanceof Error)return{name:e.name||`Error`,message:e.message};if(typeof e==`string`)return{name:`Error`,message:e};try{return{name:`Error`,message:JSON.stringify(e)}}catch{return{name:`Error`,message:String(e)}}}var ow=128;function sw(e){return e.replace(/[^\w-]+/g,`_`).slice(0,ow)}var cw=`modulepreload`,lw=function(e,t){return new URL(e,t).href},uw={},dw=function(e,t,n){let r=Promise.resolve();if(t&&t.length>0){let e=document.getElementsByTagName(`link`),i=document.querySelector(`meta[property=csp-nonce]`),a=i?.nonce||i?.getAttribute(`nonce`);function o(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}function s(e){return import.meta.resolve?import.meta.resolve(e):new URL(e,import.meta.url).href}r=o(t.map(t=>{if(t=lw(t,n),t=s(t),t in uw)return;uw[t]=!0;let r=t.endsWith(`.css`);for(let n=e.length-1;n>=0;n--){let i=e[n];if(i.href===t&&(!r||i.rel===`stylesheet`))return}let i=document.createElement(`link`);if(i.rel=r?`stylesheet`:cw,r||(i.as=`script`),i.crossOrigin=``,i.href=t,a&&i.setAttribute(`nonce`,a),document.head.appendChild(i),r)return new Promise((e,n)=>{i.addEventListener(`load`,e),i.addEventListener(`error`,()=>n(Error(`Unable to preload CSS for ${t}`)))})}).filter(e=>e!==void 0))}function i(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(let e of t||[])e.status===`rejected`&&i(e.reason);return e().catch(i)})},fw=`__connection.json`,pw=`__DEVFRAME_CONNECTION__`,mw=`x-birpc-session`,hw=`__rpc-dump/index.json`,gw=`devframe:services`,_w=`devframe_otp`,vw=`devframe_auth_token`;mx.postMessage.remoteAssetsError;var yw=class{cacheMap=new Map;options;keySerializer;constructor(e){this.options=e,this.keySerializer=e.keySerializer||(e=>qx(e))}updateOptions(e){this.options={...this.options,...e}}cached(e,t){let n=this.cacheMap.get(e);if(n)return n.get(this.keySerializer(t))}has(e,t){return this.cacheMap.get(e)?.has(this.keySerializer(t))??!1}apply(e,t){let n=this.cacheMap.get(e.m)||new Map;n.set(this.keySerializer(e.a),t),this.cacheMap.set(e.m,n)}validate(e){return this.options.functions.includes(e)}clear(e){e?this.cacheMap.delete(e):this.cacheMap.clear()}},bw=_x({docsBase:`https://devfra.me/errors`,codes:{DF0019:{why:e=>`RPC function "${e.name}" has \`agent\` set but \`jsonSerializable\` is \`false\`; MCP requires JSON-serializable data.`,fix:"Remove `jsonSerializable: false`, or remove `agent` to keep it RPC-only."},DF0020:{why:e=>`RPC function "${e.name}" declares \`jsonSerializable: true\` but the value at "${e.path}" is a ${e.type}.`,fix:"Either drop `jsonSerializable: true` (falls back to structured-clone) or change the value to a JSON-safe shape."},DF0021:{why:e=>`RPC function "${e.name}" is already registered`,fix:"Use the `force` parameter to overwrite an existing registration."},DF0022:{why:e=>`RPC function "${e.name}" is not registered. Use register() to add new functions.`},DF0023:{why:e=>`RPC function "${e.name}" is not registered`},DF0024:{why:e=>`Either handler or setup function must be provided for RPC function "${e.name}"`},DF0025:{why:e=>`Function "${e.name}" not found in dump store`},DF0026:{why:e=>`No dump match for "${e.name}" with args: ${e.args}`},DF0027:{why:e=>`Function "${e.name}" with type "${e.type}" cannot have dump configuration. Only "static" and "query" types support dumps.`},DF0028:{why:e=>`Function "${e.name}" with type "${e.type}" cannot use \`snapshot: true\`. Only "query" functions support this sugar; "static" functions have equivalent default behavior already.`,fix:"Remove `snapshot: true`, or change the function type to `query`."},DF0043:{why:e=>`RPC function "${e.name}" received an invalid argument at position ${e.index}: ${e.issues}`,fix:"Pass a value that satisfies the `args` schema declared for this function."},DF0044:{why:e=>`RPC function "${e.name}" returned a value that failed its \`returns\` schema: ${e.issues}`,fix:"Make the handler return a value that satisfies the `returns` schema, or relax the schema."}}});function xw(e){if(e.agent&&e.jsonSerializable===!1)throw bw.DF0019({name:e.name});e.agent&&!e.jsonSerializable&&(e.jsonSerializable=!0)}async function Sw(e,t){let n=e[`~standard`].validate(t);return n instanceof Promise?await n:n}function Cw(e){return e.map(e=>{let t=e.path?.map(e=>typeof e==`object`?e.key:e).join(`.`);return t?`${t}: ${e.message}`:e.message}).join(`; `)}async function ww(e,t,n){let r=n.slice();if(!t||t.length===0)return r;for(let r=0;r{n.get(t)===r&&n.delete(t)}),n.set(t,r)),await r}if(!e.__promise){let n=Promise.resolve(e.setup(t));n.catch(()=>{e.__promise===n&&(e.__promise=void 0)}),e.__promise=n}return await e.__promise}async function Dw(e,t){let n=e.handler;if(!n){let r=await Ew(e,t);if(!r.handler)throw bw.DF0024({name:e.name});n=r.handler}let r=e.args,i=e.returns;if(!r&&!i)return n;let a=n;return async(...t)=>{let n=await ww(e.name,r,t),o=await a(...n);return await Tw(e.name,i,o)}}var Ow=class{context;definitions=new Map;functions;_onChanged=[];constructor(e){this.context=e;let t=this.definitions,n=this;this.functions=new Proxy({},{get(e,r){let i=t.get(r);if(i)return Dw(i,n.context)},has(e,n){return t.has(n)},getOwnPropertyDescriptor(e,n){return{value:t.get(n)?.handler,configurable:!0,enumerable:!0}},ownKeys(){return Array.from(t.keys())}})}register(e,t=!1){if(this.definitions.has(e.name)&&!t)throw bw.DF0021({name:e.name});xw(e),this.definitions.set(e.name,e),this._onChanged.forEach(t=>t(e.name))}update(e,t=!1){if(!this.definitions.has(e.name)&&!t)throw bw.DF0022({name:e.name});xw(e),this.definitions.set(e.name,e),this._onChanged.forEach(t=>t(e.name))}onChanged(e){return this._onChanged.push(e),()=>{let t=this._onChanged.indexOf(e);t!==-1&&this._onChanged.splice(t,1)}}async getHandler(e){return await Dw(this.definitions.get(e),this.context)}getSchema(e){let t=this.definitions.get(e);if(!t)throw bw.DF0023({name:String(e)});return{args:t.args,returns:t.returns}}has(e){return this.definitions.has(e)}get(e){return this.definitions.get(e)}list(){return Array.from(this.definitions.keys())}};function kw(e,t=``){return JSON.stringify(e,function(e,n){let r=this,i=r==null?n:r[e];if(i===void 0){if(Array.isArray(r))throw jw(t,`undefined`,r,e);return n}return i!==null&&Aw(i,r,e,t),n})}function Aw(e,t,n,r){if(typeof e==`bigint`)throw jw(r,`BigInt`,t,n);if(typeof e!=`object`)return;if(e instanceof Map)throw jw(r,`Map`,t,n);if(e instanceof Set)throw jw(r,`Set`,t,n);if(e instanceof Date)throw jw(r,`Date`,t,n);if(Array.isArray(e))return;let i=Object.getPrototypeOf(e);if(i!==null&&i!==Object.prototype)throw jw(r,e.constructor?.name??`class instance`,t,n)}function jw(e,t,n,r){let i=Mw(n,r);return bw.DF0020({name:e||``,type:t,path:i})}function Mw(e,t){return Array.isArray(e)?`[${t}]`:t===``?``:t}var Nw=`__DEVFRAME_CONNECTION_META__`,Pw=`__DEVFRAME_CONNECTION_AUTH_TOKEN__`;function Fw(e){let t=[()=>window?.[e],()=>globalThis?.[e],()=>parent.window?.[e]];for(let e of t)try{let t=e();if(t)return t}catch{}}function Iw(){return Fw(pw)}function Lw(){return Fw(Nw)}function Rw(e){if(e)return e;try{let e=localStorage.getItem(Pw);if(e)return e}catch{}return Fw(Pw)}function zw(e){globalThis[pw]=e,globalThis[Nw]={...e.connectionMeta,baseUrl:e.metaBaseUrl},e.authToken&&Bw(e.authToken)}function Bw(e){try{localStorage.setItem(Pw,e)}catch{}globalThis[Pw]=e;let t=Iw();t&&(globalThis[pw]={...t,authToken:e})}function Vw(e){let t=$x(fw,e);try{return new URL(t,globalThis.location?.href).href}catch{return t}}function Hw(e,t){return t&&t!==e.authToken?{...e,authToken:t}:e}function Uw(){let e=Iw();if(e)return Hw(e,Rw()??e.authToken??e.connectionMeta.authToken);let t=Lw();if(t)return{connectionMeta:t,metaBaseUrl:t.baseUrl??Vw(`./`),authToken:Rw(t.authToken)}}async function Ww(e={}){if(e.connection){let t=Hw(e.connection,Rw(e.authToken??e.connection.authToken??e.connection.connectionMeta.authToken));return zw(t),t}let t=Array.isArray(e.baseURL)?e.baseURL:[e.baseURL??`./`];if(e.connectionMeta){let n={connectionMeta:e.connectionMeta,metaBaseUrl:Vw(t[0]??`./`),authToken:Rw(e.authToken??e.connectionMeta.authToken)};return zw(n),n}let n=Uw();if(n){let t=Hw(n,Rw(e.authToken??n.authToken??n.connectionMeta.authToken));return zw(t),t}let r=[];for(let n of t){let t=$x(fw,n),i=Vw(n);try{let n=await fetch(t);if(!n.ok)throw Error(`Failed to fetch connection meta from ${i}: ${n.status}`);let r=await n.json(),a=n.url||i,o={connectionMeta:r,metaBaseUrl:r.baseUrl?new URL(r.baseUrl,a).href:a,authToken:Rw(e.authToken??r.authToken)};return zw(o),o}catch(e){r.push(e)}}throw Error(`Failed to get connection meta from ${t.join(`, `)}`,{cause:r})}var Gw=class extends Error{name=`DevframeConnectionError`;kind;constructor(e,t,n){super(t,n),this.kind=e}};function Kw(e=_w){try{let t=globalThis.location?.hash?.replace(/^#/,``)??``;return new URLSearchParams(t).get(e)||void 0}catch{return}}function qw(e){try{let t=new URL(globalThis.location.href),n=new URLSearchParams(t.hash.replace(/^#/,``));if(!n.has(e))return;n.delete(e),t.hash=n.toString(),globalThis.history?.replaceState(globalThis.history.state,``,t.href)}catch{}}function Jw(e=_w){let t=Kw(e);return t&&qw(e),t}async function Yw(e,t={}){let n=Jw(t.param??`devframe_otp`);return n?e.isTrusted?!0:e.requestTrustWithCode(n):!1}function Xw(e){let t={},n=new WeakMap,r,i=()=>(r??=e.sharedState.get(gw,{initialValue:{}}).then(e=>(t=e.value(),e.on(`updated`,e=>{t=e}),e)),r);return i(),{state:i,has:e=>e in t,keys:()=>Object.keys(t),get:r=>{let i=t[r];if(!i)return;let a=n.get(i);return a||(a={...i,rpc:e.scope(i.scope).rpc},n.set(i,a)),a}}}function Zw(e){let t=new Map,n=new Map,r=new Map,i=new Set,a=e.connectionMeta.backend===`static`;function o(e,t){let n=r.get(e);return n&&typeof n==`object`&&!Array.isArray(n)&&typeof t==`object`&&!Array.isArray(t)?{...n,...t}:t}e.client.register({name:mx.broadcast.clientStateUpdated,type:`event`,handler:(e,n,r)=>{let i=t.get(e);i&&!i.syncIds.has(r)&&i.mutate(()=>o(e,n),r)}}),e.client.register({name:mx.broadcast.clientStatePatch,type:`event`,handler:(e,n,r)=>{let i=t.get(e);i&&!i.syncIds.has(r)&&i.patch(n,r)}});function s(t,n){let r=[];return r.push(n.on(`updated`,(n,r,i)=>{a||(r?e.callEvent(`devframe:rpc:server-state:patch`,t,r,i):e.callEvent(`devframe:rpc:server-state:set`,t,n,i))})),()=>{for(let e of r)e()}}return{keys:()=>Array.from(t.keys()),onKeyAdded(e){return i.add(e),()=>{i.delete(e)}},delete(e){let i=n.get(e);n.delete(e);let a=t.delete(e);return r.delete(e),i?.(),a},get:async(c,l)=>{if(l?.initialValue!==void 0&&r.set(c,l.initialValue),t.has(c))return t.get(c);let u=PC({initialValue:l?.initialValue,enablePatches:!1});async function d(){if(a||e.callEvent(`devframe:rpc:server-state:subscribe`,c),l?.initialValue!==void 0){t.set(c,u);for(let e of i)e(c);return e.call(`devframe:rpc:server-state:get`,c).then(e=>{e!==void 0&&u.mutate(()=>o(c,e))}).catch(e=>{console.error(`Error getting server state`,e)}),n.set(c,s(c,u)),u}{let r=await e.call(`devframe:rpc:server-state:get`,c);u.mutate(()=>o(c,r)),t.set(c,u);for(let e of i)e(c);return n.set(c,s(c,u)),u}}return new Promise(t=>{if(e.isTrusted)d().then(t);else{t(u);let n=!1;e.events.on(mx.client.isTrustedUpdated,e=>{e&&!n&&(n=!0,d())})}})}}}var Qw=new Map;function $w(e=Qw){let t=new Map;return{serialize:n=>{let r;return n.t===`q`?r=n.m:(r=t.get(n.i),t.delete(n.i)),!(n.t===`s`&&`e`in n)&&r&&e.get(r)?.jsonSerializable===!0?kw(n,r??``):`s:${$C(n)}`},deserialize:e=>{let n=e.startsWith(`s:`)?ew(e.slice(2)):JSON.parse(e);return n.t===`q`&&n.i&&n.m&&t.set(n.i,n.m),n}}}function eT(){}function tT(e){let t=e.search(/\n\n|\r\n\r\n/);if(!(t<0))return{frame:e.slice(0,t),rest:e.slice(t+(e[t]===`\r`?4:2))}}function nT(e){let t=`message`,n=[];for(let r of e.split(/\r?\n/))r.startsWith(`:`)||(r.startsWith(`event:`)?t=r.slice(6).trimStart():r.startsWith(`data:`)&&n.push(r.slice(5).replace(/^ /,``)));return{event:t,data:n}}function rT(e){let{onConnected:t=eT,onError:n=eT,onDisconnected:r=eT,definitions:i,fetch:a=globalThis.fetch.bind(globalThis)}=e,o=e.url;e.authToken&&(o=`${o}${o.includes(`?`)?`&`:`?`}${vw}=${encodeURIComponent(e.authToken)}`);let s=$w(i),c=new AbortController,l=!1,u,d,f,p,m=new Promise((e,t)=>{f=e,p=t});m.catch(()=>{});function h(e){l||(l=!0,p(e),n(e),r())}function g(){l||(l=!0,p(Error(`Devframe SSE stream closed`)),r())}function _(e,n){if(e===`session`){f(n),t();return}u?.(n)}async function ee(e){let t=e.getReader();d=t;let n=new TextDecoder,r=``;for(;;){let{done:e,value:i}=await t.read();if(e)break;for(r+=n.decode(i,{stream:!0});;){let e=tT(r);if(!e)break;r=e.rest;let{event:t,data:n}=nT(e.frame);n.length>0&&_(t,n.join(` -`))}}g()}return(async()=>{try{let e=await a(o,{headers:{accept:`text/event-stream`},signal:c.signal});if(!e.ok||!e.body)throw Error(`Devframe SSE stream request failed: ${e.status}`);await ee(e.body)}catch(e){if(c.signal.aborted){g();return}h(e instanceof Error?e:Error(String(e)))}})(),{close:()=>{l=!0,c.abort(),d?.cancel().catch(()=>{})},on:e=>{u=e},post:async e=>{let t;try{t=await m}catch{return}if(l){n(Error(`Devframe SSE channel is closed; message dropped`));return}try{let n=await a(o,{method:`POST`,headers:{"content-type":`text/plain; charset=utf-8`,[mw]:t},body:e});if(n.status===200){let e=await n.text();e&&u?.(e);return}if(!n.ok)throw Error(`Devframe SSE POST failed: ${n.status}`)}catch(e){n(e instanceof Error?e:Error(String(e)))}},serialize:s.serialize,deserialize:s.deserialize}}function iT(e,t){let{channel:n,rpcOptions:r={}}=t;return Dx(e,{...n,timeout:-1,...r,proxify:!1})}function aT(e){let{transport:t,authToken:n,connectionMeta:r,events:i,clientRpc:a,rpcOptions:o={},callTimeout:s=0}=e,c=!1,l=`connecting`,u=null,d=Promise.withResolvers();function f(e,t=null){if(t?u=t:e===`connected`&&(u=null),e===l)return;let n=l;l=e,i.emit(mx.client.connectionStatus,e,n)}let p=new Set;function m(e){for(let t of[...p])t.reject(e)}function h(){return l===`disconnected`||l===`error`?new Gw(`connection`,`[devframe] Not connected to the devframe server`,{cause:u??void 0}):l===`unauthorized`?new Gw(`auth`,`[devframe] Not authorized by the devframe server`,{cause:u??void 0}):null}function g(e,t){return new Promise((n,r)=>{let a=!1,o,c={reject(e){a||(l(),i.emit(mx.client.error,e,t),r(e))}};function l(){a=!0,p.delete(c),o&&clearTimeout(o)}p.add(c),s>0&&(o=setTimeout(()=>{c.reject(new Gw(`timeout`,`[devframe] RPC call "${t}" timed out after ${s}ms`))},s)),e.then(e=>{a||(l(),n(e))},e=>{if(a)return;l();let n=e instanceof Error?e:Error(String(e));i.emit(mx.client.error,n,t),r(n)})})}let _=new Map;for(let e of r.jsonSerializableMethods??[])_.set(e,{jsonSerializable:!0});let ee=e.createChannel({definitions:_,onError(e){f(`error`,e),i.emit(mx.client.connectionError,e),m(new Gw(`connection`,`[devframe] Connection to the devframe server failed`,{cause:e}))},onDisconnected(){l!==`error`&&f(`disconnected`),m(new Gw(`connection`,`[devframe] Disconnected from the devframe server`,{cause:u??void 0}))}}),te=iT(a.functions,{channel:ee,rpcOptions:o});a.register({name:mx.broadcast.authRevoked,type:`event`,handler:()=>{c=!1;let e=new Gw(`auth`,`[devframe] The devframe server revoked this client's trust`);f(`unauthorized`,e),i.emit(mx.client.connectionError,e),m(e),i.emit(mx.client.isTrustedUpdated,!1)}});let ne=n;async function re(e){ne=e;let t=await te.$call(`anonymous:devframe:auth`,{authToken:e,ua:navigator.userAgent,origin:location.origin});if(c=t.isTrusted,c)d.resolve(!0),f(`connected`);else{let e=new Gw(`auth`,`[devframe] The devframe server refused this client's credentials`);f(`unauthorized`,e),i.emit(mx.client.connectionError,e)}return i.emit(mx.client.isTrustedUpdated,c),t.isTrusted}async function ie(e){let t=(await te.$call(`anonymous:devframe:auth:exchange`,{code:e,ua:navigator.userAgent,origin:location.origin}))?.authToken??null;return t&&(ne=t,c=!0,d.resolve(!0),f(`connected`),i.emit(mx.client.isTrustedUpdated,!0)),t}async function ae(e={}){await te.$call(`anonymous:devframe:auth:request-code`,{ua:navigator.userAgent,origin:location.origin,...e.reissue?{reissue:!0}:{}})}async function oe(){return c?!0:re(ne??``)}async function se(e=6e4){if(c&&d.resolve(!0),e<=0)return d.promise;let t;try{return await Promise.race([d.promise,new Promise((n,r)=>{t=setTimeout(()=>{r(Error(`[devframe] Timeout waiting for rpc to be trusted`))},e)})]),c}finally{clearTimeout(t)}}return{transport:t,get isTrusted(){return c},get status(){return l},get connectionError(){return u},requestTrust:oe,requestTrustWithToken:re,requestTrustWithCode:ie,requestAuthCode:ae,ensureTrusted:se,call:(...e)=>{let t=String(e[0]),n=h();return n?(i.emit(mx.client.error,n,t),Promise.reject(n)):g(te.$call(...e),t)},callEvent:(...e)=>{let t=h();if(t){i.emit(mx.client.error,t,String(e[0]));return}return te.$callEvent(...e)},callOptional:(...e)=>{let t=String(e[0]),n=h();return n?(i.emit(mx.client.error,n,t),Promise.reject(n)):g(te.$callOptional(...e),t)},close:()=>{ee.close()}}}function oT(e,t,n){let r=(()=>{try{return new URL(t,n.href)}catch{return new URL(n.href)}})();if(e&&typeof e==`object`){if(e.host!=null||e.port!=null){let t=e.host??`${r.hostname}:${e.port}`;return new URL(e.path??`/`,`${r.protocol}//${t}`).href}return new URL(e.path??``,r).href}let i=e??``;return/^https?:\/\//i.test(i)?i:new URL(i,r).href}function sT(e){let{authToken:t,connectionMeta:n,metaBaseUrl:r,events:i,clientRpc:a,rpcOptions:o={},sseOptions:s={},callTimeout:c=0}=e,l=oT(n.sse,r??`./`,location);return aT({transport:`sse`,authToken:t,connectionMeta:n,events:i,clientRpc:a,rpcOptions:o,callTimeout:c,createChannel:e=>rT({url:l,authToken:t,definitions:e.definitions,...s,onConnected(){s.onConnected?.()},onError(t){e.onError(t),s.onError?.(t)},onDisconnected(){e.onDisconnected(),s.onDisconnected?.()}})})}function cT(e){let{name:t,message:n,cause:r,...i}=e,a=r instanceof Error?r:lT(r)?cT(r):r,o=a===void 0?Error(n):Error(n,{cause:a});return o.name=t,Object.assign(o,i),o}function lT(e){return typeof e==`object`&&!!e&&typeof e.message==`string`&&typeof e.name==`string`}function uT(e){return typeof e==`object`&&!!e&&e.type===`static`&&typeof e.path==`string`}function dT(e){return typeof e==`object`&&!!e&&e.type===`query`&&typeof e.records==`object`&&e.records!==null}function fT(e){return typeof e==`object`&&!!e&&(`output`in e||`error`in e)}function pT(e){if(e.error)throw cT(e.error);return e.output}function mT(e){return e.some(e=>e!=null)}function hT(e){return typeof e==`object`&&e&&`serialization`in e&&`data`in e?e.data:e}function gT(e,t){let n=new Map,r=new Map;function i(e,t){return t===`structured-clone`&&Array.isArray(e)?QC(e):e}function a(e,t){return i(hT(e),t)}async function o(e){n.has(e.path)||n.set(e.path,t(e.path).then(t=>a(t,e.serialization)));let r=await n.get(e.path);return fT(r)?pT(r):r}async function s(e,n){return r.has(e)||r.set(e,t(e).then(e=>a(e,n))),await r.get(e)}async function c(t,n){if(!(t in e))throw Error(`[devframe-rpc] Function "${t}" not found in dump store`);let r=e[t];if(uT(r)){if(mT(n))throw Error(`[devframe-rpc] No dump match for "${t}" with args: ${JSON.stringify(n)}`);return await o(r)}if(dT(r)){let e=qx(n),i=r.records[e];if(i)return pT(await s(i,r.serialization));if(r.fallback)return pT(await s(r.fallback,r.serialization));throw Error(`[devframe-rpc] No dump match for "${t}" with args: ${JSON.stringify(n)}`)}if(!mT(n))return r;throw Error(`[devframe-rpc] No dump match for "${t}" with args: ${JSON.stringify(n)}`)}return{call:async(e,t)=>await c(e,t),callOptional:async(t,n)=>{if(t in e)return await c(t,n)},callEvent:async(e,t)=>{}}}async function _T(e){let t=gT(await e.fetchJsonFromBases(hw),e.fetchJsonFromBases);return{transport:`static`,isTrusted:!0,status:`connected`,connectionError:null,requestTrust:async()=>!0,requestTrustWithToken:async()=>!0,requestTrustWithCode:async()=>null,requestAuthCode:async()=>{},ensureTrusted:async()=>!0,call:(...e)=>t.call(e[0],e.slice(1)),callEvent:(...e)=>t.callEvent(e[0],e.slice(1)),callOptional:(...e)=>t.callOptional(e[0],e.slice(1)),close:()=>{}}}var vT=``;function yT(e,t){return`${e}${vT}${t}`}function bT(e){let t=new Map,n=new Map;e.client.register({name:mx.broadcast.streamingChunk,type:`event`,handler(e,n,r,i){t.get(yT(e,n))?._push(r,i)}}),e.client.register({name:mx.broadcast.streamingEnd,type:`event`,handler(e,n,r){let i=yT(e,n),a=t.get(i);a&&(a._end(r),t.delete(i))}}),e.client.register({name:mx.broadcast.streamingUploadCancel,type:`event`,handler(e,t){let r=yT(e,t),i=n.get(r);i&&(i.abort(`server cancelled upload`),n.delete(r))}}),e.events.on(mx.client.isTrustedUpdated,n=>{if(n)for(let[n,r]of t){if(r.cancelled||r.done)continue;let t=n.indexOf(vT);if(t<0)continue;let i=n.slice(0,t),a=n.slice(t+1);e.callEvent(`devframe:streaming:subscribe`,i,a,{afterSeq:r.lastSeenSeq})}});function r(n,r,i={}){let a=yT(n,r),o=t.get(a);if(o)return o;let s=iw({id:r,highWaterMark:i.highWaterMark,onOverflow(e){console.warn(`[devframe] DF0029: Stream "${n}#${r}" dropped ${e} chunk(s) after exceeding the client high-water mark.`)},onCancel(){e.callEvent(`devframe:streaming:cancel`,n,r),t.delete(a)}});if(t.set(a,s),e.isTrusted)e.callEvent(`devframe:streaming:subscribe`,n,r,{afterSeq:0});else{let i=e.events.on(mx.client.isTrustedUpdated,o=>{o&&(i(),t.has(a)&&!s.cancelled&&!s.done&&e.callEvent(`devframe:streaming:subscribe`,n,r,{afterSeq:s.lastSeenSeq}))})}return s}function i(t,r){let i=yT(t,r),a=n.get(i);if(a)return a;let o=rw({id:r});return o.events.on(`chunk`,(n,i)=>{e.callEvent(`devframe:streaming:upload-chunk`,t,r,n,i)}),o.events.on(`end`,a=>{e.callEvent(`devframe:streaming:upload-end`,t,r,a),n.delete(i)}),n.set(i,o),o}return{subscribe:r,upload:i}}function xT(){}var ST=new Map;function CT(e){let t=e.url;e.authToken&&(t=`${t}?${vw}=${encodeURIComponent(e.authToken)}`);let n=new WebSocket(t),{onConnected:r=xT,onError:i=xT,onDisconnected:a=xT,definitions:o=ST}=e;n.addEventListener(`open`,e=>{r(e)}),n.addEventListener(`error`,e=>{let t=e instanceof Error?e:Error(e.type);i(t)}),n.addEventListener(`close`,e=>{a(e)});let s=$w(o);return{close:()=>{n.close()},on:e=>{n.addEventListener(`message`,t=>{e(t.data)})},post:e=>{if(n.readyState===WebSocket.OPEN){n.send(e);return}if(n.readyState===WebSocket.CONNECTING){let t=()=>{i(),n.readyState===WebSocket.OPEN&&n.send(e)},r=()=>i();function i(){n.removeEventListener(`open`,t),n.removeEventListener(`close`,r)}n.addEventListener(`open`,t),n.addEventListener(`close`,r);return}i(Error(`Devframe WebSocket is not open; message dropped`))},serialize:s.serialize,deserialize:s.deserialize}}function wT(e,t,n){let r=(()=>{try{return new URL(t,n.href)}catch{return new URL(n.href)}})(),i=r.protocol===`https:`?`wss:`:`ws:`;if(e&&typeof e==`object`){if(e.host!=null||e.port!=null){let t=e.host??`${r.hostname}:${e.port}`,n=new URL(e.path??`/`,`${i}//${t}`);return n.protocol=i,n.href}let t=new URL(e.path??``,r);return t.protocol=i,t.href}if(typeof e==`number`)return`${i}//${r.hostname}:${e}`;let a=e??``;if(/^wss?:\/\//i.test(a))return a;if(/^https?:\/\//i.test(a))return eS(a,/^https/i.test(a)?`wss://`:`ws://`);let o=new URL(a,r);return o.protocol=i,o.href}function TT(e){let{authToken:t,connectionMeta:n,metaBaseUrl:r,events:i,clientRpc:a,rpcOptions:o={},wsOptions:s={},callTimeout:c=0}=e,l=wT(n.websocket,r??`./`,location);return aT({transport:`websocket`,authToken:t,connectionMeta:n,events:i,clientRpc:a,rpcOptions:o,callTimeout:c,createChannel:e=>CT({url:l,authToken:t,definitions:e.definitions,...s,onConnected(e){s.onConnected?.(e)},onError(t){e.onError(t),s.onError?.(t)},onDisconnected(t){e.onDisconnected(),s.onDisconnected?.(t)}})})}function ET(e){return e.includes(`:`)}function DT(e,t){return ET(t)?t:`${e}:${t}`}function OT(e){return{async get(t){return(await e()).value()[t]},async set(t,n){(await e()).mutate(e=>{e[t]=n})},async delete(t){(await e()).mutate(e=>{delete e[t]})},async all(){return(await e()).value()},async onChange(t){return(await e()).on(`updated`,e=>t(e))}}}function kT(e,t,n){let r=`devframe:settings:${n}:${t}`,i;function a(){return i||=e.sharedState.get(r,{initialValue:{}}),i}return OT(a)}function AT(e,t){return{global:kT(e,t,`global`),project:kT(e,t,`project`)}}function jT(e,t){return{namespace:t,base:e,rpc:{namespace:t,register(n){if(ET(n.name))throw Error(`[devframe] Scoped client RPC registration for namespace "${t}" received an already-namespaced function name "${n.name}". Pass a bare name without a ":" separator.`);e.client.register({...n,name:`${t}:${n.name}`})},call:((n,...r)=>e.call(DT(t,n),...r)),callEvent:((n,...r)=>e.callEvent(DT(t,n),...r)),callOptional:((n,...r)=>e.callOptional(DT(t,n),...r)),sharedState:((n,r)=>e.sharedState.get(DT(t,n),r)),streaming:{subscribe:(n,r,i)=>e.streaming.subscribe(DT(t,n),r,i),upload:(n,r)=>e.streaming.upload(DT(t,n),r)}},settings:AT(e,t),scope:e.scope}}function MT(){if(typeof document<`u`){let e=document.modelContext;if(e)return e}if(typeof navigator<`u`){let e=navigator.modelContext;if(e)return e}}function NT(e,t={}){let n=t.modelContext??MT();if(!n)return()=>{};let r=n,i=new Map,a=new Map;function o(t,n){let o=sw(t.name),s=a.get(o);if(s&&s!==t.name){console.warn(`[devframe] WebMCP tool name "${o}" (from "${t.name}") collides with "${s}"; keeping the first registration.`);return}let c=new AbortController,l=Ox(t.type,n),u=r.registerTool({name:o,description:n.description,inputSchema:jx(t.args),annotations:{title:n.title??t.name,readOnlyHint:l===`read`,destructiveHint:l===`destructive`},execute:n=>PT(t,e.context,n)},{signal:c.signal});u&&`then`in u&&u.then(()=>{},()=>{}),a.set(o,t.name),i.set(t.name,()=>{c.abort(),u&&`unregister`in u&&typeof u.unregister==`function`&&u.unregister(),a.delete(o)})}function s(t){let n=t?[t]:[...e.definitions.keys()];for(let t of n){i.get(t)?.(),i.delete(t);let n=e.definitions.get(t),r=n?.agent;n&&r&&o(n,r)}}s();let c=e.onChanged(e=>s(e));return()=>{c();for(let e of i.values())e();i.clear()}}async function PT(e,t,n){try{let r=Nx(n,e.args?.length);return{content:[{type:`text`,text:FT(await(await Dw(e,t))(...r))}]}}catch(e){return{isError:!0,content:[{type:`text`,text:IT(e)}]}}}function FT(e){return e===void 0?`undefined`:typeof e==`string`?e:JSON.stringify(e,null,2)}function IT(e){if(!(e instanceof Error))return String(e);let t=e.cause instanceof Error?` (cause: ${e.cause.message})`:``;return`${e.name}: ${e.message}${t}`}function LT(e,t){if(t.backend===`static`)return`static`;let n=t.websocket!==void 0,r=t.sse!==void 0;if(e===`websocket`){if(!n)throw Error(`[devframe] transport: 'websocket' was requested, but this server does not advertise a WebSocket endpoint`);return`websocket`}if(e===`sse`){if(!r)throw Error(`[devframe] transport: 'sse' was requested, but this server does not advertise an SSE endpoint`);return`sse`}if(t.backend===`sse`&&r)return`sse`;if(n)return`websocket`;if(r)return`sse`;throw Error(`[devframe] This server advertises no RPC transport (backend "none"), so there is nothing to connect to. Enable the WebSocket or SSE endpoint on the server, or use its static/MCP surfaces instead.`)}async function RT(e={}){let{baseURL:t=`./`,rpcOptions:n={},cacheOptions:r=!1}=e,i=Jx(),a=Array.isArray(t)?t:[t],o=await Ww(e),{connectionMeta:s,metaBaseUrl:c,authToken:l}=o,u=a[0]??`./`;try{u=new URL(`.`,c).href}catch{}let d=new yw({functions:[],...typeof e.cacheOptions==`object`?e.cacheOptions:{}}),f={rpc:void 0},p=new Ow(f),m=e.webmcp===!1?void 0:NT(p),h,g=!1;async function _(e){let t=[u,...a.filter(e=>e!==u)].filter(e=>e!=null),n=[];for(let r of t)try{return await fetch($x(e,r)).then(t=>{if(!t.ok)throw Error(`Failed to fetch ${e} from ${r}: ${t.status}`);return t.json()})}catch(e){n.push(e)}throw Error(`Failed to load ${e} from ${t.join(`, `)}`,{cause:n})}let ee={authToken:l,connectionMeta:s,metaBaseUrl:c,events:i,clientRpc:p,callTimeout:e.callTimeout,rpcOptions:{...n,async onRequest(e,t,i){if(await n.onRequest?.call(this,e,t,i),r&&d?.validate(e.m)){if(d.has(e.m,e.a))return i(d.cached(e.m,e.a));let n=await t(e);d.apply(e,n)}else await t(e)}}},te=LT(e.transport??`auto`,s),ne=te===`static`?await _T({fetchJsonFromBases:_}):te===`sse`?sT({...ee,sseOptions:e.sseOptions}):TT({...ee,wsOptions:e.wsOptions}),re;try{re=new BroadcastChannel(`devframe-auth`)}catch{}let ie,ae=!1;function oe(e){return((...t)=>ae||!ie?e(...t):ie.then(()=>e(...t)))}function se(){g=!0;try{h?.(),m?.()}finally{try{re?.close()}finally{ne.close?.()}}}let ce={events:i,get isTrusted(){return ne.isTrusted},get status(){return ne.status},get connectionError(){return ne.connectionError},get transport(){return ne.transport??te},get connection(){return o},connectionMeta:s,ensureTrusted:ne.ensureTrusted,requestTrust:ne.requestTrust,requestTrustWithToken:async e=>(Bw(e),o={...o,authToken:e},ne.requestTrustWithToken(e)),requestTrustWithCode:async e=>{let t=await ne.requestTrustWithCode(e);if(!t)return!1;Bw(t),o={...o,authToken:t};try{re?.postMessage({type:`auth-update`,authToken:t})}catch{}return!0},requestAuthCode:e=>ne.requestAuthCode(e),call:oe(ne.call),callEvent:oe(ne.callEvent),callOptional:oe(ne.callOptional),client:p,sharedState:void 0,services:void 0,streaming:void 0,cacheManager:d,scope:void 0,close:se};ce.sharedState=Zw(ce),ce.streaming=bT(ce),ce.services=Xw(ce);let le=new Map;ce.scope=(e=>{if(!e)return ce;let t=le.get(e);return t||(t=jT(ce,e),le.set(e,t)),t}),f.rpc=ce;function ue(){try{return typeof window<`u`&&window.self===window.top}catch{return!1}}async function de(){if(e.simpleAuth!==!1&&ue()&&typeof globalThis.prompt==`function`)for(await ce.requestAuthCode().catch(()=>{});!ce.isTrusted;){let e=globalThis.prompt(`devframe: enter the authentication code shown in your terminal`);if(e==null)return;let t=e.trim();if(t&&await ce.requestTrustWithCode(t))return}}async function fe(){let t=await ne.requestTrust(),n=e.otpParam??`devframe_otp`,r=n?await Yw(ce,{param:n}):!1;t||r||ce.isTrusted||await de()}return ie=fe().then(()=>{ae=!0},()=>{ae=!0}),s.mcp&&dw(async()=>{let{setupBrowserAgentRpcBridge:e}=await import(`./browser-agent-rpc-BXhoSh1z-DTULcebV.js`);return{setupBrowserAgentRpcBridge:e}},[],import.meta.url).then(({setupBrowserAgentRpcBridge:e})=>{g||(h=e(ce))}).catch(()=>{}),re&&(re.onmessage=e=>{e.data?.type===`auth-update`&&e.data.authToken&&ce.requestTrustWithToken(e.data.authToken)}),ce}var zT=RT,BT=[`components`,`injectors`,`signals`,`ngrx`,`forms`,`router`,`pipes`,`http`,`analog`,`capacitor`,`errors`],VT=[`angular`,`ngrx`,`analog`,`angular-native`,`nativescript`,`capacitor`];function HT(e){return`pangular:${e}`}var UT=[`forms`,`router`,`ngrx`,`http`,`analog`,`components`],WT={refreshMs:{default:3e3,min:500,max:8e3},navigations:{default:50,min:5,max:500},formTimeline:{default:200,min:10,max:2e3},httpCalls:{default:200,min:10,max:2e3},changeLog:{default:200,min:10,max:2e3},cdCycles:{default:200,min:10,max:2e3},capacitorCalls:{default:200,min:10,max:2e3}},GT={"opted-out":`the request sets transferCache: false`,post:`POST requests are left out unless includePostRequests is set`,method:`only GET, HEAD and POST requests are cached`,"auth-headers":`it sends an Authorization, Proxy-Authorization or Cookie header`,credentials:`it sends credentials (withCredentials or credentials: include/same-origin)`,"no-cache-request":`the request asks for no-store, no-cache or private`,"no-cache-response":`the response has Cache-Control no-store, no-cache or private`,"set-cookie":`the response sets a cookie`,error:`failed responses are not stored`,mocked:`a fault rule answered before the transfer cache ran`,"cache-off-or-filter":`the transfer cache is off, or its filter (or another withHttpTransferCacheOptions setting) left it out`},KT=[[200,`OK`],[201,`Created`],[202,`Accepted`],[203,`Non-Authoritative Information`],[204,`No Content`],[205,`Reset Content`],[206,`Partial Content`],[207,`Multi-Status`],[208,`Already Reported`],[226,`IM Used`],[300,`Multiple Choices`],[301,`Moved Permanently`],[302,`Found`],[303,`See Other`],[304,`Not Modified`],[307,`Temporary Redirect`],[308,`Permanent Redirect`],[400,`Bad Request`],[401,`Unauthorized`],[402,`Payment Required`],[403,`Forbidden`],[404,`Not Found`],[405,`Method Not Allowed`],[406,`Not Acceptable`],[407,`Proxy Authentication Required`],[408,`Request Timeout`],[409,`Conflict`],[410,`Gone`],[411,`Length Required`],[412,`Precondition Failed`],[413,`Content Too Large`],[414,`URI Too Long`],[415,`Unsupported Media Type`],[416,`Range Not Satisfiable`],[417,`Expectation Failed`],[418,`I'm a Teapot`],[421,`Misdirected Request`],[422,`Unprocessable Content`],[423,`Locked`],[424,`Failed Dependency`],[425,`Too Early`],[426,`Upgrade Required`],[428,`Precondition Required`],[429,`Too Many Requests`],[431,`Request Header Fields Too Large`],[451,`Unavailable For Legal Reasons`],[499,`Client Closed Request`],[500,`Internal Server Error`],[501,`Not Implemented`],[502,`Bad Gateway`],[503,`Service Unavailable`],[504,`Gateway Timeout`],[505,`HTTP Version Not Supported`],[506,`Variant Also Negotiates`],[507,`Insufficient Storage`],[508,`Loop Detected`],[510,`Not Extended`],[511,`Network Authentication Required`],[520,`Web Server Returned an Unknown Error`],[521,`Web Server Is Down`],[522,`Connection Timed Out`],[523,`Origin Is Unreachable`],[524,`A Timeout Occurred`]];function qT(e){return KT.some(([t])=>t===e)}var JT=`pangular`,YT={mod:`Mod`,ctrl:`Ctrl`,control:`Ctrl`,meta:`Meta`,cmd:`Meta`,command:`Meta`,alt:`Alt`,option:`Alt`,opt:`Alt`,shift:`Shift`},XT=[`Mod`,`Ctrl`,`Meta`,`Alt`,`Shift`];function ZT(e){if(typeof e!=`string`||e.length>40)return;let t=e.split(`+`).map(e=>e.trim());if(t.some(e=>!e))return;let n=new Set,r;for(let e of t){let t=YT[e.toLowerCase()];if(t){if(n.has(t))return;n.add(t);continue}if(r!==void 0)return;if(/^[a-z0-9]$/i.test(e))r=e.toUpperCase();else if(/^f([1-9]|1[0-2])$/i.test(e))r=e.toUpperCase();else return}if(r&&[...n].some(e=>e!==`Shift`)&&!(n.has(`Mod`)&&(n.has(`Ctrl`)||n.has(`Meta`))))return[...XT.filter(e=>n.has(e)),r].join(`+`)}function QT(e,t,n){let r=t.split(`+`),i=r.pop(),a=new Set(r),o=a.has(`Ctrl`)||a.has(`Mod`)&&!n,s=a.has(`Meta`)||a.has(`Mod`)&&n;if(e.ctrlKey!==o||e.metaKey!==s||e.altKey!==a.has(`Alt`)||e.shiftKey!==a.has(`Shift`))return!1;if(/^F\d+$/.test(i))return e.key.toUpperCase()===i;let c=/^\d$/.test(i)?`Digit${i}`:`Key${i}`;return e.code?e.code===c:e.key.toUpperCase()===i}function $T(e,t){if(!t)return e.replace(/^Mod\b/,`Ctrl`);let n={Mod:`⌘`,Meta:`⌘`,Ctrl:`⌃`,Alt:`⌥`,Shift:`⇧`};return e.split(`+`).map(e=>n[e]??e).join(``)}var eE=[{id:`dashboard`,label:`Dashboard`},{id:`analog`,label:`Analog`,inspector:`analog`},{id:`components`,label:`Components`,inspector:`components`},{id:`routes`,label:`Routes`,inspector:`router`},{id:`signals`,label:`Signals`,inspector:`signals`},{id:`injectors`,label:`Injectors`,inspector:`injectors`},{id:`store`,label:`Store`,inspector:`ngrx`},{id:`forms`,label:`Forms`,inspector:`forms`},{id:`pipes`,label:`Pipes`,inspector:`pipes`},{id:`network`,label:`SSR & HTTP`,inspector:`http`},{id:`errors`,label:`Errors`,inspector:`errors`},{id:`capacitor`,label:`Capacitor`,inspector:`capacitor`}];function tE(e){return eE.filter(t=>!(`inspector`in t)||e.inspectors[t.inspector])}var nE={components:`a component instance, by its id in the component tree`,injectors:`a component's element injector, by the component's id`,forms:`a form, by its id`},rE=200;function iE(e){let t=new URLSearchParams({tab:e.tab});return e.id&&t.set(`id`,e.id),e.page&&t.set(`page`,e.page),`#${t.toString()}`}function aE(e){let t=new URLSearchParams(e.replace(/^#/,``)),n=e=>{let n=t.get(e)?.trim();return n&&n.length<=rE?n:void 0},r=n(`tab`);if(!r)return null;let i=n(`id`),a=n(`page`);return{tab:r,...i?{id:i}:{},...a?{page:a}:{}}}var oE=[{id:`settings`,title:`Open settings`,group:`Go to`,keywords:`preferences tabs`},{id:`pick-component`,title:`Pick component on page`,group:`Actions`,keywords:`select inspect element`,inspector:`components`},{id:`cd-record-start`,title:`Start recording change detection`,group:`Actions`,keywords:`profiler cycles`,inspector:`components`},{id:`cd-record-stop`,title:`Stop recording change detection`,group:`Actions`,keywords:`profiler cycles`,inspector:`components`},{id:`cd-clear`,title:`Clear change detection recording`,group:`Actions`,keywords:`reset recordings cycles`,inspector:`components`},{id:`theme:system`,title:`Theme: follow the system`,group:`Theme`,keywords:`auto`},{id:`theme:light`,title:`Theme: light`,group:`Theme`},{id:`theme:dark`,title:`Theme: dark`,group:`Theme`},{id:`dock:float`,title:`Popup: float`,group:`Popup`,keywords:`dock`,popupOnly:!0},{id:`dock:bottom`,title:`Popup: dock to the bottom`,group:`Popup`,keywords:`dock`,popupOnly:!0},{id:`dock:right`,title:`Popup: dock to the right`,group:`Popup`,keywords:`dock`,popupOnly:!0}];function sE(e){let t=tE(e).map(e=>({id:`tab:${e.id}`,title:`Go to ${e.label}`,group:`Go to`,keywords:`tab open`})),n=oE.filter(t=>!t.inspector||e.inspectors[t.inspector]).map(({inspector:e,...t})=>t);return[...t,...n]}function cE(e){return e&&typeof e==`object`&&!Array.isArray(e)?e:{}}function lE(e,t){return typeof e==`boolean`?e:t}var uE=100,dE=100;function fE(e){if(!Array.isArray(e))return[];let t=e.filter(e=>typeof e==`string`).map(e=>e.trim()).filter(e=>e&&e.length<=dE);return[...new Set(t)].slice(0,uE)}function pE(e,t){let{default:n,min:r,max:i}=WT[t];return typeof e!=`number`||!Number.isFinite(e)?n:Math.min(i,Math.max(r,Math.round(e)))}function mE(e,t){return Object.fromEntries(e.map(e=>[e,t(e)]))}function hE(e){let t=cE(e),n=cE(t.agent),r=cE(t.inspectors),i=cE(n.tools),a=cE(t.actions),o=lE(t.actions,!0),s=cE(t.redaction),c=cE(t.limits),l=mE(BT,e=>lE(r[e],!0));return{inspectors:l,agent:{readOnly:lE(n.readOnly,!1),tools:mE(BT,e=>l[e]&&lE(i[e],!0))},actions:mE(UT,e=>l[e]&&lE(a[e],o)),redaction:{secretNames:fE(s.secretNames),unmask:fE(s.unmask)},limits:Object.fromEntries(Object.keys(WT).map(e=>[e,pE(c[e],e)])),popup:{hotkey:gE(cE(t.popup).hotkey)}}}function gE(e){return e===!1?!1:ZT(e)??`Alt+Shift+P`}function _E(e){let t=e?.configs;return hE(t?.[JT])}function vE(e){return`${{forms:`Writing to forms`,router:`Navigating and clearing navigations`,ngrx:`Restoring NgRx state and dispatching actions`,http:`Changing HTTP mock rules and clearing HTTP calls`,analog:`Calling API routes and clearing server calls`,components:`Editing component properties and signals`}[e]} is turned off in the devtools config (actions.${e}).`}var yE={components:`Components`,injectors:`Injectors`,signals:`Signals`,ngrx:`NgRx`,forms:`Forms`,router:`Router`,pipes:`Pipes`,http:`HTTP`,analog:`Analog`,capacitor:`Capacitor`,errors:`Errors`},bE={forms:`Form writes`,router:`Navigation`,ngrx:`NgRx restore and dispatch`,http:`HTTP mocking`,analog:`Analog API calls`,components:`Component edits`},xE={refreshMs:e=>`poll every ${e} ms`,navigations:e=>`${e} navigations`,formTimeline:e=>`${e} form events`,httpCalls:e=>`${e} HTTP calls`,changeLog:e=>`${e} NgRx changes`,cdCycles:e=>`${e} change detection cycles`,capacitorCalls:e=>`${e} Capacitor plugin calls`};function SE(e){let t=[],n=(e,n)=>{n.length&&t.push({label:e,value:n.join(`, `)})},r=BT.filter(t=>e.inspectors[t]);return n(`Inspectors off`,BT.filter(t=>!e.inspectors[t]).map(e=>yE[e])),n(`Agent`,e.agent.readOnly?[`Read-only`]:[]),n(`Hidden from the agent`,r.filter(t=>!e.agent.tools[t]).map(e=>yE[e])),n(`Blocked actions`,UT.filter(t=>e.inspectors[t]&&!e.actions[t]).map(e=>bE[e])),n(`Extra secret names`,e.redaction.secretNames),n(`Unmasked names`,e.redaction.unmask),n(`Limits`,Object.keys(WT).filter(t=>e.limits[t]!==WT[t].default).map(t=>xE[t](e.limits[t]))),e.popup.hotkey!==`Alt+Shift+P`&&n(`Popup shortcut`,[e.popup.hotkey===!1?`Off`:e.popup.hotkey]),t}function CE(e){let t={tags:[],directives:[],text:``},n=e.toLowerCase();return n=n.replace(/<\s*([^<>\s]+)\s*>/g,(e,n)=>(t.tags.push({value:n,exact:!0}),` `)),n=n.replace(/\[\s*([^[\]\s]+)\s*\]/g,(e,n)=>(t.directives.push({value:n,exact:!0}),` `)),n=n.replace(/(^|\s)([<[])([^<>[\]\s]*)$/,(e,n,r,i)=>(i&&(r===`<`?t.tags:t.directives).push({value:i,exact:!1}),n)),t.text=n.replace(/\s+/g,` `).trim(),t}function wE(e){return!e.tags.length&&!e.directives.length&&!e.text}function TE(e,t){let n=e.tag.toLowerCase(),r=(e.directives??[]).map(e=>e.toLowerCase()),i=(e.attributes??[]).map(e=>e.toLowerCase()),a=[...r,...r.map(e=>e.replace(/directive$/,``)),...i];for(let{value:e,exact:r}of t.tags)if(r?n!==e:!n.startsWith(e))return!1;for(let{value:e,exact:n}of t.directives)if(!a.some(t=>n?t===e:t.includes(e)))return!1;if(t.text){let a=t.text;if(!(e.name.toLowerCase().includes(a)||n.includes(a)||r.some(e=>e.includes(a))||i.some(e=>e.includes(a))))return!1}return!0}var EE=`pangular-page-id`,DE=null;function OE(e){DE=e}function kE(){if(DE)return DE;try{return new URLSearchParams(location.search).get(`pageId`)||(window.top===window?null:sessionStorage.getItem(EE)||null)}catch{return null}}function AE(e,t){if(t.chosen&&e[t.chosen])return t.chosen;if(t.host)return t.host;if(t.previous&&e[t.previous])return t.previous;let n=null;for(let t of Object.values(e))(!n||t.reportedAt>n.reportedAt)&&(n=t);return n?.pageId??null}function jE(e,t){return e?t?e.pages?.[t]??null:e:null}function ME(e,t){return e?t?e.pages?.[t]??null:e.graph??null:null}function NE(e,t,n){return e?e.scope(`pangular`).rpc.call(t,...n===void 0?[]:[n]):Promise.resolve(null)}function PE(e,t,n){return NE(e,t,n).then(e=>e,()=>null)}function FE(e){return e?.connectionMeta.backend===`static`}function IE(e){let t=()=>{let t=e()?.scope(`pangular`).rpc;t&&(t.callEvent(`request-page-highlight`,null),t.callEvent(`request-form-highlight`,null))};return addEventListener(`pagehide`,t),()=>{removeEventListener(`pagehide`,t),t()}}function LE(e){return e.split(`@`)[1]??``}var RE={signal:`Signal Forms`,reactive:`Reactive`,template:`Template-driven`},zE={own:`validator`,directive:`template attribute`,tree:`cross-field rule`,async:`async`,parse:`parse`,submission:`server`,schema:`schema`,manual:`setErrors`},BE=PE,VE={key:`name looks secret`,"input-type":`password input`,autocomplete:`autocomplete is a secret kind`,marker:`marked as mask`,parent:`inside a secret group`,config:`listed in mask`};function HE(e){return VE[e]??e}var UE=`https://pangular-inspector.dev/security/#opt-fields-in-or-out`;async function WE(e,t){return await BE(e,`request-form-action`,t)??{ok:!1,error:`The devtools server did not answer.`}}function GE(e){return`${e.ok?e.message??`Done.`:e.error??`Failed.`}${e.skipped?.length?` Skipped: ${e.skipped.map(e=>`${e.path} (${e.reason})`).join(`, `)}.`:``}${e.status?` Status: ${e.status}.`:``}`}var KE=/"(?:[^"\\\n]|\\.)*"|`([^`\n]*)`|\*\*/g;function qE(e){return(e??``).replace(/^_Labels, paths.*_\n\n/,``).replace(KE,(e,t)=>t??(e===`**`?``:e))}function JE(e){return _E(e?.connectionMeta)}function YE(e,t){return JE(e).actions[t]}var XE={components:`components`,routes:`router`,signals:`signals`,injectors:`injectors`,store:`ngrx`,forms:`forms`,pipes:`pipes`,network:`http`,analog:`analog`,capacitor:`capacitor`,errors:`errors`};function ZE(e){return XE[e]}var QE=`https://pangular-inspector.dev/getting-started/configuration/#inspectors-and-agent-tools`;function $E(e,t){let n=XE[e];return!n||t.inspectors[n]}function eD(e,t){e&1&&(O(),Vg(0,`rect`,4)(1,`rect`,5)(2,`rect`,6)(3,`rect`,7))}function tD(e,t){e&1&&(O(),Vg(0,`path`,8)(1,`path`,9)(2,`path`,10))}function nD(e,t){e&1&&(O(),Vg(0,`circle`,11)(1,`path`,12)(2,`circle`,13))}function rD(e,t){e&1&&(O(),Vg(0,`path`,1))}function iD(e,t){e&1&&(O(),Vg(0,`path`,14)(1,`path`,15)(2,`path`,16)(3,`path`,17))}function aD(e,t){e&1&&(O(),Vg(0,`ellipse`,18)(1,`path`,19)(2,`path`,20))}function oD(e,t){e&1&&(O(),Vg(0,`rect`,21)(1,`path`,22)(2,`path`,23)(3,`path`,24))}function sD(e,t){e&1&&(O(),Vg(0,`circle`,25)(1,`path`,26)(2,`path`,27))}function cD(e,t){e&1&&(O(),Vg(0,`path`,28)(1,`path`,29)(2,`path`,30)(3,`path`,31)(4,`circle`,32))}function lD(e,t){e&1&&(O(),Vg(0,`circle`,33)(1,`path`,34))}function uD(e,t){e&1&&(O(),Vg(0,`circle`,25)(1,`path`,35)(2,`path`,36))}function dD(e,t){e&1&&(O(),Vg(0,`path`,2))}function fD(e,t){e&1&&(O(),Vg(0,`path`,3))}var pD=class e{name=$.required();static ɵfac=function(t){return new(t||e)};static ɵcmp=Kh({type:e,selectors:[[`app-tab-icon`]],inputs:{name:[1,`name`]},decls:14,vars:1,consts:[[`viewBox`,`0 0 24 24`,`width`,`16`,`height`,`16`,`fill`,`none`,`stroke`,`currentColor`,`stroke-width`,`1.8`,`stroke-linecap`,`round`,`stroke-linejoin`,`round`,`aria-hidden`,`true`],[`d`,`M22 12h-4l-3 9L9 3l-3 9H2`],[`d`,`m8 3 4 8 5-5 5 15H2L8 3z`],[`fill`,`currentColor`,`stroke`,`none`,`transform`,`translate(1.8 1.8) scale(0.85)`,`d`,`M24 3.7l-5.766 5.766 5.725 5.736-3.713 3.712L5.073 3.742 8.786.03l5.736 5.726L20.284 0 24 3.7zM.029 8.785l3.713-3.713 15.173 15.173-3.713 3.714-5.732-5.726L3.7 24 0 20.285l5.754-5.764L.029 8.785z`],[`x`,`3`,`y`,`3`,`width`,`7`,`height`,`9`,`rx`,`1.5`],[`x`,`14`,`y`,`3`,`width`,`7`,`height`,`5`,`rx`,`1.5`],[`x`,`14`,`y`,`12`,`width`,`7`,`height`,`9`,`rx`,`1.5`],[`x`,`3`,`y`,`16`,`width`,`7`,`height`,`5`,`rx`,`1.5`],[`d`,`M12 2 3 7l9 5 9-5-9-5Z`],[`d`,`m3 12 9 5 9-5`],[`d`,`m3 17 9 5 9-5`],[`cx`,`6`,`cy`,`19`,`r`,`3`],[`d`,`M9 19h8.5a3.5 3.5 0 0 0 0-7h-11a3.5 3.5 0 0 1 0-7H15`],[`cx`,`18`,`cy`,`5`,`r`,`3`],[`d`,`M12 22v-5`],[`d`,`M9 8V2`],[`d`,`M15 8V2`],[`d`,`M18 8v5a4 4 0 0 1-4 4h-4a4 4 0 0 1-4-4V8Z`],[`cx`,`12`,`cy`,`5`,`rx`,`9`,`ry`,`3`],[`d`,`M3 5v14a9 3 0 0 0 18 0V5`],[`d`,`M3 12a9 3 0 0 0 18 0`],[`x`,`3`,`y`,`4`,`width`,`18`,`height`,`16`,`rx`,`2`],[`d`,`M7 9h10`],[`d`,`M7 13h6`],[`d`,`M7 17h8`],[`cx`,`12`,`cy`,`12`,`r`,`9`],[`d`,`M3 12h18`],[`d`,`M12 3a14 14 0 0 1 0 18a14 14 0 0 1 0-18Z`],[`d`,`M4 7h10`],[`d`,`M4 17h6`],[`d`,`m14 13 4 4-4 4`],[`d`,`M10 17h8`],[`cx`,`18`,`cy`,`7`,`r`,`3`],[`cx`,`12`,`cy`,`12`,`r`,`3`],[`d`,`M19.4 15a1.7 1.7 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-1.8-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 1 1-4 0v-.1a1.7 1.7 0 0 0-1.1-1.5 1.7 1.7 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.7 1.7 0 0 0 .3-1.8 1.7 1.7 0 0 0-1.5-1H3a2 2 0 1 1 0-4h.1a1.7 1.7 0 0 0 1.5-1.1 1.7 1.7 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.7 1.7 0 0 0 1.8.3H9a1.7 1.7 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0-.3 1.8V9a1.7 1.7 0 0 0 1.5 1H21a2 2 0 1 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1Z`],[`d`,`M12 7v6`],[`d`,`M12 16.5v.01`]],template:function(e,t){if(e&1&&(O(),B(0,`svg`,0),M(1,eD,4,0)(2,tD,3,0)(3,nD,3,0)(4,rD,1,0,`:svg:path`,1)(5,iD,4,0)(6,aD,3,0)(7,oD,4,0)(8,sD,3,0)(9,cD,5,0)(10,lD,2,0)(11,uD,3,0)(12,dD,1,0,`:svg:path`,2)(13,fD,1,0,`:svg:path`,3),V()),e&2){let e;A(),N((e=t.name())===`dashboard`?1:e===`components`?2:e===`routes`?3:e===`signals`?4:e===`injectors`?5:e===`store`?6:e===`forms`?7:e===`network`?8:e===`pipes`?9:e===`settings`?10:e===`errors`?11:e===`analog`?12:e===`capacitor`?13:-1)}},styles:[`[_nghost-%COMP%] { +`))}}g()}return(async()=>{try{let e=await a(o,{headers:{accept:`text/event-stream`},signal:c.signal});if(!e.ok||!e.body)throw Error(`Devframe SSE stream request failed: ${e.status}`);await ee(e.body)}catch(e){if(c.signal.aborted){g();return}h(e instanceof Error?e:Error(String(e)))}})(),{close:()=>{l=!0,c.abort(),d?.cancel().catch(()=>{})},on:e=>{u=e},post:async e=>{let t;try{t=await m}catch{return}if(l){n(Error(`Devframe SSE channel is closed; message dropped`));return}try{let n=await a(o,{method:`POST`,headers:{"content-type":`text/plain; charset=utf-8`,[mw]:t},body:e});if(n.status===200){let e=await n.text();e&&u?.(e);return}if(!n.ok)throw Error(`Devframe SSE POST failed: ${n.status}`)}catch(e){n(e instanceof Error?e:Error(String(e)))}},serialize:s.serialize,deserialize:s.deserialize}}function iT(e,t){let{channel:n,rpcOptions:r={}}=t;return Dx(e,{...n,timeout:-1,...r,proxify:!1})}function aT(e){let{transport:t,authToken:n,connectionMeta:r,events:i,clientRpc:a,rpcOptions:o={},callTimeout:s=0}=e,c=!1,l=`connecting`,u=null,d=Promise.withResolvers();function f(e,t=null){if(t?u=t:e===`connected`&&(u=null),e===l)return;let n=l;l=e,i.emit(mx.client.connectionStatus,e,n)}let p=new Set;function m(e){for(let t of[...p])t.reject(e)}function h(){return l===`disconnected`||l===`error`?new Gw(`connection`,`[devframe] Not connected to the devframe server`,{cause:u??void 0}):l===`unauthorized`?new Gw(`auth`,`[devframe] Not authorized by the devframe server`,{cause:u??void 0}):null}function g(e,t){return new Promise((n,r)=>{let a=!1,o,c={reject(e){a||(l(),i.emit(mx.client.error,e,t),r(e))}};function l(){a=!0,p.delete(c),o&&clearTimeout(o)}p.add(c),s>0&&(o=setTimeout(()=>{c.reject(new Gw(`timeout`,`[devframe] RPC call "${t}" timed out after ${s}ms`))},s)),e.then(e=>{a||(l(),n(e))},e=>{if(a)return;l();let n=e instanceof Error?e:Error(String(e));i.emit(mx.client.error,n,t),r(n)})})}let _=new Map;for(let e of r.jsonSerializableMethods??[])_.set(e,{jsonSerializable:!0});let ee=e.createChannel({definitions:_,onError(e){f(`error`,e),i.emit(mx.client.connectionError,e),m(new Gw(`connection`,`[devframe] Connection to the devframe server failed`,{cause:e}))},onDisconnected(){l!==`error`&&f(`disconnected`),m(new Gw(`connection`,`[devframe] Disconnected from the devframe server`,{cause:u??void 0}))}}),te=iT(a.functions,{channel:ee,rpcOptions:o});a.register({name:mx.broadcast.authRevoked,type:`event`,handler:()=>{c=!1;let e=new Gw(`auth`,`[devframe] The devframe server revoked this client's trust`);f(`unauthorized`,e),i.emit(mx.client.connectionError,e),m(e),i.emit(mx.client.isTrustedUpdated,!1)}});let ne=n;async function re(e){ne=e;let t=await te.$call(`anonymous:devframe:auth`,{authToken:e,ua:navigator.userAgent,origin:location.origin});if(c=t.isTrusted,c)d.resolve(!0),f(`connected`);else{let e=new Gw(`auth`,`[devframe] The devframe server refused this client's credentials`);f(`unauthorized`,e),i.emit(mx.client.connectionError,e)}return i.emit(mx.client.isTrustedUpdated,c),t.isTrusted}async function ie(e){let t=(await te.$call(`anonymous:devframe:auth:exchange`,{code:e,ua:navigator.userAgent,origin:location.origin}))?.authToken??null;return t&&(ne=t,c=!0,d.resolve(!0),f(`connected`),i.emit(mx.client.isTrustedUpdated,!0)),t}async function ae(e={}){await te.$call(`anonymous:devframe:auth:request-code`,{ua:navigator.userAgent,origin:location.origin,...e.reissue?{reissue:!0}:{}})}async function oe(){return c?!0:re(ne??``)}async function se(e=6e4){if(c&&d.resolve(!0),e<=0)return d.promise;let t;try{return await Promise.race([d.promise,new Promise((n,r)=>{t=setTimeout(()=>{r(Error(`[devframe] Timeout waiting for rpc to be trusted`))},e)})]),c}finally{clearTimeout(t)}}return{transport:t,get isTrusted(){return c},get status(){return l},get connectionError(){return u},requestTrust:oe,requestTrustWithToken:re,requestTrustWithCode:ie,requestAuthCode:ae,ensureTrusted:se,call:(...e)=>{let t=String(e[0]),n=h();return n?(i.emit(mx.client.error,n,t),Promise.reject(n)):g(te.$call(...e),t)},callEvent:(...e)=>{let t=h();if(t){i.emit(mx.client.error,t,String(e[0]));return}return te.$callEvent(...e)},callOptional:(...e)=>{let t=String(e[0]),n=h();return n?(i.emit(mx.client.error,n,t),Promise.reject(n)):g(te.$callOptional(...e),t)},close:()=>{ee.close()}}}function oT(e,t,n){let r=(()=>{try{return new URL(t,n.href)}catch{return new URL(n.href)}})();if(e&&typeof e==`object`){if(e.host!=null||e.port!=null){let t=e.host??`${r.hostname}:${e.port}`;return new URL(e.path??`/`,`${r.protocol}//${t}`).href}return new URL(e.path??``,r).href}let i=e??``;return/^https?:\/\//i.test(i)?i:new URL(i,r).href}function sT(e){let{authToken:t,connectionMeta:n,metaBaseUrl:r,events:i,clientRpc:a,rpcOptions:o={},sseOptions:s={},callTimeout:c=0}=e,l=oT(n.sse,r??`./`,location);return aT({transport:`sse`,authToken:t,connectionMeta:n,events:i,clientRpc:a,rpcOptions:o,callTimeout:c,createChannel:e=>rT({url:l,authToken:t,definitions:e.definitions,...s,onConnected(){s.onConnected?.()},onError(t){e.onError(t),s.onError?.(t)},onDisconnected(){e.onDisconnected(),s.onDisconnected?.()}})})}function cT(e){let{name:t,message:n,cause:r,...i}=e,a=r instanceof Error?r:lT(r)?cT(r):r,o=a===void 0?Error(n):Error(n,{cause:a});return o.name=t,Object.assign(o,i),o}function lT(e){return typeof e==`object`&&!!e&&typeof e.message==`string`&&typeof e.name==`string`}function uT(e){return typeof e==`object`&&!!e&&e.type===`static`&&typeof e.path==`string`}function dT(e){return typeof e==`object`&&!!e&&e.type===`query`&&typeof e.records==`object`&&e.records!==null}function fT(e){return typeof e==`object`&&!!e&&(`output`in e||`error`in e)}function pT(e){if(e.error)throw cT(e.error);return e.output}function mT(e){return e.some(e=>e!=null)}function hT(e){return typeof e==`object`&&e&&`serialization`in e&&`data`in e?e.data:e}function gT(e,t){let n=new Map,r=new Map;function i(e,t){return t===`structured-clone`&&Array.isArray(e)?QC(e):e}function a(e,t){return i(hT(e),t)}async function o(e){n.has(e.path)||n.set(e.path,t(e.path).then(t=>a(t,e.serialization)));let r=await n.get(e.path);return fT(r)?pT(r):r}async function s(e,n){return r.has(e)||r.set(e,t(e).then(e=>a(e,n))),await r.get(e)}async function c(t,n){if(!(t in e))throw Error(`[devframe-rpc] Function "${t}" not found in dump store`);let r=e[t];if(uT(r)){if(mT(n))throw Error(`[devframe-rpc] No dump match for "${t}" with args: ${JSON.stringify(n)}`);return await o(r)}if(dT(r)){let e=qx(n),i=r.records[e];if(i)return pT(await s(i,r.serialization));if(r.fallback)return pT(await s(r.fallback,r.serialization));throw Error(`[devframe-rpc] No dump match for "${t}" with args: ${JSON.stringify(n)}`)}if(!mT(n))return r;throw Error(`[devframe-rpc] No dump match for "${t}" with args: ${JSON.stringify(n)}`)}return{call:async(e,t)=>await c(e,t),callOptional:async(t,n)=>{if(t in e)return await c(t,n)},callEvent:async(e,t)=>{}}}async function _T(e){let t=gT(await e.fetchJsonFromBases(hw),e.fetchJsonFromBases);return{transport:`static`,isTrusted:!0,status:`connected`,connectionError:null,requestTrust:async()=>!0,requestTrustWithToken:async()=>!0,requestTrustWithCode:async()=>null,requestAuthCode:async()=>{},ensureTrusted:async()=>!0,call:(...e)=>t.call(e[0],e.slice(1)),callEvent:(...e)=>t.callEvent(e[0],e.slice(1)),callOptional:(...e)=>t.callOptional(e[0],e.slice(1)),close:()=>{}}}var vT=``;function yT(e,t){return`${e}${vT}${t}`}function bT(e){let t=new Map,n=new Map;e.client.register({name:mx.broadcast.streamingChunk,type:`event`,handler(e,n,r,i){t.get(yT(e,n))?._push(r,i)}}),e.client.register({name:mx.broadcast.streamingEnd,type:`event`,handler(e,n,r){let i=yT(e,n),a=t.get(i);a&&(a._end(r),t.delete(i))}}),e.client.register({name:mx.broadcast.streamingUploadCancel,type:`event`,handler(e,t){let r=yT(e,t),i=n.get(r);i&&(i.abort(`server cancelled upload`),n.delete(r))}}),e.events.on(mx.client.isTrustedUpdated,n=>{if(n)for(let[n,r]of t){if(r.cancelled||r.done)continue;let t=n.indexOf(vT);if(t<0)continue;let i=n.slice(0,t),a=n.slice(t+1);e.callEvent(`devframe:streaming:subscribe`,i,a,{afterSeq:r.lastSeenSeq})}});function r(n,r,i={}){let a=yT(n,r),o=t.get(a);if(o)return o;let s=iw({id:r,highWaterMark:i.highWaterMark,onOverflow(e){console.warn(`[devframe] DF0029: Stream "${n}#${r}" dropped ${e} chunk(s) after exceeding the client high-water mark.`)},onCancel(){e.callEvent(`devframe:streaming:cancel`,n,r),t.delete(a)}});if(t.set(a,s),e.isTrusted)e.callEvent(`devframe:streaming:subscribe`,n,r,{afterSeq:0});else{let i=e.events.on(mx.client.isTrustedUpdated,o=>{o&&(i(),t.has(a)&&!s.cancelled&&!s.done&&e.callEvent(`devframe:streaming:subscribe`,n,r,{afterSeq:s.lastSeenSeq}))})}return s}function i(t,r){let i=yT(t,r),a=n.get(i);if(a)return a;let o=rw({id:r});return o.events.on(`chunk`,(n,i)=>{e.callEvent(`devframe:streaming:upload-chunk`,t,r,n,i)}),o.events.on(`end`,a=>{e.callEvent(`devframe:streaming:upload-end`,t,r,a),n.delete(i)}),n.set(i,o),o}return{subscribe:r,upload:i}}function xT(){}var ST=new Map;function CT(e){let t=e.url;e.authToken&&(t=`${t}?${vw}=${encodeURIComponent(e.authToken)}`);let n=new WebSocket(t),{onConnected:r=xT,onError:i=xT,onDisconnected:a=xT,definitions:o=ST}=e;n.addEventListener(`open`,e=>{r(e)}),n.addEventListener(`error`,e=>{let t=e instanceof Error?e:Error(e.type);i(t)}),n.addEventListener(`close`,e=>{a(e)});let s=$w(o);return{close:()=>{n.close()},on:e=>{n.addEventListener(`message`,t=>{e(t.data)})},post:e=>{if(n.readyState===WebSocket.OPEN){n.send(e);return}if(n.readyState===WebSocket.CONNECTING){let t=()=>{i(),n.readyState===WebSocket.OPEN&&n.send(e)},r=()=>i();function i(){n.removeEventListener(`open`,t),n.removeEventListener(`close`,r)}n.addEventListener(`open`,t),n.addEventListener(`close`,r);return}i(Error(`Devframe WebSocket is not open; message dropped`))},serialize:s.serialize,deserialize:s.deserialize}}function wT(e,t,n){let r=(()=>{try{return new URL(t,n.href)}catch{return new URL(n.href)}})(),i=r.protocol===`https:`?`wss:`:`ws:`;if(e&&typeof e==`object`){if(e.host!=null||e.port!=null){let t=e.host??`${r.hostname}:${e.port}`,n=new URL(e.path??`/`,`${i}//${t}`);return n.protocol=i,n.href}let t=new URL(e.path??``,r);return t.protocol=i,t.href}if(typeof e==`number`)return`${i}//${r.hostname}:${e}`;let a=e??``;if(/^wss?:\/\//i.test(a))return a;if(/^https?:\/\//i.test(a))return eS(a,/^https/i.test(a)?`wss://`:`ws://`);let o=new URL(a,r);return o.protocol=i,o.href}function TT(e){let{authToken:t,connectionMeta:n,metaBaseUrl:r,events:i,clientRpc:a,rpcOptions:o={},wsOptions:s={},callTimeout:c=0}=e,l=wT(n.websocket,r??`./`,location);return aT({transport:`websocket`,authToken:t,connectionMeta:n,events:i,clientRpc:a,rpcOptions:o,callTimeout:c,createChannel:e=>CT({url:l,authToken:t,definitions:e.definitions,...s,onConnected(e){s.onConnected?.(e)},onError(t){e.onError(t),s.onError?.(t)},onDisconnected(t){e.onDisconnected(),s.onDisconnected?.(t)}})})}function ET(e){return e.includes(`:`)}function DT(e,t){return ET(t)?t:`${e}:${t}`}function OT(e){return{async get(t){return(await e()).value()[t]},async set(t,n){(await e()).mutate(e=>{e[t]=n})},async delete(t){(await e()).mutate(e=>{delete e[t]})},async all(){return(await e()).value()},async onChange(t){return(await e()).on(`updated`,e=>t(e))}}}function kT(e,t,n){let r=`devframe:settings:${n}:${t}`,i;function a(){return i||=e.sharedState.get(r,{initialValue:{}}),i}return OT(a)}function AT(e,t){return{global:kT(e,t,`global`),project:kT(e,t,`project`)}}function jT(e,t){return{namespace:t,base:e,rpc:{namespace:t,register(n){if(ET(n.name))throw Error(`[devframe] Scoped client RPC registration for namespace "${t}" received an already-namespaced function name "${n.name}". Pass a bare name without a ":" separator.`);e.client.register({...n,name:`${t}:${n.name}`})},call:((n,...r)=>e.call(DT(t,n),...r)),callEvent:((n,...r)=>e.callEvent(DT(t,n),...r)),callOptional:((n,...r)=>e.callOptional(DT(t,n),...r)),sharedState:((n,r)=>e.sharedState.get(DT(t,n),r)),streaming:{subscribe:(n,r,i)=>e.streaming.subscribe(DT(t,n),r,i),upload:(n,r)=>e.streaming.upload(DT(t,n),r)}},settings:AT(e,t),scope:e.scope}}function MT(){if(typeof document<`u`){let e=document.modelContext;if(e)return e}if(typeof navigator<`u`){let e=navigator.modelContext;if(e)return e}}function NT(e,t={}){let n=t.modelContext??MT();if(!n)return()=>{};let r=n,i=new Map,a=new Map;function o(t,n){let o=sw(t.name),s=a.get(o);if(s&&s!==t.name){console.warn(`[devframe] WebMCP tool name "${o}" (from "${t.name}") collides with "${s}"; keeping the first registration.`);return}let c=new AbortController,l=Ox(t.type,n),u=r.registerTool({name:o,description:n.description,inputSchema:jx(t.args),annotations:{title:n.title??t.name,readOnlyHint:l===`read`,destructiveHint:l===`destructive`},execute:n=>PT(t,e.context,n)},{signal:c.signal});u&&`then`in u&&u.then(()=>{},()=>{}),a.set(o,t.name),i.set(t.name,()=>{c.abort(),u&&`unregister`in u&&typeof u.unregister==`function`&&u.unregister(),a.delete(o)})}function s(t){let n=t?[t]:[...e.definitions.keys()];for(let t of n){i.get(t)?.(),i.delete(t);let n=e.definitions.get(t),r=n?.agent;n&&r&&o(n,r)}}s();let c=e.onChanged(e=>s(e));return()=>{c();for(let e of i.values())e();i.clear()}}async function PT(e,t,n){try{let r=Nx(n,e.args?.length);return{content:[{type:`text`,text:FT(await(await Dw(e,t))(...r))}]}}catch(e){return{isError:!0,content:[{type:`text`,text:IT(e)}]}}}function FT(e){return e===void 0?`undefined`:typeof e==`string`?e:JSON.stringify(e,null,2)}function IT(e){if(!(e instanceof Error))return String(e);let t=e.cause instanceof Error?` (cause: ${e.cause.message})`:``;return`${e.name}: ${e.message}${t}`}function LT(e,t){if(t.backend===`static`)return`static`;let n=t.websocket!==void 0,r=t.sse!==void 0;if(e===`websocket`){if(!n)throw Error(`[devframe] transport: 'websocket' was requested, but this server does not advertise a WebSocket endpoint`);return`websocket`}if(e===`sse`){if(!r)throw Error(`[devframe] transport: 'sse' was requested, but this server does not advertise an SSE endpoint`);return`sse`}if(t.backend===`sse`&&r)return`sse`;if(n)return`websocket`;if(r)return`sse`;throw Error(`[devframe] This server advertises no RPC transport (backend "none"), so there is nothing to connect to. Enable the WebSocket or SSE endpoint on the server, or use its static/MCP surfaces instead.`)}async function RT(e={}){let{baseURL:t=`./`,rpcOptions:n={},cacheOptions:r=!1}=e,i=Jx(),a=Array.isArray(t)?t:[t],o=await Ww(e),{connectionMeta:s,metaBaseUrl:c,authToken:l}=o,u=a[0]??`./`;try{u=new URL(`.`,c).href}catch{}let d=new yw({functions:[],...typeof e.cacheOptions==`object`?e.cacheOptions:{}}),f={rpc:void 0},p=new Ow(f),m=e.webmcp===!1?void 0:NT(p),h,g=!1;async function _(e){let t=[u,...a.filter(e=>e!==u)].filter(e=>e!=null),n=[];for(let r of t)try{return await fetch($x(e,r)).then(t=>{if(!t.ok)throw Error(`Failed to fetch ${e} from ${r}: ${t.status}`);return t.json()})}catch(e){n.push(e)}throw Error(`Failed to load ${e} from ${t.join(`, `)}`,{cause:n})}let ee={authToken:l,connectionMeta:s,metaBaseUrl:c,events:i,clientRpc:p,callTimeout:e.callTimeout,rpcOptions:{...n,async onRequest(e,t,i){if(await n.onRequest?.call(this,e,t,i),r&&d?.validate(e.m)){if(d.has(e.m,e.a))return i(d.cached(e.m,e.a));let n=await t(e);d.apply(e,n)}else await t(e)}}},te=LT(e.transport??`auto`,s),ne=te===`static`?await _T({fetchJsonFromBases:_}):te===`sse`?sT({...ee,sseOptions:e.sseOptions}):TT({...ee,wsOptions:e.wsOptions}),re;try{re=new BroadcastChannel(`devframe-auth`)}catch{}let ie,ae=!1;function oe(e){return((...t)=>ae||!ie?e(...t):ie.then(()=>e(...t)))}function se(){g=!0;try{h?.(),m?.()}finally{try{re?.close()}finally{ne.close?.()}}}let ce={events:i,get isTrusted(){return ne.isTrusted},get status(){return ne.status},get connectionError(){return ne.connectionError},get transport(){return ne.transport??te},get connection(){return o},connectionMeta:s,ensureTrusted:ne.ensureTrusted,requestTrust:ne.requestTrust,requestTrustWithToken:async e=>(Bw(e),o={...o,authToken:e},ne.requestTrustWithToken(e)),requestTrustWithCode:async e=>{let t=await ne.requestTrustWithCode(e);if(!t)return!1;Bw(t),o={...o,authToken:t};try{re?.postMessage({type:`auth-update`,authToken:t})}catch{}return!0},requestAuthCode:e=>ne.requestAuthCode(e),call:oe(ne.call),callEvent:oe(ne.callEvent),callOptional:oe(ne.callOptional),client:p,sharedState:void 0,services:void 0,streaming:void 0,cacheManager:d,scope:void 0,close:se};ce.sharedState=Zw(ce),ce.streaming=bT(ce),ce.services=Xw(ce);let le=new Map;ce.scope=(e=>{if(!e)return ce;let t=le.get(e);return t||(t=jT(ce,e),le.set(e,t)),t}),f.rpc=ce;function ue(){try{return typeof window<`u`&&window.self===window.top}catch{return!1}}async function de(){if(e.simpleAuth!==!1&&ue()&&typeof globalThis.prompt==`function`)for(await ce.requestAuthCode().catch(()=>{});!ce.isTrusted;){let e=globalThis.prompt(`devframe: enter the authentication code shown in your terminal`);if(e==null)return;let t=e.trim();if(t&&await ce.requestTrustWithCode(t))return}}async function fe(){let t=await ne.requestTrust(),n=e.otpParam??`devframe_otp`,r=n?await Yw(ce,{param:n}):!1;t||r||ce.isTrusted||await de()}return ie=fe().then(()=>{ae=!0},()=>{ae=!0}),s.mcp&&dw(async()=>{let{setupBrowserAgentRpcBridge:e}=await import(`./browser-agent-rpc-BXhoSh1z-DUQ-j6wr.js`);return{setupBrowserAgentRpcBridge:e}},[],import.meta.url).then(({setupBrowserAgentRpcBridge:e})=>{g||(h=e(ce))}).catch(()=>{}),re&&(re.onmessage=e=>{e.data?.type===`auth-update`&&e.data.authToken&&ce.requestTrustWithToken(e.data.authToken)}),ce}var zT=RT,BT=[`components`,`injectors`,`signals`,`ngrx`,`forms`,`router`,`pipes`,`http`,`analog`,`capacitor`,`errors`],VT=[`angular`,`ngrx`,`analog`,`angular-native`,`nativescript`,`capacitor`];function HT(e){return`pangular:${e}`}var UT=[`forms`,`router`,`ngrx`,`http`,`analog`,`components`],WT={refreshMs:{default:3e3,min:500,max:8e3},navigations:{default:50,min:5,max:500},formTimeline:{default:200,min:10,max:2e3},httpCalls:{default:200,min:10,max:2e3},changeLog:{default:200,min:10,max:2e3},cdCycles:{default:200,min:10,max:2e3},capacitorCalls:{default:200,min:10,max:2e3}},GT={"opted-out":`the request sets transferCache: false`,post:`POST requests are left out unless includePostRequests is set`,method:`only GET, HEAD and POST requests are cached`,"auth-headers":`it sends an Authorization, Proxy-Authorization or Cookie header`,credentials:`it sends credentials (withCredentials or credentials: include/same-origin)`,"no-cache-request":`the request asks for no-store, no-cache or private`,"no-cache-response":`the response has Cache-Control no-store, no-cache or private`,"set-cookie":`the response sets a cookie`,error:`failed responses are not stored`,mocked:`a fault rule answered before the transfer cache ran`,"cache-off-or-filter":`the transfer cache is off, or its filter (or another withHttpTransferCacheOptions setting) left it out`},KT=[[200,`OK`],[201,`Created`],[202,`Accepted`],[203,`Non-Authoritative Information`],[204,`No Content`],[205,`Reset Content`],[206,`Partial Content`],[207,`Multi-Status`],[208,`Already Reported`],[226,`IM Used`],[300,`Multiple Choices`],[301,`Moved Permanently`],[302,`Found`],[303,`See Other`],[304,`Not Modified`],[307,`Temporary Redirect`],[308,`Permanent Redirect`],[400,`Bad Request`],[401,`Unauthorized`],[402,`Payment Required`],[403,`Forbidden`],[404,`Not Found`],[405,`Method Not Allowed`],[406,`Not Acceptable`],[407,`Proxy Authentication Required`],[408,`Request Timeout`],[409,`Conflict`],[410,`Gone`],[411,`Length Required`],[412,`Precondition Failed`],[413,`Content Too Large`],[414,`URI Too Long`],[415,`Unsupported Media Type`],[416,`Range Not Satisfiable`],[417,`Expectation Failed`],[418,`I'm a Teapot`],[421,`Misdirected Request`],[422,`Unprocessable Content`],[423,`Locked`],[424,`Failed Dependency`],[425,`Too Early`],[426,`Upgrade Required`],[428,`Precondition Required`],[429,`Too Many Requests`],[431,`Request Header Fields Too Large`],[451,`Unavailable For Legal Reasons`],[499,`Client Closed Request`],[500,`Internal Server Error`],[501,`Not Implemented`],[502,`Bad Gateway`],[503,`Service Unavailable`],[504,`Gateway Timeout`],[505,`HTTP Version Not Supported`],[506,`Variant Also Negotiates`],[507,`Insufficient Storage`],[508,`Loop Detected`],[510,`Not Extended`],[511,`Network Authentication Required`],[520,`Web Server Returned an Unknown Error`],[521,`Web Server Is Down`],[522,`Connection Timed Out`],[523,`Origin Is Unreachable`],[524,`A Timeout Occurred`]];function qT(e){return KT.some(([t])=>t===e)}var JT=`pangular`,YT={mod:`Mod`,ctrl:`Ctrl`,control:`Ctrl`,meta:`Meta`,cmd:`Meta`,command:`Meta`,alt:`Alt`,option:`Alt`,opt:`Alt`,shift:`Shift`},XT=[`Mod`,`Ctrl`,`Meta`,`Alt`,`Shift`];function ZT(e){if(typeof e!=`string`||e.length>40)return;let t=e.split(`+`).map(e=>e.trim());if(t.some(e=>!e))return;let n=new Set,r;for(let e of t){let t=YT[e.toLowerCase()];if(t){if(n.has(t))return;n.add(t);continue}if(r!==void 0)return;if(/^[a-z0-9]$/i.test(e))r=e.toUpperCase();else if(/^f([1-9]|1[0-2])$/i.test(e))r=e.toUpperCase();else return}if(r&&[...n].some(e=>e!==`Shift`)&&!(n.has(`Mod`)&&(n.has(`Ctrl`)||n.has(`Meta`))))return[...XT.filter(e=>n.has(e)),r].join(`+`)}function QT(e,t,n){let r=t.split(`+`),i=r.pop(),a=new Set(r),o=a.has(`Ctrl`)||a.has(`Mod`)&&!n,s=a.has(`Meta`)||a.has(`Mod`)&&n;if(e.ctrlKey!==o||e.metaKey!==s||e.altKey!==a.has(`Alt`)||e.shiftKey!==a.has(`Shift`))return!1;if(/^F\d+$/.test(i))return e.key.toUpperCase()===i;let c=/^\d$/.test(i)?`Digit${i}`:`Key${i}`;return e.code?e.code===c:e.key.toUpperCase()===i}function $T(e,t){if(!t)return e.replace(/^Mod\b/,`Ctrl`);let n={Mod:`⌘`,Meta:`⌘`,Ctrl:`⌃`,Alt:`⌥`,Shift:`⇧`};return e.split(`+`).map(e=>n[e]??e).join(``)}var eE=[{id:`dashboard`,label:`Dashboard`},{id:`timeline`,label:`Timeline`},{id:`analog`,label:`Analog`,inspector:`analog`},{id:`components`,label:`Components`,inspector:`components`},{id:`routes`,label:`Routes`,inspector:`router`},{id:`signals`,label:`Signals`,inspector:`signals`},{id:`injectors`,label:`Injectors`,inspector:`injectors`},{id:`store`,label:`Store`,inspector:`ngrx`},{id:`forms`,label:`Forms`,inspector:`forms`},{id:`pipes`,label:`Pipes`,inspector:`pipes`},{id:`network`,label:`SSR & HTTP`,inspector:`http`},{id:`errors`,label:`Errors`,inspector:`errors`},{id:`capacitor`,label:`Capacitor`,inspector:`capacitor`}];function tE(e){return eE.filter(t=>!(`inspector`in t)||e.inspectors[t.inspector])}var nE={components:`a component instance, by its id in the component tree`,injectors:`a component's element injector, by the component's id`,forms:`a form, by its id`},rE=200;function iE(e){let t=new URLSearchParams({tab:e.tab});return e.id&&t.set(`id`,e.id),e.page&&t.set(`page`,e.page),`#${t.toString()}`}function aE(e){let t=new URLSearchParams(e.replace(/^#/,``)),n=e=>{let n=t.get(e)?.trim();return n&&n.length<=rE?n:void 0},r=n(`tab`);if(!r)return null;let i=n(`id`),a=n(`page`);return{tab:r,...i?{id:i}:{},...a?{page:a}:{}}}var oE=[{id:`settings`,title:`Open settings`,group:`Go to`,keywords:`preferences tabs`},{id:`pick-component`,title:`Pick component on page`,group:`Actions`,keywords:`select inspect element`,inspector:`components`},{id:`cd-record-start`,title:`Start recording change detection`,group:`Actions`,keywords:`profiler cycles`,inspector:`components`},{id:`cd-record-stop`,title:`Stop recording change detection`,group:`Actions`,keywords:`profiler cycles`,inspector:`components`},{id:`cd-clear`,title:`Clear change detection recording`,group:`Actions`,keywords:`reset recordings cycles`,inspector:`components`},{id:`theme:system`,title:`Theme: follow the system`,group:`Theme`,keywords:`auto`},{id:`theme:light`,title:`Theme: light`,group:`Theme`},{id:`theme:dark`,title:`Theme: dark`,group:`Theme`},{id:`dock:float`,title:`Popup: float`,group:`Popup`,keywords:`dock`,popupOnly:!0},{id:`dock:bottom`,title:`Popup: dock to the bottom`,group:`Popup`,keywords:`dock`,popupOnly:!0},{id:`dock:right`,title:`Popup: dock to the right`,group:`Popup`,keywords:`dock`,popupOnly:!0},{id:`dock:reset-size`,title:`Popup: reset the docked size`,group:`Popup`,keywords:`splitter resize height width`,popupOnly:!0},{id:`popout`,title:`Popup: open in its own window`,group:`Popup`,keywords:`pop out detach new window`,popupOnly:!0},{id:`popout:return`,title:`Popup: back into the page`,group:`Popup`,keywords:`pop in attach dock window`,popupOnly:!0}];function sE(e){let t=tE(e).map(e=>({id:`tab:${e.id}`,title:`Go to ${e.label}`,group:`Go to`,keywords:`tab open`})),n=oE.filter(t=>!t.inspector||e.inspectors[t.inspector]).map(({inspector:e,...t})=>t);return[...t,...n]}function cE(e){return e&&typeof e==`object`&&!Array.isArray(e)?e:{}}function lE(e,t){return typeof e==`boolean`?e:t}var uE=100,dE=100;function fE(e){if(!Array.isArray(e))return[];let t=e.filter(e=>typeof e==`string`).map(e=>e.trim()).filter(e=>e&&e.length<=dE);return[...new Set(t)].slice(0,uE)}function pE(e,t){let{default:n,min:r,max:i}=WT[t];return typeof e!=`number`||!Number.isFinite(e)?n:Math.min(i,Math.max(r,Math.round(e)))}function mE(e,t){return Object.fromEntries(e.map(e=>[e,t(e)]))}function hE(e){let t=cE(e),n=cE(t.agent),r=cE(t.inspectors),i=cE(n.tools),a=cE(t.actions),o=lE(t.actions,!0),s=cE(t.redaction),c=cE(t.limits),l=mE(BT,e=>lE(r[e],!0));return{inspectors:l,agent:{readOnly:lE(n.readOnly,!1),tools:mE(BT,e=>l[e]&&lE(i[e],!0))},actions:mE(UT,e=>l[e]&&lE(a[e],o)),redaction:{secretNames:fE(s.secretNames),unmask:fE(s.unmask)},limits:Object.fromEntries(Object.keys(WT).map(e=>[e,pE(c[e],e)])),popup:{hotkey:gE(cE(t.popup).hotkey)}}}function gE(e){return e===!1?!1:ZT(e)??`Alt+Shift+P`}function _E(e){let t=e?.configs;return hE(t?.[JT])}function vE(e){return`${{forms:`Writing to forms`,router:`Navigating and clearing navigations`,ngrx:`Restoring NgRx state and dispatching actions`,http:`Changing HTTP mock rules and clearing HTTP calls`,analog:`Calling API routes and clearing server calls`,components:`Editing component properties and signals`}[e]} is turned off in the devtools config (actions.${e}).`}var yE={components:`Components`,injectors:`Injectors`,signals:`Signals`,ngrx:`NgRx`,forms:`Forms`,router:`Router`,pipes:`Pipes`,http:`HTTP`,analog:`Analog`,capacitor:`Capacitor`,errors:`Errors`},bE={forms:`Form writes`,router:`Navigation`,ngrx:`NgRx restore and dispatch`,http:`HTTP mocking`,analog:`Analog API calls`,components:`Component edits`},xE={refreshMs:e=>`poll every ${e} ms`,navigations:e=>`${e} navigations`,formTimeline:e=>`${e} form events`,httpCalls:e=>`${e} HTTP calls`,changeLog:e=>`${e} NgRx changes`,cdCycles:e=>`${e} change detection cycles`,capacitorCalls:e=>`${e} Capacitor plugin calls`};function SE(e){let t=[],n=(e,n)=>{n.length&&t.push({label:e,value:n.join(`, `)})},r=BT.filter(t=>e.inspectors[t]);return n(`Inspectors off`,BT.filter(t=>!e.inspectors[t]).map(e=>yE[e])),n(`Agent`,e.agent.readOnly?[`Read-only`]:[]),n(`Hidden from the agent`,r.filter(t=>!e.agent.tools[t]).map(e=>yE[e])),n(`Blocked actions`,UT.filter(t=>e.inspectors[t]&&!e.actions[t]).map(e=>bE[e])),n(`Extra secret names`,e.redaction.secretNames),n(`Unmasked names`,e.redaction.unmask),n(`Limits`,Object.keys(WT).filter(t=>e.limits[t]!==WT[t].default).map(t=>xE[t](e.limits[t]))),e.popup.hotkey!==`Alt+Shift+P`&&n(`Popup shortcut`,[e.popup.hotkey===!1?`Off`:e.popup.hotkey]),t}function CE(e){let t={tags:[],directives:[],text:``},n=e.toLowerCase();return n=n.replace(/<\s*([^<>\s]+)\s*>/g,(e,n)=>(t.tags.push({value:n,exact:!0}),` `)),n=n.replace(/\[\s*([^[\]\s]+)\s*\]/g,(e,n)=>(t.directives.push({value:n,exact:!0}),` `)),n=n.replace(/(^|\s)([<[])([^<>[\]\s]*)$/,(e,n,r,i)=>(i&&(r===`<`?t.tags:t.directives).push({value:i,exact:!1}),n)),t.text=n.replace(/\s+/g,` `).trim(),t}function wE(e){return!e.tags.length&&!e.directives.length&&!e.text}function TE(e,t){let n=e.tag.toLowerCase(),r=(e.directives??[]).map(e=>e.toLowerCase()),i=(e.attributes??[]).map(e=>e.toLowerCase()),a=[...r,...r.map(e=>e.replace(/directive$/,``)),...i];for(let{value:e,exact:r}of t.tags)if(r?n!==e:!n.startsWith(e))return!1;for(let{value:e,exact:n}of t.directives)if(!a.some(t=>n?t===e:t.includes(e)))return!1;if(t.text){let a=t.text;if(!(e.name.toLowerCase().includes(a)||n.includes(a)||r.some(e=>e.includes(a))||i.some(e=>e.includes(a))))return!1}return!0}var EE=`pangular-page-id`,DE=null;function OE(e){DE=e}function kE(){if(DE)return DE;try{return new URLSearchParams(location.search).get(`pageId`)||(window.top===window?null:sessionStorage.getItem(EE)||null)}catch{return null}}function AE(e,t){if(t.chosen&&e[t.chosen])return t.chosen;if(t.host)return t.host;if(t.previous&&e[t.previous])return t.previous;let n=null;for(let t of Object.values(e))(!n||t.reportedAt>n.reportedAt)&&(n=t);return n?.pageId??null}function jE(e,t){return e?t?e.pages?.[t]??null:e:null}function ME(e,t){return e?t?e.pages?.[t]??null:e.graph??null:null}function NE(e,t,n){return e?e.scope(`pangular`).rpc.call(t,...n===void 0?[]:[n]):Promise.resolve(null)}function PE(e,t,n){return NE(e,t,n).then(e=>e,()=>null)}function FE(e){return e?.connectionMeta.backend===`static`}function IE(e){let t=()=>{let t=e()?.scope(`pangular`).rpc;t&&(t.callEvent(`request-page-highlight`,null),t.callEvent(`request-form-highlight`,null))};return addEventListener(`pagehide`,t),()=>{removeEventListener(`pagehide`,t),t()}}function LE(e){return e.split(`@`)[1]??``}var RE={signal:`Signal Forms`,reactive:`Reactive`,template:`Template-driven`},zE={own:`validator`,directive:`template attribute`,tree:`cross-field rule`,async:`async`,parse:`parse`,submission:`server`,schema:`schema`,manual:`setErrors`},BE=PE,VE={key:`name looks secret`,"input-type":`password input`,autocomplete:`autocomplete is a secret kind`,marker:`marked as mask`,parent:`inside a secret group`,config:`listed in mask`};function HE(e){return VE[e]??e}var UE=`https://pangular-inspector.dev/security/#opt-fields-in-or-out`;async function WE(e,t){return await BE(e,`request-form-action`,t)??{ok:!1,error:`The devtools server did not answer.`}}function GE(e){return`${e.ok?e.message??`Done.`:e.error??`Failed.`}${e.skipped?.length?` Skipped: ${e.skipped.map(e=>`${e.path} (${e.reason})`).join(`, `)}.`:``}${e.status?` Status: ${e.status}.`:``}`}var KE=/"(?:[^"\\\n]|\\.)*"|`([^`\n]*)`|\*\*/g;function qE(e){return(e??``).replace(/^_Labels, paths.*_\n\n/,``).replace(KE,(e,t)=>t??(e===`**`?``:e))}function JE(e){return _E(e?.connectionMeta)}function YE(e,t){return JE(e).actions[t]}var XE={components:`components`,routes:`router`,signals:`signals`,injectors:`injectors`,store:`ngrx`,forms:`forms`,pipes:`pipes`,network:`http`,analog:`analog`,capacitor:`capacitor`,errors:`errors`};function ZE(e){return XE[e]}var QE=`https://pangular-inspector.dev/getting-started/configuration/#inspectors-and-agent-tools`;function $E(e,t){let n=XE[e];return!n||t.inspectors[n]}function eD(e,t){e&1&&(O(),Vg(0,`rect`,4)(1,`rect`,5)(2,`rect`,6)(3,`rect`,7))}function tD(e,t){e&1&&(O(),Vg(0,`path`,8)(1,`circle`,9)(2,`circle`,10)(3,`path`,11))}function nD(e,t){e&1&&(O(),Vg(0,`path`,12)(1,`path`,13)(2,`path`,14))}function rD(e,t){e&1&&(O(),Vg(0,`circle`,15)(1,`path`,16)(2,`circle`,17))}function iD(e,t){e&1&&(O(),Vg(0,`path`,1))}function aD(e,t){e&1&&(O(),Vg(0,`path`,18)(1,`path`,19)(2,`path`,20)(3,`path`,21))}function oD(e,t){e&1&&(O(),Vg(0,`ellipse`,22)(1,`path`,23)(2,`path`,24))}function sD(e,t){e&1&&(O(),Vg(0,`rect`,25)(1,`path`,26)(2,`path`,27)(3,`path`,28))}function cD(e,t){e&1&&(O(),Vg(0,`circle`,29)(1,`path`,8)(2,`path`,30))}function lD(e,t){e&1&&(O(),Vg(0,`path`,31)(1,`path`,32)(2,`path`,33)(3,`path`,34)(4,`circle`,35))}function uD(e,t){e&1&&(O(),Vg(0,`circle`,36)(1,`path`,37))}function dD(e,t){e&1&&(O(),Vg(0,`circle`,29)(1,`path`,38)(2,`path`,39))}function fD(e,t){e&1&&(O(),Vg(0,`path`,2))}function pD(e,t){e&1&&(O(),Vg(0,`path`,3))}var mD=class e{name=$.required();static ɵfac=function(t){return new(t||e)};static ɵcmp=Kh({type:e,selectors:[[`app-tab-icon`]],inputs:{name:[1,`name`]},decls:15,vars:1,consts:[[`viewBox`,`0 0 24 24`,`width`,`16`,`height`,`16`,`fill`,`none`,`stroke`,`currentColor`,`stroke-width`,`1.8`,`stroke-linecap`,`round`,`stroke-linejoin`,`round`,`aria-hidden`,`true`],[`d`,`M22 12h-4l-3 9L9 3l-3 9H2`],[`d`,`m8 3 4 8 5-5 5 15H2L8 3z`],[`fill`,`currentColor`,`stroke`,`none`,`transform`,`translate(1.8 1.8) scale(0.85)`,`d`,`M24 3.7l-5.766 5.766 5.725 5.736-3.713 3.712L5.073 3.742 8.786.03l5.736 5.726L20.284 0 24 3.7zM.029 8.785l3.713-3.713 15.173 15.173-3.713 3.714-5.732-5.726L3.7 24 0 20.285l5.754-5.764L.029 8.785z`],[`x`,`3`,`y`,`3`,`width`,`7`,`height`,`9`,`rx`,`1.5`],[`x`,`14`,`y`,`3`,`width`,`7`,`height`,`5`,`rx`,`1.5`],[`x`,`14`,`y`,`12`,`width`,`7`,`height`,`9`,`rx`,`1.5`],[`x`,`3`,`y`,`16`,`width`,`7`,`height`,`5`,`rx`,`1.5`],[`d`,`M3 12h18`],[`cx`,`7`,`cy`,`12`,`r`,`2`],[`cx`,`14`,`cy`,`12`,`r`,`2`],[`d`,`M18 7v10`],[`d`,`M12 2 3 7l9 5 9-5-9-5Z`],[`d`,`m3 12 9 5 9-5`],[`d`,`m3 17 9 5 9-5`],[`cx`,`6`,`cy`,`19`,`r`,`3`],[`d`,`M9 19h8.5a3.5 3.5 0 0 0 0-7h-11a3.5 3.5 0 0 1 0-7H15`],[`cx`,`18`,`cy`,`5`,`r`,`3`],[`d`,`M12 22v-5`],[`d`,`M9 8V2`],[`d`,`M15 8V2`],[`d`,`M18 8v5a4 4 0 0 1-4 4h-4a4 4 0 0 1-4-4V8Z`],[`cx`,`12`,`cy`,`5`,`rx`,`9`,`ry`,`3`],[`d`,`M3 5v14a9 3 0 0 0 18 0V5`],[`d`,`M3 12a9 3 0 0 0 18 0`],[`x`,`3`,`y`,`4`,`width`,`18`,`height`,`16`,`rx`,`2`],[`d`,`M7 9h10`],[`d`,`M7 13h6`],[`d`,`M7 17h8`],[`cx`,`12`,`cy`,`12`,`r`,`9`],[`d`,`M12 3a14 14 0 0 1 0 18a14 14 0 0 1 0-18Z`],[`d`,`M4 7h10`],[`d`,`M4 17h6`],[`d`,`m14 13 4 4-4 4`],[`d`,`M10 17h8`],[`cx`,`18`,`cy`,`7`,`r`,`3`],[`cx`,`12`,`cy`,`12`,`r`,`3`],[`d`,`M19.4 15a1.7 1.7 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-1.8-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 1 1-4 0v-.1a1.7 1.7 0 0 0-1.1-1.5 1.7 1.7 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.7 1.7 0 0 0 .3-1.8 1.7 1.7 0 0 0-1.5-1H3a2 2 0 1 1 0-4h.1a1.7 1.7 0 0 0 1.5-1.1 1.7 1.7 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.7 1.7 0 0 0 1.8.3H9a1.7 1.7 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0-.3 1.8V9a1.7 1.7 0 0 0 1.5 1H21a2 2 0 1 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1Z`],[`d`,`M12 7v6`],[`d`,`M12 16.5v.01`]],template:function(e,t){if(e&1&&(O(),B(0,`svg`,0),M(1,eD,4,0)(2,tD,4,0)(3,nD,3,0)(4,rD,3,0)(5,iD,1,0,`:svg:path`,1)(6,aD,4,0)(7,oD,3,0)(8,sD,4,0)(9,cD,3,0)(10,lD,5,0)(11,uD,2,0)(12,dD,3,0)(13,fD,1,0,`:svg:path`,2)(14,pD,1,0,`:svg:path`,3),V()),e&2){let e;A(),N((e=t.name())===`dashboard`?1:e===`timeline`?2:e===`components`?3:e===`routes`?4:e===`signals`?5:e===`injectors`?6:e===`store`?7:e===`forms`?8:e===`network`?9:e===`pipes`?10:e===`settings`?11:e===`errors`?12:e===`analog`?13:e===`capacitor`?14:-1)}},styles:[`[_nghost-%COMP%] { display: inline-flex; flex-shrink: 0; -}`]})},mD=(e,t)=>t.tab,hD=(e,t)=>t.label;function gD(e,t){e&1&&J(0),e&2&&X(` `,G().meta()?.projectName,` `)}function _D(e,t){e&1&&J(0,` Project details unavailable `)}function vD(e,t){e&1&&J(0,` Loading… `)}function yD(e,t){e&1&&(J(0,` Run `),L(1,`code`),J(2,`pangular build`),R(),J(3,` again to rebuild the report. `))}function bD(e,t){e&1&&J(0,` Check that the dev server is running. `)}function xD(e,t){if(e&1){let e=H();L(0,`button`,12),U(`click`,function(){return E(e),D(G(2).loadMeta())}),J(1,`Try again`),R()}}function SD(e,t){if(e&1&&(L(0,`p`,10),M(1,yD,4,0)(2,bD,1,0),R(),M(3,xD,2,0,`button`,11)),e&2){let e=G();A(),N(e.staticReport()?1:2),A(2),N(e.staticReport()?-1:3)}}function CD(e,t){e&1&&(L(0,`li`,5)(1,`span`),J(2,`Analog`),R(),J(3),R()),e&2&&(A(3),Y(t))}function wD(e,t){e&1&&(L(0,`li`)(1,`span`),J(2,`Change detection`),R(),J(3),R()),e&2&&(A(3),Y(t))}function TD(e,t){if(e&1&&(L(0,`span`,19),J(1),R(),L(2,`span`,20),J(3),R()),e&2){let e=G().$implicit,t=G();A(),Y(t.cards()[e.tab]?.value),A(2),Y(t.cards()[e.tab]?.sub)}}function ED(e,t){e&1&&(L(0,`span`,21),J(1,`–`),R(),L(2,`span`,20),J(3,`Count unavailable`),R())}function DD(e,t){e&1&&(z(0,`span`,22),L(1,`span`,20),J(2,`Counting…`),R())}function OD(e,t){if(e&1){let e=H();L(0,`button`,13),U(`click`,function(){let t=E(e).$implicit;return D(G().navigate.emit(t.tab))}),L(1,`span`,14)(2,`span`,15),z(3,`app-tab-icon`,16),R(),L(4,`span`,17),J(5,`→`),R()(),L(6,`span`,18),J(7),R(),M(8,TD,4,2)(9,ED,4,0)(10,DD,3,0),R()}if(e&2){let e,n=t.$implicit,r=t.$index,i=G().stateOf(n.tab);K(`animation-delay`,60*r,`ms`),j(`aria-busy`,i===`loading`),A(3),I(`name`,n.tab),A(4),Y(n.label),A(),N((e=i)===`ready`?8:e===`error`?9:10)}}function kD(e,t){e&1&&(L(0,`p`),J(1,`Loading…`),R())}function AD(e,t){if(e&1&&(L(0,`div`)(1,`dt`),J(2),R(),L(3,`dd`),J(4),R()()),e&2){let e=t.$implicit;A(2),Y(e.label),A(2),Y(e.value)}}function jD(e,t){if(e&1&&(L(0,`dl`),P(1,AD,5,2,`div`,null,hD),R()),e&2){let e=G();A(),F(e.configItems())}}function MD(e,t){e&1&&(L(0,`p`),J(1,`Defaults`),R())}var ND=[{tab:`components`,label:`Components`},{tab:`routes`,label:`Routes`},{tab:`signals`,label:`Signals`},{tab:`injectors`,label:`Injectors`},{tab:`store`,label:`NgRx declarations`},{tab:`pipes`,label:`Pipes`},{tab:`forms`,label:`Forms`},{tab:`network`,label:`SSR & HTTP`}];function PD(e){return ND.filter(t=>$E(t.tab,e))}var FD={zoneless:`Zoneless`,zone:`zone.js`,"zone-unused":`Zoneless, zone.js loaded`};function ID(e){return e&&Object.hasOwn(FD,e)?FD[e]:null}var LD=new Set([`signal`,`computed`,`linkedSignal`,`effect`]),RD={action:[`action`,`actions`],reducer:[`reducer`,`reducers`],effect:[`effect`,`effects`],selector:[`selector`,`selectors`],feature:[`feature`,`features`],"store-setup":[`setup call`,`setup calls`],"signal-store":[`signal store`,`signal stores`],"signal-state":[`signal state`,`signal states`],"signal-method":[`signal method`,`signal methods`]};function zD(e,t,n){return`${e} ${e===1?t:n}`}function BD(e,t){for(let n of e??[])t(n),BD(n.children,t)}function VD(e){if(!e.some(e=>e.kind))return{value:e.length,sub:`discovered in source`};let t=e.filter(e=>e.kind===`directive`).length;return{value:e.length-t,sub:`components · ${zD(t,`directive`,`directives`)}`}}function HD(e){if(!e.some(e=>e.kind))return{value:e.length,sub:`route entries in source`};let t=e.filter(e=>e.kind===`redirect`).length;return{value:new Set(e.filter(e=>e.kind===`page`).map(e=>e.fullPath??e.path??``)).size,sub:`navigable paths · ${zD(t,`redirect`,`redirects`)}`}}function UD(e){let t=new Map;for(let n of e)n.kind&&t.set(n.kind,(t.get(n.kind)??0)+1);if(!t.size)return{value:e.length,sub:`declarations in source`};let n=[...t].sort((e,t)=>t[1]-e[1]).map(([e,t])=>{let[n,r]=RD[e]??[e,e];return zD(t,n,r)});return{value:e.length,sub:n.join(` · `)}}function WD(e,t){let n=e?.forms??[],r=t?n.filter(e=>LE(e.id)===t):n;if(!r.length)return{value:0,sub:`no forms on the page yet`};let i=r.filter(e=>e.root?.status===`INVALID`).length;return{value:r.length,sub:`${r.length===1?`form`:`forms`} · ${i} invalid`}}function GD(e){return e.cancelled||e.status===void 0?!1:e.status===0||e.status>=400}function KD(e,t){let n=e?.pages??[],r=t?n.find(e=>e.pageId===t)??null:n.reduce((e,t)=>!e||t.reportedAt>e.reportedAt?t:e,null),i=t&&!r?[]:e?.serverCalls??[],a=r?.initialUrl,o=[...a?i.filter(e=>!e.pageUrl||e.pageUrl===a):i,...r?.calls??[]];if(!o.length)return{value:0,sub:`no calls recorded yet`};let s=o.filter(GD).length;return{value:o.length,sub:`${o.length===1?`call`:`calls`} · ${s} failed`}}var qD=class e{rpc=$(null);navigate=Rv();staticReport=Q(()=>FE(this.rpc()));meta=k(null);config=Q(()=>JE(this.rpc()));stats=Q(()=>this.rpc()?PD(this.config()):[]);configItems=Q(()=>SE(this.config()));metaState=k(`loading`);states=k({});rows=k({});injectorTree=k(null);signalGraph=k(null);formsState=k(null);httpState=k(null);pageId=kE();destroyRef=w(Es);stopLive=[];zone=Q(()=>ID(jE(this.injectorTree(),this.pageId)?.zone));liveInjectors=Q(()=>{let e=jE(this.injectorTree(),this.pageId);if(!e?.roots?.length)return null;let t=0,n=0,r=e=>{t++,n+=e.providers?.length??0};return BD(e.roots,r),BD(e.environment,r),{injectors:t,providers:n}});liveSignals=Q(()=>{let e=ME(this.signalGraph(),this.pageId);return e?.nodes?.length?e.nodes.filter(e=>LD.has(e.kind??``)).length:null});cards=Q(()=>{let e=this.rows(),t={};if(e.components&&(t.components=VD(e.components)),e.routes&&(t.routes=HD(e.routes)),e.store&&(t.store=UD(e.store)),e.pipes){let n=e.pipes.filter(e=>e.builtin).length;t.pipes={value:e.pipes.length-n,sub:n?`custom pipes · ${n} built-in in use`:`custom pipes in source`}}let n=this.liveSignals();n===null?e.signals&&(t.signals={value:e.signals.length,sub:`signal declarations in source`}):t.signals={value:n,sub:e.signals?`live on the page · ${e.signals.length} declared in source`:`live on the page`};let r=this.liveInjectors();r?t.injectors={value:r.injectors,sub:`live injectors · ${zD(r.providers,`provider`,`providers`)}`}:e.injectors&&(t.injectors={value:e.injectors.filter(e=>e.type!==`injection`).length,sub:`provider declarations in source`});let i=this.formsState();i&&(t.forms=WD(i,this.pageId));let a=this.httpState();return a&&(t.network=KD(a,this.pageId)),t});loadMeta(e=this.rpc()){e&&(this.metaState.set(`loading`),e.scope(`pangular`).rpc.call(`build-meta`).then(t=>{this.rpc()===e&&(this.meta.set(t),this.metaState.set(`ready`))}).catch(()=>{this.rpc()===e&&this.metaState.set(`error`)}))}constructor(){pc(()=>{let e=this.rpc();if(!e)return;let t=e.scope(`pangular`);this.states.set({}),this.rows.set({}),this.formsState.set(null),this.httpState.set(null),this.loadMeta(e);let n=JE(e),r=e=>$E(e,n);r(`components`)&&this.load(t.rpc.call(`get-components`),`components`),r(`routes`)&&this.load(t.rpc.call(`get-routes`),`routes`),r(`signals`)&&this.load(t.rpc.call(`get-signals`),`signals`),r(`injectors`)&&this.load(t.rpc.call(`get-providers`),`injectors`),r(`store`)&&this.load(t.rpc.call(`get-ngrx-store`),`store`),r(`pipes`)&&this.load(t.rpc.call(`get-pipes`),`pipes`),this.watchLive(e,n)}),this.destroyRef.onDestroy(()=>this.unwatch())}stateOf(e){return(e===`signals`?this.liveSignals():e===`injectors`?this.liveInjectors():null)===null?this.states()[e]??`loading`:`ready`}load(e,t){let n=e=>this.states.update(n=>({...n,[t]:e}));e.then(e=>{this.rows.update(n=>({...n,[t]:Array.isArray(e)?e:[]})),n(`ready`)}).catch(()=>n(`error`))}async watchLive(e,t){this.unwatch();let n=e.scope(`pangular`).rpc,r=async(t,r,i)=>{let a=e=>i&&this.states.update(t=>({...t,[i]:e}));try{let i=await n.sharedState(t);if(this.destroyRef.destroyed||this.rpc()!==e)return;r(i.value()),a(`ready`),this.stopLive.push(i.on(`updated`,e=>r(e)))}catch{if(this.destroyRef.destroyed||this.rpc()!==e)return;r(null),a(`error`)}};await Promise.all([t.inspectors.injectors&&r(`injector-tree`,e=>this.injectorTree.set(e)),t.inspectors.signals&&r(`signal-graph`,e=>this.signalGraph.set(e)),$E(`forms`,t)&&r(`forms`,e=>this.formsState.set(e??{}),`forms`),$E(`network`,t)&&r(`http`,e=>this.httpState.set(e??{}),`network`)])}unwatch(){for(let e of this.stopLive.splice(0))e()}static ɵfac=function(t){return new(t||e)};static ɵcmp=Kh({type:e,selectors:[[`app-dashboard`]],inputs:{rpc:[1,`rpc`]},outputs:{navigate:`navigate`},decls:33,vars:14,consts:[[`aria-labelledby`,`project-title`,1,`project`],[1,`project-head`],[1,`eyebrow`],[`id`,`project-title`],[1,`chips`],[1,`analog`],[1,`grid`],[`type`,`button`,1,`stat`,3,`animation-delay`],[`aria-labelledby`,`config-title`,1,`config`],[`id`,`config-title`],[1,`hint`],[`type`,`button`,1,`retry`],[`type`,`button`,1,`retry`,3,`click`],[`type`,`button`,1,`stat`,3,`click`],[1,`top`],[1,`icon`],[3,`name`],[`aria-hidden`,`true`,1,`go`],[1,`label`],[1,`big`],[1,`sub`],[1,`big`,`muted`],[`aria-hidden`,`true`,1,`big`,`skeleton`]],template:function(e,t){if(e&1&&(L(0,`section`,0)(1,`div`,1)(2,`p`,2),J(3,`Project`),R(),L(4,`h2`,3),M(5,gD,1,1)(6,_D,1,0)(7,vD,1,0),R(),M(8,SD,4,2),R(),L(9,`ul`,4)(10,`li`)(11,`span`),J(12,`Angular`),R(),J(13),R(),L(14,`li`)(15,`span`),J(16,`TypeScript`),R(),J(17),R(),L(18,`li`)(19,`span`),J(20,`SSR`),R(),J(21),R(),M(22,CD,4,1,`li`,5),M(23,wD,4,1,`li`),R()(),L(24,`div`,6),P(25,OD,11,6,`button`,7,mD),R(),L(27,`section`,8)(28,`h2`,9),J(29,`Configuration`),R(),M(30,kD,2,0,`p`)(31,jD,3,0,`dl`)(32,MD,2,0,`p`),R()),e&2){let e,n,r;j(`aria-busy`,t.metaState()===`loading`),A(4),q(`muted`,t.metaState()!==`ready`),A(),N((e=t.metaState())===`ready`?5:e===`error`?6:7),A(3),N(t.metaState()===`error`?8:-1),A(),q(`pending`,t.metaState()===`loading`),A(4),Y(t.meta()?.angularVersion??`…`),A(4),Y(t.meta()?.typescript??`…`),A(4),Y(t.meta()?t.meta()?.ssr?`On`:`Off`:`…`),A(),N((n=t.meta()?.analog)?22:-1,n),A(),N((r=t.zone())?23:-1,r),A(2),F(t.stats()),A(2),j(`aria-busy`,!t.rpc()),A(3),N(t.rpc()?t.configItems().length?31:32:30)}},dependencies:[pD],styles:[`[_nghost-%COMP%] { +}`]})},hD=(e,t)=>t.tab,gD=(e,t)=>t.label;function _D(e,t){e&1&&J(0),e&2&&X(` `,G().meta()?.projectName,` `)}function vD(e,t){e&1&&J(0,` Project details unavailable `)}function yD(e,t){e&1&&J(0,` Loading… `)}function bD(e,t){e&1&&(J(0,` Run `),L(1,`code`),J(2,`pangular build`),R(),J(3,` again to rebuild the report. `))}function xD(e,t){e&1&&J(0,` Check that the dev server is running. `)}function SD(e,t){if(e&1){let e=H();L(0,`button`,12),U(`click`,function(){return E(e),D(G(2).loadMeta())}),J(1,`Try again`),R()}}function CD(e,t){if(e&1&&(L(0,`p`,10),M(1,bD,4,0)(2,xD,1,0),R(),M(3,SD,2,0,`button`,11)),e&2){let e=G();A(),N(e.staticReport()?1:2),A(2),N(e.staticReport()?-1:3)}}function wD(e,t){e&1&&(L(0,`li`,5)(1,`span`),J(2,`Analog`),R(),J(3),R()),e&2&&(A(3),Y(t))}function TD(e,t){e&1&&(L(0,`li`)(1,`span`),J(2,`Change detection`),R(),J(3),R()),e&2&&(A(3),Y(t))}function ED(e,t){if(e&1&&(L(0,`span`,19),J(1),R(),L(2,`span`,20),J(3),R()),e&2){let e=G().$implicit,t=G();A(),Y(t.cards()[e.tab]?.value),A(2),Y(t.cards()[e.tab]?.sub)}}function DD(e,t){e&1&&(L(0,`span`,21),J(1,`–`),R(),L(2,`span`,20),J(3,`Count unavailable`),R())}function OD(e,t){e&1&&(z(0,`span`,22),L(1,`span`,20),J(2,`Counting…`),R())}function kD(e,t){if(e&1){let e=H();L(0,`button`,13),U(`click`,function(){let t=E(e).$implicit;return D(G().navigate.emit(t.tab))}),L(1,`span`,14)(2,`span`,15),z(3,`app-tab-icon`,16),R(),L(4,`span`,17),J(5,`→`),R()(),L(6,`span`,18),J(7),R(),M(8,ED,4,2)(9,DD,4,0)(10,OD,3,0),R()}if(e&2){let e,n=t.$implicit,r=t.$index,i=G().stateOf(n.tab);K(`animation-delay`,60*r,`ms`),j(`aria-busy`,i===`loading`),A(3),I(`name`,n.tab),A(4),Y(n.label),A(),N((e=i)===`ready`?8:e===`error`?9:10)}}function AD(e,t){e&1&&(L(0,`p`),J(1,`Loading…`),R())}function jD(e,t){if(e&1&&(L(0,`div`)(1,`dt`),J(2),R(),L(3,`dd`),J(4),R()()),e&2){let e=t.$implicit;A(2),Y(e.label),A(2),Y(e.value)}}function MD(e,t){if(e&1&&(L(0,`dl`),P(1,jD,5,2,`div`,null,gD),R()),e&2){let e=G();A(),F(e.configItems())}}function ND(e,t){e&1&&(L(0,`p`),J(1,`Defaults`),R())}var PD=[{tab:`components`,label:`Components`},{tab:`routes`,label:`Routes`},{tab:`signals`,label:`Signals`},{tab:`injectors`,label:`Injectors`},{tab:`store`,label:`NgRx declarations`},{tab:`pipes`,label:`Pipes`},{tab:`forms`,label:`Forms`},{tab:`network`,label:`SSR & HTTP`}];function FD(e){return PD.filter(t=>$E(t.tab,e))}var ID={zoneless:`Zoneless`,zone:`zone.js`,"zone-unused":`Zoneless, zone.js loaded`};function LD(e){return e&&Object.hasOwn(ID,e)?ID[e]:null}var RD=new Set([`signal`,`computed`,`linkedSignal`,`effect`]),zD={action:[`action`,`actions`],reducer:[`reducer`,`reducers`],effect:[`effect`,`effects`],selector:[`selector`,`selectors`],feature:[`feature`,`features`],"store-setup":[`setup call`,`setup calls`],"signal-store":[`signal store`,`signal stores`],"signal-state":[`signal state`,`signal states`],"signal-method":[`signal method`,`signal methods`]};function BD(e,t,n){return`${e} ${e===1?t:n}`}function VD(e,t){for(let n of e??[])t(n),VD(n.children,t)}function HD(e){if(!e.some(e=>e.kind))return{value:e.length,sub:`discovered in source`};let t=e.filter(e=>e.kind===`directive`).length;return{value:e.length-t,sub:`components · ${BD(t,`directive`,`directives`)}`}}function UD(e){if(!e.some(e=>e.kind))return{value:e.length,sub:`route entries in source`};let t=e.filter(e=>e.kind===`redirect`).length;return{value:new Set(e.filter(e=>e.kind===`page`).map(e=>e.fullPath??e.path??``)).size,sub:`navigable paths · ${BD(t,`redirect`,`redirects`)}`}}function WD(e){let t=new Map;for(let n of e)n.kind&&t.set(n.kind,(t.get(n.kind)??0)+1);if(!t.size)return{value:e.length,sub:`declarations in source`};let n=[...t].sort((e,t)=>t[1]-e[1]).map(([e,t])=>{let[n,r]=zD[e]??[e,e];return BD(t,n,r)});return{value:e.length,sub:n.join(` · `)}}function GD(e,t){let n=e?.forms??[],r=t?n.filter(e=>LE(e.id)===t):n;if(!r.length)return{value:0,sub:`no forms on the page yet`};let i=r.filter(e=>e.root?.status===`INVALID`).length;return{value:r.length,sub:`${r.length===1?`form`:`forms`} · ${i} invalid`}}function KD(e){return e.cancelled||e.status===void 0?!1:e.status===0||e.status>=400}function qD(e,t){let n=e?.pages??[],r=t?n.find(e=>e.pageId===t)??null:n.reduce((e,t)=>!e||t.reportedAt>e.reportedAt?t:e,null),i=t&&!r?[]:e?.serverCalls??[],a=r?.initialUrl,o=[...a?i.filter(e=>!e.pageUrl||e.pageUrl===a):i,...r?.calls??[]];if(!o.length)return{value:0,sub:`no calls recorded yet`};let s=o.filter(KD).length;return{value:o.length,sub:`${o.length===1?`call`:`calls`} · ${s} failed`}}var JD=class e{rpc=$(null);navigate=Rv();staticReport=Q(()=>FE(this.rpc()));meta=k(null);config=Q(()=>JE(this.rpc()));stats=Q(()=>this.rpc()?FD(this.config()):[]);configItems=Q(()=>SE(this.config()));metaState=k(`loading`);states=k({});rows=k({});injectorTree=k(null);signalGraph=k(null);formsState=k(null);httpState=k(null);pageId=kE();destroyRef=w(Es);stopLive=[];zone=Q(()=>LD(jE(this.injectorTree(),this.pageId)?.zone));liveInjectors=Q(()=>{let e=jE(this.injectorTree(),this.pageId);if(!e?.roots?.length)return null;let t=0,n=0,r=e=>{t++,n+=e.providers?.length??0};return VD(e.roots,r),VD(e.environment,r),{injectors:t,providers:n}});liveSignals=Q(()=>{let e=ME(this.signalGraph(),this.pageId);return e?.nodes?.length?e.nodes.filter(e=>RD.has(e.kind??``)).length:null});cards=Q(()=>{let e=this.rows(),t={};if(e.components&&(t.components=HD(e.components)),e.routes&&(t.routes=UD(e.routes)),e.store&&(t.store=WD(e.store)),e.pipes){let n=e.pipes.filter(e=>e.builtin).length;t.pipes={value:e.pipes.length-n,sub:n?`custom pipes · ${n} built-in in use`:`custom pipes in source`}}let n=this.liveSignals();n===null?e.signals&&(t.signals={value:e.signals.length,sub:`signal declarations in source`}):t.signals={value:n,sub:e.signals?`live on the page · ${e.signals.length} declared in source`:`live on the page`};let r=this.liveInjectors();r?t.injectors={value:r.injectors,sub:`live injectors · ${BD(r.providers,`provider`,`providers`)}`}:e.injectors&&(t.injectors={value:e.injectors.filter(e=>e.type!==`injection`).length,sub:`provider declarations in source`});let i=this.formsState();i&&(t.forms=GD(i,this.pageId));let a=this.httpState();return a&&(t.network=qD(a,this.pageId)),t});loadMeta(e=this.rpc()){e&&(this.metaState.set(`loading`),e.scope(`pangular`).rpc.call(`build-meta`).then(t=>{this.rpc()===e&&(this.meta.set(t),this.metaState.set(`ready`))}).catch(()=>{this.rpc()===e&&this.metaState.set(`error`)}))}constructor(){pc(()=>{let e=this.rpc();if(!e)return;let t=e.scope(`pangular`);this.states.set({}),this.rows.set({}),this.formsState.set(null),this.httpState.set(null),this.loadMeta(e);let n=JE(e),r=e=>$E(e,n);r(`components`)&&this.load(t.rpc.call(`get-components`),`components`),r(`routes`)&&this.load(t.rpc.call(`get-routes`),`routes`),r(`signals`)&&this.load(t.rpc.call(`get-signals`),`signals`),r(`injectors`)&&this.load(t.rpc.call(`get-providers`),`injectors`),r(`store`)&&this.load(t.rpc.call(`get-ngrx-store`),`store`),r(`pipes`)&&this.load(t.rpc.call(`get-pipes`),`pipes`),this.watchLive(e,n)}),this.destroyRef.onDestroy(()=>this.unwatch())}stateOf(e){return(e===`signals`?this.liveSignals():e===`injectors`?this.liveInjectors():null)===null?this.states()[e]??`loading`:`ready`}load(e,t){let n=e=>this.states.update(n=>({...n,[t]:e}));e.then(e=>{this.rows.update(n=>({...n,[t]:Array.isArray(e)?e:[]})),n(`ready`)}).catch(()=>n(`error`))}async watchLive(e,t){this.unwatch();let n=e.scope(`pangular`).rpc,r=async(t,r,i)=>{let a=e=>i&&this.states.update(t=>({...t,[i]:e}));try{let i=await n.sharedState(t);if(this.destroyRef.destroyed||this.rpc()!==e)return;r(i.value()),a(`ready`),this.stopLive.push(i.on(`updated`,e=>r(e)))}catch{if(this.destroyRef.destroyed||this.rpc()!==e)return;r(null),a(`error`)}};await Promise.all([t.inspectors.injectors&&r(`injector-tree`,e=>this.injectorTree.set(e)),t.inspectors.signals&&r(`signal-graph`,e=>this.signalGraph.set(e)),$E(`forms`,t)&&r(`forms`,e=>this.formsState.set(e??{}),`forms`),$E(`network`,t)&&r(`http`,e=>this.httpState.set(e??{}),`network`)])}unwatch(){for(let e of this.stopLive.splice(0))e()}static ɵfac=function(t){return new(t||e)};static ɵcmp=Kh({type:e,selectors:[[`app-dashboard`]],inputs:{rpc:[1,`rpc`]},outputs:{navigate:`navigate`},decls:33,vars:14,consts:[[`aria-labelledby`,`project-title`,1,`project`],[1,`project-head`],[1,`eyebrow`],[`id`,`project-title`],[1,`chips`],[1,`analog`],[1,`grid`],[`type`,`button`,1,`stat`,3,`animation-delay`],[`aria-labelledby`,`config-title`,1,`config`],[`id`,`config-title`],[1,`hint`],[`type`,`button`,1,`retry`],[`type`,`button`,1,`retry`,3,`click`],[`type`,`button`,1,`stat`,3,`click`],[1,`top`],[1,`icon`],[3,`name`],[`aria-hidden`,`true`,1,`go`],[1,`label`],[1,`big`],[1,`sub`],[1,`big`,`muted`],[`aria-hidden`,`true`,1,`big`,`skeleton`]],template:function(e,t){if(e&1&&(L(0,`section`,0)(1,`div`,1)(2,`p`,2),J(3,`Project`),R(),L(4,`h2`,3),M(5,_D,1,1)(6,vD,1,0)(7,yD,1,0),R(),M(8,CD,4,2),R(),L(9,`ul`,4)(10,`li`)(11,`span`),J(12,`Angular`),R(),J(13),R(),L(14,`li`)(15,`span`),J(16,`TypeScript`),R(),J(17),R(),L(18,`li`)(19,`span`),J(20,`SSR`),R(),J(21),R(),M(22,wD,4,1,`li`,5),M(23,TD,4,1,`li`),R()(),L(24,`div`,6),P(25,kD,11,6,`button`,7,hD),R(),L(27,`section`,8)(28,`h2`,9),J(29,`Configuration`),R(),M(30,AD,2,0,`p`)(31,MD,3,0,`dl`)(32,ND,2,0,`p`),R()),e&2){let e,n,r;j(`aria-busy`,t.metaState()===`loading`),A(4),q(`muted`,t.metaState()!==`ready`),A(),N((e=t.metaState())===`ready`?5:e===`error`?6:7),A(3),N(t.metaState()===`error`?8:-1),A(),q(`pending`,t.metaState()===`loading`),A(4),Y(t.meta()?.angularVersion??`…`),A(4),Y(t.meta()?.typescript??`…`),A(4),Y(t.meta()?t.meta()?.ssr?`On`:`Off`:`…`),A(),N((n=t.meta()?.analog)?22:-1,n),A(),N((r=t.zone())?23:-1,r),A(2),F(t.stats()),A(2),j(`aria-busy`,!t.rpc()),A(3),N(t.rpc()?t.configItems().length?31:32:30)}},dependencies:[mD],styles:[`[_nghost-%COMP%] { display: block; max-width: 1200px; } @@ -325,7 +325,7 @@ h2.muted[_ngcontent-%COMP%] { .skeleton[_ngcontent-%COMP%] { animation: none; } -}`]})},JD=`pangular:panel-action-result`,YD=6e3,XD=0;function ZD(e=window){return e.location.protocol===`chrome-extension:`&&e.parent!==e}function QD(e,t=window){let n=`pa-${Date.now().toString(36)}-${++XD}`;return new Promise(r=>{let i=e=>{t.clearTimeout(o),t.removeEventListener(`message`,a),r(e)},a=({source:e,origin:r,data:a})=>{if(e!==t.parent||r!==t.location.origin)return;let o=a;if(o?.type!==JD||o.requestId!==n)return;let{opened:s,error:c}=a;i({ok:o.ok===!0,...s===`file`||s===`class`?{opened:s}:{},...typeof c==`string`?{error:c}:{}})},o=t.setTimeout(()=>i({ok:!1,error:`timeout`}),YD);t.addEventListener(`message`,a),t.parent.postMessage({...e,requestId:n},t.location.origin)})}var $D=class e{available=ZD();reveal(e,t){return QD({type:`pangular:reveal-element`,pageId:e,id:t})}openSource(e,t,n){return QD({type:`pangular:open-source`,pageId:e,id:t,...n?{file:n.file,line:n.line}:{}})}static ɵfac=function(t){return new(t||e)};static ɵprov=Ml({token:e,factory:e.ɵfac})};function eO(e){return e?.platform===`angular-native`}function tO(e){return eO(e)||e?.platform===`nativescript`}function nO(e,t){let n=Object.values(e??{}).filter(eO);if(t&&n.some(e=>e.pageId===t))return t;let r=null;for(let e of n)(!r||e.reportedAt>r.reportedAt)&&(r=e);return r?.pageId??null}var rO=[`trigger`],iO=[`list`],aO=(e,t)=>t.value;function oO(e,t){if(e&1&&(B(0,`span`,4),J(1),V()),e&2){let e=G();A(),Y(e.current().hint)}}function sO(e,t){if(e&1&&(B(0,`span`,15),J(1),V()),e&2){let e=G().$implicit;A(),Y(e.hint)}}function cO(e,t){if(e&1){let e=H();B(0,`li`,11),W(`pointerenter`,function(){let t=E(e).$index;return D(G(2).active.set(t))})(`pointerdown`,function(e){return e.preventDefault()})(`click`,function(){let t=E(e).$index;return D(G(2).choose(t))}),O(),B(1,`svg`,12),Vg(2,`path`,13),V(),gs(),B(3,`span`,14),J(4),V(),M(5,sO,2,1,`span`,15),V()}if(e&2){let e=t.$implicit,n=t.$index,r=G(2);q(`active`,n===r.active())(`selected`,e.value===r.value()),j(`id`,r.uid+`-opt-`+n)(`aria-selected`,e.value===r.value())(`aria-disabled`,e.disabled||null),A(4),Y(e.label),A(),N(e.hint?5:-1)}}function lO(e,t){if(e&1&&(B(0,`li`,10),J(1),V()),e&2){let e=G(2);A(),Y(e.emptyText())}}function uO(e,t){if(e&1&&(B(0,`ul`,8,1),_g(`list-in`),W(`pointerdown`,function(e){return e.preventDefault()}),P(2,cO,6,9,`li`,9,aO,!1,lO,2,1,`li`,10),V()),e&2){let e=G();q(`up`,e.dropUp()),j(`id`,e.uid+`-list`)(`aria-labelledby`,e.labelledBy()||e.uid+`-trigger`),A(2),F(e.options())}}var dO=0,fO=class e{options=$.required();value=Gv(null);placeholder=$(`Select…`);ariaLabel=$(``);labelledBy=$(``);describedBy=$(``);disabled=$(!1);emptyText=$(`Nothing to choose`);uid=`app-select-${++dO}`;open=k(!1);active=k(-1);dropUp=k(!1);current=Q(()=>this.options().find(e=>e.value===this.value())??null);host=w(Fl);injector=w(ws);trigger=Jv.required(`trigger`);list=Jv(`list`);typed=``;typedAt=0;toggle(){this.open()?this.close():this.show()}choose(e){let t=this.options()[e];t&&!t.disabled&&(this.value.set(t.value),this.close(),this.trigger().nativeElement.focus())}onKey(e){let t=this.options(),n=this.open();switch(e.key){case`ArrowDown`:case`ArrowUp`:if(e.preventDefault(),!n){this.show();return}this.move(e.key===`ArrowDown`?1:-1);return;case`Home`:case`End`:if(!n)return;e.preventDefault(),this.setActive(e.key===`Home`?this.firstEnabled(0,1):this.firstEnabled(t.length-1,-1));return;case`Enter`:case` `:e.preventDefault(),n&&this.active()>=0?this.choose(this.active()):this.show();return;case`Escape`:if(!n)return;e.preventDefault(),e.stopPropagation(),this.close();return;case`Tab`:n&&this.close();return;default:e.key.length===1&&!e.metaKey&&!e.ctrlKey&&!e.altKey&&this.typeAhead(e.key)}}onBlur(e){let t=e.relatedTarget;t&&this.host.nativeElement.contains(t)||this.close()}onOutside(e){this.open()&&!this.host.nativeElement.contains(e.target)&&this.close()}show(){if(this.disabled())return;let e=this.host.nativeElement.getBoundingClientRect(),t=window.innerHeight-e.bottom;this.dropUp.set(t<220&&e.top>t);let n=this.options().findIndex(e=>e.value===this.value());this.open.set(!0),this.setActive(n>=0?n:this.firstEnabled(0,1))}close(){this.open.set(!1),this.active.set(-1)}move(e){let t=this.firstEnabled(this.active()+e,e);t>=0&&this.setActive(t)}firstEnabled(e,t){let n=this.options();for(let r=e;r>=0&&r{(this.list()?.nativeElement.querySelector(`#${this.uid}-opt-${e}`))?.scrollIntoView({block:`nearest`})},{injector:this.injector})}typeAhead(e){let t=performance.now();this.typed=t-this.typedAt>600?e.toLowerCase():this.typed+e.toLowerCase(),this.typedAt=t;let n=this.options().findIndex(e=>!e.disabled&&e.label.toLowerCase().startsWith(this.typed));n<0||(this.open()?this.setActive(n):this.value.set(this.options()[n].value))}static ɵfac=function(t){return new(t||e)};static ɵcmp=Kh({type:e,selectors:[[`app-select`]],viewQuery:function(e,t){e&1&&l_(t.trigger,rO,5)(t.list,iO,5),e&2&&u_(2)},hostAttrs:[1,`app-select`],hostVars:2,hostBindings:function(e,t){e&1&&U(`pointerdown`,function(e){return t.onOutside(e)},Fu),e&2&&q(`open`,t.open())},inputs:{options:[1,`options`],value:[1,`value`],placeholder:[1,`placeholder`],ariaLabel:[1,`ariaLabel`],labelledBy:[1,`labelledBy`],describedBy:[1,`describedBy`],disabled:[1,`disabled`],emptyText:[1,`emptyText`]},outputs:{value:`valueChange`},decls:8,vars:13,consts:[[`trigger`,``],[`list`,``],[`type`,`button`,`role`,`combobox`,`aria-haspopup`,`listbox`,1,`trigger`,3,`click`,`keydown`,`blur`,`disabled`],[1,`value`],[1,`value-hint`],[`viewBox`,`0 0 24 24`,`aria-hidden`,`true`,1,`chevron`],[`d`,`m6 9 6 6 6-6`],[`role`,`listbox`,`tabindex`,`-1`,1,`list`,3,`up`],[`role`,`listbox`,`tabindex`,`-1`,1,`list`,3,`pointerdown`],[`role`,`option`,3,`active`,`selected`],[`role`,`option`,`aria-disabled`,`true`,1,`empty`],[`role`,`option`,3,`pointerenter`,`pointerdown`,`click`],[`viewBox`,`0 0 24 24`,`aria-hidden`,`true`,1,`check`],[`d`,`m5 12 5 5 9-10`],[1,`label`],[1,`hint`]],template:function(e,t){e&1&&(B(0,`button`,2,0),W(`click`,function(){return t.toggle()})(`keydown`,function(e){return t.onKey(e)})(`blur`,function(e){return t.onBlur(e)}),B(2,`span`,3),J(3),V(),M(4,oO,2,1,`span`,4),O(),B(5,`svg`,5),Vg(6,`path`,6),V()(),M(7,uO,5,5,`ul`,7)),e&2&&(qg(`disabled`,t.disabled()),j(`id`,t.uid+`-trigger`)(`aria-controls`,t.uid+`-list`)(`aria-expanded`,t.open())(`aria-label`,t.ariaLabel()||null)(`aria-labelledby`,t.labelledBy()||null)(`aria-describedby`,t.describedBy()||null)(`aria-activedescendant`,t.open()&&t.active()>=0?t.uid+`-opt-`+t.active():null),A(2),q(`placeholder`,!t.current()),A(),X(` `,t.current()?.label??t.placeholder(),` `),A(),N(t.current()?.hint?4:-1),A(3),N(t.open()?7:-1))},styles:[`[_nghost-%COMP%] { +}`]})},YD=`pangular:panel-action-result`,XD=6e3,ZD=0;function QD(e=window){return e.location.protocol===`chrome-extension:`&&e.parent!==e}function $D(e,t=window){let n=`pa-${Date.now().toString(36)}-${++ZD}`;return new Promise(r=>{let i=e=>{t.clearTimeout(o),t.removeEventListener(`message`,a),r(e)},a=({source:e,origin:r,data:a})=>{if(e!==t.parent||r!==t.location.origin)return;let o=a;if(o?.type!==YD||o.requestId!==n)return;let{opened:s,error:c}=a;i({ok:o.ok===!0,...s===`file`||s===`class`?{opened:s}:{},...typeof c==`string`?{error:c}:{}})},o=t.setTimeout(()=>i({ok:!1,error:`timeout`}),XD);t.addEventListener(`message`,a),t.parent.postMessage({...e,requestId:n},t.location.origin)})}var eO=class e{available=QD();reveal(e,t){return $D({type:`pangular:reveal-element`,pageId:e,id:t})}openSource(e,t,n){return $D({type:`pangular:open-source`,pageId:e,id:t,...n?{file:n.file,line:n.line}:{}})}static ɵfac=function(t){return new(t||e)};static ɵprov=Ml({token:e,factory:e.ɵfac})};function tO(e){return e?.platform===`angular-native`}function nO(e){return tO(e)||e?.platform===`nativescript`}function rO(e,t){let n=Object.values(e??{}).filter(tO);if(t&&n.some(e=>e.pageId===t))return t;let r=null;for(let e of n)(!r||e.reportedAt>r.reportedAt)&&(r=e);return r?.pageId??null}var iO=[`trigger`],aO=[`list`],oO=(e,t)=>t.value;function sO(e,t){if(e&1&&(B(0,`span`,4),J(1),V()),e&2){let e=G();A(),Y(e.current().hint)}}function cO(e,t){if(e&1&&(B(0,`span`,15),J(1),V()),e&2){let e=G().$implicit;A(),Y(e.hint)}}function lO(e,t){if(e&1){let e=H();B(0,`li`,11),W(`pointerenter`,function(){let t=E(e).$index;return D(G(2).active.set(t))})(`pointerdown`,function(e){return e.preventDefault()})(`click`,function(){let t=E(e).$index;return D(G(2).choose(t))}),O(),B(1,`svg`,12),Vg(2,`path`,13),V(),gs(),B(3,`span`,14),J(4),V(),M(5,cO,2,1,`span`,15),V()}if(e&2){let e=t.$implicit,n=t.$index,r=G(2);q(`active`,n===r.active())(`selected`,e.value===r.value()),j(`id`,r.uid+`-opt-`+n)(`aria-selected`,e.value===r.value())(`aria-disabled`,e.disabled||null),A(4),Y(e.label),A(),N(e.hint?5:-1)}}function uO(e,t){if(e&1&&(B(0,`li`,10),J(1),V()),e&2){let e=G(2);A(),Y(e.emptyText())}}function dO(e,t){if(e&1&&(B(0,`ul`,8,1),_g(`list-in`),W(`pointerdown`,function(e){return e.preventDefault()}),P(2,lO,6,9,`li`,9,oO,!1,uO,2,1,`li`,10),V()),e&2){let e=G();q(`up`,e.dropUp()),j(`id`,e.uid+`-list`)(`aria-labelledby`,e.labelledBy()||e.uid+`-trigger`),A(2),F(e.options())}}var fO=0,pO=class e{options=$.required();value=Gv(null);placeholder=$(`Select…`);ariaLabel=$(``);labelledBy=$(``);describedBy=$(``);disabled=$(!1);emptyText=$(`Nothing to choose`);uid=`app-select-${++fO}`;open=k(!1);active=k(-1);dropUp=k(!1);current=Q(()=>this.options().find(e=>e.value===this.value())??null);host=w(Fl);injector=w(ws);trigger=Jv.required(`trigger`);list=Jv(`list`);typed=``;typedAt=0;toggle(){this.open()?this.close():this.show()}choose(e){let t=this.options()[e];t&&!t.disabled&&(this.value.set(t.value),this.close(),this.trigger().nativeElement.focus())}onKey(e){let t=this.options(),n=this.open();switch(e.key){case`ArrowDown`:case`ArrowUp`:if(e.preventDefault(),!n){this.show();return}this.move(e.key===`ArrowDown`?1:-1);return;case`Home`:case`End`:if(!n)return;e.preventDefault(),this.setActive(e.key===`Home`?this.firstEnabled(0,1):this.firstEnabled(t.length-1,-1));return;case`Enter`:case` `:e.preventDefault(),n&&this.active()>=0?this.choose(this.active()):this.show();return;case`Escape`:if(!n)return;e.preventDefault(),e.stopPropagation(),this.close();return;case`Tab`:n&&this.close();return;default:e.key.length===1&&!e.metaKey&&!e.ctrlKey&&!e.altKey&&this.typeAhead(e.key)}}onBlur(e){let t=e.relatedTarget;t&&this.host.nativeElement.contains(t)||this.close()}onOutside(e){this.open()&&!this.host.nativeElement.contains(e.target)&&this.close()}show(){if(this.disabled())return;let e=this.host.nativeElement.getBoundingClientRect(),t=window.innerHeight-e.bottom;this.dropUp.set(t<220&&e.top>t);let n=this.options().findIndex(e=>e.value===this.value());this.open.set(!0),this.setActive(n>=0?n:this.firstEnabled(0,1))}close(){this.open.set(!1),this.active.set(-1)}move(e){let t=this.firstEnabled(this.active()+e,e);t>=0&&this.setActive(t)}firstEnabled(e,t){let n=this.options();for(let r=e;r>=0&&r{(this.list()?.nativeElement.querySelector(`#${this.uid}-opt-${e}`))?.scrollIntoView({block:`nearest`})},{injector:this.injector})}typeAhead(e){let t=performance.now();this.typed=t-this.typedAt>600?e.toLowerCase():this.typed+e.toLowerCase(),this.typedAt=t;let n=this.options().findIndex(e=>!e.disabled&&e.label.toLowerCase().startsWith(this.typed));n<0||(this.open()?this.setActive(n):this.value.set(this.options()[n].value))}static ɵfac=function(t){return new(t||e)};static ɵcmp=Kh({type:e,selectors:[[`app-select`]],viewQuery:function(e,t){e&1&&l_(t.trigger,iO,5)(t.list,aO,5),e&2&&u_(2)},hostAttrs:[1,`app-select`],hostVars:2,hostBindings:function(e,t){e&1&&U(`pointerdown`,function(e){return t.onOutside(e)},Fu),e&2&&q(`open`,t.open())},inputs:{options:[1,`options`],value:[1,`value`],placeholder:[1,`placeholder`],ariaLabel:[1,`ariaLabel`],labelledBy:[1,`labelledBy`],describedBy:[1,`describedBy`],disabled:[1,`disabled`],emptyText:[1,`emptyText`]},outputs:{value:`valueChange`},decls:8,vars:13,consts:[[`trigger`,``],[`list`,``],[`type`,`button`,`role`,`combobox`,`aria-haspopup`,`listbox`,1,`trigger`,3,`click`,`keydown`,`blur`,`disabled`],[1,`value`],[1,`value-hint`],[`viewBox`,`0 0 24 24`,`aria-hidden`,`true`,1,`chevron`],[`d`,`m6 9 6 6 6-6`],[`role`,`listbox`,`tabindex`,`-1`,1,`list`,3,`up`],[`role`,`listbox`,`tabindex`,`-1`,1,`list`,3,`pointerdown`],[`role`,`option`,3,`active`,`selected`],[`role`,`option`,`aria-disabled`,`true`,1,`empty`],[`role`,`option`,3,`pointerenter`,`pointerdown`,`click`],[`viewBox`,`0 0 24 24`,`aria-hidden`,`true`,1,`check`],[`d`,`m5 12 5 5 9-10`],[1,`label`],[1,`hint`]],template:function(e,t){e&1&&(B(0,`button`,2,0),W(`click`,function(){return t.toggle()})(`keydown`,function(e){return t.onKey(e)})(`blur`,function(e){return t.onBlur(e)}),B(2,`span`,3),J(3),V(),M(4,sO,2,1,`span`,4),O(),B(5,`svg`,5),Vg(6,`path`,6),V()(),M(7,dO,5,5,`ul`,7)),e&2&&(qg(`disabled`,t.disabled()),j(`id`,t.uid+`-trigger`)(`aria-controls`,t.uid+`-list`)(`aria-expanded`,t.open())(`aria-label`,t.ariaLabel()||null)(`aria-labelledby`,t.labelledBy()||null)(`aria-describedby`,t.describedBy()||null)(`aria-activedescendant`,t.open()&&t.active()>=0?t.uid+`-opt-`+t.active():null),A(2),q(`placeholder`,!t.current()),A(),X(` `,t.current()?.label??t.placeholder(),` `),A(),N(t.current()?.hint?4:-1),A(3),N(t.open()?7:-1))},styles:[`[_nghost-%COMP%] { position: relative; display: inline-flex; min-width: 0; @@ -520,7 +520,7 @@ li.selected[_ngcontent-%COMP%] .check[_ngcontent-%COMP%] { .chevron[_ngcontent-%COMP%] { transition: none; } -}`]})};function pO(e,t){let n=CE(t);if(wE(n))return{nodes:e,matches:0};let r=0,i=e=>e.flatMap(e=>{let t=TE(e,n);t&&r++;let a=i(e.children);return t||a.length?[{...e,children:a}]:[]});return{nodes:i(e),matches:r}}function mO(e){return e.flatMap(e=>e.kind===`directive`?mO(e.children):[{...e,children:mO(e.children)}])}function hO(e){return(e.attributes?.length?e.attributes:e.directives??[e.name]).map(e=>`[${e}]`).join(` `)}var gO={hydrated:`Hydrated`,skipped:`Skipped`,mismatched:`Mismatch`,client:`Client`};function _O(e){return gO[e]}function vO(e,t,n,r=!1,i=0){let a=r?`${e}+`:`${e}`;if(n)return`${t} of ${r?`${e+i}+`:e+i}`;let o=i?` · ${i} directive ${i===1?`host`:`hosts`}`:``;return`${a} ${e===1&&!r?`instance`:`instances`}${o}`}function yO(e,t,n,r=!1,i=0){if(!n.trim())return``;if(!t)return`No components match.`;let a=e+i,o=r?`${a}+`:`${a}`,s=!r&&a===1;return`${t} of ${o} ${i?s?`row`:`rows`:s?`instance`:`instances`} match.`}function bO(e){let t=e?.components,n=e?.depth;return t&&n?`Showing the first ${t} component instances, and none nested more than ${n} elements deep. Others are not listed or searchable.`:t?`Showing the first ${t} component instances. Others are not listed or searchable.`:n?`Components nested more than ${n} elements deep are not listed or searchable.`:`The page has more components than the tree shows.`}function xO(e,t){let n=t?.detail?.id??null,r=t?.pageId??null;if(r!==e.pageId)return{pageId:r,selectedId:n,detailId:n,destroyed:null};let i=e.selectedId;return n&&n!==e.detailId&&n!==i&&(i=n),i&&t&&!t.truncated&&!SO(t.roots,i)?{pageId:r,selectedId:null,detailId:n,destroyed:i}:{pageId:r,selectedId:i,detailId:n,destroyed:null}}function SO(e,t){return e.some(e=>e.id===t||SO(e.children,t))}function CO(e,t,n){let r=e.indexOf(n);if(r<0)return null;for(let n=r+1;n=0;n--)if(t.has(e[n]))return e[n];return null}var wO=(e,t)=>t.key,TO=(e,t)=>t.id;function EO(e,t){e&1&&(B(0,`p`,2),J(1,` The HTML came from the server, but no component was hydrated, so the app rendered again on the client. Add `),B(2,`code`),J(3,`provideClientHydration()`),V(),J(4,` and use a development build to see each component's status. `),V())}function DO(e,t){if(e&1&&(B(0,`li`)(1,`span`,9),J(2),V(),J(3),V()),e&2){let e=t.$implicit;M_(`count count-`+e.key),A(2),Y(e.value),A(),X(` `,e.label,` `)}}function OO(e,t){if(e&1&&(B(0,`span`,15),J(1,`Expected `),B(2,`code`),J(3),V()()),e&2){let e=G().$implicit;A(3),Y(e.expected)}}function kO(e,t){if(e&1&&(B(0,`span`,15),J(1,`Found `),B(2,`code`),J(3),V()()),e&2){let e=G().$implicit;A(3),Y(e.actual)}}function AO(e,t){if(e&1){let e=H();B(0,`li`)(1,`button`,12),W(`click`,function(){let t=E(e).$implicit;return D(G(4).showNode.emit(t.id))})(`focus`,function(){let t=E(e).$implicit;return D(G(4).highlight.emit(t.id))})(`blur`,function(){return E(e),D(G(4).highlight.emit(null))})(`mouseenter`,function(){let t=E(e).$implicit;return D(G(4).highlight.emit(t.id))})(`mouseleave`,function(){return E(e),D(G(4).highlight.emit(null))}),B(2,`span`,13),J(3),V(),B(4,`span`,14),J(5),V(),M(6,OO,4,1,`span`,15),M(7,kO,4,1,`span`,15),V()()}if(e&2){let e=t.$implicit;A(3),Y(e.name),A(2),X(`<`,e.tag,`>`),A(),N(e.expected?6:-1),A(),N(e.actual?7:-1)}}function jO(e,t){if(e&1&&(B(0,`h3`,10),J(1,`Mismatches`),V(),B(2,`ul`,11),P(3,AO,8,4,`li`,null,TO),V()),e&2){let e=G(2);A(3),F(e.mismatches)}}function MO(e,t){if(e&1){let e=H();B(0,`div`,3)(1,`ul`,4),P(2,DO,4,4,`li`,5,wO),V(),B(4,`button`,6),W(`click`,function(){return E(e),D(G(2).toggleOverlay())}),O(),B(5,`svg`,7),Vg(6,`rect`,8),V(),J(7,` Outline on page `),V()(),M(8,jO,5,0)}if(e&2){let e=G(),t=G();A(2),F(t.counts()),A(2),j(`aria-pressed`,t.overlayOn()),A(4),N(e.mismatches.length?8:-1)}}function NO(e,t){e&1&&M(0,EO,5,0,`p`,2)(1,MO,9,2),e&2&&N(+!!t.hydrated)}function PO(e,t){e&1&&(B(0,`p`,2),J(1,` This page was not rendered on the server, so there is nothing to hydrate. With server-side rendering and `),B(2,`code`),J(3,`provideClientHydration()`),V(),J(4,`, each component shows whether it was hydrated, skipped or mismatched. `),V())}var FO=class e{report=$(void 0);overlay=Rv();showNode=Rv();highlight=Rv();overlayOn=Fv(()=>this.report()?.overlay===!0);counts=Q(()=>{let e=this.report()?.counts;return e?[{key:`hydrated`,label:`hydrated`,value:e.hydrated},{key:`skipped`,label:`skipped`,value:e.skipped},{key:`mismatched`,label:`mismatched`,value:e.mismatched},{key:`client`,label:`created on the client`,value:e.client},{key:`dehydratedBlocks`,label:e.dehydratedBlocks===1?`dehydrated @defer block`:`dehydrated @defer blocks`,value:e.dehydratedBlocks}]:[]});toggleOverlay(){let e=!this.overlayOn();this.overlayOn.set(e),this.overlay.emit(e)}static ɵfac=function(t){return new(t||e)};static ɵcmp=Kh({type:e,selectors:[[`app-hydration-section`]],inputs:{report:[1,`report`]},outputs:{overlay:`overlay`,showNode:`showNode`,highlight:`highlight`},decls:5,vars:1,consts:[[`aria-labelledby`,`ct-hydration-title`,1,`hydration`],[`id`,`ct-hydration-title`,1,`title`],[1,`empty`],[1,`summary`],[`aria-label`,`Hydration summary`,1,`counts`],[3,`class`],[`type`,`button`,1,`outline`,3,`click`],[`viewBox`,`0 0 24 24`,`aria-hidden`,`true`],[`x`,`3`,`y`,`3`,`width`,`18`,`height`,`18`,`rx`,`2`,`stroke-dasharray`,`4 3`],[1,`num`],[1,`label`],[1,`mismatches`],[`type`,`button`,1,`mismatch`,3,`click`,`focus`,`blur`,`mouseenter`,`mouseleave`],[1,`name`,`mono`],[1,`mono`,`muted`],[1,`detail-line`]],template:function(e,t){if(e&1&&(B(0,`section`,0)(1,`h2`,1),J(2,`Hydration`),V(),M(3,NO,2,1)(4,PO,5,0,`p`,2),V()),e&2){let e;A(3),N((e=t.report())?3:4,e)}},styles:[`.hydration[_ngcontent-%COMP%] { +}`]})};function mO(e,t){let n=CE(t);if(wE(n))return{nodes:e,matches:0};let r=0,i=e=>e.flatMap(e=>{let t=TE(e,n);t&&r++;let a=i(e.children);return t||a.length?[{...e,children:a}]:[]});return{nodes:i(e),matches:r}}function hO(e){return e.flatMap(e=>e.kind===`directive`?hO(e.children):[{...e,children:hO(e.children)}])}function gO(e){return(e.attributes?.length?e.attributes:e.directives??[e.name]).map(e=>`[${e}]`).join(` `)}var _O={hydrated:`Hydrated`,skipped:`Skipped`,mismatched:`Mismatch`,client:`Client`};function vO(e){return _O[e]}function yO(e,t,n,r=!1,i=0){let a=r?`${e}+`:`${e}`;if(n)return`${t} of ${r?`${e+i}+`:e+i}`;let o=i?` · ${i} directive ${i===1?`host`:`hosts`}`:``;return`${a} ${e===1&&!r?`instance`:`instances`}${o}`}function bO(e,t,n,r=!1,i=0){if(!n.trim())return``;if(!t)return`No components match.`;let a=e+i,o=r?`${a}+`:`${a}`,s=!r&&a===1;return`${t} of ${o} ${i?s?`row`:`rows`:s?`instance`:`instances`} match.`}function xO(e){let t=e?.components,n=e?.depth;return t&&n?`Showing the first ${t} component instances, and none nested more than ${n} elements deep. Others are not listed or searchable.`:t?`Showing the first ${t} component instances. Others are not listed or searchable.`:n?`Components nested more than ${n} elements deep are not listed or searchable.`:`The page has more components than the tree shows.`}function SO(e,t){let n=t?.detail?.id??null,r=t?.pageId??null;if(r!==e.pageId)return{pageId:r,selectedId:n,detailId:n,destroyed:null};let i=e.selectedId;return n&&n!==e.detailId&&n!==i&&(i=n),i&&t&&!t.truncated&&!CO(t.roots,i)?{pageId:r,selectedId:null,detailId:n,destroyed:i}:{pageId:r,selectedId:i,detailId:n,destroyed:null}}function CO(e,t){return e.some(e=>e.id===t||CO(e.children,t))}function wO(e,t,n){let r=e.indexOf(n);if(r<0)return null;for(let n=r+1;n=0;n--)if(t.has(e[n]))return e[n];return null}var TO=(e,t)=>t.key,EO=(e,t)=>t.id;function DO(e,t){e&1&&(B(0,`p`,2),J(1,` The HTML came from the server, but no component was hydrated, so the app rendered again on the client. Add `),B(2,`code`),J(3,`provideClientHydration()`),V(),J(4,` and use a development build to see each component's status. `),V())}function OO(e,t){if(e&1&&(B(0,`li`)(1,`span`,9),J(2),V(),J(3),V()),e&2){let e=t.$implicit;M_(`count count-`+e.key),A(2),Y(e.value),A(),X(` `,e.label,` `)}}function kO(e,t){if(e&1&&(B(0,`span`,15),J(1,`Expected `),B(2,`code`),J(3),V()()),e&2){let e=G().$implicit;A(3),Y(e.expected)}}function AO(e,t){if(e&1&&(B(0,`span`,15),J(1,`Found `),B(2,`code`),J(3),V()()),e&2){let e=G().$implicit;A(3),Y(e.actual)}}function jO(e,t){if(e&1){let e=H();B(0,`li`)(1,`button`,12),W(`click`,function(){let t=E(e).$implicit;return D(G(4).showNode.emit(t.id))})(`focus`,function(){let t=E(e).$implicit;return D(G(4).highlight.emit(t.id))})(`blur`,function(){return E(e),D(G(4).highlight.emit(null))})(`mouseenter`,function(){let t=E(e).$implicit;return D(G(4).highlight.emit(t.id))})(`mouseleave`,function(){return E(e),D(G(4).highlight.emit(null))}),B(2,`span`,13),J(3),V(),B(4,`span`,14),J(5),V(),M(6,kO,4,1,`span`,15),M(7,AO,4,1,`span`,15),V()()}if(e&2){let e=t.$implicit;A(3),Y(e.name),A(2),X(`<`,e.tag,`>`),A(),N(e.expected?6:-1),A(),N(e.actual?7:-1)}}function MO(e,t){if(e&1&&(B(0,`h3`,10),J(1,`Mismatches`),V(),B(2,`ul`,11),P(3,jO,8,4,`li`,null,EO),V()),e&2){let e=G(2);A(3),F(e.mismatches)}}function NO(e,t){if(e&1){let e=H();B(0,`div`,3)(1,`ul`,4),P(2,OO,4,4,`li`,5,TO),V(),B(4,`button`,6),W(`click`,function(){return E(e),D(G(2).toggleOverlay())}),O(),B(5,`svg`,7),Vg(6,`rect`,8),V(),J(7,` Outline on page `),V()(),M(8,MO,5,0)}if(e&2){let e=G(),t=G();A(2),F(t.counts()),A(2),j(`aria-pressed`,t.overlayOn()),A(4),N(e.mismatches.length?8:-1)}}function PO(e,t){e&1&&M(0,DO,5,0,`p`,2)(1,NO,9,2),e&2&&N(+!!t.hydrated)}function FO(e,t){e&1&&(B(0,`p`,2),J(1,` This page was not rendered on the server, so there is nothing to hydrate. With server-side rendering and `),B(2,`code`),J(3,`provideClientHydration()`),V(),J(4,`, each component shows whether it was hydrated, skipped or mismatched. `),V())}var IO=class e{report=$(void 0);overlay=Rv();showNode=Rv();highlight=Rv();overlayOn=Fv(()=>this.report()?.overlay===!0);counts=Q(()=>{let e=this.report()?.counts;return e?[{key:`hydrated`,label:`hydrated`,value:e.hydrated},{key:`skipped`,label:`skipped`,value:e.skipped},{key:`mismatched`,label:`mismatched`,value:e.mismatched},{key:`client`,label:`created on the client`,value:e.client},{key:`dehydratedBlocks`,label:e.dehydratedBlocks===1?`dehydrated @defer block`:`dehydrated @defer blocks`,value:e.dehydratedBlocks}]:[]});toggleOverlay(){let e=!this.overlayOn();this.overlayOn.set(e),this.overlay.emit(e)}static ɵfac=function(t){return new(t||e)};static ɵcmp=Kh({type:e,selectors:[[`app-hydration-section`]],inputs:{report:[1,`report`]},outputs:{overlay:`overlay`,showNode:`showNode`,highlight:`highlight`},decls:5,vars:1,consts:[[`aria-labelledby`,`ct-hydration-title`,1,`hydration`],[`id`,`ct-hydration-title`,1,`title`],[1,`empty`],[1,`summary`],[`aria-label`,`Hydration summary`,1,`counts`],[3,`class`],[`type`,`button`,1,`outline`,3,`click`],[`viewBox`,`0 0 24 24`,`aria-hidden`,`true`],[`x`,`3`,`y`,`3`,`width`,`18`,`height`,`18`,`rx`,`2`,`stroke-dasharray`,`4 3`],[1,`num`],[1,`label`],[1,`mismatches`],[`type`,`button`,1,`mismatch`,3,`click`,`focus`,`blur`,`mouseenter`,`mouseleave`],[1,`name`,`mono`],[1,`mono`,`muted`],[1,`detail-line`]],template:function(e,t){if(e&1&&(B(0,`section`,0)(1,`h2`,1),J(2,`Hydration`),V(),M(3,PO,2,1)(4,FO,5,0,`p`,2),V()),e&2){let e;A(3),N((e=t.report())?3:4,e)}},styles:[`.hydration[_ngcontent-%COMP%] { margin-top: 16px; } @@ -691,7 +691,7 @@ svg[_ngcontent-%COMP%] { button[_ngcontent-%COMP%] { transition: none; } -}`]})};function IO(e){return new Date(e).toLocaleTimeString()}var LO=/[ \t\n\r]/;function RO(e){let t=e.trim(),n;try{n=JSON.parse(t)}catch{return null}if(typeof n!=`object`||!n)return null;let r=``,i=0,a=()=>`\n${` `.repeat(i)}`;for(let e=0;e0?0:-1)},styles:[`[_nghost-%COMP%] { +}`]})};function LO(e){return new Date(e).toLocaleTimeString()}var RO=/[ \t\n\r]/;function zO(e){let t=e.trim(),n;try{n=JSON.parse(t)}catch{return null}if(typeof n!=`object`||!n)return null;let r=``,i=0,a=()=>`\n${` `.repeat(i)}`;for(let e=0;e0?0:-1)},styles:[`[_nghost-%COMP%] { display: block; } @@ -718,7 +718,7 @@ a[_ngcontent-%COMP%] { a[_ngcontent-%COMP%]:focus-visible { outline: 2px solid var(--%NS%accent); outline-offset: 2px; -}`]})},HO=16.6;function UO(e){return e.ms>HO}function WO(e){return`${e<10?e.toFixed(2):Math.round(e)} ms`}function GO(e,t){if(!t)return[...e];let n=Math.min(t.from,t.to),r=Math.max(t.from,t.to),i=e.filter(e=>e.id>=n&&e.id<=r);return i.length?i:[...e]}function KO(e){let t=new Map;for(let n of e)for(let e of n.components){let n=t.get(e.name);n?(n.checks+=e.checks,n.ms+=e.ms,n.maxMs=Math.max(n.maxMs,e.ms),n.cycles+=1):t.set(e.name,{name:e.name,checks:e.checks,ms:e.ms,maxMs:e.ms,cycles:1})}return[...t.values()].sort((e,t)=>t.ms-e.ms||e.name.localeCompare(t.name))}function qO(e){let t=e.reduce((e,t)=>Math.max(e,t.ms),0);return Math.max(t,HO*1.25)}function JO(e,t,n=3){let r=e.map(e=>({item:e,value:t(e)})).filter(e=>e.value>0).sort((e,t)=>t.value-e.value),i=r.reduce((e,t)=>e+t.value,0);if(!i)return[];let a=n/i,o=[],s=0,c=0,l=n,u=1,d=[],f=(e,t)=>{let n=e.reduce((e,t)=>e+t,0),r=Math.max(...e),i=Math.min(...e);return Math.max(t*t*r/(n*n),n*n/(t*t*i))},p=()=>{let e=d.reduce((e,t)=>e+t.area,0);if(l>=u){let t=e/u,n=c;for(let e of d){let r=e.area/t;o.push({item:e.item,x:s,y:n,w:t,h:r}),n+=r}s+=t,l-=t}else{let t=e/l,n=s;for(let e of d){let r=e.area/t;o.push({item:e.item,x:n,y:c,w:r,h:t}),n+=r}c+=t,u-=t}d=[]};for(let e of r){let t=e.value*a,n=Math.min(l,u),r=d.map(e=>e.area);d.length&&f([...r,t],n)>f(r,n)&&p(),d.push({item:e.item,area:t})}return d.length&&p(),o.map(e=>({item:e.item,x:e.x/n*100,y:e.y*100,w:e.w/n*100,h:e.h*100}))}var YO={c:`component`,h:`hook`,r:`afterRender`,o:`output`};function XO(e,t){if(t===`c`)return e||null;if(t===`h`){let t=e.lastIndexOf(`.`);return t>0?e.slice(0,t):null}if(t===`o`){let t=e.indexOf(` (`),n=t>0?e.slice(0,t):e;return n&&n!==`?`?n:null}return null}function ZO(e,t){let n=e.filter(e=>e.spans?.length);if(!n.length)return null;let r=n.map(e=>{let t=e.spans,n=Math.min(0,...t.map(e=>e[1])),r=Math.max(e.ms,...t.map(e=>e[1]+e[2]));return{cycle:e,spans:t,from:n,width:Math.max(r-n,.001)}}),i=r.reduce((e,t)=>e+t.width,0),a=[],o=[],s=0,c=0;for(let{cycle:e,spans:n,from:l,width:u}of r){o.push({id:e.id,x:s/i*100,w:u/i*100,droppedSpans:e.droppedSpans??0});let r=[...n].sort((e,t)=>e[1]-t[1]||e[3]-t[3]),d=[];for(let[n,o,u,f,p]of r){let r=t[n]??`unknown`,m=d[f-1],h=f>0&&m!==void 0&&ot.cycle.id,$O=(e,t)=>t.id;function ek(e,t){if(e&1){let e=H();B(0,`button`,27),W(`click`,function(){return E(e),D(G().showAll())}),J(1,`Show all cycles`),V()}}function tk(e,t){if(e&1&&Vg(0,`div`,28),e&2){let e=t;K(`left`,e.left,`%`)(`width`,e.width,`%`)}}function nk(e,t){if(e&1&&(B(0,`div`,29),Vg(1,`span`,30),V()),e&2){let e=t.$implicit,n=G();q(`over`,e.over)(`out`,!n.inRange(e.index)&&!!n.range())(`active`,n.focused()&&n.active()===e.index),qg(`id`,n.optionId(e.index)),j(`data-index`,e.index)(`aria-selected`,n.inRange(e.index))(`aria-label`,e.label),A(),K(`height`,e.height,`%`)}}function rk(e,t){e&1&&(B(0,`span`,33),J(1,`over budget`),V())}function ik(e,t){if(e&1&&(B(0,`p`),J(1,` after `),B(2,`span`,35),J(3),V()()),e&2){let e=G();A(3),Y(e.cycle.trigger)}}function ak(e,t){if(e&1&&(B(0,`p`,34)(1,`span`,36),J(2),V(),B(3,`span`),J(4),V()()),e&2){let e=t.$implicit,n=G(2);A(2),Y(e.name),A(2),Y(n.fmt(e.ms))}}function ok(e,t){if(e&1&&(B(0,`div`,31)(1,`p`,32)(2,`strong`),J(3),V(),B(4,`span`),J(5),V(),M(6,rk,2,0,`span`,33),V(),M(7,ik,4,1,`p`),B(8,`p`),J(9),V(),P(10,ak,5,2,`p`,34,kg),V()),e&2){let e=t,n=G();K(`left`,e.left)(`right`,e.right),A(3),Y(n.fmt(e.cycle.ms)),A(2),X(`cycle `,e.cycle.id),A(),N(e.over?6:-1),A(),N(e.cycle.trigger?7:-1),A(2),iv(` `,e.cycle.checks,` `,e.cycle.checks===1?`check`:`checks`,`, `,e.cycle.passes,` `,e.cycle.passes===1?`pass`:`passes`,` `),A(),F(e.top)}}function sk(e,t){if(e&1&&Vg(0,`span`),e&2){let e=t.$implicit;q(`over`,e.over)}}function ck(e,t){if(e&1&&J(0),e&2){let e=t,n=G(2);Z(` `,e.name,` (`,n.fmt(e.ms),`) `)}}function lk(e,t){if(e&1&&(B(0,`tr`)(1,`td`,37),J(2),V(),B(3,`td`,37),J(4),V(),B(5,`td`),J(6),V(),B(7,`td`,37),J(8),V(),B(9,`td`,37),J(10),V(),B(11,`td`,35),J(12),V(),B(13,`td`,35),M(14,ck,1,2),V()()),e&2){let e,n=t.$implicit,r=G();A(2),Y(n.id),A(2),Y(r.fmt(n.ms)),A(2),Y(r.isOver(n)?`Over`:`Within`),A(2),Y(n.checks),A(2),Y(n.passes),A(2),Y(n.trigger??``),A(2),N((e=n.components[0])?14:-1,e)}}var uk=0,dk=80,fk=10,pk=class e{cycles=$([]);range=Gv(null);gapUntil=dk;budgetLabel=`${HO} ms budget`;uid=`cd-chart-${uk++}`;hintId=`${this.uid}-hint`;active=k(null);anchor=null;dragging=!1;hover=k(null);focused=k(!1);max=Q(()=>qO(this.cycles()));budgetAt=Q(()=>HO/this.max()*100);bars=Q(()=>{let e=this.max();return this.cycles().map((t,n)=>({cycle:t,index:n,height:t.ms/e*100,over:UO(t),label:this.describe(t)}))});firstId=Q(()=>this.cycles()[0]?.id??0);lastId=Q(()=>this.cycles().at(-1)?.id??0);span=Q(()=>{let e=this.range();if(!e)return null;let t=this.cycles(),n=Math.min(e.from,e.to),r=Math.max(e.from,e.to),i=t.findIndex(e=>e.id>=n),a=-1;for(let e=t.length-1;e>=0;e--)if(t[e].id<=r){a=e;break}return i<0||aGO(this.cycles(),this.range()));startId=Q(()=>this.selected()[0]?.id??``);endId=Q(()=>this.selected().at(-1)?.id??``);band=Q(()=>{let e=this.span(),t=this.cycles().length;return!e||!t?null:{left:e.start/t*100,width:(e.end-e.start+1)/t*100}});summary=Q(()=>{let e=this.selected(),t=e.filter(UO).length;return`${this.span()?e.length===1?`Cycle ${e[0].id}`:`Cycles ${e[0].id} to ${e.at(-1).id} (${e.length})`:`All ${e.length} ${e.length===1?`cycle`:`cycles`}`}. ${t} over the ${HO} ms budget.`});tableLabel=Q(()=>this.span()?`Picked cycles, ${this.summary()}`:`All recorded cycles`);activeOptionId=Q(()=>{let e=this.active();return this.focused()&&e!==null&&e{let e=this.hover()??(this.focused()?this.active():null),t=e===null?void 0:this.cycles()[e];return!t||e===null?null:{cycle:t,over:UO(t),top:t.components.slice(0,3),...this.tipSide(e)}});tipSide(e){let t=this.cycles().length;return e+.5=t.start&&e<=t.end}describe(e){let t=[`Cycle ${e.id}`,WO(e.ms)];UO(e)&&t.push(`over budget`),e.trigger&&t.push(`after ${e.trigger}`),t.push(`${e.checks} ${e.checks===1?`check`:`checks`}`);let n=e.components[0];return n&&t.push(`slowest ${n.name} ${WO(n.ms)}`),t.join(`, `)}select(e,t){let n=this.cycles(),r=n[Math.min(e,t)],i=n[Math.max(e,t)];r&&i&&this.range.set({from:r.id,to:i.id})}showAll(){this.range.set(null),this.anchor=null}setEnd(e,t){if(!this.cycles().length)return;let n=t.target,r=Number.parseInt(n.value,10);if(Number.isNaN(r)){n.value=String(e===`from`?this.startId():this.endId());return}let i=Math.min(Math.max(r,this.firstId()),this.lastId()),a=this.selected(),o=e===`from`?i:a[0]?.id??this.firstId(),s=e===`to`?i:a.at(-1)?.id??this.lastId();this.range.set({from:Math.min(o,s),to:Math.max(o,s)}),n.value=String(e===`from`?Math.min(o,s):Math.max(o,s))}onFocus(){this.focused.set(!0),(this.active()===null||this.active()>=this.cycles().length)&&this.active.set(this.span()?.start??Math.max(this.cycles().length-1,0)),this.anchor??=this.active()}onKey(e){let t=this.cycles().length;if(!t)return;let n=Math.min(this.active()??t-1,t-1),r=null;switch(e.key){case`ArrowRight`:case`ArrowDown`:r=n+1;break;case`ArrowLeft`:case`ArrowUp`:r=n-1;break;case`PageDown`:r=n+fk;break;case`PageUp`:r=n-fk;break;case`Home`:r=0;break;case`End`:r=t-1;break;case`Enter`:case` `:e.preventDefault(),this.anchor=n,this.select(n,n);return;case`Escape`:this.range()&&(e.preventDefault(),this.showAll());return;default:return}e.preventDefault(),r=Math.min(Math.max(r,0),t-1),this.active.set(r),this.hover.set(null),e.shiftKey?(this.anchor??=n,this.select(this.anchor,r)):this.anchor=r}indexAt(e){let t=e.target?.closest?.(`[data-index]`);if(t)return Number(t.getAttribute(`data-index`));let n=e.currentTarget?.getBoundingClientRect(),r=this.cycles().length;if(!n?.width||!r)return null;let i=(e.clientX-n.left)/n.width;return Math.min(Math.max(Math.floor(i*r),0),r-1)}startDrag(e){if(e.button!==0)return;let t=this.indexAt(e);t!==null&&(this.dragging=!0,this.anchor=t,this.active.set(t),this.select(t,t))}onPointerMove(e){let t=this.indexAt(e);this.hover.set(t),this.dragging&&t!==null&&this.anchor!==null&&(this.active.set(t),this.select(this.anchor,t))}endDrag(){this.dragging=!1}static ɵfac=function(t){return new(t||e)};static ɵcmp=Kh({type:e,selectors:[[`app-cd-cycle-chart`]],hostBindings:function(e,t){e&1&&U(`pointerup`,function(){return t.endDrag()},Pu)(`pointercancel`,function(){return t.endDrag()},Pu)},inputs:{cycles:[1,`cycles`],range:[1,`range`]},outputs:{range:`rangeChange`},decls:69,vars:25,consts:[[1,`range`],[`type`,`number`,`inputmode`,`numeric`,3,`change`,`min`,`max`,`value`],[`type`,`button`],[1,`summary`],[1,`hint`,3,`id`],[1,`plot`],[`aria-hidden`,`true`,1,`axis`],[1,`tick`,`top`],[1,`tick`,`zero`],[1,`area`],[`aria-hidden`,`true`,1,`band`,3,`left`,`width`],[`aria-hidden`,`true`,1,`budget`],[`role`,`listbox`,`aria-multiselectable`,`true`,`aria-orientation`,`horizontal`,`tabindex`,`0`,1,`bars`,3,`keydown`,`focus`,`blur`,`pointerdown`,`pointermove`,`pointerleave`],[`role`,`option`,1,`col`,3,`id`,`over`,`out`,`active`],[`aria-hidden`,`true`,1,`tip`,3,`left`,`right`],[`aria-hidden`,`true`,1,`markers`],[3,`over`],[`aria-label`,`Chart key`,1,`legend`],[`aria-hidden`,`true`,1,`key`,`ok`],[`aria-hidden`,`true`,1,`key`,`over`],[`aria-hidden`,`true`,1,`key-marker`],[`aria-hidden`,`true`,1,`key`,`line`],[1,`table`],[`tabindex`,`0`,`role`,`region`,1,`table-wrap`],[1,`sr-only`],[`scope`,`col`,1,`num`],[`scope`,`col`],[`type`,`button`,3,`click`],[`aria-hidden`,`true`,1,`band`],[`role`,`option`,1,`col`,3,`id`],[1,`bar`],[`aria-hidden`,`true`,1,`tip`],[1,`tip-head`],[1,`flag`],[1,`tip-row`],[1,`mono`],[1,`mono`,`name`],[1,`num`]],template:function(e,t){if(e&1&&(B(0,`div`,0)(1,`label`)(2,`span`),J(3,`Start`),V(),B(4,`input`,1),W(`change`,function(e){return t.setEnd(`from`,e)}),V()(),B(5,`label`)(6,`span`),J(7,`End`),V(),B(8,`input`,1),W(`change`,function(e){return t.setEnd(`to`,e)}),V()(),M(9,ek,2,0,`button`,2),B(10,`p`,3),J(11),V()(),B(12,`p`,4),J(13,` Drag across the bars to pick a range of cycles. With the chart focused, the arrow keys move between cycles, Shift with an arrow key extends the range, Enter picks one cycle and Escape shows all cycles again. `),V(),B(14,`div`,5)(15,`div`,6)(16,`span`,7),J(17),V(),B(18,`span`,8),J(19,`0 ms`),V()(),B(20,`div`,9),M(21,tk,1,4,`div`,10),B(22,`div`,11)(23,`span`),J(24),V()(),B(25,`div`,12),W(`keydown`,function(e){return t.onKey(e)})(`focus`,function(){return t.onFocus()})(`blur`,function(){return t.focused.set(!1)})(`pointerdown`,function(e){return t.startDrag(e)})(`pointermove`,function(e){return t.onPointerMove(e)})(`pointerleave`,function(){return t.hover.set(null)}),P(26,nk,2,12,`div`,13,QO),V(),M(28,ok,12,12,`div`,14),V()(),B(29,`div`,15),P(30,sk,1,2,`span`,16,QO),V(),B(32,`ul`,17)(33,`li`),Vg(34,`span`,18),J(35,`Within budget`),V(),B(36,`li`),Vg(37,`span`,19)(38,`span`,20),J(39,`Over budget `),V(),B(40,`li`),Vg(41,`span`,21),J(42),V()(),B(43,`details`,22)(44,`summary`),J(45,`Show the cycles as a table`),V(),B(46,`div`,23)(47,`table`)(48,`caption`,24),J(49),V(),B(50,`thead`)(51,`tr`)(52,`th`,25),J(53,`Cycle`),V(),B(54,`th`,25),J(55,`Duration`),V(),B(56,`th`,26),J(57,`Budget`),V(),B(58,`th`,25),J(59,`Checks`),V(),B(60,`th`,25),J(61,`Passes`),V(),B(62,`th`,26),J(63,`After`),V(),B(64,`th`,26),J(65,`Slowest component`),V()()(),B(66,`tbody`),P(67,lk,15,7,`tr`,null,$O),V()()()()),e&2){let e,n;A(4),qg(`min`,t.firstId())(`max`,t.lastId())(`value`,t.startId()),A(4),qg(`min`,t.firstId())(`max`,t.lastId())(`value`,t.endId()),A(),N(t.range()?9:-1),A(2),Y(t.summary()),A(),qg(`id`,t.hintId),A(2),q(`flush`,t.bars().length>t.gapUntil),A(3),Y(t.fmt(t.max())),A(4),N((e=t.band())?21:-1,e),A(),K(`bottom`,t.budgetAt(),`%`),A(2),Y(t.budgetLabel),A(),j(`aria-label`,`Change detection cycles, `+t.summary())(`aria-describedby`,t.hintId)(`aria-activedescendant`,t.activeOptionId()),A(),F(t.bars()),A(2),N((n=t.tip())?28:-1,n),A(),q(`flush`,t.bars().length>t.gapUntil),A(),F(t.bars()),A(12),Y(t.budgetLabel),A(4),j(`aria-label`,t.tableLabel()),A(3),X(` `,t.tableLabel(),` `),A(18),F(t.selected())}},styles:[`[_nghost-%COMP%] { +}`]})},UO=16.6;function WO(e){return e.ms>UO}function GO(e){return`${e<10?e.toFixed(2):Math.round(e)} ms`}function KO(e,t){if(!t)return[...e];let n=Math.min(t.from,t.to),r=Math.max(t.from,t.to),i=e.filter(e=>e.id>=n&&e.id<=r);return i.length?i:[...e]}function qO(e){let t=new Map;for(let n of e)for(let e of n.components){let n=t.get(e.name);n?(n.checks+=e.checks,n.ms+=e.ms,n.maxMs=Math.max(n.maxMs,e.ms),n.cycles+=1):t.set(e.name,{name:e.name,checks:e.checks,ms:e.ms,maxMs:e.ms,cycles:1})}return[...t.values()].sort((e,t)=>t.ms-e.ms||e.name.localeCompare(t.name))}function JO(e){let t=e.reduce((e,t)=>Math.max(e,t.ms),0);return Math.max(t,UO*1.25)}function YO(e,t,n=3){let r=e.map(e=>({item:e,value:t(e)})).filter(e=>e.value>0).sort((e,t)=>t.value-e.value),i=r.reduce((e,t)=>e+t.value,0);if(!i)return[];let a=n/i,o=[],s=0,c=0,l=n,u=1,d=[],f=(e,t)=>{let n=e.reduce((e,t)=>e+t,0),r=Math.max(...e),i=Math.min(...e);return Math.max(t*t*r/(n*n),n*n/(t*t*i))},p=()=>{let e=d.reduce((e,t)=>e+t.area,0);if(l>=u){let t=e/u,n=c;for(let e of d){let r=e.area/t;o.push({item:e.item,x:s,y:n,w:t,h:r}),n+=r}s+=t,l-=t}else{let t=e/l,n=s;for(let e of d){let r=e.area/t;o.push({item:e.item,x:n,y:c,w:r,h:t}),n+=r}c+=t,u-=t}d=[]};for(let e of r){let t=e.value*a,n=Math.min(l,u),r=d.map(e=>e.area);d.length&&f([...r,t],n)>f(r,n)&&p(),d.push({item:e.item,area:t})}return d.length&&p(),o.map(e=>({item:e.item,x:e.x/n*100,y:e.y*100,w:e.w/n*100,h:e.h*100}))}var XO={c:`component`,h:`hook`,r:`afterRender`,o:`output`};function ZO(e,t){if(t===`c`)return e||null;if(t===`h`){let t=e.lastIndexOf(`.`);return t>0?e.slice(0,t):null}if(t===`o`){let t=e.indexOf(` (`),n=t>0?e.slice(0,t):e;return n&&n!==`?`?n:null}return null}function QO(e,t){let n=e.filter(e=>e.spans?.length);if(!n.length)return null;let r=n.map(e=>{let t=e.spans,n=Math.min(0,...t.map(e=>e[1])),r=Math.max(e.ms,...t.map(e=>e[1]+e[2]));return{cycle:e,spans:t,from:n,width:Math.max(r-n,.001)}}),i=r.reduce((e,t)=>e+t.width,0),a=[],o=[],s=0,c=0;for(let{cycle:e,spans:n,from:l,width:u}of r){o.push({id:e.id,x:s/i*100,w:u/i*100,droppedSpans:e.droppedSpans??0});let r=[...n].sort((e,t)=>e[1]-t[1]||e[3]-t[3]),d=[];for(let[n,o,u,f,p]of r){let r=t[n]??`unknown`,m=d[f-1],h=f>0&&m!==void 0&&ot.cycle.id,ek=(e,t)=>t.id;function tk(e,t){if(e&1){let e=H();B(0,`button`,27),W(`click`,function(){return E(e),D(G().showAll())}),J(1,`Show all cycles`),V()}}function nk(e,t){if(e&1&&Vg(0,`div`,28),e&2){let e=t;K(`left`,e.left,`%`)(`width`,e.width,`%`)}}function rk(e,t){if(e&1&&(B(0,`div`,29),Vg(1,`span`,30),V()),e&2){let e=t.$implicit,n=G();q(`over`,e.over)(`out`,!n.inRange(e.index)&&!!n.range())(`active`,n.focused()&&n.active()===e.index),qg(`id`,n.optionId(e.index)),j(`data-index`,e.index)(`aria-selected`,n.inRange(e.index))(`aria-label`,e.label),A(),K(`height`,e.height,`%`)}}function ik(e,t){e&1&&(B(0,`span`,33),J(1,`over budget`),V())}function ak(e,t){if(e&1&&(B(0,`p`),J(1,` after `),B(2,`span`,35),J(3),V()()),e&2){let e=G();A(3),Y(e.cycle.trigger)}}function ok(e,t){if(e&1&&(B(0,`p`,34)(1,`span`,36),J(2),V(),B(3,`span`),J(4),V()()),e&2){let e=t.$implicit,n=G(2);A(2),Y(e.name),A(2),Y(n.fmt(e.ms))}}function sk(e,t){if(e&1&&(B(0,`div`,31)(1,`p`,32)(2,`strong`),J(3),V(),B(4,`span`),J(5),V(),M(6,ik,2,0,`span`,33),V(),M(7,ak,4,1,`p`),B(8,`p`),J(9),V(),P(10,ok,5,2,`p`,34,kg),V()),e&2){let e=t,n=G();K(`left`,e.left)(`right`,e.right),A(3),Y(n.fmt(e.cycle.ms)),A(2),X(`cycle `,e.cycle.id),A(),N(e.over?6:-1),A(),N(e.cycle.trigger?7:-1),A(2),iv(` `,e.cycle.checks,` `,e.cycle.checks===1?`check`:`checks`,`, `,e.cycle.passes,` `,e.cycle.passes===1?`pass`:`passes`,` `),A(),F(e.top)}}function ck(e,t){if(e&1&&Vg(0,`span`),e&2){let e=t.$implicit;q(`over`,e.over)}}function lk(e,t){if(e&1&&J(0),e&2){let e=t,n=G(2);Z(` `,e.name,` (`,n.fmt(e.ms),`) `)}}function uk(e,t){if(e&1&&(B(0,`tr`)(1,`td`,37),J(2),V(),B(3,`td`,37),J(4),V(),B(5,`td`),J(6),V(),B(7,`td`,37),J(8),V(),B(9,`td`,37),J(10),V(),B(11,`td`,35),J(12),V(),B(13,`td`,35),M(14,lk,1,2),V()()),e&2){let e,n=t.$implicit,r=G();A(2),Y(n.id),A(2),Y(r.fmt(n.ms)),A(2),Y(r.isOver(n)?`Over`:`Within`),A(2),Y(n.checks),A(2),Y(n.passes),A(2),Y(n.trigger??``),A(2),N((e=n.components[0])?14:-1,e)}}var dk=0,fk=80,pk=10,mk=class e{cycles=$([]);range=Gv(null);gapUntil=fk;budgetLabel=`${UO} ms budget`;uid=`cd-chart-${dk++}`;hintId=`${this.uid}-hint`;active=k(null);anchor=null;dragging=!1;hover=k(null);focused=k(!1);max=Q(()=>JO(this.cycles()));budgetAt=Q(()=>UO/this.max()*100);bars=Q(()=>{let e=this.max();return this.cycles().map((t,n)=>({cycle:t,index:n,height:t.ms/e*100,over:WO(t),label:this.describe(t)}))});firstId=Q(()=>this.cycles()[0]?.id??0);lastId=Q(()=>this.cycles().at(-1)?.id??0);span=Q(()=>{let e=this.range();if(!e)return null;let t=this.cycles(),n=Math.min(e.from,e.to),r=Math.max(e.from,e.to),i=t.findIndex(e=>e.id>=n),a=-1;for(let e=t.length-1;e>=0;e--)if(t[e].id<=r){a=e;break}return i<0||aKO(this.cycles(),this.range()));startId=Q(()=>this.selected()[0]?.id??``);endId=Q(()=>this.selected().at(-1)?.id??``);band=Q(()=>{let e=this.span(),t=this.cycles().length;return!e||!t?null:{left:e.start/t*100,width:(e.end-e.start+1)/t*100}});summary=Q(()=>{let e=this.selected(),t=e.filter(WO).length;return`${this.span()?e.length===1?`Cycle ${e[0].id}`:`Cycles ${e[0].id} to ${e.at(-1).id} (${e.length})`:`All ${e.length} ${e.length===1?`cycle`:`cycles`}`}. ${t} over the ${UO} ms budget.`});tableLabel=Q(()=>this.span()?`Picked cycles, ${this.summary()}`:`All recorded cycles`);activeOptionId=Q(()=>{let e=this.active();return this.focused()&&e!==null&&e{let e=this.hover()??(this.focused()?this.active():null),t=e===null?void 0:this.cycles()[e];return!t||e===null?null:{cycle:t,over:WO(t),top:t.components.slice(0,3),...this.tipSide(e)}});tipSide(e){let t=this.cycles().length;return e+.5=t.start&&e<=t.end}describe(e){let t=[`Cycle ${e.id}`,GO(e.ms)];WO(e)&&t.push(`over budget`),e.trigger&&t.push(`after ${e.trigger}`),t.push(`${e.checks} ${e.checks===1?`check`:`checks`}`);let n=e.components[0];return n&&t.push(`slowest ${n.name} ${GO(n.ms)}`),t.join(`, `)}select(e,t){let n=this.cycles(),r=n[Math.min(e,t)],i=n[Math.max(e,t)];r&&i&&this.range.set({from:r.id,to:i.id})}showAll(){this.range.set(null),this.anchor=null}setEnd(e,t){if(!this.cycles().length)return;let n=t.target,r=Number.parseInt(n.value,10);if(Number.isNaN(r)){n.value=String(e===`from`?this.startId():this.endId());return}let i=Math.min(Math.max(r,this.firstId()),this.lastId()),a=this.selected(),o=e===`from`?i:a[0]?.id??this.firstId(),s=e===`to`?i:a.at(-1)?.id??this.lastId();this.range.set({from:Math.min(o,s),to:Math.max(o,s)}),n.value=String(e===`from`?Math.min(o,s):Math.max(o,s))}onFocus(){this.focused.set(!0),(this.active()===null||this.active()>=this.cycles().length)&&this.active.set(this.span()?.start??Math.max(this.cycles().length-1,0)),this.anchor??=this.active()}onKey(e){let t=this.cycles().length;if(!t)return;let n=Math.min(this.active()??t-1,t-1),r=null;switch(e.key){case`ArrowRight`:case`ArrowDown`:r=n+1;break;case`ArrowLeft`:case`ArrowUp`:r=n-1;break;case`PageDown`:r=n+pk;break;case`PageUp`:r=n-pk;break;case`Home`:r=0;break;case`End`:r=t-1;break;case`Enter`:case` `:e.preventDefault(),this.anchor=n,this.select(n,n);return;case`Escape`:this.range()&&(e.preventDefault(),this.showAll());return;default:return}e.preventDefault(),r=Math.min(Math.max(r,0),t-1),this.active.set(r),this.hover.set(null),e.shiftKey?(this.anchor??=n,this.select(this.anchor,r)):this.anchor=r}indexAt(e){let t=e.target?.closest?.(`[data-index]`);if(t)return Number(t.getAttribute(`data-index`));let n=e.currentTarget?.getBoundingClientRect(),r=this.cycles().length;if(!n?.width||!r)return null;let i=(e.clientX-n.left)/n.width;return Math.min(Math.max(Math.floor(i*r),0),r-1)}startDrag(e){if(e.button!==0)return;let t=this.indexAt(e);t!==null&&(this.dragging=!0,this.anchor=t,this.active.set(t),this.select(t,t))}onPointerMove(e){let t=this.indexAt(e);this.hover.set(t),this.dragging&&t!==null&&this.anchor!==null&&(this.active.set(t),this.select(this.anchor,t))}endDrag(){this.dragging=!1}static ɵfac=function(t){return new(t||e)};static ɵcmp=Kh({type:e,selectors:[[`app-cd-cycle-chart`]],hostBindings:function(e,t){e&1&&U(`pointerup`,function(){return t.endDrag()},Pu)(`pointercancel`,function(){return t.endDrag()},Pu)},inputs:{cycles:[1,`cycles`],range:[1,`range`]},outputs:{range:`rangeChange`},decls:69,vars:25,consts:[[1,`range`],[`type`,`number`,`inputmode`,`numeric`,3,`change`,`min`,`max`,`value`],[`type`,`button`],[1,`summary`],[1,`hint`,3,`id`],[1,`plot`],[`aria-hidden`,`true`,1,`axis`],[1,`tick`,`top`],[1,`tick`,`zero`],[1,`area`],[`aria-hidden`,`true`,1,`band`,3,`left`,`width`],[`aria-hidden`,`true`,1,`budget`],[`role`,`listbox`,`aria-multiselectable`,`true`,`aria-orientation`,`horizontal`,`tabindex`,`0`,1,`bars`,3,`keydown`,`focus`,`blur`,`pointerdown`,`pointermove`,`pointerleave`],[`role`,`option`,1,`col`,3,`id`,`over`,`out`,`active`],[`aria-hidden`,`true`,1,`tip`,3,`left`,`right`],[`aria-hidden`,`true`,1,`markers`],[3,`over`],[`aria-label`,`Chart key`,1,`legend`],[`aria-hidden`,`true`,1,`key`,`ok`],[`aria-hidden`,`true`,1,`key`,`over`],[`aria-hidden`,`true`,1,`key-marker`],[`aria-hidden`,`true`,1,`key`,`line`],[1,`table`],[`tabindex`,`0`,`role`,`region`,1,`table-wrap`],[1,`sr-only`],[`scope`,`col`,1,`num`],[`scope`,`col`],[`type`,`button`,3,`click`],[`aria-hidden`,`true`,1,`band`],[`role`,`option`,1,`col`,3,`id`],[1,`bar`],[`aria-hidden`,`true`,1,`tip`],[1,`tip-head`],[1,`flag`],[1,`tip-row`],[1,`mono`],[1,`mono`,`name`],[1,`num`]],template:function(e,t){if(e&1&&(B(0,`div`,0)(1,`label`)(2,`span`),J(3,`Start`),V(),B(4,`input`,1),W(`change`,function(e){return t.setEnd(`from`,e)}),V()(),B(5,`label`)(6,`span`),J(7,`End`),V(),B(8,`input`,1),W(`change`,function(e){return t.setEnd(`to`,e)}),V()(),M(9,tk,2,0,`button`,2),B(10,`p`,3),J(11),V()(),B(12,`p`,4),J(13,` Drag across the bars to pick a range of cycles. With the chart focused, the arrow keys move between cycles, Shift with an arrow key extends the range, Enter picks one cycle and Escape shows all cycles again. `),V(),B(14,`div`,5)(15,`div`,6)(16,`span`,7),J(17),V(),B(18,`span`,8),J(19,`0 ms`),V()(),B(20,`div`,9),M(21,nk,1,4,`div`,10),B(22,`div`,11)(23,`span`),J(24),V()(),B(25,`div`,12),W(`keydown`,function(e){return t.onKey(e)})(`focus`,function(){return t.onFocus()})(`blur`,function(){return t.focused.set(!1)})(`pointerdown`,function(e){return t.startDrag(e)})(`pointermove`,function(e){return t.onPointerMove(e)})(`pointerleave`,function(){return t.hover.set(null)}),P(26,rk,2,12,`div`,13,$O),V(),M(28,sk,12,12,`div`,14),V()(),B(29,`div`,15),P(30,ck,1,2,`span`,16,$O),V(),B(32,`ul`,17)(33,`li`),Vg(34,`span`,18),J(35,`Within budget`),V(),B(36,`li`),Vg(37,`span`,19)(38,`span`,20),J(39,`Over budget `),V(),B(40,`li`),Vg(41,`span`,21),J(42),V()(),B(43,`details`,22)(44,`summary`),J(45,`Show the cycles as a table`),V(),B(46,`div`,23)(47,`table`)(48,`caption`,24),J(49),V(),B(50,`thead`)(51,`tr`)(52,`th`,25),J(53,`Cycle`),V(),B(54,`th`,25),J(55,`Duration`),V(),B(56,`th`,26),J(57,`Budget`),V(),B(58,`th`,25),J(59,`Checks`),V(),B(60,`th`,25),J(61,`Passes`),V(),B(62,`th`,26),J(63,`After`),V(),B(64,`th`,26),J(65,`Slowest component`),V()()(),B(66,`tbody`),P(67,uk,15,7,`tr`,null,ek),V()()()()),e&2){let e,n;A(4),qg(`min`,t.firstId())(`max`,t.lastId())(`value`,t.startId()),A(4),qg(`min`,t.firstId())(`max`,t.lastId())(`value`,t.endId()),A(),N(t.range()?9:-1),A(2),Y(t.summary()),A(),qg(`id`,t.hintId),A(2),q(`flush`,t.bars().length>t.gapUntil),A(3),Y(t.fmt(t.max())),A(4),N((e=t.band())?21:-1,e),A(),K(`bottom`,t.budgetAt(),`%`),A(2),Y(t.budgetLabel),A(),j(`aria-label`,`Change detection cycles, `+t.summary())(`aria-describedby`,t.hintId)(`aria-activedescendant`,t.activeOptionId()),A(),F(t.bars()),A(2),N((n=t.tip())?28:-1,n),A(),q(`flush`,t.bars().length>t.gapUntil),A(),F(t.bars()),A(12),Y(t.budgetLabel),A(4),j(`aria-label`,t.tableLabel()),A(3),X(` `,t.tableLabel(),` `),A(18),F(t.selected())}},styles:[`[_nghost-%COMP%] { display: block; min-width: 0; } @@ -1098,7 +1098,7 @@ td.mono[_ngcontent-%COMP%] { .bar[_ngcontent-%COMP%] { transition: none; } -}`]})},mk=(e,t)=>t.id,hk=(e,t)=>t.index,gk=(e,t)=>t.kind;function _k(e,t){if(e&1&&(B(0,`p`,0),J(1),V()),e&2){let e=G(2);A(),Z(` Pick `,e.max,` cycles or fewer to see their spans. `,e.count(),` are picked. `)}}function vk(e,t){if(e&1&&(B(0,`span`),J(1),V()),e&2){let e=t.$implicit;K(`left`,e.x,`%`)(`width`,e.w,`%`),A(),X(`cycle `,e.id)}}function yk(e,t){if(e&1){let e=H();B(0,`button`,11),W(`focus`,function(){let t=E(e).$implicit;return D(G(3).current.set(t.index))})(`mouseenter`,function(){let t=E(e).$implicit;return D(G(3).hover.set(t.index))})(`mouseleave`,function(){return E(e),D(G(3).hover.set(null))})(`click`,function(){let t=E(e).$implicit;return D(G(3).choose(t))}),B(1,`span`,12),J(2),V()()}if(e&2){let e=t.$implicit,n=G(3);M_(pv(`block kind-`,e.kind)),K(`left`,e.x,`%`)(`width`,e.w,`%`)(`top`,e.depth*n.row,`px`),q(`narrow`,e.w<4),j(`tabindex`,e.index===n.tabStop()?0:-1)(`data-index`,e.index)(`aria-label`,n.describe(e))(`aria-disabled`,e.component?null:`true`),A(2),Y(e.name)}}function bk(e,t){if(e&1&&(B(0,`li`),Vg(1,`span`,13),J(2),V()),e&2){let e=t.$implicit;A(),M_(pv(`key kind-`,e.kind)),A(),Y(e.label)}}function xk(e,t){if(e&1&&(B(0,`p`,0),J(1),V()),e&2){let e=G(3);A(),Z(` Each cycle keeps its longest spans. `,e.dropped(),` shorter `,e.dropped()===1?`span was`:`spans were`,` left out. `)}}function Sk(e,t){if(e&1&&(B(0,`tr`)(1,`td`,17),J(2),V(),B(3,`td`,18),J(4),V(),B(5,`td`),J(6),V(),B(7,`td`,17),J(8),V(),B(9,`td`,17),J(10),V(),B(11,`td`,17),J(12),V(),B(13,`td`,17),J(14),V()()),e&2){let e=t.$implicit,n=G(4);A(2),Y(e.cycleId),A(),K(`padding-left`,6+e.depth*8,`px`),A(),Y(e.name),A(2),Y(n.kindLabel(e)),A(2),Y(e.depth),A(2),Y(n.fmt(e.start)),A(2),Y(n.fmt(e.ms)),A(2),Y(n.fmt(e.selfMs))}}function Ck(e,t){if(e&1&&(B(0,`div`,10)(1,`table`)(2,`caption`,14),J(3),V(),B(4,`thead`)(5,`tr`)(6,`th`,15),J(7,`Cycle`),V(),B(8,`th`,16),J(9,`Span`),V(),B(10,`th`,16),J(11,`Kind`),V(),B(12,`th`,15),J(13,`Depth`),V(),B(14,`th`,15),J(15,`Start`),V(),B(16,`th`,15),J(17,`Total`),V(),B(18,`th`,15),J(19,`Self`),V()()(),B(20,`tbody`),P(21,Sk,15,9,`tr`,null,hk),V()()()),e&2){let e=G(),t=G(2);j(`aria-label`,t.tableLabel()),A(3),X(` `,t.tableLabel(),` `),A(18),F(e.blocks)}}function wk(e,t){if(e&1){let e=H();B(0,`div`,1),W(`keydown`,function(t){return E(e),D(G(2).onKey(t))}),B(1,`div`,2),P(2,vk,2,5,`span`,3,mk),V(),B(4,`div`,4),P(5,yk,3,16,`button`,5,hk),V()(),B(7,`p`,6),J(8),V(),B(9,`ul`,7),P(10,bk,3,4,`li`,null,gk),V(),B(12,`p`,8),J(13,` Arrow keys move along a row, Up goes to the enclosing span and Down to the first nested one. Enter on a component shows it in the tree. `),V(),M(14,xk,2,2,`p`,0),B(15,`details`,9),W(`toggle`,function(t){return E(e),D(G(2).onToggle(t))}),B(16,`summary`),J(17,`Show the spans as a table`),V(),M(18,Ck,23,2,`div`,10),V()}if(e&2){let e=t,n=G(2);j(`aria-label`,n.label()),A(2),F(e.cycles),A(2),K(`height`,e.depth*n.row,`px`),A(),F(e.blocks),A(3),Y(n.readout()),A(2),F(n.kinds),A(4),N(n.dropped()?14:-1),A(4),N(n.tableOpen()?18:-1)}}function Tk(e,t){if(e&1&&(B(0,`h3`),J(1,`Spans in the picked cycles`),V(),M(2,_k,2,2,`p`,0)(3,wk,19,6)),e&2){let e,t=G();A(2),N(t.tooMany()?2:(e=t.graph())?3:-1,e)}}var Ek=22;function Dk(e,t){for(let n=e.length-1;n>=0;n--)if(t(e[n]))return e[n]}var Ok=class e{cycles=$([]);names=$([]);scope=$(`the recording`);pick=Rv();row=Ek;max=30;kinds=[`c`,`h`,`r`,`o`].map(e=>({kind:e,label:YO[e]}));current=k(0);tableOpen=k(!1);hover=k(null);hasSpans=Q(()=>this.cycles().some(e=>e.spans?.length));count=Q(()=>this.cycles().length);tooMany=Q(()=>this.count()>30);graph=Q(()=>this.tooMany()?null:ZO(this.cycles(),this.names()));tabStop=Q(()=>Math.min(this.current(),Math.max((this.graph()?.blocks.length??1)-1,0)));dropped=Q(()=>this.graph()?.cycles.reduce((e,t)=>e+t.droppedSpans,0)??0);label=Q(()=>`Spans in ${this.scope()} over time, nested by depth. Pick a component to show it in the tree.`);readout=Q(()=>{let e=(this.graph()?.blocks??[])[this.hover()??this.tabStop()];return e?this.describe(e):``});tableLabel=Q(()=>`Timed spans in ${this.scope()}`);fmt(e){return WO(e)}kindLabel(e){return YO[e.kind]}onToggle(e){this.tableOpen.set(e.target.open)}describe(e){let t=[e.name,YO[e.kind],`${WO(e.ms)} total`];return e.selfMs!==e.ms&&t.push(`${WO(e.selfMs)} self`),t.push(e.start<0?`cycle ${e.cycleId}, ${WO(-e.start)} before it started`:`cycle ${e.cycleId} at ${WO(e.start)}`),t.join(`, `)}choose(e){this.current.set(e.index),e.component&&this.pick.emit(e.component)}onKey(e){let t=this.graph()?.blocks??[];if(!t.length)return;let n=t[this.tabStop()],r;switch(e.key){case`ArrowRight`:r=t.find(e=>e.index>n.index&&e.depth===n.depth);break;case`ArrowLeft`:r=Dk(t,e=>e.indexe.indexe.parent===n.index)??t.find(e=>e.index>n.index&&e.depth===n.depth+1);break;case`Home`:r=t[0];break;case`End`:r=t.at(-1);break;default:return}e.preventDefault(),this.hover.set(null),r&&(this.current.set(r.index),e.currentTarget.querySelector(`button[data-index="${r.index}"]`)?.focus())}static ɵfac=function(t){return new(t||e)};static ɵcmp=Kh({type:e,selectors:[[`app-cd-flame-graph`]],inputs:{cycles:[1,`cycles`],names:[1,`names`],scope:[1,`scope`]},outputs:{pick:`pick`},decls:1,vars:1,consts:[[1,`note`],[`role`,`group`,1,`flame`,3,`keydown`],[`aria-hidden`,`true`,1,`cycles`],[3,`left`,`width`],[1,`rows`],[`type`,`button`,3,`class`,`narrow`,`left`,`width`,`top`],[`aria-hidden`,`true`,1,`readout`],[`aria-label`,`Span kinds`,1,`legend`],[1,`hint`],[1,`table`,3,`toggle`],[`tabindex`,`0`,`role`,`region`,1,`table-wrap`],[`type`,`button`,3,`focus`,`mouseenter`,`mouseleave`,`click`],[1,`text`],[`aria-hidden`,`true`],[1,`sr-only`],[`scope`,`col`,1,`num`],[`scope`,`col`],[1,`num`],[1,`mono`]],template:function(e,t){e&1&&M(0,Tk,4,1),e&2&&N(t.hasSpans()?0:-1)},styles:[`[_nghost-%COMP%] { +}`]})},hk=(e,t)=>t.id,gk=(e,t)=>t.index,_k=(e,t)=>t.kind;function vk(e,t){if(e&1&&(B(0,`p`,0),J(1),V()),e&2){let e=G(2);A(),Z(` Pick `,e.max,` cycles or fewer to see their spans. `,e.count(),` are picked. `)}}function yk(e,t){if(e&1&&(B(0,`span`),J(1),V()),e&2){let e=t.$implicit;K(`left`,e.x,`%`)(`width`,e.w,`%`),A(),X(`cycle `,e.id)}}function bk(e,t){if(e&1){let e=H();B(0,`button`,11),W(`focus`,function(){let t=E(e).$implicit;return D(G(3).current.set(t.index))})(`mouseenter`,function(){let t=E(e).$implicit;return D(G(3).hover.set(t.index))})(`mouseleave`,function(){return E(e),D(G(3).hover.set(null))})(`click`,function(){let t=E(e).$implicit;return D(G(3).choose(t))}),B(1,`span`,12),J(2),V()()}if(e&2){let e=t.$implicit,n=G(3);M_(pv(`block kind-`,e.kind)),K(`left`,e.x,`%`)(`width`,e.w,`%`)(`top`,e.depth*n.row,`px`),q(`narrow`,e.w<4),j(`tabindex`,e.index===n.tabStop()?0:-1)(`data-index`,e.index)(`aria-label`,n.describe(e))(`aria-disabled`,e.component?null:`true`),A(2),Y(e.name)}}function xk(e,t){if(e&1&&(B(0,`li`),Vg(1,`span`,13),J(2),V()),e&2){let e=t.$implicit;A(),M_(pv(`key kind-`,e.kind)),A(),Y(e.label)}}function Sk(e,t){if(e&1&&(B(0,`p`,0),J(1),V()),e&2){let e=G(3);A(),Z(` Each cycle keeps its longest spans. `,e.dropped(),` shorter `,e.dropped()===1?`span was`:`spans were`,` left out. `)}}function Ck(e,t){if(e&1&&(B(0,`tr`)(1,`td`,17),J(2),V(),B(3,`td`,18),J(4),V(),B(5,`td`),J(6),V(),B(7,`td`,17),J(8),V(),B(9,`td`,17),J(10),V(),B(11,`td`,17),J(12),V(),B(13,`td`,17),J(14),V()()),e&2){let e=t.$implicit,n=G(4);A(2),Y(e.cycleId),A(),K(`padding-left`,6+e.depth*8,`px`),A(),Y(e.name),A(2),Y(n.kindLabel(e)),A(2),Y(e.depth),A(2),Y(n.fmt(e.start)),A(2),Y(n.fmt(e.ms)),A(2),Y(n.fmt(e.selfMs))}}function wk(e,t){if(e&1&&(B(0,`div`,10)(1,`table`)(2,`caption`,14),J(3),V(),B(4,`thead`)(5,`tr`)(6,`th`,15),J(7,`Cycle`),V(),B(8,`th`,16),J(9,`Span`),V(),B(10,`th`,16),J(11,`Kind`),V(),B(12,`th`,15),J(13,`Depth`),V(),B(14,`th`,15),J(15,`Start`),V(),B(16,`th`,15),J(17,`Total`),V(),B(18,`th`,15),J(19,`Self`),V()()(),B(20,`tbody`),P(21,Ck,15,9,`tr`,null,gk),V()()()),e&2){let e=G(),t=G(2);j(`aria-label`,t.tableLabel()),A(3),X(` `,t.tableLabel(),` `),A(18),F(e.blocks)}}function Tk(e,t){if(e&1){let e=H();B(0,`div`,1),W(`keydown`,function(t){return E(e),D(G(2).onKey(t))}),B(1,`div`,2),P(2,yk,2,5,`span`,3,hk),V(),B(4,`div`,4),P(5,bk,3,16,`button`,5,gk),V()(),B(7,`p`,6),J(8),V(),B(9,`ul`,7),P(10,xk,3,4,`li`,null,_k),V(),B(12,`p`,8),J(13,` Arrow keys move along a row, Up goes to the enclosing span and Down to the first nested one. Enter on a component shows it in the tree. `),V(),M(14,Sk,2,2,`p`,0),B(15,`details`,9),W(`toggle`,function(t){return E(e),D(G(2).onToggle(t))}),B(16,`summary`),J(17,`Show the spans as a table`),V(),M(18,wk,23,2,`div`,10),V()}if(e&2){let e=t,n=G(2);j(`aria-label`,n.label()),A(2),F(e.cycles),A(2),K(`height`,e.depth*n.row,`px`),A(),F(e.blocks),A(3),Y(n.readout()),A(2),F(n.kinds),A(4),N(n.dropped()?14:-1),A(4),N(n.tableOpen()?18:-1)}}function Ek(e,t){if(e&1&&(B(0,`h3`),J(1,`Spans in the picked cycles`),V(),M(2,vk,2,2,`p`,0)(3,Tk,19,6)),e&2){let e,t=G();A(2),N(t.tooMany()?2:(e=t.graph())?3:-1,e)}}var Dk=22;function Ok(e,t){for(let n=e.length-1;n>=0;n--)if(t(e[n]))return e[n]}var kk=class e{cycles=$([]);names=$([]);scope=$(`the recording`);pick=Rv();row=Dk;max=30;kinds=[`c`,`h`,`r`,`o`].map(e=>({kind:e,label:XO[e]}));current=k(0);tableOpen=k(!1);hover=k(null);hasSpans=Q(()=>this.cycles().some(e=>e.spans?.length));count=Q(()=>this.cycles().length);tooMany=Q(()=>this.count()>30);graph=Q(()=>this.tooMany()?null:QO(this.cycles(),this.names()));tabStop=Q(()=>Math.min(this.current(),Math.max((this.graph()?.blocks.length??1)-1,0)));dropped=Q(()=>this.graph()?.cycles.reduce((e,t)=>e+t.droppedSpans,0)??0);label=Q(()=>`Spans in ${this.scope()} over time, nested by depth. Pick a component to show it in the tree.`);readout=Q(()=>{let e=(this.graph()?.blocks??[])[this.hover()??this.tabStop()];return e?this.describe(e):``});tableLabel=Q(()=>`Timed spans in ${this.scope()}`);fmt(e){return GO(e)}kindLabel(e){return XO[e.kind]}onToggle(e){this.tableOpen.set(e.target.open)}describe(e){let t=[e.name,XO[e.kind],`${GO(e.ms)} total`];return e.selfMs!==e.ms&&t.push(`${GO(e.selfMs)} self`),t.push(e.start<0?`cycle ${e.cycleId}, ${GO(-e.start)} before it started`:`cycle ${e.cycleId} at ${GO(e.start)}`),t.join(`, `)}choose(e){this.current.set(e.index),e.component&&this.pick.emit(e.component)}onKey(e){let t=this.graph()?.blocks??[];if(!t.length)return;let n=t[this.tabStop()],r;switch(e.key){case`ArrowRight`:r=t.find(e=>e.index>n.index&&e.depth===n.depth);break;case`ArrowLeft`:r=Ok(t,e=>e.indexe.indexe.parent===n.index)??t.find(e=>e.index>n.index&&e.depth===n.depth+1);break;case`Home`:r=t[0];break;case`End`:r=t.at(-1);break;default:return}e.preventDefault(),this.hover.set(null),r&&(this.current.set(r.index),e.currentTarget.querySelector(`button[data-index="${r.index}"]`)?.focus())}static ɵfac=function(t){return new(t||e)};static ɵcmp=Kh({type:e,selectors:[[`app-cd-flame-graph`]],inputs:{cycles:[1,`cycles`],names:[1,`names`],scope:[1,`scope`]},outputs:{pick:`pick`},decls:1,vars:1,consts:[[1,`note`],[`role`,`group`,1,`flame`,3,`keydown`],[`aria-hidden`,`true`,1,`cycles`],[3,`left`,`width`],[1,`rows`],[`type`,`button`,3,`class`,`narrow`,`left`,`width`,`top`],[`aria-hidden`,`true`,1,`readout`],[`aria-label`,`Span kinds`,1,`legend`],[1,`hint`],[1,`table`,3,`toggle`],[`tabindex`,`0`,`role`,`region`,1,`table-wrap`],[`type`,`button`,3,`focus`,`mouseenter`,`mouseleave`,`click`],[1,`text`],[`aria-hidden`,`true`],[1,`sr-only`],[`scope`,`col`,1,`num`],[`scope`,`col`],[1,`num`],[1,`mono`]],template:function(e,t){e&1&&M(0,Ek,4,1),e&2&&N(t.hasSpans()?0:-1)},styles:[`[_nghost-%COMP%] { display: block; min-width: 0; } @@ -1305,7 +1305,7 @@ td.mono[_ngcontent-%COMP%] { .num[_ngcontent-%COMP%] { text-align: right; font-variant-numeric: tabular-nums; -}`]})};function kk(e,t){if(e&1&&(B(0,`tr`)(1,`td`,5),J(2),V(),B(3,`td`,6),J(4),V(),B(5,`td`,6),J(6),V(),B(7,`td`,6),J(8),V()()),e&2){let e=t.$implicit,n=G(3);A(2),Z(``,e.name,`.`,e.hook),A(2),Y(e.calls),A(2),Y(n.ms(e.ms)),A(2),Y(n.ms(e.maxMs))}}function Ak(e,t){if(e&1&&(B(0,`div`)(1,`h3`),J(2,`Lifecycle hooks`),V(),B(3,`table`)(4,`thead`)(5,`tr`)(6,`th`,3),J(7,`Hook`),V(),B(8,`th`,4),J(9,`Calls`),V(),B(10,`th`,4),J(11,`Time`),V(),B(12,`th`,4),J(13,`Slowest`),V()()(),B(14,`tbody`),P(15,kk,9,5,`tr`,null,kg),V()()()),e&2){let e=G(2);A(15),F(e.hooks())}}function jk(e,t){if(e&1&&J(0),e&2){let e=G().$implicit;X(` (`,e.listener,`) `)}}function Mk(e,t){if(e&1&&(B(0,`tr`)(1,`td`,5),J(2),M(3,jk,1,1),V(),B(4,`td`,6),J(5),V(),B(6,`td`,6),J(7),V(),B(8,`td`,6),J(9),V()()),e&2){let e=t.$implicit,n=G(3);A(2),X(` `,e.name,` `),A(),N(e.listener?3:-1),A(2),Y(e.calls),A(2),Y(n.ms(e.ms)),A(2),Y(n.ms(e.maxMs))}}function Nk(e,t){if(e&1&&(B(0,`div`)(1,`h3`),J(2,`Output listeners`),V(),B(3,`table`)(4,`thead`)(5,`tr`)(6,`th`,3),J(7,`Listener`),V(),B(8,`th`,4),J(9,`Calls`),V(),B(10,`th`,4),J(11,`Time`),V(),B(12,`th`,4),J(13,`Slowest`),V()()(),B(14,`tbody`),P(15,Mk,10,5,`tr`,null,kg),V()()()),e&2){let e=G(2);A(15),F(e.outputs())}}function Pk(e,t){if(e&1&&(B(0,`p`,1),J(1),V()),e&2){let e=t,n=G(2);A(),iv(` afterRender callbacks ran `,e.runs,` `,e.runs===1?`time`:`times`,`, `,n.ms(e.ms),` in total, slowest `,n.ms(e.maxMs),`. `)}}function Fk(e,t){if(e&1&&(B(0,`p`,2),J(1),V()),e&2){let e=G(2);A(),Z(` `,e.dropped(),` more hook or output `,e.dropped()===1?`row is`:`rows are`,` left out. A recording lists at most 100 of each. `)}}function Ik(e,t){if(e&1&&(B(0,`div`,0),M(1,Ak,17,0,`div`),M(2,Nk,17,0,`div`),V(),M(3,Pk,2,4,`p`,1),M(4,Fk,2,2,`p`,2)),e&2){let e,t=G();A(),N(t.hooks().length?1:-1),A(),N(t.outputs().length?2:-1),A(),N((e=t.afterRender())?3:-1,e),A(),N(t.dropped()?4:-1)}}var Lk=10;function Rk(e){return`${e<10?e.toFixed(2):Math.round(e)} ms`}var zk=class e{page=$(null);hooks=Q(()=>(this.page()?.hooks??[]).slice(0,Lk));outputs=Q(()=>(this.page()?.outputs??[]).slice(0,Lk));afterRender=Q(()=>{let e=this.page()?.afterRender;return e?.runs?e:null});dropped=Q(()=>this.page()?.droppedTimings??0);ms(e){return Rk(e)}static ɵfac=function(t){return new(t||e)};static ɵcmp=Kh({type:e,selectors:[[`app-cd-timing`]],inputs:{page:[1,`page`]},decls:1,vars:1,consts:[[1,`timing`],[1,`after-render`],[1,`note`],[`scope`,`col`],[`scope`,`col`,1,`num`],[1,`mono`],[1,`num`]],template:function(e,t){e&1&&M(0,Ik,5,4),e&2&&N(t.hooks().length||t.outputs().length||t.afterRender()?0:-1)},styles:[`[_nghost-%COMP%] { +}`]})};function Ak(e,t){if(e&1&&(B(0,`tr`)(1,`td`,5),J(2),V(),B(3,`td`,6),J(4),V(),B(5,`td`,6),J(6),V(),B(7,`td`,6),J(8),V()()),e&2){let e=t.$implicit,n=G(3);A(2),Z(``,e.name,`.`,e.hook),A(2),Y(e.calls),A(2),Y(n.ms(e.ms)),A(2),Y(n.ms(e.maxMs))}}function jk(e,t){if(e&1&&(B(0,`div`)(1,`h3`),J(2,`Lifecycle hooks`),V(),B(3,`table`)(4,`thead`)(5,`tr`)(6,`th`,3),J(7,`Hook`),V(),B(8,`th`,4),J(9,`Calls`),V(),B(10,`th`,4),J(11,`Time`),V(),B(12,`th`,4),J(13,`Slowest`),V()()(),B(14,`tbody`),P(15,Ak,9,5,`tr`,null,kg),V()()()),e&2){let e=G(2);A(15),F(e.hooks())}}function Mk(e,t){if(e&1&&J(0),e&2){let e=G().$implicit;X(` (`,e.listener,`) `)}}function Nk(e,t){if(e&1&&(B(0,`tr`)(1,`td`,5),J(2),M(3,Mk,1,1),V(),B(4,`td`,6),J(5),V(),B(6,`td`,6),J(7),V(),B(8,`td`,6),J(9),V()()),e&2){let e=t.$implicit,n=G(3);A(2),X(` `,e.name,` `),A(),N(e.listener?3:-1),A(2),Y(e.calls),A(2),Y(n.ms(e.ms)),A(2),Y(n.ms(e.maxMs))}}function Pk(e,t){if(e&1&&(B(0,`div`)(1,`h3`),J(2,`Output listeners`),V(),B(3,`table`)(4,`thead`)(5,`tr`)(6,`th`,3),J(7,`Listener`),V(),B(8,`th`,4),J(9,`Calls`),V(),B(10,`th`,4),J(11,`Time`),V(),B(12,`th`,4),J(13,`Slowest`),V()()(),B(14,`tbody`),P(15,Nk,10,5,`tr`,null,kg),V()()()),e&2){let e=G(2);A(15),F(e.outputs())}}function Fk(e,t){if(e&1&&(B(0,`p`,1),J(1),V()),e&2){let e=t,n=G(2);A(),iv(` afterRender callbacks ran `,e.runs,` `,e.runs===1?`time`:`times`,`, `,n.ms(e.ms),` in total, slowest `,n.ms(e.maxMs),`. `)}}function Ik(e,t){if(e&1&&(B(0,`p`,2),J(1),V()),e&2){let e=G(2);A(),Z(` `,e.dropped(),` more hook or output `,e.dropped()===1?`row is`:`rows are`,` left out. A recording lists at most 100 of each. `)}}function Lk(e,t){if(e&1&&(B(0,`div`,0),M(1,jk,17,0,`div`),M(2,Pk,17,0,`div`),V(),M(3,Fk,2,4,`p`,1),M(4,Ik,2,2,`p`,2)),e&2){let e,t=G();A(),N(t.hooks().length?1:-1),A(),N(t.outputs().length?2:-1),A(),N((e=t.afterRender())?3:-1,e),A(),N(t.dropped()?4:-1)}}var Rk=10;function zk(e){return`${e<10?e.toFixed(2):Math.round(e)} ms`}var Bk=class e{page=$(null);hooks=Q(()=>(this.page()?.hooks??[]).slice(0,Rk));outputs=Q(()=>(this.page()?.outputs??[]).slice(0,Rk));afterRender=Q(()=>{let e=this.page()?.afterRender;return e?.runs?e:null});dropped=Q(()=>this.page()?.droppedTimings??0);ms(e){return zk(e)}static ɵfac=function(t){return new(t||e)};static ɵcmp=Kh({type:e,selectors:[[`app-cd-timing`]],inputs:{page:[1,`page`]},decls:1,vars:1,consts:[[1,`timing`],[1,`after-render`],[1,`note`],[`scope`,`col`],[`scope`,`col`,1,`num`],[1,`mono`],[1,`num`]],template:function(e,t){e&1&&M(0,Lk,5,4),e&2&&N(t.hooks().length||t.outputs().length||t.afterRender()?0:-1)},styles:[`[_nghost-%COMP%] { display: block; } @@ -1365,7 +1365,7 @@ td.mono[_ngcontent-%COMP%] { margin: 8px 0 0; color: var(--%NS%text-2); font-size: 12px; -}`]})},Bk=864e13,Vk=200,Hk=300,Uk=100,Wk=10,Gk=100,Kk=2e3,qk=2e3,Jk=4e3,Yk=1e3,Xk=100,Zk={component:`c`,hook:`h`,afterRender:`r`,output:`o`},Qk=Object.values(Zk),$k=e=>!!e&&typeof e==`object`&&!Array.isArray(e),eA=e=>typeof e==`number`&&Number.isFinite(e)&&e>=0?e:0,tA=(e,t)=>typeof e==`string`?e.slice(0,t):``,nA=e=>eA(e)>0?eA(e):void 0,rA=(e,t)=>Array.isArray(e)?e.slice(0,t):[];function iA(e){if(!$k(e))return null;let t=tA(e.name,Vk);return t?{name:t,checks:eA(e.checks),ms:eA(e.ms)}:null}function aA(e,t){if(!Array.isArray(e)||e.length!==5)return null;let[n,r,i,a,o]=e;return typeof n!=`number`||!Number.isInteger(n)||n<0||n>=t||typeof r!=`number`||!Number.isFinite(r)||Math.abs(r)>1e7||typeof a!=`number`||!Number.isInteger(a)||a<0||a>Yk||typeof o!=`string`||!Qk.includes(o)?null:[n,r,eA(i),a,o]}function oA(e,t){if(!$k(e)||typeof e.id!=`number`)return null;let n=rA(e.components,Wk).map(iA).filter(e=>!!e),r=tA(e.trigger,Hk),i=rA(e.spans,40).map(e=>aA(e,t)).filter(e=>!!e),a=Array.isArray(e.spans)?e.spans.length-40:0,o=Math.floor(eA(e.droppedSpans))+Math.max(0,a),s={t:typeof e.t==`number`?eA(e.t):void 0,hookMs:nA(e.hookMs),afterRenderMs:nA(e.afterRenderMs),outputMs:nA(e.outputMs)};return{id:eA(e.id),at:eA(e.at),ms:eA(e.ms),passes:eA(e.passes),...r?{trigger:r}:{},checks:eA(e.checks),components:n,...Object.fromEntries(Object.entries(s).filter(([,e])=>e!==void 0)),...i.length?{spans:i}:{},...o?{droppedSpans:o}:{}}}function sA(e){let t=iA(e);return!t||!$k(e)?null:{...t,maxMs:eA(e.maxMs),cycles:eA(e.cycles)}}function cA(e){if(!$k(e))return null;let t=tA(e.name,Vk),n=tA(e.hook,Uk);if(!t||!n)return null;let{calls:r,ms:i,maxMs:a}=e;return{name:t,hook:n,calls:eA(r),ms:eA(i),maxMs:eA(a)}}function lA(e){if(!$k(e))return null;let t=tA(e.name,Vk);if(!t)return null;let n=tA(e.listener,Vk);return{name:t,...n?{listener:n}:{},calls:eA(e.calls),ms:eA(e.ms),maxMs:eA(e.maxMs)}}function uA(e){if($k(e)&&eA(e.runs))return{runs:eA(e.runs),ms:eA(e.ms),maxMs:eA(e.maxMs)}}function dA(e,t){if(!$k(e))return null;let n=rA(e.spanNames,Jk).map(e=>tA(e,Hk)),r=Array.isArray(e.cycles)?e.cycles.slice(-Math.min(t,qk)).map(e=>oA(e,n.length)).filter(e=>!!e):[],i={},a=0;if($k(e.hosts)){let t=Object.entries(e.hosts);a=Math.max(0,t.length-Kk);for(let[e,n]of t.slice(0,Kk))e.length<50&&eA(n)&&(i[e]=eA(n))}let o=Array.isArray(e.components)?e.components:[],s=Math.floor(eA(e.droppedClasses))+Math.max(0,o.length-Gk),c=Math.floor(eA(e.droppedHosts))+a,l=rA(e.hooks,Xk).map(cA).filter(e=>!!e),u=rA(e.outputs,Xk).map(lA).filter(e=>!!e),d=Math.max(0,rA(e.hooks,1/0).length-Xk)+Math.max(0,rA(e.outputs,1/0).length-Xk),f=Math.floor(eA(e.droppedTimings))+d,p=uA(e.afterRender),m=r.some(e=>e.spans?.length);return{recording:e.recording===!0,startedAt:typeof e.startedAt==`number`&&e.startedAt<=Bk?eA(e.startedAt):null,dropped:eA(e.dropped),cycles:r,components:o.slice(0,Gk).map(sA).filter(e=>!!e),...s?{droppedClasses:s}:{},hosts:i,...c?{droppedHosts:c}:{},...l.length?{hooks:l}:{},...u.length?{outputs:u}:{},...f?{droppedTimings:f}:{},...p?{afterRender:p}:{},...m?{spanNames:n}:{}}}var fA=`pangular-inspector/change-detection`;function pA(e,t,n=Date.now()){return{format:fA,version:1,exportedAt:n,pageId:t,recording:{...dA(e,qk)??e,recording:!1,hosts:{}}}}function mA(e,t){if(e.length>2e7||e.length*3>2e7&&new TextEncoder().encode(e).byteLength>2e7)return{ok:!1,error:`The file is larger than 20 MB.`};let n;try{n=JSON.parse(e)}catch{return{ok:!1,error:`The file is not valid JSON.`}}if(!$k(n))return{ok:!1,error:`The file is not a recording.`};if(n.format!==void 0&&n.format!==`pangular-inspector/change-detection`)return{ok:!1,error:`The file is not a change detection recording.`};let r=$k(n.recording)?n.recording:n;if(!Array.isArray(r.cycles))return{ok:!1,error:`The file has no change detection cycles.`};let i=dA(r,t);if(!i)return{ok:!1,error:`The file is not a recording.`};let a=tA(n.pageId??r.pageId,49)||`imported`,o=nA(n.exportedAt);return{ok:!0,pageId:a,...o?{exportedAt:o}:{},recording:{...i,recording:!1}}}var hA=1,gA=1,_A=2,vA={c:`component`,h:`lifecycle-hook`,r:`after-render`,o:`output`},yA=e=>Math.round(e*1e6)/1e3;function bA(e,t){let n=e.spanNames??[],r=e.cycles[0]?.at??0,i=e.startedAt??r,a=[],o=0,s=0;for(let t of e.cycles){let e=t.t??Math.max(0,t.at-i),r=e+t.ms;a.push({name:`Change detection #${t.id}`,cat:`change-detection`,ph:`X`,pid:hA,tid:gA,ts:yA(e),dur:yA(t.ms),args:{checks:t.checks,passes:t.passes,...t.trigger?{trigger:t.trigger}:{},...t.hookMs?{hookMs:t.hookMs}:{},...t.afterRenderMs?{afterRenderMs:t.afterRenderMs}:{},...t.outputMs?{outputMs:t.outputMs}:{},slowest:t.components.map(e=>`${e.name} ${e.checks}x ${e.ms}ms`)}}),s+=t.droppedSpans??0;let c=[{end:r}],l=[...t.spans??[]].sort((e,t)=>e[1]-t[1]||t[2]-e[2]);for(let[r,i,s,u,d]of l){o++;let l=n[r]??`?`;if(d===Zk.output){let n=e+i;a.push({name:l,cat:vA[d],ph:`X`,pid:hA,tid:_A,ts:yA(n),dur:yA(i<0?Math.max(0,Math.min(s,-i)):s),args:{cycle:t.id}});continue}let f=Math.max(e,e+i);for(;c.length>1&&c.at(-1).end<=f;)c.pop();let p=c.at(-1).end,m=Math.min(f+s,p);me.ts??0));if(c<0)for(let e of a)e.ts=yA((e.ts-c)/1e3);a.sort((e,t)=>e.ts-t.ts||t.dur-e.dur);let l=(e,t,n)=>({name:t,ph:`M`,pid:hA,tid:e,args:{name:n}});return{traceEvents:[l(0,`process_name`,`Angular change detection (page ${t})`),l(gA,`thread_name`,`Change detection`),l(_A,`thread_name`,`Outputs`),...a],displayTimeUnit:`ms`,otherData:{source:`Pangular Inspector change detection recording`,pageId:t,cycles:e.cycles.length,droppedCycles:e.dropped,spans:o,droppedSpans:s,...e.startedAt&&e.startedAt<=Bk?{startedAt:new Date(e.startedAt).toISOString()}:{}}}}function xA(e,t){if(e&1&&(B(0,`p`,5),J(1),V()),e&2){let e=G();A(),Y(e.error())}}function SA(e,t){let n=new Blob([JSON.stringify(e,null,2)],{type:`application/json`}),r=URL.createObjectURL(n),i=document.createElement(`a`);i.href=r,i.download=t,document.body.append(i),i.click(),i.remove(),setTimeout(()=>URL.revokeObjectURL(r),0)}var CA=class e{page=$(null);maxCycles=$(200);imported=Rv();error=k(``);hasCycles(){return(this.page()?.cycles.length??0)>0}exportJson(){let e=this.page();e&&SA(pA(e,e.pageId),`change-detection-${e.pageId}.json`)}exportTrace(){let e=this.page();e&&SA(bA(e,e.pageId),`change-detection-${e.pageId}.trace.json`)}async read(e){let t=e.files?.[0];if(e.value=``,!t)return;if(t.size>2e7){this.error.set(`${t.name}: The file is larger than 20 MB.`);return}let n;try{n=await t.text()}catch{this.error.set(`Could not read the file.`);return}let r=mA(n,this.maxCycles());if(!r.ok){this.error.set(`${t.name}: ${r.error}`);return}this.error.set(``),this.imported.emit({fileName:t.name,page:{...r.recording,pageId:r.pageId,supported:!0}})}static ɵfac=function(t){return new(t||e)};static ɵcmp=Kh({type:e,selectors:[[`app-cd-transfer`]],inputs:{page:[1,`page`],maxCycles:[1,`maxCycles`]},outputs:{imported:`imported`},decls:10,vars:3,consts:[[`file`,``],[`role`,`group`,`aria-label`,`Export and import the recording`,1,`transfer`],[`type`,`button`,3,`click`,`disabled`],[`type`,`button`,3,`click`],[`type`,`file`,`accept`,`.json,application/json`,`hidden`,``,3,`change`],[`role`,`alert`,1,`error`]],template:function(e,t){if(e&1){let e=H();B(0,`div`,1)(1,`button`,2),W(`click`,function(){return t.exportJson()}),J(2,`Export JSON`),V(),B(3,`button`,2),W(`click`,function(){return t.exportTrace()}),J(4,` Export trace (Perfetto) `),V(),B(5,`button`,3),W(`click`,function(){return E(e),D(d_(8).click())}),J(6,`Import`),V(),B(7,`input`,4,0),W(`change`,function(){E(e);let n=d_(8);return D(t.read(n))}),V()(),M(9,xA,2,1,`p`,5)}e&2&&(A(),qg(`disabled`,!t.hasCycles()),A(2),qg(`disabled`,!t.hasCycles()),A(6),N(t.error()?9:-1))},styles:[`[_nghost-%COMP%] { +}`]})},Vk=864e13,Hk=200,Uk=300,Wk=100,Gk=10,Kk=100,qk=2e3,Jk=2e3,Yk=4e3,Xk=1e3,Zk=100,Qk={component:`c`,hook:`h`,afterRender:`r`,output:`o`},$k=Object.values(Qk),eA=e=>!!e&&typeof e==`object`&&!Array.isArray(e),tA=e=>typeof e==`number`&&Number.isFinite(e)&&e>=0?e:0,nA=(e,t)=>typeof e==`string`?e.slice(0,t):``,rA=e=>tA(e)>0?tA(e):void 0,iA=(e,t)=>Array.isArray(e)?e.slice(0,t):[];function aA(e){if(!eA(e))return null;let t=nA(e.name,Hk);return t?{name:t,checks:tA(e.checks),ms:tA(e.ms)}:null}function oA(e,t){if(!Array.isArray(e)||e.length!==5)return null;let[n,r,i,a,o]=e;return typeof n!=`number`||!Number.isInteger(n)||n<0||n>=t||typeof r!=`number`||!Number.isFinite(r)||Math.abs(r)>1e7||typeof a!=`number`||!Number.isInteger(a)||a<0||a>Xk||typeof o!=`string`||!$k.includes(o)?null:[n,r,tA(i),a,o]}function sA(e,t){if(!eA(e)||typeof e.id!=`number`)return null;let n=iA(e.components,Gk).map(aA).filter(e=>!!e),r=nA(e.trigger,Uk),i=iA(e.spans,40).map(e=>oA(e,t)).filter(e=>!!e),a=Array.isArray(e.spans)?e.spans.length-40:0,o=Math.floor(tA(e.droppedSpans))+Math.max(0,a),s={t:typeof e.t==`number`?tA(e.t):void 0,hookMs:rA(e.hookMs),afterRenderMs:rA(e.afterRenderMs),outputMs:rA(e.outputMs)};return{id:tA(e.id),at:tA(e.at),ms:tA(e.ms),passes:tA(e.passes),...r?{trigger:r}:{},checks:tA(e.checks),components:n,...Object.fromEntries(Object.entries(s).filter(([,e])=>e!==void 0)),...i.length?{spans:i}:{},...o?{droppedSpans:o}:{}}}function cA(e){let t=aA(e);return!t||!eA(e)?null:{...t,maxMs:tA(e.maxMs),cycles:tA(e.cycles)}}function lA(e){if(!eA(e))return null;let t=nA(e.name,Hk),n=nA(e.hook,Wk);if(!t||!n)return null;let{calls:r,ms:i,maxMs:a}=e;return{name:t,hook:n,calls:tA(r),ms:tA(i),maxMs:tA(a)}}function uA(e){if(!eA(e))return null;let t=nA(e.name,Hk);if(!t)return null;let n=nA(e.listener,Hk);return{name:t,...n?{listener:n}:{},calls:tA(e.calls),ms:tA(e.ms),maxMs:tA(e.maxMs)}}function dA(e){if(eA(e)&&tA(e.runs))return{runs:tA(e.runs),ms:tA(e.ms),maxMs:tA(e.maxMs)}}function fA(e,t){if(!eA(e))return null;let n=iA(e.spanNames,Yk).map(e=>nA(e,Uk)),r=Array.isArray(e.cycles)?e.cycles.slice(-Math.min(t,Jk)).map(e=>sA(e,n.length)).filter(e=>!!e):[],i={},a=0;if(eA(e.hosts)){let t=Object.entries(e.hosts);a=Math.max(0,t.length-qk);for(let[e,n]of t.slice(0,qk))e.length<50&&tA(n)&&(i[e]=tA(n))}let o=Array.isArray(e.components)?e.components:[],s=Math.floor(tA(e.droppedClasses))+Math.max(0,o.length-Kk),c=Math.floor(tA(e.droppedHosts))+a,l=iA(e.hooks,Zk).map(lA).filter(e=>!!e),u=iA(e.outputs,Zk).map(uA).filter(e=>!!e),d=Math.max(0,iA(e.hooks,1/0).length-Zk)+Math.max(0,iA(e.outputs,1/0).length-Zk),f=Math.floor(tA(e.droppedTimings))+d,p=dA(e.afterRender),m=r.some(e=>e.spans?.length);return{recording:e.recording===!0,startedAt:typeof e.startedAt==`number`&&e.startedAt<=Vk?tA(e.startedAt):null,dropped:tA(e.dropped),cycles:r,components:o.slice(0,Kk).map(cA).filter(e=>!!e),...s?{droppedClasses:s}:{},hosts:i,...c?{droppedHosts:c}:{},...l.length?{hooks:l}:{},...u.length?{outputs:u}:{},...f?{droppedTimings:f}:{},...p?{afterRender:p}:{},...m?{spanNames:n}:{}}}var pA=`pangular-inspector/change-detection`;function mA(e,t,n=Date.now()){return{format:pA,version:1,exportedAt:n,pageId:t,recording:{...fA(e,Jk)??e,recording:!1,hosts:{}}}}function hA(e,t){if(e.length>2e7||e.length*3>2e7&&new TextEncoder().encode(e).byteLength>2e7)return{ok:!1,error:`The file is larger than 20 MB.`};let n;try{n=JSON.parse(e)}catch{return{ok:!1,error:`The file is not valid JSON.`}}if(!eA(n))return{ok:!1,error:`The file is not a recording.`};if(n.format!==void 0&&n.format!==`pangular-inspector/change-detection`)return{ok:!1,error:`The file is not a change detection recording.`};let r=eA(n.recording)?n.recording:n;if(!Array.isArray(r.cycles))return{ok:!1,error:`The file has no change detection cycles.`};let i=fA(r,t);if(!i)return{ok:!1,error:`The file is not a recording.`};let a=nA(n.pageId??r.pageId,49)||`imported`,o=rA(n.exportedAt);return{ok:!0,pageId:a,...o?{exportedAt:o}:{},recording:{...i,recording:!1}}}var gA=1,_A=1,vA=2,yA={c:`component`,h:`lifecycle-hook`,r:`after-render`,o:`output`},bA=e=>Math.round(e*1e6)/1e3;function xA(e,t){let n=e.spanNames??[],r=e.cycles[0]?.at??0,i=e.startedAt??r,a=[],o=0,s=0;for(let t of e.cycles){let e=t.t??Math.max(0,t.at-i),r=e+t.ms;a.push({name:`Change detection #${t.id}`,cat:`change-detection`,ph:`X`,pid:gA,tid:_A,ts:bA(e),dur:bA(t.ms),args:{checks:t.checks,passes:t.passes,...t.trigger?{trigger:t.trigger}:{},...t.hookMs?{hookMs:t.hookMs}:{},...t.afterRenderMs?{afterRenderMs:t.afterRenderMs}:{},...t.outputMs?{outputMs:t.outputMs}:{},slowest:t.components.map(e=>`${e.name} ${e.checks}x ${e.ms}ms`)}}),s+=t.droppedSpans??0;let c=[{end:r}],l=[...t.spans??[]].sort((e,t)=>e[1]-t[1]||t[2]-e[2]);for(let[r,i,s,u,d]of l){o++;let l=n[r]??`?`;if(d===Qk.output){let n=e+i;a.push({name:l,cat:yA[d],ph:`X`,pid:gA,tid:vA,ts:bA(n),dur:bA(i<0?Math.max(0,Math.min(s,-i)):s),args:{cycle:t.id}});continue}let f=Math.max(e,e+i);for(;c.length>1&&c.at(-1).end<=f;)c.pop();let p=c.at(-1).end,m=Math.min(f+s,p);me.ts??0));if(c<0)for(let e of a)e.ts=bA((e.ts-c)/1e3);a.sort((e,t)=>e.ts-t.ts||t.dur-e.dur);let l=(e,t,n)=>({name:t,ph:`M`,pid:gA,tid:e,args:{name:n}});return{traceEvents:[l(0,`process_name`,`Angular change detection (page ${t})`),l(_A,`thread_name`,`Change detection`),l(vA,`thread_name`,`Outputs`),...a],displayTimeUnit:`ms`,otherData:{source:`Pangular Inspector change detection recording`,pageId:t,cycles:e.cycles.length,droppedCycles:e.dropped,spans:o,droppedSpans:s,...e.startedAt&&e.startedAt<=Vk?{startedAt:new Date(e.startedAt).toISOString()}:{}}}}function SA(e,t){if(e&1&&(B(0,`p`,5),J(1),V()),e&2){let e=G();A(),Y(e.error())}}function CA(e,t){let n=new Blob([JSON.stringify(e,null,2)],{type:`application/json`}),r=URL.createObjectURL(n),i=document.createElement(`a`);i.href=r,i.download=t,document.body.append(i),i.click(),i.remove(),setTimeout(()=>URL.revokeObjectURL(r),0)}var wA=class e{page=$(null);maxCycles=$(200);imported=Rv();error=k(``);hasCycles(){return(this.page()?.cycles.length??0)>0}exportJson(){let e=this.page();e&&CA(mA(e,e.pageId),`change-detection-${e.pageId}.json`)}exportTrace(){let e=this.page();e&&CA(xA(e,e.pageId),`change-detection-${e.pageId}.trace.json`)}async read(e){let t=e.files?.[0];if(e.value=``,!t)return;if(t.size>2e7){this.error.set(`${t.name}: The file is larger than 20 MB.`);return}let n;try{n=await t.text()}catch{this.error.set(`Could not read the file.`);return}let r=hA(n,this.maxCycles());if(!r.ok){this.error.set(`${t.name}: ${r.error}`);return}this.error.set(``),this.imported.emit({fileName:t.name,page:{...r.recording,pageId:r.pageId,supported:!0}})}static ɵfac=function(t){return new(t||e)};static ɵcmp=Kh({type:e,selectors:[[`app-cd-transfer`]],inputs:{page:[1,`page`],maxCycles:[1,`maxCycles`]},outputs:{imported:`imported`},decls:10,vars:3,consts:[[`file`,``],[`role`,`group`,`aria-label`,`Export and import the recording`,1,`transfer`],[`type`,`button`,3,`click`,`disabled`],[`type`,`button`,3,`click`],[`type`,`file`,`accept`,`.json,application/json`,`hidden`,``,3,`change`],[`role`,`alert`,1,`error`]],template:function(e,t){if(e&1){let e=H();B(0,`div`,1)(1,`button`,2),W(`click`,function(){return t.exportJson()}),J(2,`Export JSON`),V(),B(3,`button`,2),W(`click`,function(){return t.exportTrace()}),J(4,` Export trace (Perfetto) `),V(),B(5,`button`,3),W(`click`,function(){return E(e),D(d_(8).click())}),J(6,`Import`),V(),B(7,`input`,4,0),W(`change`,function(){E(e);let n=d_(8);return D(t.read(n))}),V()(),M(9,SA,2,1,`p`,5)}e&2&&(A(),qg(`disabled`,!t.hasCycles()),A(2),qg(`disabled`,!t.hasCycles()),A(6),N(t.error()?9:-1))},styles:[`[_nghost-%COMP%] { display: contents; } @@ -1407,7 +1407,7 @@ button[_ngcontent-%COMP%]:disabled { margin: 0; color: var(--%NS%danger-text); font-size: 12px; -}`]})},wA=(e,t)=>t.stat.name;function TA(e,t){if(e&1&&(B(0,`span`,8),J(1),V()),e&2){let e=G(2).$implicit;A(),X(``,e.other,` more`)}}function EA(e,t){if(e&1&&(B(0,`div`,6)(1,`span`,7),J(2),V(),M(3,TA,2,1,`span`,8),V()),e&2){let e=G().$implicit;K(`--%NS%tone`,e.tone+`%`),A(2),Y(e.label),A(),N(e.roomy?3:-1)}}function DA(e,t){if(e&1&&(B(0,`span`,10),J(1),V(),B(2,`span`,11),J(3),V()),e&2){let e=G(2).$implicit,t=G(2);A(),Y(e.stat.name),A(2),Y(t.fmt(e.stat.ms))}}function OA(e,t){if(e&1){let e=H();B(0,`button`,9),W(`focus`,function(){E(e);let t=G().$index;return D(G(2).current.set(t))})(`click`,function(){E(e);let t=G().$implicit;return D(G(2).pick.emit(t.stat.name))}),M(1,DA,4,2),V()}if(e&2){let e=G(),t=e.$implicit,n=e.$index,r=G(2);K(`--%NS%tone`,t.tone+`%`),j(`tabindex`,n===r.tabStop()?0:-1)(`aria-label`,t.label)(`data-index`,n),A(),N(t.roomy?1:-1)}}function kA(e,t){if(e&1&&(B(0,`li`),M(1,EA,4,4,`div`,4)(2,OA,2,6,`button`,5),V()),e&2){let e=t.$implicit;K(`left`,e.x,`%`)(`top`,e.y,`%`)(`width`,e.w,`%`)(`height`,e.h,`%`),A(),N(e.other?1:2)}}function AA(e,t){if(e&1){let e=H();B(0,`ul`,2),W(`keydown`,function(t){return E(e),D(G().onKey(t))}),P(1,kA,3,9,`li`,3,wA),V()}if(e&2){let e=G();j(`aria-label`,e.label()),A(),F(e.cells())}}function jA(e,t){e&1&&(B(0,`p`,1),J(1,`No component time in these cycles.`),V())}var MA=24,NA=3,PA=class e{stats=$([]);scope=$(`the recording`);pick=Rv();current=k(0);label=Q(()=>`Self time per component in ${this.scope()}, largest first. Pick one to show it in the tree.`);cells=Q(()=>{let e=this.stats().filter(e=>e.ms>0),t=e.slice(0,MA),n=e.slice(MA),r=n.reduce((e,t)=>e+t.ms,0),i=t.map(e=>({stat:e,other:0}));n.length&&i.push({stat:{name:`Other components`,checks:0,ms:r,maxMs:0,cycles:0},other:n.length});let a=e.reduce((e,t)=>e+t.ms,0),o=t[0]?.ms??1,s=JO(i,e=>e.stat.ms,NA);return s.sort((e,t)=>Number(e.item.other>0)-Number(t.item.other>0)),s.map(e=>{let{stat:t,other:n}=e.item,r=a?Math.round(t.ms/a*100):0;return{stat:t,other:n,x:e.x,y:e.y,w:e.w,h:e.h,tone:n?0:14+Math.round(26*Math.min(t.ms/o,1)),roomy:e.w>=10&&e.h>=22,label:n?`${n} more components, ${WO(t.ms)}, ${r}% of self time`:`${t.name}, ${WO(t.ms)} self time, ${r}%, ${t.checks} ${t.checks===1?`check`:`checks`}`}})});tabStop=Q(()=>Math.min(this.current(),Math.max(this.cells().filter(e=>!e.other).length-1,0)));fmt(e){return WO(e)}onKey(e){let t=this.cells().filter(e=>!e.other).length;if(!t)return;let n=this.tabStop(),r;switch(e.key){case`ArrowRight`:case`ArrowDown`:r=Math.min(n+1,t-1);break;case`ArrowLeft`:case`ArrowUp`:r=Math.max(n-1,0);break;case`Home`:r=0;break;case`End`:r=t-1;break;default:return}e.preventDefault(),this.current.set(r),e.currentTarget.querySelector(`button[data-index="${r}"]`)?.focus()}static ɵfac=function(t){return new(t||e)};static ɵcmp=Kh({type:e,selectors:[[`app-cd-treemap`]],inputs:{stats:[1,`stats`],scope:[1,`scope`]},outputs:{pick:`pick`},decls:2,vars:1,consts:[[1,`map`],[1,`empty`],[1,`map`,3,`keydown`],[3,`left`,`top`,`width`,`height`],[1,`cell`,`other`,3,`--%NS%tone`],[`type`,`button`,1,`cell`,3,`--%NS%tone`],[1,`cell`,`other`],[1,`sr-only`],[`aria-hidden`,`true`,1,`name`],[`type`,`button`,1,`cell`,3,`focus`,`click`],[1,`name`,`mono`],[1,`ms`]],template:function(e,t){e&1&&M(0,AA,3,1,`ul`,0)(1,jA,2,0,`p`,1),e&2&&N(+!t.cells().length)},styles:[`[_nghost-%COMP%] { +}`]})},TA=(e,t)=>t.stat.name;function EA(e,t){if(e&1&&(B(0,`span`,8),J(1),V()),e&2){let e=G(2).$implicit;A(),X(``,e.other,` more`)}}function DA(e,t){if(e&1&&(B(0,`div`,6)(1,`span`,7),J(2),V(),M(3,EA,2,1,`span`,8),V()),e&2){let e=G().$implicit;K(`--%NS%tone`,e.tone+`%`),A(2),Y(e.label),A(),N(e.roomy?3:-1)}}function OA(e,t){if(e&1&&(B(0,`span`,10),J(1),V(),B(2,`span`,11),J(3),V()),e&2){let e=G(2).$implicit,t=G(2);A(),Y(e.stat.name),A(2),Y(t.fmt(e.stat.ms))}}function kA(e,t){if(e&1){let e=H();B(0,`button`,9),W(`focus`,function(){E(e);let t=G().$index;return D(G(2).current.set(t))})(`click`,function(){E(e);let t=G().$implicit;return D(G(2).pick.emit(t.stat.name))}),M(1,OA,4,2),V()}if(e&2){let e=G(),t=e.$implicit,n=e.$index,r=G(2);K(`--%NS%tone`,t.tone+`%`),j(`tabindex`,n===r.tabStop()?0:-1)(`aria-label`,t.label)(`data-index`,n),A(),N(t.roomy?1:-1)}}function AA(e,t){if(e&1&&(B(0,`li`),M(1,DA,4,4,`div`,4)(2,kA,2,6,`button`,5),V()),e&2){let e=t.$implicit;K(`left`,e.x,`%`)(`top`,e.y,`%`)(`width`,e.w,`%`)(`height`,e.h,`%`),A(),N(e.other?1:2)}}function jA(e,t){if(e&1){let e=H();B(0,`ul`,2),W(`keydown`,function(t){return E(e),D(G().onKey(t))}),P(1,AA,3,9,`li`,3,TA),V()}if(e&2){let e=G();j(`aria-label`,e.label()),A(),F(e.cells())}}function MA(e,t){e&1&&(B(0,`p`,1),J(1,`No component time in these cycles.`),V())}var NA=24,PA=3,FA=class e{stats=$([]);scope=$(`the recording`);pick=Rv();current=k(0);label=Q(()=>`Self time per component in ${this.scope()}, largest first. Pick one to show it in the tree.`);cells=Q(()=>{let e=this.stats().filter(e=>e.ms>0),t=e.slice(0,NA),n=e.slice(NA),r=n.reduce((e,t)=>e+t.ms,0),i=t.map(e=>({stat:e,other:0}));n.length&&i.push({stat:{name:`Other components`,checks:0,ms:r,maxMs:0,cycles:0},other:n.length});let a=e.reduce((e,t)=>e+t.ms,0),o=t[0]?.ms??1,s=YO(i,e=>e.stat.ms,PA);return s.sort((e,t)=>Number(e.item.other>0)-Number(t.item.other>0)),s.map(e=>{let{stat:t,other:n}=e.item,r=a?Math.round(t.ms/a*100):0;return{stat:t,other:n,x:e.x,y:e.y,w:e.w,h:e.h,tone:n?0:14+Math.round(26*Math.min(t.ms/o,1)),roomy:e.w>=10&&e.h>=22,label:n?`${n} more components, ${GO(t.ms)}, ${r}% of self time`:`${t.name}, ${GO(t.ms)} self time, ${r}%, ${t.checks} ${t.checks===1?`check`:`checks`}`}})});tabStop=Q(()=>Math.min(this.current(),Math.max(this.cells().filter(e=>!e.other).length-1,0)));fmt(e){return GO(e)}onKey(e){let t=this.cells().filter(e=>!e.other).length;if(!t)return;let n=this.tabStop(),r;switch(e.key){case`ArrowRight`:case`ArrowDown`:r=Math.min(n+1,t-1);break;case`ArrowLeft`:case`ArrowUp`:r=Math.max(n-1,0);break;case`Home`:r=0;break;case`End`:r=t-1;break;default:return}e.preventDefault(),this.current.set(r),e.currentTarget.querySelector(`button[data-index="${r}"]`)?.focus()}static ɵfac=function(t){return new(t||e)};static ɵcmp=Kh({type:e,selectors:[[`app-cd-treemap`]],inputs:{stats:[1,`stats`],scope:[1,`scope`]},outputs:{pick:`pick`},decls:2,vars:1,consts:[[1,`map`],[1,`empty`],[1,`map`,3,`keydown`],[3,`left`,`top`,`width`,`height`],[1,`cell`,`other`,3,`--%NS%tone`],[`type`,`button`,1,`cell`,3,`--%NS%tone`],[1,`cell`,`other`],[1,`sr-only`],[`aria-hidden`,`true`,1,`name`],[`type`,`button`,1,`cell`,3,`focus`,`click`],[1,`name`,`mono`],[1,`ms`]],template:function(e,t){e&1&&M(0,jA,3,1,`ul`,0)(1,MA,2,0,`p`,1),e&2&&N(+!t.cells().length)},styles:[`[_nghost-%COMP%] { display: block; min-width: 0; } @@ -1483,7 +1483,7 @@ button.cell[_ngcontent-%COMP%]:focus-visible { .empty[_ngcontent-%COMP%] { margin: 8px 0; color: var(--%NS%text-2); -}`]})},FA=(e,t)=>t.id;function IA(e,t){if(e&1){let e=H();L(0,`button`,14),U(`click`,function(){return E(e),D(G(2).clear())}),J(1,`Clear`),R()}}function LA(e,t){if(e&1){let e=H();L(0,`button`,11),U(`click`,function(){return E(e),D(G().toggle())}),z(1,`span`,12),J(2),R(),M(3,IA,2,0,`button`,13)}if(e&2){let e=G();q(`on`,e.recording()),I(`disabled`,!e.pageId()),A(2),X(` `,e.recording()?`Stop recording`:`Record`,` `),A(),N(e.cycles().length?3:-1)}}function RA(e,t){if(e&1){let e=H();L(0,`p`,6)(1,`span`,15),J(2,`Imported recording`),R(),L(3,`span`,16),J(4),R(),L(5,`button`,14),U(`click`,function(){return E(e),D(G().closeImport())}),J(6,`Back to live recording`),R()()}e&2&&(A(4),Y(t.fileName))}function zA(e,t){e&1&&(L(0,`p`,8),J(1,`This page can't record change detection. It needs Angular 20 or later.`),R())}function BA(e,t){e&1&&(L(0,`p`,8),J(1,`Angular profiling stays on until the page reloads.`),R())}function VA(e,t){e&1&&(L(0,`p`,8),J(1,` This page has no `),L(2,`span`,16),J(3,`ng.enableProfiling`),R(),J(4,`. It needs Angular 20 or later in a development build. `),R())}function HA(e,t){if(e&1){let e=H();L(0,`div`,17)(1,`span`,18),J(2,`Live views`),R(),L(3,`button`,19),U(`click`,function(){E(e);let t=G();return D(t.setView(`flash`,!t.views().flash))}),z(4,`span`,12),J(5,` Flash checks `),R(),L(6,`button`,19),U(`click`,function(){E(e);let t=G();return D(t.setView(`track`,!t.views().track))}),z(7,`span`,12),J(8,` Performance track `),R(),L(9,`button`,19),U(`click`,function(){return E(e),D(G().setView(`angularProfiling`,!0))}),z(10,`span`,12),J(11,` Angular profiling `),R()(),L(12,`p`,7),J(13,` Flash checks draws a box over each component on the page as Angular checks it, with its name and how often it was checked, fading over a second. Performance track adds a Pangular track to the Chrome Performance panel with every component check, hook and output. Angular profiling turns on Angular's own track there. `),R(),M(14,BA,2,0,`p`,8)(15,VA,5,0,`p`,8)}if(e&2){let e=G();A(3),I(`disabled`,!e.canShowViews()),j(`aria-pressed`,e.views().flash),A(3),I(`disabled`,!e.canShowViews()),j(`aria-pressed`,e.views().track),A(3),I(`disabled`,!e.canShowViews()||e.views().angularProfiling||!e.views().angularProfilingAvailable),j(`aria-pressed`,e.views().angularProfiling),A(5),N(e.views().angularProfiling?14:e.views().angularProfilingAvailable?-1:15)}}function UA(e,t){if(e&1&&(L(0,`p`,8),J(1),R()),e&2){let e=t.$implicit;A(),Y(e)}}function WA(e,t){if(e&1&&(L(0,`p`,22),J(1),R()),e&2){let e=G(2);A(),X(` `,e.range()?`The picked cycles have no timed spans, so there is no flame graph for them.`:`This recording has no timed spans, so there is no flame graph. A recording from an older version, or a file saved without spans, has none.`,` `)}}function GA(e,t){e&1&&(L(0,`p`,8),J(1,` For picked cycles, totals come from the 10 slowest components each cycle lists. `),R())}function KA(e,t){if(e&1){let e=H();L(0,`h3`),J(1,`Cycles`),R(),L(2,`app-cd-cycle-chart`,20),cv(`rangeChange`,function(t){E(e);let n=G();return sv(n.range,t)||(n.range=t),D(t)}),R(),L(3,`app-cd-flame-graph`,21),U(`pick`,function(t){return E(e),D(G().showComponent.emit(t))}),R(),M(4,WA,2,1,`p`,22),L(5,`h3`),J(6,`Self time by component`),R(),M(7,GA,2,0,`p`,8),L(8,`app-cd-treemap`,23),U(`pick`,function(t){return E(e),D(G().showComponent.emit(t))}),R()}if(e&2){let e=G();A(2),I(`cycles`,e.cycles()),ov(`range`,e.range),A(),I(`cycles`,e.picked())(`names`,e.spanNames())(`scope`,e.scope()),A(),N(e.hasSpans()?-1:4),A(3),N(e.range()?7:-1),A(),I(`stats`,e.stats())(`scope`,e.scope())}}function qA(e,t){if(e&1&&(L(0,`tr`)(1,`td`,16),J(2),R(),L(3,`td`,29),J(4),R(),L(5,`td`,29),J(6),R(),L(7,`td`,29),J(8),R()()),e&2){let e=t.$implicit,n=G(2);A(2),Y(e.name),A(2),Y(e.checks),A(2),Y(n.ms(e.ms)),A(2),Y(n.ms(e.maxMs))}}function JA(e,t){e&1&&(L(0,`tr`)(1,`td`,30),J(2,`No component time in the picked cycles.`),R()())}function YA(e,t){if(e&1&&(L(0,`span`,32),J(1,`after `),L(2,`span`,16),J(3),R()()),e&2){let e=G().$implicit;A(3),Y(e.trigger)}}function XA(e,t){if(e&1&&(L(0,`span`,33),J(1),R()),e&2){let e=G().$implicit,t=G(2);A(),X(`hooks `,t.ms(e.hookMs))}}function ZA(e,t){if(e&1&&(L(0,`span`,33),J(1),R()),e&2){let e=G().$implicit,t=G(2);A(),X(`afterRender `,t.ms(e.afterRenderMs))}}function QA(e,t){if(e&1&&(L(0,`span`,34),J(1,` slowest `),L(2,`span`,16),J(3),R(),J(4),R()),e&2){let e=t,n=G(3);A(3),Y(e.name),A(),X(` `,n.ms(e.ms),` `)}}function $A(e,t){if(e&1&&(L(0,`li`)(1,`span`,31),J(2),R(),L(3,`span`,16),J(4),R(),L(5,`span`),J(6),R(),M(7,YA,4,1,`span`,32),M(8,XA,2,1,`span`,33),M(9,ZA,2,1,`span`,33),M(10,QA,5,2,`span`,34),R()),e&2){let e,n=t.$implicit,r=G(2);A(2),Y(r.clock(n.at)),A(2),Y(r.ms(n.ms)),A(2),iv(` `,n.checks,` `,n.checks===1?`check`:`checks`,`, `,n.passes,` `,n.passes===1?`pass`:`passes`,` `),A(),N(n.trigger?7:-1),A(),N(n.hookMs?8:-1),A(),N(n.afterRenderMs?9:-1),A(),N((e=n.components[0])?10:-1,e)}}function ej(e,t){if(e&1&&(L(0,`div`,24)(1,`div`)(2,`h3`),J(3),R(),L(4,`table`)(5,`thead`)(6,`tr`)(7,`th`,25),J(8,`Component`),R(),L(9,`th`,26),J(10,`Checks`),R(),L(11,`th`,26),J(12,`Self time`),R(),L(13,`th`,26),J(14,`Slowest`),R()()(),L(15,`tbody`),P(16,qA,9,4,`tr`,null,kg,!1,JA,3,0,`tr`),R()()(),L(19,`div`)(20,`h3`),J(21,`Latest cycles`),R(),L(22,`ol`,27),P(23,$A,11,10,`li`,null,FA),R()()(),z(25,`app-cd-timing`,28)),e&2){let e=G();A(3),X(` `,e.range()?`Slowest components in the picked cycles`:`Slowest components`,` `),A(13),F(e.slowest()),A(7),F(e.latest()),A(2),I(`page`,e.page())}}function tj(e,t){e&1&&(L(0,`p`,10),J(1,`Use the app. Cycles show up here as Angular runs change detection.`),R())}var nj=10,rj=class e{livePage=$(null,{alias:`page`});pageId=$(null);rpc=$(null);showComponent=Rv();range=k(null);imported=k(null);page=Q(()=>this.imported()?.page??this.livePage());views=Fv(()=>this.livePage()?.options??{flash:!1,track:!1,angularProfiling:!1,angularProfilingAvailable:!0});canShowViews=Q(()=>!!this.pageId()&&this.livePage()?.supported!==!1);message=k(``);announcement=k(``);recording=Q(()=>this.page()?.recording===!0);cycles=Q(()=>this.page()?.cycles??[]);maxCycles=Q(()=>JE(this.rpc()).limits.cdCycles);picked=Q(()=>GO(this.cycles(),this.range()));hasSpans=Q(()=>this.picked().some(e=>e.spans?.length));spanNames=Q(()=>this.page()?.spanNames??[]);stats=Q(()=>this.range()?KO(this.picked()):[...this.page()?.components??[]].sort((e,t)=>t.ms-e.ms));scope=Q(()=>{let e=this.range()?this.picked():[];return e.length?e.length===1?`cycle ${e[0].id}`:`cycles ${e[0].id} to ${e.at(-1).id}`:`the recording`});slowest=Q(()=>this.stats().slice(0,nj));latest=Q(()=>this.cycles().slice(-30).reverse());capNotes=Q(()=>{let e=this.page()?.droppedClasses??0,t=this.page()?.droppedHosts??0,n=[];return e>0&&n.push(`${e} more component ${e===1?`class was`:`classes were`} checked than a recording keeps, so ${e===1?`it is`:`they are`} left out of these totals.`),t>0&&n.push(`${t} component ${t===1?`instance was`:`instances were`} checked past the 2000 a recording counts, so ${t===1?`its tree row shows`:`their tree rows show`} no check count.`),n});status=Q(()=>{if(this.message())return this.message();let e=this.cycles().length;return this.imported()?`${e} ${e===1?`cycle`:`cycles`} imported.`:this.recording()?`Recording. ${e} ${e===1?`cycle`:`cycles`} so far.`:e?`${e} ${e===1?`cycle`:`cycles`} recorded.`:``});ms(e){return`${e<10?e.toFixed(2):Math.round(e)} ms`}clock(e){return IO(e)}async toggle(){this.recording()?await this.send({on:!1},``,()=>{let e=this.cycles().length;return`Recording stopped. ${e} ${e===1?`cycle`:`cycles`} recorded.`}):await this.send({on:!0},`Starting…`,()=>`Recording started.`)}showImport(e){this.message.set(``),this.range.set(null),this.imported.set(e);let t=e.page.cycles.length;this.announce(`Imported ${e.fileName}: ${t} ${t===1?`cycle`:`cycles`}. Showing the imported recording.`)}closeImport(){this.imported.set(null),this.range.set(null),this.announce(`Showing the live recording.`)}async clear(){this.range.set(null),await this.send({clear:!0},``,()=>`Cleared the recorded cycles.`)}async setView(e,t){let n=this.views();this.views.set({...n,[e]:t});let r={flash:t?`Flash checks on.`:`Flash checks off.`,track:t?`Performance track on. Record in the Chrome Performance panel to see it.`:`Performance track off.`,angularProfiling:`Angular profiling on. It stays on until the page reloads.`}[e];await this.send({[e]:t},``,()=>r,`request-change-detection-options`)||this.views.set(n)}async send(e,t,n,r=`request-change-detection-record`){this.message.set(t);try{if(!this.rpc())throw Error(`no client`);return await NE(this.rpc(),r,{pageId:this.pageId(),...e}),this.message.set(``),this.announce(n()),!0}catch{let e=`Could not reach the page.`;return this.message.set(e),this.announce(e),!1}}announce(e){this.announcement.update(t=>t===e?`${e} `:e)}static ɵfac=function(t){return new(t||e)};static ɵcmp=Kh({type:e,selectors:[[`app-cd-recording`]],inputs:{livePage:[1,`page`,`livePage`],pageId:[1,`pageId`],rpc:[1,`rpc`]},outputs:{showComponent:`showComponent`},decls:21,vars:12,consts:[[`aria-labelledby`,`cd-heading`,1,`cd`],[1,`cd-head`],[`id`,`cd-heading`],[3,`imported`,`page`,`maxCycles`],[1,`status`],[`role`,`status`,1,`sr-only`],[1,`imported`],[1,`hint`],[1,`note`],[`what`,`cycles`,`limit`,`cdCycles`,3,`dropped`,`max`],[1,`empty`],[`type`,`button`,3,`click`,`disabled`],[`aria-hidden`,`true`,1,`dot`],[`type`,`button`],[`type`,`button`,3,`click`],[1,`badge`],[1,`mono`],[`role`,`group`,`aria-labelledby`,`cd-views-label`,1,`views`],[`id`,`cd-views-label`,1,`views-label`],[`type`,`button`,1,`view`,3,`click`,`disabled`],[3,`rangeChange`,`cycles`,`range`],[3,`pick`,`cycles`,`names`,`scope`],[1,`spans-note`],[3,`pick`,`stats`,`scope`],[1,`cd-grid`],[`scope`,`col`],[`scope`,`col`,1,`num`],[`aria-label`,`Latest change detection cycles, newest first`,1,`cycles`],[3,`page`],[1,`num`],[`colspan`,`4`],[1,`when`],[1,`trigger`],[1,`timing`],[1,`top`]],template:function(e,t){if(e&1&&(L(0,`section`,0)(1,`div`,1)(2,`h2`,2),J(3,`Change detection`),R(),M(4,LA,4,5),L(5,`app-cd-transfer`,3),U(`imported`,function(e){return t.showImport(e)}),R(),L(6,`p`,4),J(7),R(),L(8,`p`,5),J(9),R()(),M(10,RA,7,1,`p`,6),L(11,`p`,7),J(12,` Records each change detection cycle with Angular's profiler: how long it took, how many components it checked and the output that ran before it. Tree rows show how often each instance was checked. Times come from a development build. `),R(),M(13,zA,2,0,`p`,8),M(14,HA,16,7),z(15,`app-limit-note`,9),P(16,UA,2,1,`p`,8,Ag),M(18,KA,9,9),M(19,ej,26,3)(20,tj,2,0,`p`,10),R()),e&2){let e;A(4),N(t.imported()?-1:4),A(),I(`page`,t.page())(`maxCycles`,t.maxCycles()),A(2),Y(t.status()),A(2),Y(t.announcement()),A(),N((e=t.imported())?10:-1,e),A(3),N(t.page()?.supported===!1?13:-1),A(),N(t.imported()?-1:14),A(),I(`dropped`,t.page()?.dropped??0)(`max`,t.maxCycles()),A(),F(t.capNotes()),A(2),N(t.cycles().length?18:-1),A(),N(t.page()?.components?.length?19:t.recording()?20:-1)}},dependencies:[VO,zk,CA,pk,Ok,PA],styles:[`[_nghost-%COMP%] { +}`]})},IA=(e,t)=>t.id;function LA(e,t){if(e&1){let e=H();L(0,`button`,14),U(`click`,function(){return E(e),D(G(2).clear())}),J(1,`Clear`),R()}}function RA(e,t){if(e&1){let e=H();L(0,`button`,11),U(`click`,function(){return E(e),D(G().toggle())}),z(1,`span`,12),J(2),R(),M(3,LA,2,0,`button`,13)}if(e&2){let e=G();q(`on`,e.recording()),I(`disabled`,!e.pageId()),A(2),X(` `,e.recording()?`Stop recording`:`Record`,` `),A(),N(e.cycles().length?3:-1)}}function zA(e,t){if(e&1){let e=H();L(0,`p`,6)(1,`span`,15),J(2,`Imported recording`),R(),L(3,`span`,16),J(4),R(),L(5,`button`,14),U(`click`,function(){return E(e),D(G().closeImport())}),J(6,`Back to live recording`),R()()}e&2&&(A(4),Y(t.fileName))}function BA(e,t){e&1&&(L(0,`p`,8),J(1,`This page can't record change detection. It needs Angular 20 or later.`),R())}function VA(e,t){e&1&&(L(0,`p`,8),J(1,`Angular profiling stays on until the page reloads.`),R())}function HA(e,t){e&1&&(L(0,`p`,8),J(1,` This page has no `),L(2,`span`,16),J(3,`ng.enableProfiling`),R(),J(4,`. It needs Angular 20 or later in a development build. `),R())}function UA(e,t){if(e&1){let e=H();L(0,`div`,17)(1,`span`,18),J(2,`Live views`),R(),L(3,`button`,19),U(`click`,function(){E(e);let t=G();return D(t.setView(`flash`,!t.views().flash))}),z(4,`span`,12),J(5,` Flash checks `),R(),L(6,`button`,19),U(`click`,function(){E(e);let t=G();return D(t.setView(`track`,!t.views().track))}),z(7,`span`,12),J(8,` Performance track `),R(),L(9,`button`,19),U(`click`,function(){return E(e),D(G().setView(`angularProfiling`,!0))}),z(10,`span`,12),J(11,` Angular profiling `),R()(),L(12,`p`,7),J(13,` Flash checks draws a box over each component on the page as Angular checks it, with its name and how often it was checked, fading over a second. Performance track adds a Pangular track to the Chrome Performance panel with every component check, hook and output. Angular profiling turns on Angular's own track there. `),R(),M(14,VA,2,0,`p`,8)(15,HA,5,0,`p`,8)}if(e&2){let e=G();A(3),I(`disabled`,!e.canShowViews()),j(`aria-pressed`,e.views().flash),A(3),I(`disabled`,!e.canShowViews()),j(`aria-pressed`,e.views().track),A(3),I(`disabled`,!e.canShowViews()||e.views().angularProfiling||!e.views().angularProfilingAvailable),j(`aria-pressed`,e.views().angularProfiling),A(5),N(e.views().angularProfiling?14:e.views().angularProfilingAvailable?-1:15)}}function WA(e,t){if(e&1&&(L(0,`p`,8),J(1),R()),e&2){let e=t.$implicit;A(),Y(e)}}function GA(e,t){if(e&1&&(L(0,`p`,22),J(1),R()),e&2){let e=G(2);A(),X(` `,e.range()?`The picked cycles have no timed spans, so there is no flame graph for them.`:`This recording has no timed spans, so there is no flame graph. A recording from an older version, or a file saved without spans, has none.`,` `)}}function KA(e,t){e&1&&(L(0,`p`,8),J(1,` For picked cycles, totals come from the 10 slowest components each cycle lists. `),R())}function qA(e,t){if(e&1){let e=H();L(0,`h3`),J(1,`Cycles`),R(),L(2,`app-cd-cycle-chart`,20),cv(`rangeChange`,function(t){E(e);let n=G();return sv(n.range,t)||(n.range=t),D(t)}),R(),L(3,`app-cd-flame-graph`,21),U(`pick`,function(t){return E(e),D(G().showComponent.emit(t))}),R(),M(4,GA,2,1,`p`,22),L(5,`h3`),J(6,`Self time by component`),R(),M(7,KA,2,0,`p`,8),L(8,`app-cd-treemap`,23),U(`pick`,function(t){return E(e),D(G().showComponent.emit(t))}),R()}if(e&2){let e=G();A(2),I(`cycles`,e.cycles()),ov(`range`,e.range),A(),I(`cycles`,e.picked())(`names`,e.spanNames())(`scope`,e.scope()),A(),N(e.hasSpans()?-1:4),A(3),N(e.range()?7:-1),A(),I(`stats`,e.stats())(`scope`,e.scope())}}function JA(e,t){if(e&1&&(L(0,`tr`)(1,`td`,16),J(2),R(),L(3,`td`,29),J(4),R(),L(5,`td`,29),J(6),R(),L(7,`td`,29),J(8),R()()),e&2){let e=t.$implicit,n=G(2);A(2),Y(e.name),A(2),Y(e.checks),A(2),Y(n.ms(e.ms)),A(2),Y(n.ms(e.maxMs))}}function YA(e,t){e&1&&(L(0,`tr`)(1,`td`,30),J(2,`No component time in the picked cycles.`),R()())}function XA(e,t){if(e&1&&(L(0,`span`,32),J(1,`after `),L(2,`span`,16),J(3),R()()),e&2){let e=G().$implicit;A(3),Y(e.trigger)}}function ZA(e,t){if(e&1&&(L(0,`span`,33),J(1),R()),e&2){let e=G().$implicit,t=G(2);A(),X(`hooks `,t.ms(e.hookMs))}}function QA(e,t){if(e&1&&(L(0,`span`,33),J(1),R()),e&2){let e=G().$implicit,t=G(2);A(),X(`afterRender `,t.ms(e.afterRenderMs))}}function $A(e,t){if(e&1&&(L(0,`span`,34),J(1,` slowest `),L(2,`span`,16),J(3),R(),J(4),R()),e&2){let e=t,n=G(3);A(3),Y(e.name),A(),X(` `,n.ms(e.ms),` `)}}function ej(e,t){if(e&1&&(L(0,`li`)(1,`span`,31),J(2),R(),L(3,`span`,16),J(4),R(),L(5,`span`),J(6),R(),M(7,XA,4,1,`span`,32),M(8,ZA,2,1,`span`,33),M(9,QA,2,1,`span`,33),M(10,$A,5,2,`span`,34),R()),e&2){let e,n=t.$implicit,r=G(2);A(2),Y(r.clock(n.at)),A(2),Y(r.ms(n.ms)),A(2),iv(` `,n.checks,` `,n.checks===1?`check`:`checks`,`, `,n.passes,` `,n.passes===1?`pass`:`passes`,` `),A(),N(n.trigger?7:-1),A(),N(n.hookMs?8:-1),A(),N(n.afterRenderMs?9:-1),A(),N((e=n.components[0])?10:-1,e)}}function tj(e,t){if(e&1&&(L(0,`div`,24)(1,`div`)(2,`h3`),J(3),R(),L(4,`table`)(5,`thead`)(6,`tr`)(7,`th`,25),J(8,`Component`),R(),L(9,`th`,26),J(10,`Checks`),R(),L(11,`th`,26),J(12,`Self time`),R(),L(13,`th`,26),J(14,`Slowest`),R()()(),L(15,`tbody`),P(16,JA,9,4,`tr`,null,kg,!1,YA,3,0,`tr`),R()()(),L(19,`div`)(20,`h3`),J(21,`Latest cycles`),R(),L(22,`ol`,27),P(23,ej,11,10,`li`,null,IA),R()()(),z(25,`app-cd-timing`,28)),e&2){let e=G();A(3),X(` `,e.range()?`Slowest components in the picked cycles`:`Slowest components`,` `),A(13),F(e.slowest()),A(7),F(e.latest()),A(2),I(`page`,e.page())}}function nj(e,t){e&1&&(L(0,`p`,10),J(1,`Use the app. Cycles show up here as Angular runs change detection.`),R())}var rj=10,ij=class e{livePage=$(null,{alias:`page`});pageId=$(null);rpc=$(null);showComponent=Rv();range=k(null);imported=k(null);page=Q(()=>this.imported()?.page??this.livePage());views=Fv(()=>this.livePage()?.options??{flash:!1,track:!1,angularProfiling:!1,angularProfilingAvailable:!0});canShowViews=Q(()=>!!this.pageId()&&this.livePage()?.supported!==!1);message=k(``);announcement=k(``);recording=Q(()=>this.page()?.recording===!0);cycles=Q(()=>this.page()?.cycles??[]);maxCycles=Q(()=>JE(this.rpc()).limits.cdCycles);picked=Q(()=>KO(this.cycles(),this.range()));hasSpans=Q(()=>this.picked().some(e=>e.spans?.length));spanNames=Q(()=>this.page()?.spanNames??[]);stats=Q(()=>this.range()?qO(this.picked()):[...this.page()?.components??[]].sort((e,t)=>t.ms-e.ms));scope=Q(()=>{let e=this.range()?this.picked():[];return e.length?e.length===1?`cycle ${e[0].id}`:`cycles ${e[0].id} to ${e.at(-1).id}`:`the recording`});slowest=Q(()=>this.stats().slice(0,rj));latest=Q(()=>this.cycles().slice(-30).reverse());capNotes=Q(()=>{let e=this.page()?.droppedClasses??0,t=this.page()?.droppedHosts??0,n=[];return e>0&&n.push(`${e} more component ${e===1?`class was`:`classes were`} checked than a recording keeps, so ${e===1?`it is`:`they are`} left out of these totals.`),t>0&&n.push(`${t} component ${t===1?`instance was`:`instances were`} checked past the 2000 a recording counts, so ${t===1?`its tree row shows`:`their tree rows show`} no check count.`),n});status=Q(()=>{if(this.message())return this.message();let e=this.cycles().length;return this.imported()?`${e} ${e===1?`cycle`:`cycles`} imported.`:this.recording()?`Recording. ${e} ${e===1?`cycle`:`cycles`} so far.`:e?`${e} ${e===1?`cycle`:`cycles`} recorded.`:``});ms(e){return`${e<10?e.toFixed(2):Math.round(e)} ms`}clock(e){return LO(e)}async toggle(){this.recording()?await this.send({on:!1},``,()=>{let e=this.cycles().length;return`Recording stopped. ${e} ${e===1?`cycle`:`cycles`} recorded.`}):await this.send({on:!0},`Starting…`,()=>`Recording started.`)}showImport(e){this.message.set(``),this.range.set(null),this.imported.set(e);let t=e.page.cycles.length;this.announce(`Imported ${e.fileName}: ${t} ${t===1?`cycle`:`cycles`}. Showing the imported recording.`)}closeImport(){this.imported.set(null),this.range.set(null),this.announce(`Showing the live recording.`)}async clear(){this.range.set(null),await this.send({clear:!0},``,()=>`Cleared the recorded cycles.`)}async setView(e,t){let n=this.views();this.views.set({...n,[e]:t});let r={flash:t?`Flash checks on.`:`Flash checks off.`,track:t?`Performance track on. Record in the Chrome Performance panel to see it.`:`Performance track off.`,angularProfiling:`Angular profiling on. It stays on until the page reloads.`}[e];await this.send({[e]:t},``,()=>r,`request-change-detection-options`)||this.views.set(n)}async send(e,t,n,r=`request-change-detection-record`){this.message.set(t);try{if(!this.rpc())throw Error(`no client`);return await NE(this.rpc(),r,{pageId:this.pageId(),...e}),this.message.set(``),this.announce(n()),!0}catch{let e=`Could not reach the page.`;return this.message.set(e),this.announce(e),!1}}announce(e){this.announcement.update(t=>t===e?`${e} `:e)}static ɵfac=function(t){return new(t||e)};static ɵcmp=Kh({type:e,selectors:[[`app-cd-recording`]],inputs:{livePage:[1,`page`,`livePage`],pageId:[1,`pageId`],rpc:[1,`rpc`]},outputs:{showComponent:`showComponent`},decls:21,vars:12,consts:[[`aria-labelledby`,`cd-heading`,1,`cd`],[1,`cd-head`],[`id`,`cd-heading`],[3,`imported`,`page`,`maxCycles`],[1,`status`],[`role`,`status`,1,`sr-only`],[1,`imported`],[1,`hint`],[1,`note`],[`what`,`cycles`,`limit`,`cdCycles`,3,`dropped`,`max`],[1,`empty`],[`type`,`button`,3,`click`,`disabled`],[`aria-hidden`,`true`,1,`dot`],[`type`,`button`],[`type`,`button`,3,`click`],[1,`badge`],[1,`mono`],[`role`,`group`,`aria-labelledby`,`cd-views-label`,1,`views`],[`id`,`cd-views-label`,1,`views-label`],[`type`,`button`,1,`view`,3,`click`,`disabled`],[3,`rangeChange`,`cycles`,`range`],[3,`pick`,`cycles`,`names`,`scope`],[1,`spans-note`],[3,`pick`,`stats`,`scope`],[1,`cd-grid`],[`scope`,`col`],[`scope`,`col`,1,`num`],[`aria-label`,`Latest change detection cycles, newest first`,1,`cycles`],[3,`page`],[1,`num`],[`colspan`,`4`],[1,`when`],[1,`trigger`],[1,`timing`],[1,`top`]],template:function(e,t){if(e&1&&(L(0,`section`,0)(1,`div`,1)(2,`h2`,2),J(3,`Change detection`),R(),M(4,RA,4,5),L(5,`app-cd-transfer`,3),U(`imported`,function(e){return t.showImport(e)}),R(),L(6,`p`,4),J(7),R(),L(8,`p`,5),J(9),R()(),M(10,zA,7,1,`p`,6),L(11,`p`,7),J(12,` Records each change detection cycle with Angular's profiler: how long it took, how many components it checked and the output that ran before it. Tree rows show how often each instance was checked. Times come from a development build. `),R(),M(13,BA,2,0,`p`,8),M(14,UA,16,7),z(15,`app-limit-note`,9),P(16,WA,2,1,`p`,8,Ag),M(18,qA,9,9),M(19,tj,26,3)(20,nj,2,0,`p`,10),R()),e&2){let e;A(4),N(t.imported()?-1:4),A(),I(`page`,t.page())(`maxCycles`,t.maxCycles()),A(2),Y(t.status()),A(2),Y(t.announcement()),A(),N((e=t.imported())?10:-1,e),A(3),N(t.page()?.supported===!1?13:-1),A(),N(t.imported()?-1:14),A(),I(`dropped`,t.page()?.dropped??0)(`max`,t.maxCycles()),A(),F(t.capNotes()),A(2),N(t.cycles().length?18:-1),A(),N(t.page()?.components?.length?19:t.recording()?20:-1)}},dependencies:[HO,Bk,wA,mk,kk,FA],styles:[`[_nghost-%COMP%] { display: block; margin-top: 16px; } @@ -1685,7 +1685,7 @@ td.mono[_ngcontent-%COMP%] { .trigger[_ngcontent-%COMP%] .mono[_ngcontent-%COMP%], .top[_ngcontent-%COMP%] .mono[_ngcontent-%COMP%] { overflow-wrap: anywhere; -}`]})},ij=[`*`];function aj(e,t){e&1&&J(0),e&2&&Y(G(2).label())}function oj(e,t){e&1&&s_(0,0,null,aj,1,1)}function sj(e,t){if(e&1&&(L(0,`span`,4),J(1),R()),e&2){let e=G(2);A(),Y(e.message())}}function cj(e,t){if(e&1){let e=H();L(0,`button`,2),U(`click`,function(){return E(e),D(G().open())}),Gg(1,3),R(),M(2,sj,2,1,`span`,4)}if(e&2){let e=G(),t=d_(1);I(`title`,`Open `+e.label()+` in your editor`),j(`aria-label`,`Open `+e.label()+` in your editor`),A(),I(`ngTemplateOutlet`,t),A(),N(e.message()?2:-1)}}function lj(e,t){if(e&1&&(L(0,`span`,1),Gg(1,3),R()),e&2){let e=G(),t=d_(1);I(`title`,e.label()),A(),I(`ngTemplateOutlet`,t)}}var uj=class e{source=k(k(null));client=Q(()=>this.source()());available=Q(()=>{let e=this.client();return!!e&&!FE(e)});use(e){this.source.set(e)}async open(e,t){if(!this.available())return{ok:!1,error:`No server is connected.`};try{let n=await NE(this.client(),`open-in-editor`,{file:e,...t?{line:t}:{}});return n?.ok?{ok:!0}:{ok:!1,error:n?.error??`The server did not open it.`}}catch{return{ok:!1,error:`The server did not open it.`}}}static ɵfac=function(t){return new(t||e)};static ɵprov=Ml({token:e,factory:e.ɵfac})};function dj(e,t){return t?`${e}:${t}`:e}var fj=class e{editor=w(uj);file=$.required();line=$(void 0);label=Q(()=>dj(this.file(),this.line()));message=k(``);async open(){this.message.set(``);let e=await this.editor.open(this.file(),this.line()??void 0);e.ok||this.message.set(`Not opened: ${e.error}`)}static ɵfac=function(t){return new(t||e)};static ɵcmp=Kh({type:e,selectors:[[`app-source-link`]],inputs:{file:[1,`file`],line:[1,`line`]},ngContentSelectors:ij,decls:4,vars:1,consts:[[`text`,``],[1,`source-text`,3,`title`],[`type`,`button`,1,`source-link`,3,`click`,`title`],[3,`ngTemplateOutlet`],[`role`,`alert`,1,`message`]],template:function(e,t){e&1&&(o_(),sg(0,oj,2,0,`ng-template`,null,0,Ev),M(2,cj,3,4)(3,lj,2,2,`span`,1)),e&2&&(A(2),N(t.editor.available()?2:3))},dependencies:[lb],styles:[`[_nghost-%COMP%] { +}`]})},aj=[`*`];function oj(e,t){e&1&&J(0),e&2&&Y(G(2).label())}function sj(e,t){e&1&&s_(0,0,null,oj,1,1)}function cj(e,t){if(e&1&&(L(0,`span`,4),J(1),R()),e&2){let e=G(2);A(),Y(e.message())}}function lj(e,t){if(e&1){let e=H();L(0,`button`,2),U(`click`,function(){return E(e),D(G().open())}),Gg(1,3),R(),M(2,cj,2,1,`span`,4)}if(e&2){let e=G(),t=d_(1);I(`title`,`Open `+e.label()+` in your editor`),j(`aria-label`,`Open `+e.label()+` in your editor`),A(),I(`ngTemplateOutlet`,t),A(),N(e.message()?2:-1)}}function uj(e,t){if(e&1&&(L(0,`span`,1),Gg(1,3),R()),e&2){let e=G(),t=d_(1);I(`title`,e.label()),A(),I(`ngTemplateOutlet`,t)}}var dj=class e{source=k(k(null));client=Q(()=>this.source()());available=Q(()=>{let e=this.client();return!!e&&!FE(e)});use(e){this.source.set(e)}async open(e,t){if(!this.available())return{ok:!1,error:`No server is connected.`};try{let n=await NE(this.client(),`open-in-editor`,{file:e,...t?{line:t}:{}});return n?.ok?{ok:!0}:{ok:!1,error:n?.error??`The server did not open it.`}}catch{return{ok:!1,error:`The server did not open it.`}}}static ɵfac=function(t){return new(t||e)};static ɵprov=Ml({token:e,factory:e.ɵfac})};function fj(e,t){return t?`${e}:${t}`:e}var pj=class e{editor=w(dj);file=$.required();line=$(void 0);label=Q(()=>fj(this.file(),this.line()));message=k(``);async open(){this.message.set(``);let e=await this.editor.open(this.file(),this.line()??void 0);e.ok||this.message.set(`Not opened: ${e.error}`)}static ɵfac=function(t){return new(t||e)};static ɵcmp=Kh({type:e,selectors:[[`app-source-link`]],inputs:{file:[1,`file`],line:[1,`line`]},ngContentSelectors:aj,decls:4,vars:1,consts:[[`text`,``],[1,`source-text`,3,`title`],[`type`,`button`,1,`source-link`,3,`click`,`title`],[3,`ngTemplateOutlet`],[`role`,`alert`,1,`message`]],template:function(e,t){e&1&&(o_(),sg(0,sj,2,0,`ng-template`,null,0,Ev),M(2,lj,3,4)(3,uj,2,2,`span`,1)),e&2&&(A(2),N(t.editor.available()?2:3))},dependencies:[lb],styles:[`[_nghost-%COMP%] { display: inline; min-width: 0; } @@ -1720,7 +1720,7 @@ td.mono[_ngcontent-%COMP%] { color: var(--%NS%danger-text); font-family: var(--%NS%font-sans); font-size: 0.92em; -}`]})},pj=[`field`],mj=[`editButton`],hj=[`valueBox`];function gj(e,t){if(e&1){let e=H();B(0,`div`,5)(1,`label`,6),J(2),V(),B(3,`input`,7,0),W(`input`,function(t){return E(e),D(G().draft.set(t.target.value))})(`keydown.enter`,function(t){E(e);let n=G();return t.preventDefault(),D(n.apply())})(`keydown.escape`,function(t){E(e);let n=G();return t.preventDefault(),t.stopPropagation(),D(n.cancel())}),V(),B(5,`button`,8),W(`click`,function(){return E(e),D(G().apply())}),J(6,`Apply`),V(),B(7,`button`,9),W(`click`,function(){return E(e),D(G().cancel())}),J(8,`Cancel`),V()(),B(9,`p`,10),J(10),V()}if(e&2){let e=G();A(),qg(`htmlFor`,e.fieldId),A(),X(`New value of `,e.prop().name,`, as JSON`),A(),qg(`id`,e.fieldId)(`value`,e.draft())(`readOnly`,e.busy()),j(`aria-busy`,e.busy()?`true`:null)(`aria-invalid`,e.error()?`true`:null)(`aria-describedby`,e.hintId+(e.error()?` `+e.errorId:``)),A(2),qg(`disabled`,e.busy()),A(4),qg(`id`,e.hintId),A(),X(` JSON, such as "text", 42, true or `,e.objectSample,`. Enter applies, Escape cancels. `)}}function _j(e,t){if(e&1&&(B(0,`pre`,null,1),J(2),xv(3,`json`),V()),e&2){let e=G(2);j(`tabindex`,e.scrollable()?0:null)(`role`,e.scrollable()?`region`:null)(`aria-label`,e.scrollable()?`Value of `+e.prop().name:null),A(2),Y(Cv(3,4,e.prop().value))}}function vj(e,t){if(e&1){let e=H();B(0,`button`,14,2),W(`click`,function(){return E(e),D(G(2).start())}),J(2,` Edit `),V()}if(e&2){let e=G(2);qg(`disabled`,e.busy()),j(`aria-label`,`Edit `+e.prop().name)}}function yj(e,t){if(e&1&&(B(0,`p`,13),J(1),V()),e&2){let e=G(2);A(),Y(e.prop().locked)}}function bj(e,t){if(e&1&&(B(0,`div`,11),M(1,_j,4,6,`pre`),M(2,vj,3,2,`button`,12),V(),M(3,yj,2,1,`p`,13)),e&2){let e=G();A(),N(e.bare()?-1:1),A(),N(e.editable()?2:-1),A(),N(e.writes()&&e.prop().locked?3:-1)}}function xj(e,t){if(e&1&&(B(0,`p`,3),J(1),V()),e&2){let e=G();qg(`id`,e.errorId),A(),Y(e.error())}}var Sj=0;function Cj(e){let t=e.trim();if(!t)return{error:`Type a JSON value, such as "text", 42, true, null or [1, 2].`};try{return{value:JSON.parse(t)}}catch{return{error:`Not valid JSON. Put text in double quotes, such as "Paris".`}}}var wj=class e{prop=$.required();componentId=$.required();pageId=$(null);rpc=$(null);writes=$(!1);bare=$(!1);readDraft=$(null);fieldId=`prop-edit-${++Sj}`;hintId=`${this.fieldId}-hint`;errorId=`${this.fieldId}-error`;objectSample=`{"a": 1}`;editing=k(!1);draft=k(``);busy=k(!1);error=k(``);done=k(``);editable=Q(()=>this.writes()&&!!this.prop().edit&&!!this.pageId()&&!!this.rpc());field=Jv(`field`);editButton=Jv(`editButton`);valueBox=Jv(`valueBox`);injector=w(ws);scrollable=k(!1);constructor(){uy(()=>{this.prop();let e=this.valueBox()?.nativeElement;this.scrollable.set(!!e&&e.scrollHeight>e.clientHeight+1)})}async start(){let e=this.prop().value;this.error.set(``),this.done.set(``);let t=this.readDraft();if(t){this.busy.set(!0);let n=await t();if(this.busy.set(!1),n.error!==void 0){this.error.set(n.error);return}e=n.value}this.draft.set(e===void 0?``:JSON.stringify(e)),this.editing.set(!0),Vd(()=>{let e=this.field()?.nativeElement;e?.focus(),e?.select()},{injector:this.injector})}cancel(){this.editing()&&(this.editing.set(!1),this.busy.set(!1),this.error.set(``),this.focusEdit())}async apply(){if(this.busy())return;let e=Cj(this.draft());if(`error`in e){this.error.set(e.error);return}let t=this.prop(),n=this.rpc(),r=this.pageId();if(!n||!r||!t.edit)return;this.busy.set(!0),this.error.set(``);let i;try{i=await n.scope(`pangular`).rpc.call(`request-component-edit`,{pageId:r,request:{id:this.componentId(),path:t.prop,value:e.value,kind:t.edit}})??{}}catch{i={error:`The devtools server did not answer. Try again.`}}this.busy.set(!1),this.editing()&&(i.ok?(this.editing.set(!1),this.done.set(`Set ${t.name}.`),this.focusEdit()):(this.error.set(i.error??`The page did not write it.`),this.field()?.nativeElement.focus()))}focusEdit(){Vd(()=>this.editButton()?.nativeElement.focus(),{injector:this.injector})}static ɵfac=function(t){return new(t||e)};static ɵcmp=Kh({type:e,selectors:[[`app-prop-value`]],viewQuery:function(e,t){e&1&&l_(t.field,pj,5)(t.editButton,mj,5)(t.valueBox,hj,5),e&2&&u_(3)},inputs:{prop:[1,`prop`],componentId:[1,`componentId`],pageId:[1,`pageId`],rpc:[1,`rpc`],writes:[1,`writes`],bare:[1,`bare`],readDraft:[1,`readDraft`]},decls:5,vars:3,consts:[[`field`,``],[`valueBox`,``],[`editButton`,``],[`role`,`alert`,1,`error`,3,`id`],[`role`,`status`,1,`sr-only`],[1,`editor`],[1,`sr-only`,3,`for`],[`type`,`text`,`autocomplete`,`off`,`spellcheck`,`false`,1,`field`,`mono`,3,`input`,`keydown.enter`,`keydown.escape`,`id`,`value`,`readOnly`],[`type`,`button`,1,`primary`,3,`click`,`disabled`],[`type`,`button`,3,`click`],[1,`hint`,3,`id`],[1,`view`],[`type`,`button`,1,`edit`,3,`disabled`],[1,`locked`],[`type`,`button`,1,`edit`,3,`click`,`disabled`]],template:function(e,t){e&1&&(M(0,gj,11,11)(1,bj,4,3),M(2,xj,2,2,`p`,3),B(3,`p`,4),J(4),V()),e&2&&(N(+!t.editing()),A(2),N(t.error()?2:-1),A(2),Y(t.done()))},dependencies:[hb],styles:[`[_nghost-%COMP%] { +}`]})},mj=[`field`],hj=[`editButton`],gj=[`valueBox`];function _j(e,t){if(e&1){let e=H();B(0,`div`,5)(1,`label`,6),J(2),V(),B(3,`input`,7,0),W(`input`,function(t){return E(e),D(G().draft.set(t.target.value))})(`keydown.enter`,function(t){E(e);let n=G();return t.preventDefault(),D(n.apply())})(`keydown.escape`,function(t){E(e);let n=G();return t.preventDefault(),t.stopPropagation(),D(n.cancel())}),V(),B(5,`button`,8),W(`click`,function(){return E(e),D(G().apply())}),J(6,`Apply`),V(),B(7,`button`,9),W(`click`,function(){return E(e),D(G().cancel())}),J(8,`Cancel`),V()(),B(9,`p`,10),J(10),V()}if(e&2){let e=G();A(),qg(`htmlFor`,e.fieldId),A(),X(`New value of `,e.prop().name,`, as JSON`),A(),qg(`id`,e.fieldId)(`value`,e.draft())(`readOnly`,e.busy()),j(`aria-busy`,e.busy()?`true`:null)(`aria-invalid`,e.error()?`true`:null)(`aria-describedby`,e.hintId+(e.error()?` `+e.errorId:``)),A(2),qg(`disabled`,e.busy()),A(4),qg(`id`,e.hintId),A(),X(` JSON, such as "text", 42, true or `,e.objectSample,`. Enter applies, Escape cancels. `)}}function vj(e,t){if(e&1&&(B(0,`pre`,null,1),J(2),xv(3,`json`),V()),e&2){let e=G(2);j(`tabindex`,e.scrollable()?0:null)(`role`,e.scrollable()?`region`:null)(`aria-label`,e.scrollable()?`Value of `+e.prop().name:null),A(2),Y(Cv(3,4,e.prop().value))}}function yj(e,t){if(e&1){let e=H();B(0,`button`,14,2),W(`click`,function(){return E(e),D(G(2).start())}),J(2,` Edit `),V()}if(e&2){let e=G(2);qg(`disabled`,e.busy()),j(`aria-label`,`Edit `+e.prop().name)}}function bj(e,t){if(e&1&&(B(0,`p`,13),J(1),V()),e&2){let e=G(2);A(),Y(e.prop().locked)}}function xj(e,t){if(e&1&&(B(0,`div`,11),M(1,vj,4,6,`pre`),M(2,yj,3,2,`button`,12),V(),M(3,bj,2,1,`p`,13)),e&2){let e=G();A(),N(e.bare()?-1:1),A(),N(e.editable()?2:-1),A(),N(e.writes()&&e.prop().locked?3:-1)}}function Sj(e,t){if(e&1&&(B(0,`p`,3),J(1),V()),e&2){let e=G();qg(`id`,e.errorId),A(),Y(e.error())}}var Cj=0;function wj(e){let t=e.trim();if(!t)return{error:`Type a JSON value, such as "text", 42, true, null or [1, 2].`};try{return{value:JSON.parse(t)}}catch{return{error:`Not valid JSON. Put text in double quotes, such as "Paris".`}}}var Tj=class e{prop=$.required();componentId=$.required();pageId=$(null);rpc=$(null);writes=$(!1);bare=$(!1);readDraft=$(null);fieldId=`prop-edit-${++Cj}`;hintId=`${this.fieldId}-hint`;errorId=`${this.fieldId}-error`;objectSample=`{"a": 1}`;editing=k(!1);draft=k(``);busy=k(!1);error=k(``);done=k(``);editable=Q(()=>this.writes()&&!!this.prop().edit&&!!this.pageId()&&!!this.rpc());field=Jv(`field`);editButton=Jv(`editButton`);valueBox=Jv(`valueBox`);injector=w(ws);scrollable=k(!1);constructor(){uy(()=>{this.prop();let e=this.valueBox()?.nativeElement;this.scrollable.set(!!e&&e.scrollHeight>e.clientHeight+1)})}async start(){let e=this.prop().value;this.error.set(``),this.done.set(``);let t=this.readDraft();if(t){this.busy.set(!0);let n=await t();if(this.busy.set(!1),n.error!==void 0){this.error.set(n.error);return}e=n.value}this.draft.set(e===void 0?``:JSON.stringify(e)),this.editing.set(!0),Vd(()=>{let e=this.field()?.nativeElement;e?.focus(),e?.select()},{injector:this.injector})}cancel(){this.editing()&&(this.editing.set(!1),this.busy.set(!1),this.error.set(``),this.focusEdit())}async apply(){if(this.busy())return;let e=wj(this.draft());if(`error`in e){this.error.set(e.error);return}let t=this.prop(),n=this.rpc(),r=this.pageId();if(!n||!r||!t.edit)return;this.busy.set(!0),this.error.set(``);let i;try{i=await n.scope(`pangular`).rpc.call(`request-component-edit`,{pageId:r,request:{id:this.componentId(),path:t.prop,value:e.value,kind:t.edit}})??{}}catch{i={error:`The devtools server did not answer. Try again.`}}this.busy.set(!1),this.editing()&&(i.ok?(this.editing.set(!1),this.done.set(`Set ${t.name}.`),this.focusEdit()):(this.error.set(i.error??`The page did not write it.`),this.field()?.nativeElement.focus()))}focusEdit(){Vd(()=>this.editButton()?.nativeElement.focus(),{injector:this.injector})}static ɵfac=function(t){return new(t||e)};static ɵcmp=Kh({type:e,selectors:[[`app-prop-value`]],viewQuery:function(e,t){e&1&&l_(t.field,mj,5)(t.editButton,hj,5)(t.valueBox,gj,5),e&2&&u_(3)},inputs:{prop:[1,`prop`],componentId:[1,`componentId`],pageId:[1,`pageId`],rpc:[1,`rpc`],writes:[1,`writes`],bare:[1,`bare`],readDraft:[1,`readDraft`]},decls:5,vars:3,consts:[[`field`,``],[`valueBox`,``],[`editButton`,``],[`role`,`alert`,1,`error`,3,`id`],[`role`,`status`,1,`sr-only`],[1,`editor`],[1,`sr-only`,3,`for`],[`type`,`text`,`autocomplete`,`off`,`spellcheck`,`false`,1,`field`,`mono`,3,`input`,`keydown.enter`,`keydown.escape`,`id`,`value`,`readOnly`],[`type`,`button`,1,`primary`,3,`click`,`disabled`],[`type`,`button`,3,`click`],[1,`hint`,3,`id`],[1,`view`],[`type`,`button`,1,`edit`,3,`disabled`],[1,`locked`],[`type`,`button`,1,`edit`,3,`click`,`disabled`]],template:function(e,t){e&1&&(M(0,_j,11,11)(1,xj,4,3),M(2,Sj,2,2,`p`,3),B(3,`p`,4),J(4),V()),e&2&&(N(+!t.editing()),A(2),N(t.error()?2:-1),A(2),Y(t.done()))},dependencies:[hb],styles:[`[_nghost-%COMP%] { display: block; min-width: 0; } @@ -1843,7 +1843,7 @@ button.edit[_ngcontent-%COMP%] { .error[_ngcontent-%COMP%] { color: var(--%NS%danger-text); -}`]})},Tj=1e3,Ej={depth:5,limit:100},Dj=`[redacted]`;function Oj(e){return!!e&&typeof e==`object`&&!Array.isArray(e)}function kj(e){return Oj(e)&&typeof e.$more==`number`&&Object.keys(e).length===1}function Aj(e){return Oj(e)&&typeof e.$collapsed==`string`&&typeof e.size==`number`&&Object.keys(e).length===2}function jj(e){return e===`Array`?`array`:e===`Map`?`map`:e===`Set`?`set`:`object`}function Mj(e){return e.$type===`Map`&&Array.isArray(e.entries)}function Nj(e){return e.$type===`Set`&&Array.isArray(e.values)}function Pj(e){return Object.keys(e).filter(e=>e!==`$type`&&e!==`$more`)}function Fj(e){if(!e||typeof e!=`object`)return null;if(Aj(e))return e.size<=0?null:{kind:jj(e.$collapsed),type:e.$collapsed,size:e.size,collapsed:!0};if(kj(e))return null;if(Array.isArray(e)){let t=e.at(-1),n=kj(t)?t.$more:0,r=e.length-+!!n+n;return r?{kind:`array`,type:`Array`,size:r,collapsed:!1}:null}let t=e;if(Mj(t)||Nj(t)){let e=typeof t.size==`number`?t.size:0,n=Mj(t)?`map`:`set`;return e?{kind:n,type:n===`map`?`Map`:`Set`,size:e,collapsed:!1}:null}let n=typeof t.$more==`number`?t.$more:0,r=Pj(t).length+n,i=typeof t.$type==`string`?t.$type:`Object`;return r?{kind:`object`,type:i,size:r,collapsed:!1}:null}function Ij(e){if(typeof e==`string`)return JSON.stringify(e);let t=Fj(e);if(t)return zj(t);try{return JSON.stringify(e)??String(e)}catch{return String(e)}}function Lj(e){let t=Fj(e);if(!t||t.collapsed)return{children:[],more:0};if(Array.isArray(e)){let n=(kj(e.at(-1))?e.slice(0,-1):e).map((e,t)=>({key:String(t),label:String(t),value:e}));return{children:n,more:t.size-n.length}}let n=e;if(t.kind===`map`){let e=n.entries.map((e,t)=>{let[n,r]=Array.isArray(e)?e:[void 0,e];return{key:String(t),label:Ij(n),value:r}});return{children:e,more:Math.max(0,t.size-e.length)}}if(t.kind===`set`){let e=n.values.map((e,t)=>({key:String(t),label:String(t),value:e}));return{children:e,more:Math.max(0,t.size-e.length)}}return{children:Pj(n).map(e=>({key:e,label:e,value:n[e]})),more:typeof n.$more==`number`?n.$more:0}}function Rj(e,t,n){return`${e} ${e===1?t:n}`}function zj(e){let t={array:Rj(e.size,`item`,`items`),object:Rj(e.size,`key`,`keys`),map:Rj(e.size,`entry`,`entries`),set:Rj(e.size,`value`,`values`)}[e.kind];return`${e.type} (${t})`}function Bj(e,t){return JSON.stringify(t===void 0?e:[t,...e])}function Vj(e,t){return e.map((e,n)=>n===0?e:(t?t[n-1]!==`object`:/^\d+$/.test(e))?`[${e}]`:`.${e}`).join(``)}function Hj(e){return e.every(e=>e&&e===e.trim()&&!/[.[\]]/.test(e))?e.join(`.`):null}var Uj=/^\[(Getter|Unreadable|Circular|Truncated|Function\b.*|Promise)\]$/;function Wj(e){return e===null||typeof e==`boolean`?!0:typeof e==`number`?Number.isFinite(e):typeof e==`string`&&e!==Dj&&!Uj.test(e)&&!e.endsWith(`…`)}function Gj(e,t){return!e||t.kind===`map`||t.kind===`set`||e===`signal`&&t.kind===`object`&&t.type!==`Object`?null:e}function Kj(e,t,n,r,i=500){let a=[],o=r.trim().toLowerCase();if(!o)return a;let s=new Set,c=(e,t)=>{let r=Bj(e);if(!(s.has(r)||a.length>=i)){s.add(r);for(let r of Lj(t).children){let t=[...e,r.key];if(r.label.toLowerCase().includes(o)&&a.push(t),!Fj(r.value))continue;let i=n(t)??(Fj(r.value)?.collapsed?void 0:r.value);i!==void 0&&c(t,i)}}};return c(e,n(e)??t),a}var qj=(e,t)=>t.id;function Jj(e,t){if(e&1){let e=H();L(0,`button`,2),U(`click`,function(){E(e);let t=G().$implicit;return D(G(3).toggle(t))}),O(),L(1,`svg`,3),z(2,`path`,4),R(),gs(),L(3,`span`,14),J(4),R(),L(5,`span`,5),J(6),R()()}if(e&2){let e=G().$implicit;j(`aria-expanded`,e.open)(`aria-label`,e.label+`: `+e.text),A(4),X(``,e.label,`:`),A(2),Y(e.text)}}function Yj(e,t){if(e&1&&z(0,`app-prop-value`,15),e&2){let e=G(5);I(`prop`,t)(`componentId`,e.componentId())(`pageId`,e.pageId())(`rpc`,e.rpc())(`writes`,e.writes())}}function Xj(e,t){if(e&1&&(L(0,`span`,16),J(1),R()),e&2){let e=G(2).$implicit;A(),Y(e.text)}}function Zj(e,t){if(e&1&&(L(0,`span`,14),J(1),R(),M(2,Yj,1,5,`app-prop-value`,15)(3,Xj,2,1,`span`,16)),e&2){let e,t=G().$implicit;A(),X(``,t.label,`:`),A(),N((e=t.edit)?2:3,e)}}function Qj(e,t){if(e&1){let e=H();L(0,`button`,17),U(`click`,function(){E(e);let t=G().$implicit;return D(G(3).more(t))}),J(1),L(2,`span`,18),J(3),R()()}if(e&2){let e=G().$implicit,t=G(3);A(),X(` Show `,t.step(e),` more `),A(2),X(`(`,e.more,` not listed)`)}}function $j(e,t){e&1&&(L(0,`span`,12),J(1,`Reading from the page…`),R())}function eM(e,t){if(e&1&&(L(0,`span`,13),J(1),R()),e&2){let e=G().$implicit;A(),Y(e.text)}}function tM(e,t){if(e&1&&(L(0,`li`,9),M(1,Jj,7,4,`button`,10)(2,Zj,4,2)(3,Qj,4,2,`button`,11)(4,$j,2,0,`span`,12)(5,eM,2,1,`span`,13),R()),e&2){let e,n=t.$implicit;K(`padding-left`,n.depth*14,`px`),A(),N((e=n.kind)===`node`?1:e===`leaf`?2:e===`more`?3:e===`loading`?4:e===`error`?5:-1)}}function nM(e,t){if(e&1&&(L(0,`ul`,7),P(1,tM,6,3,`li`,8,qj),R()),e&2){let e=G(2);I(`id`,e.listId),j(`aria-label`,e.prop().name+` entries`),A(),F(e.rows())}}function rM(e,t){if(e&1){let e=H();L(0,`div`,1)(1,`button`,2),U(`click`,function(){return E(e),D(G().toggleRoot())}),O(),L(2,`svg`,3),z(3,`path`,4),R(),gs(),L(4,`span`,5),J(5),R()(),z(6,`app-prop-value`,6),R(),M(7,nM,3,2,`ul`,7)}if(e&2){let e=t,n=G();A(),j(`aria-expanded`,n.rootOpen())(`aria-controls`,n.rootOpen()?n.listId:null)(`aria-label`,n.prop().name+`: `+n.summaryOf(e)),A(4),Y(n.summaryOf(e)),A(),I(`bare`,!0)(`prop`,n.prop())(`componentId`,n.componentId())(`pageId`,n.pageId())(`rpc`,n.rpc())(`writes`,n.writes())(`readDraft`,n.readWhole),A(),N(n.rootOpen()?7:-1)}}function iM(e,t){if(e&1&&z(0,`app-prop-value`,0),e&2){let e=G();I(`prop`,e.prop())(`componentId`,e.componentId())(`pageId`,e.pageId())(`rpc`,e.rpc())(`writes`,e.writes())}}var aM=0;function oM(e){if(e===void 0)return`undefined`;try{return JSON.stringify(e)??String(e)}catch{return String(e)}}function sM(e){if(Array.isArray(e))return e.every(sM);if(e&&typeof e==`object`){let t=e;return![`$collapsed`,`$more`,`$type`].some(e=>Object.hasOwn(t,e))&&Object.values(t).every(sM)}return Wj(e)}var cM=class e{prop=$.required();store=$.required();componentId=$.required();pageId=$(null);rpc=$(null);writes=$(!1);directive=$(void 0);query=$(``);listId=`value-tree-${++aM}`;host=w(Fl);pendingMore=null;constructor(){uy(()=>{let e=this.rows(),t=this.pendingMore;if(!t)return;let n=e.findIndex(e=>(e.kind===`leaf`||e.kind===`node`)&&!t.has(e.id));if(n<0)return;this.pendingMore=null;let r=document.activeElement;r&&r!==document.body&&r.isConnected||this.focusNear(n)})}rootPath=Q(()=>[this.prop().prop]);rootValue=Q(()=>this.store().level(this.rootPath(),this.directive())??this.prop().value);root=Q(()=>Fj(this.rootValue()));needle=Q(()=>this.query().trim().toLowerCase());matches=Q(()=>{let e=this.needle(),t=this.prop();if(!e||[t.name,t.prop].some(t=>t.toLowerCase().includes(e)))return null;let n=this.store(),r=this.directive();return Kj(this.rootPath(),this.rootValue(),e=>n.level(e,r),e)});rootOpen=Q(()=>!!this.matches()?.length||this.store().isOpen(this.rootPath(),this.directive()));rows=Q(()=>{let e=this.root();if(!e)return[];let t=this.store(),n=this.directive(),r=this.matches(),i=new Set,a=new Set;for(let e of r??[]){a.add(Bj(e));for(let t=2;t<=e.length;t++)i.add(Bj(e.slice(0,t)))}let o=this.prop(),s=this.writes()&&n===void 0,c=[],l=(e,o,d,f,p,m)=>{let{children:h,more:g}=Lj(e);for(let e of h){let h=[...o,e.key],g=Bj(h);if(r&&!m&&!i.has(g))continue;let _=Fj(e.value),ee=Bj(h,n);if(!_){let t=p&&Wj(e.value)?Hj(h):null;c.push({id:ee,kind:`leaf`,depth:f,path:h,label:e.label,text:oM(e.value),edit:s&&t&&p?{name:Vj(h,[...d]),prop:t,value:e.value,edit:p}:void 0});continue}let te=!!r&&!m&&i.has(g)&&!a.has(g)||t.isOpen(h,n);if(c.push({id:ee,kind:`node`,depth:f,path:h,label:e.label,text:zj(_),container:_,open:te}),!te)continue;let ne=t.level(h,n)??(_.collapsed?void 0:e.value),re=[...d,_.kind];ne===void 0?t.errors().has(ee)?c.push({...u(h,f+1,`error`),text:t.errors().get(ee)}):c.push(u(h,f+1,`loading`)):l(ne,h,re,f+1,Gj(p,_),m||a.has(g))}g>0&&(!r||m)&&c.push({...u(o,f,`more`),more:g,listed:h.length})},u=(e,t,r)=>({id:`${Bj(e,n)}:${r}`,kind:r,depth:t,path:e,label:``,text:``});return l(this.rootValue(),this.rootPath(),[e.kind],0,s?Gj(o.editInside??null,e):null,!r),c});readWhole=async()=>{let e=await this.store().read(this.rootPath(),Ej.depth,Ej.limit);return e.ok?sM(e.value)?{value:e.value}:{error:`Too large to edit as a whole. Open it to edit the values inside.`}:{error:e.error??`The page did not read it.`}};summaryOf(e){return zj(e)}toggleRoot(){this.pendingMore=null,this.store().toggle(this.rootPath(),this.directive(),!1)}toggle(e){this.pendingMore=null,this.store().toggle(e.path,this.directive(),!!e.container?.collapsed)}more(e){this.pendingMore=new Set(this.rows().map(e=>e.id)),this.store().more(e.path,this.directive(),e.listed??0)}focusNear(e){let t=this.host.nativeElement,n=Array.from(t.querySelectorAll(`#${this.listId} > li`)),r=e=>e?.querySelector(`button:not([disabled]), input, textarea, [tabindex="0"]`);(n.slice(e).map(r).find(Boolean)??n.slice(0,e).reverse().map(r).find(Boolean)??t.querySelector(`.root > .toggle`))?.focus()}step(e){return Math.min(100,e.more??0)}static ɵfac=function(t){return new(t||e)};static ɵcmp=Kh({type:e,selectors:[[`app-value-tree`]],inputs:{prop:[1,`prop`],store:[1,`store`],componentId:[1,`componentId`],pageId:[1,`pageId`],rpc:[1,`rpc`],writes:[1,`writes`],directive:[1,`directive`],query:[1,`query`]},decls:2,vars:1,consts:[[3,`prop`,`componentId`,`pageId`,`rpc`,`writes`],[1,`root`],[`type`,`button`,1,`toggle`,3,`click`],[`viewBox`,`0 0 24 24`,`aria-hidden`,`true`,1,`chevron`],[`d`,`m9 6 6 6-6 6`],[1,`summary`,`mono`],[1,`whole`,3,`bare`,`prop`,`componentId`,`pageId`,`rpc`,`writes`,`readDraft`],[1,`entries`,3,`id`],[1,`entry`,3,`padding-left`],[1,`entry`],[`type`,`button`,1,`toggle`],[`type`,`button`,1,`more`],[`role`,`status`,1,`muted`],[`role`,`alert`,1,`error`],[1,`key`,`mono`],[1,`leaf-edit`,3,`prop`,`componentId`,`pageId`,`rpc`,`writes`],[1,`leaf`,`mono`],[`type`,`button`,1,`more`,3,`click`],[1,`muted`]],template:function(e,t){if(e&1&&M(0,rM,8,12)(1,iM,1,5,`app-prop-value`,0),e&2){let e;N((e=t.root())?0:1,e)}},dependencies:[wj],styles:[`[_nghost-%COMP%] { +}`]})},Ej=1e3,Dj={depth:5,limit:100},Oj=`[redacted]`;function kj(e){return!!e&&typeof e==`object`&&!Array.isArray(e)}function Aj(e){return kj(e)&&typeof e.$more==`number`&&Object.keys(e).length===1}function jj(e){return kj(e)&&typeof e.$collapsed==`string`&&typeof e.size==`number`&&Object.keys(e).length===2}function Mj(e){return e===`Array`?`array`:e===`Map`?`map`:e===`Set`?`set`:`object`}function Nj(e){return e.$type===`Map`&&Array.isArray(e.entries)}function Pj(e){return e.$type===`Set`&&Array.isArray(e.values)}function Fj(e){return Object.keys(e).filter(e=>e!==`$type`&&e!==`$more`)}function Ij(e){if(!e||typeof e!=`object`)return null;if(jj(e))return e.size<=0?null:{kind:Mj(e.$collapsed),type:e.$collapsed,size:e.size,collapsed:!0};if(Aj(e))return null;if(Array.isArray(e)){let t=e.at(-1),n=Aj(t)?t.$more:0,r=e.length-+!!n+n;return r?{kind:`array`,type:`Array`,size:r,collapsed:!1}:null}let t=e;if(Nj(t)||Pj(t)){let e=typeof t.size==`number`?t.size:0,n=Nj(t)?`map`:`set`;return e?{kind:n,type:n===`map`?`Map`:`Set`,size:e,collapsed:!1}:null}let n=typeof t.$more==`number`?t.$more:0,r=Fj(t).length+n,i=typeof t.$type==`string`?t.$type:`Object`;return r?{kind:`object`,type:i,size:r,collapsed:!1}:null}function Lj(e){if(typeof e==`string`)return JSON.stringify(e);let t=Ij(e);if(t)return Bj(t);try{return JSON.stringify(e)??String(e)}catch{return String(e)}}function Rj(e){let t=Ij(e);if(!t||t.collapsed)return{children:[],more:0};if(Array.isArray(e)){let n=(Aj(e.at(-1))?e.slice(0,-1):e).map((e,t)=>({key:String(t),label:String(t),value:e}));return{children:n,more:t.size-n.length}}let n=e;if(t.kind===`map`){let e=n.entries.map((e,t)=>{let[n,r]=Array.isArray(e)?e:[void 0,e];return{key:String(t),label:Lj(n),value:r}});return{children:e,more:Math.max(0,t.size-e.length)}}if(t.kind===`set`){let e=n.values.map((e,t)=>({key:String(t),label:String(t),value:e}));return{children:e,more:Math.max(0,t.size-e.length)}}return{children:Fj(n).map(e=>({key:e,label:e,value:n[e]})),more:typeof n.$more==`number`?n.$more:0}}function zj(e,t,n){return`${e} ${e===1?t:n}`}function Bj(e){let t={array:zj(e.size,`item`,`items`),object:zj(e.size,`key`,`keys`),map:zj(e.size,`entry`,`entries`),set:zj(e.size,`value`,`values`)}[e.kind];return`${e.type} (${t})`}function Vj(e,t){return JSON.stringify(t===void 0?e:[t,...e])}function Hj(e,t){return e.map((e,n)=>n===0?e:(t?t[n-1]!==`object`:/^\d+$/.test(e))?`[${e}]`:`.${e}`).join(``)}function Uj(e){return e.every(e=>e&&e===e.trim()&&!/[.[\]]/.test(e))?e.join(`.`):null}var Wj=/^\[(Getter|Unreadable|Circular|Truncated|Function\b.*|Promise)\]$/;function Gj(e){return e===null||typeof e==`boolean`?!0:typeof e==`number`?Number.isFinite(e):typeof e==`string`&&e!==Oj&&!Wj.test(e)&&!e.endsWith(`…`)}function Kj(e,t){return!e||t.kind===`map`||t.kind===`set`||e===`signal`&&t.kind===`object`&&t.type!==`Object`?null:e}function qj(e,t,n,r,i=500){let a=[],o=r.trim().toLowerCase();if(!o)return a;let s=new Set,c=(e,t)=>{let r=Vj(e);if(!(s.has(r)||a.length>=i)){s.add(r);for(let r of Rj(t).children){let t=[...e,r.key];if(r.label.toLowerCase().includes(o)&&a.push(t),!Ij(r.value))continue;let i=n(t)??(Ij(r.value)?.collapsed?void 0:r.value);i!==void 0&&c(t,i)}}};return c(e,n(e)??t),a}var Jj=(e,t)=>t.id;function Yj(e,t){if(e&1){let e=H();L(0,`button`,2),U(`click`,function(){E(e);let t=G().$implicit;return D(G(3).toggle(t))}),O(),L(1,`svg`,3),z(2,`path`,4),R(),gs(),L(3,`span`,14),J(4),R(),L(5,`span`,5),J(6),R()()}if(e&2){let e=G().$implicit;j(`aria-expanded`,e.open)(`aria-label`,e.label+`: `+e.text),A(4),X(``,e.label,`:`),A(2),Y(e.text)}}function Xj(e,t){if(e&1&&z(0,`app-prop-value`,15),e&2){let e=G(5);I(`prop`,t)(`componentId`,e.componentId())(`pageId`,e.pageId())(`rpc`,e.rpc())(`writes`,e.writes())}}function Zj(e,t){if(e&1&&(L(0,`span`,16),J(1),R()),e&2){let e=G(2).$implicit;A(),Y(e.text)}}function Qj(e,t){if(e&1&&(L(0,`span`,14),J(1),R(),M(2,Xj,1,5,`app-prop-value`,15)(3,Zj,2,1,`span`,16)),e&2){let e,t=G().$implicit;A(),X(``,t.label,`:`),A(),N((e=t.edit)?2:3,e)}}function $j(e,t){if(e&1){let e=H();L(0,`button`,17),U(`click`,function(){E(e);let t=G().$implicit;return D(G(3).more(t))}),J(1),L(2,`span`,18),J(3),R()()}if(e&2){let e=G().$implicit,t=G(3);A(),X(` Show `,t.step(e),` more `),A(2),X(`(`,e.more,` not listed)`)}}function eM(e,t){e&1&&(L(0,`span`,12),J(1,`Reading from the page…`),R())}function tM(e,t){if(e&1&&(L(0,`span`,13),J(1),R()),e&2){let e=G().$implicit;A(),Y(e.text)}}function nM(e,t){if(e&1&&(L(0,`li`,9),M(1,Yj,7,4,`button`,10)(2,Qj,4,2)(3,$j,4,2,`button`,11)(4,eM,2,0,`span`,12)(5,tM,2,1,`span`,13),R()),e&2){let e,n=t.$implicit;K(`padding-left`,n.depth*14,`px`),A(),N((e=n.kind)===`node`?1:e===`leaf`?2:e===`more`?3:e===`loading`?4:e===`error`?5:-1)}}function rM(e,t){if(e&1&&(L(0,`ul`,7),P(1,nM,6,3,`li`,8,Jj),R()),e&2){let e=G(2);I(`id`,e.listId),j(`aria-label`,e.prop().name+` entries`),A(),F(e.rows())}}function iM(e,t){if(e&1){let e=H();L(0,`div`,1)(1,`button`,2),U(`click`,function(){return E(e),D(G().toggleRoot())}),O(),L(2,`svg`,3),z(3,`path`,4),R(),gs(),L(4,`span`,5),J(5),R()(),z(6,`app-prop-value`,6),R(),M(7,rM,3,2,`ul`,7)}if(e&2){let e=t,n=G();A(),j(`aria-expanded`,n.rootOpen())(`aria-controls`,n.rootOpen()?n.listId:null)(`aria-label`,n.prop().name+`: `+n.summaryOf(e)),A(4),Y(n.summaryOf(e)),A(),I(`bare`,!0)(`prop`,n.prop())(`componentId`,n.componentId())(`pageId`,n.pageId())(`rpc`,n.rpc())(`writes`,n.writes())(`readDraft`,n.readWhole),A(),N(n.rootOpen()?7:-1)}}function aM(e,t){if(e&1&&z(0,`app-prop-value`,0),e&2){let e=G();I(`prop`,e.prop())(`componentId`,e.componentId())(`pageId`,e.pageId())(`rpc`,e.rpc())(`writes`,e.writes())}}var oM=0;function sM(e){if(e===void 0)return`undefined`;try{return JSON.stringify(e)??String(e)}catch{return String(e)}}function cM(e){if(Array.isArray(e))return e.every(cM);if(e&&typeof e==`object`){let t=e;return![`$collapsed`,`$more`,`$type`].some(e=>Object.hasOwn(t,e))&&Object.values(t).every(cM)}return Gj(e)}var lM=class e{prop=$.required();store=$.required();componentId=$.required();pageId=$(null);rpc=$(null);writes=$(!1);directive=$(void 0);query=$(``);listId=`value-tree-${++oM}`;host=w(Fl);pendingMore=null;constructor(){uy(()=>{let e=this.rows(),t=this.pendingMore;if(!t)return;let n=e.findIndex(e=>(e.kind===`leaf`||e.kind===`node`)&&!t.has(e.id));if(n<0)return;this.pendingMore=null;let r=document.activeElement;r&&r!==document.body&&r.isConnected||this.focusNear(n)})}rootPath=Q(()=>[this.prop().prop]);rootValue=Q(()=>this.store().level(this.rootPath(),this.directive())??this.prop().value);root=Q(()=>Ij(this.rootValue()));needle=Q(()=>this.query().trim().toLowerCase());matches=Q(()=>{let e=this.needle(),t=this.prop();if(!e||[t.name,t.prop].some(t=>t.toLowerCase().includes(e)))return null;let n=this.store(),r=this.directive();return qj(this.rootPath(),this.rootValue(),e=>n.level(e,r),e)});rootOpen=Q(()=>!!this.matches()?.length||this.store().isOpen(this.rootPath(),this.directive()));rows=Q(()=>{let e=this.root();if(!e)return[];let t=this.store(),n=this.directive(),r=this.matches(),i=new Set,a=new Set;for(let e of r??[]){a.add(Vj(e));for(let t=2;t<=e.length;t++)i.add(Vj(e.slice(0,t)))}let o=this.prop(),s=this.writes()&&n===void 0,c=[],l=(e,o,d,f,p,m)=>{let{children:h,more:g}=Rj(e);for(let e of h){let h=[...o,e.key],g=Vj(h);if(r&&!m&&!i.has(g))continue;let _=Ij(e.value),ee=Vj(h,n);if(!_){let t=p&&Gj(e.value)?Uj(h):null;c.push({id:ee,kind:`leaf`,depth:f,path:h,label:e.label,text:sM(e.value),edit:s&&t&&p?{name:Hj(h,[...d]),prop:t,value:e.value,edit:p}:void 0});continue}let te=!!r&&!m&&i.has(g)&&!a.has(g)||t.isOpen(h,n);if(c.push({id:ee,kind:`node`,depth:f,path:h,label:e.label,text:Bj(_),container:_,open:te}),!te)continue;let ne=t.level(h,n)??(_.collapsed?void 0:e.value),re=[...d,_.kind];ne===void 0?t.errors().has(ee)?c.push({...u(h,f+1,`error`),text:t.errors().get(ee)}):c.push(u(h,f+1,`loading`)):l(ne,h,re,f+1,Kj(p,_),m||a.has(g))}g>0&&(!r||m)&&c.push({...u(o,f,`more`),more:g,listed:h.length})},u=(e,t,r)=>({id:`${Vj(e,n)}:${r}`,kind:r,depth:t,path:e,label:``,text:``});return l(this.rootValue(),this.rootPath(),[e.kind],0,s?Kj(o.editInside??null,e):null,!r),c});readWhole=async()=>{let e=await this.store().read(this.rootPath(),Dj.depth,Dj.limit);return e.ok?cM(e.value)?{value:e.value}:{error:`Too large to edit as a whole. Open it to edit the values inside.`}:{error:e.error??`The page did not read it.`}};summaryOf(e){return Bj(e)}toggleRoot(){this.pendingMore=null,this.store().toggle(this.rootPath(),this.directive(),!1)}toggle(e){this.pendingMore=null,this.store().toggle(e.path,this.directive(),!!e.container?.collapsed)}more(e){this.pendingMore=new Set(this.rows().map(e=>e.id)),this.store().more(e.path,this.directive(),e.listed??0)}focusNear(e){let t=this.host.nativeElement,n=Array.from(t.querySelectorAll(`#${this.listId} > li`)),r=e=>e?.querySelector(`button:not([disabled]), input, textarea, [tabindex="0"]`);(n.slice(e).map(r).find(Boolean)??n.slice(0,e).reverse().map(r).find(Boolean)??t.querySelector(`.root > .toggle`))?.focus()}step(e){return Math.min(100,e.more??0)}static ɵfac=function(t){return new(t||e)};static ɵcmp=Kh({type:e,selectors:[[`app-value-tree`]],inputs:{prop:[1,`prop`],store:[1,`store`],componentId:[1,`componentId`],pageId:[1,`pageId`],rpc:[1,`rpc`],writes:[1,`writes`],directive:[1,`directive`],query:[1,`query`]},decls:2,vars:1,consts:[[3,`prop`,`componentId`,`pageId`,`rpc`,`writes`],[1,`root`],[`type`,`button`,1,`toggle`,3,`click`],[`viewBox`,`0 0 24 24`,`aria-hidden`,`true`,1,`chevron`],[`d`,`m9 6 6 6-6 6`],[1,`summary`,`mono`],[1,`whole`,3,`bare`,`prop`,`componentId`,`pageId`,`rpc`,`writes`,`readDraft`],[1,`entries`,3,`id`],[1,`entry`,3,`padding-left`],[1,`entry`],[`type`,`button`,1,`toggle`],[`type`,`button`,1,`more`],[`role`,`status`,1,`muted`],[`role`,`alert`,1,`error`],[1,`key`,`mono`],[1,`leaf-edit`,3,`prop`,`componentId`,`pageId`,`rpc`,`writes`],[1,`leaf`,`mono`],[`type`,`button`,1,`more`,3,`click`],[1,`muted`]],template:function(e,t){if(e&1&&M(0,iM,8,12)(1,aM,1,5,`app-prop-value`,0),e&2){let e;N((e=t.root())?0:1,e)}},dependencies:[Tj],styles:[`[_nghost-%COMP%] { display: block; min-width: 0; } @@ -1965,7 +1965,7 @@ button.edit[_ngcontent-%COMP%] { .more[_ngcontent-%COMP%] { transition: none; } -}`]})};function lM(e,t){return e.length>t.length&&e.startsWith(`${t.slice(0,-1)},`)}var uM=class{target={rpc:null,pageId:null,componentId:null};fetched=k(new Map);pushed=k({});open=k(new Set);loading=k(new Set);errors=k(new Map);limits=new Map;generation=0;follow(e,t){let n=e.componentId===this.target.componentId&&e.pageId===this.target.pageId;this.target=e,n||(this.generation++,this.fetched.set(new Map),this.open.set(new Set),this.loading.set(new Set),this.errors.set(new Map),this.limits.clear()),this.pushed.set(t??{})}level(e,t){let n=Bj(e,t),r=this.pushed();return Object.hasOwn(r,n)?r[n]:this.fetched().get(n)}isOpen(e,t){return this.open().has(Bj(e,t))}toggle(e,t,n){let r=Bj(e,t),i=new Set(this.open());if(i.has(r)){for(let e of i)(e===r||lM(e,r))&&i.delete(e);this.open.set(i),this.send({path:e,directive:t,forget:!0});return}i.add(r),this.open.set(i),n&&this.load(e,t,this.limits.get(r)??30)}more(e,t,n){let r=Bj(e,t),i=Math.min(Tj,Math.max(n,this.limits.get(r)??0)+100);this.limits.set(r,i),this.load(e,t,i)}async read(e,t,n){return this.send({path:e,depth:t,limit:n})}async load(e,t,n){let r=Bj(e,t),i=this.generation;this.loading.update(e=>new Set(e).add(r));let a=await this.send({path:e,directive:t,limit:n,watch:!0});i===this.generation&&(this.loading.update(e=>{let t=new Set(e);return t.delete(r),t}),this.errors.update(e=>{let t=new Map(e);return a.ok?t.delete(r):t.set(r,a.error??`The page did not read it.`),t}),a.ok&&this.fetched.update(e=>new Map(e).set(r,a.value)))}async send(e){let{rpc:t,pageId:n,componentId:r}=this.target;if(!t||!n||!r)return{error:`No live page to read from.`};let{directive:i,...a}=e;try{return await t.scope(`pangular`).rpc.call(`request-component-value`,{pageId:n,request:{id:r,...a,...i===void 0?{}:{directive:i}}})??{}}catch{return{error:`The devtools server did not answer. Try again.`}}}};function dM(e,t){if(e&1&&(B(0,`span`,1),J(1),V()),e&2){let e=G(2);A(),X(`: `,e.label())}}function fM(e,t){if(e&1&&(B(0,`span`,1),J(1),V()),e&2){let e=G(3);A(),X(`: `,e.label())}}function pM(e,t){if(e&1){let e=H();B(0,`button`,4),W(`click`,function(){return E(e),D(G(2).store(`log`))}),J(1,` Log to console `),M(2,fM,2,1,`span`,1),V()}if(e&2){let e=G(2);qg(`disabled`,e.busy()||!e.rpc()||!e.pageId()),A(2),N(e.label()?2:-1)}}function mM(e,t){if(e&1){let e=H();B(0,`button`,0),W(`click`,function(){return E(e),D(G().store())}),J(1,` Store as global `),M(2,dM,2,1,`span`,1),V(),M(3,pM,3,2,`button`,2),B(4,`span`,3),J(5),V()}if(e&2){let e=G();qg(`disabled`,e.busy()||!e.rpc()||!e.pageId()),A(2),N(e.label()?2:-1),A(),N(e.canLog()?3:-1),A(2),Y(e.message())}}var hM=class e{rpc=$(null);pageId=$(null);target=$.required();label=$(``);canLog=$(!1);staticReport=Q(()=>FE(this.rpc()));busy=k(!1);message=k(``);async store(e=`store`){let t=this.rpc(),n=this.pageId();if(t&&n){this.busy.set(!0),this.message.set(``);try{let r=await t.scope(`pangular`).rpc.call(`request-console-global`,{pageId:n,target:this.target(),...e===`log`?{mode:e}:{}}),i=e===`log`?r?.ok?`Logged in the page console.`:`Not logged.`:r?.ok&&r.name?`Stored as ${r.name}.`:`Not stored.`;this.message.set(r?.message||i)}catch{this.message.set(`Could not reach the page.`)}finally{this.busy.set(!1)}}}static ɵfac=function(t){return new(t||e)};static ɵcmp=Kh({type:e,selectors:[[`app-store-global`]],inputs:{rpc:[1,`rpc`],pageId:[1,`pageId`],target:[1,`target`],label:[1,`label`],canLog:[1,`canLog`]},decls:1,vars:1,consts:[[`type`,`button`,1,`store`,3,`click`,`disabled`],[1,`sr-only`],[`type`,`button`,1,`store`,`log`,3,`disabled`],[`role`,`status`,1,`status`],[`type`,`button`,1,`store`,`log`,3,`click`,`disabled`]],template:function(e,t){e&1&&M(0,mM,6,4),e&2&&N(t.staticReport()?-1:0)},styles:[`[_nghost-%COMP%] { +}`]})};function uM(e,t){return e.length>t.length&&e.startsWith(`${t.slice(0,-1)},`)}var dM=class{target={rpc:null,pageId:null,componentId:null};fetched=k(new Map);pushed=k({});open=k(new Set);loading=k(new Set);errors=k(new Map);limits=new Map;generation=0;follow(e,t){let n=e.componentId===this.target.componentId&&e.pageId===this.target.pageId;this.target=e,n||(this.generation++,this.fetched.set(new Map),this.open.set(new Set),this.loading.set(new Set),this.errors.set(new Map),this.limits.clear()),this.pushed.set(t??{})}level(e,t){let n=Vj(e,t),r=this.pushed();return Object.hasOwn(r,n)?r[n]:this.fetched().get(n)}isOpen(e,t){return this.open().has(Vj(e,t))}toggle(e,t,n){let r=Vj(e,t),i=new Set(this.open());if(i.has(r)){for(let e of i)(e===r||uM(e,r))&&i.delete(e);this.open.set(i),this.send({path:e,directive:t,forget:!0});return}i.add(r),this.open.set(i),n&&this.load(e,t,this.limits.get(r)??30)}more(e,t,n){let r=Vj(e,t),i=Math.min(Ej,Math.max(n,this.limits.get(r)??0)+100);this.limits.set(r,i),this.load(e,t,i)}async read(e,t,n){return this.send({path:e,depth:t,limit:n})}async load(e,t,n){let r=Vj(e,t),i=this.generation;this.loading.update(e=>new Set(e).add(r));let a=await this.send({path:e,directive:t,limit:n,watch:!0});i===this.generation&&(this.loading.update(e=>{let t=new Set(e);return t.delete(r),t}),this.errors.update(e=>{let t=new Map(e);return a.ok?t.delete(r):t.set(r,a.error??`The page did not read it.`),t}),a.ok&&this.fetched.update(e=>new Map(e).set(r,a.value)))}async send(e){let{rpc:t,pageId:n,componentId:r}=this.target;if(!t||!n||!r)return{error:`No live page to read from.`};let{directive:i,...a}=e;try{return await t.scope(`pangular`).rpc.call(`request-component-value`,{pageId:n,request:{id:r,...a,...i===void 0?{}:{directive:i}}})??{}}catch{return{error:`The devtools server did not answer. Try again.`}}}};function fM(e,t){if(e&1&&(B(0,`span`,1),J(1),V()),e&2){let e=G(2);A(),X(`: `,e.label())}}function pM(e,t){if(e&1&&(B(0,`span`,1),J(1),V()),e&2){let e=G(3);A(),X(`: `,e.label())}}function mM(e,t){if(e&1){let e=H();B(0,`button`,4),W(`click`,function(){return E(e),D(G(2).store(`log`))}),J(1,` Log to console `),M(2,pM,2,1,`span`,1),V()}if(e&2){let e=G(2);qg(`disabled`,e.busy()||!e.rpc()||!e.pageId()),A(2),N(e.label()?2:-1)}}function hM(e,t){if(e&1){let e=H();B(0,`button`,0),W(`click`,function(){return E(e),D(G().store())}),J(1,` Store as global `),M(2,fM,2,1,`span`,1),V(),M(3,mM,3,2,`button`,2),B(4,`span`,3),J(5),V()}if(e&2){let e=G();qg(`disabled`,e.busy()||!e.rpc()||!e.pageId()),A(2),N(e.label()?2:-1),A(),N(e.canLog()?3:-1),A(2),Y(e.message())}}var gM=class e{rpc=$(null);pageId=$(null);target=$.required();label=$(``);canLog=$(!1);staticReport=Q(()=>FE(this.rpc()));busy=k(!1);message=k(``);async store(e=`store`){let t=this.rpc(),n=this.pageId();if(t&&n){this.busy.set(!0),this.message.set(``);try{let r=await t.scope(`pangular`).rpc.call(`request-console-global`,{pageId:n,target:this.target(),...e===`log`?{mode:e}:{}}),i=e===`log`?r?.ok?`Logged in the page console.`:`Not logged.`:r?.ok&&r.name?`Stored as ${r.name}.`:`Not stored.`;this.message.set(r?.message||i)}catch{this.message.set(`Could not reach the page.`)}finally{this.busy.set(!1)}}}static ɵfac=function(t){return new(t||e)};static ɵcmp=Kh({type:e,selectors:[[`app-store-global`]],inputs:{rpc:[1,`rpc`],pageId:[1,`pageId`],target:[1,`target`],label:[1,`label`],canLog:[1,`canLog`]},decls:1,vars:1,consts:[[`type`,`button`,1,`store`,3,`click`,`disabled`],[1,`sr-only`],[`type`,`button`,1,`store`,`log`,3,`disabled`],[`role`,`status`,1,`status`],[`type`,`button`,1,`store`,`log`,3,`click`,`disabled`]],template:function(e,t){e&1&&M(0,hM,6,4),e&2&&N(t.staticReport()?-1:0)},styles:[`[_nghost-%COMP%] { display: inline-flex; flex-wrap: wrap; align-items: center; @@ -1985,7 +1985,7 @@ button.edit[_ngcontent-%COMP%] { .status[_ngcontent-%COMP%]:empty { display: none; -}`]})},gM=(e,t)=>t.id;function _M(e,t){if(e&1&&(B(0,`p`,4),J(1),V()),e&2){let e=G(2);A(),X(`The page has more @for blocks than the `,e.length,` listed.`)}}function vM(e,t){e&1&&(B(0,`span`,9),J(1,`@empty showing`),V())}function yM(e,t){e&1&&(B(0,`span`,10),J(1,`@empty`),V())}function bM(e,t){e&1&&(B(0,`span`,10),J(1,`renders nothing`),V())}function xM(e,t){if(e&1){let e=H();B(0,`li`)(1,`button`,6),W(`click`,function(){let t=E(e).$implicit;return D(G(3).showOwner.emit(t))})(`focus`,function(){let t=E(e).$implicit,n=G(3);return D(n.highlight.emit(n.target(t)))})(`blur`,function(){return E(e),D(G(3).highlight.emit(null))})(`mouseenter`,function(){let t=E(e).$implicit,n=G(3);return D(n.highlight.emit(n.target(t)))})(`mouseleave`,function(){return E(e),D(G(3).highlight.emit(null))}),B(2,`span`,7),J(3),V(),B(4,`span`,8),J(5),V(),M(6,vM,2,0,`span`,9)(7,yM,2,0,`span`,10)(8,bM,2,0,`span`,10),B(9,`span`,11),J(10),V()()()}if(e&2){let e=t.$implicit,n=G(3);A(),j(`aria-disabled`,e.owner?null:`true`),A(2),Y(e.owner?.name??`Unknown component`),A(2),Z(` `,e.count,` `,e.count===1?`item`:`items`,` `),A(),N(e.showingEmpty?6:e.hasEmptyBlock?7:e.count===0?8:-1),A(4),Y(n.trackLabel(e))}}function SM(e,t){if(e&1&&(B(0,`section`,1)(1,`h2`,2),J(2,` @for blocks `),B(3,`span`,3),J(4),V()(),M(5,_M,2,1,`p`,4),B(6,`ul`,5),P(7,xM,11,6,`li`,null,gM),V()()),e&2){let e=G(),t=G();A(4),Y(t.truncated()?e.length+`+`:e.length),A(),N(t.truncated()?5:-1),A(2),F(e)}}function CM(e,t){e&1&&M(0,SM,9,2,`section`,1),e&2&&N(t.length?0:-1)}function wM(e,t){e&1&&(B(0,`p`,0),J(1,` @for blocks are listed from Angular 22.0 in development builds. This page's Angular does not report them. `),V())}var TM={$index:`track $index`,item:`track item`,function:`track expression`},EM=class e{blocks=$(void 0);truncated=$(!1);supported=$(null);showOwner=Rv();highlight=Rv();target(e){return e.rootIds[0]??e.owner?.id??null}trackLabel(e){return e.track?TM[e.track]:`track unknown`}static ɵfac=function(t){return new(t||e)};static ɵcmp=Kh({type:e,selectors:[[`app-for-blocks`]],inputs:{blocks:[1,`blocks`],truncated:[1,`truncated`],supported:[1,`supported`]},outputs:{showOwner:`showOwner`,highlight:`highlight`},decls:2,vars:1,consts:[[1,`note`,`for`],[`aria-labelledby`,`ct-for-title`,1,`for`],[`id`,`ct-for-title`,1,`for-title`],[1,`pill`],[1,`note`],[1,`for-list`],[`type`,`button`,1,`for-row`,3,`click`,`focus`,`blur`,`mouseenter`,`mouseleave`],[1,`name`,`mono`],[1,`count`],[1,`flag`,`empty`],[1,`flag`],[1,`track`,`mono`]],template:function(e,t){if(e&1&&M(0,CM,1,1)(1,wM,2,0,`p`,0),e&2){let e;N((e=t.blocks())?0:t.supported()===!1?1:-1,e)}},styles:[`[_nghost-%COMP%] { +}`]})},_M=(e,t)=>t.id;function vM(e,t){if(e&1&&(B(0,`p`,4),J(1),V()),e&2){let e=G(2);A(),X(`The page has more @for blocks than the `,e.length,` listed.`)}}function yM(e,t){e&1&&(B(0,`span`,9),J(1,`@empty showing`),V())}function bM(e,t){e&1&&(B(0,`span`,10),J(1,`@empty`),V())}function xM(e,t){e&1&&(B(0,`span`,10),J(1,`renders nothing`),V())}function SM(e,t){if(e&1){let e=H();B(0,`li`)(1,`button`,6),W(`click`,function(){let t=E(e).$implicit;return D(G(3).showOwner.emit(t))})(`focus`,function(){let t=E(e).$implicit,n=G(3);return D(n.highlight.emit(n.target(t)))})(`blur`,function(){return E(e),D(G(3).highlight.emit(null))})(`mouseenter`,function(){let t=E(e).$implicit,n=G(3);return D(n.highlight.emit(n.target(t)))})(`mouseleave`,function(){return E(e),D(G(3).highlight.emit(null))}),B(2,`span`,7),J(3),V(),B(4,`span`,8),J(5),V(),M(6,yM,2,0,`span`,9)(7,bM,2,0,`span`,10)(8,xM,2,0,`span`,10),B(9,`span`,11),J(10),V()()()}if(e&2){let e=t.$implicit,n=G(3);A(),j(`aria-disabled`,e.owner?null:`true`),A(2),Y(e.owner?.name??`Unknown component`),A(2),Z(` `,e.count,` `,e.count===1?`item`:`items`,` `),A(),N(e.showingEmpty?6:e.hasEmptyBlock?7:e.count===0?8:-1),A(4),Y(n.trackLabel(e))}}function CM(e,t){if(e&1&&(B(0,`section`,1)(1,`h2`,2),J(2,` @for blocks `),B(3,`span`,3),J(4),V()(),M(5,vM,2,1,`p`,4),B(6,`ul`,5),P(7,SM,11,6,`li`,null,_M),V()()),e&2){let e=G(),t=G();A(4),Y(t.truncated()?e.length+`+`:e.length),A(),N(t.truncated()?5:-1),A(2),F(e)}}function wM(e,t){e&1&&M(0,CM,9,2,`section`,1),e&2&&N(t.length?0:-1)}function TM(e,t){e&1&&(B(0,`p`,0),J(1,` @for blocks are listed from Angular 22.0 in development builds. This page's Angular does not report them. `),V())}var EM={$index:`track $index`,item:`track item`,function:`track expression`},DM=class e{blocks=$(void 0);truncated=$(!1);supported=$(null);showOwner=Rv();highlight=Rv();target(e){return e.rootIds[0]??e.owner?.id??null}trackLabel(e){return e.track?EM[e.track]:`track unknown`}static ɵfac=function(t){return new(t||e)};static ɵcmp=Kh({type:e,selectors:[[`app-for-blocks`]],inputs:{blocks:[1,`blocks`],truncated:[1,`truncated`],supported:[1,`supported`]},outputs:{showOwner:`showOwner`,highlight:`highlight`},decls:2,vars:1,consts:[[1,`note`,`for`],[`aria-labelledby`,`ct-for-title`,1,`for`],[`id`,`ct-for-title`,1,`for-title`],[1,`pill`],[1,`note`],[1,`for-list`],[`type`,`button`,1,`for-row`,3,`click`,`focus`,`blur`,`mouseenter`,`mouseleave`],[1,`name`,`mono`],[1,`count`],[1,`flag`,`empty`],[1,`flag`],[1,`track`,`mono`]],template:function(e,t){if(e&1&&M(0,wM,1,1)(1,TM,2,0,`p`,0),e&2){let e;N((e=t.blocks())?0:t.supported()===!1?1:-1,e)}},styles:[`[_nghost-%COMP%] { display: block; } @@ -2103,7 +2103,7 @@ button.edit[_ngcontent-%COMP%] { min-width: 0; text-overflow: ellipsis; white-space: nowrap; -}`]})},DM=[`filterInput`],OM=[`propFilterInput`],kM=()=>[],AM=e=>({kind:`component`,id:e}),jM=(e,t)=>t.node.id,MM=(e,t)=>t.outlet+t.route,NM=(e,t)=>t+e,PM=(e,t)=>t.formId,FM=(e,t)=>t.name+t.index,IM=(e,t)=>t.name,LM=(e,t)=>t.token+e,RM=(e,t)=>t.id;function zM(e,t){return this.sourceKey(t)}function BM(e,t){if(e&1){let e=H();L(0,`span`,33),J(1,`Page`),R(),L(2,`app-select`,34),U(`valueChange`,function(t){return E(e),D(G(2).selectPage(t))}),R()}if(e&2){let e=G(2);A(2),I(`options`,e.pageOptions())(`value`,e.page()?.pageId??null)}}function VM(e,t){if(e&1){let e=H();L(0,`button`,35),U(`click`,function(){return E(e),D(G(2).toggleDirectiveHosts())}),O(),L(1,`svg`,13),z(2,`path`,36)(3,`path`,37),R(),J(4,` Directive hosts `),R()}e&2&&j(`aria-pressed`,G(2).showDirectiveHosts())}function HM(e,t){if(e&1){let e=H();L(0,`button`,38),U(`click`,function(){E(e);let t=G(2);return D(t.picking()?t.cancelPick():t.pick())}),O(),L(1,`svg`,13),z(2,`path`,39),R(),J(3),R()}if(e&2){let e=G(2);q(`on`,e.picking()),A(3),X(` `,e.picking()?`Cancel pick`:`Pick component on page`,` `)}}function UM(e,t){if(e&1&&(L(0,`p`,20),J(1),R()),e&2){let e=G(2);A(),Y(e.pickMessage())}}function WM(e,t){if(e&1&&(L(0,`p`,21),J(1),R()),e&2){let e=G(2);A(),Y(e.truncationText())}}function GM(e,t){e&1&&(L(0,`p`,41),J(1,`The filter only searches the components the tree lists.`),R())}function KM(e,t){e&1&&(L(0,`p`,41),J(1,`Directive hosts are hidden, so they are not searched.`),R())}function qM(e,t){if(e&1){let e=H();L(0,`div`,24)(1,`p`,40),J(2),R(),M(3,GM,2,0,`p`,41),M(4,KM,2,0,`p`,41),L(5,`button`,42),U(`click`,function(){return E(e),D(G(2).clearFilter())}),J(6,`Clear filter`),R()()}if(e&2){let e=G(2);A(2),X(`No components match “`,e.filter().trim(),`”`),A(),N(e.page().truncated?3:-1),A(),N(!e.showDirectiveHosts()&&e.page().directiveHostCount?4:-1)}}function JM(e,t){if(e&1){let e=H();L(0,`span`,50),U(`click`,function(t){E(e);let n=G().$implicit;return D(G(2).toggle(n.node.id,t))}),O(),L(1,`svg`,51),z(2,`path`,52),R()()}if(e&2){let e=G().$implicit;q(`open`,e.expanded)}}function YM(e,t){e&1&&z(0,`span`,45)}function XM(e,t){if(e&1&&(L(0,`span`,53),J(1),R(),L(2,`span`,54),J(3),R(),L(4,`span`,23),J(5,`directive host`),R()),e&2){let e=G().$implicit,t=G(2);A(),X(`<`,e.node.tag,`>`),A(),j(`title`,e.node.directives?.join(`, `)),A(),Y(t.hostLabel(e.node))}}function ZM(e,t){if(e&1&&(L(0,`span`,55),J(1),R(),L(2,`span`,56),J(3),R()),e&2){let e=G().$implicit;A(),Y(e.node.name),A(2),X(`<`,e.node.tag,`>`)}}function QM(e,t){if(e&1&&(L(0,`span`)(1,`span`,23),J(2,`Hydration: `),R(),J(3),R()),e&2){let e=t,n=G(3);M_(`hyd hyd-`+e),A(3),Y(n.hydrationText(e))}}function $M(e,t){if(e&1&&(L(0,`span`,47),J(1),R()),e&2){let e=t.$implicit;A(),Y(e.route||`/`)}}function eN(e,t){if(e&1&&(L(0,`span`,48),J(1),L(2,`span`,23),J(3),R()()),e&2){let e=t;A(),X(` `,e),A(2),X(` `,e===1?`check`:`checks`,` while recording`)}}function tN(e,t){if(e&1&&(L(0,`span`,49),J(1),L(2,`span`,23),J(3),R()()),e&2){let e=G().$implicit;j(`title`,e.node.directives.join(`, `)),A(),X(` +`,e.node.directives.length,` `),A(2),X(` `,e.node.directives.length===1?`directive`:`directives`,` `)}}function nN(e,t){if(e&1){let e=H();L(0,`div`,43),U(`click`,function(){let t=E(e).$implicit;return D(G(2).select(t.node.id))})(`focus`,function(){let t=E(e).$implicit,n=G(2);return n.focusId.set(t.node.id),D(n.highlight(t.node.id))})(`blur`,function(){return E(e),D(G(2).highlight(null))})(`mouseenter`,function(){let t=E(e).$implicit;return D(G(2).highlight(t.node.id))})(`mouseleave`,function(){return E(e),D(G(2).highlight(null))}),M(1,JM,3,2,`span`,44)(2,YM,1,0,`span`,45),M(3,XM,6,3)(4,ZM,4,2),M(5,QM,4,3,`span`,46),P(6,$M,2,1,`span`,47,MM),M(8,eN,4,2,`span`,48),M(9,tN,4,3,`span`,49),R()}if(e&2){let e,n,r=t.$implicit,i=G(2);K(`--%NS%depth`,r.depth),q(`selected`,i.selectedId()===r.node.id)(`dir-host`,r.node.kind===`directive`),j(`aria-level`,r.depth+1)(`aria-expanded`,r.hasChildren?r.expanded:null)(`aria-selected`,i.selectedId()===r.node.id)(`tabindex`,i.rovingId()===r.node.id?0:-1)(`data-id`,r.node.id),A(),N(r.hasChildren?1:2),A(2),N(r.node.kind===`directive`?3:4),A(2),N((e=r.node.hydration)?5:-1,e),A(),F(i.routedById().get(r.node.id)??mv(16,kM)),A(2),N((n=i.cdHosts()[r.node.id])?8:-1,n),A(),N(r.node.kind!==`directive`&&r.node.directives?.length?9:-1)}}function rN(e,t){if(e&1&&(L(0,`li`,64),J(1),R()),e&2){let e=t.$implicit;A(),Y(e)}}function iN(e,t){if(e&1){let e=H();L(0,`div`,66)(1,`button`,42),U(`click`,function(){let t=E(e);return D(G(3).reveal(t.id,!0))}),J(2),R()()}e&2&&(A(2),X(` Show `,t.name,` `))}function aN(e,t){if(e&1&&(L(0,`section`,27)(1,`header`,57)(2,`span`,58),J(3,`directive host`),R(),L(4,`h2`,59),J(5),R(),L(6,`span`,60),J(7),R()(),L(8,`div`,61)(9,`h3`),J(10,` Directives `),L(11,`span`,62),J(12),R()(),L(13,`ul`,63),P(14,rN,2,1,`li`,64,NM),R()(),L(16,`div`,61)(17,`p`,65),J(18,` This element hosts directives but no component. Their inputs are bound in the template of the component that renders it. `),R(),M(19,iN,3,1,`div`,66),R()()),e&2){let e,n=t,r=G(2);A(5),X(`<`,n.tag,`>`),A(2),Y(r.hostLabel(n)),A(5),Y(n.directives?.length??0),A(2),F(n.directives??mv(4,kM)),A(5),N((e=r.ownerOf(n.id))?19:-1,e)}}function oN(e,t){if(e&1&&(L(0,`p`,67),z(1,`app-source-link`,71),R()),e&2){let e=t;A(),I(`file`,e.file)(`line`,e.line)}}function sN(e,t){if(e&1){let e=H();L(0,`div`,72)(1,`button`,42),U(`click`,function(){E(e);let t=G();return D(G(2).revealInElements(t))}),J(2,`Reveal in Elements`),R(),L(3,`button`,42),U(`click`,function(){E(e);let t=G();return D(G(2).openSource(t))}),J(4,`Open source`),R()(),L(5,`p`,73),J(6),R()}if(e&2){let e=G(3);A(6),Y(e.actionMessage())}}function cN(e,t){if(e&1){let e=H();L(0,`button`,75),U(`click`,function(){let t=E(e).$implicit;return D(G(5).showForm.emit(t.formId))}),J(1),R()}if(e&2){let e=t.$implicit;A(),X(` Show `,e.label,` in Forms `)}}function lN(e,t){if(e&1&&(L(0,`div`,68),P(1,cN,2,1,`button`,74,PM),R()),e&2){let e=G(),t=G(3);A(),F(t.formsIn(e.file))}}function uN(e,t){e&1&&M(0,lN,3,0,`div`,68),e&2&&N(G(3).formsIn(t.file).length?0:-1)}function dN(e,t){if(e&1){let e=H();L(0,`div`,68)(1,`button`,42),U(`click`,function(){E(e);let t=G();return D(G(2).openInjector(t))}),J(2,` Show in Injectors `),L(3,`span`,23),J(4),R()()()}if(e&2){let e=G();A(4),X(`: `,e.name)}}function fN(e,t){if(e&1&&z(0,`app-store-global`,69),e&2){let e=G(),t=G(2);I(`rpc`,t.rpc())(`pageId`,t.page()?.pageId)(`target`,hv(4,AM,e.id))(`label`,e.name)}}function pN(e,t){if(e&1&&(L(0,`dt`),J(1,`Routed`),R(),L(2,`dd`,77),J(3),R()),e&2){let e=t.$implicit;A(3),Z(``,e.route||`/`,` in outlet `,e.outlet)}}function mN(e,t){if(e&1&&(L(0,`span`,10),J(1),R()),e&2){let e=G(4);A(),Z(``,e.shownValues()?.count??0,` of `,e.valueTotal())}}function hN(e,t){if(e&1){let e=H();L(0,`div`,81)(1,`p`,40),J(2),R(),L(3,`p`,41),J(4,` The filter searches names, and the nested keys of values you opened. `),R(),L(5,`button`,42),U(`click`,function(){return E(e),D(G(5).clearPropFilter())}),J(6,`Clear filter`),R()()}if(e&2){let e=G(5);A(2),X(` No inputs, outputs or properties match “`,e.propFilter().trim(),`” `)}}function gN(e,t){if(e&1&&(L(0,`span`,85),J(1),R()),e&2){let e=G().$implicit;A(),X(`(`,e.prop,`)`)}}function _N(e,t){if(e&1&&(L(0,`li`,83)(1,`span`,84),J(2),M(3,gN,2,1,`span`,85),R(),z(4,`app-value-tree`,86),R()),e&2){let e=t.$implicit,n=G(4),r=G(3);A(2),X(` `,e.name,` `),A(),N(e.prop===e.name?-1:3),A(),I(`prop`,e)(`store`,r.values)(`query`,r.propFilter())(`componentId`,n.id)(`pageId`,r.page()?.pageId??null)(`rpc`,r.rpc())(`writes`,r.canEdit())}}function vN(e,t){if(e&1&&(L(0,`ul`,82),P(1,_N,5,9,`li`,83,IM),R()),e&2){let e=G(2);A(),F(e.inputs)}}function yN(e,t){e&1&&(L(0,`p`,65),J(1,`No inputs.`),R())}function bN(e,t){if(e&1&&(L(0,`div`,61)(1,`h3`),J(2,` Inputs `),L(3,`span`,62),J(4),R()(),M(5,vN,3,0,`ul`,82)(6,yN,2,0,`p`,65),R()),e&2){let e=G();A(4),Y(e.inputs.length),A(),N(e.inputs.length?5:6)}}function xN(e,t){if(e&1&&(L(0,`li`,88)(1,`span`,77),J(2),R(),L(3,`span`,89),J(4),R()()),e&2){let e=t.$implicit;q(`on`,e.listened),A(2),Y(e.name),A(2),Y(e.listened?`listened`:`no listener`)}}function SN(e,t){if(e&1&&(L(0,`ul`,63),P(1,xN,5,4,`li`,87,IM),R()),e&2){let e=G(2);A(),F(e.outputs)}}function CN(e,t){e&1&&(L(0,`p`,65),J(1,`No outputs.`),R())}function wN(e,t){if(e&1&&(L(0,`div`,61)(1,`h3`),J(2,` Outputs `),L(3,`span`,62),J(4),R()(),M(5,SN,3,0,`ul`,63)(6,CN,2,0,`p`,65),R()),e&2){let e=G();A(4),Y(e.outputs.length),A(),N(e.outputs.length?5:6)}}function TN(e,t){if(e&1&&(L(0,`span`,90),J(1),R()),e&2){let e=G().$implicit;A(),Y(e.kind)}}function EN(e,t){if(e&1&&(L(0,`li`,83)(1,`span`,84),J(2),M(3,TN,2,1,`span`,90),R(),z(4,`app-value-tree`,86),R()),e&2){let e=t.$implicit,n=G(4),r=G(3);A(2),X(` `,e.name,` `),A(),N(e.kind?3:-1),A(),I(`prop`,e)(`store`,r.values)(`query`,r.propFilter())(`componentId`,n.id)(`pageId`,r.page()?.pageId??null)(`rpc`,r.rpc())(`writes`,r.canEdit())}}function DN(e,t){if(e&1&&(L(0,`ul`,82),P(1,EN,5,9,`li`,83,IM),R()),e&2){let e=G(2);A(),F(e.properties)}}function ON(e,t){e&1&&(L(0,`p`,65),J(1,`No other properties.`),R())}function kN(e,t){if(e&1&&(L(0,`div`,61)(1,`h3`),J(2,` Properties `),L(3,`span`,62),J(4),R()(),M(5,DN,3,0,`ul`,82)(6,ON,2,0,`p`,65),R()),e&2){let e=G();A(4),Y(e.properties.length),A(),N(e.properties.length?5:6)}}function AN(e,t){if(e&1&&(L(0,`li`,64),J(1),R()),e&2){let e=t.$implicit;A(),Y(e)}}function jN(e,t){if(e&1&&(L(0,`div`,61)(1,`h3`),J(2,` DOM listeners `),L(3,`span`,62),J(4),R()(),L(5,`ul`,63),P(6,AN,2,1,`li`,64,Ag),R()()),e&2){let e=G(2);A(4),Y(e.listeners.length),A(2),F(e.listeners)}}function MN(e,t){if(e&1&&(L(0,`li`,83)(1,`span`,84),J(2),R(),z(3,`app-value-tree`,91),R()),e&2){let e=t.$implicit,n=G(2).$implicit,r=G(2),i=G(3);A(2),Y(e.name),A(),I(`prop`,e)(`store`,i.values)(`query`,i.propFilter())(`directive`,n.index)(`componentId`,r.id)(`pageId`,i.page()?.pageId??null)(`rpc`,i.rpc())}}function NN(e,t){if(e&1&&(L(0,`ul`,82),P(1,MN,4,8,`li`,83,IM),R()),e&2){let e=G().$implicit;A(),F(e.inputs)}}function PN(e,t){if(e&1&&(L(0,`li`,88)(1,`span`,77),J(2),R(),L(3,`span`,89),J(4),R()()),e&2){let e=t.$implicit;q(`on`,e.listened),A(2),Y(e.name),A(2),Y(e.listened?`listened`:`no listener`)}}function FN(e,t){if(e&1&&(L(0,`ul`,63),P(1,PN,5,4,`li`,87,IM),R()),e&2){let e=G().$implicit;A(),F(e.outputs)}}function IN(e,t){e&1&&(L(0,`p`,65),J(1,`No inputs or outputs.`),R())}function LN(e,t){if(e&1&&(L(0,`div`,61)(1,`h3`)(2,`span`,77),J(3),R(),L(4,`span`,90),J(5,`directive on host`),R()(),M(6,NN,3,0,`ul`,82),M(7,FN,3,0,`ul`,63),M(8,IN,2,0,`p`,65),R()),e&2){let e=t.$implicit;A(3),Y(e.name),A(3),N(e.inputs.length?6:-1),A(),N(e.outputs.length?7:-1),A(),N(!e.inputs.length&&!e.outputs.length?8:-1)}}function RN(e,t){if(e&1&&(M(0,hN,7,1,`div`,81),M(1,bN,7,2,`div`,61),M(2,wN,7,2,`div`,61),M(3,kN,7,2,`div`,61),M(4,jN,8,1,`div`,61),P(5,LN,9,4,`div`,61,FM)),e&2){let e=t,n=G(),r=G(3);N(r.propQuery()&&!e.count?0:-1),A(),N(!r.propQuery()||e.inputs.length?1:-1),A(),N(!r.propQuery()||e.outputs.length?2:-1),A(),N(!r.propQuery()||e.properties.length?3:-1),A(),N(!r.propQuery()&&n.listeners.length?4:-1),A(),F(e.directives)}}function zN(e,t){if(e&1&&(L(0,`span`,90),J(1),R()),e&2){let e=t.$implicit;A(),Y(e)}}function BN(e,t){if(e&1&&(L(0,`span`,94),J(1,`from `),L(2,`span`,77),J(3),R()()),e&2){let e=G().$implicit;A(3),Y(e.providedByName??`an injector`)}}function VN(e,t){e&1&&(L(0,`span`,95),J(1,`not provided`),R())}function HN(e,t){if(e&1&&(L(0,`li`,92)(1,`span`,93),J(2),R(),P(3,zN,2,1,`span`,90,Ag),M(5,BN,4,1,`span`,94)(6,VN,2,0,`span`,95),R()),e&2){let e=t.$implicit;A(2),Y(e.token),A(),F(e.flags),A(2),N(e.providedBy?5:6)}}function UN(e,t){if(e&1&&(L(0,`ul`,80),P(1,HN,7,2,`li`,92,LM),R()),e&2){let e=G();A(),F(e.dependencies)}}function WN(e,t){e&1&&(L(0,`p`,65),J(1,`Nothing is injected through the constructor or inject().`),R())}function GN(e,t){if(e&1){let e=H();L(0,`dl`,76)(1,`dt`),J(2,`Change detection`),R(),L(3,`dd`),J(4),R(),L(5,`dt`),J(6,`Encapsulation`),R(),L(7,`dd`),J(8),R(),L(9,`dt`),J(10,`Host path`),R(),L(11,`dd`,77),J(12),R(),P(13,pN,4,2,null,null,MM),R(),L(15,`div`,78)(16,`div`,4),O(),L(17,`svg`,5),z(18,`circle`,6)(19,`path`,7),R(),gs(),L(20,`input`,79,1),U(`input`,function(t){return E(e),D(G(3).propFilter.set(t.target.value))})(`keydown.escape`,function(t){return E(e),D(G(3).clearPropFilter(t))}),R()(),M(22,mN,2,2,`span`,10),L(23,`p`,19),J(24),R()(),M(25,RN,7,5),L(26,`div`,61)(27,`h3`),J(28,` Injected `),L(29,`span`,62),J(30),R()(),M(31,UN,3,0,`ul`,80)(32,WN,2,0,`p`,65),R()}if(e&2){let e,n=t,r=G(),i=G(2);A(4),Y(n.changeDetection??`Unknown`),A(4),Y(n.encapsulation??`Unknown`),A(4),Y(n.path),A(),F(i.routedById().get(r.id)??mv(9,kM)),A(7),I(`value`,i.propFilter()),A(2),N(i.propQuery()?22:-1),A(2),Y(i.propFilterStatus()),A(),N((e=i.shownValues())?25:-1,e),A(5),Y(n.dependencies.length),A(),N(n.dependencies.length?31:32)}}function KN(e,t){e&1&&(L(0,`div`,70),z(1,`span`,96),L(2,`p`,41),J(3,`Reading live values from the page…`),R()())}function qN(e,t){if(e&1&&(L(0,`section`,27)(1,`header`,57)(2,`span`,58),J(3,`component`),R(),L(4,`h2`,59),J(5),R(),L(6,`span`,60),J(7),R(),M(8,oN,2,2,`p`,67),M(9,sN,7,1),M(10,uN,1,1),M(11,dN,5,1,`div`,68),M(12,fN,1,6,`app-store-global`,69),R(),M(13,GN,33,10)(14,KN,4,0,`div`,70),R()),e&2){let e,n,r,i=t,a=G(2);A(5),Y(i.name),A(2),X(`<`,i.tag,`>`),A(),N((e=a.whereOf(i))?8:-1,e),A(),N(a.devtoolsActions()?9:-1),A(),N((n=a.sourceFor(i))?10:-1,n),A(),N(a.canShowInjector()&&!a.staticReport()?11:-1),A(),N(!a.staticReport()&&!a.native()?12:-1),A(),N((r=a.detail())?13:14,r)}}function JN(e,t){e&1&&(L(0,`p`,40),J(1,`The selected component was destroyed`),R(),L(2,`p`,41)(3,`span`,77),J(4),R(),J(5,` is no longer on the page. Select another component to see its live values. `),R()),e&2&&(A(4),Y(t))}function YN(e,t){e&1&&(L(0,`p`,41),J(1,` Select a component to see its live inputs, properties, outputs, change detection and injected services. `),R())}function XN(e,t){if(e&1&&(L(0,`div`,28),M(1,JN,6,1)(2,YN,2,0,`p`,41),R()),e&2){let e,t=G(2);A(),N((e=t.destroyed())?1:2,e)}}function ZN(e,t){e&1&&(L(0,`span`,90),J(1,`hydrate never`),R())}function QN(e,t){if(e&1&&(L(0,`span`,90),J(1),R()),e&2){let e=G().$implicit;A(),Y(e.hydration)}}function $N(e,t){if(e&1){let e=H();L(0,`li`)(1,`button`,99),U(`click`,function(){let t=E(e).$implicit;return D(G(3).showOwner(t))})(`focus`,function(){let t=E(e).$implicit;return D(G(3).highlightBlock(t))})(`blur`,function(){return E(e),D(G(3).highlight(null))})(`mouseenter`,function(){let t=E(e).$implicit;return D(G(3).highlightBlock(t))})(`mouseleave`,function(){return E(e),D(G(3).highlight(null))}),L(2,`span`,55),J(3),R(),L(4,`span`,100),J(5),R(),M(6,ZN,2,0,`span`,90)(7,QN,2,1,`span`,90),L(8,`span`,101),J(9),R()()()}if(e&2){let e=t.$implicit;A(),j(`aria-disabled`,e.owner?null:`true`),A(2),Y(e.owner?.name??`Unknown component`),A(),M_(`defer-state-`+e.state),A(),Y(e.state),A(),N(e.hydrateNever?6:e.hydration===`not-configured`?-1:7),A(3),Y(e.triggers.join(`, `)||`no triggers`)}}function eP(e,t){if(e&1&&(L(0,`section`,29)(1,`h2`,97),J(2,` Defer blocks `),L(3,`span`,62),J(4),R()(),L(5,`ul`,98),P(6,$N,10,7,`li`,null,RM),R()()),e&2){let e=G(2);A(4),Y(e.deferCount()),A(2),F(e.deferBlocks())}}function tP(e,t){if(e&1){let e=H();L(0,`app-hydration-section`,102),U(`overlay`,function(t){return E(e),D(G(2).setHydrationOverlay(t))})(`showNode`,function(t){return E(e),D(G(2).revealNode(t))})(`highlight`,function(t){return E(e),D(G(2).highlight(t))}),R()}e&2&&I(`report`,G(2).page()?.hydration)}function nP(e,t){if(e&1){let e=H();L(0,`app-cd-recording`,103),U(`showComponent`,function(t){return E(e),D(G(2).showCdComponent(t))}),R()}if(e&2){let e=G(2);I(`page`,e.cdPage())(`pageId`,e.page().pageId)(`rpc`,e.rpc())}}function rP(e,t){if(e&1){let e=H();L(0,`p`,2),J(1,` Every component instance on the page, in the order Angular rendered them. Hover a row to highlight its host in the page, and select it to read its live inputs, properties, outputs and injected services. `),R(),L(2,`div`,3)(3,`div`,4),O(),L(4,`svg`,5),z(5,`circle`,6)(6,`path`,7),R(),gs(),L(7,`input`,8,0),U(`input`,function(t){return E(e),D(G().setFilter(t.target.value))})(`keydown.escape`,function(){return E(e),D(G().setFilter(``))}),R(),L(9,`span`,9),J(10,` Type plain text, a tag such as