@ng-annotate/angular 0.4.0 → 0.5.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.
- package/builders.json +10 -0
- package/dist/builders/dev-server/index.d.ts +3 -0
- package/dist/builders/dev-server/index.js +216 -0
- package/dist/builders/dev-server/schema.json +7 -0
- package/dist/fesm2022/ng-annotate-angular.mjs +4 -0
- package/dist/fesm2022/ng-annotate-angular.mjs.map +1 -1
- package/package.json +13 -2
- package/schematics/ng-add/index.js +36 -26
- package/schematics/ng-add/index.spec.ts +76 -36
- package/schematics/ng-add/index.ts +45 -29
package/builders.json
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "@angular-devkit/architect/builders-schema.json",
|
|
3
|
+
"builders": {
|
|
4
|
+
"dev-server": {
|
|
5
|
+
"implementation": "./dist/builders/dev-server/index",
|
|
6
|
+
"schema": "./dist/builders/dev-server/schema.json",
|
|
7
|
+
"description": "ng-annotate dev server — wraps @angular/build:dev-server with integrated WebSocket support and component manifest injection. No proxy configuration required."
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const architect_1 = require("@angular-devkit/architect");
|
|
7
|
+
const build_1 = require("@angular/build");
|
|
8
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const node_crypto_1 = require("node:crypto");
|
|
11
|
+
const ws_1 = require("ws");
|
|
12
|
+
// ─── Store ────────────────────────────────────────────────────────────────────
|
|
13
|
+
const STORE_DIR = '.ng-annotate';
|
|
14
|
+
function makeStore(projectRoot) {
|
|
15
|
+
const storePath = node_path_1.default.join(projectRoot, STORE_DIR, 'store.json');
|
|
16
|
+
function ensureStore() {
|
|
17
|
+
const dir = node_path_1.default.dirname(storePath);
|
|
18
|
+
if (!node_fs_1.default.existsSync(dir))
|
|
19
|
+
node_fs_1.default.mkdirSync(dir, { recursive: true });
|
|
20
|
+
if (!node_fs_1.default.existsSync(storePath)) {
|
|
21
|
+
node_fs_1.default.writeFileSync(storePath, JSON.stringify({ sessions: {}, annotations: {} }, null, 2), 'utf8');
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
let writeQueue = Promise.resolve();
|
|
25
|
+
async function withLock(fn) {
|
|
26
|
+
ensureStore();
|
|
27
|
+
const result = writeQueue.then(() => {
|
|
28
|
+
const raw = node_fs_1.default.readFileSync(storePath, 'utf8');
|
|
29
|
+
const data = JSON.parse(raw);
|
|
30
|
+
const updated = fn(data);
|
|
31
|
+
node_fs_1.default.writeFileSync(storePath, JSON.stringify(updated, null, 2), 'utf8');
|
|
32
|
+
return updated;
|
|
33
|
+
});
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
35
|
+
writeQueue = result.catch(() => { });
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
function readStore() {
|
|
39
|
+
ensureStore();
|
|
40
|
+
return JSON.parse(node_fs_1.default.readFileSync(storePath, 'utf8'));
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
async createSession(url) {
|
|
44
|
+
const now = new Date().toISOString();
|
|
45
|
+
const session = { id: (0, node_crypto_1.randomUUID)(), createdAt: now, lastSeenAt: now, active: true, url };
|
|
46
|
+
await withLock((data) => { data.sessions[session.id] = session; return data; });
|
|
47
|
+
return session;
|
|
48
|
+
},
|
|
49
|
+
async updateSession(id, patch) {
|
|
50
|
+
await withLock((data) => {
|
|
51
|
+
const s = data.sessions[id];
|
|
52
|
+
if (s)
|
|
53
|
+
data.sessions[id] = { ...s, ...patch, id };
|
|
54
|
+
return data;
|
|
55
|
+
});
|
|
56
|
+
},
|
|
57
|
+
async createAnnotation(payload) {
|
|
58
|
+
const annotation = {
|
|
59
|
+
id: (0, node_crypto_1.randomUUID)(),
|
|
60
|
+
createdAt: new Date().toISOString(),
|
|
61
|
+
status: 'pending',
|
|
62
|
+
replies: [],
|
|
63
|
+
...payload,
|
|
64
|
+
};
|
|
65
|
+
await withLock((data) => { data.annotations[annotation.id] = annotation; return data; });
|
|
66
|
+
return annotation;
|
|
67
|
+
},
|
|
68
|
+
listAnnotations(sessionId) {
|
|
69
|
+
const data = readStore();
|
|
70
|
+
return Object.values(data.annotations)
|
|
71
|
+
.filter((a) => a?.sessionId === sessionId)
|
|
72
|
+
.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
73
|
+
},
|
|
74
|
+
async addReply(annotationId, reply) {
|
|
75
|
+
let result;
|
|
76
|
+
await withLock((data) => {
|
|
77
|
+
const annotation = data.annotations[annotationId];
|
|
78
|
+
if (!annotation)
|
|
79
|
+
return data;
|
|
80
|
+
annotation.replies.push({ id: (0, node_crypto_1.randomUUID)(), createdAt: new Date().toISOString(), ...reply });
|
|
81
|
+
result = annotation;
|
|
82
|
+
return data;
|
|
83
|
+
});
|
|
84
|
+
return result;
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function buildManifest(projectRoot) {
|
|
89
|
+
const manifest = {};
|
|
90
|
+
const srcDir = node_path_1.default.join(projectRoot, 'src');
|
|
91
|
+
if (!node_fs_1.default.existsSync(srcDir))
|
|
92
|
+
return manifest;
|
|
93
|
+
function scan(dir) {
|
|
94
|
+
for (const entry of node_fs_1.default.readdirSync(dir, { withFileTypes: true })) {
|
|
95
|
+
const fullPath = node_path_1.default.join(dir, entry.name);
|
|
96
|
+
if (entry.isDirectory()) {
|
|
97
|
+
scan(fullPath);
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (!entry.name.endsWith('.ts') || entry.name.endsWith('.spec.ts'))
|
|
101
|
+
continue;
|
|
102
|
+
const code = node_fs_1.default.readFileSync(fullPath, 'utf8');
|
|
103
|
+
if (!code.includes('@Component'))
|
|
104
|
+
continue;
|
|
105
|
+
const classMatch = /export\s+class\s+(\w+)/.exec(code);
|
|
106
|
+
if (!classMatch)
|
|
107
|
+
continue;
|
|
108
|
+
const relPath = node_path_1.default.relative(projectRoot, fullPath).replace(/\\/g, '/');
|
|
109
|
+
const item = { component: relPath };
|
|
110
|
+
const templateMatch = /templateUrl\s*:\s*['"`]([^'"`]+)['"`]/.exec(code);
|
|
111
|
+
if (templateMatch) {
|
|
112
|
+
const templateAbs = node_path_1.default.resolve(node_path_1.default.dirname(fullPath), templateMatch[1]);
|
|
113
|
+
item.template = node_path_1.default.relative(projectRoot, templateAbs).replace(/\\/g, '/');
|
|
114
|
+
}
|
|
115
|
+
manifest[classMatch[1]] = item;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
scan(srcDir);
|
|
119
|
+
return manifest;
|
|
120
|
+
}
|
|
121
|
+
// ─── WebSocket handler ────────────────────────────────────────────────────────
|
|
122
|
+
const SYNC_INTERVAL_MS = 2000;
|
|
123
|
+
function safeSend(ws, data) {
|
|
124
|
+
if (ws.readyState !== ws_1.WebSocket.OPEN)
|
|
125
|
+
return;
|
|
126
|
+
ws.send(JSON.stringify(data), (err) => { if (err) { /* connection closed mid-send */ } });
|
|
127
|
+
}
|
|
128
|
+
function createAnnotateWsHandler(store) {
|
|
129
|
+
const wss = new ws_1.WebSocketServer({ noServer: true });
|
|
130
|
+
const sessionSockets = new Map();
|
|
131
|
+
wss.on('connection', (ws, req) => {
|
|
132
|
+
void (async () => {
|
|
133
|
+
const url = req.headers.referer ?? req.headers.origin ?? '';
|
|
134
|
+
let sessionId;
|
|
135
|
+
try {
|
|
136
|
+
const session = await store.createSession(url);
|
|
137
|
+
sessionId = session.id;
|
|
138
|
+
safeSend(ws, { type: 'session:created', session });
|
|
139
|
+
sessionSockets.set(sessionId, ws);
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
process.stderr.write(`[ng-annotate] Failed to create session: ${String(err)}\n`);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
ws.on('message', (raw) => {
|
|
146
|
+
void (async () => {
|
|
147
|
+
let msg;
|
|
148
|
+
try {
|
|
149
|
+
msg = JSON.parse(raw.toString());
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
try {
|
|
155
|
+
if (msg.type === 'annotation:create' && msg.payload) {
|
|
156
|
+
const annotation = await store.createAnnotation({ ...msg.payload, sessionId });
|
|
157
|
+
safeSend(ws, { type: 'annotation:created', annotation });
|
|
158
|
+
}
|
|
159
|
+
else if (msg.type === 'annotation:reply' && msg.id && msg.message) {
|
|
160
|
+
const updated = await store.addReply(msg.id, { author: 'user', message: msg.message });
|
|
161
|
+
if (updated)
|
|
162
|
+
safeSend(ws, { type: 'annotation:updated', annotation: updated });
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
process.stderr.write(`[ng-annotate] Failed to process message: ${String(err)}\n`);
|
|
167
|
+
}
|
|
168
|
+
})();
|
|
169
|
+
});
|
|
170
|
+
ws.on('close', () => {
|
|
171
|
+
void store.updateSession(sessionId, { active: false });
|
|
172
|
+
sessionSockets.delete(sessionId);
|
|
173
|
+
});
|
|
174
|
+
})();
|
|
175
|
+
});
|
|
176
|
+
setInterval(() => {
|
|
177
|
+
for (const [sessionId, ws] of sessionSockets) {
|
|
178
|
+
if (ws.readyState !== ws_1.WebSocket.OPEN)
|
|
179
|
+
continue;
|
|
180
|
+
const annotations = store.listAnnotations(sessionId);
|
|
181
|
+
safeSend(ws, { type: 'annotations:sync', annotations });
|
|
182
|
+
}
|
|
183
|
+
}, SYNC_INTERVAL_MS);
|
|
184
|
+
return { wss };
|
|
185
|
+
}
|
|
186
|
+
// ─── Builder ──────────────────────────────────────────────────────────────────
|
|
187
|
+
exports.default = (0, architect_1.createBuilder)((options, context) => {
|
|
188
|
+
const projectRoot = context.workspaceRoot;
|
|
189
|
+
const store = makeStore(projectRoot);
|
|
190
|
+
const manifest = buildManifest(projectRoot);
|
|
191
|
+
const { wss } = createAnnotateWsHandler(store);
|
|
192
|
+
let wsAttached = false;
|
|
193
|
+
const middleware = (req, _res, next) => {
|
|
194
|
+
if (!wsAttached) {
|
|
195
|
+
wsAttached = true;
|
|
196
|
+
const httpServer = req.socket.server;
|
|
197
|
+
httpServer.on('upgrade', (upgradeReq, socket, head) => {
|
|
198
|
+
if (upgradeReq.url === '/__annotate') {
|
|
199
|
+
wss.handleUpgrade(upgradeReq, socket, head, (ws) => {
|
|
200
|
+
wss.emit('connection', ws, upgradeReq);
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
process.stderr.write(`[ng-annotate] WebSocket handler attached to Angular dev server\n`);
|
|
205
|
+
}
|
|
206
|
+
next();
|
|
207
|
+
};
|
|
208
|
+
const indexHtmlTransformer = (content) => {
|
|
209
|
+
const script = `<script>window.__NG_ANNOTATE_MANIFEST__ = ${JSON.stringify(manifest)};</script>`;
|
|
210
|
+
return Promise.resolve(content.replace('</head>', ` ${script}\n</head>`));
|
|
211
|
+
};
|
|
212
|
+
return (0, build_1.executeDevServerBuilder)(options, context, {
|
|
213
|
+
middleware: [middleware],
|
|
214
|
+
indexHtmlTransformer,
|
|
215
|
+
});
|
|
216
|
+
});
|
|
@@ -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 <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 <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,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ng-annotate/angular",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"schematics": "./schematics/collection.json",
|
|
5
|
+
"builders": "./builders.json",
|
|
5
6
|
"description": "Angular library for ng-annotate-mcp — browser overlay for annotating components and routing instructions to an AI agent",
|
|
6
7
|
"keywords": [
|
|
7
8
|
"angular",
|
|
@@ -19,6 +20,7 @@
|
|
|
19
20
|
"files": [
|
|
20
21
|
"dist",
|
|
21
22
|
"schematics",
|
|
23
|
+
"builders.json",
|
|
22
24
|
"README.md"
|
|
23
25
|
],
|
|
24
26
|
"main": "dist/fesm2022/ng-annotate-angular.mjs",
|
|
@@ -34,21 +36,30 @@
|
|
|
34
36
|
}
|
|
35
37
|
},
|
|
36
38
|
"scripts": {
|
|
37
|
-
"build": "ng-packagr -p ng-package.json && npm run build:schematics",
|
|
39
|
+
"build": "ng-packagr -p ng-package.json && npm run build:schematics && npm run build:builders",
|
|
38
40
|
"build:watch": "ng-packagr -p ng-package.json --watch",
|
|
39
41
|
"build:schematics": "tsc -p schematics/tsconfig.json",
|
|
42
|
+
"build:builders": "tsc -p builders/tsconfig.json && node -e \"require('fs').cpSync('src/builders/dev-server/schema.json','dist/builders/dev-server/schema.json')\"",
|
|
40
43
|
"test": "vitest run schematics",
|
|
41
44
|
"test:watch": "vitest schematics",
|
|
42
45
|
"lint": "eslint src/",
|
|
43
46
|
"lint:fix": "eslint src/ --fix"
|
|
44
47
|
},
|
|
45
48
|
"peerDependencies": {
|
|
49
|
+
"@angular-devkit/architect": ">=0.1700.0",
|
|
50
|
+
"@angular/build": ">=21.0.0",
|
|
46
51
|
"@angular/core": ">=21.0.0",
|
|
47
52
|
"rxjs": ">=7.0.0"
|
|
48
53
|
},
|
|
54
|
+
"dependencies": {
|
|
55
|
+
"ws": "^8.19.0"
|
|
56
|
+
},
|
|
49
57
|
"devDependencies": {
|
|
58
|
+
"@angular-devkit/architect": ">=0.2100.0",
|
|
50
59
|
"@angular-devkit/schematics": ">=17.0.0",
|
|
60
|
+
"@angular/build": "^21.0.0",
|
|
51
61
|
"@angular/core": "^21.0.0",
|
|
62
|
+
"@types/ws": "^8.18.1",
|
|
52
63
|
"ng-packagr": "^21.2.0",
|
|
53
64
|
"rxjs": "^7.0.0",
|
|
54
65
|
"typescript": "~5.9.0",
|
|
@@ -40,6 +40,8 @@ const fs = __importStar(require("fs"));
|
|
|
40
40
|
const path = __importStar(require("path"));
|
|
41
41
|
const helpers_1 = require("./helpers");
|
|
42
42
|
const MIN_ANGULAR_MAJOR = 21;
|
|
43
|
+
const NG_ANNOTATE_BUILDER = '@ng-annotate/angular:dev-server';
|
|
44
|
+
const ANGULAR_DEV_SERVER_BUILDER = '@angular/build:dev-server';
|
|
43
45
|
function checkAngularVersion() {
|
|
44
46
|
return (tree) => {
|
|
45
47
|
const pkgPath = 'package.json';
|
|
@@ -55,32 +57,45 @@ function checkAngularVersion() {
|
|
|
55
57
|
}
|
|
56
58
|
};
|
|
57
59
|
}
|
|
58
|
-
function
|
|
60
|
+
function updateAngularJsonBuilder() {
|
|
59
61
|
return (tree, context) => {
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
tree.create('vite.config.ts', created);
|
|
65
|
-
context.logger.info('✅ Created vite.config.ts with ngAnnotateMcp()');
|
|
62
|
+
const angularJsonPath = 'angular.json';
|
|
63
|
+
if (!tree.exists(angularJsonPath)) {
|
|
64
|
+
context.logger.warn('⚠️ Could not find angular.json — update the serve builder manually:\n' +
|
|
65
|
+
` "builder": "${NG_ANNOTATE_BUILDER}"`);
|
|
66
66
|
return;
|
|
67
67
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
const angularJson = JSON.parse(tree.read(angularJsonPath).toString('utf-8'));
|
|
69
|
+
const projects = angularJson['projects'];
|
|
70
|
+
if (!projects)
|
|
71
71
|
return;
|
|
72
|
+
let changed = false;
|
|
73
|
+
for (const projectName of Object.keys(projects)) {
|
|
74
|
+
const project = projects[projectName];
|
|
75
|
+
const architect = project['architect'];
|
|
76
|
+
if (!architect)
|
|
77
|
+
continue;
|
|
78
|
+
const serve = architect['serve'];
|
|
79
|
+
if (!serve)
|
|
80
|
+
continue;
|
|
81
|
+
const currentBuilder = serve['builder'];
|
|
82
|
+
if (currentBuilder === NG_ANNOTATE_BUILDER) {
|
|
83
|
+
context.logger.info(`ng-annotate builder already configured in ${projectName}, skipping.`);
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (currentBuilder !== ANGULAR_DEV_SERVER_BUILDER) {
|
|
87
|
+
context.logger.warn(`⚠️ Project "${projectName}" uses builder "${String(currentBuilder)}" which is not ` +
|
|
88
|
+
`"${ANGULAR_DEV_SERVER_BUILDER}". Skipping automatic builder update — ` +
|
|
89
|
+
`set it to "${NG_ANNOTATE_BUILDER}" manually if compatible.`);
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
serve['builder'] = NG_ANNOTATE_BUILDER;
|
|
93
|
+
changed = true;
|
|
94
|
+
context.logger.info(`✅ Updated angular.json serve builder for "${projectName}"`);
|
|
72
95
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
// Existing plugins array — prepend into it
|
|
76
|
-
content = content.replace(/plugins\s*:\s*\[/, 'plugins: [...ngAnnotateMcp(), ');
|
|
77
|
-
}
|
|
78
|
-
else {
|
|
79
|
-
// No plugins array — inject one into defineConfig({...})
|
|
80
|
-
content = content.replace(/defineConfig\(\s*\{/, 'defineConfig({\n plugins: [...ngAnnotateMcp()],');
|
|
96
|
+
if (changed) {
|
|
97
|
+
tree.overwrite(angularJsonPath, JSON.stringify(angularJson, null, 2) + '\n');
|
|
81
98
|
}
|
|
82
|
-
tree.overwrite(viteConfigPath, content);
|
|
83
|
-
context.logger.info(`✅ Added ngAnnotateMcp() to ${viteConfigPath}`);
|
|
84
99
|
};
|
|
85
100
|
}
|
|
86
101
|
function addProviders() {
|
|
@@ -226,11 +241,6 @@ function addDevDependency() {
|
|
|
226
241
|
const pkg = JSON.parse(tree.read(pkgPath).toString('utf-8'));
|
|
227
242
|
pkg['devDependencies'] ?? (pkg['devDependencies'] = {});
|
|
228
243
|
let changed = false;
|
|
229
|
-
if (!pkg['devDependencies']['@ng-annotate/vite-plugin']) {
|
|
230
|
-
pkg['devDependencies']['@ng-annotate/vite-plugin'] = 'latest';
|
|
231
|
-
changed = true;
|
|
232
|
-
context.logger.info('✅ Added @ng-annotate/vite-plugin to devDependencies');
|
|
233
|
-
}
|
|
234
244
|
if (!pkg['devDependencies']['@ng-annotate/mcp-server']) {
|
|
235
245
|
pkg['devDependencies']['@ng-annotate/mcp-server'] = 'latest';
|
|
236
246
|
changed = true;
|
|
@@ -266,7 +276,7 @@ function default_1(options) {
|
|
|
266
276
|
return (0, schematics_1.chain)([
|
|
267
277
|
checkAngularVersion(),
|
|
268
278
|
addDevDependency(),
|
|
269
|
-
|
|
279
|
+
updateAngularJsonBuilder(),
|
|
270
280
|
addProviders(),
|
|
271
281
|
addMcpConfig(options),
|
|
272
282
|
addGitignore(),
|
|
@@ -72,8 +72,21 @@ const BASE_PKG = JSON.stringify({
|
|
|
72
72
|
devDependencies: {},
|
|
73
73
|
});
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
const ANGULAR_JSON = JSON.stringify({
|
|
76
|
+
projects: {
|
|
77
|
+
'my-app': {
|
|
78
|
+
architect: {
|
|
79
|
+
serve: {
|
|
80
|
+
builder: '@angular/build:dev-server',
|
|
81
|
+
options: {},
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
async function runSchematic(tree: Tree): Promise<ReturnType<typeof Tree.empty> & { readText(p: string): string }> {
|
|
89
|
+
return runner.runSchematic('ng-add', { aiTool: 'claude-code' }, tree) as Promise<ReturnType<typeof Tree.empty> & { readText(p: string): string }>;
|
|
77
90
|
}
|
|
78
91
|
|
|
79
92
|
function makeTree(files: Record<string, string>): Tree {
|
|
@@ -84,51 +97,76 @@ function makeTree(files: Record<string, string>): Tree {
|
|
|
84
97
|
return tree;
|
|
85
98
|
}
|
|
86
99
|
|
|
87
|
-
|
|
88
|
-
it('creates vite.config.ts when none exists', async () => {
|
|
89
|
-
const tree = makeTree({ 'package.json': BASE_PKG });
|
|
90
|
-
const result = await runSchematic(tree);
|
|
91
|
-
expect(result.exists('vite.config.ts')).toBe(true);
|
|
92
|
-
const content = result.readText('vite.config.ts');
|
|
93
|
-
expect(content).toContain("import { ngAnnotateMcp } from '@ng-annotate/vite-plugin'");
|
|
94
|
-
expect(content).toContain('plugins: [...ngAnnotateMcp()]');
|
|
95
|
-
});
|
|
100
|
+
// ─── updateAngularJsonBuilder ─────────────────────────────────────────────────
|
|
96
101
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
102
|
+
describe('ng-add schematic — updateAngularJsonBuilder', () => {
|
|
103
|
+
it('updates builder to @ng-annotate/angular:dev-server', async () => {
|
|
104
|
+
const tree = makeTree({ 'package.json': BASE_PKG, 'angular.json': ANGULAR_JSON });
|
|
105
|
+
const result = await runSchematic(tree);
|
|
106
|
+
const angular = JSON.parse(result.readText('angular.json')) as Record<string, unknown>;
|
|
107
|
+
const projects = angular['projects'] as Record<string, Record<string, unknown>>;
|
|
108
|
+
const serve = (projects['my-app']['architect'] as Record<string, Record<string, unknown>>)['serve'];
|
|
109
|
+
expect((serve['builder'] as string)).toBe('@ng-annotate/angular:dev-server');
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('skips when already using ng-annotate builder', async () => {
|
|
113
|
+
const withNgAnnotate = JSON.stringify({
|
|
114
|
+
projects: {
|
|
115
|
+
'my-app': {
|
|
116
|
+
architect: {
|
|
117
|
+
serve: {
|
|
118
|
+
builder: '@ng-annotate/angular:dev-server',
|
|
119
|
+
options: {},
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
const tree = makeTree({ 'package.json': BASE_PKG, 'angular.json': withNgAnnotate });
|
|
126
|
+
const result = await runSchematic(tree);
|
|
127
|
+
const angular = JSON.parse(result.readText('angular.json')) as Record<string, unknown>;
|
|
128
|
+
const projects = angular['projects'] as Record<string, Record<string, unknown>>;
|
|
129
|
+
const serve = (projects['my-app']['architect'] as Record<string, Record<string, unknown>>)['serve'];
|
|
130
|
+
expect((serve['builder'] as string)).toBe('@ng-annotate/angular:dev-server');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('warns but does not change unknown builders', async () => {
|
|
134
|
+
const withCustomBuilder = JSON.stringify({
|
|
135
|
+
projects: {
|
|
136
|
+
'my-app': {
|
|
137
|
+
architect: {
|
|
138
|
+
serve: {
|
|
139
|
+
builder: '@custom/builder:dev-server',
|
|
140
|
+
options: {},
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
},
|
|
101
145
|
});
|
|
146
|
+
const tree = makeTree({ 'package.json': BASE_PKG, 'angular.json': withCustomBuilder });
|
|
102
147
|
const result = await runSchematic(tree);
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
148
|
+
const angular = JSON.parse(result.readText('angular.json')) as Record<string, unknown>;
|
|
149
|
+
const projects = angular['projects'] as Record<string, Record<string, unknown>>;
|
|
150
|
+
const serve = (projects['my-app']['architect'] as Record<string, Record<string, unknown>>)['serve'];
|
|
151
|
+
// Should NOT overwrite a custom builder
|
|
152
|
+
expect((serve['builder'] as string)).toBe('@custom/builder:dev-server');
|
|
106
153
|
});
|
|
107
154
|
|
|
108
|
-
it('
|
|
109
|
-
const tree = makeTree({
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
});
|
|
113
|
-
// Already has the import but no plugins — should not be skipped
|
|
114
|
-
// Re-create without the import to test the injection path
|
|
115
|
-
const tree2 = makeTree({
|
|
116
|
-
'package.json': BASE_PKG,
|
|
117
|
-
'vite.config.ts': `import { defineConfig } from 'vite';\n\nexport default defineConfig({});\n`,
|
|
118
|
-
});
|
|
119
|
-
const result = await runSchematic(tree2);
|
|
120
|
-
const content = result.readText('vite.config.ts');
|
|
121
|
-
expect(content).toContain('plugins: [...ngAnnotateMcp()]');
|
|
155
|
+
it('does not create vite.config.ts (Angular CLI never loads it)', async () => {
|
|
156
|
+
const tree = makeTree({ 'package.json': BASE_PKG, 'angular.json': ANGULAR_JSON });
|
|
157
|
+
const result = await runSchematic(tree);
|
|
158
|
+
expect(result.exists('vite.config.ts')).toBe(false);
|
|
122
159
|
});
|
|
123
160
|
|
|
124
|
-
it('
|
|
125
|
-
const
|
|
126
|
-
const tree = makeTree({ 'package.json': BASE_PKG, 'vite.config.ts': original });
|
|
161
|
+
it('does not create proxy.conf.json (builder handles WS internally)', async () => {
|
|
162
|
+
const tree = makeTree({ 'package.json': BASE_PKG, 'angular.json': ANGULAR_JSON });
|
|
127
163
|
const result = await runSchematic(tree);
|
|
128
|
-
expect(result.
|
|
164
|
+
expect(result.exists('proxy.conf.json')).toBe(false);
|
|
129
165
|
});
|
|
130
166
|
});
|
|
131
167
|
|
|
168
|
+
// ─── addProviders ─────────────────────────────────────────────────────────────
|
|
169
|
+
|
|
132
170
|
describe('ng-add schematic — addProviders', () => {
|
|
133
171
|
it('adds import and provideNgAnnotate() to standard app.config.ts', async () => {
|
|
134
172
|
const tree = makeTree({
|
|
@@ -162,6 +200,8 @@ describe('ng-add schematic — addProviders', () => {
|
|
|
162
200
|
});
|
|
163
201
|
});
|
|
164
202
|
|
|
203
|
+
// ─── addGitignore ─────────────────────────────────────────────────────────────
|
|
204
|
+
|
|
165
205
|
describe('ng-add schematic — addGitignore', () => {
|
|
166
206
|
it('creates .gitignore with .ng-annotate/ when none exists', async () => {
|
|
167
207
|
const tree = makeTree({ 'package.json': BASE_PKG });
|
|
@@ -9,6 +9,8 @@ interface Options {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
const MIN_ANGULAR_MAJOR = 21;
|
|
12
|
+
const NG_ANNOTATE_BUILDER = '@ng-annotate/angular:dev-server';
|
|
13
|
+
const ANGULAR_DEV_SERVER_BUILDER = '@angular/build:dev-server';
|
|
12
14
|
|
|
13
15
|
function checkAngularVersion(): Rule {
|
|
14
16
|
return (tree: Tree) => {
|
|
@@ -33,37 +35,57 @@ function checkAngularVersion(): Rule {
|
|
|
33
35
|
};
|
|
34
36
|
}
|
|
35
37
|
|
|
36
|
-
function
|
|
38
|
+
function updateAngularJsonBuilder(): Rule {
|
|
37
39
|
return (tree: Tree, context: SchematicContext) => {
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
context.logger.info('✅ Created vite.config.ts with ngAnnotateMcp()');
|
|
40
|
+
const angularJsonPath = 'angular.json';
|
|
41
|
+
if (!tree.exists(angularJsonPath)) {
|
|
42
|
+
context.logger.warn(
|
|
43
|
+
'⚠️ Could not find angular.json — update the serve builder manually:\n' +
|
|
44
|
+
` "builder": "${NG_ANNOTATE_BUILDER}"`,
|
|
45
|
+
);
|
|
45
46
|
return;
|
|
46
47
|
}
|
|
47
48
|
|
|
48
|
-
|
|
49
|
+
const angularJson = JSON.parse(tree.read(angularJsonPath)!.toString('utf-8')) as Record<
|
|
50
|
+
string,
|
|
51
|
+
unknown
|
|
52
|
+
>;
|
|
53
|
+
const projects = angularJson['projects'] as Record<string, unknown> | undefined;
|
|
54
|
+
if (!projects) return;
|
|
55
|
+
|
|
56
|
+
let changed = false;
|
|
57
|
+
for (const projectName of Object.keys(projects)) {
|
|
58
|
+
const project = projects[projectName] as Record<string, unknown>;
|
|
59
|
+
const architect = project['architect'] as Record<string, unknown> | undefined;
|
|
60
|
+
if (!architect) continue;
|
|
49
61
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
62
|
+
const serve = architect['serve'] as Record<string, unknown> | undefined;
|
|
63
|
+
if (!serve) continue;
|
|
54
64
|
|
|
55
|
-
|
|
65
|
+
const currentBuilder = serve['builder'] as string | undefined;
|
|
56
66
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
67
|
+
if (currentBuilder === NG_ANNOTATE_BUILDER) {
|
|
68
|
+
context.logger.info(`ng-annotate builder already configured in ${projectName}, skipping.`);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (currentBuilder !== ANGULAR_DEV_SERVER_BUILDER) {
|
|
73
|
+
context.logger.warn(
|
|
74
|
+
`⚠️ Project "${projectName}" uses builder "${String(currentBuilder)}" which is not ` +
|
|
75
|
+
`"${ANGULAR_DEV_SERVER_BUILDER}". Skipping automatic builder update — ` +
|
|
76
|
+
`set it to "${NG_ANNOTATE_BUILDER}" manually if compatible.`,
|
|
77
|
+
);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
serve['builder'] = NG_ANNOTATE_BUILDER;
|
|
82
|
+
changed = true;
|
|
83
|
+
context.logger.info(`✅ Updated angular.json serve builder for "${projectName}"`);
|
|
63
84
|
}
|
|
64
85
|
|
|
65
|
-
|
|
66
|
-
|
|
86
|
+
if (changed) {
|
|
87
|
+
tree.overwrite(angularJsonPath, JSON.stringify(angularJson, null, 2) + '\n');
|
|
88
|
+
}
|
|
67
89
|
};
|
|
68
90
|
}
|
|
69
91
|
|
|
@@ -250,12 +272,6 @@ function addDevDependency(): Rule {
|
|
|
250
272
|
|
|
251
273
|
let changed = false;
|
|
252
274
|
|
|
253
|
-
if (!pkg['devDependencies']['@ng-annotate/vite-plugin']) {
|
|
254
|
-
pkg['devDependencies']['@ng-annotate/vite-plugin'] = 'latest';
|
|
255
|
-
changed = true;
|
|
256
|
-
context.logger.info('✅ Added @ng-annotate/vite-plugin to devDependencies');
|
|
257
|
-
}
|
|
258
|
-
|
|
259
275
|
if (!pkg['devDependencies']['@ng-annotate/mcp-server']) {
|
|
260
276
|
pkg['devDependencies']['@ng-annotate/mcp-server'] = 'latest';
|
|
261
277
|
changed = true;
|
|
@@ -297,7 +313,7 @@ export default function (options: Options): Rule {
|
|
|
297
313
|
return chain([
|
|
298
314
|
checkAngularVersion(),
|
|
299
315
|
addDevDependency(),
|
|
300
|
-
|
|
316
|
+
updateAngularJsonBuilder(),
|
|
301
317
|
addProviders(),
|
|
302
318
|
addMcpConfig(options),
|
|
303
319
|
addGitignore(),
|