archbyte 0.5.0 → 0.5.2
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/bin/archbyte.js +7 -2
- package/dist/agents/pipeline/agents/service-describer.js +4 -4
- package/dist/agents/pipeline/merger.js +26 -34
- package/dist/agents/static/component-detector.js +71 -107
- package/dist/agents/static/connection-mapper.js +24 -25
- package/dist/agents/static/deep-drill.d.ts +72 -0
- package/dist/agents/static/deep-drill.js +388 -0
- package/dist/agents/static/doc-parser.js +73 -48
- package/dist/agents/static/env-detector.js +3 -6
- package/dist/agents/static/event-detector.js +20 -26
- package/dist/agents/static/infra-analyzer.js +15 -1
- package/dist/agents/static/structure-scanner.js +56 -57
- package/dist/agents/static/taxonomy.d.ts +19 -0
- package/dist/agents/static/taxonomy.js +147 -0
- package/dist/agents/tools/local-fs.js +5 -2
- package/dist/cli/analyze.js +15 -5
- package/dist/cli/run.js +1 -1
- package/dist/server/src/index.js +226 -72
- package/package.json +1 -1
- package/ui/dist/assets/index-BQouokNH.css +1 -0
- package/ui/dist/assets/index-CWGPRsWP.js +72 -0
- package/ui/dist/index.html +2 -2
- package/ui/dist/assets/index-BXsZyipz.js +0 -72
- package/ui/dist/assets/index-ow1c3Nxp.css +0 -1
package/dist/server/src/index.js
CHANGED
|
@@ -20,6 +20,25 @@ let sourceWatcher = null;
|
|
|
20
20
|
const pendingSourceChanges = new Map();
|
|
21
21
|
let changeDebounceTimer = null;
|
|
22
22
|
const CHANGE_DEBOUNCE_MS = 2500;
|
|
23
|
+
function pendingChangesPath() {
|
|
24
|
+
return path.join(config.workspaceRoot, ".archbyte", "pending-changes.json");
|
|
25
|
+
}
|
|
26
|
+
function savePendingChanges() {
|
|
27
|
+
const entries = Array.from(pendingSourceChanges.values());
|
|
28
|
+
writeFile(pendingChangesPath(), JSON.stringify(entries), "utf-8").catch(() => { });
|
|
29
|
+
}
|
|
30
|
+
function loadPendingChanges() {
|
|
31
|
+
try {
|
|
32
|
+
const raw = readFileSync(pendingChangesPath(), "utf-8");
|
|
33
|
+
const entries = JSON.parse(raw);
|
|
34
|
+
for (const entry of entries) {
|
|
35
|
+
pendingSourceChanges.set(entry.path, entry);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// No persisted changes — fresh start
|
|
40
|
+
}
|
|
41
|
+
}
|
|
23
42
|
const sessionChanges = [];
|
|
24
43
|
const MAX_SESSION_CHANGES = 50;
|
|
25
44
|
let currentArchitecture = null;
|
|
@@ -33,7 +52,7 @@ function getArchbyteBin() {
|
|
|
33
52
|
}
|
|
34
53
|
catch {
|
|
35
54
|
// Fallback: resolve relative to this package
|
|
36
|
-
return path.resolve(__dirname, "../../../
|
|
55
|
+
return path.resolve(__dirname, "../../../bin/archbyte.js");
|
|
37
56
|
}
|
|
38
57
|
}
|
|
39
58
|
// Broadcast ops event to SSE clients
|
|
@@ -119,6 +138,11 @@ function createHttpServer() {
|
|
|
119
138
|
return;
|
|
120
139
|
}
|
|
121
140
|
const url = req.url || "/";
|
|
141
|
+
// Log API actions to terminal (skip static files, SSE, health checks)
|
|
142
|
+
if (url.startsWith("/api/") && url !== "/api/health") {
|
|
143
|
+
const label = url.replace("/api/", "").split("?")[0];
|
|
144
|
+
console.error(`[archbyte] ${req.method} ${label}`);
|
|
145
|
+
}
|
|
122
146
|
// SSE endpoint
|
|
123
147
|
if (url === "/events") {
|
|
124
148
|
res.writeHead(200, {
|
|
@@ -260,77 +284,72 @@ function createHttpServer() {
|
|
|
260
284
|
}));
|
|
261
285
|
return;
|
|
262
286
|
}
|
|
263
|
-
// API:
|
|
264
|
-
if (url.startsWith("/api/
|
|
287
|
+
// API: Deep Drill — focused static analysis of a single component
|
|
288
|
+
if (url.startsWith("/api/component/") && url.includes("/deep") && req.method === "GET") {
|
|
265
289
|
const arch = currentArchitecture || (await loadArchitecture());
|
|
266
290
|
if (!arch) {
|
|
267
291
|
res.writeHead(404, { "Content-Type": "application/json" });
|
|
268
292
|
res.end(JSON.stringify({ error: "No architecture loaded" }));
|
|
269
293
|
return;
|
|
270
294
|
}
|
|
271
|
-
|
|
272
|
-
const
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
const reverse = new Map();
|
|
279
|
-
for (const nd of arch.nodes) {
|
|
280
|
-
forward.set(nd.id, []);
|
|
281
|
-
reverse.set(nd.id, []);
|
|
295
|
+
// Extract component ID: /api/component/{id}/deep
|
|
296
|
+
const urlPath = url.split("?")[0];
|
|
297
|
+
const match = urlPath.match(/^\/api\/component\/(.+)\/deep$/);
|
|
298
|
+
if (!match) {
|
|
299
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
300
|
+
res.end(JSON.stringify({ error: "Invalid URL format" }));
|
|
301
|
+
return;
|
|
282
302
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
303
|
+
const componentId = decodeURIComponent(match[1]);
|
|
304
|
+
const node = arch.nodes.find((n) => n.id === componentId);
|
|
305
|
+
if (!node) {
|
|
306
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
307
|
+
res.end(JSON.stringify({ error: `Component not found: ${componentId}` }));
|
|
308
|
+
return;
|
|
286
309
|
}
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
const
|
|
295
|
-
const result = [];
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
310
|
+
const nodePath = node.path;
|
|
311
|
+
if (!nodePath) {
|
|
312
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
313
|
+
res.end(JSON.stringify({ error: `Component "${node.label}" has no path` }));
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
try {
|
|
317
|
+
const { runDeepDrill } = await import("../../agents/static/deep-drill.js");
|
|
318
|
+
const result = await runDeepDrill(config.workspaceRoot, componentId, node.label.split("\n")[0], nodePath);
|
|
319
|
+
// Enrich with connection data from architecture edges
|
|
320
|
+
const outgoing = [];
|
|
321
|
+
const incoming = [];
|
|
322
|
+
const nodeMap = new Map();
|
|
323
|
+
for (const n of arch.nodes)
|
|
324
|
+
nodeMap.set(n.id, n);
|
|
325
|
+
for (const edge of arch.edges) {
|
|
326
|
+
if (edge.source === componentId) {
|
|
327
|
+
const target = nodeMap.get(edge.target);
|
|
328
|
+
outgoing.push({
|
|
329
|
+
targetId: edge.target,
|
|
330
|
+
targetName: target?.label.split("\n")[0] || edge.target,
|
|
331
|
+
type: edge.label || "depends-on",
|
|
332
|
+
description: edge.label || "",
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
if (edge.target === componentId) {
|
|
336
|
+
const source = nodeMap.get(edge.source);
|
|
337
|
+
incoming.push({
|
|
338
|
+
sourceId: edge.source,
|
|
339
|
+
sourceName: source?.label.split("\n")[0] || edge.source,
|
|
340
|
+
type: edge.label || "depends-on",
|
|
341
|
+
description: edge.label || "",
|
|
342
|
+
});
|
|
307
343
|
}
|
|
308
344
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
affectedSet.add(n.id);
|
|
317
|
-
for (const n of downstream)
|
|
318
|
-
affectedSet.add(n.id);
|
|
319
|
-
// Affected edges: edges between any two nodes in the affected set
|
|
320
|
-
const affectedEdges = [];
|
|
321
|
-
for (const edge of arch.edges) {
|
|
322
|
-
if (affectedSet.has(edge.source) && affectedSet.has(edge.target)) {
|
|
323
|
-
affectedEdges.push(edge.id);
|
|
324
|
-
}
|
|
345
|
+
result.connections = { outgoing, incoming };
|
|
346
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
347
|
+
res.end(JSON.stringify(result));
|
|
348
|
+
}
|
|
349
|
+
catch (error) {
|
|
350
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
351
|
+
res.end(JSON.stringify({ error: String(error) }));
|
|
325
352
|
}
|
|
326
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
327
|
-
res.end(JSON.stringify({
|
|
328
|
-
nodeId,
|
|
329
|
-
upstream,
|
|
330
|
-
downstream,
|
|
331
|
-
affectedEdges,
|
|
332
|
-
totalAffected: affectedSet.size - 1,
|
|
333
|
-
}));
|
|
334
353
|
return;
|
|
335
354
|
}
|
|
336
355
|
// API: Audit — unified validation + health endpoint
|
|
@@ -1115,7 +1134,7 @@ function createHttpServer() {
|
|
|
1115
1134
|
const child = spawn(process.execPath, [bin, "generate"], {
|
|
1116
1135
|
cwd: config.workspaceRoot,
|
|
1117
1136
|
stdio: ["ignore", "pipe", "pipe"],
|
|
1118
|
-
env: { ...process.env, FORCE_COLOR: "0" },
|
|
1137
|
+
env: { ...process.env, FORCE_COLOR: "0", ARCHBYTE_INTERNAL: "1" },
|
|
1119
1138
|
});
|
|
1120
1139
|
runningWorkflows.set("__generate__", child);
|
|
1121
1140
|
broadcastOpsEvent({ type: "generate:started" });
|
|
@@ -1133,6 +1152,18 @@ function createHttpServer() {
|
|
|
1133
1152
|
res.end(JSON.stringify({ running: runningWorkflows.has("__generate__") }));
|
|
1134
1153
|
return;
|
|
1135
1154
|
}
|
|
1155
|
+
// API: Reload — re-read architecture, reconcile pending changes
|
|
1156
|
+
if (url === "/api/reload" && req.method === "POST") {
|
|
1157
|
+
currentArchitecture = await loadArchitecture();
|
|
1158
|
+
reconcilePendingWithGit();
|
|
1159
|
+
broadcastUpdate();
|
|
1160
|
+
if (pendingSourceChanges.size > 0) {
|
|
1161
|
+
broadcastPendingChanges();
|
|
1162
|
+
}
|
|
1163
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1164
|
+
res.end(JSON.stringify({ ok: true }));
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1136
1167
|
// API: Run analyze (static or LLM) + generate
|
|
1137
1168
|
if (url === "/api/analyze" && req.method === "POST") {
|
|
1138
1169
|
if (isAnalyzing) {
|
|
@@ -1164,10 +1195,17 @@ function createHttpServer() {
|
|
|
1164
1195
|
}
|
|
1165
1196
|
// API: Pending source file changes — hydrates UI on reconnect
|
|
1166
1197
|
if (url === "/api/changes/pending" && req.method === "GET") {
|
|
1167
|
-
const
|
|
1168
|
-
const count =
|
|
1198
|
+
const changes = Array.from(pendingSourceChanges.values()).map((c) => ({ event: c.event, path: c.path }));
|
|
1199
|
+
const count = changes.length;
|
|
1200
|
+
const { componentChanges, unmapped } = mapFilesToComponents(changes, currentArchitecture);
|
|
1169
1201
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1170
|
-
res.end(JSON.stringify({
|
|
1202
|
+
res.end(JSON.stringify({
|
|
1203
|
+
count,
|
|
1204
|
+
files: changes.slice(0, 20).map((c) => ({ event: c.event, path: c.path })),
|
|
1205
|
+
componentChanges,
|
|
1206
|
+
unmapped,
|
|
1207
|
+
truncated: count > 20,
|
|
1208
|
+
}));
|
|
1171
1209
|
return;
|
|
1172
1210
|
}
|
|
1173
1211
|
// API: Run workflow
|
|
@@ -1188,7 +1226,7 @@ function createHttpServer() {
|
|
|
1188
1226
|
const child = spawn(process.execPath, [bin, "workflow", "--run", id], {
|
|
1189
1227
|
cwd: config.workspaceRoot,
|
|
1190
1228
|
stdio: ["ignore", "pipe", "pipe"],
|
|
1191
|
-
env: { ...process.env, FORCE_COLOR: "0" },
|
|
1229
|
+
env: { ...process.env, FORCE_COLOR: "0", ARCHBYTE_INTERNAL: "1" },
|
|
1192
1230
|
});
|
|
1193
1231
|
runningWorkflows.set(id, child);
|
|
1194
1232
|
broadcastOpsEvent({ type: "workflow:started", id });
|
|
@@ -1419,6 +1457,22 @@ function createHttpServer() {
|
|
|
1419
1457
|
}
|
|
1420
1458
|
return;
|
|
1421
1459
|
}
|
|
1460
|
+
// API: UI event logging — fire-and-forget from UI to server console
|
|
1461
|
+
if (url === "/api/ui-event" && req.method === "POST") {
|
|
1462
|
+
let body = "";
|
|
1463
|
+
req.on("data", (chunk) => { body += chunk.toString(); });
|
|
1464
|
+
req.on("end", () => {
|
|
1465
|
+
try {
|
|
1466
|
+
const { event, detail } = JSON.parse(body);
|
|
1467
|
+
const msg = detail ? `${event} — ${detail}` : event;
|
|
1468
|
+
console.error(`[archbyte] ui: ${msg}`);
|
|
1469
|
+
}
|
|
1470
|
+
catch { }
|
|
1471
|
+
res.writeHead(204);
|
|
1472
|
+
res.end();
|
|
1473
|
+
});
|
|
1474
|
+
return;
|
|
1475
|
+
}
|
|
1422
1476
|
// API: Telemetry — record agent timing data
|
|
1423
1477
|
if (url === "/api/telemetry" && req.method === "POST") {
|
|
1424
1478
|
let body = "";
|
|
@@ -1666,6 +1720,7 @@ function runAnalyzePipeline(mode = "static", fileChanges) {
|
|
|
1666
1720
|
fileChanges = Array.from(pendingSourceChanges.values()).map((c) => ({ event: c.event, path: c.path }));
|
|
1667
1721
|
}
|
|
1668
1722
|
pendingSourceChanges.clear();
|
|
1723
|
+
savePendingChanges();
|
|
1669
1724
|
if (changeDebounceTimer) {
|
|
1670
1725
|
clearTimeout(changeDebounceTimer);
|
|
1671
1726
|
changeDebounceTimer = null;
|
|
@@ -1683,20 +1738,26 @@ function runAnalyzePipeline(mode = "static", fileChanges) {
|
|
|
1683
1738
|
const analyzeChild = spawn(process.execPath, analyzeArgs, {
|
|
1684
1739
|
cwd: config.workspaceRoot,
|
|
1685
1740
|
stdio: ["ignore", "pipe", "pipe"],
|
|
1686
|
-
env: { ...process.env, FORCE_COLOR: "0" },
|
|
1741
|
+
env: { ...process.env, FORCE_COLOR: "0", ARCHBYTE_INTERNAL: "1" },
|
|
1687
1742
|
});
|
|
1743
|
+
let analyzeStderr = "";
|
|
1744
|
+
analyzeChild.stdout?.on("data", (d) => process.stderr.write(`[analyze] ${d}`));
|
|
1745
|
+
analyzeChild.stderr?.on("data", (d) => { analyzeStderr += d; process.stderr.write(`[analyze] ${d}`); });
|
|
1688
1746
|
analyzeChild.on("close", (analyzeCode) => {
|
|
1689
1747
|
if (analyzeCode !== 0) {
|
|
1690
1748
|
isAnalyzing = false;
|
|
1691
|
-
|
|
1749
|
+
const errMsg = analyzeStderr.trim().split("\n").pop() || `Analyze failed (exit ${analyzeCode})`;
|
|
1750
|
+
broadcastOpsEvent({ type: "analyzing:finished", code: analyzeCode, success: false, error: errMsg });
|
|
1692
1751
|
return;
|
|
1693
1752
|
}
|
|
1694
1753
|
// Chain: generate after successful analyze
|
|
1695
1754
|
const genChild = spawn(process.execPath, [bin, "generate"], {
|
|
1696
1755
|
cwd: config.workspaceRoot,
|
|
1697
1756
|
stdio: ["ignore", "pipe", "pipe"],
|
|
1698
|
-
env: { ...process.env, FORCE_COLOR: "0" },
|
|
1757
|
+
env: { ...process.env, FORCE_COLOR: "0", ARCHBYTE_INTERNAL: "1" },
|
|
1699
1758
|
});
|
|
1759
|
+
genChild.stdout?.on("data", (d) => process.stderr.write(`[generate] ${d}`));
|
|
1760
|
+
genChild.stderr?.on("data", (d) => process.stderr.write(`[generate] ${d}`));
|
|
1700
1761
|
genChild.on("close", async (genCode) => {
|
|
1701
1762
|
isAnalyzing = false;
|
|
1702
1763
|
const durationMs = Date.now() - pipelineStart;
|
|
@@ -1761,15 +1822,52 @@ function setupWatcher() {
|
|
|
1761
1822
|
broadcastUpdate();
|
|
1762
1823
|
});
|
|
1763
1824
|
}
|
|
1825
|
+
// Map changed file paths to architecture components using longest-prefix matching
|
|
1826
|
+
function mapFilesToComponents(changes, arch) {
|
|
1827
|
+
if (!arch || arch.nodes.length === 0) {
|
|
1828
|
+
return { componentChanges: [], unmapped: changes.map((c) => ({ path: c.path, event: c.event })) };
|
|
1829
|
+
}
|
|
1830
|
+
// Build sorted list of component paths (longest first for greedy matching)
|
|
1831
|
+
const componentPaths = arch.nodes
|
|
1832
|
+
.filter((n) => n.path)
|
|
1833
|
+
.map((n) => ({ id: n.id, name: n.label.split("\n")[0], path: n.path }))
|
|
1834
|
+
.sort((a, b) => b.path.length - a.path.length);
|
|
1835
|
+
const groups = new Map();
|
|
1836
|
+
const unmapped = [];
|
|
1837
|
+
for (const change of changes) {
|
|
1838
|
+
let matched = false;
|
|
1839
|
+
for (const comp of componentPaths) {
|
|
1840
|
+
const prefix = comp.path.endsWith("/") ? comp.path : `${comp.path}/`;
|
|
1841
|
+
if (change.path.startsWith(prefix) || change.path === comp.path) {
|
|
1842
|
+
if (!groups.has(comp.id)) {
|
|
1843
|
+
groups.set(comp.id, { componentId: comp.id, componentName: comp.name, files: [] });
|
|
1844
|
+
}
|
|
1845
|
+
groups.get(comp.id).files.push({ path: change.path, event: change.event });
|
|
1846
|
+
matched = true;
|
|
1847
|
+
break;
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
if (!matched) {
|
|
1851
|
+
unmapped.push({ path: change.path, event: change.event });
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
return {
|
|
1855
|
+
componentChanges: Array.from(groups.values()),
|
|
1856
|
+
unmapped,
|
|
1857
|
+
};
|
|
1858
|
+
}
|
|
1764
1859
|
// Broadcast pending source changes to SSE clients
|
|
1765
1860
|
function broadcastPendingChanges() {
|
|
1766
|
-
const
|
|
1767
|
-
const count =
|
|
1861
|
+
const changes = Array.from(pendingSourceChanges.values()).map((c) => ({ event: c.event, path: c.path }));
|
|
1862
|
+
const count = changes.length;
|
|
1768
1863
|
const truncated = count > 20;
|
|
1864
|
+
const { componentChanges, unmapped } = mapFilesToComponents(changes, currentArchitecture);
|
|
1769
1865
|
broadcastOpsEvent({
|
|
1770
1866
|
type: "changes:detected",
|
|
1771
1867
|
count,
|
|
1772
|
-
files:
|
|
1868
|
+
files: changes.slice(0, 20).map((c) => c.path),
|
|
1869
|
+
componentChanges,
|
|
1870
|
+
unmapped,
|
|
1773
1871
|
truncated,
|
|
1774
1872
|
});
|
|
1775
1873
|
}
|
|
@@ -1808,9 +1906,60 @@ function setupSourceWatcher() {
|
|
|
1808
1906
|
changeDebounceTimer = setTimeout(() => {
|
|
1809
1907
|
changeDebounceTimer = null;
|
|
1810
1908
|
broadcastPendingChanges();
|
|
1909
|
+
savePendingChanges();
|
|
1811
1910
|
}, CHANGE_DEBOUNCE_MS);
|
|
1812
1911
|
});
|
|
1813
1912
|
}
|
|
1913
|
+
// Watch for git commits — reconcile pending changes against actual dirty files
|
|
1914
|
+
let gitWatcher = null;
|
|
1915
|
+
let gitDebounceTimer = null;
|
|
1916
|
+
function setupGitWatcher() {
|
|
1917
|
+
const gitIndex = path.join(config.workspaceRoot, ".git", "index");
|
|
1918
|
+
if (!existsSync(gitIndex))
|
|
1919
|
+
return;
|
|
1920
|
+
gitWatcher = watch(gitIndex, { ignoreInitial: true, depth: 0 });
|
|
1921
|
+
gitWatcher.on("change", () => {
|
|
1922
|
+
// Debounce — git writes the index multiple times during a commit
|
|
1923
|
+
if (gitDebounceTimer)
|
|
1924
|
+
clearTimeout(gitDebounceTimer);
|
|
1925
|
+
gitDebounceTimer = setTimeout(() => {
|
|
1926
|
+
gitDebounceTimer = null;
|
|
1927
|
+
reconcilePendingWithGit();
|
|
1928
|
+
}, 1000);
|
|
1929
|
+
});
|
|
1930
|
+
}
|
|
1931
|
+
function reconcilePendingWithGit() {
|
|
1932
|
+
if (pendingSourceChanges.size === 0)
|
|
1933
|
+
return;
|
|
1934
|
+
try {
|
|
1935
|
+
// Get files that are still dirty (modified, untracked, staged)
|
|
1936
|
+
const output = execSync("git status --porcelain", {
|
|
1937
|
+
cwd: config.workspaceRoot,
|
|
1938
|
+
encoding: "utf-8",
|
|
1939
|
+
timeout: 5000,
|
|
1940
|
+
});
|
|
1941
|
+
const dirtyFiles = new Set(output.trim().split("\n").filter(Boolean).map((line) => line.slice(3).trim()));
|
|
1942
|
+
let changed = false;
|
|
1943
|
+
for (const [filePath] of pendingSourceChanges) {
|
|
1944
|
+
if (!dirtyFiles.has(filePath)) {
|
|
1945
|
+
pendingSourceChanges.delete(filePath);
|
|
1946
|
+
changed = true;
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
if (changed) {
|
|
1950
|
+
savePendingChanges();
|
|
1951
|
+
if (pendingSourceChanges.size === 0) {
|
|
1952
|
+
broadcastOpsEvent({ type: "changes:cleared" });
|
|
1953
|
+
}
|
|
1954
|
+
else {
|
|
1955
|
+
broadcastPendingChanges();
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
catch {
|
|
1960
|
+
// Not a git repo or git not available — ignore
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1814
1963
|
// Graceful shutdown
|
|
1815
1964
|
function setupShutdown() {
|
|
1816
1965
|
const shutdown = async () => {
|
|
@@ -1839,6 +1988,7 @@ function setupShutdown() {
|
|
|
1839
1988
|
sseClients.clear();
|
|
1840
1989
|
await sourceWatcher?.close();
|
|
1841
1990
|
await diagramWatcher?.close();
|
|
1991
|
+
await gitWatcher?.close();
|
|
1842
1992
|
httpServer?.close();
|
|
1843
1993
|
process.exit(0);
|
|
1844
1994
|
};
|
|
@@ -1929,8 +2079,12 @@ export async function startServer(cfg) {
|
|
|
1929
2079
|
console.error("[archbyte] Failed to start HTTP server:", err);
|
|
1930
2080
|
process.exit(1);
|
|
1931
2081
|
}
|
|
2082
|
+
currentArchitecture = await loadArchitecture();
|
|
1932
2083
|
setupWatcher();
|
|
2084
|
+
loadPendingChanges();
|
|
2085
|
+
reconcilePendingWithGit();
|
|
1933
2086
|
setupSourceWatcher();
|
|
2087
|
+
setupGitWatcher();
|
|
1934
2088
|
console.error(`[archbyte] Serving ${config.name}`);
|
|
1935
2089
|
console.error(`[archbyte] Diagram: ${config.diagramPath}`);
|
|
1936
2090
|
// Listen for 'q' keypress to quit gracefully
|
package/package.json
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import"https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;600;700&display=swap";.react-flow{direction:ltr;--xy-edge-stroke-default: #b1b1b7;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #555;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(255, 255, 255, .5);--xy-minimap-background-color-default: #fff;--xy-minimap-mask-background-color-default: rgba(240, 240, 240, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #e2e2e2;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: transparent;--xy-background-pattern-dots-color-default: #91919a;--xy-background-pattern-lines-color-default: #eee;--xy-background-pattern-cross-color-default: #e2e2e2;background-color:var(--xy-background-color, var(--xy-background-color-default));--xy-node-color-default: inherit;--xy-node-border-default: 1px solid #1a192b;--xy-node-background-color-default: #fff;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(0, 0, 0, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #1a192b;--xy-node-border-radius-default: 3px;--xy-handle-background-color-default: #1a192b;--xy-handle-border-color-default: #fff;--xy-selection-background-color-default: rgba(0, 89, 220, .08);--xy-selection-border-default: 1px dotted rgba(0, 89, 220, .8);--xy-controls-button-background-color-default: #fefefe;--xy-controls-button-background-color-hover-default: #f4f4f4;--xy-controls-button-color-default: inherit;--xy-controls-button-color-hover-default: inherit;--xy-controls-button-border-color-default: #eee;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #ffffff;--xy-edge-label-color-default: inherit;--xy-resize-background-color-default: #3367d9}.react-flow.dark{--xy-edge-stroke-default: #3e3e3e;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #727272;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(150, 150, 150, .25);--xy-minimap-background-color-default: #141414;--xy-minimap-mask-background-color-default: rgba(60, 60, 60, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #2b2b2b;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: #141414;--xy-background-pattern-dots-color-default: #777;--xy-background-pattern-lines-color-default: #777;--xy-background-pattern-cross-color-default: #777;--xy-node-color-default: #f8f8f8;--xy-node-border-default: 1px solid #3c3c3c;--xy-node-background-color-default: #1e1e1e;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(255, 255, 255, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #999;--xy-handle-background-color-default: #bebebe;--xy-handle-border-color-default: #1e1e1e;--xy-selection-background-color-default: rgba(200, 200, 220, .08);--xy-selection-border-default: 1px dotted rgba(200, 200, 220, .8);--xy-controls-button-background-color-default: #2b2b2b;--xy-controls-button-background-color-hover-default: #3e3e3e;--xy-controls-button-color-default: #f8f8f8;--xy-controls-button-color-hover-default: #fff;--xy-controls-button-border-color-default: #5b5b5b;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #141414;--xy-edge-label-color-default: #f8f8f8}.react-flow__background{background-color:var(--xy-background-color-props, var(--xy-background-color, var(--xy-background-color-default)));pointer-events:none;z-index:-1}.react-flow__container{position:absolute;width:100%;height:100%;top:0;left:0}.react-flow__pane{z-index:1}.react-flow__pane.draggable{cursor:grab}.react-flow__pane.dragging{cursor:grabbing}.react-flow__pane.selection{cursor:pointer}.react-flow__viewport{transform-origin:0 0;z-index:2;pointer-events:none}.react-flow__renderer{z-index:4}.react-flow__selection{z-index:6}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible{outline:none}.react-flow__edge-path{stroke:var(--xy-edge-stroke, var(--xy-edge-stroke-default));stroke-width:var(--xy-edge-stroke-width, var(--xy-edge-stroke-width-default));fill:none}.react-flow__connection-path{stroke:var(--xy-connectionline-stroke, var(--xy-connectionline-stroke-default));stroke-width:var(--xy-connectionline-stroke-width, var(--xy-connectionline-stroke-width-default));fill:none}.react-flow .react-flow__edges{position:absolute}.react-flow .react-flow__edges svg{overflow:visible;position:absolute;pointer-events:none}.react-flow__edge{pointer-events:visibleStroke}.react-flow__edge.selectable{cursor:pointer}.react-flow__edge.animated path{stroke-dasharray:5;animation:dashdraw .5s linear infinite}.react-flow__edge.animated path.react-flow__edge-interaction{stroke-dasharray:none;animation:none}.react-flow__edge.inactive{pointer-events:none}.react-flow__edge.selected,.react-flow__edge:focus,.react-flow__edge:focus-visible{outline:none}.react-flow__edge.selected .react-flow__edge-path,.react-flow__edge.selectable:focus .react-flow__edge-path,.react-flow__edge.selectable:focus-visible .react-flow__edge-path{stroke:var(--xy-edge-stroke-selected, var(--xy-edge-stroke-selected-default))}.react-flow__edge-textwrapper{pointer-events:all}.react-flow__edge .react-flow__edge-text{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__arrowhead polyline{stroke:var(--xy-edge-stroke, var(--xy-edge-stroke-default))}.react-flow__arrowhead polyline.arrowclosed{fill:var(--xy-edge-stroke, var(--xy-edge-stroke-default))}.react-flow__connection{pointer-events:none}.react-flow__connection .animated{stroke-dasharray:5;animation:dashdraw .5s linear infinite}svg.react-flow__connectionline{z-index:1001;overflow:visible;position:absolute}.react-flow__nodes{pointer-events:none;transform-origin:0 0}.react-flow__node{position:absolute;-webkit-user-select:none;-moz-user-select:none;user-select:none;pointer-events:all;transform-origin:0 0;box-sizing:border-box;cursor:default}.react-flow__node.selectable{cursor:pointer}.react-flow__node.draggable{cursor:grab;pointer-events:all}.react-flow__node.draggable.dragging{cursor:grabbing}.react-flow__nodesselection{z-index:3;transform-origin:left top;pointer-events:none}.react-flow__nodesselection-rect{position:absolute;pointer-events:all;cursor:grab}.react-flow__handle{position:absolute;pointer-events:none;min-width:5px;min-height:5px;width:6px;height:6px;background-color:var(--xy-handle-background-color, var(--xy-handle-background-color-default));border:1px solid var(--xy-handle-border-color, var(--xy-handle-border-color-default));border-radius:100%}.react-flow__handle.connectingfrom{pointer-events:all}.react-flow__handle.connectionindicator{pointer-events:all;cursor:crosshair}.react-flow__handle-bottom{top:auto;left:50%;bottom:0;transform:translate(-50%,50%)}.react-flow__handle-top{top:0;left:50%;transform:translate(-50%,-50%)}.react-flow__handle-left{top:50%;left:0;transform:translate(-50%,-50%)}.react-flow__handle-right{top:50%;right:0;transform:translate(50%,-50%)}.react-flow__edgeupdater{cursor:move;pointer-events:all}.react-flow__pane.selection .react-flow__panel{pointer-events:none}.react-flow__panel{position:absolute;z-index:5;margin:15px}.react-flow__panel.top{top:0}.react-flow__panel.bottom{bottom:0}.react-flow__panel.top.center,.react-flow__panel.bottom.center{left:50%;transform:translate(-15px) translate(-50%)}.react-flow__panel.left{left:0}.react-flow__panel.right{right:0}.react-flow__panel.left.center,.react-flow__panel.right.center{top:50%;transform:translateY(-15px) translateY(-50%)}.react-flow__attribution{font-size:10px;background:var(--xy-attribution-background-color, var(--xy-attribution-background-color-default));padding:2px 3px;margin:0}.react-flow__attribution a{text-decoration:none;color:#999}@keyframes dashdraw{0%{stroke-dashoffset:10}}.react-flow__edgelabel-renderer{position:absolute;width:100%;height:100%;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;left:0;top:0}.react-flow__viewport-portal{position:absolute;width:100%;height:100%;left:0;top:0;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__minimap{background:var( --xy-minimap-background-color-props, var(--xy-minimap-background-color, var(--xy-minimap-background-color-default)) )}.react-flow__minimap-svg{display:block}.react-flow__minimap-mask{fill:var( --xy-minimap-mask-background-color-props, var(--xy-minimap-mask-background-color, var(--xy-minimap-mask-background-color-default)) );stroke:var( --xy-minimap-mask-stroke-color-props, var(--xy-minimap-mask-stroke-color, var(--xy-minimap-mask-stroke-color-default)) );stroke-width:var( --xy-minimap-mask-stroke-width-props, var(--xy-minimap-mask-stroke-width, var(--xy-minimap-mask-stroke-width-default)) )}.react-flow__minimap-node{fill:var( --xy-minimap-node-background-color-props, var(--xy-minimap-node-background-color, var(--xy-minimap-node-background-color-default)) );stroke:var( --xy-minimap-node-stroke-color-props, var(--xy-minimap-node-stroke-color, var(--xy-minimap-node-stroke-color-default)) );stroke-width:var( --xy-minimap-node-stroke-width-props, var(--xy-minimap-node-stroke-width, var(--xy-minimap-node-stroke-width-default)) )}.react-flow__background-pattern.dots{fill:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-dots-color-default)) )}.react-flow__background-pattern.lines{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-lines-color-default)) )}.react-flow__background-pattern.cross{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-cross-color-default)) )}.react-flow__controls{display:flex;flex-direction:column;box-shadow:var(--xy-controls-box-shadow, var(--xy-controls-box-shadow-default))}.react-flow__controls.horizontal{flex-direction:row}.react-flow__controls-button{display:flex;justify-content:center;align-items:center;height:26px;width:26px;padding:4px;border:none;background:var(--xy-controls-button-background-color, var(--xy-controls-button-background-color-default));border-bottom:1px solid var( --xy-controls-button-border-color-props, var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) );color:var( --xy-controls-button-color-props, var(--xy-controls-button-color, var(--xy-controls-button-color-default)) );cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__controls-button svg{width:100%;max-width:12px;max-height:12px;fill:currentColor}.react-flow__edge.updating .react-flow__edge-path{stroke:#777}.react-flow__edge-text{font-size:10px}.react-flow__node.selectable:focus,.react-flow__node.selectable:focus-visible{outline:none}.react-flow__node-input,.react-flow__node-default,.react-flow__node-output,.react-flow__node-group{padding:10px;border-radius:var(--xy-node-border-radius, var(--xy-node-border-radius-default));width:150px;font-size:12px;color:var(--xy-node-color, var(--xy-node-color-default));text-align:center;border:var(--xy-node-border, var(--xy-node-border-default));background-color:var(--xy-node-background-color, var(--xy-node-background-color-default))}.react-flow__node-input.selectable:hover,.react-flow__node-default.selectable:hover,.react-flow__node-output.selectable:hover,.react-flow__node-group.selectable:hover{box-shadow:var(--xy-node-boxshadow-hover, var(--xy-node-boxshadow-hover-default))}.react-flow__node-input.selectable.selected,.react-flow__node-input.selectable:focus,.react-flow__node-input.selectable:focus-visible,.react-flow__node-default.selectable.selected,.react-flow__node-default.selectable:focus,.react-flow__node-default.selectable:focus-visible,.react-flow__node-output.selectable.selected,.react-flow__node-output.selectable:focus,.react-flow__node-output.selectable:focus-visible,.react-flow__node-group.selectable.selected,.react-flow__node-group.selectable:focus,.react-flow__node-group.selectable:focus-visible{box-shadow:var(--xy-node-boxshadow-selected, var(--xy-node-boxshadow-selected-default))}.react-flow__node-group{background-color:var(--xy-node-group-background-color, var(--xy-node-group-background-color-default))}.react-flow__nodesselection-rect,.react-flow__selection{background:var(--xy-selection-background-color, var(--xy-selection-background-color-default));border:var(--xy-selection-border, var(--xy-selection-border-default))}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible,.react-flow__selection:focus,.react-flow__selection:focus-visible{outline:none}.react-flow__controls-button:hover{background:var( --xy-controls-button-background-color-hover-props, var(--xy-controls-button-background-color-hover, var(--xy-controls-button-background-color-hover-default)) );color:var( --xy-controls-button-color-hover-props, var(--xy-controls-button-color-hover, var(--xy-controls-button-color-hover-default)) )}.react-flow__controls-button:disabled{pointer-events:none}.react-flow__controls-button:disabled svg{fill-opacity:.4}.react-flow__controls-button:last-child{border-bottom:none}.react-flow__controls.horizontal .react-flow__controls-button{border-bottom:none;border-right:1px solid var( --xy-controls-button-border-color-props, var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) )}.react-flow__controls.horizontal .react-flow__controls-button:last-child{border-right:none}.react-flow__resize-control{position:absolute}.react-flow__resize-control.left,.react-flow__resize-control.right{cursor:ew-resize}.react-flow__resize-control.top,.react-flow__resize-control.bottom{cursor:ns-resize}.react-flow__resize-control.top.left,.react-flow__resize-control.bottom.right{cursor:nwse-resize}.react-flow__resize-control.bottom.left,.react-flow__resize-control.top.right{cursor:nesw-resize}.react-flow__resize-control.handle{width:5px;height:5px;border:1px solid #fff;border-radius:1px;background-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));translate:-50% -50%}.react-flow__resize-control.handle.left{left:0;top:50%}.react-flow__resize-control.handle.right{left:100%;top:50%}.react-flow__resize-control.handle.top{left:50%;top:0}.react-flow__resize-control.handle.bottom{left:50%;top:100%}.react-flow__resize-control.handle.top.left,.react-flow__resize-control.handle.bottom.left{left:0}.react-flow__resize-control.handle.top.right,.react-flow__resize-control.handle.bottom.right{left:100%}.react-flow__resize-control.line{border-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));border-width:0;border-style:solid}.react-flow__resize-control.line.left,.react-flow__resize-control.line.right{width:1px;transform:translate(-50%);top:0;height:100%}.react-flow__resize-control.line.left{left:0;border-left-width:1px}.react-flow__resize-control.line.right{left:100%;border-right-width:1px}.react-flow__resize-control.line.top,.react-flow__resize-control.line.bottom{height:1px;transform:translateY(-50%);left:0;width:100%}.react-flow__resize-control.line.top{top:0;border-top-width:1px}.react-flow__resize-control.line.bottom{border-bottom-width:1px;top:100%}.react-flow__edge-textbg{fill:var(--xy-edge-label-background-color, var(--xy-edge-label-background-color-default))}.react-flow__edge-text{fill:var(--xy-edge-label-color, var(--xy-edge-label-color-default))}:root{--bg-primary: hsl(222 47% 5%);--bg-secondary: hsl(222 47% 7%);--bg-tertiary: hsl(222 47% 10%);--bg-hover: hsl(222 47% 12%);--text-primary: hsl(210 40% 96%);--text-secondary: hsl(215 20% 55%);--text-muted: hsl(215 15% 40%);--border-color: hsl(220 25% 15%);--border-light: hsl(220 20% 12%);--shadow-color: rgba(0, 0, 0, .3);--canvas-bg: hsl(222 47% 5%);--canvas-dots: hsl(220 25% 15%);--watermark-color: rgba(255, 255, 255, .02);--accent-cyan: hsl(185 75% 50%);--accent-green: hsl(142 76% 42%);--accent-amber: hsl(38 92% 50%);--accent-red: hsl(0 90% 55%);--glow-cyan: 0 0 20px hsl(185 75% 50% / .15);--glow-green: 0 0 12px hsl(142 76% 42% / .3)}[data-theme=light]{--bg-primary: hsl(220 20% 97%);--bg-secondary: hsl(220 18% 94%);--bg-tertiary: hsl(220 16% 90%);--bg-hover: hsl(220 14% 86%);--text-primary: hsl(220 30% 12%);--text-secondary: hsl(220 15% 40%);--text-muted: hsl(220 10% 58%);--border-color: hsl(220 15% 82%);--border-light: hsl(220 12% 88%);--shadow-color: rgba(0, 0, 0, .08);--canvas-bg: hsl(220 20% 97%);--canvas-dots: hsl(220 15% 82%);--watermark-color: rgba(0, 0, 0, .02);--accent-cyan: hsl(185 80% 35%);--accent-green: hsl(142 72% 32%);--accent-amber: hsl(38 90% 42%);--accent-red: hsl(0 85% 45%);--glow-cyan: 0 0 12px hsl(185 80% 35% / .12);--glow-green: 0 0 8px hsl(142 72% 32% / .2)}*{margin:0;padding:0;box-sizing:border-box}html,body,#root{width:100%;height:100%;overflow:hidden;font-family:JetBrains Mono,monospace}.app{width:100%;height:100%;display:flex;flex-direction:column;background:var(--bg-primary)}::-webkit-scrollbar{width:6px}::-webkit-scrollbar-track{background:var(--bg-primary)}::-webkit-scrollbar-thumb{background:var(--border-color);border-radius:3px}::-webkit-scrollbar-thumb:hover{background:#303a50}.branding-header{position:relative;z-index:10;background:#080c16d9;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);padding:10px 24px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid hsl(185 75% 50% / .1);box-shadow:0 1px 12px #20cfdf0d}.archbyte-brand{display:flex;align-items:center;gap:10px}.archbyte-logo{font-size:20px}.archbyte-logo-img{height:22px;width:auto}.archbyte-name{font-size:20px;font-weight:700;color:var(--text-primary);letter-spacing:-.3px}.archbyte-tagline{font-size:13px;color:var(--text-muted);padding-left:14px;margin-left:4px;border-left:1px solid var(--border-color)}.branding-account{display:flex;align-items:center;gap:10px}.account-tier-badge{font-size:11px;font-weight:600;padding:3px 10px;border-radius:4px;letter-spacing:.3px;text-transform:uppercase}.account-tier-badge.premium{background:#672eb8;color:#fff}.account-tier-badge.free{background:#182543;color:var(--text-muted)}.account-email{font-size:12px;color:var(--text-muted);position:relative}.account-pro-tag{margin-left:6px;font-size:9px;font-weight:700;letter-spacing:.5px;padding:1px 4px;border-radius:3px;background:#a65eed26;color:#a65eed;border:1px solid hsl(270 80% 65% / .4);vertical-align:middle}.project-info-wrapper{position:relative;display:inline-flex;align-items:center}.project-info-trigger{background:none;border:1px solid transparent;color:var(--text-secondary);font-family:JetBrains Mono,monospace;font-size:13px;font-weight:500;cursor:pointer;transition:all .2s;margin-left:10px;padding:3px 10px 3px 14px;border-left:1px solid var(--border-color);border-radius:0}.project-info-trigger:hover{color:var(--text-primary);background:var(--bg-tertiary)}.project-info-popup{position:absolute;top:calc(100% + 8px);left:0;background:#090e1af2;-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);border:1px solid var(--border-color);border-radius:12px;padding:16px;width:340px;z-index:100;box-shadow:0 8px 32px var(--shadow-color);animation:popupFadeIn .15s ease-out}@keyframes popupFadeIn{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.project-info-header{display:flex;align-items:center;gap:8px;margin-bottom:10px}.project-info-name{font-size:15px;font-weight:700;color:var(--text-primary);letter-spacing:-.3px}.project-info-lang{font-size:10px;font-weight:600;color:#4cd9e6;background:#20cfdf1f;padding:2px 8px;border-radius:10px;text-transform:uppercase;letter-spacing:.5px}.project-info-desc{font-size:12px;color:var(--text-secondary);line-height:1.5;margin:0 0 14px;padding-bottom:14px;border-bottom:1px solid var(--border-color)}.project-info-stats{display:flex;gap:6px;flex-wrap:wrap}.project-info-stat{display:flex;flex-direction:column;align-items:center;background:var(--bg-tertiary);padding:8px 12px;border-radius:8px;min-width:56px;flex:1}.project-info-stat-value{font-size:16px;font-weight:700;color:var(--text-primary);font-family:JetBrains Mono,monospace}.project-info-stat-label{font-size:9px;color:var(--text-muted);text-transform:uppercase;letter-spacing:.5px;margin-top:2px}.project-info-badge{margin-top:10px;font-size:10px;font-weight:600;color:#f6c155;background:#f2a60d1a;padding:3px 10px;border-radius:10px;display:inline-block}.project-info-vscode{display:flex;align-items:center;gap:8px;margin-top:12px;padding:8px 12px;border-radius:8px;font-size:12px;font-weight:600;color:#75b3f0;background:#1980e61a;border:1px solid hsl(210 80% 50% / .2);text-decoration:none;cursor:pointer;transition:all .2s}.project-info-vscode:hover{background:#1980e633;border-color:#1980e666;color:#a3ccf5}.project-info-vscode svg{flex-shrink:0}[data-theme=light] .project-info-popup{background:#f9fafbf5}[data-theme=light] .project-info-vscode{color:#1466b8;background:#1980e614;border-color:#1980e633}[data-theme=light] .project-info-vscode:hover{background:#1980e626;border-color:#1980e659;color:#0f4d8a}.brand-attribution{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--text-secondary);text-decoration:none;transition:color .2s}.brand-attribution:hover{color:var(--text-primary)}.brand-avatar{width:28px;height:28px;border-radius:50%;object-fit:cover;border:2px solid hsl(185 75% 50% / .3);transition:border-color .2s,box-shadow .2s}.brand-attribution:hover .brand-avatar{border-color:var(--accent-cyan);box-shadow:0 0 12px #20cfdf4d}.brand-attribution-link{color:var(--accent-cyan);font-weight:600}.brand-attribution:hover .brand-attribution-link{color:#63dee9}.toolbar{display:flex;align-items:center;justify-content:space-between;padding:6px 16px;background:var(--bg-secondary);border-bottom:1px solid var(--border-color)}.toolbar-left,.toolbar-right{display:flex;align-items:center;gap:16px}.toolbar-right{gap:8px}.toolbar-right .dropdown-menu{left:auto;right:0}.toolbar-section{display:flex;flex-direction:column;gap:3px;padding-right:12px;border-right:1px solid hsl(222 30% 25%)}.toolbar-section:last-child{padding-right:0;border-right:none}.toolbar-section-label{font-size:9px;font-weight:600;text-transform:uppercase;letter-spacing:.08em;color:var(--text-muted);text-align:center;-webkit-user-select:none;user-select:none}.toolbar-section-items,.toolbar-group{display:flex;align-items:center;gap:6px}.toolbar-divider{width:1px;height:24px;background:var(--border-color);margin:0 8px}.toolbar-btn-icon{display:flex;align-items:center;justify-content:center;width:32px;height:32px;padding:0;background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:6px;font-size:15px;font-weight:600;font-family:system-ui,sans-serif;line-height:1;text-align:center;color:var(--text-secondary);cursor:pointer;transition:all .2s}.toolbar-btn-icon:hover{background:var(--bg-hover);color:var(--text-primary);box-shadow:var(--glow-cyan)}.toolbar-btn-icon:disabled{opacity:.5;cursor:not-allowed}.toolbar-analyzing{display:flex;align-items:center;gap:6px;font-size:11px;color:var(--accent-cyan, hsl(185, 75%, 50%));padding:4px 10px;background:#20cfdf1a;border:1px solid hsla(185,75%,50%,.3);border-radius:6px;font-family:JetBrains Mono,monospace}.analyzing-pulse{width:6px;height:6px;border-radius:50%;background:var(--accent-cyan, hsl(185, 75%, 50%));animation:pulse-analyzing 1.5s ease-in-out infinite}@keyframes pulse-analyzing{0%,to{opacity:1;transform:scale(1)}50%{opacity:.4;transform:scale(.8)}}.toolbar-error-btn{background:#ef444426!important;border-color:#ef444466!important;color:#ef4444!important;font-size:11px;animation:pulse-error 2s ease-in-out infinite}.toolbar-error-btn:hover{background:#ef444440!important}@keyframes pulse-error{0%,to{opacity:1}50%{opacity:.7}}.toolbar-btn{display:flex;align-items:center;gap:4px;height:32px;padding:0 10px;background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:6px;font-size:12px;font-weight:500;color:var(--text-secondary);cursor:pointer;transition:all .2s}.toolbar-btn:hover{background:var(--bg-hover);color:var(--text-primary);box-shadow:var(--glow-cyan)}.toolbar-btn:disabled{opacity:.5;cursor:not-allowed}.toolbar-btn.active{background:#20cfdf26;border-color:var(--accent-cyan);color:var(--accent-cyan)}.premium-btn{position:relative;border:1px solid hsl(270 80% 65% / .5)!important}.premium-btn.premium-locked{opacity:.4;cursor:not-allowed}.premium-btn.premium-locked:hover{opacity:.55}.premium-btn:after{content:"PRO";position:absolute;top:3px;right:4px;font-size:7px;font-weight:700;letter-spacing:.5px;line-height:1;color:#a65eed}.dropdown-item{display:flex;align-items:center;gap:8px;width:100%;padding:8px 14px;font-size:12px;font-weight:500;color:var(--text-secondary);background:transparent;border:none;cursor:pointer;text-align:left;transition:all .15s}.dropdown-item:hover{background:var(--bg-hover);color:var(--text-primary)}.dropdown-item.active{color:var(--accent-cyan);background:var(--bg-hover)}.shortcut-item{display:flex;align-items:center;justify-content:space-between;padding:6px 14px;font-size:12px;color:var(--text-secondary)}.shortcut-item kbd{background:var(--bg-tertiary);padding:2px 6px;border-radius:4px;font-family:JetBrains Mono,monospace;font-size:11px;font-weight:600;color:var(--text-primary);margin-right:12px;border:1px solid var(--border-color)}.dropdown-wrapper{position:relative}.dropdown-menu{position:absolute;top:100%;left:0;margin-top:4px;background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:8px;padding:4px;z-index:50;min-width:160px;box-shadow:0 4px 16px var(--shadow-color);animation:fade-in-up .15s ease-out}.dropdown-label{padding:6px 10px 4px;font-size:9px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--text-muted)}.dropdown-separator{height:1px;background:var(--border-color);margin:4px 0}.panel-btn{display:inline-flex;align-items:center;justify-content:center;gap:4px;padding:6px 10px;font-size:11px;font-family:JetBrains Mono,monospace;font-weight:500;border-radius:6px;cursor:pointer;border:1px solid var(--border-color);background:var(--bg-tertiary);color:var(--text-secondary);transition:all .2s;white-space:nowrap}.panel-btn:hover{background:var(--bg-hover);color:var(--text-primary)}.panel-btn.cyan{border-color:#20cfdf4d;color:var(--accent-cyan);background:#20cfdf1a}.panel-btn.cyan:hover{background:#20cfdf33}.panel-btn.green{border-color:#1abc554d;color:var(--accent-green);background:#1abc551a}.panel-btn.green:hover{background:#1abc5533}.panel-btn.amber{border-color:#f59f0a4d;color:var(--accent-amber);background:#f59f0a1a}.panel-btn.amber:hover{background:#f59f0a33}.panel-btn.red{border-color:#f425254d;color:var(--accent-red);background:#f425251a}.panel-btn.red:hover{background:#f4252533}.panel-btn.small{flex:0;padding:3px 8px;font-size:10px}.panel-close-btn{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;border:none;background:transparent;color:var(--text-muted);cursor:pointer;border-radius:4px;font-size:14px;transition:all .2s}.panel-close-btn:hover{background:var(--bg-hover);color:var(--text-primary)}.save-btn{padding:6px 14px;background:var(--accent-cyan);color:var(--bg-primary);border:none;border-radius:6px;font-family:JetBrains Mono,monospace;font-size:12px;font-weight:600;cursor:pointer;transition:background .2s}.save-btn:hover{background:#4cd9e6}.save-btn:disabled{opacity:.5;cursor:not-allowed}.discard-btn{padding:6px 14px;background:transparent;color:var(--text-secondary);border:1px solid var(--border-color);border-radius:6px;font-family:JetBrains Mono,monospace;font-size:12px;cursor:pointer;transition:all .2s}.discard-btn:hover{background:var(--bg-hover);color:var(--text-primary)}.discard-btn:disabled{opacity:.5;cursor:not-allowed}.tech-badge{display:inline-flex;padding:2px 8px;font-size:10px;font-family:JetBrains Mono,monospace;border-radius:4px;background:#20cfdf1a;color:var(--accent-cyan);border:1px solid hsl(185 75% 50% / .3)}.active-badge{font-size:9px;font-weight:700;padding:2px 6px;border-radius:4px;background:#20cfdf26;color:var(--accent-cyan);border:1px solid hsl(185 75% 50% / .3)}.validation-badge{font-size:10px;font-weight:600;text-transform:uppercase;padding:2px 8px;border-radius:4px}.validation-badge.pass{background:#1abc5526;color:var(--accent-green);border:1px solid hsl(142 76% 42% / .3)}.validation-badge.fail{background:#f4252526;color:var(--accent-red);border:1px solid hsl(0 90% 55% / .3)}.ops-tabs{display:flex;gap:2px;background:var(--bg-tertiary);border-radius:6px;padding:2px}.ops-tab{padding:4px 12px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;border:none;background:transparent;color:var(--text-muted);cursor:pointer;border-radius:4px;font-family:JetBrains Mono,monospace;transition:all .2s}.ops-tab.active{background:var(--bg-hover);color:var(--text-primary)}.ops-tab:hover{color:var(--text-primary)}.dropdown-item.disabled{opacity:.4;cursor:default;pointer-events:none}.watermark{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);font-size:72px;font-weight:700;color:var(--watermark-color);pointer-events:none;z-index:0;-webkit-user-select:none;user-select:none;letter-spacing:-2px}.environment-tabs{display:flex;background:var(--bg-secondary);border-bottom:1px solid var(--border-color);padding:0 16px;gap:4px;height:48px;align-items:stretch}.env-tab{display:flex;align-items:center;gap:8px;padding:0 20px;background:transparent;border:none;border-bottom:3px solid transparent;cursor:pointer;font-size:13px;font-weight:500;color:var(--text-secondary);transition:all .2s;font-family:JetBrains Mono,monospace}.env-tab:hover{background:var(--bg-hover)}.env-tab.selected{border-bottom-color:var(--env-color);color:var(--text-primary);box-shadow:0 2px 12px color-mix(in srgb,var(--env-color) 20%,transparent)}.env-indicator{width:8px;height:8px;border-radius:50%;background:var(--env-color);opacity:.4;transition:opacity .2s}.env-tab.selected .env-indicator{opacity:1;animation:breathe 3s infinite}.env-label{font-weight:600}.env-meta{font-size:11px;color:var(--text-muted);font-weight:400;margin-left:8px;padding-left:8px;border-left:1px solid var(--border-color)}.component-info-panel{position:absolute;top:16px;left:16px;background:#090e1ae6;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);padding:16px;border-radius:12px;border:1px solid var(--border-color);font-size:12px;color:var(--text-primary);z-index:10;width:280px;box-shadow:0 2px 12px var(--shadow-color)}.node-details-panel{position:absolute;top:16px;left:16px;background:#090e1ae6;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);padding:16px;border-radius:12px;border:1px solid var(--border-color);font-size:12px;color:var(--text-primary);z-index:15;width:300px;max-height:calc(100vh - 200px);overflow-y:auto;box-shadow:0 2px 12px var(--shadow-color)}.node-details-header{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:12px;padding-bottom:12px;border-bottom:1px solid var(--border-color)}.node-details-title{font-size:14px;font-weight:600;color:var(--text-primary)}.node-details-type{font-size:10px;color:var(--text-muted);text-transform:uppercase;margin-top:2px}.node-details-type-row{display:flex;align-items:center;gap:8px;font-size:10px;color:var(--text-muted);text-transform:uppercase;margin-top:2px}.node-details-type-row .panel-btn{text-transform:none;font-size:10px;padding:2px 8px}.node-details-section{margin-bottom:12px}.node-details-section-title{font-size:10px;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:.5px;margin-bottom:6px}.node-details-path{font-family:JetBrains Mono,monospace;font-size:11px;color:var(--text-secondary);background:var(--bg-tertiary);padding:6px 8px;border-radius:4px;word-break:break-all;border:1px solid var(--border-light)}.node-details-tech-stack{display:flex;flex-wrap:wrap;gap:4px}.node-details-connections{display:flex;flex-direction:column;gap:4px}.connection-item{display:flex;align-items:center;gap:6px;font-size:11px;color:var(--text-secondary)}.connection-item-detailed{padding:8px 10px;background:var(--bg-tertiary);border-radius:6px;margin-bottom:6px;border:1px solid var(--border-light)}.connection-header{display:flex;align-items:center;gap:6px;font-size:12px;font-weight:500;color:var(--text-primary)}.connection-arrow{color:var(--text-muted)}.connection-target{color:var(--text-primary)}.connection-label{font-size:11px;color:var(--text-secondary);margin-top:4px;padding-left:18px;line-height:1.4}.node-details-actions{display:flex;gap:8px;margin-top:12px;padding-top:12px;border-top:1px solid var(--border-color)}.env-badge{display:inline-block;padding:4px 12px;border-radius:20px;color:var(--text-primary);font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;margin-bottom:12px}.env-details{padding:10px 0;border-top:1px solid var(--border-color);border-bottom:1px solid var(--border-color);margin-bottom:12px}.env-detail-row{display:flex;justify-content:space-between;align-items:center;padding:4px 0}.env-detail-label{font-size:10px;color:var(--text-muted);text-transform:uppercase}.env-detail-value{font-size:11px;font-weight:500;color:var(--text-secondary)}.component-list{display:flex;flex-direction:column;gap:6px}.component-list-title{font-weight:600;color:var(--text-muted);text-transform:uppercase;font-size:10px;letter-spacing:1px;margin-bottom:4px}.component-item{display:flex;flex-direction:column;align-items:flex-start;gap:2px;padding:8px 10px;background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:8px;cursor:pointer;transition:all .2s;text-align:left;width:100%}.component-item:hover{background:var(--bg-hover);border-color:var(--accent-cyan);transform:translate(2px);box-shadow:var(--glow-cyan)}.component-name{font-weight:600;font-size:12px;color:var(--text-primary)}.component-path{font-size:10px;color:var(--text-muted);font-family:JetBrains Mono,monospace}.git-info{display:flex;align-items:center;gap:8px;margin-top:4px}.git-branch{display:inline-flex;align-items:center;padding:2px 6px;background:#20cfdf1a;border:1px solid hsl(185 75% 50% / .3);border-radius:4px;font-size:10px;font-family:JetBrains Mono,monospace;color:var(--accent-cyan)}.git-hash{display:inline-flex;align-items:center;padding:2px 6px;background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:4px;font-size:10px;font-family:JetBrains Mono,monospace;color:var(--text-secondary)}.no-git-info{color:var(--text-muted);font-style:italic;text-align:center;padding:12px}.canvas-container{flex:1;position:relative}.react-flow{background:var(--canvas-bg)!important}.react-flow__background{background:var(--canvas-bg)}.react-flow__controls{background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:8px;box-shadow:0 2px 8px var(--shadow-color)}.react-flow__controls-button{background:var(--bg-secondary);border-color:var(--border-color);fill:#fff;color:#fff}.react-flow__controls-button:hover{background:var(--bg-hover);fill:var(--text-primary)}.arch-node{padding:14px 16px;border-radius:12px;font-size:12px;font-weight:500;border:1.5px solid;min-width:130px;text-align:left;box-shadow:0 4px 16px #00000040,inset 0 1px #ffffff0d;transition:transform .2s,box-shadow .2s;display:flex;flex-direction:column;gap:6px;position:relative;overflow:hidden}.arch-node:before{content:"";position:absolute;top:0;left:0;right:0;height:1px;background:linear-gradient(90deg,transparent,rgba(255,255,255,.08),transparent)}.arch-node:hover{transform:translateY(-2px);box-shadow:0 8px 24px #00000059,inset 0 1px #ffffff0d}.arch-node .node-type-icon{position:absolute;top:10px;right:12px;font-size:16px;opacity:.35;line-height:1}.arch-node .node-content{flex:1;min-width:0}.arch-node .label{font-weight:700;font-size:13px;margin-bottom:2px;letter-spacing:-.2px;padding-right:20px}.arch-node .sublabel{font-size:10px;opacity:.7}.arch-node .node-desc{font-size:9px;opacity:.5;line-height:1.3;margin-top:2px;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}.arch-node .node-tech-row{display:flex;flex-wrap:wrap;gap:3px;margin-top:2px}.arch-node .node-tech-pill{font-size:8px;font-weight:600;padding:1px 6px;border-radius:3px;background:#ffffff0f;border:1px solid rgba(255,255,255,.08);opacity:.7;letter-spacing:.2px;text-transform:uppercase}.arch-node.presentation{background:linear-gradient(145deg,#122436,#152e47);border-color:#2680d999;color:#c6d9ec}.arch-node.presentation:hover{border-color:#3c8cdd;box-shadow:0 0 24px #2680d933,0 8px 24px #00000059}.arch-node.presentation .node-tech-pill{border-color:#2680d940;color:#99bfe6}.arch-node.application{background:linear-gradient(145deg,#322415,#432e19);border-color:#e88c3099;color:#ecd9c6}.arch-node.application:hover{border-color:#eb9947;box-shadow:0 0 24px #e88c3033,0 8px 24px #00000059}.arch-node.application .node-tech-pill{border-color:#e88c3040;color:#e6bf99}.arch-node.data{background:linear-gradient(145deg,#14291b,#183923);border-color:#2eb85c99;color:#c9e8d4}.arch-node.data:hover{border-color:#3c6;box-shadow:0 0 24px #2eb85c33,0 8px 24px #00000059}.arch-node.data .node-tech-pill{border-color:#2eb85c40;color:#9fdfb5}.arch-node.external{background:linear-gradient(145deg,#27192e,#331e3e);border-color:#9f53c699;color:#ddcde4}.arch-node.external:hover{border-color:#a6c;box-shadow:0 0 24px #9f53c633,0 8px 24px #00000059}.arch-node.external .node-tech-pill{border-color:#9f53c640;color:#c8a6d9}.arch-node.deployment{background:linear-gradient(145deg,#2e1919,#3e1e1e);border-color:#c339;color:#e4cdcd}.arch-node.deployment:hover{border-color:#d14747;box-shadow:0 0 24px #c333,0 8px 24px #00000059}.arch-node.deployment .node-tech-pill{border-color:#cc333340;color:#df9f9f}.react-flow__resize-control{border-radius:2px!important}.react-flow__resize-control.handle{width:8px!important;height:8px!important}.react-flow__node.selected .arch-node{box-shadow:0 0 0 2px #20cfdf66,0 4px 16px #0006}.arch-node.active{animation:pulse-border 2s infinite}@keyframes pulse-border{0%,to{box-shadow:0 0 #1abc5566}50%{box-shadow:0 0 0 8px #1abc5500}}.arch-node.highlighted{animation:highlight-glow 1.5s ease-in-out;box-shadow:0 0 20px #20cfdf99,0 0 40px #20cfdf4d;transform:scale(1.05);z-index:100}@keyframes highlight-glow{0%{box-shadow:0 0 #20cfdf00;transform:scale(1)}20%{box-shadow:0 0 30px #20cfdfcc,0 0 60px #20cfdf66;transform:scale(1.08)}to{box-shadow:0 0 20px #20cfdf99,0 0 40px #20cfdf4d;transform:scale(1.05)}}.node-actions-panel{position:absolute;top:60px;right:16px;background:#090e1ae6;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border:1px solid var(--border-color);border-radius:10px;padding:12px;box-shadow:0 4px 12px var(--shadow-color);z-index:10;min-width:180px}.node-actions-title{font-size:11px;font-weight:600;color:var(--text-secondary);margin-bottom:10px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:200px}.node-actions-buttons{display:flex;gap:8px;margin-bottom:8px}.node-actions-hint{font-size:10px;color:var(--text-muted);text-align:center;padding-top:8px;border-top:1px solid var(--border-color)}.lock-icon{margin-right:6px}.arch-node.locked{opacity:.7;cursor:not-allowed}.arch-node.locked:after{content:"🔒";position:absolute;top:4px;right:4px;font-size:10px}.hidden-nodes-panel{position:absolute;bottom:60px;right:16px;background:#090e1ae6;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border:1px solid var(--border-color);border-radius:10px;padding:12px;box-shadow:0 4px 12px var(--shadow-color);z-index:10;min-width:200px;max-width:280px;max-height:300px;overflow-y:auto}.hidden-nodes-title{display:flex;justify-content:space-between;align-items:center;font-size:12px;font-weight:600;color:var(--text-secondary);margin-bottom:10px;padding-bottom:8px;border-bottom:1px solid var(--border-color)}.hidden-nodes-list{display:flex;flex-direction:column;gap:6px}.hidden-node-item{display:flex;justify-content:space-between;align-items:center;padding:6px 8px;background:var(--bg-tertiary);border-radius:6px}.hidden-node-name{font-size:11px;color:var(--text-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:140px}.status-bar{background:var(--bg-secondary);border-top:1px solid var(--border-color);padding:10px 20px;display:grid;grid-template-columns:1fr auto 1fr;align-items:center;font-size:12px;color:var(--text-secondary)}.status-left{display:flex;align-items:center;gap:16px;justify-self:start}.status-center{display:flex;align-items:center;justify-self:center}.status-indicator{display:flex;align-items:center;gap:8px}.status-dot{width:8px;height:8px;border-radius:50%;background:var(--accent-green);animation:breathe 2s infinite}.status-dot.disconnected{background:var(--accent-red);animation:none}@keyframes breathe{0%,to{opacity:1;transform:scale(1)}50%{opacity:.6;transform:scale(.95)}}.status-right{display:flex;align-items:center;gap:20px;justify-self:end}.layer-dropdown-item{gap:10px!important;transition:opacity .2s}.layer-dropdown-item.hidden-layer{opacity:.4}.layer-toggle-indicator{margin-left:auto;font-size:10px;color:var(--text-muted)}.legend-color{width:16px;height:16px;border-radius:4px;border:2px solid}.legend-color.presentation{background:#132639;border-color:#2680d9}.legend-color.application{background:#362617;border-color:#e88c30}.legend-color.data{background:#162d1d;border-color:#2eb85c}.legend-color.external{background:#2a1b32;border-color:#9f53c6}.legend-color.deployment{background:#321b1b;border-color:#c33}.loading{display:flex;align-items:center;justify-content:center;height:100%;color:var(--text-secondary);font-size:14px}.loading-spinner{width:24px;height:24px;border:3px solid var(--border-color);border-top-color:var(--accent-cyan);border-radius:50%;animation:spin 1s linear infinite;margin-right:12px}@keyframes spin{to{transform:rotate(360deg)}}.agent-panel{position:absolute;bottom:60px;left:16px;background:#090e1ae6;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);padding:12px 16px;border-radius:10px;border:1px solid var(--border-color);font-size:11px;color:var(--text-primary);z-index:10;max-width:300px;box-shadow:0 2px 12px var(--shadow-color)}.agent-panel-title{font-weight:600;margin-bottom:8px;color:var(--text-muted);text-transform:uppercase;font-size:10px}.agent-item{display:flex;align-items:center;gap:8px;padding:6px 0;border-bottom:1px solid var(--border-color)}.agent-item:last-child{border-bottom:none}.agent-dot{width:6px;height:6px;border-radius:50%}.agent-dot.active{background:var(--accent-green);animation:breathe 1.5s infinite}.agent-name{font-weight:500;color:var(--text-primary)}.agent-target{color:var(--text-muted);margin-left:auto}.flow-dropdown{min-width:240px}.flow-dot-inline{display:inline-block;width:8px;height:8px;border-radius:50%;flex-shrink:0}.toolbar-btn.has-active-flow{background:#20cfdf1a;border-color:#20cfdf4d;color:var(--accent-cyan);gap:6px}.toolbar-btn.has-new-flow{position:relative}.flow-new-indicator{display:inline-block;width:7px;height:7px;border-radius:50%;background:var(--accent-green);margin-left:5px;vertical-align:middle;animation:flow-new-blink 1.2s ease-in-out infinite}@keyframes flow-new-blink{0%,to{opacity:1}50%{opacity:.2}}.toolbar-btn.has-hidden{color:var(--accent-amber)}.flow-category-header{display:flex;align-items:center;gap:6px;padding:6px 10px 4px;font-size:9px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--text-muted)}.flow-dropdown-item{gap:10px!important}.flow-dropdown-item.active{background:#20cfdf1a!important;color:var(--accent-cyan)!important}.flow-dropdown-name{flex:1}.flow-clear{color:var(--accent-red)!important}.flow-progress-bar{position:absolute;bottom:16px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:12px;background:#090e1af2;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border:1px solid var(--border-color);border-radius:12px;padding:10px 16px;z-index:15;max-width:90vw;box-shadow:0 4px 20px var(--shadow-color),var(--glow-cyan);animation:slide-up .3s ease-out}.flow-progress-dot{width:10px;height:10px;border-radius:50%;flex-shrink:0;animation:breathe 2s infinite}.flow-progress-name{font-size:12px;font-weight:600;color:var(--text-primary);white-space:nowrap;flex-shrink:0}.flow-progress-steps{display:flex;gap:4px;overflow-x:auto;max-width:500px}.flow-progress-step{padding:4px 8px;background:var(--bg-tertiary);border-radius:4px;font-size:10px;border-left:2px solid transparent;transition:all .3s;opacity:.4;color:var(--text-secondary);white-space:nowrap;flex-shrink:0}.flow-progress-step.active{opacity:1;background:var(--bg-hover);color:var(--text-primary)}.flow-progress-step.current{font-weight:600;box-shadow:0 1px 4px var(--shadow-color)}.flow-progress-controls{display:flex;gap:4px;flex-shrink:0;margin-left:4px}.flow-progress-btn{width:28px;height:28px;display:flex;align-items:center;justify-content:center;border:1px solid var(--border-color);background:var(--bg-tertiary);border-radius:6px;cursor:pointer;font-size:12px;color:var(--text-secondary);transition:all .2s}.flow-progress-btn:hover{background:var(--bg-hover);color:var(--text-primary)}.flow-progress-btn.stop{border-color:#f425254d;color:var(--accent-red)}.flow-progress-btn.stop:hover{background:#f4252526}.flow-hud-overlay{position:fixed;top:0;right:0;bottom:0;left:0;z-index:100;display:flex;align-items:center;justify-content:center;background:#070a13d9;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);animation:flowHudFadeIn .2s ease-out}@keyframes flowHudFadeIn{0%{opacity:0}to{opacity:1}}.flow-hud-panel{width:90vw;height:85vh;max-width:1400px;background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:16px;display:flex;flex-direction:column;overflow:hidden;box-shadow:0 0 60px #20cfdf14,0 8px 32px #0006}.flow-hud-header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid var(--border-color);flex-shrink:0}.flow-hud-title{display:flex;align-items:center;gap:10px;font-size:15px;font-weight:700;color:var(--text-primary);letter-spacing:.3px}.flow-hud-dot{width:10px;height:10px;border-radius:50%;flex-shrink:0}.flow-hud-controls{display:flex;align-items:center;gap:6px}.flow-hud-btn{background:var(--bg-tertiary);border:1px solid var(--border-color);color:var(--text-primary);width:32px;height:32px;border-radius:8px;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:14px;transition:all .15s}.flow-hud-btn:hover{background:var(--bg-hover);border-color:var(--accent-cyan)}.flow-hud-close{background:var(--bg-tertiary);border:1px solid var(--border-color);color:var(--text-muted);width:32px;height:32px;border-radius:8px;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:16px;font-weight:600;transition:all .15s}.flow-hud-close:hover{background:#f4252526;border-color:var(--accent-red);color:var(--accent-red)}.flow-hud-description{padding:10px 20px;font-size:12px;color:var(--text-muted);border-bottom:1px solid var(--border-color);flex-shrink:0}.flow-hud-canvas{flex:1;min-height:0;position:relative}.flow-hud-canvas .react-flow__controls{bottom:10px;left:10px}.flow-hud-steps{display:flex;align-items:center;gap:8px;padding:14px 20px;border-top:1px solid var(--border-color);overflow-x:auto;flex-shrink:0}.flow-hud-step{display:flex;align-items:center;gap:6px;padding:6px 12px;border-radius:8px;font-size:11px;color:var(--text-muted);background:var(--bg-tertiary);border:1px solid var(--border-color);white-space:nowrap;transition:all .3s}.flow-hud-step.active{color:var(--text-primary);border-color:currentColor}.flow-hud-step.current{box-shadow:0 0 12px currentColor}.flow-hud-step-num{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border-radius:50%;font-size:10px;font-weight:700;color:var(--text-primary);background:var(--border-color);flex-shrink:0;transition:background .3s}.git-repo-badge{display:inline-flex;align-items:center;padding:2px 8px;background:#2a1b32;border:1px solid hsl(280 50% 55% / .3);border-radius:4px;font-size:11px;font-family:JetBrains Mono,monospace;font-weight:500;color:#bf8cd9;text-decoration:none;transition:all .2s}.git-repo-badge:hover{background:#382442;border-color:#9f53c680}.save-panel{position:fixed;bottom:55px;left:50%;transform:translate(-50%);background:#20cfdf26;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);color:var(--text-primary);padding:12px 20px;border-radius:10px;border:1px solid var(--accent-cyan);display:flex;gap:16px;align-items:center;box-shadow:var(--glow-cyan);z-index:100;animation:slide-up .3s ease-out}@keyframes slide-up{0%{opacity:0;transform:translate(-50%) translateY(20px)}to{opacity:1;transform:translate(-50%) translateY(0)}}.save-panel-text{font-size:13px;font-weight:500}.react-flow__node.selected{outline:2px solid var(--accent-cyan);outline-offset:2px}.react-flow__handle{width:10px;height:10px;background:var(--accent-cyan);border:2px solid var(--bg-primary);opacity:0;transition:opacity .2s}.react-flow__node:hover .react-flow__handle,.react-flow__node.selected .react-flow__handle{opacity:1}.react-flow__handle:hover{transform:scale(1.2)}.clipboard-indicator{position:fixed;bottom:120px;left:50%;transform:translate(-50%);background:var(--bg-hover);color:var(--text-primary);padding:8px 16px;border-radius:6px;font-size:12px;border:1px solid var(--border-color);z-index:100;animation:fade-in-out 2s ease-in-out;pointer-events:none}@keyframes fade-in-out{0%,to{opacity:0;transform:translate(-50%) translateY(10px)}20%,80%{opacity:1;transform:translate(-50%) translateY(0)}}@keyframes fade-in-up{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}@keyframes scan-line{0%{top:-100%}to{top:200%}}.stats-overlay{position:absolute;top:60px;right:16px;background:var(--bg-secondary);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);padding:12px;border-radius:12px;border:1px solid var(--border-color);z-index:10;width:200px;box-shadow:var(--glow-cyan)}[data-drag-handle]{cursor:grab}[data-drag-handle]:active{cursor:grabbing}.stats-overlay-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px}.stats-overlay-title{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:1px;color:var(--accent-cyan)}.stats-scan-info{display:flex;flex-wrap:wrap;gap:6px 12px;padding:8px 0;margin-bottom:8px;border-bottom:1px solid var(--border-color);font-size:11px;color:var(--text-muted)}.scan-info-item{white-space:nowrap}.scan-info-item:not(:last-child):after{content:"|";margin-left:12px;opacity:.3}.stats-grid{display:grid;grid-template-columns:1fr 1fr;gap:6px}.stat-card{padding:6px 8px;background:var(--bg-tertiary);border-radius:6px;border:1px solid var(--border-color);overflow:hidden}.stat-value{font-size:16px;font-weight:700;color:var(--text-primary)}.stat-label{font-size:9px;text-transform:uppercase;letter-spacing:.5px;color:var(--text-muted);margin-top:2px}.stat-card.highlight{border-color:var(--accent-cyan);box-shadow:var(--glow-cyan)}.validation-panel{position:absolute;top:60px;right:16px;background:var(--bg-secondary);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);padding:16px;border-radius:12px;border:1px solid var(--border-color);z-index:10;width:280px;max-height:calc(100vh - 200px);overflow-y:auto;box-shadow:0 2px 12px var(--shadow-color)}.validation-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px}.validation-title{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:1px;color:var(--text-muted)}.violation-item{padding:10px;background:var(--bg-tertiary);border-radius:8px;margin-bottom:6px;border-left:3px solid;cursor:pointer;transition:all .2s}.violation-item:hover{background:var(--bg-hover)}.violation-item.error{border-left-color:var(--accent-red)}.violation-item.warn{border-left-color:var(--accent-amber)}.violation-rule{font-size:10px;font-weight:600;text-transform:uppercase;color:var(--text-muted);margin-bottom:4px}.violation-message{font-size:11px;color:var(--text-secondary)}.ops-panel{position:absolute;top:60px;right:16px;background:var(--bg-secondary);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border-radius:12px;border:1px solid var(--border-color);z-index:10;width:280px;max-height:calc(100vh - 200px);overflow-y:auto;box-shadow:0 2px 12px var(--shadow-color)}.audit-panel{position:absolute;top:60px;right:16px;background:var(--bg-secondary);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);padding:0;border-radius:12px;border:1px solid var(--border-color);z-index:10;width:340px;max-height:calc(100vh - 200px);overflow-y:auto;box-shadow:0 2px 12px var(--shadow-color);display:flex;flex-direction:column}.audit-header{display:flex;justify-content:space-between;align-items:center;padding:12px 16px 0}.audit-title{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:1px;color:var(--text-muted)}.audit-tabs{display:flex;border-bottom:1px solid var(--border-color);margin:12px 0 0;padding:0 8px;gap:4px}.audit-tab{flex:1;padding:8px 12px;background:transparent;border:none;color:var(--text-muted);font-size:10px;font-weight:600;text-transform:uppercase;cursor:pointer;border-bottom:2px solid transparent;transition:all .2s;white-space:nowrap}.audit-tab:hover{color:var(--text-secondary)}.audit-tab.active{color:var(--accent-cyan);border-bottom-color:var(--accent-cyan)}.audit-panel>:not(.audit-header):not(.audit-tabs){padding:0 16px}.audit-summary{padding:10px;background:var(--bg-tertiary);border-radius:8px;margin:12px 0;text-align:center}.audit-metrics{padding:10px;background:var(--bg-tertiary);border-radius:8px;margin-bottom:12px}.audit-metric-row{display:flex;justify-content:space-between;font-size:11px;padding:6px 0;color:var(--text-secondary)}.audit-metric-label{flex:1}.audit-section{margin-bottom:12px}.audit-section-title{font-size:10px;font-weight:600;text-transform:uppercase;color:var(--text-muted);margin-bottom:8px;padding:0 4px}.audit-section-header{display:flex;align-items:center;gap:6px;cursor:pointer;padding:8px 4px;border-radius:4px;-webkit-user-select:none;user-select:none;transition:all .2s ease}.audit-section-header:hover{background:#b9bf7f14}.audit-section-toggle{display:inline-flex;align-items:center;justify-content:center;width:12px;font-size:8px;color:#20cfdf;transition:transform .2s ease}.audit-section-content{display:flex;flex-direction:column;gap:6px;padding:0 4px 8px;animation:slideDown .2s ease}@keyframes slideDown{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.audit-issue{padding:10px;background:var(--bg-tertiary);border-radius:8px;margin-bottom:6px;border-left:3px solid;cursor:pointer;transition:all .2s}.audit-issue:hover{background:var(--bg-hover)}.audit-issue.error{border-left-color:var(--accent-red)}.audit-issue.warn{border-left-color:var(--accent-amber)}.audit-issue-rule{font-size:11px;font-weight:600;margin-bottom:4px;color:var(--text-primary)}.audit-issue-message{font-size:10px;color:var(--text-secondary);line-height:1.4}.audit-components{margin-top:12px}.audit-component-item{display:flex;align-items:center;gap:8px;padding:10px;background:var(--bg-tertiary);border-radius:8px;margin-bottom:6px;cursor:pointer;transition:all .2s;font-size:11px}.audit-component-item:hover{background:var(--bg-hover)}.audit-component-info{flex:1;min-width:0}.audit-component-name{font-weight:500;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.audit-badge{display:inline-block;padding:4px 8px;border-radius:4px;font-size:9px;font-weight:600;margin-left:8px}.audit-badge.pass{background:var(--accent-green);color:#000}.audit-badge.fail{background:var(--accent-red);color:#fff}.ops-header{display:flex;justify-content:space-between;align-items:center;padding:10px 12px 0}.ops-section{padding:12px}.ops-empty{color:var(--text-muted);font-size:11px;text-align:center;padding:20px 12px}.ops-hint{display:block;margin-top:6px;font-family:JetBrains Mono,monospace;font-size:10px;color:var(--accent-cyan);opacity:.7}.wf-item{border:1px solid var(--border-color);border-radius:8px;margin-bottom:6px;overflow:hidden}.wf-header{display:flex;justify-content:space-between;align-items:center;padding:8px 10px;cursor:pointer;transition:background .15s}.wf-header:hover{background:var(--bg-hover)}.wf-header-left{display:flex;align-items:center;gap:6px;overflow:hidden}.wf-expand{font-size:11px;color:var(--text-muted);width:12px;flex-shrink:0}.wf-name{font-size:11px;font-weight:600;color:var(--text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wf-tag{font-size:8px;color:var(--text-muted);background:var(--bg-tertiary);padding:1px 5px;border-radius:3px;flex-shrink:0}.wf-status{font-size:9px;font-weight:600;flex-shrink:0}.wf-status.completed{color:var(--accent-green)}.wf-status.running{color:var(--accent-amber)}.wf-status.failed{color:var(--accent-red)}.wf-detail{padding:0 10px 10px;border-top:1px solid var(--border-color)}.wf-desc{font-size:10px;color:var(--text-muted);padding:8px 0 6px}.wf-steps{display:flex;flex-direction:column;gap:3px}.wf-step{display:flex;align-items:center;gap:6px;font-size:10px;padding:3px 0}.wf-step-icon{font-size:10px;width:14px;text-align:center}.wf-step.completed .wf-step-icon{color:var(--accent-green)}.wf-step.running .wf-step-icon{color:var(--accent-amber)}.wf-step.failed .wf-step-icon{color:var(--accent-red)}.wf-step.pending .wf-step-icon{color:var(--text-muted)}.wf-step-id{color:var(--text-secondary)}.wf-step.completed .wf-step-id{color:var(--text-muted)}.wf-progress{display:flex;align-items:center;gap:8px;margin-top:6px}.wf-progress-bar{flex:1;height:4px;background:var(--bg-tertiary);border-radius:2px;overflow:hidden}.wf-progress-fill{height:100%;background:var(--accent-cyan);border-radius:2px;transition:width .3s ease}.wf-progress-label{font-size:10px;font-weight:600;color:var(--text-muted);min-width:28px;text-align:right}.wf-steps-preview{font-size:10px;color:var(--text-muted);font-family:JetBrains Mono,monospace;padding:4px 0}.wf-run-hint{font-size:9px;font-family:JetBrains Mono,monospace;color:var(--accent-cyan);opacity:.6;margin-top:6px}.wf-actions{display:flex;gap:6px;margin-top:8px}.wf-actions .panel-btn{flex:1}.wf-error{font-size:11px;color:var(--accent-red);background:#f425251a;border:1px solid hsl(0 90% 55% / .25);border-radius:6px;padding:6px 10px;margin-bottom:8px}.change-indicator{position:absolute;top:-6px;right:-6px;width:16px;height:16px;border-radius:50%;font-size:10px;font-weight:700;display:flex;align-items:center;justify-content:center;z-index:10;animation:change-pulse 2s ease-in-out infinite}.change-indicator.change-added{background:var(--accent-green);color:#fff}.change-indicator.change-modified{background:var(--accent-amber);color:#000}.change-indicator.change-removed{background:var(--accent-red);color:#fff}.arch-node.change-added{box-shadow:0 0 0 2px var(--accent-green),0 0 12px #1abc554d}.arch-node.change-modified{box-shadow:0 0 0 2px var(--accent-amber),0 0 12px #f59f0a4d}.arch-node.change-removed{box-shadow:0 0 0 2px var(--accent-red),0 0 12px #f425254d;opacity:.6}@keyframes change-pulse{0%,to{transform:scale(1)}50%{transform:scale(1.3)}}.chat-panel{position:absolute;bottom:16px;right:16px;width:350px;height:400px;background:#090e1af2;-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);border:1px solid var(--border-color);border-radius:12px;display:flex;flex-direction:column;z-index:20;box-shadow:0 4px 24px var(--shadow-color),var(--glow-cyan);animation:slide-up-chat .2s ease-out}@keyframes slide-up-chat{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}.chat-header{display:flex;justify-content:space-between;align-items:center;padding:10px 14px;border-bottom:1px solid var(--border-color);flex-shrink:0}.chat-title{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--accent-cyan)}.chat-messages{flex:1;overflow-y:auto;padding:12px;display:flex;flex-direction:column;gap:8px;min-height:0}.chat-empty{color:var(--text-muted);font-size:12px;text-align:center;padding:40px 20px}.chat-message{max-width:85%;padding:8px 12px;border-radius:10px;font-size:12px;line-height:1.5;word-wrap:break-word;white-space:pre-wrap}.chat-message.user{align-self:flex-end;background:#20cfdf26;border:1px solid hsl(185 75% 50% / .25);color:var(--text-primary)}.chat-message.assistant{align-self:flex-start;background:var(--bg-tertiary);border:1px solid var(--border-color);color:var(--text-secondary)}.chat-message-content{font-family:JetBrains Mono,monospace}.chat-cursor{display:inline-block;width:6px;height:14px;background:var(--accent-cyan);margin-left:2px;vertical-align:text-bottom;animation:chat-blink .8s step-end infinite}@keyframes chat-blink{0%,to{opacity:1}50%{opacity:0}}.chat-input-bar{display:flex;gap:6px;padding:10px 12px;border-top:1px solid var(--border-color);flex-shrink:0}.chat-input{flex:1;height:32px;background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:6px;padding:0 10px;font-size:12px;font-family:JetBrains Mono,monospace;color:var(--text-primary);outline:none;transition:border-color .2s}.chat-input::placeholder{color:var(--text-muted)}.chat-input:focus{border-color:var(--accent-cyan);box-shadow:0 0 0 2px #20cfdf26}.chat-send-btn,.chat-stop-btn{display:flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:6px;border:1px solid var(--border-color);cursor:pointer;font-size:14px;font-weight:700;transition:all .2s;flex-shrink:0}.chat-send-btn{background:#20cfdf26;color:var(--accent-cyan);border-color:#20cfdf4d}.chat-send-btn:hover:not(:disabled){background:#20cfdf40}.chat-send-btn:disabled{opacity:.3;cursor:not-allowed}.chat-stop-btn{background:#f4252526;color:var(--accent-red);border-color:#f425254d;font-size:10px}.chat-stop-btn:hover{background:#f4252540}[data-theme=light] .arch-node{box-shadow:0 2px 12px #00000014,inset 0 1px #fff9}[data-theme=light] .arch-node:hover{box-shadow:0 4px 20px #0000001f,inset 0 1px #fff9}[data-theme=light] .arch-node:before{background:linear-gradient(90deg,transparent,rgba(255,255,255,.4),transparent)}[data-theme=light] .arch-node .node-tech-pill{background:#0000000a;border-color:#0000001a}[data-theme=light] .arch-node.presentation{background:linear-gradient(145deg,#e9f0f7,#d7e6f4);border-color:#428cd780;color:#19334d}[data-theme=light] .arch-node.presentation:hover{border-color:#428cd7;box-shadow:0 0 20px #428cd726,0 4px 20px #0000001f}[data-theme=light] .arch-node.presentation .node-tech-pill{color:#2e669e}[data-theme=light] .arch-node.application{background:linear-gradient(145deg,#f7f0e8,#f5e6d6);border-color:#df802080;color:#452e17}[data-theme=light] .arch-node.application:hover{border-color:#df8020;box-shadow:0 0 20px #df802026,0 4px 20px #0000001f}[data-theme=light] .arch-node.application .node-tech-pill{color:#8f5924}[data-theme=light] .arch-node.data{background:linear-gradient(145deg,#e6f4eb,#d5f1de);border-color:#2e9e5380;color:#1c4028}[data-theme=light] .arch-node.data:hover{border-color:#2e9e53;box-shadow:0 0 20px #2e9e5326,0 4px 20px #0000001f}[data-theme=light] .arch-node.data .node-tech-pill{color:#2a6f41}[data-theme=light] .arch-node.external{background:linear-gradient(145deg,#f1eaf5,#e9daf1);border-color:#9540bf80;color:#392145}[data-theme=light] .arch-node.external:hover{border-color:#9540bf;box-shadow:0 0 20px #9540bf26,0 4px 20px #0000001f}[data-theme=light] .arch-node.external .node-tech-pill{color:#743d8f}[data-theme=light] .arch-node.deployment{background:linear-gradient(145deg,#f7eded,#f4dddd);border-color:#be373780;color:#452121}[data-theme=light] .arch-node.deployment:hover{border-color:#be3737;box-shadow:0 0 20px #be373726,0 4px 20px #0000001f}[data-theme=light] .arch-node.deployment .node-tech-pill{color:#813131}[data-theme=light] .legend-color.presentation{background:#d9e6f2;border-color:#428cd7}[data-theme=light] .legend-color.application{background:#f2e6d9;border-color:#df8020}[data-theme=light] .legend-color.data{background:#dbf0e2;border-color:#2e9e53}[data-theme=light] .legend-color.external{background:#e8ddee;border-color:#9540bf}[data-theme=light] .legend-color.deployment{background:#f0dbdb;border-color:#be3737}[data-theme=light] .branding-header{background:#f6f7f9eb;border-bottom-color:#1295a126;box-shadow:0 1px 12px #1295a10f}[data-theme=light] .component-info-panel,[data-theme=light] .node-details-panel,[data-theme=light] .node-actions-panel,[data-theme=light] .hidden-nodes-panel,[data-theme=light] .agent-panel,[data-theme=light] .chat-panel{background:#f6f7f9eb;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px)}[data-theme=light] .flow-progress-bar{background:#f6f7f9f2;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px)}[data-theme=light] .save-panel{background:#1295a11a}[data-theme=light] ::-webkit-scrollbar-track{background:var(--bg-secondary)}[data-theme=light] ::-webkit-scrollbar-thumb{background:#bec4cf}[data-theme=light] ::-webkit-scrollbar-thumb:hover{background:#a1a9ba}[data-theme=light] .git-repo-badge{background:#efe7f3;border-color:#9540bf4d;color:#739}[data-theme=light] .git-repo-badge:hover{background:#e4d6eb;border-color:#9540bf80}[data-theme=light] .toolbar-btn.active{background:#1295a11a}[data-theme=light] .flow-progress-btn.stop{border-color:#d4111140}[data-theme=light] .flow-progress-btn.stop:hover{background:#d411111a}[data-theme=light] .react-flow__controls-button{background:var(--bg-secondary);border-color:var(--border-color);fill:var(--text-primary);color:var(--text-primary)}[data-theme=light] .react-flow__controls-button:hover{background:var(--bg-hover)}[data-theme=light] .react-flow__handle{border-color:var(--bg-primary)}[data-theme=light] .react-flow__node.selected .arch-node{box-shadow:0 0 0 2px #1295a159,0 4px 12px #0000001f}.health-panel{position:absolute;top:16px;right:16px;width:320px;max-height:70vh;overflow-y:auto;background:#090e1aeb;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border:1px solid var(--border-color);border-radius:12px;z-index:10;box-shadow:0 4px 24px var(--shadow-color)}.health-header{display:flex;align-items:center;justify-content:space-between;padding:12px 14px;border-bottom:1px solid var(--border-color)}.health-title{font-size:13px;font-weight:700;color:var(--text-primary)}.health-graph-summary{display:flex;gap:4px;padding:10px 14px;border-bottom:1px solid var(--border-color)}.health-stat{display:flex;flex-direction:column;align-items:center;flex:1;padding:6px;background:var(--bg-tertiary);border-radius:6px}.health-stat-value{font-size:16px;font-weight:700;font-family:JetBrains Mono,monospace}.health-stat-label{font-size:9px;color:var(--text-muted);text-transform:uppercase;letter-spacing:.3px;margin-top:2px}.health-list{padding:4px 0;max-height:350px;overflow-y:auto}.health-item{display:flex;align-items:center;gap:10px;padding:8px 14px;cursor:pointer;transition:background .15s}.health-item:hover{background:var(--bg-hover)}.health-item-score{font-size:16px;font-weight:700;font-family:JetBrains Mono,monospace;min-width:32px;text-align:center}.health-item-info{flex:1;min-width:0}.health-item-name{font-size:12px;font-weight:600;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.health-item-badges{display:flex;gap:4px;margin-top:2px}.health-flag{font-size:9px;font-weight:600;padding:1px 5px;border-radius:3px;text-transform:uppercase;letter-spacing:.3px}.health-flag.orphan{background:#f59f0a26;color:var(--accent-amber);border:1px solid hsl(38 92% 50% / .3)}.health-flag.hub{background:#a65eed26;color:#a65eed;border:1px solid hsl(270 80% 65% / .3)}.health-flag.cycle{background:#f4252526;color:var(--accent-red);border:1px solid hsl(0 90% 55% / .3)}.health-flag.ok{background:#1abc5526;color:var(--accent-green);border:1px solid hsl(142 76% 42% / .3)}.health-item-connections{font-size:10px;color:var(--text-muted);font-family:JetBrains Mono,monospace;white-space:nowrap}.arch-node.health-green{border-color:var(--accent-green)!important;box-shadow:0 0 8px #1abc554d!important}.arch-node.health-amber{border-color:var(--accent-amber)!important;box-shadow:0 0 8px #f59f0a4d!important}.arch-node.health-red{border-color:var(--accent-red)!important;box-shadow:0 0 12px #f4252566!important}.arch-node.health-orphan{border-style:dashed!important}.arch-node.health-cycle{animation:health-pulse 2s ease-in-out infinite!important}@keyframes health-pulse{0%,to{box-shadow:0 0 8px #f425254d}50%{box-shadow:0 0 20px #f4252599}}.health-hub-badge{position:absolute;top:-8px;right:-8px;font-size:8px;font-weight:700;padding:2px 5px;border-radius:4px;background:#a65eed;color:#fff;letter-spacing:.3px;z-index:2}.pending-change-badge{position:absolute;top:-6px;right:-6px;font-size:9px;font-weight:700;padding:1px 5px;border-radius:8px;background:var(--accent-amber);color:#070a13;font-family:JetBrains Mono,monospace;cursor:pointer;z-index:2}.pending-sticky-note{background:#fceec5;border:1px solid hsl(40 60% 70%);border-radius:4px;padding:8px 10px;margin:0 -2px 4px;font-family:JetBrains Mono,monospace;font-size:10px;line-height:1.5;color:#38332e}.pending-sticky-header{font-weight:700;font-size:8px;text-transform:uppercase;letter-spacing:.3px;color:#74593e;margin-bottom:3px;padding-bottom:3px;border-bottom:1px solid hsl(40 50% 75%)}.pending-file-row{display:flex;align-items:center;gap:3px}.pending-file-event{font-weight:700;width:8px;flex-shrink:0}.pending-file-row.add .pending-file-event{color:#1f7a40}.pending-file-row.change .pending-file-event{color:#ad661f}.pending-file-row.unlink .pending-file-event{color:#c32222}.pending-file-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#464039}.pending-file-more{color:#998066;font-size:7px;margin-top:2px}.arch-node.has-pending-changes{box-shadow:0 0 0 2px #f59f0a66,0 0 12px #f59f0a26}.changelog-panel{position:absolute;top:16px;right:16px;width:340px;max-height:65vh;overflow-y:auto;background:#090e1aeb;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border:1px solid var(--border-color);border-radius:12px;z-index:10;box-shadow:0 4px 24px var(--shadow-color)}.changelog-header{display:flex;align-items:center;justify-content:space-between;padding:12px 14px;border-bottom:1px solid var(--border-color)}.changelog-title{font-size:13px;font-weight:700;color:var(--text-primary)}.changelog-summary{display:flex;align-items:center;gap:8px;padding:8px 14px;border-bottom:1px solid var(--border-color);font-size:11px}.changelog-summary-total{color:var(--text-secondary);font-weight:600}.changelog-badge{font-size:10px;font-weight:700;padding:1px 6px;border-radius:4px;font-family:JetBrains Mono,monospace}.changelog-badge.added{background:#1abc5526;color:var(--accent-green)}.changelog-badge.modified{background:#f59f0a26;color:var(--accent-amber)}.changelog-badge.removed{background:#f4252526;color:var(--accent-red)}.changelog-list{padding:4px 0}.changelog-entry{border-bottom:1px solid var(--border-light)}.changelog-entry:last-child{border-bottom:none}.changelog-entry-header{display:flex;align-items:center;gap:8px;padding:8px 14px;cursor:pointer;transition:background .15s}.changelog-entry-header:hover{background:var(--bg-hover)}.changelog-entry-time{font-size:11px;color:var(--text-muted);min-width:50px;font-family:JetBrains Mono,monospace}.changelog-entry-stats{display:flex;align-items:center;gap:6px;flex:1}.changelog-entry-files{font-size:11px;color:var(--text-secondary)}.changelog-expand{font-size:10px;color:var(--text-muted)}.changelog-entry-details{padding:4px 14px 10px;border-top:1px solid var(--border-light);margin:0 8px}.changelog-diff-section{margin-top:6px}.changelog-diff-label{font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;margin-bottom:2px}.changelog-diff-label.added{color:var(--accent-green)}.changelog-diff-label.modified{color:var(--accent-amber)}.changelog-diff-label.removed{color:var(--accent-red)}.changelog-diff-item{font-size:11px;color:var(--text-secondary);padding:2px 0 2px 8px;border-left:2px solid var(--border-color)}.changelog-entry-meta{font-size:10px;color:var(--text-muted);margin-top:6px;text-align:right}.changelog-dot{display:inline-block;width:6px;height:6px;border-radius:50%;background:var(--accent-green);margin-left:4px;animation:breathe 2s infinite}@keyframes breathe{0%,to{opacity:1}50%{opacity:.4}}.rules-config-toggle{display:flex;justify-content:center;padding:8px 14px;border-top:1px solid var(--border-color)}.rules-config{padding:8px 14px 12px;border-top:1px solid var(--border-color)}.rules-section-title{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:var(--text-muted);margin-bottom:6px}.rule-config-item{display:flex;flex-wrap:wrap;align-items:center;gap:6px;padding:6px 8px;border-radius:6px;margin-bottom:4px;background:var(--bg-tertiary);position:relative}.rule-config-item.custom{border-left:3px solid hsl(185 75% 50% / .5)}.rule-config-name{font-size:11px;font-weight:600;color:var(--text-primary);display:flex;align-items:center;gap:6px}.rule-config-desc{font-size:10px;color:var(--text-muted);width:100%}.rule-config-matcher{font-size:10px;color:var(--text-secondary);font-family:JetBrains Mono,monospace}.rule-level-badge{font-size:8px;font-weight:700;padding:1px 5px;border-radius:3px;text-transform:uppercase}.rule-level-badge.error{background:#f4252526;color:var(--accent-red)}.rule-level-badge.warn{background:#f59f0a26;color:var(--accent-amber)}.rule-type-badge{font-size:8px;font-weight:700;padding:1px 5px;border-radius:3px;text-transform:uppercase;margin-left:4px}.rule-type-badge.custom{background:#20cfdf26;color:var(--accent-cyan)}.rule-delete-btn{position:absolute;right:4px;top:50%;transform:translateY(-50%);flex:0!important;padding:2px 6px!important;min-width:auto}.rule-add-form{margin-top:10px;padding:10px;background:var(--bg-tertiary);border-radius:8px;border:1px solid var(--border-color)}.rule-add-title{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:var(--text-muted);margin-bottom:6px}.rule-input{width:100%;height:28px;background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:4px;padding:0 8px;font-size:11px;font-family:JetBrains Mono,monospace;color:var(--text-primary);outline:none;margin-bottom:4px}.rule-input:focus{border-color:var(--accent-cyan)}.rule-input::placeholder{color:var(--text-muted)}.rule-add-row{display:flex;align-items:center;gap:6px}.rule-arrow{font-size:12px;color:var(--text-muted);flex-shrink:0}.rule-select{height:28px;background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:4px;padding:0 8px;font-size:11px;font-family:JetBrains Mono,monospace;color:var(--text-primary);outline:none;flex:1}.rules-save-row{display:flex;justify-content:flex-end;margin-top:10px}[data-theme=light] .health-panel,[data-theme=light] .changelog-panel{background:#f9fafbf5}[data-theme=light] .arch-node.health-green{box-shadow:0 0 6px #178c4240!important}.rules-config-modal{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:var(--bg-primary);border:1px solid var(--border-color);border-radius:12px;box-shadow:0 25px 80px #000c;z-index:10000;max-width:650px;width:95%;max-height:75vh;overflow:hidden;display:flex;flex-direction:column}.rules-modal-header{display:flex;justify-content:space-between;align-items:center;padding:16px;border-bottom:1px solid var(--border-color);flex-shrink:0;background:var(--bg-secondary)}.rules-modal-title{font-weight:600;color:var(--text-primary);font-size:14px;letter-spacing:.5px}.rules-modal-tabs{display:flex;border-bottom:1px solid var(--border-color);background:var(--bg-secondary);flex-shrink:0}.rules-modal-tab{flex:1;padding:8px 12px;border:none;background:transparent;color:var(--text-secondary);font-size:11px;cursor:pointer;transition:all .2s ease;border-bottom:2px solid transparent}.rules-modal-tab:hover{color:var(--text-primary);background:#b9bf7f0d}.rules-modal-tab.active{color:#20cfdf;border-bottom-color:#20cfdf}.rules-modal-content{flex:1;overflow-y:auto;padding:16px;scrollbar-width:thin;scrollbar-color:var(--border-color) transparent}[data-theme=light] .arch-node.health-amber{box-shadow:0 0 6px #cb850b40!important}[data-theme=light] .arch-node.health-red{box-shadow:0 0 8px #d411114d!important}.changes-banner{display:flex;align-items:center;gap:10px;padding:6px 14px;background:#f59f0a14;border-bottom:1px solid hsl(38 92% 50% / .25);font-family:JetBrains Mono,monospace;font-size:12px;animation:changes-banner-slide-in .25s ease-out}@keyframes changes-banner-slide-in{0%{opacity:0;transform:translateY(-100%)}to{opacity:1;transform:translateY(0)}}.changes-banner-pulse{width:7px;height:7px;border-radius:50%;background:var(--accent-amber);flex-shrink:0;animation:changes-pulse 1.8s ease-in-out infinite}@keyframes changes-pulse{0%,to{opacity:1;box-shadow:0 0 #f59f0a66}50%{opacity:.6;box-shadow:0 0 0 4px #f59f0a00}}.changes-banner-text{color:var(--accent-amber);font-weight:600;letter-spacing:.02em;white-space:nowrap}.changes-banner-actions{display:flex;gap:6px;margin-left:4px}.changes-banner-btn{padding:3px 10px;border-radius:4px;border:1px solid transparent;font-family:JetBrains Mono,monospace;font-size:11px;font-weight:500;cursor:pointer;transition:all .15s ease}.changes-banner-btn.quick{background:#20cfdf1f;border-color:#20cfdf4d;color:var(--accent-cyan)}.changes-banner-btn.quick:hover{background:#20cfdf38;border-color:#20cfdf80}.changes-banner-btn.full{background:#8d52e01f;border-color:#8d52e04d;color:#aa7de8}.changes-banner-btn.full:hover{background:#8d52e038;border-color:#8d52e080}.changes-banner-dismiss{margin-left:auto;background:none;border:none;color:var(--text-muted);font-size:16px;cursor:pointer;padding:0 4px;line-height:1;transition:color .15s ease}.changes-banner-dismiss:hover{color:var(--text-primary)}.changes-banner-file-count{font-weight:400;color:var(--text-muted);margin-left:4px;font-size:11px}.changes-banner-components{display:flex;flex-wrap:wrap;gap:4px}.changes-component-chip{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;background:#f59f0a1a;border:1px solid hsl(38 92% 50% / .25);border-radius:4px;font-family:JetBrains Mono,monospace;font-size:10px;font-weight:500;color:var(--accent-amber);cursor:pointer;transition:background .15s;white-space:nowrap}.changes-component-chip:hover{background:#f59f0a33}.changes-component-chip.unmapped{color:var(--text-muted);background:var(--bg-tertiary);border-color:var(--border-color);cursor:default}.changes-component-count{font-size:9px;font-weight:700;background:#f59f0a33;padding:0 4px;border-radius:3px;min-width:14px;text-align:center}.changes-event-dot{width:6px;height:6px;border-radius:50%;flex-shrink:0}.changes-event-dot.new{background:var(--accent-green)}.changes-event-dot.mod{background:var(--accent-amber)}.changes-event-dot.del{background:var(--accent-red)}[data-theme=light] .changes-banner{background:#cb850b0f;border-bottom-color:#cb850b33}[data-theme=light] .changes-banner-text{color:var(--accent-amber)}[data-theme=light] .changes-banner-btn.quick{background:#1295a11a;border-color:#1295a140;color:var(--accent-cyan)}[data-theme=light] .changes-banner-btn.full{background:#7333cc1a;border-color:#7333cc40;color:#672eb8}.deep-drill-panel{position:absolute;top:16px;left:16px;background:#090e1aeb;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);padding:0;border-radius:12px;border:1px solid hsl(142 76% 42% / .2);font-size:12px;color:var(--text-primary);z-index:16;width:400px;max-width:calc(100vw - 32px);max-height:80vh;overflow-y:auto;box-shadow:0 4px 24px #0006,0 0 0 1px #1abc5514}.deep-drill-header{display:flex;justify-content:space-between;align-items:flex-start;padding:14px 16px;border-bottom:1px solid hsl(142 76% 42% / .15);background:#1abc550d;border-radius:12px 12px 0 0}.deep-drill-title{font-size:14px;font-weight:600;color:var(--accent-green)}.deep-drill-subtitle{font-size:10px;color:var(--text-muted);font-family:JetBrains Mono,monospace;margin-top:2px}.deep-drill-loading{display:flex;align-items:center;justify-content:center;gap:10px;padding:40px 16px;color:var(--text-secondary);font-size:12px}.deep-drill-body{padding:8px 0}.deep-drill-meta{padding:4px 16px 8px;font-size:10px;color:var(--text-muted)}.dd-section{border-top:1px solid var(--border-light)}.dd-section-header{display:flex;align-items:center;gap:6px;width:100%;padding:10px 16px;background:transparent;border:none;color:var(--text-secondary);font-size:11px;font-weight:600;font-family:JetBrains Mono,monospace;text-transform:uppercase;letter-spacing:.5px;cursor:pointer;transition:color .15s}.dd-section-header:hover{color:var(--text-primary)}.dd-section-chevron{font-size:10px;color:var(--text-muted)}.dd-section-content{padding:0 16px 12px}.dd-metric-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-bottom:8px}.dd-metric-card{background:var(--bg-tertiary);border:1px solid var(--border-light);border-radius:8px;padding:10px;text-align:center}.dd-metric-value{font-size:18px;font-weight:700;color:var(--accent-cyan);line-height:1.2}.dd-metric-label{font-size:9px;color:var(--text-muted);text-transform:uppercase;letter-spacing:.5px;margin-top:4px}.dd-lang-badges{display:flex;flex-wrap:wrap;gap:4px}.dd-lang-badge{display:inline-block;padding:2px 8px;background:#20cfdf1a;border:1px solid hsl(185 75% 50% / .2);border-radius:4px;font-size:10px;color:var(--accent-cyan)}.dd-key-files{display:flex;flex-direction:column;gap:4px;margin-bottom:8px}.dd-key-file-item{display:flex;align-items:center;gap:8px;padding:4px 0}.dd-role-badge{display:inline-block;padding:1px 6px;background:#1abc551a;border:1px solid hsl(142 76% 42% / .2);border-radius:3px;font-size:9px;font-weight:600;color:var(--accent-green);text-transform:uppercase;white-space:nowrap;min-width:60px;text-align:center}.dd-file-path{font-family:JetBrains Mono,monospace;font-size:10px;color:var(--text-secondary);word-break:break-all}.dd-subsection-title{font-size:10px;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:.5px;margin:8px 0 6px}.dd-exports{display:flex;flex-wrap:wrap;gap:4px}.dd-export-name{display:inline-block;padding:2px 8px;background:#f59f0a1a;border:1px solid hsl(38 92% 50% / .2);border-radius:4px;font-size:10px;font-family:JetBrains Mono,monospace;color:var(--accent-amber)}.dd-dep-list{display:flex;flex-wrap:wrap;gap:4px}.dd-dep-badge{display:inline-block;padding:2px 8px;background:var(--bg-tertiary);border:1px solid var(--border-light);border-radius:4px;font-size:10px;color:var(--text-secondary)}.dd-dep-badge.categorized{background:#20cfdf14;border-color:#20cfdf26;color:var(--accent-cyan)}.dd-dep-badge.dev{opacity:.7}.dd-dep-more{font-size:10px;color:var(--text-muted);padding:2px 4px}.dd-pattern-group{margin-bottom:10px}.dd-pattern-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:4px}.dd-pattern-label{font-size:11px;font-weight:500;color:var(--text-primary)}.dd-pattern-count{font-size:10px;color:var(--text-muted);background:var(--bg-tertiary);padding:1px 6px;border-radius:3px}.dd-pattern-matches{display:flex;flex-direction:column;gap:2px}.dd-pattern-match{display:flex;flex-direction:column;padding:4px 8px;background:var(--bg-tertiary);border-radius:4px;border:1px solid var(--border-light)}.dd-match-file{font-size:9px;color:var(--text-muted);font-family:JetBrains Mono,monospace}.dd-match-content{font-size:10px;color:var(--text-secondary);font-family:JetBrains Mono,monospace;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dd-connection-item{display:flex;align-items:center;gap:6px;padding:4px 8px;background:var(--bg-tertiary);border-radius:4px;border:1px solid var(--border-light);margin-bottom:4px}.dd-conn-arrow{font-size:11px;color:var(--text-muted);font-family:JetBrains Mono,monospace}.dd-conn-name{font-size:11px;font-weight:500;color:var(--text-primary)}.dd-conn-type{font-size:9px;color:var(--text-muted);background:var(--bg-hover);padding:1px 5px;border-radius:3px;margin-left:auto}.dd-file-tree{font-family:JetBrains Mono,monospace;font-size:10px;max-height:200px;overflow-y:auto}.dd-tree-entry{padding:1px 0;color:var(--text-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dd-tree-entry.dir{color:var(--accent-cyan)}.dd-tree-icon{font-size:10px;margin-right:2px}[data-theme=light] .deep-drill-panel{background:#f6f7f9f2;border-color:#178c4233}[data-theme=light] .deep-drill-header{background:#178c420d;border-bottom-color:#178c4226}[data-theme=light] .dd-metric-value{color:var(--accent-cyan)}
|