angular-scan 0.0.4 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { signal, Injectable, InjectionToken, inject, DOCUMENT, PLATFORM_ID, isDevMode, NgZone, ChangeDetectionStrategy, Component, makeEnvironmentProviders, provideEnvironmentInitializer, EnvironmentInjector, ApplicationRef, createComponent } from '@angular/core';
2
+ import { signal, Injectable, InjectionToken, inject, DOCUMENT, PLATFORM_ID, isDevMode, Injector, runInInjectionContext, afterEveryRender, ChangeDetectionStrategy, Component, makeEnvironmentProviders, provideEnvironmentInitializer, EnvironmentInjector, ApplicationRef, createComponent } from '@angular/core';
3
3
  import { isPlatformBrowser, DOCUMENT as DOCUMENT$1 } from '@angular/common';
4
4
 
5
5
  class ComponentTracker {
@@ -174,10 +174,25 @@ class CanvasOverlay {
174
174
 
175
175
  const ANGULAR_SCAN_OPTIONS = new InjectionToken('ANGULAR_SCAN_OPTIONS', { providedIn: 'root', factory: () => ({}) });
176
176
 
177
+ class ScanConfigService {
178
+ options = inject(ANGULAR_SCAN_OPTIONS);
179
+ enabled = signal(this.options.enabled !== false, ...(ngDevMode ? [{ debugName: "enabled" }] : []));
180
+ showOverlay = signal(true, ...(ngDevMode ? [{ debugName: "showOverlay" }] : []));
181
+ showBadges = signal(this.options.showBadges !== false, ...(ngDevMode ? [{ debugName: "showBadges" }] : []));
182
+ flashDurationMs = signal(this.options.flashDurationMs ?? 500, ...(ngDevMode ? [{ debugName: "flashDurationMs" }] : []));
183
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: ScanConfigService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
184
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: ScanConfigService, providedIn: 'root' });
185
+ }
186
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: ScanConfigService, decorators: [{
187
+ type: Injectable,
188
+ args: [{ providedIn: 'root' }]
189
+ }] });
190
+
177
191
  class OverlayService {
178
192
  document = inject(DOCUMENT);
179
193
  platformId = inject(PLATFORM_ID);
180
194
  options = inject(ANGULAR_SCAN_OPTIONS);
195
+ config = inject(ScanConfigService);
181
196
  canvas = new CanvasOverlay();
182
197
  // Badge elements keyed by host element
183
198
  badges = new Map();
@@ -197,11 +212,15 @@ class OverlayService {
197
212
  this.badges.clear();
198
213
  }
199
214
  onComponentChecked(stats) {
200
- const durationMs = this.options.flashDurationMs ?? 500;
201
- this.canvas.flash(stats.hostElement, stats.lastRenderKind, durationMs);
202
- if (this.options.showBadges !== false) {
215
+ if (this.config.showOverlay()) {
216
+ this.canvas.flash(stats.hostElement, stats.lastRenderKind, this.config.flashDurationMs());
217
+ }
218
+ if (this.config.showBadges()) {
203
219
  this.updateBadge(stats);
204
220
  }
221
+ else {
222
+ this.removeBadge(stats.hostElement);
223
+ }
205
224
  }
206
225
  removeBadge(el) {
207
226
  this.badges.get(el)?.remove();
@@ -284,20 +303,22 @@ class ScannerService {
284
303
  overlay = inject(OverlayService);
285
304
  options = inject(ANGULAR_SCAN_OPTIONS);
286
305
  platformId = inject(PLATFORM_ID);
287
- ngZone = inject(NgZone);
306
+ injector = inject(Injector);
307
+ config = inject(ScanConfigService);
288
308
  removeProfiler = null;
309
+ afterRenderRef = null;
289
310
  mutationObserver = null;
290
311
  // Accumulated during a single CD tick (raw DOM nodes, no signal writes)
291
312
  mutatedNodes = new Set();
292
313
  // Component instances visited during the sync (template update) phase
293
314
  tickInstances = new Set();
315
+ // Updates waiting to be flushed in afterEveryRender
316
+ pendingFlush = [];
294
317
  // Tracks which phase of CD we're in
295
318
  inSyncPhase = false;
296
319
  inTick = false;
297
320
  // The toolbar component instance — excluded from tracking to avoid self-loops
298
321
  toolbarInstance = null;
299
- // Throttle toolbar signal updates to at most once per animation frame
300
- toolbarRafPending = false;
301
322
  initialize() {
302
323
  if (!isDevMode())
303
324
  return;
@@ -311,6 +332,10 @@ class ScannerService {
311
332
  'Ensure you are running in development mode.');
312
333
  return;
313
334
  }
335
+ // afterEveryRender is Angular's safe hook for post-render writes.
336
+ // Signal writes here cause exactly one extra toolbar render pass then stabilize,
337
+ // avoiding the infinite loop that queueMicrotask causes in zone.js + signals hybrid apps.
338
+ this.afterRenderRef = runInInjectionContext(this.injector, () => afterEveryRender({ write: () => this.flushPendingUpdates() }));
314
339
  this.mutationObserver = new MutationObserver(records => {
315
340
  for (const r of records) {
316
341
  this.mutatedNodes.add(r.target);
@@ -331,7 +356,7 @@ class ScannerService {
331
356
  break;
332
357
  case TemplateUpdateStart$1:
333
358
  // Only record during the real update phase, not the dev-mode checkNoChanges pass
334
- if (this.inSyncPhase && instance && instance !== this.toolbarInstance) {
359
+ if (this.config.enabled() && this.inSyncPhase && instance && instance !== this.toolbarInstance) {
335
360
  this.tickInstances.add(instance);
336
361
  }
337
362
  break;
@@ -389,28 +414,23 @@ class ScannerService {
389
414
  // Component may have been destroyed during the tick
390
415
  }
391
416
  }
392
- // Run signal writes OUTSIDE Angular's zone so zone.js does not schedule a
393
- // new CD cycle from them — which would cause an infinite loop in NgModule apps
394
- // where queueMicrotask is patched by zone.js.
395
- this.ngZone.runOutsideAngular(() => {
396
- queueMicrotask(() => {
397
- for (const update of pendingUpdates) {
398
- const stats = this.tracker.recordRender(update.instance, update.hostElement, update.kind);
399
- this.overlay.onComponentChecked(stats);
400
- }
401
- // Throttle the toolbar signal snapshot to at most once per animation frame.
402
- // Re-enter the zone so the toolbar (OnPush + signals) gets a CD cycle to repaint.
403
- if (!this.toolbarRafPending) {
404
- this.toolbarRafPending = true;
405
- requestAnimationFrame(() => {
406
- this.ngZone.run(() => {
407
- this.toolbarRafPending = false;
408
- this.tracker.snapshotTrackedComponents();
409
- });
410
- });
411
- }
412
- });
413
- });
417
+ // Queue for afterEveryRender to flush safely no direct signal writes here.
418
+ this.pendingFlush.push(...pendingUpdates);
419
+ }
420
+ /**
421
+ * Called by afterEveryRender. Drains pendingFlush into signals safely.
422
+ * If there are no pending updates, returns immediately so Angular stabilizes.
423
+ * If there are updates, signals are written → toolbar re-renders (one extra pass) → stabilizes.
424
+ */
425
+ flushPendingUpdates() {
426
+ const updates = this.pendingFlush.splice(0);
427
+ if (updates.length === 0)
428
+ return;
429
+ for (const update of updates) {
430
+ const stats = this.tracker.recordRender(update.instance, update.hostElement, update.kind);
431
+ this.overlay.onComponentChecked(stats);
432
+ }
433
+ this.tracker.snapshotTrackedComponents();
414
434
  }
415
435
  /**
416
436
  * Build a Set of host Elements by walking each mutated node up the DOM tree
@@ -442,8 +462,11 @@ class ScannerService {
442
462
  ngOnDestroy() {
443
463
  this.removeProfiler?.();
444
464
  this.removeProfiler = null;
465
+ this.afterRenderRef?.destroy();
466
+ this.afterRenderRef = null;
445
467
  this.mutationObserver?.disconnect();
446
468
  this.mutationObserver = null;
469
+ this.pendingFlush = [];
447
470
  this.overlay.destroy();
448
471
  }
449
472
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: ScannerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
@@ -456,20 +479,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
456
479
 
457
480
  class ToolbarComponent {
458
481
  tracker = inject(ComponentTracker);
482
+ config = inject(ScanConfigService);
459
483
  expanded = signal(false, ...(ngDevMode ? [{ debugName: "expanded" }] : []));
460
- enabled = signal(true, ...(ngDevMode ? [{ debugName: "enabled" }] : []));
484
+ settingsOpen = signal(false, ...(ngDevMode ? [{ debugName: "settingsOpen" }] : []));
461
485
  toggleExpanded() {
462
486
  this.expanded.update(v => !v);
487
+ if (this.expanded())
488
+ this.settingsOpen.set(false);
489
+ }
490
+ toggleSettings() {
491
+ this.settingsOpen.update(v => !v);
492
+ if (this.settingsOpen())
493
+ this.expanded.set(false);
494
+ }
495
+ resetStats() {
496
+ this.tracker.reset();
463
497
  }
464
- toggleEnabled() {
465
- this.enabled.update(v => !v);
498
+ onFlashDurationChange(event) {
499
+ const value = Number(event.target.value);
500
+ this.config.flashDurationMs.set(value);
466
501
  }
467
502
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: ToolbarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
468
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: ToolbarComponent, isStandalone: true, selector: "angular-scan-toolbar", host: { properties: { "style.display": "\"block\"" } }, ngImport: i0, template: "<div\nclass=\"toolbar\"\nrole=\"complementary\"\naria-label=\"Angular Scan DevTools\"\n>\n<div class=\"toolbar__header\">\n <span class=\"toolbar__logo\" aria-hidden=\"true\">\u25C9</span>\n <span class=\"toolbar__title\">angular-scan</span>\n\n <button\n class=\"toolbar__btn\"\n type=\"button\"\n [attr.aria-pressed]=\"enabled()\"\n [title]=\"enabled() ? 'Pause scanning' : 'Resume scanning'\"\n (click)=\"toggleEnabled()\"\n >{{ enabled() ? '\u23F8' : '\u25B6' }}</button>\n\n <button\n class=\"toolbar__btn\"\n type=\"button\"\n [attr.aria-pressed]=\"expanded()\"\n [title]=\"expanded() ? 'Collapse inspector' : 'Expand inspector'\"\n (click)=\"toggleExpanded()\"\n >{{ expanded() ? '\u25B2' : '\u25BC' }}</button>\n</div>\n\n<div class=\"toolbar__stats\">\n <span class=\"toolbar__stat\" title=\"Total change detection checks\">\n <span class=\"toolbar__stat-label\">checks</span>\n <strong class=\"toolbar__stat-value\">{{ tracker.totalRenders() }}</strong>\n </span>\n <span\n class=\"toolbar__stat\"\n [class.toolbar__stat--warn]=\"tracker.totalUnnecessary() > 0\"\n title=\"Unnecessary renders: component was checked but DOM did not change\"\n >\n <span class=\"toolbar__stat-label\">wasted</span>\n <strong class=\"toolbar__stat-value\">{{ tracker.totalUnnecessary() }}</strong>\n </span>\n</div>\n\n@if (expanded()) {\n <div\n class=\"toolbar__inspector\"\n role=\"list\"\n aria-label=\"Component render counts\"\n >\n @for (comp of tracker.trackedComponents(); track comp.hostElement) {\n <div\n class=\"toolbar__component\"\n role=\"listitem\"\n [class.toolbar__component--warn]=\"comp.lastRenderKind === 'unnecessary'\"\n [title]=\"comp.componentName + ' \u2014 ' + comp.totalRenders + ' checks, ' + comp.unnecessaryRenders + ' wasted'\"\n >\n <span class=\"toolbar__component-name\">{{ comp.componentName }}</span>\n <span class=\"toolbar__component-count\">{{ comp.totalRenders }}</span>\n @if (comp.unnecessaryRenders > 0) {\n <span class=\"toolbar__component-wasted\" aria-label=\"wasted renders\">\n {{ comp.unnecessaryRenders }}W\n </span>\n }\n </div>\n }\n </div>\n}\n</div>", styles: [":host{position:fixed;bottom:0;right:0;z-index:2147483647;font-family:ui-monospace,Cascadia Code,Source Code Pro,Menlo,Consolas,monospace;font-size:11px;line-height:1.4}.toolbar{background:#0f1117;color:#c9d1d9;border:1px solid #30363d;border-bottom:none;border-right:none;border-radius:6px 0 0;min-width:200px;max-width:300px;box-shadow:-2px -2px 12px #00000080}.toolbar__header{display:flex;align-items:center;gap:4px;padding:5px 8px;border-bottom:1px solid #21262d}.toolbar__logo{color:#58a6ff;font-size:12px}.toolbar__title{font-weight:600;color:#58a6ff;flex:1;letter-spacing:.02em}.toolbar__btn{background:transparent;border:1px solid #30363d;color:#8b949e;border-radius:4px;cursor:pointer;padding:2px 6px;font-size:10px;transition:color .15s,border-color .15s}.toolbar__btn:hover{color:#c9d1d9;border-color:#58a6ff}.toolbar__btn:focus-visible{outline:2px solid #58a6ff;outline-offset:2px}.toolbar__stats{display:flex;gap:12px;padding:5px 8px}.toolbar__stat{display:flex;flex-direction:column;gap:1px}.toolbar__stat-label{color:#8b949e;font-size:9px;text-transform:uppercase;letter-spacing:.05em}.toolbar__stat-value{color:#c9d1d9}.toolbar__stat--warn .toolbar__stat-value{color:#f85149}.toolbar__inspector{max-height:220px;overflow-y:auto;border-top:1px solid #21262d}.toolbar__inspector::-webkit-scrollbar{width:4px}.toolbar__inspector::-webkit-scrollbar-track{background:transparent}.toolbar__inspector::-webkit-scrollbar-thumb{background:#30363d;border-radius:2px}.toolbar__component{display:flex;align-items:center;gap:6px;padding:3px 8px;border-bottom:1px solid #161b22;transition:background .1s}.toolbar__component:hover{background:#161b22}.toolbar__component--warn{background:#f851490f}.toolbar__component-name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#8b949e}.toolbar__component-count{font-weight:600;color:#ffa657;min-width:20px;text-align:right}.toolbar__component-wasted{color:#f85149;font-size:9px;font-weight:600}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
503
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: ToolbarComponent, isStandalone: true, selector: "angular-scan-toolbar", host: { properties: { "style.display": "\"block\"" } }, ngImport: i0, template: "<div\n class=\"toolbar\"\n role=\"complementary\"\n aria-label=\"Angular Scan DevTools\"\n>\n <div class=\"toolbar__header\">\n <span class=\"toolbar__logo\" aria-hidden=\"true\">\u25C9</span>\n <span class=\"toolbar__title\">angular-scan</span>\n\n <button\n class=\"toolbar__btn\"\n type=\"button\"\n [attr.aria-pressed]=\"!config.enabled()\"\n [title]=\"config.enabled() ? 'Pause scanning' : 'Resume scanning'\"\n (click)=\"config.enabled.update(v => !v)\"\n >{{ config.enabled() ? '\u23F8' : '\u25B6' }}</button>\n\n <button\n class=\"toolbar__btn\"\n type=\"button\"\n [attr.aria-pressed]=\"settingsOpen()\"\n title=\"Settings\"\n (click)=\"toggleSettings()\"\n >\u2699</button>\n\n <button\n class=\"toolbar__btn\"\n type=\"button\"\n [attr.aria-pressed]=\"expanded()\"\n [title]=\"expanded() ? 'Collapse inspector' : 'Expand inspector'\"\n (click)=\"toggleExpanded()\"\n >{{ expanded() ? '\u25B2' : '\u25BC' }}</button>\n </div>\n\n <div class=\"toolbar__stats\">\n <span class=\"toolbar__stat\" title=\"Total change detection checks\">\n <span class=\"toolbar__stat-label\">checks</span>\n <strong class=\"toolbar__stat-value\">{{ tracker.totalRenders() }}</strong>\n </span>\n <span\n class=\"toolbar__stat\"\n [class.toolbar__stat--warn]=\"tracker.totalUnnecessary() > 0\"\n title=\"Unnecessary renders: component was checked but DOM did not change\"\n >\n <span class=\"toolbar__stat-label\">wasted</span>\n <strong class=\"toolbar__stat-value\">{{ tracker.totalUnnecessary() }}</strong>\n </span>\n </div>\n\n @if (settingsOpen()) {\n <div class=\"toolbar__settings\" role=\"group\" aria-label=\"Scan settings\">\n\n <label class=\"toolbar__setting\">\n <span class=\"toolbar__setting-label\">Enable scanning</span>\n <button\n class=\"toolbar__toggle\"\n type=\"button\"\n role=\"switch\"\n [attr.aria-checked]=\"config.enabled()\"\n (click)=\"config.enabled.update(v => !v)\"\n [class.toolbar__toggle--on]=\"config.enabled()\"\n >{{ config.enabled() ? 'ON' : 'OFF' }}</button>\n </label>\n\n <label class=\"toolbar__setting\">\n <span class=\"toolbar__setting-label\">Flash overlay</span>\n <button\n class=\"toolbar__toggle\"\n type=\"button\"\n role=\"switch\"\n [attr.aria-checked]=\"config.showOverlay()\"\n (click)=\"config.showOverlay.update(v => !v)\"\n [class.toolbar__toggle--on]=\"config.showOverlay()\"\n >{{ config.showOverlay() ? 'ON' : 'OFF' }}</button>\n </label>\n\n <label class=\"toolbar__setting\">\n <span class=\"toolbar__setting-label\">Render badges</span>\n <button\n class=\"toolbar__toggle\"\n type=\"button\"\n role=\"switch\"\n [attr.aria-checked]=\"config.showBadges()\"\n (click)=\"config.showBadges.update(v => !v)\"\n [class.toolbar__toggle--on]=\"config.showBadges()\"\n >{{ config.showBadges() ? 'ON' : 'OFF' }}</button>\n </label>\n\n <label class=\"toolbar__setting toolbar__setting--column\">\n <span class=\"toolbar__setting-label\">\n Flash duration\n <span class=\"toolbar__setting-value\">{{ config.flashDurationMs() }}ms</span>\n </span>\n <input\n class=\"toolbar__slider\"\n type=\"range\"\n min=\"100\"\n max=\"2000\"\n step=\"100\"\n [value]=\"config.flashDurationMs()\"\n (input)=\"onFlashDurationChange($event)\"\n aria-label=\"Flash duration in milliseconds\"\n />\n </label>\n\n <button\n class=\"toolbar__reset\"\n type=\"button\"\n title=\"Clear all render stats\"\n (click)=\"resetStats()\"\n >\u21BA Reset stats</button>\n\n </div>\n }\n\n @if (expanded()) {\n <div\n class=\"toolbar__inspector\"\n role=\"list\"\n aria-label=\"Component render counts\"\n >\n @for (comp of tracker.trackedComponents(); track comp.hostElement) {\n <div\n class=\"toolbar__component\"\n role=\"listitem\"\n [class.toolbar__component--warn]=\"comp.lastRenderKind === 'unnecessary'\"\n [title]=\"comp.componentName + ' \u2014 ' + comp.totalRenders + ' checks, ' + comp.unnecessaryRenders + ' wasted'\"\n >\n <span class=\"toolbar__component-name\">{{ comp.componentName }}</span>\n <span class=\"toolbar__component-count\">{{ comp.totalRenders }}</span>\n @if (comp.unnecessaryRenders > 0) {\n <span class=\"toolbar__component-wasted\" aria-label=\"wasted renders\">\n {{ comp.unnecessaryRenders }}W\n </span>\n }\n </div>\n }\n </div>\n }\n</div>\n", styles: [":host{position:fixed;bottom:0;right:0;z-index:2147483647;font-family:ui-monospace,Cascadia Code,Source Code Pro,Menlo,Consolas,monospace;font-size:11px;line-height:1.4}.toolbar{background:#0f1117;color:#c9d1d9;border:1px solid #30363d;border-bottom:none;border-right:none;border-radius:6px 0 0;min-width:200px;max-width:300px;box-shadow:-2px -2px 12px #00000080}.toolbar__header{display:flex;align-items:center;gap:4px;padding:5px 8px;border-bottom:1px solid #21262d}.toolbar__logo{color:#58a6ff;font-size:12px}.toolbar__title{font-weight:600;color:#58a6ff;flex:1;letter-spacing:.02em}.toolbar__btn{background:transparent;border:1px solid #30363d;color:#8b949e;border-radius:4px;cursor:pointer;padding:2px 6px;font-size:10px;transition:color .15s,border-color .15s}.toolbar__btn:hover{color:#c9d1d9;border-color:#58a6ff}.toolbar__btn:focus-visible{outline:2px solid #58a6ff;outline-offset:2px}.toolbar__stats{display:flex;gap:12px;padding:5px 8px}.toolbar__stat{display:flex;flex-direction:column;gap:1px}.toolbar__stat-label{color:#8b949e;font-size:9px;text-transform:uppercase;letter-spacing:.05em}.toolbar__stat-value{color:#c9d1d9}.toolbar__stat--warn .toolbar__stat-value{color:#f85149}.toolbar__inspector{max-height:220px;overflow-y:auto;border-top:1px solid #21262d}.toolbar__inspector::-webkit-scrollbar{width:4px}.toolbar__inspector::-webkit-scrollbar-track{background:transparent}.toolbar__inspector::-webkit-scrollbar-thumb{background:#30363d;border-radius:2px}.toolbar__component{display:flex;align-items:center;gap:6px;padding:3px 8px;border-bottom:1px solid #161b22;transition:background .1s}.toolbar__component:hover{background:#161b22}.toolbar__component--warn{background:#f851490f}.toolbar__component-name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#8b949e}.toolbar__component-count{font-weight:600;color:#ffa657;min-width:20px;text-align:right}.toolbar__component-wasted{color:#f85149;font-size:9px;font-weight:600}.toolbar__settings{display:flex;flex-direction:column;gap:6px;padding:8px;border-top:1px solid #21262d}.toolbar__setting{display:flex;align-items:center;justify-content:space-between;gap:8px;cursor:default}.toolbar__setting--column{flex-direction:column;align-items:stretch;gap:4px}.toolbar__setting-label{color:#8b949e;font-size:10px;display:flex;align-items:center;justify-content:space-between;gap:4px}.toolbar__setting-value{color:#58a6ff;font-weight:600}.toolbar__toggle{background:#21262d;border:1px solid #30363d;color:#8b949e;border-radius:3px;cursor:pointer;padding:1px 6px;font-size:9px;font-weight:600;font-family:inherit;letter-spacing:.05em;min-width:32px;transition:background .15s,color .15s,border-color .15s}.toolbar__toggle--on{background:#58a6ff26;border-color:#58a6ff;color:#58a6ff}.toolbar__toggle:focus-visible{outline:2px solid #58a6ff;outline-offset:2px}.toolbar__slider{width:100%;accent-color:#58a6ff;cursor:pointer;height:4px}.toolbar__reset{background:transparent;border:1px solid #30363d;color:#8b949e;border-radius:4px;cursor:pointer;padding:3px 8px;font-size:10px;font-family:inherit;margin-top:2px;transition:color .15s,border-color .15s;align-self:flex-start}.toolbar__reset:hover{color:#f85149;border-color:#f85149}.toolbar__reset:focus-visible{outline:2px solid #58a6ff;outline-offset:2px}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
469
504
  }
470
505
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: ToolbarComponent, decorators: [{
471
506
  type: Component,
472
- args: [{ selector: 'angular-scan-toolbar', changeDetection: ChangeDetectionStrategy.OnPush, host: { '[style.display]': '"block"' }, template: "<div\nclass=\"toolbar\"\nrole=\"complementary\"\naria-label=\"Angular Scan DevTools\"\n>\n<div class=\"toolbar__header\">\n <span class=\"toolbar__logo\" aria-hidden=\"true\">\u25C9</span>\n <span class=\"toolbar__title\">angular-scan</span>\n\n <button\n class=\"toolbar__btn\"\n type=\"button\"\n [attr.aria-pressed]=\"enabled()\"\n [title]=\"enabled() ? 'Pause scanning' : 'Resume scanning'\"\n (click)=\"toggleEnabled()\"\n >{{ enabled() ? '\u23F8' : '\u25B6' }}</button>\n\n <button\n class=\"toolbar__btn\"\n type=\"button\"\n [attr.aria-pressed]=\"expanded()\"\n [title]=\"expanded() ? 'Collapse inspector' : 'Expand inspector'\"\n (click)=\"toggleExpanded()\"\n >{{ expanded() ? '\u25B2' : '\u25BC' }}</button>\n</div>\n\n<div class=\"toolbar__stats\">\n <span class=\"toolbar__stat\" title=\"Total change detection checks\">\n <span class=\"toolbar__stat-label\">checks</span>\n <strong class=\"toolbar__stat-value\">{{ tracker.totalRenders() }}</strong>\n </span>\n <span\n class=\"toolbar__stat\"\n [class.toolbar__stat--warn]=\"tracker.totalUnnecessary() > 0\"\n title=\"Unnecessary renders: component was checked but DOM did not change\"\n >\n <span class=\"toolbar__stat-label\">wasted</span>\n <strong class=\"toolbar__stat-value\">{{ tracker.totalUnnecessary() }}</strong>\n </span>\n</div>\n\n@if (expanded()) {\n <div\n class=\"toolbar__inspector\"\n role=\"list\"\n aria-label=\"Component render counts\"\n >\n @for (comp of tracker.trackedComponents(); track comp.hostElement) {\n <div\n class=\"toolbar__component\"\n role=\"listitem\"\n [class.toolbar__component--warn]=\"comp.lastRenderKind === 'unnecessary'\"\n [title]=\"comp.componentName + ' \u2014 ' + comp.totalRenders + ' checks, ' + comp.unnecessaryRenders + ' wasted'\"\n >\n <span class=\"toolbar__component-name\">{{ comp.componentName }}</span>\n <span class=\"toolbar__component-count\">{{ comp.totalRenders }}</span>\n @if (comp.unnecessaryRenders > 0) {\n <span class=\"toolbar__component-wasted\" aria-label=\"wasted renders\">\n {{ comp.unnecessaryRenders }}W\n </span>\n }\n </div>\n }\n </div>\n}\n</div>", styles: [":host{position:fixed;bottom:0;right:0;z-index:2147483647;font-family:ui-monospace,Cascadia Code,Source Code Pro,Menlo,Consolas,monospace;font-size:11px;line-height:1.4}.toolbar{background:#0f1117;color:#c9d1d9;border:1px solid #30363d;border-bottom:none;border-right:none;border-radius:6px 0 0;min-width:200px;max-width:300px;box-shadow:-2px -2px 12px #00000080}.toolbar__header{display:flex;align-items:center;gap:4px;padding:5px 8px;border-bottom:1px solid #21262d}.toolbar__logo{color:#58a6ff;font-size:12px}.toolbar__title{font-weight:600;color:#58a6ff;flex:1;letter-spacing:.02em}.toolbar__btn{background:transparent;border:1px solid #30363d;color:#8b949e;border-radius:4px;cursor:pointer;padding:2px 6px;font-size:10px;transition:color .15s,border-color .15s}.toolbar__btn:hover{color:#c9d1d9;border-color:#58a6ff}.toolbar__btn:focus-visible{outline:2px solid #58a6ff;outline-offset:2px}.toolbar__stats{display:flex;gap:12px;padding:5px 8px}.toolbar__stat{display:flex;flex-direction:column;gap:1px}.toolbar__stat-label{color:#8b949e;font-size:9px;text-transform:uppercase;letter-spacing:.05em}.toolbar__stat-value{color:#c9d1d9}.toolbar__stat--warn .toolbar__stat-value{color:#f85149}.toolbar__inspector{max-height:220px;overflow-y:auto;border-top:1px solid #21262d}.toolbar__inspector::-webkit-scrollbar{width:4px}.toolbar__inspector::-webkit-scrollbar-track{background:transparent}.toolbar__inspector::-webkit-scrollbar-thumb{background:#30363d;border-radius:2px}.toolbar__component{display:flex;align-items:center;gap:6px;padding:3px 8px;border-bottom:1px solid #161b22;transition:background .1s}.toolbar__component:hover{background:#161b22}.toolbar__component--warn{background:#f851490f}.toolbar__component-name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#8b949e}.toolbar__component-count{font-weight:600;color:#ffa657;min-width:20px;text-align:right}.toolbar__component-wasted{color:#f85149;font-size:9px;font-weight:600}\n"] }]
507
+ args: [{ selector: 'angular-scan-toolbar', changeDetection: ChangeDetectionStrategy.OnPush, host: { '[style.display]': '"block"' }, template: "<div\n class=\"toolbar\"\n role=\"complementary\"\n aria-label=\"Angular Scan DevTools\"\n>\n <div class=\"toolbar__header\">\n <span class=\"toolbar__logo\" aria-hidden=\"true\">\u25C9</span>\n <span class=\"toolbar__title\">angular-scan</span>\n\n <button\n class=\"toolbar__btn\"\n type=\"button\"\n [attr.aria-pressed]=\"!config.enabled()\"\n [title]=\"config.enabled() ? 'Pause scanning' : 'Resume scanning'\"\n (click)=\"config.enabled.update(v => !v)\"\n >{{ config.enabled() ? '\u23F8' : '\u25B6' }}</button>\n\n <button\n class=\"toolbar__btn\"\n type=\"button\"\n [attr.aria-pressed]=\"settingsOpen()\"\n title=\"Settings\"\n (click)=\"toggleSettings()\"\n >\u2699</button>\n\n <button\n class=\"toolbar__btn\"\n type=\"button\"\n [attr.aria-pressed]=\"expanded()\"\n [title]=\"expanded() ? 'Collapse inspector' : 'Expand inspector'\"\n (click)=\"toggleExpanded()\"\n >{{ expanded() ? '\u25B2' : '\u25BC' }}</button>\n </div>\n\n <div class=\"toolbar__stats\">\n <span class=\"toolbar__stat\" title=\"Total change detection checks\">\n <span class=\"toolbar__stat-label\">checks</span>\n <strong class=\"toolbar__stat-value\">{{ tracker.totalRenders() }}</strong>\n </span>\n <span\n class=\"toolbar__stat\"\n [class.toolbar__stat--warn]=\"tracker.totalUnnecessary() > 0\"\n title=\"Unnecessary renders: component was checked but DOM did not change\"\n >\n <span class=\"toolbar__stat-label\">wasted</span>\n <strong class=\"toolbar__stat-value\">{{ tracker.totalUnnecessary() }}</strong>\n </span>\n </div>\n\n @if (settingsOpen()) {\n <div class=\"toolbar__settings\" role=\"group\" aria-label=\"Scan settings\">\n\n <label class=\"toolbar__setting\">\n <span class=\"toolbar__setting-label\">Enable scanning</span>\n <button\n class=\"toolbar__toggle\"\n type=\"button\"\n role=\"switch\"\n [attr.aria-checked]=\"config.enabled()\"\n (click)=\"config.enabled.update(v => !v)\"\n [class.toolbar__toggle--on]=\"config.enabled()\"\n >{{ config.enabled() ? 'ON' : 'OFF' }}</button>\n </label>\n\n <label class=\"toolbar__setting\">\n <span class=\"toolbar__setting-label\">Flash overlay</span>\n <button\n class=\"toolbar__toggle\"\n type=\"button\"\n role=\"switch\"\n [attr.aria-checked]=\"config.showOverlay()\"\n (click)=\"config.showOverlay.update(v => !v)\"\n [class.toolbar__toggle--on]=\"config.showOverlay()\"\n >{{ config.showOverlay() ? 'ON' : 'OFF' }}</button>\n </label>\n\n <label class=\"toolbar__setting\">\n <span class=\"toolbar__setting-label\">Render badges</span>\n <button\n class=\"toolbar__toggle\"\n type=\"button\"\n role=\"switch\"\n [attr.aria-checked]=\"config.showBadges()\"\n (click)=\"config.showBadges.update(v => !v)\"\n [class.toolbar__toggle--on]=\"config.showBadges()\"\n >{{ config.showBadges() ? 'ON' : 'OFF' }}</button>\n </label>\n\n <label class=\"toolbar__setting toolbar__setting--column\">\n <span class=\"toolbar__setting-label\">\n Flash duration\n <span class=\"toolbar__setting-value\">{{ config.flashDurationMs() }}ms</span>\n </span>\n <input\n class=\"toolbar__slider\"\n type=\"range\"\n min=\"100\"\n max=\"2000\"\n step=\"100\"\n [value]=\"config.flashDurationMs()\"\n (input)=\"onFlashDurationChange($event)\"\n aria-label=\"Flash duration in milliseconds\"\n />\n </label>\n\n <button\n class=\"toolbar__reset\"\n type=\"button\"\n title=\"Clear all render stats\"\n (click)=\"resetStats()\"\n >\u21BA Reset stats</button>\n\n </div>\n }\n\n @if (expanded()) {\n <div\n class=\"toolbar__inspector\"\n role=\"list\"\n aria-label=\"Component render counts\"\n >\n @for (comp of tracker.trackedComponents(); track comp.hostElement) {\n <div\n class=\"toolbar__component\"\n role=\"listitem\"\n [class.toolbar__component--warn]=\"comp.lastRenderKind === 'unnecessary'\"\n [title]=\"comp.componentName + ' \u2014 ' + comp.totalRenders + ' checks, ' + comp.unnecessaryRenders + ' wasted'\"\n >\n <span class=\"toolbar__component-name\">{{ comp.componentName }}</span>\n <span class=\"toolbar__component-count\">{{ comp.totalRenders }}</span>\n @if (comp.unnecessaryRenders > 0) {\n <span class=\"toolbar__component-wasted\" aria-label=\"wasted renders\">\n {{ comp.unnecessaryRenders }}W\n </span>\n }\n </div>\n }\n </div>\n }\n</div>\n", styles: [":host{position:fixed;bottom:0;right:0;z-index:2147483647;font-family:ui-monospace,Cascadia Code,Source Code Pro,Menlo,Consolas,monospace;font-size:11px;line-height:1.4}.toolbar{background:#0f1117;color:#c9d1d9;border:1px solid #30363d;border-bottom:none;border-right:none;border-radius:6px 0 0;min-width:200px;max-width:300px;box-shadow:-2px -2px 12px #00000080}.toolbar__header{display:flex;align-items:center;gap:4px;padding:5px 8px;border-bottom:1px solid #21262d}.toolbar__logo{color:#58a6ff;font-size:12px}.toolbar__title{font-weight:600;color:#58a6ff;flex:1;letter-spacing:.02em}.toolbar__btn{background:transparent;border:1px solid #30363d;color:#8b949e;border-radius:4px;cursor:pointer;padding:2px 6px;font-size:10px;transition:color .15s,border-color .15s}.toolbar__btn:hover{color:#c9d1d9;border-color:#58a6ff}.toolbar__btn:focus-visible{outline:2px solid #58a6ff;outline-offset:2px}.toolbar__stats{display:flex;gap:12px;padding:5px 8px}.toolbar__stat{display:flex;flex-direction:column;gap:1px}.toolbar__stat-label{color:#8b949e;font-size:9px;text-transform:uppercase;letter-spacing:.05em}.toolbar__stat-value{color:#c9d1d9}.toolbar__stat--warn .toolbar__stat-value{color:#f85149}.toolbar__inspector{max-height:220px;overflow-y:auto;border-top:1px solid #21262d}.toolbar__inspector::-webkit-scrollbar{width:4px}.toolbar__inspector::-webkit-scrollbar-track{background:transparent}.toolbar__inspector::-webkit-scrollbar-thumb{background:#30363d;border-radius:2px}.toolbar__component{display:flex;align-items:center;gap:6px;padding:3px 8px;border-bottom:1px solid #161b22;transition:background .1s}.toolbar__component:hover{background:#161b22}.toolbar__component--warn{background:#f851490f}.toolbar__component-name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#8b949e}.toolbar__component-count{font-weight:600;color:#ffa657;min-width:20px;text-align:right}.toolbar__component-wasted{color:#f85149;font-size:9px;font-weight:600}.toolbar__settings{display:flex;flex-direction:column;gap:6px;padding:8px;border-top:1px solid #21262d}.toolbar__setting{display:flex;align-items:center;justify-content:space-between;gap:8px;cursor:default}.toolbar__setting--column{flex-direction:column;align-items:stretch;gap:4px}.toolbar__setting-label{color:#8b949e;font-size:10px;display:flex;align-items:center;justify-content:space-between;gap:4px}.toolbar__setting-value{color:#58a6ff;font-weight:600}.toolbar__toggle{background:#21262d;border:1px solid #30363d;color:#8b949e;border-radius:3px;cursor:pointer;padding:1px 6px;font-size:9px;font-weight:600;font-family:inherit;letter-spacing:.05em;min-width:32px;transition:background .15s,color .15s,border-color .15s}.toolbar__toggle--on{background:#58a6ff26;border-color:#58a6ff;color:#58a6ff}.toolbar__toggle:focus-visible{outline:2px solid #58a6ff;outline-offset:2px}.toolbar__slider{width:100%;accent-color:#58a6ff;cursor:pointer;height:4px}.toolbar__reset{background:transparent;border:1px solid #30363d;color:#8b949e;border-radius:4px;cursor:pointer;padding:3px 8px;font-size:10px;font-family:inherit;margin-top:2px;transition:color .15s,border-color .15s;align-self:flex-start}.toolbar__reset:hover{color:#f85149;border-color:#f85149}.toolbar__reset:focus-visible{outline:2px solid #58a6ff;outline-offset:2px}\n"] }]
473
508
  }] });
474
509
 
475
510
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"angular-scan.mjs","sources":["../../../projects/angular-scan/src/lib/component-tracker.ts","../../../projects/angular-scan/src/lib/overlay/canvas-overlay.ts","../../../projects/angular-scan/src/lib/tokens.ts","../../../projects/angular-scan/src/lib/overlay/overlay.service.ts","../../../projects/angular-scan/src/lib/ng-debug.ts","../../../projects/angular-scan/src/lib/scanner.service.ts","../../../projects/angular-scan/src/lib/toolbar/toolbar.component.ts","../../../projects/angular-scan/src/lib/toolbar/toolbar.component.html","../../../projects/angular-scan/src/lib/provide-angular-scan.ts","../../../projects/angular-scan/src/lib/scan.ts","../../../projects/angular-scan/src/public-api.ts","../../../projects/angular-scan/src/angular-scan.ts"],"sourcesContent":["import { Injectable, signal } from '@angular/core';\nimport type { ComponentStats, RenderKind } from './types';\n\n@Injectable({ providedIn: 'root' })\nexport class ComponentTracker {\n // WeakMap: component instance → stats. GC-safe: entries are freed when\n // the component instance is garbage collected after destruction.\n private readonly byInstance = new WeakMap<object, ComponentStats>();\n\n // Strong map: host element → component instance. Used for mutation-to-component\n // lookup. Entries must be manually removed when components are destroyed.\n private readonly byElement = new Map<Element, object>();\n\n private readonly _totalRenders = signal(0);\n private readonly _totalUnnecessary = signal(0);\n private readonly _trackedComponents = signal<readonly ComponentStats[]>([]);\n\n readonly totalRenders = this._totalRenders.asReadonly();\n readonly totalUnnecessary = this._totalUnnecessary.asReadonly();\n readonly trackedComponents = this._trackedComponents.asReadonly();\n\n /**\n * Record a render event for a component.\n * NOTE: Must be called outside of a profiler callback to avoid triggering CD.\n * Use queueMicrotask() to defer calls from profiler events.\n */\n recordRender(instance: object, hostElement: Element, kind: RenderKind): ComponentStats {\n let stats = this.byInstance.get(instance);\n\n if (!stats) {\n stats = {\n componentName: instance.constructor.name,\n hostElement,\n totalRenders: 0,\n unnecessaryRenders: 0,\n lastRenderKind: kind,\n lastRenderTimestamp: 0,\n };\n this.byInstance.set(instance, stats);\n this.byElement.set(hostElement, instance);\n }\n\n stats.totalRenders++;\n stats.lastRenderKind = kind;\n stats.lastRenderTimestamp = Date.now();\n\n if (kind === 'unnecessary') {\n stats.unnecessaryRenders++;\n this._totalUnnecessary.update(n => n + 1);\n }\n\n this._totalRenders.update(n => n + 1);\n return stats;\n }\n\n /**\n * Rebuild the trackedComponents signal from the current element map.\n * Call once per tick after all recordRender() calls.\n */\n snapshotTrackedComponents(): void {\n const list: ComponentStats[] = [];\n for (const [, instance] of this.byElement) {\n const stats = this.byInstance.get(instance);\n if (stats) list.push(stats);\n }\n this._trackedComponents.set(list);\n }\n\n /** Remove a component from tracking (call when its host element is removed). */\n unregister(el: Element): void {\n this.byElement.delete(el);\n }\n\n reset(): void {\n this.byElement.clear();\n this._totalRenders.set(0);\n this._totalUnnecessary.set(0);\n this._trackedComponents.set([]);\n }\n}\n","import type { RenderKind } from '../types';\n\ninterface FlashRect {\n x: number;\n y: number;\n width: number;\n height: number;\n kind: RenderKind;\n startTime: number;\n durationMs: number;\n}\n\n// rgb(...) strings for fill and stroke\nconst RENDER_COLOR = '255, 200, 0'; // yellow — normal re-render\nconst UNNECESSARY_COLOR = '255, 60, 60'; // red — unnecessary render\n\nexport class CanvasOverlay {\n private canvas: HTMLCanvasElement | null = null;\n private ctx: CanvasRenderingContext2D | null = null;\n private rects: FlashRect[] = [];\n private rafId = 0;\n private attached = false;\n\n attach(doc: Document): void {\n if (this.attached) return;\n\n const canvas = doc.createElement('canvas');\n canvas.setAttribute('aria-hidden', 'true');\n canvas.setAttribute('role', 'presentation');\n canvas.style.cssText = [\n 'position:fixed',\n 'top:0',\n 'left:0',\n 'width:100%',\n 'height:100%',\n 'pointer-events:none',\n `z-index:2147483646`,\n ].join(';');\n\n doc.body.appendChild(canvas);\n this.canvas = canvas;\n this.ctx = canvas.getContext('2d');\n this.attached = true;\n\n this.resize();\n window.addEventListener('resize', this.onResize);\n\n this.scheduleFrame();\n }\n\n detach(): void {\n if (!this.attached) return;\n this.attached = false;\n cancelAnimationFrame(this.rafId);\n window.removeEventListener('resize', this.onResize);\n this.canvas?.remove();\n this.canvas = null;\n this.ctx = null;\n this.rects = [];\n }\n\n /** Queue a flash animation over an element's bounding rect. */\n flash(element: Element, kind: RenderKind, durationMs: number): void {\n if (!this.attached) return;\n\n const rect = element.getBoundingClientRect();\n if (rect.width === 0 || rect.height === 0) return;\n\n this.rects.push({\n x: rect.left,\n y: rect.top,\n width: rect.width,\n height: rect.height,\n kind,\n startTime: performance.now(),\n durationMs,\n });\n }\n\n private readonly onResize = (): void => this.resize();\n\n private resize(): void {\n if (!this.canvas) return;\n this.canvas.width = window.innerWidth;\n this.canvas.height = window.innerHeight;\n }\n\n private scheduleFrame(): void {\n if (!this.attached) return;\n this.rafId = requestAnimationFrame(this.drawFrame);\n }\n\n private readonly drawFrame = (now: number): void => {\n if (!this.ctx || !this.canvas || !this.attached) return;\n\n this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\n\n // Decay alphas and remove expired rects\n this.rects = this.rects.filter(r => {\n const elapsed = now - r.startTime;\n return elapsed < r.durationMs;\n });\n\n for (const r of this.rects) {\n const elapsed = now - r.startTime;\n const alpha = Math.max(0, 1 - elapsed / r.durationMs);\n const color = r.kind === 'render' ? RENDER_COLOR : UNNECESSARY_COLOR;\n\n // Semi-transparent fill\n this.ctx!.fillStyle = `rgba(${color}, ${alpha * 0.1})`;\n this.ctx!.fillRect(r.x, r.y, r.width, r.height);\n\n // Solid border\n this.ctx!.strokeStyle = `rgba(${color}, ${alpha * 0.9})`;\n this.ctx!.lineWidth = 2;\n this.ctx!.strokeRect(r.x + 1, r.y + 1, r.width - 2, r.height - 2);\n }\n\n this.scheduleFrame();\n };\n}\n","import { InjectionToken } from '@angular/core';\nimport type { AngularScanOptions } from './types';\n\nexport const ANGULAR_SCAN_OPTIONS = new InjectionToken<AngularScanOptions>(\n 'ANGULAR_SCAN_OPTIONS',\n { providedIn: 'root', factory: () => ({}) }\n);\n","import { Injectable, inject, isDevMode, DOCUMENT, PLATFORM_ID } from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\nimport { CanvasOverlay } from './canvas-overlay';\nimport { ANGULAR_SCAN_OPTIONS } from '../tokens';\nimport type { ComponentStats } from '../types';\n\n@Injectable({ providedIn: 'root' })\nexport class OverlayService {\n private readonly document = inject(DOCUMENT);\n private readonly platformId = inject(PLATFORM_ID);\n private readonly options = inject(ANGULAR_SCAN_OPTIONS);\n private readonly canvas = new CanvasOverlay();\n\n // Badge elements keyed by host element\n private readonly badges = new Map<Element, HTMLElement>();\n\n initialize(): void {\n if (!isDevMode()) return;\n if (!isPlatformBrowser(this.platformId)) return;\n if (this.options.enabled === false) return;\n\n this.canvas.attach(this.document);\n }\n\n destroy(): void {\n this.canvas.detach();\n for (const badge of this.badges.values()) badge.remove();\n this.badges.clear();\n }\n\n onComponentChecked(stats: ComponentStats): void {\n const durationMs = this.options.flashDurationMs ?? 500;\n this.canvas.flash(stats.hostElement, stats.lastRenderKind, durationMs);\n\n if (this.options.showBadges !== false) {\n this.updateBadge(stats);\n }\n }\n\n removeBadge(el: Element): void {\n this.badges.get(el)?.remove();\n this.badges.delete(el);\n }\n\n private updateBadge(stats: ComponentStats): void {\n let badge = this.badges.get(stats.hostElement);\n\n if (!badge) {\n badge = this.document.createElement('div');\n badge.setAttribute('aria-hidden', 'true');\n badge.setAttribute('role', 'presentation');\n badge.style.cssText = [\n 'position:absolute',\n 'top:2px',\n 'right:2px',\n 'z-index:2147483645',\n 'pointer-events:none',\n 'font:bold 9px/13px monospace',\n 'padding:1px 4px',\n 'border-radius:3px',\n 'min-width:16px',\n 'text-align:center',\n 'color:#fff',\n 'white-space:nowrap',\n ].join(';');\n\n // Host element must be non-static to contain an absolutely-positioned badge\n this.ensurePositioned(stats.hostElement);\n stats.hostElement.appendChild(badge);\n this.badges.set(stats.hostElement, badge);\n }\n\n const isUnnecessary = stats.lastRenderKind === 'unnecessary';\n badge.style.background = isUnnecessary ? '#f44336' : '#ff9800';\n badge.textContent = String(stats.totalRenders);\n badge.title = `${stats.componentName}: ${stats.totalRenders} renders, ${stats.unnecessaryRenders} unnecessary`;\n }\n\n private ensurePositioned(el: Element): void {\n const htmlEl = el as HTMLElement;\n if (getComputedStyle(htmlEl).position === 'static') {\n htmlEl.style.position = 'relative';\n }\n }\n}\n","import type { NgDebugApi } from './types';\n\n/**\n * Returns the Angular debug API (window.ng) if available.\n * Returns null in SSR, production builds, or when Angular DevTools APIs are absent.\n *\n * Safe to call before Angular is bootstrapped — the API is attached to window\n * by Angular's dev mode runtime.\n */\nexport function getNgDebugApi(): NgDebugApi | null {\n if (typeof window === 'undefined') return null;\n\n const ng = (window as unknown as Record<string, unknown>)['ng'];\n if (!ng || typeof ng !== 'object') return null;\n\n const api = ng as Partial<NgDebugApi>;\n\n // ɵsetProfiler is the sentinel for dev mode — absent in production builds\n if (typeof api.ɵsetProfiler !== 'function') return null;\n if (typeof api.getHostElement !== 'function') return null;\n\n return api as NgDebugApi;\n}\n","import {\n Injectable,\n inject,\n isDevMode,\n OnDestroy,\n PLATFORM_ID,\n NgZone,\n} from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\nimport { ComponentTracker } from './component-tracker';\nimport { OverlayService } from './overlay/overlay.service';\nimport { ANGULAR_SCAN_OPTIONS } from './tokens';\nimport { getNgDebugApi } from './ng-debug';\nimport type { NgDebugApi, RenderKind } from './types';\n\n// Angular profiler event IDs (stable contract in dev mode, used by Angular DevTools)\nconst TemplateUpdateStart = 2;\nconst ChangeDetectionStart = 12;\nconst ChangeDetectionEnd = 13;\nconst ChangeDetectionSyncStart = 14;\nconst ChangeDetectionSyncEnd = 15;\n\ninterface PendingUpdate {\n instance: object;\n hostElement: Element;\n kind: RenderKind;\n}\n\n@Injectable({ providedIn: 'root' })\nexport class ScannerService implements OnDestroy {\n private readonly tracker = inject(ComponentTracker);\n private readonly overlay = inject(OverlayService);\n private readonly options = inject(ANGULAR_SCAN_OPTIONS);\n private readonly platformId = inject(PLATFORM_ID);\n\n private readonly ngZone = inject(NgZone);\n\n private removeProfiler: (() => void) | null = null;\n private mutationObserver: MutationObserver | null = null;\n\n // Accumulated during a single CD tick (raw DOM nodes, no signal writes)\n private mutatedNodes = new Set<Node>();\n // Component instances visited during the sync (template update) phase\n private tickInstances = new Set<object>();\n\n // Tracks which phase of CD we're in\n private inSyncPhase = false;\n private inTick = false;\n\n // The toolbar component instance — excluded from tracking to avoid self-loops\n private toolbarInstance: object | null = null;\n\n // Throttle toolbar signal updates to at most once per animation frame\n private toolbarRafPending = false;\n\n initialize(): void {\n if (!isDevMode()) return;\n if (!isPlatformBrowser(this.platformId)) return;\n if (this.options.enabled === false) return;\n\n const ng = getNgDebugApi();\n if (!ng) {\n console.warn(\n '[angular-scan] Angular debug APIs (window.ng) not available. ' +\n 'Ensure you are running in development mode.'\n );\n return;\n }\n\n this.mutationObserver = new MutationObserver(records => {\n for (const r of records) {\n this.mutatedNodes.add(r.target);\n r.addedNodes.forEach(n => this.mutatedNodes.add(n));\n r.removedNodes.forEach(n => this.mutatedNodes.add(n));\n }\n });\n\n this.removeProfiler = ng.ɵsetProfiler((event, instance) => {\n switch (event) {\n case ChangeDetectionStart:\n this.onTickStart();\n break;\n case ChangeDetectionSyncStart:\n this.inSyncPhase = true;\n break;\n case ChangeDetectionSyncEnd:\n this.inSyncPhase = false;\n break;\n case TemplateUpdateStart:\n // Only record during the real update phase, not the dev-mode checkNoChanges pass\n if (this.inSyncPhase && instance && instance !== this.toolbarInstance) {\n this.tickInstances.add(instance);\n }\n break;\n case ChangeDetectionEnd:\n this.onTickEnd(ng);\n break;\n }\n });\n }\n\n /** Exclude a component instance from render tracking (used for the toolbar). */\n setToolbarInstance(instance: object): void {\n this.toolbarInstance = instance;\n }\n\n private onTickStart(): void {\n if (this.inTick) return;\n this.inTick = true;\n this.mutatedNodes.clear();\n this.tickInstances.clear();\n\n this.mutationObserver?.observe(document.body, {\n subtree: true,\n childList: true,\n attributes: true,\n characterData: true,\n });\n }\n\n private onTickEnd(ng: NgDebugApi): void {\n if (!this.inTick) return;\n this.inTick = false;\n this.inSyncPhase = false;\n\n // Synchronously flush pending mutation records before disconnecting\n const pending = this.mutationObserver?.takeRecords() ?? [];\n for (const r of pending) {\n this.mutatedNodes.add(r.target);\n r.addedNodes.forEach(n => this.mutatedNodes.add(n));\n r.removedNodes.forEach(n => this.mutatedNodes.add(n));\n }\n this.mutationObserver?.disconnect();\n\n // Build a set of component host elements that had DOM mutations\n // Walk mutated nodes UP to find their owning component host element\n const mutatedHosts = this.buildMutatedHosts(ng);\n\n // Collect updates as pure data — NO signal writes here (would trigger CD)\n const pendingUpdates: PendingUpdate[] = [];\n\n for (const instance of this.tickInstances) {\n try {\n const hostElement = ng.getHostElement(instance);\n if (!hostElement) continue;\n\n const hadMutation = mutatedHosts.has(hostElement);\n const kind: RenderKind = hadMutation ? 'render' : 'unnecessary';\n\n pendingUpdates.push({ instance, hostElement, kind });\n } catch {\n // Component may have been destroyed during the tick\n }\n }\n\n // Run signal writes OUTSIDE Angular's zone so zone.js does not schedule a\n // new CD cycle from them — which would cause an infinite loop in NgModule apps\n // where queueMicrotask is patched by zone.js.\n this.ngZone.runOutsideAngular(() => {\n queueMicrotask(() => {\n for (const update of pendingUpdates) {\n const stats = this.tracker.recordRender(update.instance, update.hostElement, update.kind);\n this.overlay.onComponentChecked(stats);\n }\n\n // Throttle the toolbar signal snapshot to at most once per animation frame.\n // Re-enter the zone so the toolbar (OnPush + signals) gets a CD cycle to repaint.\n if (!this.toolbarRafPending) {\n this.toolbarRafPending = true;\n requestAnimationFrame(() => {\n this.ngZone.run(() => {\n this.toolbarRafPending = false;\n this.tracker.snapshotTrackedComponents();\n });\n });\n }\n });\n });\n }\n\n /**\n * Build a Set of host Elements by walking each mutated node up the DOM tree\n * until an Angular component boundary is found.\n * This is O(mutatedNodes × depth) but depth is typically < 20.\n */\n private buildMutatedHosts(ng: NgDebugApi): Set<Element> {\n const hosts = new Set<Element>();\n\n for (const node of this.mutatedNodes) {\n let current: Node | null = node;\n\n while (current && current !== document.body) {\n if (current instanceof Element) {\n try {\n const component = ng.getComponent(current);\n if (component) {\n hosts.add(current);\n break;\n }\n } catch {\n // ignore\n }\n }\n current = current.parentNode;\n }\n }\n\n return hosts;\n }\n\n ngOnDestroy(): void {\n this.removeProfiler?.();\n this.removeProfiler = null;\n this.mutationObserver?.disconnect();\n this.mutationObserver = null;\n this.overlay.destroy();\n }\n}\n","import {\n Component,\n ChangeDetectionStrategy,\n inject,\n signal,\n} from '@angular/core';\nimport { ComponentTracker } from '../component-tracker';\n\n@Component({\n selector: 'angular-scan-toolbar',\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './toolbar.component.html',\n styleUrls: ['./toolbar.component.scss'],\n host: { '[style.display]': '\"block\"' },\n})\nexport class ToolbarComponent {\n protected readonly tracker = inject(ComponentTracker);\n\n protected readonly expanded = signal(false);\n protected readonly enabled = signal(true);\n\n protected toggleExpanded(): void {\n this.expanded.update(v => !v);\n }\n\n protected toggleEnabled(): void {\n this.enabled.update(v => !v);\n }\n}\n","<div\nclass=\"toolbar\"\nrole=\"complementary\"\naria-label=\"Angular Scan DevTools\"\n>\n<div class=\"toolbar__header\">\n <span class=\"toolbar__logo\" aria-hidden=\"true\">◉</span>\n <span class=\"toolbar__title\">angular-scan</span>\n\n <button\n class=\"toolbar__btn\"\n type=\"button\"\n [attr.aria-pressed]=\"enabled()\"\n [title]=\"enabled() ? 'Pause scanning' : 'Resume scanning'\"\n (click)=\"toggleEnabled()\"\n >{{ enabled() ? '⏸' : '▶' }}</button>\n\n <button\n class=\"toolbar__btn\"\n type=\"button\"\n [attr.aria-pressed]=\"expanded()\"\n [title]=\"expanded() ? 'Collapse inspector' : 'Expand inspector'\"\n (click)=\"toggleExpanded()\"\n >{{ expanded() ? '▲' : '▼' }}</button>\n</div>\n\n<div class=\"toolbar__stats\">\n <span class=\"toolbar__stat\" title=\"Total change detection checks\">\n <span class=\"toolbar__stat-label\">checks</span>\n <strong class=\"toolbar__stat-value\">{{ tracker.totalRenders() }}</strong>\n </span>\n <span\n class=\"toolbar__stat\"\n [class.toolbar__stat--warn]=\"tracker.totalUnnecessary() > 0\"\n title=\"Unnecessary renders: component was checked but DOM did not change\"\n >\n <span class=\"toolbar__stat-label\">wasted</span>\n <strong class=\"toolbar__stat-value\">{{ tracker.totalUnnecessary() }}</strong>\n </span>\n</div>\n\n@if (expanded()) {\n <div\n class=\"toolbar__inspector\"\n role=\"list\"\n aria-label=\"Component render counts\"\n >\n @for (comp of tracker.trackedComponents(); track comp.hostElement) {\n <div\n class=\"toolbar__component\"\n role=\"listitem\"\n [class.toolbar__component--warn]=\"comp.lastRenderKind === 'unnecessary'\"\n [title]=\"comp.componentName + ' — ' + comp.totalRenders + ' checks, ' + comp.unnecessaryRenders + ' wasted'\"\n >\n <span class=\"toolbar__component-name\">{{ comp.componentName }}</span>\n <span class=\"toolbar__component-count\">{{ comp.totalRenders }}</span>\n @if (comp.unnecessaryRenders > 0) {\n <span class=\"toolbar__component-wasted\" aria-label=\"wasted renders\">\n {{ comp.unnecessaryRenders }}W\n </span>\n }\n </div>\n }\n </div>\n}\n</div>","import {\n EnvironmentProviders,\n makeEnvironmentProviders,\n isDevMode,\n provideEnvironmentInitializer,\n inject,\n ApplicationRef,\n createComponent,\n EnvironmentInjector,\n} from '@angular/core';\nimport { DOCUMENT } from '@angular/common';\nimport { ScannerService } from './scanner.service';\nimport { OverlayService } from './overlay/overlay.service';\nimport { ToolbarComponent } from './toolbar/toolbar.component';\nimport { ANGULAR_SCAN_OPTIONS } from './tokens';\nimport type { AngularScanOptions } from './types';\n\n/**\n * Provides angular-scan for development-time render tracking.\n *\n * Add to your application providers:\n * ```ts\n * bootstrapApplication(AppComponent, {\n * providers: [provideAngularScan()]\n * });\n * ```\n *\n * This is a complete no-op in production builds (`isDevMode() === false`),\n * so it can safely be left in your app config.\n */\nexport function provideAngularScan(options: AngularScanOptions = {}): EnvironmentProviders {\n // Return empty providers in production — entire library is tree-shaken\n if (!isDevMode()) {\n return makeEnvironmentProviders([]);\n }\n\n const resolvedOptions: AngularScanOptions = {\n enabled: true,\n flashDurationMs: 500,\n showBadges: true,\n showToolbar: true,\n ...options,\n };\n\n return makeEnvironmentProviders([\n {\n provide: ANGULAR_SCAN_OPTIONS,\n useValue: resolvedOptions,\n },\n provideEnvironmentInitializer(() => {\n const overlay = inject(OverlayService);\n const scanner = inject(ScannerService);\n const opts = inject(ANGULAR_SCAN_OPTIONS);\n\n overlay.initialize();\n scanner.initialize();\n\n if (opts.showToolbar !== false) {\n const injector = inject(EnvironmentInjector);\n const doc = inject(DOCUMENT);\n const appRef = inject(ApplicationRef);\n\n // Create the toolbar outside Angular's component tree so it doesn't\n // appear in CD tracking. Attach it to ApplicationRef so signals work.\n const toolbarRef = createComponent(ToolbarComponent, {\n environmentInjector: injector,\n });\n\n doc.body.appendChild(toolbarRef.location.nativeElement);\n appRef.attachView(toolbarRef.hostView);\n\n // Tell the scanner to ignore the toolbar's own renders\n scanner.setToolbarInstance(toolbarRef.instance);\n }\n }),\n ]);\n}\n","import { isDevMode } from '@angular/core';\nimport { getNgDebugApi } from './ng-debug';\nimport { CanvasOverlay } from './overlay/canvas-overlay';\nimport type { AngularScanOptions, RenderKind } from './types';\n\n// Profiler event constants\nconst TemplateUpdateStart = 2;\nconst ChangeDetectionStart = 12;\nconst ChangeDetectionEnd = 13;\nconst ChangeDetectionSyncStart = 14;\nconst ChangeDetectionSyncEnd = 15;\n\n/**\n * Imperative API for angular-scan.\n *\n * Use this when you cannot modify application providers (e.g. micro-frontends,\n * third-party apps). Call before `bootstrapApplication()`.\n *\n * ```ts\n * // main.ts\n * import { scan } from 'angular-scan';\n * scan();\n * bootstrapApplication(AppComponent, appConfig);\n * ```\n *\n * @returns A teardown function that stops scanning and removes the overlay.\n */\nexport function scan(options: AngularScanOptions = {}): () => void {\n if (!isDevMode()) return noop;\n if (typeof window === 'undefined') return noop;\n if (options.enabled === false) return noop;\n\n const ng = getNgDebugApi();\n if (!ng) {\n console.warn(\n '[angular-scan] Angular debug APIs (window.ng) not available. ' +\n 'Ensure you are running in development mode.'\n );\n return noop;\n }\n\n const durationMs = options.flashDurationMs ?? 500;\n const showBadges = options.showBadges !== false;\n\n const overlay = new CanvasOverlay();\n overlay.attach(document);\n\n // State for the current tick\n let inTick = false;\n let inSyncPhase = false;\n const mutatedNodes = new Set<Node>();\n const tickInstances = new Set<object>();\n const renderCounts = new WeakMap<object, number>();\n const unnecessaryCounts = new WeakMap<object, number>();\n const badges = new Map<Element, HTMLElement>();\n\n const observer = new MutationObserver(records => {\n for (const r of records) {\n mutatedNodes.add(r.target);\n r.addedNodes.forEach(n => mutatedNodes.add(n));\n r.removedNodes.forEach(n => mutatedNodes.add(n));\n }\n });\n\n const removeProfiler = ng.ɵsetProfiler((event, instance) => {\n switch (event) {\n case ChangeDetectionStart:\n if (inTick) break;\n inTick = true;\n mutatedNodes.clear();\n tickInstances.clear();\n observer.observe(document.body, {\n subtree: true, childList: true, attributes: true, characterData: true,\n });\n break;\n\n case ChangeDetectionSyncStart:\n inSyncPhase = true;\n break;\n\n case ChangeDetectionSyncEnd:\n inSyncPhase = false;\n break;\n\n case TemplateUpdateStart:\n if (inSyncPhase && instance) tickInstances.add(instance);\n break;\n\n case ChangeDetectionEnd: {\n if (!inTick) break;\n inTick = false;\n inSyncPhase = false;\n\n const pending = observer.takeRecords();\n for (const r of pending) {\n mutatedNodes.add(r.target);\n r.addedNodes.forEach(n => mutatedNodes.add(n));\n r.removedNodes.forEach(n => mutatedNodes.add(n));\n }\n observer.disconnect();\n\n // Build mutated host set\n const mutatedHosts = new Set<Element>();\n for (const node of mutatedNodes) {\n let current: Node | null = node;\n while (current && current !== document.body) {\n if (current instanceof Element) {\n try {\n if (ng.getComponent(current)) { mutatedHosts.add(current); break; }\n } catch { /* ignore */ }\n }\n current = current.parentNode;\n }\n }\n\n // Collect updates without writing to any signals\n const updates: Array<{ instance: object; hostEl: Element; kind: RenderKind }> = [];\n for (const inst of tickInstances) {\n try {\n const hostEl = ng.getHostElement(inst);\n if (!hostEl) continue;\n const kind: RenderKind = mutatedHosts.has(hostEl) ? 'render' : 'unnecessary';\n updates.push({ instance: inst, hostEl, kind });\n } catch { /* component destroyed */ }\n }\n\n queueMicrotask(() => {\n for (const { instance, hostEl, kind } of updates) {\n const count = (renderCounts.get(instance) ?? 0) + 1;\n renderCounts.set(instance, count);\n\n if (kind === 'unnecessary') {\n unnecessaryCounts.set(instance, (unnecessaryCounts.get(instance) ?? 0) + 1);\n }\n\n overlay.flash(hostEl, kind, durationMs);\n\n if (showBadges) updateBadge(hostEl, count, kind);\n }\n });\n break;\n }\n }\n });\n\n function updateBadge(hostEl: Element, count: number, kind: RenderKind): void {\n let badge = badges.get(hostEl);\n if (!badge) {\n badge = document.createElement('div');\n badge.setAttribute('aria-hidden', 'true');\n badge.setAttribute('role', 'presentation');\n badge.style.cssText = [\n 'position:absolute', 'top:2px', 'right:2px',\n 'z-index:2147483645', 'pointer-events:none',\n 'font:bold 9px/13px monospace', 'padding:1px 4px',\n 'border-radius:3px', 'min-width:16px', 'text-align:center', 'color:#fff',\n ].join(';');\n const htmlEl = hostEl as HTMLElement;\n if (getComputedStyle(htmlEl).position === 'static') htmlEl.style.position = 'relative';\n hostEl.appendChild(badge);\n badges.set(hostEl, badge);\n }\n badge.style.background = kind === 'unnecessary' ? '#f44336' : '#ff9800';\n badge.textContent = String(count);\n }\n\n return () => {\n removeProfiler();\n observer.disconnect();\n overlay.detach();\n for (const b of badges.values()) b.remove();\n badges.clear();\n };\n}\n\nfunction noop(): void {}\n","/*\n * Public API Surface of angular-scan\n */\n\nexport { provideAngularScan } from './lib/provide-angular-scan';\nexport { scan } from './lib/scan';\nexport type { AngularScanOptions, ComponentStats, RenderKind } from './lib/types';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["TemplateUpdateStart","ChangeDetectionStart","ChangeDetectionEnd","ChangeDetectionSyncStart","ChangeDetectionSyncEnd","DOCUMENT"],"mappings":"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"}
1
+ {"version":3,"file":"angular-scan.mjs","sources":["../../../projects/angular-scan/src/lib/component-tracker.ts","../../../projects/angular-scan/src/lib/overlay/canvas-overlay.ts","../../../projects/angular-scan/src/lib/tokens.ts","../../../projects/angular-scan/src/lib/scan-config.service.ts","../../../projects/angular-scan/src/lib/overlay/overlay.service.ts","../../../projects/angular-scan/src/lib/ng-debug.ts","../../../projects/angular-scan/src/lib/scanner.service.ts","../../../projects/angular-scan/src/lib/toolbar/toolbar.component.ts","../../../projects/angular-scan/src/lib/toolbar/toolbar.component.html","../../../projects/angular-scan/src/lib/provide-angular-scan.ts","../../../projects/angular-scan/src/lib/scan.ts","../../../projects/angular-scan/src/public-api.ts","../../../projects/angular-scan/src/angular-scan.ts"],"sourcesContent":["import { Injectable, signal } from '@angular/core';\nimport type { ComponentStats, RenderKind } from './types';\n\n@Injectable({ providedIn: 'root' })\nexport class ComponentTracker {\n // WeakMap: component instance → stats. GC-safe: entries are freed when\n // the component instance is garbage collected after destruction.\n private readonly byInstance = new WeakMap<object, ComponentStats>();\n\n // Strong map: host element → component instance. Used for mutation-to-component\n // lookup. Entries must be manually removed when components are destroyed.\n private readonly byElement = new Map<Element, object>();\n\n private readonly _totalRenders = signal(0);\n private readonly _totalUnnecessary = signal(0);\n private readonly _trackedComponents = signal<readonly ComponentStats[]>([]);\n\n readonly totalRenders = this._totalRenders.asReadonly();\n readonly totalUnnecessary = this._totalUnnecessary.asReadonly();\n readonly trackedComponents = this._trackedComponents.asReadonly();\n\n /**\n * Record a render event for a component.\n * NOTE: Must be called outside of a profiler callback to avoid triggering CD.\n * Use queueMicrotask() to defer calls from profiler events.\n */\n recordRender(instance: object, hostElement: Element, kind: RenderKind): ComponentStats {\n let stats = this.byInstance.get(instance);\n\n if (!stats) {\n stats = {\n componentName: instance.constructor.name,\n hostElement,\n totalRenders: 0,\n unnecessaryRenders: 0,\n lastRenderKind: kind,\n lastRenderTimestamp: 0,\n };\n this.byInstance.set(instance, stats);\n this.byElement.set(hostElement, instance);\n }\n\n stats.totalRenders++;\n stats.lastRenderKind = kind;\n stats.lastRenderTimestamp = Date.now();\n\n if (kind === 'unnecessary') {\n stats.unnecessaryRenders++;\n this._totalUnnecessary.update(n => n + 1);\n }\n\n this._totalRenders.update(n => n + 1);\n return stats;\n }\n\n /**\n * Rebuild the trackedComponents signal from the current element map.\n * Call once per tick after all recordRender() calls.\n */\n snapshotTrackedComponents(): void {\n const list: ComponentStats[] = [];\n for (const [, instance] of this.byElement) {\n const stats = this.byInstance.get(instance);\n if (stats) list.push(stats);\n }\n this._trackedComponents.set(list);\n }\n\n /** Remove a component from tracking (call when its host element is removed). */\n unregister(el: Element): void {\n this.byElement.delete(el);\n }\n\n reset(): void {\n this.byElement.clear();\n this._totalRenders.set(0);\n this._totalUnnecessary.set(0);\n this._trackedComponents.set([]);\n }\n}\n","import type { RenderKind } from '../types';\n\ninterface FlashRect {\n x: number;\n y: number;\n width: number;\n height: number;\n kind: RenderKind;\n startTime: number;\n durationMs: number;\n}\n\n// rgb(...) strings for fill and stroke\nconst RENDER_COLOR = '255, 200, 0'; // yellow — normal re-render\nconst UNNECESSARY_COLOR = '255, 60, 60'; // red — unnecessary render\n\nexport class CanvasOverlay {\n private canvas: HTMLCanvasElement | null = null;\n private ctx: CanvasRenderingContext2D | null = null;\n private rects: FlashRect[] = [];\n private rafId = 0;\n private attached = false;\n\n attach(doc: Document): void {\n if (this.attached) return;\n\n const canvas = doc.createElement('canvas');\n canvas.setAttribute('aria-hidden', 'true');\n canvas.setAttribute('role', 'presentation');\n canvas.style.cssText = [\n 'position:fixed',\n 'top:0',\n 'left:0',\n 'width:100%',\n 'height:100%',\n 'pointer-events:none',\n `z-index:2147483646`,\n ].join(';');\n\n doc.body.appendChild(canvas);\n this.canvas = canvas;\n this.ctx = canvas.getContext('2d');\n this.attached = true;\n\n this.resize();\n window.addEventListener('resize', this.onResize);\n\n this.scheduleFrame();\n }\n\n detach(): void {\n if (!this.attached) return;\n this.attached = false;\n cancelAnimationFrame(this.rafId);\n window.removeEventListener('resize', this.onResize);\n this.canvas?.remove();\n this.canvas = null;\n this.ctx = null;\n this.rects = [];\n }\n\n /** Queue a flash animation over an element's bounding rect. */\n flash(element: Element, kind: RenderKind, durationMs: number): void {\n if (!this.attached) return;\n\n const rect = element.getBoundingClientRect();\n if (rect.width === 0 || rect.height === 0) return;\n\n this.rects.push({\n x: rect.left,\n y: rect.top,\n width: rect.width,\n height: rect.height,\n kind,\n startTime: performance.now(),\n durationMs,\n });\n }\n\n private readonly onResize = (): void => this.resize();\n\n private resize(): void {\n if (!this.canvas) return;\n this.canvas.width = window.innerWidth;\n this.canvas.height = window.innerHeight;\n }\n\n private scheduleFrame(): void {\n if (!this.attached) return;\n this.rafId = requestAnimationFrame(this.drawFrame);\n }\n\n private readonly drawFrame = (now: number): void => {\n if (!this.ctx || !this.canvas || !this.attached) return;\n\n this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\n\n // Decay alphas and remove expired rects\n this.rects = this.rects.filter(r => {\n const elapsed = now - r.startTime;\n return elapsed < r.durationMs;\n });\n\n for (const r of this.rects) {\n const elapsed = now - r.startTime;\n const alpha = Math.max(0, 1 - elapsed / r.durationMs);\n const color = r.kind === 'render' ? RENDER_COLOR : UNNECESSARY_COLOR;\n\n // Semi-transparent fill\n this.ctx!.fillStyle = `rgba(${color}, ${alpha * 0.1})`;\n this.ctx!.fillRect(r.x, r.y, r.width, r.height);\n\n // Solid border\n this.ctx!.strokeStyle = `rgba(${color}, ${alpha * 0.9})`;\n this.ctx!.lineWidth = 2;\n this.ctx!.strokeRect(r.x + 1, r.y + 1, r.width - 2, r.height - 2);\n }\n\n this.scheduleFrame();\n };\n}\n","import { InjectionToken } from '@angular/core';\nimport type { AngularScanOptions } from './types';\n\nexport const ANGULAR_SCAN_OPTIONS = new InjectionToken<AngularScanOptions>(\n 'ANGULAR_SCAN_OPTIONS',\n { providedIn: 'root', factory: () => ({}) }\n);\n","import { Injectable, inject, signal } from '@angular/core';\nimport { ANGULAR_SCAN_OPTIONS } from './tokens';\n\n@Injectable({ providedIn: 'root' })\nexport class ScanConfigService {\n private readonly options = inject(ANGULAR_SCAN_OPTIONS);\n\n readonly enabled = signal(this.options.enabled !== false);\n readonly showOverlay = signal(true);\n readonly showBadges = signal(this.options.showBadges !== false);\n readonly flashDurationMs = signal(this.options.flashDurationMs ?? 500);\n}\n","import { Injectable, inject, isDevMode, DOCUMENT, PLATFORM_ID } from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\nimport { CanvasOverlay } from './canvas-overlay';\nimport { ANGULAR_SCAN_OPTIONS } from '../tokens';\nimport { ScanConfigService } from '../scan-config.service';\nimport type { ComponentStats } from '../types';\n\n@Injectable({ providedIn: 'root' })\nexport class OverlayService {\n private readonly document = inject(DOCUMENT);\n private readonly platformId = inject(PLATFORM_ID);\n private readonly options = inject(ANGULAR_SCAN_OPTIONS);\n private readonly config = inject(ScanConfigService);\n private readonly canvas = new CanvasOverlay();\n\n // Badge elements keyed by host element\n private readonly badges = new Map<Element, HTMLElement>();\n\n initialize(): void {\n if (!isDevMode()) return;\n if (!isPlatformBrowser(this.platformId)) return;\n if (this.options.enabled === false) return;\n\n this.canvas.attach(this.document);\n }\n\n destroy(): void {\n this.canvas.detach();\n for (const badge of this.badges.values()) badge.remove();\n this.badges.clear();\n }\n\n onComponentChecked(stats: ComponentStats): void {\n if (this.config.showOverlay()) {\n this.canvas.flash(stats.hostElement, stats.lastRenderKind, this.config.flashDurationMs());\n }\n\n if (this.config.showBadges()) {\n this.updateBadge(stats);\n } else {\n this.removeBadge(stats.hostElement);\n }\n }\n\n removeBadge(el: Element): void {\n this.badges.get(el)?.remove();\n this.badges.delete(el);\n }\n\n private updateBadge(stats: ComponentStats): void {\n let badge = this.badges.get(stats.hostElement);\n\n if (!badge) {\n badge = this.document.createElement('div');\n badge.setAttribute('aria-hidden', 'true');\n badge.setAttribute('role', 'presentation');\n badge.style.cssText = [\n 'position:absolute',\n 'top:2px',\n 'right:2px',\n 'z-index:2147483645',\n 'pointer-events:none',\n 'font:bold 9px/13px monospace',\n 'padding:1px 4px',\n 'border-radius:3px',\n 'min-width:16px',\n 'text-align:center',\n 'color:#fff',\n 'white-space:nowrap',\n ].join(';');\n\n // Host element must be non-static to contain an absolutely-positioned badge\n this.ensurePositioned(stats.hostElement);\n stats.hostElement.appendChild(badge);\n this.badges.set(stats.hostElement, badge);\n }\n\n const isUnnecessary = stats.lastRenderKind === 'unnecessary';\n badge.style.background = isUnnecessary ? '#f44336' : '#ff9800';\n badge.textContent = String(stats.totalRenders);\n badge.title = `${stats.componentName}: ${stats.totalRenders} renders, ${stats.unnecessaryRenders} unnecessary`;\n }\n\n private ensurePositioned(el: Element): void {\n const htmlEl = el as HTMLElement;\n if (getComputedStyle(htmlEl).position === 'static') {\n htmlEl.style.position = 'relative';\n }\n }\n}\n","import type { NgDebugApi } from './types';\n\n/**\n * Returns the Angular debug API (window.ng) if available.\n * Returns null in SSR, production builds, or when Angular DevTools APIs are absent.\n *\n * Safe to call before Angular is bootstrapped — the API is attached to window\n * by Angular's dev mode runtime.\n */\nexport function getNgDebugApi(): NgDebugApi | null {\n if (typeof window === 'undefined') return null;\n\n const ng = (window as unknown as Record<string, unknown>)['ng'];\n if (!ng || typeof ng !== 'object') return null;\n\n const api = ng as Partial<NgDebugApi>;\n\n // ɵsetProfiler is the sentinel for dev mode — absent in production builds\n if (typeof api.ɵsetProfiler !== 'function') return null;\n if (typeof api.getHostElement !== 'function') return null;\n\n return api as NgDebugApi;\n}\n","import {\n Injectable,\n inject,\n isDevMode,\n OnDestroy,\n PLATFORM_ID,\n Injector,\n runInInjectionContext,\n afterEveryRender,\n AfterRenderRef,\n} from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\nimport { ComponentTracker } from './component-tracker';\nimport { OverlayService } from './overlay/overlay.service';\nimport { ANGULAR_SCAN_OPTIONS } from './tokens';\nimport { getNgDebugApi } from './ng-debug';\nimport { ScanConfigService } from './scan-config.service';\nimport type { NgDebugApi, RenderKind } from './types';\n\n// Angular profiler event IDs (stable contract in dev mode, used by Angular DevTools)\nconst TemplateUpdateStart = 2;\nconst ChangeDetectionStart = 12;\nconst ChangeDetectionEnd = 13;\nconst ChangeDetectionSyncStart = 14;\nconst ChangeDetectionSyncEnd = 15;\n\ninterface PendingUpdate {\n instance: object;\n hostElement: Element;\n kind: RenderKind;\n}\n\n@Injectable({ providedIn: 'root' })\nexport class ScannerService implements OnDestroy {\n private readonly tracker = inject(ComponentTracker);\n private readonly overlay = inject(OverlayService);\n private readonly options = inject(ANGULAR_SCAN_OPTIONS);\n private readonly platformId = inject(PLATFORM_ID);\n\n private readonly injector = inject(Injector);\n private readonly config = inject(ScanConfigService);\n\n private removeProfiler: (() => void) | null = null;\n private afterRenderRef: AfterRenderRef | null = null;\n private mutationObserver: MutationObserver | null = null;\n\n // Accumulated during a single CD tick (raw DOM nodes, no signal writes)\n private mutatedNodes = new Set<Node>();\n // Component instances visited during the sync (template update) phase\n private tickInstances = new Set<object>();\n\n // Updates waiting to be flushed in afterEveryRender\n private pendingFlush: PendingUpdate[] = [];\n\n // Tracks which phase of CD we're in\n private inSyncPhase = false;\n private inTick = false;\n\n // The toolbar component instance — excluded from tracking to avoid self-loops\n private toolbarInstance: object | null = null;\n\n initialize(): void {\n if (!isDevMode()) return;\n if (!isPlatformBrowser(this.platformId)) return;\n if (this.options.enabled === false) return;\n\n const ng = getNgDebugApi();\n if (!ng) {\n console.warn(\n '[angular-scan] Angular debug APIs (window.ng) not available. ' +\n 'Ensure you are running in development mode.'\n );\n return;\n }\n\n // afterEveryRender is Angular's safe hook for post-render writes.\n // Signal writes here cause exactly one extra toolbar render pass then stabilize,\n // avoiding the infinite loop that queueMicrotask causes in zone.js + signals hybrid apps.\n this.afterRenderRef = runInInjectionContext(this.injector, () =>\n afterEveryRender({ write: () => this.flushPendingUpdates() })\n );\n\n this.mutationObserver = new MutationObserver(records => {\n for (const r of records) {\n this.mutatedNodes.add(r.target);\n r.addedNodes.forEach(n => this.mutatedNodes.add(n));\n r.removedNodes.forEach(n => this.mutatedNodes.add(n));\n }\n });\n\n this.removeProfiler = ng.ɵsetProfiler((event, instance) => {\n switch (event) {\n case ChangeDetectionStart:\n this.onTickStart();\n break;\n case ChangeDetectionSyncStart:\n this.inSyncPhase = true;\n break;\n case ChangeDetectionSyncEnd:\n this.inSyncPhase = false;\n break;\n case TemplateUpdateStart:\n // Only record during the real update phase, not the dev-mode checkNoChanges pass\n if (this.config.enabled() && this.inSyncPhase && instance && instance !== this.toolbarInstance) {\n this.tickInstances.add(instance);\n }\n break;\n case ChangeDetectionEnd:\n this.onTickEnd(ng);\n break;\n }\n });\n }\n\n /** Exclude a component instance from render tracking (used for the toolbar). */\n setToolbarInstance(instance: object): void {\n this.toolbarInstance = instance;\n }\n\n private onTickStart(): void {\n if (this.inTick) return;\n this.inTick = true;\n this.mutatedNodes.clear();\n this.tickInstances.clear();\n\n this.mutationObserver?.observe(document.body, {\n subtree: true,\n childList: true,\n attributes: true,\n characterData: true,\n });\n }\n\n private onTickEnd(ng: NgDebugApi): void {\n if (!this.inTick) return;\n this.inTick = false;\n this.inSyncPhase = false;\n\n // Synchronously flush pending mutation records before disconnecting\n const pending = this.mutationObserver?.takeRecords() ?? [];\n for (const r of pending) {\n this.mutatedNodes.add(r.target);\n r.addedNodes.forEach(n => this.mutatedNodes.add(n));\n r.removedNodes.forEach(n => this.mutatedNodes.add(n));\n }\n this.mutationObserver?.disconnect();\n\n // Build a set of component host elements that had DOM mutations\n // Walk mutated nodes UP to find their owning component host element\n const mutatedHosts = this.buildMutatedHosts(ng);\n\n // Collect updates as pure data — NO signal writes here (would trigger CD)\n const pendingUpdates: PendingUpdate[] = [];\n\n for (const instance of this.tickInstances) {\n try {\n const hostElement = ng.getHostElement(instance);\n if (!hostElement) continue;\n\n const hadMutation = mutatedHosts.has(hostElement);\n const kind: RenderKind = hadMutation ? 'render' : 'unnecessary';\n\n pendingUpdates.push({ instance, hostElement, kind });\n } catch {\n // Component may have been destroyed during the tick\n }\n }\n\n // Queue for afterEveryRender to flush safely — no direct signal writes here.\n this.pendingFlush.push(...pendingUpdates);\n }\n\n /**\n * Called by afterEveryRender. Drains pendingFlush into signals safely.\n * If there are no pending updates, returns immediately so Angular stabilizes.\n * If there are updates, signals are written → toolbar re-renders (one extra pass) → stabilizes.\n */\n private flushPendingUpdates(): void {\n const updates = this.pendingFlush.splice(0);\n if (updates.length === 0) return;\n\n for (const update of updates) {\n const stats = this.tracker.recordRender(update.instance, update.hostElement, update.kind);\n this.overlay.onComponentChecked(stats);\n }\n this.tracker.snapshotTrackedComponents();\n }\n\n /**\n * Build a Set of host Elements by walking each mutated node up the DOM tree\n * until an Angular component boundary is found.\n * This is O(mutatedNodes × depth) but depth is typically < 20.\n */\n private buildMutatedHosts(ng: NgDebugApi): Set<Element> {\n const hosts = new Set<Element>();\n\n for (const node of this.mutatedNodes) {\n let current: Node | null = node;\n\n while (current && current !== document.body) {\n if (current instanceof Element) {\n try {\n const component = ng.getComponent(current);\n if (component) {\n hosts.add(current);\n break;\n }\n } catch {\n // ignore\n }\n }\n current = current.parentNode;\n }\n }\n\n return hosts;\n }\n\n ngOnDestroy(): void {\n this.removeProfiler?.();\n this.removeProfiler = null;\n this.afterRenderRef?.destroy();\n this.afterRenderRef = null;\n this.mutationObserver?.disconnect();\n this.mutationObserver = null;\n this.pendingFlush = [];\n this.overlay.destroy();\n }\n}\n","import {\n Component,\n ChangeDetectionStrategy,\n inject,\n signal,\n} from '@angular/core';\nimport { ComponentTracker } from '../component-tracker';\nimport { ScanConfigService } from '../scan-config.service';\n\n@Component({\n selector: 'angular-scan-toolbar',\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './toolbar.component.html',\n styleUrls: ['./toolbar.component.scss'],\n host: { '[style.display]': '\"block\"' },\n})\nexport class ToolbarComponent {\n protected readonly tracker = inject(ComponentTracker);\n protected readonly config = inject(ScanConfigService);\n\n protected readonly expanded = signal(false);\n protected readonly settingsOpen = signal(false);\n\n protected toggleExpanded(): void {\n this.expanded.update(v => !v);\n if (this.expanded()) this.settingsOpen.set(false);\n }\n\n protected toggleSettings(): void {\n this.settingsOpen.update(v => !v);\n if (this.settingsOpen()) this.expanded.set(false);\n }\n\n protected resetStats(): void {\n this.tracker.reset();\n }\n\n protected onFlashDurationChange(event: Event): void {\n const value = Number((event.target as HTMLInputElement).value);\n this.config.flashDurationMs.set(value);\n }\n}\n","<div\n class=\"toolbar\"\n role=\"complementary\"\n aria-label=\"Angular Scan DevTools\"\n>\n <div class=\"toolbar__header\">\n <span class=\"toolbar__logo\" aria-hidden=\"true\">◉</span>\n <span class=\"toolbar__title\">angular-scan</span>\n\n <button\n class=\"toolbar__btn\"\n type=\"button\"\n [attr.aria-pressed]=\"!config.enabled()\"\n [title]=\"config.enabled() ? 'Pause scanning' : 'Resume scanning'\"\n (click)=\"config.enabled.update(v => !v)\"\n >{{ config.enabled() ? '⏸' : '▶' }}</button>\n\n <button\n class=\"toolbar__btn\"\n type=\"button\"\n [attr.aria-pressed]=\"settingsOpen()\"\n title=\"Settings\"\n (click)=\"toggleSettings()\"\n >⚙</button>\n\n <button\n class=\"toolbar__btn\"\n type=\"button\"\n [attr.aria-pressed]=\"expanded()\"\n [title]=\"expanded() ? 'Collapse inspector' : 'Expand inspector'\"\n (click)=\"toggleExpanded()\"\n >{{ expanded() ? '▲' : '▼' }}</button>\n </div>\n\n <div class=\"toolbar__stats\">\n <span class=\"toolbar__stat\" title=\"Total change detection checks\">\n <span class=\"toolbar__stat-label\">checks</span>\n <strong class=\"toolbar__stat-value\">{{ tracker.totalRenders() }}</strong>\n </span>\n <span\n class=\"toolbar__stat\"\n [class.toolbar__stat--warn]=\"tracker.totalUnnecessary() > 0\"\n title=\"Unnecessary renders: component was checked but DOM did not change\"\n >\n <span class=\"toolbar__stat-label\">wasted</span>\n <strong class=\"toolbar__stat-value\">{{ tracker.totalUnnecessary() }}</strong>\n </span>\n </div>\n\n @if (settingsOpen()) {\n <div class=\"toolbar__settings\" role=\"group\" aria-label=\"Scan settings\">\n\n <label class=\"toolbar__setting\">\n <span class=\"toolbar__setting-label\">Enable scanning</span>\n <button\n class=\"toolbar__toggle\"\n type=\"button\"\n role=\"switch\"\n [attr.aria-checked]=\"config.enabled()\"\n (click)=\"config.enabled.update(v => !v)\"\n [class.toolbar__toggle--on]=\"config.enabled()\"\n >{{ config.enabled() ? 'ON' : 'OFF' }}</button>\n </label>\n\n <label class=\"toolbar__setting\">\n <span class=\"toolbar__setting-label\">Flash overlay</span>\n <button\n class=\"toolbar__toggle\"\n type=\"button\"\n role=\"switch\"\n [attr.aria-checked]=\"config.showOverlay()\"\n (click)=\"config.showOverlay.update(v => !v)\"\n [class.toolbar__toggle--on]=\"config.showOverlay()\"\n >{{ config.showOverlay() ? 'ON' : 'OFF' }}</button>\n </label>\n\n <label class=\"toolbar__setting\">\n <span class=\"toolbar__setting-label\">Render badges</span>\n <button\n class=\"toolbar__toggle\"\n type=\"button\"\n role=\"switch\"\n [attr.aria-checked]=\"config.showBadges()\"\n (click)=\"config.showBadges.update(v => !v)\"\n [class.toolbar__toggle--on]=\"config.showBadges()\"\n >{{ config.showBadges() ? 'ON' : 'OFF' }}</button>\n </label>\n\n <label class=\"toolbar__setting toolbar__setting--column\">\n <span class=\"toolbar__setting-label\">\n Flash duration\n <span class=\"toolbar__setting-value\">{{ config.flashDurationMs() }}ms</span>\n </span>\n <input\n class=\"toolbar__slider\"\n type=\"range\"\n min=\"100\"\n max=\"2000\"\n step=\"100\"\n [value]=\"config.flashDurationMs()\"\n (input)=\"onFlashDurationChange($event)\"\n aria-label=\"Flash duration in milliseconds\"\n />\n </label>\n\n <button\n class=\"toolbar__reset\"\n type=\"button\"\n title=\"Clear all render stats\"\n (click)=\"resetStats()\"\n >↺ Reset stats</button>\n\n </div>\n }\n\n @if (expanded()) {\n <div\n class=\"toolbar__inspector\"\n role=\"list\"\n aria-label=\"Component render counts\"\n >\n @for (comp of tracker.trackedComponents(); track comp.hostElement) {\n <div\n class=\"toolbar__component\"\n role=\"listitem\"\n [class.toolbar__component--warn]=\"comp.lastRenderKind === 'unnecessary'\"\n [title]=\"comp.componentName + ' — ' + comp.totalRenders + ' checks, ' + comp.unnecessaryRenders + ' wasted'\"\n >\n <span class=\"toolbar__component-name\">{{ comp.componentName }}</span>\n <span class=\"toolbar__component-count\">{{ comp.totalRenders }}</span>\n @if (comp.unnecessaryRenders > 0) {\n <span class=\"toolbar__component-wasted\" aria-label=\"wasted renders\">\n {{ comp.unnecessaryRenders }}W\n </span>\n }\n </div>\n }\n </div>\n }\n</div>\n","import {\n EnvironmentProviders,\n makeEnvironmentProviders,\n isDevMode,\n provideEnvironmentInitializer,\n inject,\n ApplicationRef,\n createComponent,\n EnvironmentInjector,\n} from '@angular/core';\nimport { DOCUMENT } from '@angular/common';\nimport { ScannerService } from './scanner.service';\nimport { OverlayService } from './overlay/overlay.service';\nimport { ToolbarComponent } from './toolbar/toolbar.component';\nimport { ANGULAR_SCAN_OPTIONS } from './tokens';\nimport type { AngularScanOptions } from './types';\n\n/**\n * Provides angular-scan for development-time render tracking.\n *\n * Add to your application providers:\n * ```ts\n * bootstrapApplication(AppComponent, {\n * providers: [provideAngularScan()]\n * });\n * ```\n *\n * This is a complete no-op in production builds (`isDevMode() === false`),\n * so it can safely be left in your app config.\n */\nexport function provideAngularScan(options: AngularScanOptions = {}): EnvironmentProviders {\n // Return empty providers in production — entire library is tree-shaken\n if (!isDevMode()) {\n return makeEnvironmentProviders([]);\n }\n\n const resolvedOptions: AngularScanOptions = {\n enabled: true,\n flashDurationMs: 500,\n showBadges: true,\n showToolbar: true,\n ...options,\n };\n\n return makeEnvironmentProviders([\n {\n provide: ANGULAR_SCAN_OPTIONS,\n useValue: resolvedOptions,\n },\n provideEnvironmentInitializer(() => {\n const overlay = inject(OverlayService);\n const scanner = inject(ScannerService);\n const opts = inject(ANGULAR_SCAN_OPTIONS);\n\n overlay.initialize();\n scanner.initialize();\n\n if (opts.showToolbar !== false) {\n const injector = inject(EnvironmentInjector);\n const doc = inject(DOCUMENT);\n const appRef = inject(ApplicationRef);\n\n // Create the toolbar outside Angular's component tree so it doesn't\n // appear in CD tracking. Attach it to ApplicationRef so signals work.\n const toolbarRef = createComponent(ToolbarComponent, {\n environmentInjector: injector,\n });\n\n doc.body.appendChild(toolbarRef.location.nativeElement);\n appRef.attachView(toolbarRef.hostView);\n\n // Tell the scanner to ignore the toolbar's own renders\n scanner.setToolbarInstance(toolbarRef.instance);\n }\n }),\n ]);\n}\n","import { isDevMode } from '@angular/core';\nimport { getNgDebugApi } from './ng-debug';\nimport { CanvasOverlay } from './overlay/canvas-overlay';\nimport type { AngularScanOptions, RenderKind } from './types';\n\n// Profiler event constants\nconst TemplateUpdateStart = 2;\nconst ChangeDetectionStart = 12;\nconst ChangeDetectionEnd = 13;\nconst ChangeDetectionSyncStart = 14;\nconst ChangeDetectionSyncEnd = 15;\n\n/**\n * Imperative API for angular-scan.\n *\n * Use this when you cannot modify application providers (e.g. micro-frontends,\n * third-party apps). Call before `bootstrapApplication()`.\n *\n * ```ts\n * // main.ts\n * import { scan } from 'angular-scan';\n * scan();\n * bootstrapApplication(AppComponent, appConfig);\n * ```\n *\n * @returns A teardown function that stops scanning and removes the overlay.\n */\nexport function scan(options: AngularScanOptions = {}): () => void {\n if (!isDevMode()) return noop;\n if (typeof window === 'undefined') return noop;\n if (options.enabled === false) return noop;\n\n const ng = getNgDebugApi();\n if (!ng) {\n console.warn(\n '[angular-scan] Angular debug APIs (window.ng) not available. ' +\n 'Ensure you are running in development mode.'\n );\n return noop;\n }\n\n const durationMs = options.flashDurationMs ?? 500;\n const showBadges = options.showBadges !== false;\n\n const overlay = new CanvasOverlay();\n overlay.attach(document);\n\n // State for the current tick\n let inTick = false;\n let inSyncPhase = false;\n const mutatedNodes = new Set<Node>();\n const tickInstances = new Set<object>();\n const renderCounts = new WeakMap<object, number>();\n const unnecessaryCounts = new WeakMap<object, number>();\n const badges = new Map<Element, HTMLElement>();\n\n const observer = new MutationObserver(records => {\n for (const r of records) {\n mutatedNodes.add(r.target);\n r.addedNodes.forEach(n => mutatedNodes.add(n));\n r.removedNodes.forEach(n => mutatedNodes.add(n));\n }\n });\n\n const removeProfiler = ng.ɵsetProfiler((event, instance) => {\n switch (event) {\n case ChangeDetectionStart:\n if (inTick) break;\n inTick = true;\n mutatedNodes.clear();\n tickInstances.clear();\n observer.observe(document.body, {\n subtree: true, childList: true, attributes: true, characterData: true,\n });\n break;\n\n case ChangeDetectionSyncStart:\n inSyncPhase = true;\n break;\n\n case ChangeDetectionSyncEnd:\n inSyncPhase = false;\n break;\n\n case TemplateUpdateStart:\n if (inSyncPhase && instance) tickInstances.add(instance);\n break;\n\n case ChangeDetectionEnd: {\n if (!inTick) break;\n inTick = false;\n inSyncPhase = false;\n\n const pending = observer.takeRecords();\n for (const r of pending) {\n mutatedNodes.add(r.target);\n r.addedNodes.forEach(n => mutatedNodes.add(n));\n r.removedNodes.forEach(n => mutatedNodes.add(n));\n }\n observer.disconnect();\n\n // Build mutated host set\n const mutatedHosts = new Set<Element>();\n for (const node of mutatedNodes) {\n let current: Node | null = node;\n while (current && current !== document.body) {\n if (current instanceof Element) {\n try {\n if (ng.getComponent(current)) { mutatedHosts.add(current); break; }\n } catch { /* ignore */ }\n }\n current = current.parentNode;\n }\n }\n\n // Collect updates without writing to any signals\n const updates: Array<{ instance: object; hostEl: Element; kind: RenderKind }> = [];\n for (const inst of tickInstances) {\n try {\n const hostEl = ng.getHostElement(inst);\n if (!hostEl) continue;\n const kind: RenderKind = mutatedHosts.has(hostEl) ? 'render' : 'unnecessary';\n updates.push({ instance: inst, hostEl, kind });\n } catch { /* component destroyed */ }\n }\n\n queueMicrotask(() => {\n for (const { instance, hostEl, kind } of updates) {\n const count = (renderCounts.get(instance) ?? 0) + 1;\n renderCounts.set(instance, count);\n\n if (kind === 'unnecessary') {\n unnecessaryCounts.set(instance, (unnecessaryCounts.get(instance) ?? 0) + 1);\n }\n\n overlay.flash(hostEl, kind, durationMs);\n\n if (showBadges) updateBadge(hostEl, count, kind);\n }\n });\n break;\n }\n }\n });\n\n function updateBadge(hostEl: Element, count: number, kind: RenderKind): void {\n let badge = badges.get(hostEl);\n if (!badge) {\n badge = document.createElement('div');\n badge.setAttribute('aria-hidden', 'true');\n badge.setAttribute('role', 'presentation');\n badge.style.cssText = [\n 'position:absolute', 'top:2px', 'right:2px',\n 'z-index:2147483645', 'pointer-events:none',\n 'font:bold 9px/13px monospace', 'padding:1px 4px',\n 'border-radius:3px', 'min-width:16px', 'text-align:center', 'color:#fff',\n ].join(';');\n const htmlEl = hostEl as HTMLElement;\n if (getComputedStyle(htmlEl).position === 'static') htmlEl.style.position = 'relative';\n hostEl.appendChild(badge);\n badges.set(hostEl, badge);\n }\n badge.style.background = kind === 'unnecessary' ? '#f44336' : '#ff9800';\n badge.textContent = String(count);\n }\n\n return () => {\n removeProfiler();\n observer.disconnect();\n overlay.detach();\n for (const b of badges.values()) b.remove();\n badges.clear();\n };\n}\n\nfunction noop(): void {}\n","/*\n * Public API Surface of angular-scan\n */\n\nexport { provideAngularScan } from './lib/provide-angular-scan';\nexport { scan } from './lib/scan';\nexport type { AngularScanOptions, ComponentStats, RenderKind } from './lib/types';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["TemplateUpdateStart","ChangeDetectionStart","ChangeDetectionEnd","ChangeDetectionSyncStart","ChangeDetectionSyncEnd","DOCUMENT"],"mappings":";;;;MAIa,gBAAgB,CAAA;;;AAGV,IAAA,UAAU,GAAG,IAAI,OAAO,EAA0B;;;AAIlD,IAAA,SAAS,GAAG,IAAI,GAAG,EAAmB;AAEtC,IAAA,aAAa,GAAG,MAAM,CAAC,CAAC,yDAAC;AACzB,IAAA,iBAAiB,GAAG,MAAM,CAAC,CAAC,6DAAC;AAC7B,IAAA,kBAAkB,GAAG,MAAM,CAA4B,EAAE,8DAAC;AAElE,IAAA,YAAY,GAAG,IAAI,CAAC,aAAa,CAAC,UAAU,EAAE;AAC9C,IAAA,gBAAgB,GAAG,IAAI,CAAC,iBAAiB,CAAC,UAAU,EAAE;AACtD,IAAA,iBAAiB,GAAG,IAAI,CAAC,kBAAkB,CAAC,UAAU,EAAE;AAEjE;;;;AAIG;AACH,IAAA,YAAY,CAAC,QAAgB,EAAE,WAAoB,EAAE,IAAgB,EAAA;QACnE,IAAI,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC;QAEzC,IAAI,CAAC,KAAK,EAAE;AACV,YAAA,KAAK,GAAG;AACN,gBAAA,aAAa,EAAE,QAAQ,CAAC,WAAW,CAAC,IAAI;gBACxC,WAAW;AACX,gBAAA,YAAY,EAAE,CAAC;AACf,gBAAA,kBAAkB,EAAE,CAAC;AACrB,gBAAA,cAAc,EAAE,IAAI;AACpB,gBAAA,mBAAmB,EAAE,CAAC;aACvB;YACD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC;YACpC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,EAAE,QAAQ,CAAC;QAC3C;QAEA,KAAK,CAAC,YAAY,EAAE;AACpB,QAAA,KAAK,CAAC,cAAc,GAAG,IAAI;AAC3B,QAAA,KAAK,CAAC,mBAAmB,GAAG,IAAI,CAAC,GAAG,EAAE;AAEtC,QAAA,IAAI,IAAI,KAAK,aAAa,EAAE;YAC1B,KAAK,CAAC,kBAAkB,EAAE;AAC1B,YAAA,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC3C;AAEA,QAAA,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACrC,QAAA,OAAO,KAAK;IACd;AAEA;;;AAGG;IACH,yBAAyB,GAAA;QACvB,MAAM,IAAI,GAAqB,EAAE;QACjC,KAAK,MAAM,GAAG,QAAQ,CAAC,IAAI,IAAI,CAAC,SAAS,EAAE;YACzC,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC;AAC3C,YAAA,IAAI,KAAK;AAAE,gBAAA,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;QAC7B;AACA,QAAA,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC;IACnC;;AAGA,IAAA,UAAU,CAAC,EAAW,EAAA;AACpB,QAAA,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;IAC3B;IAEA,KAAK,GAAA;AACH,QAAA,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE;AACtB,QAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;AACzB,QAAA,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC;AAC7B,QAAA,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC;IACjC;uGA1EW,gBAAgB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;AAAhB,IAAA,OAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,gBAAgB,cADH,MAAM,EAAA,CAAA;;2FACnB,gBAAgB,EAAA,UAAA,EAAA,CAAA;kBAD5B,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE;;;ACSlC;AACA,MAAM,YAAY,GAAG,aAAa,CAAC;AACnC,MAAM,iBAAiB,GAAG,aAAa,CAAC;MAE3B,aAAa,CAAA;IAChB,MAAM,GAA6B,IAAI;IACvC,GAAG,GAAoC,IAAI;IAC3C,KAAK,GAAgB,EAAE;IACvB,KAAK,GAAG,CAAC;IACT,QAAQ,GAAG,KAAK;AAExB,IAAA,MAAM,CAAC,GAAa,EAAA;QAClB,IAAI,IAAI,CAAC,QAAQ;YAAE;QAEnB,MAAM,MAAM,GAAG,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC;AAC1C,QAAA,MAAM,CAAC,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC;AAC1C,QAAA,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,cAAc,CAAC;AAC3C,QAAA,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG;YACrB,gBAAgB;YAChB,OAAO;YACP,QAAQ;YACR,YAAY;YACZ,aAAa;YACb,qBAAqB;YACrB,CAAA,kBAAA,CAAoB;AACrB,SAAA,CAAC,IAAI,CAAC,GAAG,CAAC;AAEX,QAAA,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;AAC5B,QAAA,IAAI,CAAC,MAAM,GAAG,MAAM;QACpB,IAAI,CAAC,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC;AAClC,QAAA,IAAI,CAAC,QAAQ,GAAG,IAAI;QAEpB,IAAI,CAAC,MAAM,EAAE;QACb,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC;QAEhD,IAAI,CAAC,aAAa,EAAE;IACtB;IAEA,MAAM,GAAA;QACJ,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE;AACpB,QAAA,IAAI,CAAC,QAAQ,GAAG,KAAK;AACrB,QAAA,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC;QAChC,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC;AACnD,QAAA,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE;AACrB,QAAA,IAAI,CAAC,MAAM,GAAG,IAAI;AAClB,QAAA,IAAI,CAAC,GAAG,GAAG,IAAI;AACf,QAAA,IAAI,CAAC,KAAK,GAAG,EAAE;IACjB;;AAGA,IAAA,KAAK,CAAC,OAAgB,EAAE,IAAgB,EAAE,UAAkB,EAAA;QAC1D,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE;AAEpB,QAAA,MAAM,IAAI,GAAG,OAAO,CAAC,qBAAqB,EAAE;QAC5C,IAAI,IAAI,CAAC,KAAK,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE;AAE3C,QAAA,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;YACd,CAAC,EAAE,IAAI,CAAC,IAAI;YACZ,CAAC,EAAE,IAAI,CAAC,GAAG;YACX,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,IAAI;AACJ,YAAA,SAAS,EAAE,WAAW,CAAC,GAAG,EAAE;YAC5B,UAAU;AACX,SAAA,CAAC;IACJ;IAEiB,QAAQ,GAAG,MAAY,IAAI,CAAC,MAAM,EAAE;IAE7C,MAAM,GAAA;QACZ,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE;QAClB,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,UAAU;QACrC,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,WAAW;IACzC;IAEQ,aAAa,GAAA;QACnB,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE;QACpB,IAAI,CAAC,KAAK,GAAG,qBAAqB,CAAC,IAAI,CAAC,SAAS,CAAC;IACpD;AAEiB,IAAA,SAAS,GAAG,CAAC,GAAW,KAAU;AACjD,QAAA,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE;QAEjD,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;;QAG/D,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAG;AACjC,YAAA,MAAM,OAAO,GAAG,GAAG,GAAG,CAAC,CAAC,SAAS;AACjC,YAAA,OAAO,OAAO,GAAG,CAAC,CAAC,UAAU;AAC/B,QAAA,CAAC,CAAC;AAEF,QAAA,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE;AAC1B,YAAA,MAAM,OAAO,GAAG,GAAG,GAAG,CAAC,CAAC,SAAS;AACjC,YAAA,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,OAAO,GAAG,CAAC,CAAC,UAAU,CAAC;AACrD,YAAA,MAAM,KAAK,GAAG,CAAC,CAAC,IAAI,KAAK,QAAQ,GAAG,YAAY,GAAG,iBAAiB;;AAGpE,YAAA,IAAI,CAAC,GAAI,CAAC,SAAS,GAAG,CAAA,KAAA,EAAQ,KAAK,CAAA,EAAA,EAAK,KAAK,GAAG,GAAG,CAAA,CAAA,CAAG;YACtD,IAAI,CAAC,GAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC;;AAG/C,YAAA,IAAI,CAAC,GAAI,CAAC,WAAW,GAAG,CAAA,KAAA,EAAQ,KAAK,CAAA,EAAA,EAAK,KAAK,GAAG,GAAG,CAAA,CAAA,CAAG;AACxD,YAAA,IAAI,CAAC,GAAI,CAAC,SAAS,GAAG,CAAC;AACvB,YAAA,IAAI,CAAC,GAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;QACnE;QAEA,IAAI,CAAC,aAAa,EAAE;AACtB,IAAA,CAAC;AACF;;ACrHM,MAAM,oBAAoB,GAAG,IAAI,cAAc,CACpD,sBAAsB,EACtB,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,CAC5C;;MCFY,iBAAiB,CAAA;AACX,IAAA,OAAO,GAAG,MAAM,CAAC,oBAAoB,CAAC;IAE9C,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,KAAK,KAAK,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,SAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAC;AAChD,IAAA,WAAW,GAAG,MAAM,CAAC,IAAI,uDAAC;IAC1B,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,KAAK,KAAK,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,YAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAC;IACtD,eAAe,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,IAAI,GAAG,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,iBAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAC;uGAN3D,iBAAiB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;AAAjB,IAAA,OAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,iBAAiB,cADJ,MAAM,EAAA,CAAA;;2FACnB,iBAAiB,EAAA,UAAA,EAAA,CAAA;kBAD7B,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE;;;MCKrB,cAAc,CAAA;AACR,IAAA,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;AAC3B,IAAA,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC;AAChC,IAAA,OAAO,GAAG,MAAM,CAAC,oBAAoB,CAAC;AACtC,IAAA,MAAM,GAAG,MAAM,CAAC,iBAAiB,CAAC;AAClC,IAAA,MAAM,GAAG,IAAI,aAAa,EAAE;;AAG5B,IAAA,MAAM,GAAG,IAAI,GAAG,EAAwB;IAEzD,UAAU,GAAA;QACR,IAAI,CAAC,SAAS,EAAE;YAAE;AAClB,QAAA,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,UAAU,CAAC;YAAE;AACzC,QAAA,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,KAAK,KAAK;YAAE;QAEpC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC;IACnC;IAEA,OAAO,GAAA;AACL,QAAA,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE;QACpB,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE;YAAE,KAAK,CAAC,MAAM,EAAE;AACxD,QAAA,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE;IACrB;AAEA,IAAA,kBAAkB,CAAC,KAAqB,EAAA;AACtC,QAAA,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,EAAE;YAC7B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,cAAc,EAAE,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC;QAC3F;AAEA,QAAA,IAAI,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE;AAC5B,YAAA,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC;QACzB;aAAO;AACL,YAAA,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,WAAW,CAAC;QACrC;IACF;AAEA,IAAA,WAAW,CAAC,EAAW,EAAA;QACrB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE;AAC7B,QAAA,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;IACxB;AAEQ,IAAA,WAAW,CAAC,KAAqB,EAAA;AACvC,QAAA,IAAI,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC;QAE9C,IAAI,CAAC,KAAK,EAAE;YACV,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC;AAC1C,YAAA,KAAK,CAAC,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC;AACzC,YAAA,KAAK,CAAC,YAAY,CAAC,MAAM,EAAE,cAAc,CAAC;AAC1C,YAAA,KAAK,CAAC,KAAK,CAAC,OAAO,GAAG;gBACpB,mBAAmB;gBACnB,SAAS;gBACT,WAAW;gBACX,oBAAoB;gBACpB,qBAAqB;gBACrB,8BAA8B;gBAC9B,iBAAiB;gBACjB,mBAAmB;gBACnB,gBAAgB;gBAChB,mBAAmB;gBACnB,YAAY;gBACZ,oBAAoB;AACrB,aAAA,CAAC,IAAI,CAAC,GAAG,CAAC;;AAGX,YAAA,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,WAAW,CAAC;AACxC,YAAA,KAAK,CAAC,WAAW,CAAC,WAAW,CAAC,KAAK,CAAC;YACpC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC;QAC3C;AAEA,QAAA,MAAM,aAAa,GAAG,KAAK,CAAC,cAAc,KAAK,aAAa;AAC5D,QAAA,KAAK,CAAC,KAAK,CAAC,UAAU,GAAG,aAAa,GAAG,SAAS,GAAG,SAAS;QAC9D,KAAK,CAAC,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC;AAC9C,QAAA,KAAK,CAAC,KAAK,GAAG,CAAA,EAAG,KAAK,CAAC,aAAa,CAAA,EAAA,EAAK,KAAK,CAAC,YAAY,CAAA,UAAA,EAAa,KAAK,CAAC,kBAAkB,cAAc;IAChH;AAEQ,IAAA,gBAAgB,CAAC,EAAW,EAAA;QAClC,MAAM,MAAM,GAAG,EAAiB;QAChC,IAAI,gBAAgB,CAAC,MAAM,CAAC,CAAC,QAAQ,KAAK,QAAQ,EAAE;AAClD,YAAA,MAAM,CAAC,KAAK,CAAC,QAAQ,GAAG,UAAU;QACpC;IACF;uGAhFW,cAAc,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;AAAd,IAAA,OAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,cAAc,cADD,MAAM,EAAA,CAAA;;2FACnB,cAAc,EAAA,UAAA,EAAA,CAAA;kBAD1B,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE;;;ACLlC;;;;;;AAMG;SACa,aAAa,GAAA;IAC3B,IAAI,OAAO,MAAM,KAAK,WAAW;AAAE,QAAA,OAAO,IAAI;AAE9C,IAAA,MAAM,EAAE,GAAI,MAA6C,CAAC,IAAI,CAAC;AAC/D,IAAA,IAAI,CAAC,EAAE,IAAI,OAAO,EAAE,KAAK,QAAQ;AAAE,QAAA,OAAO,IAAI;IAE9C,MAAM,GAAG,GAAG,EAAyB;;AAGrC,IAAA,IAAI,OAAO,GAAG,CAAC,YAAY,KAAK,UAAU;AAAE,QAAA,OAAO,IAAI;AACvD,IAAA,IAAI,OAAO,GAAG,CAAC,cAAc,KAAK,UAAU;AAAE,QAAA,OAAO,IAAI;AAEzD,IAAA,OAAO,GAAiB;AAC1B;;ACHA;AACA,MAAMA,qBAAmB,GAAG,CAAC;AAC7B,MAAMC,sBAAoB,GAAG,EAAE;AAC/B,MAAMC,oBAAkB,GAAG,EAAE;AAC7B,MAAMC,0BAAwB,GAAG,EAAE;AACnC,MAAMC,wBAAsB,GAAG,EAAE;MASpB,cAAc,CAAA;AACR,IAAA,OAAO,GAAG,MAAM,CAAC,gBAAgB,CAAC;AAClC,IAAA,OAAO,GAAG,MAAM,CAAC,cAAc,CAAC;AAChC,IAAA,OAAO,GAAG,MAAM,CAAC,oBAAoB,CAAC;AACtC,IAAA,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC;AAEhC,IAAA,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;AAC3B,IAAA,MAAM,GAAG,MAAM,CAAC,iBAAiB,CAAC;IAE3C,cAAc,GAAwB,IAAI;IAC1C,cAAc,GAA0B,IAAI;IAC5C,gBAAgB,GAA4B,IAAI;;AAGhD,IAAA,YAAY,GAAG,IAAI,GAAG,EAAQ;;AAE9B,IAAA,aAAa,GAAG,IAAI,GAAG,EAAU;;IAGjC,YAAY,GAAoB,EAAE;;IAGlC,WAAW,GAAG,KAAK;IACnB,MAAM,GAAG,KAAK;;IAGd,eAAe,GAAkB,IAAI;IAE7C,UAAU,GAAA;QACR,IAAI,CAAC,SAAS,EAAE;YAAE;AAClB,QAAA,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,UAAU,CAAC;YAAE;AACzC,QAAA,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,KAAK,KAAK;YAAE;AAEpC,QAAA,MAAM,EAAE,GAAG,aAAa,EAAE;QAC1B,IAAI,CAAC,EAAE,EAAE;YACP,OAAO,CAAC,IAAI,CACV,+DAA+D;AAC/D,gBAAA,6CAA6C,CAC9C;YACD;QACF;;;;QAKA,IAAI,CAAC,cAAc,GAAG,qBAAqB,CAAC,IAAI,CAAC,QAAQ,EAAE,MACzD,gBAAgB,CAAC,EAAE,KAAK,EAAE,MAAM,IAAI,CAAC,mBAAmB,EAAE,EAAE,CAAC,CAC9D;QAED,IAAI,CAAC,gBAAgB,GAAG,IAAI,gBAAgB,CAAC,OAAO,IAAG;AACrD,YAAA,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE;gBACvB,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC;AAC/B,gBAAA,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,IAAI,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACnD,gBAAA,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,IAAI,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACvD;AACF,QAAA,CAAC,CAAC;AAEF,QAAA,IAAI,CAAC,cAAc,GAAG,EAAE,CAAC,YAAY,CAAC,CAAC,KAAK,EAAE,QAAQ,KAAI;YACxD,QAAQ,KAAK;AACX,gBAAA,KAAKH,sBAAoB;oBACvB,IAAI,CAAC,WAAW,EAAE;oBAClB;AACF,gBAAA,KAAKE,0BAAwB;AAC3B,oBAAA,IAAI,CAAC,WAAW,GAAG,IAAI;oBACvB;AACF,gBAAA,KAAKC,wBAAsB;AACzB,oBAAA,IAAI,CAAC,WAAW,GAAG,KAAK;oBACxB;AACF,gBAAA,KAAKJ,qBAAmB;;AAEtB,oBAAA,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,IAAI,CAAC,WAAW,IAAI,QAAQ,IAAI,QAAQ,KAAK,IAAI,CAAC,eAAe,EAAE;AAC9F,wBAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC;oBAClC;oBACA;AACF,gBAAA,KAAKE,oBAAkB;AACrB,oBAAA,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;oBAClB;;AAEN,QAAA,CAAC,CAAC;IACJ;;AAGA,IAAA,kBAAkB,CAAC,QAAgB,EAAA;AACjC,QAAA,IAAI,CAAC,eAAe,GAAG,QAAQ;IACjC;IAEQ,WAAW,GAAA;QACjB,IAAI,IAAI,CAAC,MAAM;YAAE;AACjB,QAAA,IAAI,CAAC,MAAM,GAAG,IAAI;AAClB,QAAA,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE;AACzB,QAAA,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE;QAE1B,IAAI,CAAC,gBAAgB,EAAE,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE;AAC5C,YAAA,OAAO,EAAE,IAAI;AACb,YAAA,SAAS,EAAE,IAAI;AACf,YAAA,UAAU,EAAE,IAAI;AAChB,YAAA,aAAa,EAAE,IAAI;AACpB,SAAA,CAAC;IACJ;AAEQ,IAAA,SAAS,CAAC,EAAc,EAAA;QAC9B,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE;AAClB,QAAA,IAAI,CAAC,MAAM,GAAG,KAAK;AACnB,QAAA,IAAI,CAAC,WAAW,GAAG,KAAK;;QAGxB,MAAM,OAAO,GAAG,IAAI,CAAC,gBAAgB,EAAE,WAAW,EAAE,IAAI,EAAE;AAC1D,QAAA,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE;YACvB,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC;AAC/B,YAAA,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,IAAI,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACnD,YAAA,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,IAAI,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACvD;AACA,QAAA,IAAI,CAAC,gBAAgB,EAAE,UAAU,EAAE;;;QAInC,MAAM,YAAY,GAAG,IAAI,CAAC,iBAAiB,CAAC,EAAE,CAAC;;QAG/C,MAAM,cAAc,GAAoB,EAAE;AAE1C,QAAA,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,aAAa,EAAE;AACzC,YAAA,IAAI;gBACF,MAAM,WAAW,GAAG,EAAE,CAAC,cAAc,CAAC,QAAQ,CAAC;AAC/C,gBAAA,IAAI,CAAC,WAAW;oBAAE;gBAElB,MAAM,WAAW,GAAG,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC;gBACjD,MAAM,IAAI,GAAe,WAAW,GAAG,QAAQ,GAAG,aAAa;gBAE/D,cAAc,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;YACtD;AAAE,YAAA,MAAM;;YAER;QACF;;QAGA,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,cAAc,CAAC;IAC3C;AAEA;;;;AAIG;IACK,mBAAmB,GAAA;QACzB,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC;AAC3C,QAAA,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE;AAE1B,QAAA,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE;YAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC;AACzF,YAAA,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC,KAAK,CAAC;QACxC;AACA,QAAA,IAAI,CAAC,OAAO,CAAC,yBAAyB,EAAE;IAC1C;AAEA;;;;AAIG;AACK,IAAA,iBAAiB,CAAC,EAAc,EAAA;AACtC,QAAA,MAAM,KAAK,GAAG,IAAI,GAAG,EAAW;AAEhC,QAAA,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,YAAY,EAAE;YACpC,IAAI,OAAO,GAAgB,IAAI;YAE/B,OAAO,OAAO,IAAI,OAAO,KAAK,QAAQ,CAAC,IAAI,EAAE;AAC3C,gBAAA,IAAI,OAAO,YAAY,OAAO,EAAE;AAC9B,oBAAA,IAAI;wBACF,MAAM,SAAS,GAAG,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC;wBAC1C,IAAI,SAAS,EAAE;AACb,4BAAA,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC;4BAClB;wBACF;oBACF;AAAE,oBAAA,MAAM;;oBAER;gBACF;AACA,gBAAA,OAAO,GAAG,OAAO,CAAC,UAAU;YAC9B;QACF;AAEA,QAAA,OAAO,KAAK;IACd;IAEA,WAAW,GAAA;AACT,QAAA,IAAI,CAAC,cAAc,IAAI;AACvB,QAAA,IAAI,CAAC,cAAc,GAAG,IAAI;AAC1B,QAAA,IAAI,CAAC,cAAc,EAAE,OAAO,EAAE;AAC9B,QAAA,IAAI,CAAC,cAAc,GAAG,IAAI;AAC1B,QAAA,IAAI,CAAC,gBAAgB,EAAE,UAAU,EAAE;AACnC,QAAA,IAAI,CAAC,gBAAgB,GAAG,IAAI;AAC5B,QAAA,IAAI,CAAC,YAAY,GAAG,EAAE;AACtB,QAAA,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE;IACxB;uGAlMW,cAAc,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;AAAd,IAAA,OAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,cAAc,cADD,MAAM,EAAA,CAAA;;2FACnB,cAAc,EAAA,UAAA,EAAA,CAAA;kBAD1B,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE;;;MChBrB,gBAAgB,CAAA;AACR,IAAA,OAAO,GAAG,MAAM,CAAC,gBAAgB,CAAC;AAClC,IAAA,MAAM,GAAG,MAAM,CAAC,iBAAiB,CAAC;AAElC,IAAA,QAAQ,GAAG,MAAM,CAAC,KAAK,oDAAC;AACxB,IAAA,YAAY,GAAG,MAAM,CAAC,KAAK,wDAAC;IAErC,cAAc,GAAA;AACtB,QAAA,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAC7B,IAAI,IAAI,CAAC,QAAQ,EAAE;AAAE,YAAA,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC;IACnD;IAEU,cAAc,GAAA;AACtB,QAAA,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACjC,IAAI,IAAI,CAAC,YAAY,EAAE;AAAE,YAAA,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC;IACnD;IAEU,UAAU,GAAA;AAClB,QAAA,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE;IACtB;AAEU,IAAA,qBAAqB,CAAC,KAAY,EAAA;QAC1C,MAAM,KAAK,GAAG,MAAM,CAAE,KAAK,CAAC,MAA2B,CAAC,KAAK,CAAC;QAC9D,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC;IACxC;uGAxBW,gBAAgB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAhB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,gBAAgB,wIChB7B,o1JA4IA,EAAA,MAAA,EAAA,CAAA,ytGAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FD5Ha,gBAAgB,EAAA,UAAA,EAAA,CAAA;kBAP5B,SAAS;+BACE,sBAAsB,EAAA,eAAA,EACf,uBAAuB,CAAC,MAAM,QAGzC,EAAE,iBAAiB,EAAE,SAAS,EAAE,EAAA,QAAA,EAAA,o1JAAA,EAAA,MAAA,EAAA,CAAA,ytGAAA,CAAA,EAAA;;;AEGxC;;;;;;;;;;;;AAYG;AACG,SAAU,kBAAkB,CAAC,OAAA,GAA8B,EAAE,EAAA;;AAEjE,IAAA,IAAI,CAAC,SAAS,EAAE,EAAE;AAChB,QAAA,OAAO,wBAAwB,CAAC,EAAE,CAAC;IACrC;AAEA,IAAA,MAAM,eAAe,GAAuB;AAC1C,QAAA,OAAO,EAAE,IAAI;AACb,QAAA,eAAe,EAAE,GAAG;AACpB,QAAA,UAAU,EAAE,IAAI;AAChB,QAAA,WAAW,EAAE,IAAI;AACjB,QAAA,GAAG,OAAO;KACX;AAED,IAAA,OAAO,wBAAwB,CAAC;AAC9B,QAAA;AACE,YAAA,OAAO,EAAE,oBAAoB;AAC7B,YAAA,QAAQ,EAAE,eAAe;AAC1B,SAAA;QACD,6BAA6B,CAAC,MAAK;AAC/B,YAAA,MAAM,OAAO,GAAG,MAAM,CAAC,cAAc,CAAC;AACtC,YAAA,MAAM,OAAO,GAAG,MAAM,CAAC,cAAc,CAAC;AACtC,YAAA,MAAM,IAAI,GAAG,MAAM,CAAC,oBAAoB,CAAC;YAEzC,OAAO,CAAC,UAAU,EAAE;YACpB,OAAO,CAAC,UAAU,EAAE;AAEpB,YAAA,IAAI,IAAI,CAAC,WAAW,KAAK,KAAK,EAAE;AAC9B,gBAAA,MAAM,QAAQ,GAAG,MAAM,CAAC,mBAAmB,CAAC;AAC5C,gBAAA,MAAM,GAAG,GAAG,MAAM,CAACG,UAAQ,CAAC;AAC5B,gBAAA,MAAM,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC;;;AAIrC,gBAAA,MAAM,UAAU,GAAG,eAAe,CAAC,gBAAgB,EAAE;AACnD,oBAAA,mBAAmB,EAAE,QAAQ;AAC9B,iBAAA,CAAC;gBAEF,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,QAAQ,CAAC,aAAa,CAAC;AACvD,gBAAA,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,QAAQ,CAAC;;AAGtC,gBAAA,OAAO,CAAC,kBAAkB,CAAC,UAAU,CAAC,QAAQ,CAAC;YACjD;AACJ,QAAA,CAAC,CAAC;AACH,KAAA,CAAC;AACJ;;ACvEA;AACA,MAAM,mBAAmB,GAAG,CAAC;AAC7B,MAAM,oBAAoB,GAAG,EAAE;AAC/B,MAAM,kBAAkB,GAAG,EAAE;AAC7B,MAAM,wBAAwB,GAAG,EAAE;AACnC,MAAM,sBAAsB,GAAG,EAAE;AAEjC;;;;;;;;;;;;;;AAcG;AACG,SAAU,IAAI,CAAC,OAAA,GAA8B,EAAE,EAAA;IACnD,IAAI,CAAC,SAAS,EAAE;AAAE,QAAA,OAAO,IAAI;IAC7B,IAAI,OAAO,MAAM,KAAK,WAAW;AAAE,QAAA,OAAO,IAAI;AAC9C,IAAA,IAAI,OAAO,CAAC,OAAO,KAAK,KAAK;AAAE,QAAA,OAAO,IAAI;AAE1C,IAAA,MAAM,EAAE,GAAG,aAAa,EAAE;IAC1B,IAAI,CAAC,EAAE,EAAE;QACP,OAAO,CAAC,IAAI,CACV,+DAA+D;AAC/D,YAAA,6CAA6C,CAC9C;AACD,QAAA,OAAO,IAAI;IACb;AAEA,IAAA,MAAM,UAAU,GAAG,OAAO,CAAC,eAAe,IAAI,GAAG;AACjD,IAAA,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,KAAK,KAAK;AAE/C,IAAA,MAAM,OAAO,GAAG,IAAI,aAAa,EAAE;AACnC,IAAA,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC;;IAGxB,IAAI,MAAM,GAAG,KAAK;IAClB,IAAI,WAAW,GAAG,KAAK;AACvB,IAAA,MAAM,YAAY,GAAG,IAAI,GAAG,EAAQ;AACpC,IAAA,MAAM,aAAa,GAAG,IAAI,GAAG,EAAU;AACvC,IAAA,MAAM,YAAY,GAAG,IAAI,OAAO,EAAkB;AAClD,IAAA,MAAM,iBAAiB,GAAG,IAAI,OAAO,EAAkB;AACvD,IAAA,MAAM,MAAM,GAAG,IAAI,GAAG,EAAwB;AAE9C,IAAA,MAAM,QAAQ,GAAG,IAAI,gBAAgB,CAAC,OAAO,IAAG;AAC9C,QAAA,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE;AACvB,YAAA,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC;AAC1B,YAAA,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,IAAI,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AAC9C,YAAA,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,IAAI,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAClD;AACF,IAAA,CAAC,CAAC;IAEF,MAAM,cAAc,GAAG,EAAE,CAAC,YAAY,CAAC,CAAC,KAAK,EAAE,QAAQ,KAAI;QACzD,QAAQ,KAAK;AACX,YAAA,KAAK,oBAAoB;AACvB,gBAAA,IAAI,MAAM;oBAAE;gBACZ,MAAM,GAAG,IAAI;gBACb,YAAY,CAAC,KAAK,EAAE;gBACpB,aAAa,CAAC,KAAK,EAAE;AACrB,gBAAA,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE;AAC9B,oBAAA,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI;AACtE,iBAAA,CAAC;gBACF;AAEF,YAAA,KAAK,wBAAwB;gBAC3B,WAAW,GAAG,IAAI;gBAClB;AAEF,YAAA,KAAK,sBAAsB;gBACzB,WAAW,GAAG,KAAK;gBACnB;AAEF,YAAA,KAAK,mBAAmB;gBACtB,IAAI,WAAW,IAAI,QAAQ;AAAE,oBAAA,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC;gBACxD;YAEF,KAAK,kBAAkB,EAAE;AACvB,gBAAA,IAAI,CAAC,MAAM;oBAAE;gBACb,MAAM,GAAG,KAAK;gBACd,WAAW,GAAG,KAAK;AAEnB,gBAAA,MAAM,OAAO,GAAG,QAAQ,CAAC,WAAW,EAAE;AACtC,gBAAA,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE;AACvB,oBAAA,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC;AAC1B,oBAAA,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,IAAI,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AAC9C,oBAAA,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,IAAI,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;gBAClD;gBACA,QAAQ,CAAC,UAAU,EAAE;;AAGrB,gBAAA,MAAM,YAAY,GAAG,IAAI,GAAG,EAAW;AACvC,gBAAA,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE;oBAC/B,IAAI,OAAO,GAAgB,IAAI;oBAC/B,OAAO,OAAO,IAAI,OAAO,KAAK,QAAQ,CAAC,IAAI,EAAE;AAC3C,wBAAA,IAAI,OAAO,YAAY,OAAO,EAAE;AAC9B,4BAAA,IAAI;AACF,gCAAA,IAAI,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,EAAE;AAAE,oCAAA,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC;oCAAE;gCAAO;4BACpE;AAAE,4BAAA,MAAM,eAAe;wBACzB;AACA,wBAAA,OAAO,GAAG,OAAO,CAAC,UAAU;oBAC9B;gBACF;;gBAGA,MAAM,OAAO,GAAmE,EAAE;AAClF,gBAAA,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE;AAChC,oBAAA,IAAI;wBACF,MAAM,MAAM,GAAG,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC;AACtC,wBAAA,IAAI,CAAC,MAAM;4BAAE;AACb,wBAAA,MAAM,IAAI,GAAe,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,QAAQ,GAAG,aAAa;AAC5E,wBAAA,OAAO,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;oBAChD;AAAE,oBAAA,MAAM,4BAA4B;gBACtC;gBAEA,cAAc,CAAC,MAAK;oBAClB,KAAK,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,OAAO,EAAE;AAChD,wBAAA,MAAM,KAAK,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;AACnD,wBAAA,YAAY,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC;AAEjC,wBAAA,IAAI,IAAI,KAAK,aAAa,EAAE;AAC1B,4BAAA,iBAAiB,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,iBAAiB,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;wBAC7E;wBAEA,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,UAAU,CAAC;AAEvC,wBAAA,IAAI,UAAU;AAAE,4BAAA,WAAW,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC;oBAClD;AACF,gBAAA,CAAC,CAAC;gBACF;YACF;;AAEJ,IAAA,CAAC,CAAC;AAEF,IAAA,SAAS,WAAW,CAAC,MAAe,EAAE,KAAa,EAAE,IAAgB,EAAA;QACnE,IAAI,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC;QAC9B,IAAI,CAAC,KAAK,EAAE;AACV,YAAA,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC;AACrC,YAAA,KAAK,CAAC,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC;AACzC,YAAA,KAAK,CAAC,YAAY,CAAC,MAAM,EAAE,cAAc,CAAC;AAC1C,YAAA,KAAK,CAAC,KAAK,CAAC,OAAO,GAAG;gBACpB,mBAAmB,EAAE,SAAS,EAAE,WAAW;AAC3C,gBAAA,oBAAoB,EAAE,qBAAqB;AAC3C,gBAAA,8BAA8B,EAAE,iBAAiB;AACjD,gBAAA,mBAAmB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,YAAY;AACzE,aAAA,CAAC,IAAI,CAAC,GAAG,CAAC;YACX,MAAM,MAAM,GAAG,MAAqB;AACpC,YAAA,IAAI,gBAAgB,CAAC,MAAM,CAAC,CAAC,QAAQ,KAAK,QAAQ;AAAE,gBAAA,MAAM,CAAC,KAAK,CAAC,QAAQ,GAAG,UAAU;AACtF,YAAA,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC;AACzB,YAAA,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC;QAC3B;AACA,QAAA,KAAK,CAAC,KAAK,CAAC,UAAU,GAAG,IAAI,KAAK,aAAa,GAAG,SAAS,GAAG,SAAS;AACvE,QAAA,KAAK,CAAC,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC;IACnC;AAEA,IAAA,OAAO,MAAK;AACV,QAAA,cAAc,EAAE;QAChB,QAAQ,CAAC,UAAU,EAAE;QACrB,OAAO,CAAC,MAAM,EAAE;AAChB,QAAA,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE;YAAE,CAAC,CAAC,MAAM,EAAE;QAC3C,MAAM,CAAC,KAAK,EAAE;AAChB,IAAA,CAAC;AACH;AAEA,SAAS,IAAI,KAAU;;AC/KvB;;AAEG;;ACFH;;AAEG;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "angular-scan",
3
- "version": "0.0.4",
3
+ "version": "0.1.0",
4
4
  "peerDependencies": {
5
5
  "@angular/common": ">=20.0.0",
6
6
  "@angular/core": ">=20.0.0"