@ng-annotate/angular 0.3.14 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -163,6 +163,10 @@ class BridgeService {
163
163
  const current = this.annotations$.getValue();
164
164
  this.annotations$.next([...current, annotation]);
165
165
  }
166
+ else if (data.type === 'manifest:update') {
167
+ const { manifest } = data;
168
+ window.__NG_ANNOTATE_MANIFEST__ = manifest;
169
+ }
166
170
  }
167
171
  catch {
168
172
  // ignore malformed messages
@@ -1 +1 @@
1
- {"version":3,"file":"ng-annotate-angular.mjs","sources":["../../src/inspector.service.ts","../../src/bridge.service.ts","../../src/overlay/overlay.component.ts","../../src/ng-annotate.module.ts","../../src/provide-ng-annotate.ts","../../src/ng-annotate-angular.ts"],"sourcesContent":["import { Injectable } from '@angular/core';\nimport type { ComponentContext } from './types';\n\ninterface NgDevMode {\n getComponent: (element: Element) => unknown;\n}\n\ndeclare const ng: NgDevMode;\n\ninterface NgCmp {\n selectors?: unknown[][];\n inputs?: Record<string, string>;\n}\n\ninterface ComponentInstance {\n constructor: { name: string; ɵcmp?: NgCmp } & (new (...args: unknown[]) => unknown);\n [key: string]: unknown;\n}\n\ninterface ManifestEntry {\n component: string;\n template?: string;\n}\n\nconst DOM_SNAPSHOT_MAX = 5000;\n\n@Injectable()\nexport class InspectorService {\n getComponentContext(element: Element): ComponentContext | null {\n const component = this.findNearestComponent(element);\n if (!component) return null;\n\n const componentName = (component as ComponentInstance).constructor.name;\n const { component: componentFilePath, template: templateFilePath } =\n this.resolveFilePaths(componentName);\n const selector = this.getSelector(component as ComponentInstance);\n const inputs = this.getInputs(component as ComponentInstance);\n const domSnapshot = this.snapshot(element);\n const componentTreePath = this.buildTreePath(element);\n\n return {\n componentName,\n componentFilePath,\n ...(templateFilePath ? { templateFilePath } : {}),\n selector,\n inputs,\n domSnapshot,\n componentTreePath,\n };\n }\n\n private findNearestComponent(element: Element): unknown {\n let current: Element | null = element;\n while (current) {\n try {\n const comp = ng.getComponent(current);\n if (comp) return comp;\n } catch {\n // ignore\n }\n current = current.parentElement;\n }\n return null;\n }\n\n private getSelector(component: ComponentInstance): string {\n try {\n const cmp = component.constructor.ɵcmp;\n if (!cmp?.selectors?.length) return 'unknown-selector';\n const first = cmp.selectors[0];\n if (!first.length) return 'unknown-selector';\n\n // Element selector: [['app-foo']]\n // Attribute selector: [['', 'appFoo', '']]\n if (first[0] === '') {\n // Attribute selector — find non-empty entries\n const attrParts: string[] = [];\n for (let i = 1; i < first.length; i += 2) {\n if (typeof first[i] === 'string' && first[i]) {\n attrParts.push(`[${String(first[i])}]`);\n }\n }\n return attrParts.join('') || 'unknown-selector';\n }\n return String(first[0]);\n } catch {\n return 'unknown-selector';\n }\n }\n\n private getInputs(component: ComponentInstance): Record<string, unknown> {\n try {\n const cmp = component.constructor.ɵcmp;\n if (!cmp?.inputs) return {};\n const result: Record<string, unknown> = {};\n for (const [propName] of Object.entries(cmp.inputs)) {\n if (typeof propName === 'symbol') continue;\n if (propName.startsWith('ɵ')) continue;\n result[propName] = (component as Record<string, unknown>)[propName];\n }\n return result;\n } catch {\n return {};\n }\n }\n\n private buildTreePath(element: Element): string[] {\n const path: string[] = [];\n let current: Element | null = element.parentElement;\n while (current) {\n try {\n const comp = ng.getComponent(current);\n if (comp) {\n path.unshift((comp as ComponentInstance).constructor.name);\n }\n } catch {\n // ignore\n }\n current = current.parentElement;\n }\n return path;\n }\n\n private snapshot(element: Element): string {\n const html = element.outerHTML;\n if (html.length <= DOM_SNAPSHOT_MAX) return html;\n return html.slice(0, DOM_SNAPSHOT_MAX) + '<!-- truncated -->';\n }\n\n private resolveFilePaths(componentName: string): { component: string; template?: string } {\n try {\n const manifest = (window as unknown as { __NG_ANNOTATE_MANIFEST__?: Record<string, ManifestEntry> })\n .__NG_ANNOTATE_MANIFEST__;\n const entry = manifest?.[componentName];\n if (entry) return entry;\n } catch {\n // ignore\n }\n return { component: `(unresolved: ${componentName})` };\n }\n}\n","import { Injectable, NgZone, OnDestroy, inject } from '@angular/core';\nimport { BehaviorSubject } from 'rxjs';\nimport type { Session, Annotation } from './types';\n\ntype BridgeMessage =\n | { type: 'session:created'; session: Session }\n | { type: 'annotations:sync'; annotations: Annotation[] }\n | { type: 'annotation:created'; annotation: Annotation }\n | { type: string };\n\n@Injectable()\nexport class BridgeService implements OnDestroy {\n readonly session$ = new BehaviorSubject<Session | null>(null);\n readonly annotations$ = new BehaviorSubject<Annotation[]>([]);\n readonly connected$ = new BehaviorSubject<boolean>(false);\n\n private readonly zone = inject(NgZone);\n private ws: WebSocket | null = null;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n\n init(): void {\n this.connect();\n }\n\n private connect(): void {\n const wsUrl = `ws://${location.host}/__annotate`;\n this.ws = new WebSocket(wsUrl);\n\n this.ws.onopen = () => {\n this.zone.run(() => {\n this.connected$.next(true);\n });\n };\n\n this.ws.onmessage = (event: MessageEvent<string>) => {\n this.zone.run(() => {\n try {\n const data = JSON.parse(event.data) as BridgeMessage;\n if (data.type === 'session:created') {\n this.session$.next((data as Extract<BridgeMessage, { type: 'session:created' }>).session);\n } else if (data.type === 'annotations:sync') {\n this.annotations$.next(\n (data as Extract<BridgeMessage, { type: 'annotations:sync' }>).annotations,\n );\n } else if (data.type === 'annotation:created') {\n const annotation = (\n data as Extract<BridgeMessage, { type: 'annotation:created' }>\n ).annotation;\n const current = this.annotations$.getValue();\n this.annotations$.next([...current, annotation]);\n }\n } catch {\n // ignore malformed messages\n }\n });\n };\n\n this.ws.onclose = () => {\n this.zone.run(() => {\n this.connected$.next(false);\n this.reconnectTimer = setTimeout(() => { this.connect(); }, 3000);\n });\n };\n\n this.ws.onerror = (event) => {\n console.warn('[ng-annotate] WebSocket error', event);\n };\n }\n\n createAnnotation(payload: Record<string, unknown>): void {\n this.send({ type: 'annotation:create', payload });\n }\n\n replyToAnnotation(id: string, message: string): void {\n this.send({ type: 'annotation:reply', id, message });\n }\n\n deleteAnnotation(id: string): void {\n this.send({ type: 'annotation:delete', id });\n }\n\n private send(msg: Record<string, unknown>): void {\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(JSON.stringify(msg));\n }\n }\n\n ngOnDestroy(): void {\n if (this.reconnectTimer !== null) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n this.ws?.close();\n }\n}\n","import {\n Component,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n HostListener,\n OnInit,\n ViewChild,\n ElementRef,\n inject,\n} from '@angular/core';\nimport { JsonPipe } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { InspectorService } from '../inspector.service';\nimport { BridgeService } from '../bridge.service';\nimport type { Annotation, ComponentContext, AnnotationStatus } from '../types';\n\ntype OverlayMode = 'hidden' | 'inspect' | 'annotate' | 'thread';\n\ninterface HighlightRect {\n top: string;\n left: string;\n width: string;\n height: string;\n}\n\ninterface AnnotationBadge {\n annotation: Annotation;\n top: string;\n left: string;\n icon: string;\n label: string;\n}\n\n@Component({\n selector: 'nga-overlay',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [JsonPipe, FormsModule],\n styles: [`\n :host {\n position: fixed; top: 0; left: 0; width: 100%; height: 100%;\n pointer-events: none; z-index: 9999;\n }\n .nga-highlight-rect {\n position: fixed; border: 2px solid #3b82f6;\n background: rgba(59,130,246,0.1); pointer-events: none;\n transition: top 0.05s, left 0.05s, width 0.05s, height 0.05s;\n }\n .nga-component-label {\n position: absolute; top: -22px; left: 0; background: #1e293b; color: #f8fafc;\n font-family: monospace; font-size: 11px; padding: 2px 6px;\n border-radius: 3px; white-space: nowrap;\n }\n .nga-annotate-panel, .nga-thread-panel {\n pointer-events: all; position: fixed; right: 16px; top: 50%;\n transform: translateY(-50%); background: #ffffff; border: 1px solid #e2e8f0;\n border-radius: 8px; box-shadow: 0 4px 24px rgba(0,0,0,0.15);\n padding: 16px; min-width: 320px; max-width: 400px;\n }\n .nga-panel-title {\n margin: 0 0 12px; font-size: 14px; font-weight: 600;\n color: #1e293b; font-family: monospace;\n }\n .nga-inputs { margin-bottom: 10px; }\n .nga-input-row {\n display: flex; gap: 8px; font-size: 12px;\n font-family: monospace; margin-bottom: 4px;\n }\n .nga-input-key { color: #64748b; min-width: 80px; }\n .nga-input-val {\n color: #1e293b; overflow: hidden;\n text-overflow: ellipsis; white-space: nowrap;\n }\n .nga-selection {\n font-size: 12px; color: #475569; margin-bottom: 8px; font-style: italic;\n }\n .nga-textarea, .nga-reply-input {\n width: 100%; box-sizing: border-box; border: 1px solid #cbd5e1;\n border-radius: 4px; padding: 8px; font-size: 13px;\n font-family: inherit; resize: vertical; margin-bottom: 10px;\n }\n .nga-textarea:focus, .nga-reply-input:focus {\n outline: none; border-color: #3b82f6;\n }\n .nga-actions { display: flex; gap: 8px; }\n .nga-btn {\n padding: 6px 14px; border-radius: 4px; font-size: 13px;\n cursor: pointer; border: none; transition: opacity 0.15s;\n }\n .nga-btn:disabled { opacity: 0.4; cursor: not-allowed; }\n .nga-btn-submit { background: #3b82f6; color: #ffffff; }\n .nga-btn-cancel { background: #f1f5f9; color: #475569; }\n .nga-replies { max-height: 200px; overflow-y: auto; margin-bottom: 10px; }\n .nga-reply { display: flex; gap: 8px; margin-bottom: 8px; font-size: 13px; }\n .nga-reply-author { font-weight: 600; min-width: 48px; }\n .nga-reply-author--agent { color: #7c3aed; }\n .nga-reply-author--user { color: #2563eb; }\n .nga-badge {\n pointer-events: all; position: fixed; width: 18px; height: 18px;\n border-radius: 50%; display: flex; align-items: center;\n justify-content: center; font-size: 10px; cursor: pointer;\n transform: translate(-50%, -50%);\n }\n .nga-badge--pending { background: #3b82f6; color: #ffffff; }\n .nga-badge--acknowledged { background: #f59e0b; color: #ffffff; }\n .nga-badge--resolved { background: #22c55e; color: #ffffff; }\n .nga-badge--dismissed { background: #94a3b8; color: #ffffff; }\n .nga-keyboard-hint {\n pointer-events: none; position: fixed; bottom: 16px; right: 16px;\n background: rgba(15,23,42,0.7); color: #f8fafc; font-size: 12px;\n padding: 6px 10px; border-radius: 6px;\n }\n .nga-keyboard-hint kbd {\n background: rgba(255,255,255,0.15); border-radius: 3px;\n padding: 1px 5px; font-family: monospace;\n }\n `],\n template: `\n <!-- Keyboard hint -->\n @if (mode === 'hidden') {\n <div class=\"nga-keyboard-hint\">\n <kbd>Alt+Shift+A</kbd> to annotate\n </div>\n }\n @if (mode === 'inspect') {\n <div class=\"nga-keyboard-hint\">\n Click a component &nbsp; <kbd>Esc</kbd> to cancel\n </div>\n }\n\n <!-- Inspect highlight rect -->\n @if (mode === 'inspect' && hoveredContext !== null && highlightRect !== null) {\n <div\n class=\"nga-highlight-rect\"\n [style.top]=\"highlightRect.top\"\n [style.left]=\"highlightRect.left\"\n [style.width]=\"highlightRect.width\"\n [style.height]=\"highlightRect.height\"\n >\n <span class=\"nga-component-label\">{{ hoveredContext.componentName }}</span>\n </div>\n }\n\n <!-- Annotate panel -->\n @if (mode === 'annotate' && selectedContext !== null) {\n <div class=\"nga-annotate-panel\">\n <h3 class=\"nga-panel-title\">{{ selectedContext.componentName }}</h3>\n\n @if (inputEntries().length > 0) {\n <div class=\"nga-inputs\">\n @for (entry of inputEntries(); track entry.key) {\n <div class=\"nga-input-row\">\n <span class=\"nga-input-key\">{{ entry.key }}:</span>\n <span class=\"nga-input-val\">{{ entry.value | json }}</span>\n </div>\n }\n </div>\n }\n\n @if (selectionText) {\n <div class=\"nga-selection\">\n <em>\"{{ selectionText }}\"</em>\n </div>\n }\n\n <textarea\n #textArea\n class=\"nga-textarea\"\n [(ngModel)]=\"annotationText\"\n placeholder=\"Describe the change...\"\n rows=\"4\"\n ></textarea>\n\n <div class=\"nga-actions\">\n <button class=\"nga-btn nga-btn-submit\" (click)=\"submit()\" [disabled]=\"annotationText.trim() === ''\">\n Submit\n </button>\n <button class=\"nga-btn nga-btn-cancel\" (click)=\"cancel()\">Cancel</button>\n </div>\n </div>\n }\n\n <!-- Thread panel -->\n @if (mode === 'thread' && threadAnnotation !== null) {\n <div class=\"nga-thread-panel\">\n <h3 class=\"nga-panel-title\">{{ threadAnnotation.componentName }}</h3>\n\n <div class=\"nga-replies\">\n @for (reply of threadAnnotation.replies; track reply.message) {\n <div class=\"nga-reply\">\n <span class=\"nga-reply-author nga-reply-author--{{ reply.author }}\">{{ reply.author }}</span>\n <span class=\"nga-reply-text\">{{ reply.message }}</span>\n </div>\n }\n </div>\n\n <input\n class=\"nga-reply-input\"\n type=\"text\"\n [(ngModel)]=\"replyText\"\n placeholder=\"Reply...\"\n (keydown.enter)=\"sendReply()\"\n />\n\n <div class=\"nga-actions\">\n <button class=\"nga-btn nga-btn-submit\" (click)=\"sendReply()\" [disabled]=\"replyText.trim() === ''\">\n Send\n </button>\n <button class=\"nga-btn nga-btn-cancel\" (click)=\"closeThread()\">Close</button>\n </div>\n </div>\n }\n\n <!-- Annotation badges -->\n @for (badge of badges; track badge.annotation.id) {\n <div\n class=\"nga-badge nga-badge--{{ badge.annotation.status }}\"\n [style.top]=\"badge.top\"\n [style.left]=\"badge.left\"\n (click)=\"openThread(badge.annotation)\"\n [title]=\"badge.label\"\n >\n {{ badge.icon }}\n </div>\n }\n `,\n})\nexport class OverlayComponent implements OnInit {\n @ViewChild('textArea') textArea?: ElementRef<HTMLTextAreaElement>;\n\n mode: OverlayMode = 'hidden';\n hoveredContext: ComponentContext | null = null;\n highlightRect: HighlightRect | null = null;\n selectedContext: ComponentContext | null = null;\n annotationText = '';\n selectionText = '';\n threadAnnotation: Annotation | null = null;\n replyText = '';\n badges: AnnotationBadge[] = [];\n\n private readonly inspector = inject(InspectorService);\n private readonly bridge = inject(BridgeService);\n private readonly cdr = inject(ChangeDetectorRef);\n\n ngOnInit(): void {\n this.bridge.annotations$.subscribe((annotations) => {\n this.updateBadges(annotations);\n this.cdr.markForCheck();\n });\n }\n\n @HostListener('document:keydown.alt.shift.a', ['$event'])\n toggleInspect(event?: Event): void {\n event?.preventDefault();\n if (this.mode === 'hidden') this.mode = 'inspect';\n else if (this.mode === 'inspect') this.mode = 'hidden';\n else if (this.mode === 'annotate') this.mode = 'inspect';\n this.cdr.markForCheck();\n }\n\n @HostListener('document:keydown.escape')\n onEscape(): void {\n if (this.mode === 'annotate') this.mode = 'inspect';\n else if (this.mode === 'inspect') this.mode = 'hidden';\n else if (this.mode === 'thread') this.mode = 'hidden';\n this.cdr.markForCheck();\n }\n\n @HostListener('window:scroll')\n @HostListener('window:resize')\n onScrollOrResize(): void {\n if (this.badges.length > 0) {\n this.refreshBadgePositions();\n this.cdr.markForCheck();\n }\n }\n\n @HostListener('document:mousemove', ['$event'])\n onMouseMove(event: MouseEvent): void {\n if (this.mode !== 'inspect') return;\n const target = event.target as Element;\n if (target.closest('nga-overlay')) return;\n const context = this.inspector.getComponentContext(target);\n this.hoveredContext = context;\n if (context) {\n const rect = target.getBoundingClientRect();\n this.highlightRect = {\n top: `${rect.top.toString()}px`,\n left: `${rect.left.toString()}px`,\n width: `${rect.width.toString()}px`,\n height: `${rect.height.toString()}px`,\n };\n } else {\n this.highlightRect = null;\n }\n this.cdr.markForCheck();\n }\n\n @HostListener('document:click', ['$event'])\n onClick(event: MouseEvent): void {\n if (this.mode !== 'inspect') return;\n const target = event.target as Element;\n if (target.closest('nga-overlay')) return;\n const context = this.inspector.getComponentContext(target);\n if (!context) return;\n\n event.preventDefault();\n event.stopPropagation();\n\n this.selectedContext = context;\n this.annotationText = '';\n this.selectionText = window.getSelection()?.toString() ?? '';\n this.mode = 'annotate';\n this.cdr.markForCheck();\n\n setTimeout(() => { this.textArea?.nativeElement.focus(); }, 0);\n }\n\n submit(): void {\n if (!this.selectedContext || !this.annotationText.trim()) return;\n this.bridge.createAnnotation({\n ...this.selectedContext,\n annotationText: this.annotationText.trim(),\n selectionText: this.selectionText || undefined,\n });\n this.selectedContext = null;\n this.annotationText = '';\n this.mode = 'inspect';\n this.cdr.markForCheck();\n }\n\n cancel(): void {\n this.mode = 'inspect';\n this.cdr.markForCheck();\n }\n\n openThread(annotation: Annotation): void {\n this.threadAnnotation = annotation;\n this.mode = 'thread';\n this.cdr.markForCheck();\n }\n\n closeThread(): void {\n this.threadAnnotation = null;\n this.mode = 'hidden';\n this.cdr.markForCheck();\n }\n\n sendReply(): void {\n if (!this.threadAnnotation || !this.replyText.trim()) return;\n this.bridge.replyToAnnotation(this.threadAnnotation.id, this.replyText.trim());\n this.replyText = '';\n this.cdr.markForCheck();\n }\n\n inputEntries(): { key: string; value: unknown }[] {\n if (!this.selectedContext) return [];\n return Object.entries(this.selectedContext.inputs)\n .slice(0, 5)\n .map(([key, value]) => ({ key, value }));\n }\n\n private updateBadges(annotations: Annotation[]): void {\n this.badges = annotations\n .map((annotation) => {\n const el = this.findComponentElement(annotation.componentName, annotation.selector);\n if (!el) return null;\n const rect = el.getBoundingClientRect();\n return {\n annotation,\n top: `${rect.top.toString()}px`,\n left: `${(rect.left + rect.width - 12).toString()}px`,\n icon: this.badgeIcon(annotation.status),\n label: `${annotation.componentName}: ${annotation.annotationText.slice(0, 40)}`,\n };\n })\n .filter((b): b is AnnotationBadge => b !== null);\n }\n\n private refreshBadgePositions(): void {\n this.badges = this.badges.map((badge) => {\n const el = this.findComponentElement(badge.annotation.componentName, badge.annotation.selector);\n if (!el) return badge;\n const rect = el.getBoundingClientRect();\n return {\n ...badge,\n top: `${rect.top.toString()}px`,\n left: `${(rect.left + rect.width - 12).toString()}px`,\n };\n });\n }\n\n private findComponentElement(componentName: string, selector: string): Element | null {\n const bySelector = document.querySelector(selector);\n if (bySelector) return bySelector;\n\n const all = document.querySelectorAll('*');\n for (const el of Array.from(all)) {\n try {\n const comp = (window as unknown as { ng?: { getComponent: (el: Element) => { constructor: { name: string } } | null } })\n .ng;\n if (comp?.getComponent(el)?.constructor.name === componentName) return el;\n } catch {\n // ignore\n }\n }\n return null;\n }\n\n private badgeIcon(status: AnnotationStatus): string {\n const icons: Record<AnnotationStatus, string> = {\n pending: '●',\n acknowledged: '◐',\n resolved: '✓',\n dismissed: '✕',\n };\n return icons[status];\n }\n}\n","import {\n NgModule,\n isDevMode,\n provideAppInitializer,\n inject,\n ApplicationRef,\n EnvironmentInjector,\n createComponent,\n} from '@angular/core';\nimport { InspectorService } from './inspector.service';\nimport { BridgeService } from './bridge.service';\nimport { OverlayComponent } from './overlay/overlay.component';\n\n@NgModule({\n providers: isDevMode()\n ? [\n InspectorService,\n BridgeService,\n provideAppInitializer(() => {\n const bridge = inject(BridgeService);\n const appRef = inject(ApplicationRef);\n const envInjector = inject(EnvironmentInjector);\n bridge.init();\n const overlayRef = createComponent(OverlayComponent, { environmentInjector: envInjector });\n appRef.attachView(overlayRef.hostView);\n document.body.appendChild(overlayRef.location.nativeElement);\n }),\n ]\n : [],\n})\n// eslint-disable-next-line @typescript-eslint/no-extraneous-class -- required by NgModule pattern\nexport class NgAnnotateModule {}\n","import {\n ApplicationRef,\n EnvironmentInjector,\n createComponent,\n inject,\n isDevMode,\n makeEnvironmentProviders,\n provideAppInitializer,\n} from '@angular/core';\nimport { InspectorService } from './inspector.service';\nimport { BridgeService } from './bridge.service';\nimport { OverlayComponent } from './overlay/overlay.component';\n\nexport function provideNgAnnotate() {\n return makeEnvironmentProviders([\n InspectorService,\n BridgeService,\n provideAppInitializer(() => {\n if (!isDevMode()) return;\n const bridge = inject(BridgeService);\n const appRef = inject(ApplicationRef);\n const envInjector = inject(EnvironmentInjector);\n bridge.init();\n const overlayRef = createComponent(OverlayComponent, { environmentInjector: envInjector });\n appRef.attachView(overlayRef.hostView);\n document.body.appendChild(overlayRef.location.nativeElement);\n }),\n ]);\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ng-annotate-angular.mjs","sources":["../../src/inspector.service.ts","../../src/bridge.service.ts","../../src/overlay/overlay.component.ts","../../src/ng-annotate.module.ts","../../src/provide-ng-annotate.ts","../../src/ng-annotate-angular.ts"],"sourcesContent":["import { Injectable } from '@angular/core';\nimport type { ComponentContext } from './types';\n\ninterface NgDevMode {\n getComponent: (element: Element) => unknown;\n}\n\ndeclare const ng: NgDevMode;\n\ninterface NgCmp {\n selectors?: unknown[][];\n inputs?: Record<string, string>;\n}\n\ninterface ComponentInstance {\n constructor: { name: string; ɵcmp?: NgCmp } & (new (...args: unknown[]) => unknown);\n [key: string]: unknown;\n}\n\ninterface ManifestEntry {\n component: string;\n template?: string;\n}\n\nconst DOM_SNAPSHOT_MAX = 5000;\n\n@Injectable()\nexport class InspectorService {\n getComponentContext(element: Element): ComponentContext | null {\n const component = this.findNearestComponent(element);\n if (!component) return null;\n\n const componentName = (component as ComponentInstance).constructor.name;\n const { component: componentFilePath, template: templateFilePath } =\n this.resolveFilePaths(componentName);\n const selector = this.getSelector(component as ComponentInstance);\n const inputs = this.getInputs(component as ComponentInstance);\n const domSnapshot = this.snapshot(element);\n const componentTreePath = this.buildTreePath(element);\n\n return {\n componentName,\n componentFilePath,\n ...(templateFilePath ? { templateFilePath } : {}),\n selector,\n inputs,\n domSnapshot,\n componentTreePath,\n };\n }\n\n private findNearestComponent(element: Element): unknown {\n let current: Element | null = element;\n while (current) {\n try {\n const comp = ng.getComponent(current);\n if (comp) return comp;\n } catch {\n // ignore\n }\n current = current.parentElement;\n }\n return null;\n }\n\n private getSelector(component: ComponentInstance): string {\n try {\n const cmp = component.constructor.ɵcmp;\n if (!cmp?.selectors?.length) return 'unknown-selector';\n const first = cmp.selectors[0];\n if (!first.length) return 'unknown-selector';\n\n // Element selector: [['app-foo']]\n // Attribute selector: [['', 'appFoo', '']]\n if (first[0] === '') {\n // Attribute selector — find non-empty entries\n const attrParts: string[] = [];\n for (let i = 1; i < first.length; i += 2) {\n if (typeof first[i] === 'string' && first[i]) {\n attrParts.push(`[${String(first[i])}]`);\n }\n }\n return attrParts.join('') || 'unknown-selector';\n }\n return String(first[0]);\n } catch {\n return 'unknown-selector';\n }\n }\n\n private getInputs(component: ComponentInstance): Record<string, unknown> {\n try {\n const cmp = component.constructor.ɵcmp;\n if (!cmp?.inputs) return {};\n const result: Record<string, unknown> = {};\n for (const [propName] of Object.entries(cmp.inputs)) {\n if (typeof propName === 'symbol') continue;\n if (propName.startsWith('ɵ')) continue;\n result[propName] = (component as Record<string, unknown>)[propName];\n }\n return result;\n } catch {\n return {};\n }\n }\n\n private buildTreePath(element: Element): string[] {\n const path: string[] = [];\n let current: Element | null = element.parentElement;\n while (current) {\n try {\n const comp = ng.getComponent(current);\n if (comp) {\n path.unshift((comp as ComponentInstance).constructor.name);\n }\n } catch {\n // ignore\n }\n current = current.parentElement;\n }\n return path;\n }\n\n private snapshot(element: Element): string {\n const html = element.outerHTML;\n if (html.length <= DOM_SNAPSHOT_MAX) return html;\n return html.slice(0, DOM_SNAPSHOT_MAX) + '<!-- truncated -->';\n }\n\n private resolveFilePaths(componentName: string): { component: string; template?: string } {\n try {\n const manifest = (window as unknown as { __NG_ANNOTATE_MANIFEST__?: Record<string, ManifestEntry> })\n .__NG_ANNOTATE_MANIFEST__;\n const entry = manifest?.[componentName];\n if (entry) return entry;\n } catch {\n // ignore\n }\n return { component: `(unresolved: ${componentName})` };\n }\n}\n","import { Injectable, NgZone, OnDestroy, inject } from '@angular/core';\nimport { BehaviorSubject } from 'rxjs';\nimport type { Session, Annotation } from './types';\n\ntype BridgeMessage =\n | { type: 'session:created'; session: Session }\n | { type: 'annotations:sync'; annotations: Annotation[] }\n | { type: 'annotation:created'; annotation: Annotation }\n | { type: 'manifest:update'; manifest: Record<string, unknown> }\n | { type: string };\n\n@Injectable()\nexport class BridgeService implements OnDestroy {\n readonly session$ = new BehaviorSubject<Session | null>(null);\n readonly annotations$ = new BehaviorSubject<Annotation[]>([]);\n readonly connected$ = new BehaviorSubject<boolean>(false);\n\n private readonly zone = inject(NgZone);\n private ws: WebSocket | null = null;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n\n init(): void {\n this.connect();\n }\n\n private connect(): void {\n const wsUrl = `ws://${location.host}/__annotate`;\n this.ws = new WebSocket(wsUrl);\n\n this.ws.onopen = () => {\n this.zone.run(() => {\n this.connected$.next(true);\n });\n };\n\n this.ws.onmessage = (event: MessageEvent<string>) => {\n this.zone.run(() => {\n try {\n const data = JSON.parse(event.data) as BridgeMessage;\n if (data.type === 'session:created') {\n this.session$.next((data as Extract<BridgeMessage, { type: 'session:created' }>).session);\n } else if (data.type === 'annotations:sync') {\n this.annotations$.next(\n (data as Extract<BridgeMessage, { type: 'annotations:sync' }>).annotations,\n );\n } else if (data.type === 'annotation:created') {\n const annotation = (\n data as Extract<BridgeMessage, { type: 'annotation:created' }>\n ).annotation;\n const current = this.annotations$.getValue();\n this.annotations$.next([...current, annotation]);\n } else if (data.type === 'manifest:update') {\n const { manifest } = data as Extract<BridgeMessage, { type: 'manifest:update' }>;\n (window as unknown as { __NG_ANNOTATE_MANIFEST__?: unknown }).__NG_ANNOTATE_MANIFEST__ = manifest;\n }\n } catch {\n // ignore malformed messages\n }\n });\n };\n\n this.ws.onclose = () => {\n this.zone.run(() => {\n this.connected$.next(false);\n this.reconnectTimer = setTimeout(() => { this.connect(); }, 3000);\n });\n };\n\n this.ws.onerror = (event) => {\n console.warn('[ng-annotate] WebSocket error', event);\n };\n }\n\n createAnnotation(payload: Record<string, unknown>): void {\n this.send({ type: 'annotation:create', payload });\n }\n\n replyToAnnotation(id: string, message: string): void {\n this.send({ type: 'annotation:reply', id, message });\n }\n\n deleteAnnotation(id: string): void {\n this.send({ type: 'annotation:delete', id });\n }\n\n private send(msg: Record<string, unknown>): void {\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(JSON.stringify(msg));\n }\n }\n\n ngOnDestroy(): void {\n if (this.reconnectTimer !== null) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n this.ws?.close();\n }\n}\n","import {\n Component,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n HostListener,\n OnInit,\n ViewChild,\n ElementRef,\n inject,\n} from '@angular/core';\nimport { JsonPipe } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { InspectorService } from '../inspector.service';\nimport { BridgeService } from '../bridge.service';\nimport type { Annotation, ComponentContext, AnnotationStatus } from '../types';\n\ntype OverlayMode = 'hidden' | 'inspect' | 'annotate' | 'thread';\n\ninterface HighlightRect {\n top: string;\n left: string;\n width: string;\n height: string;\n}\n\ninterface AnnotationBadge {\n annotation: Annotation;\n top: string;\n left: string;\n icon: string;\n label: string;\n}\n\n@Component({\n selector: 'nga-overlay',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [JsonPipe, FormsModule],\n styles: [`\n :host {\n position: fixed; top: 0; left: 0; width: 100%; height: 100%;\n pointer-events: none; z-index: 9999;\n }\n .nga-highlight-rect {\n position: fixed; border: 2px solid #3b82f6;\n background: rgba(59,130,246,0.1); pointer-events: none;\n transition: top 0.05s, left 0.05s, width 0.05s, height 0.05s;\n }\n .nga-component-label {\n position: absolute; top: -22px; left: 0; background: #1e293b; color: #f8fafc;\n font-family: monospace; font-size: 11px; padding: 2px 6px;\n border-radius: 3px; white-space: nowrap;\n }\n .nga-annotate-panel, .nga-thread-panel {\n pointer-events: all; position: fixed; right: 16px; top: 50%;\n transform: translateY(-50%); background: #ffffff; border: 1px solid #e2e8f0;\n border-radius: 8px; box-shadow: 0 4px 24px rgba(0,0,0,0.15);\n padding: 16px; min-width: 320px; max-width: 400px;\n }\n .nga-panel-title {\n margin: 0 0 12px; font-size: 14px; font-weight: 600;\n color: #1e293b; font-family: monospace;\n }\n .nga-inputs { margin-bottom: 10px; }\n .nga-input-row {\n display: flex; gap: 8px; font-size: 12px;\n font-family: monospace; margin-bottom: 4px;\n }\n .nga-input-key { color: #64748b; min-width: 80px; }\n .nga-input-val {\n color: #1e293b; overflow: hidden;\n text-overflow: ellipsis; white-space: nowrap;\n }\n .nga-selection {\n font-size: 12px; color: #475569; margin-bottom: 8px; font-style: italic;\n }\n .nga-textarea, .nga-reply-input {\n width: 100%; box-sizing: border-box; border: 1px solid #cbd5e1;\n border-radius: 4px; padding: 8px; font-size: 13px;\n font-family: inherit; resize: vertical; margin-bottom: 10px;\n }\n .nga-textarea:focus, .nga-reply-input:focus {\n outline: none; border-color: #3b82f6;\n }\n .nga-actions { display: flex; gap: 8px; }\n .nga-btn {\n padding: 6px 14px; border-radius: 4px; font-size: 13px;\n cursor: pointer; border: none; transition: opacity 0.15s;\n }\n .nga-btn:disabled { opacity: 0.4; cursor: not-allowed; }\n .nga-btn-submit { background: #3b82f6; color: #ffffff; }\n .nga-btn-cancel { background: #f1f5f9; color: #475569; }\n .nga-replies { max-height: 200px; overflow-y: auto; margin-bottom: 10px; }\n .nga-reply { display: flex; gap: 8px; margin-bottom: 8px; font-size: 13px; }\n .nga-reply-author { font-weight: 600; min-width: 48px; }\n .nga-reply-author--agent { color: #7c3aed; }\n .nga-reply-author--user { color: #2563eb; }\n .nga-badge {\n pointer-events: all; position: fixed; width: 18px; height: 18px;\n border-radius: 50%; display: flex; align-items: center;\n justify-content: center; font-size: 10px; cursor: pointer;\n transform: translate(-50%, -50%);\n }\n .nga-badge--pending { background: #3b82f6; color: #ffffff; }\n .nga-badge--acknowledged { background: #f59e0b; color: #ffffff; }\n .nga-badge--resolved { background: #22c55e; color: #ffffff; }\n .nga-badge--dismissed { background: #94a3b8; color: #ffffff; }\n .nga-keyboard-hint {\n pointer-events: none; position: fixed; bottom: 16px; right: 16px;\n background: rgba(15,23,42,0.7); color: #f8fafc; font-size: 12px;\n padding: 6px 10px; border-radius: 6px;\n }\n .nga-keyboard-hint kbd {\n background: rgba(255,255,255,0.15); border-radius: 3px;\n padding: 1px 5px; font-family: monospace;\n }\n `],\n template: `\n <!-- Keyboard hint -->\n @if (mode === 'hidden') {\n <div class=\"nga-keyboard-hint\">\n <kbd>Alt+Shift+A</kbd> to annotate\n </div>\n }\n @if (mode === 'inspect') {\n <div class=\"nga-keyboard-hint\">\n Click a component &nbsp; <kbd>Esc</kbd> to cancel\n </div>\n }\n\n <!-- Inspect highlight rect -->\n @if (mode === 'inspect' && hoveredContext !== null && highlightRect !== null) {\n <div\n class=\"nga-highlight-rect\"\n [style.top]=\"highlightRect.top\"\n [style.left]=\"highlightRect.left\"\n [style.width]=\"highlightRect.width\"\n [style.height]=\"highlightRect.height\"\n >\n <span class=\"nga-component-label\">{{ hoveredContext.componentName }}</span>\n </div>\n }\n\n <!-- Annotate panel -->\n @if (mode === 'annotate' && selectedContext !== null) {\n <div class=\"nga-annotate-panel\">\n <h3 class=\"nga-panel-title\">{{ selectedContext.componentName }}</h3>\n\n @if (inputEntries().length > 0) {\n <div class=\"nga-inputs\">\n @for (entry of inputEntries(); track entry.key) {\n <div class=\"nga-input-row\">\n <span class=\"nga-input-key\">{{ entry.key }}:</span>\n <span class=\"nga-input-val\">{{ entry.value | json }}</span>\n </div>\n }\n </div>\n }\n\n @if (selectionText) {\n <div class=\"nga-selection\">\n <em>\"{{ selectionText }}\"</em>\n </div>\n }\n\n <textarea\n #textArea\n class=\"nga-textarea\"\n [(ngModel)]=\"annotationText\"\n placeholder=\"Describe the change...\"\n rows=\"4\"\n ></textarea>\n\n <div class=\"nga-actions\">\n <button class=\"nga-btn nga-btn-submit\" (click)=\"submit()\" [disabled]=\"annotationText.trim() === ''\">\n Submit\n </button>\n <button class=\"nga-btn nga-btn-cancel\" (click)=\"cancel()\">Cancel</button>\n </div>\n </div>\n }\n\n <!-- Thread panel -->\n @if (mode === 'thread' && threadAnnotation !== null) {\n <div class=\"nga-thread-panel\">\n <h3 class=\"nga-panel-title\">{{ threadAnnotation.componentName }}</h3>\n\n <div class=\"nga-replies\">\n @for (reply of threadAnnotation.replies; track reply.message) {\n <div class=\"nga-reply\">\n <span class=\"nga-reply-author nga-reply-author--{{ reply.author }}\">{{ reply.author }}</span>\n <span class=\"nga-reply-text\">{{ reply.message }}</span>\n </div>\n }\n </div>\n\n <input\n class=\"nga-reply-input\"\n type=\"text\"\n [(ngModel)]=\"replyText\"\n placeholder=\"Reply...\"\n (keydown.enter)=\"sendReply()\"\n />\n\n <div class=\"nga-actions\">\n <button class=\"nga-btn nga-btn-submit\" (click)=\"sendReply()\" [disabled]=\"replyText.trim() === ''\">\n Send\n </button>\n <button class=\"nga-btn nga-btn-cancel\" (click)=\"closeThread()\">Close</button>\n </div>\n </div>\n }\n\n <!-- Annotation badges -->\n @for (badge of badges; track badge.annotation.id) {\n <div\n class=\"nga-badge nga-badge--{{ badge.annotation.status }}\"\n [style.top]=\"badge.top\"\n [style.left]=\"badge.left\"\n (click)=\"openThread(badge.annotation)\"\n [title]=\"badge.label\"\n >\n {{ badge.icon }}\n </div>\n }\n `,\n})\nexport class OverlayComponent implements OnInit {\n @ViewChild('textArea') textArea?: ElementRef<HTMLTextAreaElement>;\n\n mode: OverlayMode = 'hidden';\n hoveredContext: ComponentContext | null = null;\n highlightRect: HighlightRect | null = null;\n selectedContext: ComponentContext | null = null;\n annotationText = '';\n selectionText = '';\n threadAnnotation: Annotation | null = null;\n replyText = '';\n badges: AnnotationBadge[] = [];\n\n private readonly inspector = inject(InspectorService);\n private readonly bridge = inject(BridgeService);\n private readonly cdr = inject(ChangeDetectorRef);\n\n ngOnInit(): void {\n this.bridge.annotations$.subscribe((annotations) => {\n this.updateBadges(annotations);\n this.cdr.markForCheck();\n });\n }\n\n @HostListener('document:keydown.alt.shift.a', ['$event'])\n toggleInspect(event?: Event): void {\n event?.preventDefault();\n if (this.mode === 'hidden') this.mode = 'inspect';\n else if (this.mode === 'inspect') this.mode = 'hidden';\n else if (this.mode === 'annotate') this.mode = 'inspect';\n this.cdr.markForCheck();\n }\n\n @HostListener('document:keydown.escape')\n onEscape(): void {\n if (this.mode === 'annotate') this.mode = 'inspect';\n else if (this.mode === 'inspect') this.mode = 'hidden';\n else if (this.mode === 'thread') this.mode = 'hidden';\n this.cdr.markForCheck();\n }\n\n @HostListener('window:scroll')\n @HostListener('window:resize')\n onScrollOrResize(): void {\n if (this.badges.length > 0) {\n this.refreshBadgePositions();\n this.cdr.markForCheck();\n }\n }\n\n @HostListener('document:mousemove', ['$event'])\n onMouseMove(event: MouseEvent): void {\n if (this.mode !== 'inspect') return;\n const target = event.target as Element;\n if (target.closest('nga-overlay')) return;\n const context = this.inspector.getComponentContext(target);\n this.hoveredContext = context;\n if (context) {\n const rect = target.getBoundingClientRect();\n this.highlightRect = {\n top: `${rect.top.toString()}px`,\n left: `${rect.left.toString()}px`,\n width: `${rect.width.toString()}px`,\n height: `${rect.height.toString()}px`,\n };\n } else {\n this.highlightRect = null;\n }\n this.cdr.markForCheck();\n }\n\n @HostListener('document:click', ['$event'])\n onClick(event: MouseEvent): void {\n if (this.mode !== 'inspect') return;\n const target = event.target as Element;\n if (target.closest('nga-overlay')) return;\n const context = this.inspector.getComponentContext(target);\n if (!context) return;\n\n event.preventDefault();\n event.stopPropagation();\n\n this.selectedContext = context;\n this.annotationText = '';\n this.selectionText = window.getSelection()?.toString() ?? '';\n this.mode = 'annotate';\n this.cdr.markForCheck();\n\n setTimeout(() => { this.textArea?.nativeElement.focus(); }, 0);\n }\n\n submit(): void {\n if (!this.selectedContext || !this.annotationText.trim()) return;\n this.bridge.createAnnotation({\n ...this.selectedContext,\n annotationText: this.annotationText.trim(),\n selectionText: this.selectionText || undefined,\n });\n this.selectedContext = null;\n this.annotationText = '';\n this.mode = 'inspect';\n this.cdr.markForCheck();\n }\n\n cancel(): void {\n this.mode = 'inspect';\n this.cdr.markForCheck();\n }\n\n openThread(annotation: Annotation): void {\n this.threadAnnotation = annotation;\n this.mode = 'thread';\n this.cdr.markForCheck();\n }\n\n closeThread(): void {\n this.threadAnnotation = null;\n this.mode = 'hidden';\n this.cdr.markForCheck();\n }\n\n sendReply(): void {\n if (!this.threadAnnotation || !this.replyText.trim()) return;\n this.bridge.replyToAnnotation(this.threadAnnotation.id, this.replyText.trim());\n this.replyText = '';\n this.cdr.markForCheck();\n }\n\n inputEntries(): { key: string; value: unknown }[] {\n if (!this.selectedContext) return [];\n return Object.entries(this.selectedContext.inputs)\n .slice(0, 5)\n .map(([key, value]) => ({ key, value }));\n }\n\n private updateBadges(annotations: Annotation[]): void {\n this.badges = annotations\n .map((annotation) => {\n const el = this.findComponentElement(annotation.componentName, annotation.selector);\n if (!el) return null;\n const rect = el.getBoundingClientRect();\n return {\n annotation,\n top: `${rect.top.toString()}px`,\n left: `${(rect.left + rect.width - 12).toString()}px`,\n icon: this.badgeIcon(annotation.status),\n label: `${annotation.componentName}: ${annotation.annotationText.slice(0, 40)}`,\n };\n })\n .filter((b): b is AnnotationBadge => b !== null);\n }\n\n private refreshBadgePositions(): void {\n this.badges = this.badges.map((badge) => {\n const el = this.findComponentElement(badge.annotation.componentName, badge.annotation.selector);\n if (!el) return badge;\n const rect = el.getBoundingClientRect();\n return {\n ...badge,\n top: `${rect.top.toString()}px`,\n left: `${(rect.left + rect.width - 12).toString()}px`,\n };\n });\n }\n\n private findComponentElement(componentName: string, selector: string): Element | null {\n const bySelector = document.querySelector(selector);\n if (bySelector) return bySelector;\n\n const all = document.querySelectorAll('*');\n for (const el of Array.from(all)) {\n try {\n const comp = (window as unknown as { ng?: { getComponent: (el: Element) => { constructor: { name: string } } | null } })\n .ng;\n if (comp?.getComponent(el)?.constructor.name === componentName) return el;\n } catch {\n // ignore\n }\n }\n return null;\n }\n\n private badgeIcon(status: AnnotationStatus): string {\n const icons: Record<AnnotationStatus, string> = {\n pending: '●',\n acknowledged: '◐',\n resolved: '✓',\n dismissed: '✕',\n };\n return icons[status];\n }\n}\n","import {\n NgModule,\n isDevMode,\n provideAppInitializer,\n inject,\n ApplicationRef,\n EnvironmentInjector,\n createComponent,\n} from '@angular/core';\nimport { InspectorService } from './inspector.service';\nimport { BridgeService } from './bridge.service';\nimport { OverlayComponent } from './overlay/overlay.component';\n\n@NgModule({\n providers: isDevMode()\n ? [\n InspectorService,\n BridgeService,\n provideAppInitializer(() => {\n const bridge = inject(BridgeService);\n const appRef = inject(ApplicationRef);\n const envInjector = inject(EnvironmentInjector);\n bridge.init();\n const overlayRef = createComponent(OverlayComponent, { environmentInjector: envInjector });\n appRef.attachView(overlayRef.hostView);\n document.body.appendChild(overlayRef.location.nativeElement);\n }),\n ]\n : [],\n})\n// eslint-disable-next-line @typescript-eslint/no-extraneous-class -- required by NgModule pattern\nexport class NgAnnotateModule {}\n","import {\n ApplicationRef,\n EnvironmentInjector,\n createComponent,\n inject,\n isDevMode,\n makeEnvironmentProviders,\n provideAppInitializer,\n} from '@angular/core';\nimport { InspectorService } from './inspector.service';\nimport { BridgeService } from './bridge.service';\nimport { OverlayComponent } from './overlay/overlay.component';\n\nexport function provideNgAnnotate() {\n return makeEnvironmentProviders([\n InspectorService,\n BridgeService,\n provideAppInitializer(() => {\n if (!isDevMode()) return;\n const bridge = inject(BridgeService);\n const appRef = inject(ApplicationRef);\n const envInjector = inject(EnvironmentInjector);\n bridge.init();\n const overlayRef = createComponent(OverlayComponent, { environmentInjector: envInjector });\n appRef.attachView(overlayRef.hostView);\n document.body.appendChild(overlayRef.location.nativeElement);\n }),\n ]);\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;;;AAwBA,MAAM,gBAAgB,GAAG,IAAI;MAGhB,gBAAgB,CAAA;AAC3B,IAAA,mBAAmB,CAAC,OAAgB,EAAA;QAClC,MAAM,SAAS,GAAG,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC;AACpD,QAAA,IAAI,CAAC,SAAS;AAAE,YAAA,OAAO,IAAI;AAE3B,QAAA,MAAM,aAAa,GAAI,SAA+B,CAAC,WAAW,CAAC,IAAI;AACvE,QAAA,MAAM,EAAE,SAAS,EAAE,iBAAiB,EAAE,QAAQ,EAAE,gBAAgB,EAAE,GAChE,IAAI,CAAC,gBAAgB,CAAC,aAAa,CAAC;QACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,SAA8B,CAAC;QACjE,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,SAA8B,CAAC;QAC7D,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;QAC1C,MAAM,iBAAiB,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC;QAErD,OAAO;YACL,aAAa;YACb,iBAAiB;AACjB,YAAA,IAAI,gBAAgB,GAAG,EAAE,gBAAgB,EAAE,GAAG,EAAE,CAAC;YACjD,QAAQ;YACR,MAAM;YACN,WAAW;YACX,iBAAiB;SAClB;IACH;AAEQ,IAAA,oBAAoB,CAAC,OAAgB,EAAA;QAC3C,IAAI,OAAO,GAAmB,OAAO;QACrC,OAAO,OAAO,EAAE;AACd,YAAA,IAAI;gBACF,MAAM,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC;AACrC,gBAAA,IAAI,IAAI;AAAE,oBAAA,OAAO,IAAI;YACvB;AAAE,YAAA,MAAM;;YAER;AACA,YAAA,OAAO,GAAG,OAAO,CAAC,aAAa;QACjC;AACA,QAAA,OAAO,IAAI;IACb;AAEQ,IAAA,WAAW,CAAC,SAA4B,EAAA;AAC9C,QAAA,IAAI;AACF,YAAA,MAAM,GAAG,GAAG,SAAS,CAAC,WAAW,CAAC,IAAI;AACtC,YAAA,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM;AAAE,gBAAA,OAAO,kBAAkB;YACtD,MAAM,KAAK,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;YAC9B,IAAI,CAAC,KAAK,CAAC,MAAM;AAAE,gBAAA,OAAO,kBAAkB;;;AAI5C,YAAA,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE;;gBAEnB,MAAM,SAAS,GAAa,EAAE;AAC9B,gBAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE;AACxC,oBAAA,IAAI,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE;AAC5C,wBAAA,SAAS,CAAC,IAAI,CAAC,CAAA,CAAA,EAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA,CAAA,CAAG,CAAC;oBACzC;gBACF;gBACA,OAAO,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,kBAAkB;YACjD;AACA,YAAA,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACzB;AAAE,QAAA,MAAM;AACN,YAAA,OAAO,kBAAkB;QAC3B;IACF;AAEQ,IAAA,SAAS,CAAC,SAA4B,EAAA;AAC5C,QAAA,IAAI;AACF,YAAA,MAAM,GAAG,GAAG,SAAS,CAAC,WAAW,CAAC,IAAI;YACtC,IAAI,CAAC,GAAG,EAAE,MAAM;AAAE,gBAAA,OAAO,EAAE;YAC3B,MAAM,MAAM,GAA4B,EAAE;AAC1C,YAAA,KAAK,MAAM,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE;gBACnD,IAAI,OAAO,QAAQ,KAAK,QAAQ;oBAAE;AAClC,gBAAA,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC;oBAAE;gBAC9B,MAAM,CAAC,QAAQ,CAAC,GAAI,SAAqC,CAAC,QAAQ,CAAC;YACrE;AACA,YAAA,OAAO,MAAM;QACf;AAAE,QAAA,MAAM;AACN,YAAA,OAAO,EAAE;QACX;IACF;AAEQ,IAAA,aAAa,CAAC,OAAgB,EAAA;QACpC,MAAM,IAAI,GAAa,EAAE;AACzB,QAAA,IAAI,OAAO,GAAmB,OAAO,CAAC,aAAa;QACnD,OAAO,OAAO,EAAE;AACd,YAAA,IAAI;gBACF,MAAM,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC;gBACrC,IAAI,IAAI,EAAE;oBACR,IAAI,CAAC,OAAO,CAAE,IAA0B,CAAC,WAAW,CAAC,IAAI,CAAC;gBAC5D;YACF;AAAE,YAAA,MAAM;;YAER;AACA,YAAA,OAAO,GAAG,OAAO,CAAC,aAAa;QACjC;AACA,QAAA,OAAO,IAAI;IACb;AAEQ,IAAA,QAAQ,CAAC,OAAgB,EAAA;AAC/B,QAAA,MAAM,IAAI,GAAG,OAAO,CAAC,SAAS;AAC9B,QAAA,IAAI,IAAI,CAAC,MAAM,IAAI,gBAAgB;AAAE,YAAA,OAAO,IAAI;QAChD,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,oBAAoB;IAC/D;AAEQ,IAAA,gBAAgB,CAAC,aAAqB,EAAA;AAC5C,QAAA,IAAI;YACF,MAAM,QAAQ,GAAI;AACf,iBAAA,wBAAwB;AAC3B,YAAA,MAAM,KAAK,GAAG,QAAQ,GAAG,aAAa,CAAC;AACvC,YAAA,IAAI,KAAK;AAAE,gBAAA,OAAO,KAAK;QACzB;AAAE,QAAA,MAAM;;QAER;AACA,QAAA,OAAO,EAAE,SAAS,EAAE,gBAAgB,aAAa,CAAA,CAAA,CAAG,EAAE;IACxD;uGAhHW,gBAAgB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;2GAAhB,gBAAgB,EAAA,CAAA;;2FAAhB,gBAAgB,EAAA,UAAA,EAAA,CAAA;kBAD5B;;;MCdY,aAAa,CAAA;AACf,IAAA,QAAQ,GAAG,IAAI,eAAe,CAAiB,IAAI,CAAC;AACpD,IAAA,YAAY,GAAG,IAAI,eAAe,CAAe,EAAE,CAAC;AACpD,IAAA,UAAU,GAAG,IAAI,eAAe,CAAU,KAAK,CAAC;AAExC,IAAA,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC;IAC9B,EAAE,GAAqB,IAAI;IAC3B,cAAc,GAAyC,IAAI;IAEnE,IAAI,GAAA;QACF,IAAI,CAAC,OAAO,EAAE;IAChB;IAEQ,OAAO,GAAA;AACb,QAAA,MAAM,KAAK,GAAG,CAAA,KAAA,EAAQ,QAAQ,CAAC,IAAI,aAAa;QAChD,IAAI,CAAC,EAAE,GAAG,IAAI,SAAS,CAAC,KAAK,CAAC;AAE9B,QAAA,IAAI,CAAC,EAAE,CAAC,MAAM,GAAG,MAAK;AACpB,YAAA,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAK;AACjB,gBAAA,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC;AAC5B,YAAA,CAAC,CAAC;AACJ,QAAA,CAAC;QAED,IAAI,CAAC,EAAE,CAAC,SAAS,GAAG,CAAC,KAA2B,KAAI;AAClD,YAAA,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAK;AACjB,gBAAA,IAAI;oBACF,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAkB;AACpD,oBAAA,IAAI,IAAI,CAAC,IAAI,KAAK,iBAAiB,EAAE;wBACnC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAE,IAA4D,CAAC,OAAO,CAAC;oBAC3F;AAAO,yBAAA,IAAI,IAAI,CAAC,IAAI,KAAK,kBAAkB,EAAE;wBAC3C,IAAI,CAAC,YAAY,CAAC,IAAI,CACnB,IAA6D,CAAC,WAAW,CAC3E;oBACH;AAAO,yBAAA,IAAI,IAAI,CAAC,IAAI,KAAK,oBAAoB,EAAE;AAC7C,wBAAA,MAAM,UAAU,GACd,IACD,CAAC,UAAU;wBACZ,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE;AAC5C,wBAAA,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,GAAG,OAAO,EAAE,UAAU,CAAC,CAAC;oBAClD;AAAO,yBAAA,IAAI,IAAI,CAAC,IAAI,KAAK,iBAAiB,EAAE;AAC1C,wBAAA,MAAM,EAAE,QAAQ,EAAE,GAAG,IAA2D;AAC/E,wBAAA,MAA4D,CAAC,wBAAwB,GAAG,QAAQ;oBACnG;gBACF;AAAE,gBAAA,MAAM;;gBAER;AACF,YAAA,CAAC,CAAC;AACJ,QAAA,CAAC;AAED,QAAA,IAAI,CAAC,EAAE,CAAC,OAAO,GAAG,MAAK;AACrB,YAAA,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAK;AACjB,gBAAA,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC;AAC3B,gBAAA,IAAI,CAAC,cAAc,GAAG,UAAU,CAAC,MAAK,EAAG,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC;AACnE,YAAA,CAAC,CAAC;AACJ,QAAA,CAAC;QAED,IAAI,CAAC,EAAE,CAAC,OAAO,GAAG,CAAC,KAAK,KAAI;AAC1B,YAAA,OAAO,CAAC,IAAI,CAAC,+BAA+B,EAAE,KAAK,CAAC;AACtD,QAAA,CAAC;IACH;AAEA,IAAA,gBAAgB,CAAC,OAAgC,EAAA;QAC/C,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,CAAC;IACnD;IAEA,iBAAiB,CAAC,EAAU,EAAE,OAAe,EAAA;AAC3C,QAAA,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC;IACtD;AAEA,IAAA,gBAAgB,CAAC,EAAU,EAAA;QACzB,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,EAAE,EAAE,CAAC;IAC9C;AAEQ,IAAA,IAAI,CAAC,GAA4B,EAAA;QACvC,IAAI,IAAI,CAAC,EAAE,EAAE,UAAU,KAAK,SAAS,CAAC,IAAI,EAAE;AAC1C,YAAA,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACnC;IACF;IAEA,WAAW,GAAA;AACT,QAAA,IAAI,IAAI,CAAC,cAAc,KAAK,IAAI,EAAE;AAChC,YAAA,YAAY,CAAC,IAAI,CAAC,cAAc,CAAC;AACjC,YAAA,IAAI,CAAC,cAAc,GAAG,IAAI;QAC5B;AACA,QAAA,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE;IAClB;uGArFW,aAAa,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;2GAAb,aAAa,EAAA,CAAA;;2FAAb,aAAa,EAAA,UAAA,EAAA,CAAA;kBADzB;;;MCuNY,gBAAgB,CAAA;AACJ,IAAA,QAAQ;IAE/B,IAAI,GAAgB,QAAQ;IAC5B,cAAc,GAA4B,IAAI;IAC9C,aAAa,GAAyB,IAAI;IAC1C,eAAe,GAA4B,IAAI;IAC/C,cAAc,GAAG,EAAE;IACnB,aAAa,GAAG,EAAE;IAClB,gBAAgB,GAAsB,IAAI;IAC1C,SAAS,GAAG,EAAE;IACd,MAAM,GAAsB,EAAE;AAEb,IAAA,SAAS,GAAG,MAAM,CAAC,gBAAgB,CAAC;AACpC,IAAA,MAAM,GAAG,MAAM,CAAC,aAAa,CAAC;AAC9B,IAAA,GAAG,GAAG,MAAM,CAAC,iBAAiB,CAAC;IAEhD,QAAQ,GAAA;QACN,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC,WAAW,KAAI;AACjD,YAAA,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC;AAC9B,YAAA,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE;AACzB,QAAA,CAAC,CAAC;IACJ;AAGA,IAAA,aAAa,CAAC,KAAa,EAAA;QACzB,KAAK,EAAE,cAAc,EAAE;AACvB,QAAA,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ;AAAE,YAAA,IAAI,CAAC,IAAI,GAAG,SAAS;AAC5C,aAAA,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;AAAE,YAAA,IAAI,CAAC,IAAI,GAAG,QAAQ;AACjD,aAAA,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU;AAAE,YAAA,IAAI,CAAC,IAAI,GAAG,SAAS;AACxD,QAAA,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE;IACzB;IAGA,QAAQ,GAAA;AACN,QAAA,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU;AAAE,YAAA,IAAI,CAAC,IAAI,GAAG,SAAS;AAC9C,aAAA,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;AAAE,YAAA,IAAI,CAAC,IAAI,GAAG,QAAQ;AACjD,aAAA,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ;AAAE,YAAA,IAAI,CAAC,IAAI,GAAG,QAAQ;AACrD,QAAA,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE;IACzB;IAIA,gBAAgB,GAAA;QACd,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE;YAC1B,IAAI,CAAC,qBAAqB,EAAE;AAC5B,YAAA,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE;QACzB;IACF;AAGA,IAAA,WAAW,CAAC,KAAiB,EAAA;AAC3B,QAAA,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;YAAE;AAC7B,QAAA,MAAM,MAAM,GAAG,KAAK,CAAC,MAAiB;AACtC,QAAA,IAAI,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC;YAAE;QACnC,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,mBAAmB,CAAC,MAAM,CAAC;AAC1D,QAAA,IAAI,CAAC,cAAc,GAAG,OAAO;QAC7B,IAAI,OAAO,EAAE;AACX,YAAA,MAAM,IAAI,GAAG,MAAM,CAAC,qBAAqB,EAAE;YAC3C,IAAI,CAAC,aAAa,GAAG;gBACnB,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAA,EAAA,CAAI;gBAC/B,IAAI,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAA,EAAA,CAAI;gBACjC,KAAK,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAA,EAAA,CAAI;gBACnC,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAA,EAAA,CAAI;aACtC;QACH;aAAO;AACL,YAAA,IAAI,CAAC,aAAa,GAAG,IAAI;QAC3B;AACA,QAAA,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE;IACzB;AAGA,IAAA,OAAO,CAAC,KAAiB,EAAA;AACvB,QAAA,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;YAAE;AAC7B,QAAA,MAAM,MAAM,GAAG,KAAK,CAAC,MAAiB;AACtC,QAAA,IAAI,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC;YAAE;QACnC,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,mBAAmB,CAAC,MAAM,CAAC;AAC1D,QAAA,IAAI,CAAC,OAAO;YAAE;QAEd,KAAK,CAAC,cAAc,EAAE;QACtB,KAAK,CAAC,eAAe,EAAE;AAEvB,QAAA,IAAI,CAAC,eAAe,GAAG,OAAO;AAC9B,QAAA,IAAI,CAAC,cAAc,GAAG,EAAE;AACxB,QAAA,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC,YAAY,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;AAC5D,QAAA,IAAI,CAAC,IAAI,GAAG,UAAU;AACtB,QAAA,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE;AAEvB,QAAA,UAAU,CAAC,QAAQ,IAAI,CAAC,QAAQ,EAAE,aAAa,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAChE;IAEA,MAAM,GAAA;QACJ,IAAI,CAAC,IAAI,CAAC,eAAe,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE;YAAE;AAC1D,QAAA,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC;YAC3B,GAAG,IAAI,CAAC,eAAe;AACvB,YAAA,cAAc,EAAE,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE;AAC1C,YAAA,aAAa,EAAE,IAAI,CAAC,aAAa,IAAI,SAAS;AAC/C,SAAA,CAAC;AACF,QAAA,IAAI,CAAC,eAAe,GAAG,IAAI;AAC3B,QAAA,IAAI,CAAC,cAAc,GAAG,EAAE;AACxB,QAAA,IAAI,CAAC,IAAI,GAAG,SAAS;AACrB,QAAA,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE;IACzB;IAEA,MAAM,GAAA;AACJ,QAAA,IAAI,CAAC,IAAI,GAAG,SAAS;AACrB,QAAA,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE;IACzB;AAEA,IAAA,UAAU,CAAC,UAAsB,EAAA;AAC/B,QAAA,IAAI,CAAC,gBAAgB,GAAG,UAAU;AAClC,QAAA,IAAI,CAAC,IAAI,GAAG,QAAQ;AACpB,QAAA,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE;IACzB;IAEA,WAAW,GAAA;AACT,QAAA,IAAI,CAAC,gBAAgB,GAAG,IAAI;AAC5B,QAAA,IAAI,CAAC,IAAI,GAAG,QAAQ;AACpB,QAAA,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE;IACzB;IAEA,SAAS,GAAA;QACP,IAAI,CAAC,IAAI,CAAC,gBAAgB,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE;YAAE;AACtD,QAAA,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,gBAAgB,CAAC,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;AAC9E,QAAA,IAAI,CAAC,SAAS,GAAG,EAAE;AACnB,QAAA,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE;IACzB;IAEA,YAAY,GAAA;QACV,IAAI,CAAC,IAAI,CAAC,eAAe;AAAE,YAAA,OAAO,EAAE;QACpC,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,eAAe,CAAC,MAAM;AAC9C,aAAA,KAAK,CAAC,CAAC,EAAE,CAAC;AACV,aAAA,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;IAC5C;AAEQ,IAAA,YAAY,CAAC,WAAyB,EAAA;QAC5C,IAAI,CAAC,MAAM,GAAG;AACX,aAAA,GAAG,CAAC,CAAC,UAAU,KAAI;AAClB,YAAA,MAAM,EAAE,GAAG,IAAI,CAAC,oBAAoB,CAAC,UAAU,CAAC,aAAa,EAAE,UAAU,CAAC,QAAQ,CAAC;AACnF,YAAA,IAAI,CAAC,EAAE;AAAE,gBAAA,OAAO,IAAI;AACpB,YAAA,MAAM,IAAI,GAAG,EAAE,CAAC,qBAAqB,EAAE;YACvC,OAAO;gBACL,UAAU;gBACV,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAA,EAAA,CAAI;AAC/B,gBAAA,IAAI,EAAE,CAAA,EAAG,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,GAAG,EAAE,EAAE,QAAQ,EAAE,CAAA,EAAA,CAAI;gBACrD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,MAAM,CAAC;AACvC,gBAAA,KAAK,EAAE,CAAA,EAAG,UAAU,CAAC,aAAa,KAAK,UAAU,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA,CAAE;aAChF;AACH,QAAA,CAAC;aACA,MAAM,CAAC,CAAC,CAAC,KAA2B,CAAC,KAAK,IAAI,CAAC;IACpD;IAEQ,qBAAqB,GAAA;AAC3B,QAAA,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,KAAI;AACtC,YAAA,MAAM,EAAE,GAAG,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC,UAAU,CAAC,aAAa,EAAE,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC;AAC/F,YAAA,IAAI,CAAC,EAAE;AAAE,gBAAA,OAAO,KAAK;AACrB,YAAA,MAAM,IAAI,GAAG,EAAE,CAAC,qBAAqB,EAAE;YACvC,OAAO;AACL,gBAAA,GAAG,KAAK;gBACR,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAA,EAAA,CAAI;AAC/B,gBAAA,IAAI,EAAE,CAAA,EAAG,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,GAAG,EAAE,EAAE,QAAQ,EAAE,CAAA,EAAA,CAAI;aACtD;AACH,QAAA,CAAC,CAAC;IACJ;IAEQ,oBAAoB,CAAC,aAAqB,EAAE,QAAgB,EAAA;QAClE,MAAM,UAAU,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC;AACnD,QAAA,IAAI,UAAU;AAAE,YAAA,OAAO,UAAU;QAEjC,MAAM,GAAG,GAAG,QAAQ,CAAC,gBAAgB,CAAC,GAAG,CAAC;QAC1C,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;AAChC,YAAA,IAAI;gBACF,MAAM,IAAI,GAAI;AACX,qBAAA,EAAE;gBACL,IAAI,IAAI,EAAE,YAAY,CAAC,EAAE,CAAC,EAAE,WAAW,CAAC,IAAI,KAAK,aAAa;AAAE,oBAAA,OAAO,EAAE;YAC3E;AAAE,YAAA,MAAM;;YAER;QACF;AACA,QAAA,OAAO,IAAI;IACb;AAEQ,IAAA,SAAS,CAAC,MAAwB,EAAA;AACxC,QAAA,MAAM,KAAK,GAAqC;AAC9C,YAAA,OAAO,EAAE,GAAG;AACZ,YAAA,YAAY,EAAE,GAAG;AACjB,YAAA,QAAQ,EAAE,GAAG;AACb,YAAA,SAAS,EAAE,GAAG;SACf;AACD,QAAA,OAAO,KAAK,CAAC,MAAM,CAAC;IACtB;uGA9LW,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,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,aAAA,EAAA,IAAA,EAAA,EAAA,SAAA,EAAA,EAAA,8BAAA,EAAA,uBAAA,EAAA,yBAAA,EAAA,YAAA,EAAA,eAAA,EAAA,oBAAA,EAAA,eAAA,EAAA,oBAAA,EAAA,oBAAA,EAAA,qBAAA,EAAA,gBAAA,EAAA,iBAAA,EAAA,EAAA,EAAA,WAAA,EAAA,CAAA,EAAA,YAAA,EAAA,UAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EAAA,CAAA,UAAA,CAAA,EAAA,WAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EA9GjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4GT,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,miFAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EA5LmB,WAAW,0mBAArB,QAAQ,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FA8LP,gBAAgB,EAAA,UAAA,EAAA,CAAA;kBAjM5B,SAAS;+BACE,aAAa,EAAA,eAAA,EACN,uBAAuB,CAAC,MAAM,EAAA,OAAA,EACtC,CAAC,QAAQ,EAAE,WAAW,CAAC,EAAA,QAAA,EAgFtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4GT,EAAA,CAAA,EAAA,MAAA,EAAA,CAAA,miFAAA,CAAA,EAAA;;sBAGA,SAAS;uBAAC,UAAU;;sBAuBpB,YAAY;uBAAC,8BAA8B,EAAE,CAAC,QAAQ,CAAC;;sBASvD,YAAY;uBAAC,yBAAyB;;sBAQtC,YAAY;uBAAC,eAAe;;sBAC5B,YAAY;uBAAC,eAAe;;sBAQ5B,YAAY;uBAAC,oBAAoB,EAAE,CAAC,QAAQ,CAAC;;sBAqB7C,YAAY;uBAAC,gBAAgB,EAAE,CAAC,QAAQ,CAAC;;;AC3Q5C;MACa,gBAAgB,CAAA;uGAAhB,gBAAgB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,QAAA,EAAA,CAAA;wGAAhB,gBAAgB,EAAA,CAAA;wGAAhB,gBAAgB,EAAA,SAAA,EAjBhB,SAAS;AAClB,cAAE;gBACE,gBAAgB;gBAChB,aAAa;gBACb,qBAAqB,CAAC,MAAK;AACzB,oBAAA,MAAM,MAAM,GAAG,MAAM,CAAC,aAAa,CAAC;AACpC,oBAAA,MAAM,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC;AACrC,oBAAA,MAAM,WAAW,GAAG,MAAM,CAAC,mBAAmB,CAAC;oBAC/C,MAAM,CAAC,IAAI,EAAE;AACb,oBAAA,MAAM,UAAU,GAAG,eAAe,CAAC,gBAAgB,EAAE,EAAE,mBAAmB,EAAE,WAAW,EAAE,CAAC;AAC1F,oBAAA,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,QAAQ,CAAC;oBACtC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,QAAQ,CAAC,aAAa,CAAC;AAC9D,gBAAA,CAAC,CAAC;AACH;AACH,cAAE,EAAE,EAAA,CAAA;;2FAGK,gBAAgB,EAAA,UAAA,EAAA,CAAA;kBAlB5B,QAAQ;AAAC,YAAA,IAAA,EAAA,CAAA;oBACR,SAAS,EAAE,SAAS;AAClB,0BAAE;4BACE,gBAAgB;4BAChB,aAAa;4BACb,qBAAqB,CAAC,MAAK;AACzB,gCAAA,MAAM,MAAM,GAAG,MAAM,CAAC,aAAa,CAAC;AACpC,gCAAA,MAAM,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC;AACrC,gCAAA,MAAM,WAAW,GAAG,MAAM,CAAC,mBAAmB,CAAC;gCAC/C,MAAM,CAAC,IAAI,EAAE;AACb,gCAAA,MAAM,UAAU,GAAG,eAAe,CAAC,gBAAgB,EAAE,EAAE,mBAAmB,EAAE,WAAW,EAAE,CAAC;AAC1F,gCAAA,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,QAAQ,CAAC;gCACtC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,QAAQ,CAAC,aAAa,CAAC;AAC9D,4BAAA,CAAC,CAAC;AACH;AACH,0BAAE,EAAE;AACP,iBAAA;;;SChBe,iBAAiB,GAAA;AAC/B,IAAA,OAAO,wBAAwB,CAAC;QAC9B,gBAAgB;QAChB,aAAa;QACb,qBAAqB,CAAC,MAAK;YACzB,IAAI,CAAC,SAAS,EAAE;gBAAE;AAClB,YAAA,MAAM,MAAM,GAAG,MAAM,CAAC,aAAa,CAAC;AACpC,YAAA,MAAM,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC;AACrC,YAAA,MAAM,WAAW,GAAG,MAAM,CAAC,mBAAmB,CAAC;YAC/C,MAAM,CAAC,IAAI,EAAE;AACb,YAAA,MAAM,UAAU,GAAG,eAAe,CAAC,gBAAgB,EAAE,EAAE,mBAAmB,EAAE,WAAW,EAAE,CAAC;AAC1F,YAAA,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,QAAQ,CAAC;YACtC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,QAAQ,CAAC,aAAa,CAAC;AAC9D,QAAA,CAAC,CAAC;AACH,KAAA,CAAC;AACJ;;AC5BA;;AAEG;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ng-annotate/angular",
3
- "version": "0.3.14",
3
+ "version": "0.4.1",
4
4
  "schematics": "./schematics/collection.json",
5
5
  "description": "Angular library for ng-annotate-mcp — browser overlay for annotating components and routing instructions to an AI agent",
6
6
  "keywords": [
@@ -55,8 +55,52 @@ function checkAngularVersion() {
55
55
  }
56
56
  };
57
57
  }
58
+ function addProxyConfig() {
59
+ return (tree, context) => {
60
+ // Create proxy.conf.json
61
+ const proxyPath = 'proxy.conf.json';
62
+ if (!tree.exists(proxyPath)) {
63
+ tree.create(proxyPath, JSON.stringify({ '/__annotate': { target: 'ws://localhost:4201', ws: true } }, null, 2) + '\n');
64
+ context.logger.info('✅ Created proxy.conf.json');
65
+ }
66
+ // Update angular.json serve options
67
+ const angularJsonPath = 'angular.json';
68
+ if (!tree.exists(angularJsonPath)) {
69
+ context.logger.warn('⚠️ Could not find angular.json — add proxyConfig manually');
70
+ return;
71
+ }
72
+ const angularJson = JSON.parse(tree.read(angularJsonPath).toString('utf-8'));
73
+ const projects = angularJson['projects'];
74
+ if (!projects)
75
+ return;
76
+ let changed = false;
77
+ for (const projectName of Object.keys(projects)) {
78
+ const project = projects[projectName];
79
+ const architect = project['architect'];
80
+ if (!architect)
81
+ continue;
82
+ const serve = architect['serve'];
83
+ if (!serve)
84
+ continue;
85
+ const options = (serve['options'] ?? {});
86
+ if (options['proxyConfig'])
87
+ continue;
88
+ serve['options'] = { ...options, proxyConfig: 'proxy.conf.json' };
89
+ changed = true;
90
+ context.logger.info(`✅ Added proxyConfig to angular.json (${projectName})`);
91
+ }
92
+ if (changed) {
93
+ tree.overwrite(angularJsonPath, JSON.stringify(angularJson, null, 2) + '\n');
94
+ }
95
+ };
96
+ }
58
97
  function addVitePlugin() {
59
98
  return (tree, context) => {
99
+ // Angular CLI does not load vite.config.ts plugins — skip for Angular projects
100
+ if (tree.exists('angular.json')) {
101
+ context.logger.info('Angular project detected — skipping vite.config.ts setup (using proxy instead).');
102
+ return;
103
+ }
60
104
  const candidates = ['vite.config.ts', 'vite.config.js', 'vite.config.mts'];
61
105
  const viteConfigPath = candidates.find((p) => tree.exists(p));
62
106
  if (!viteConfigPath) {
@@ -266,6 +310,7 @@ function default_1(options) {
266
310
  return (0, schematics_1.chain)([
267
311
  checkAngularVersion(),
268
312
  addDevDependency(),
313
+ addProxyConfig(),
269
314
  addVitePlugin(),
270
315
  addProviders(),
271
316
  addMcpConfig(options),
@@ -162,6 +162,72 @@ describe('ng-add schematic — addProviders', () => {
162
162
  });
163
163
  });
164
164
 
165
+ describe('ng-add schematic — addProxyConfig', () => {
166
+ const ANGULAR_JSON = JSON.stringify({
167
+ projects: {
168
+ 'my-app': {
169
+ architect: {
170
+ serve: {
171
+ builder: '@angular/build:dev-server',
172
+ options: {},
173
+ },
174
+ },
175
+ },
176
+ },
177
+ });
178
+
179
+ it('creates proxy.conf.json and updates angular.json', async () => {
180
+ const tree = makeTree({ 'package.json': BASE_PKG, 'angular.json': ANGULAR_JSON });
181
+ const result = await runSchematic(tree);
182
+ expect(result.exists('proxy.conf.json')).toBe(true);
183
+ expect(result.readText('proxy.conf.json')).toContain('/__annotate');
184
+ const angular = JSON.parse(result.readText('angular.json')) as Record<string, unknown>;
185
+ const projects = angular['projects'] as Record<string, Record<string, unknown>>;
186
+ const serve = (projects['my-app']['architect'] as Record<string, Record<string, unknown>>)['serve'];
187
+ expect((serve['options'] as Record<string, unknown>)['proxyConfig']).toBe('proxy.conf.json');
188
+ });
189
+
190
+ it('skips proxy.conf.json if already exists', async () => {
191
+ const original = '{"/__annotate":{"target":"ws://localhost:4201","ws":true}}\n';
192
+ const tree = makeTree({
193
+ 'package.json': BASE_PKG,
194
+ 'angular.json': ANGULAR_JSON,
195
+ 'proxy.conf.json': original,
196
+ });
197
+ const result = await runSchematic(tree);
198
+ expect(result.readText('proxy.conf.json')).toBe(original);
199
+ });
200
+
201
+ it('skips angular.json proxyConfig if already set', async () => {
202
+ const withProxy = JSON.stringify({
203
+ projects: {
204
+ 'my-app': {
205
+ architect: {
206
+ serve: {
207
+ builder: '@angular/build:dev-server',
208
+ options: { proxyConfig: 'proxy.conf.json' },
209
+ },
210
+ },
211
+ },
212
+ },
213
+ });
214
+ const tree = makeTree({ 'package.json': BASE_PKG, 'angular.json': withProxy });
215
+ const result = await runSchematic(tree);
216
+ // Content should be unchanged (same proxyConfig value)
217
+ const angular = JSON.parse(result.readText('angular.json')) as Record<string, unknown>;
218
+ const projects = angular['projects'] as Record<string, Record<string, unknown>>;
219
+ const serve = (projects['my-app']['architect'] as Record<string, Record<string, unknown>>)['serve'];
220
+ expect((serve['options'] as Record<string, unknown>)['proxyConfig']).toBe('proxy.conf.json');
221
+ });
222
+
223
+ it('skips vite.config.ts setup when angular.json present', async () => {
224
+ const tree = makeTree({ 'package.json': BASE_PKG, 'angular.json': ANGULAR_JSON });
225
+ const result = await runSchematic(tree);
226
+ // Should NOT create vite.config.ts (Angular doesn't load vite plugins)
227
+ expect(result.exists('vite.config.ts')).toBe(false);
228
+ });
229
+ });
230
+
165
231
  describe('ng-add schematic — addGitignore', () => {
166
232
  it('creates .gitignore with .ng-annotate/ when none exists', async () => {
167
233
  const tree = makeTree({ 'package.json': BASE_PKG });
@@ -33,8 +33,63 @@ function checkAngularVersion(): Rule {
33
33
  };
34
34
  }
35
35
 
36
+ function addProxyConfig(): Rule {
37
+ return (tree: Tree, context: SchematicContext) => {
38
+ // Create proxy.conf.json
39
+ const proxyPath = 'proxy.conf.json';
40
+ if (!tree.exists(proxyPath)) {
41
+ tree.create(
42
+ proxyPath,
43
+ JSON.stringify({ '/__annotate': { target: 'ws://localhost:4201', ws: true } }, null, 2) + '\n',
44
+ );
45
+ context.logger.info('✅ Created proxy.conf.json');
46
+ }
47
+
48
+ // Update angular.json serve options
49
+ const angularJsonPath = 'angular.json';
50
+ if (!tree.exists(angularJsonPath)) {
51
+ context.logger.warn('⚠️ Could not find angular.json — add proxyConfig manually');
52
+ return;
53
+ }
54
+
55
+ const angularJson = JSON.parse(tree.read(angularJsonPath)!.toString('utf-8')) as Record<
56
+ string,
57
+ unknown
58
+ >;
59
+ const projects = angularJson['projects'] as Record<string, unknown> | undefined;
60
+ if (!projects) return;
61
+
62
+ let changed = false;
63
+ for (const projectName of Object.keys(projects)) {
64
+ const project = projects[projectName] as Record<string, unknown>;
65
+ const architect = project['architect'] as Record<string, unknown> | undefined;
66
+ if (!architect) continue;
67
+
68
+ const serve = architect['serve'] as Record<string, unknown> | undefined;
69
+ if (!serve) continue;
70
+
71
+ const options = (serve['options'] ?? {}) as Record<string, unknown>;
72
+ if (options['proxyConfig']) continue;
73
+
74
+ serve['options'] = { ...options, proxyConfig: 'proxy.conf.json' };
75
+ changed = true;
76
+ context.logger.info(`✅ Added proxyConfig to angular.json (${projectName})`);
77
+ }
78
+
79
+ if (changed) {
80
+ tree.overwrite(angularJsonPath, JSON.stringify(angularJson, null, 2) + '\n');
81
+ }
82
+ };
83
+ }
84
+
36
85
  function addVitePlugin(): Rule {
37
86
  return (tree: Tree, context: SchematicContext) => {
87
+ // Angular CLI does not load vite.config.ts plugins — skip for Angular projects
88
+ if (tree.exists('angular.json')) {
89
+ context.logger.info('Angular project detected — skipping vite.config.ts setup (using proxy instead).');
90
+ return;
91
+ }
92
+
38
93
  const candidates = ['vite.config.ts', 'vite.config.js', 'vite.config.mts'];
39
94
  const viteConfigPath = candidates.find((p) => tree.exists(p));
40
95
 
@@ -297,6 +352,7 @@ export default function (options: Options): Rule {
297
352
  return chain([
298
353
  checkAngularVersion(),
299
354
  addDevDependency(),
355
+ addProxyConfig(),
300
356
  addVitePlugin(),
301
357
  addProviders(),
302
358
  addMcpConfig(options),