@syke1/mcp-server 1.3.9 → 1.3.11
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/dist/languages/plugin.js +1 -0
- package/dist/web/public/app.js +304 -45
- package/dist/web/public/style.css +304 -28
- package/package.json +1 -1
package/dist/languages/plugin.js
CHANGED
|
@@ -101,6 +101,7 @@ const SKIP_DIRS = new Set([
|
|
|
101
101
|
"node_modules", ".git", "dist", "build", ".dart_tool", ".pub-cache",
|
|
102
102
|
"__pycache__", ".mypy_cache", ".pytest_cache", "venv", ".venv",
|
|
103
103
|
"target", "vendor", ".gradle", "bin", "obj",
|
|
104
|
+
".next", "out", ".nuxt", ".output",
|
|
104
105
|
]);
|
|
105
106
|
function discoverAllFiles(rootDir, extensions, extraSkipDirs) {
|
|
106
107
|
const results = [];
|
package/dist/web/public/app.js
CHANGED
|
@@ -18,6 +18,8 @@ let crawlData = null;
|
|
|
18
18
|
let modifyingNodes = new Set(); // nodes currently being modified by AI
|
|
19
19
|
let heartbeatNodes = new Map(); // nodeId → { riskLevel, startTime, interval }
|
|
20
20
|
let diffScrollAnim = null; // animation for diff scroll
|
|
21
|
+
let knownNodeIds = new Set(); // track existing nodes for star-birth detection
|
|
22
|
+
let birthAnimations = new Map(); // nodeId → { startTime, spawnPos, targetPos }
|
|
21
23
|
|
|
22
24
|
const LAYER_HEX = {
|
|
23
25
|
FE: "#00d4ff", BE: "#c084fc", DB: "#ff6b35",
|
|
@@ -27,12 +29,12 @@ const LAYER_HEX = {
|
|
|
27
29
|
const LAYER_KEYS = ["FE", "BE", "DB", "API", "CONFIG", "UTIL"];
|
|
28
30
|
|
|
29
31
|
const LAYER_CENTERS = {
|
|
30
|
-
FE: { x: -
|
|
31
|
-
BE: { x:
|
|
32
|
-
DB: { x:
|
|
33
|
-
API: { x:
|
|
34
|
-
CONFIG: { x: -
|
|
35
|
-
UTIL: { x:
|
|
32
|
+
FE: { x: -2500, y: 1000, z: -1000 },
|
|
33
|
+
BE: { x: 2500, y: 1000, z: 1000 },
|
|
34
|
+
DB: { x: 0, y: -2200, z: 1800 },
|
|
35
|
+
API: { x: 2200, y: -1200, z: -1800 },
|
|
36
|
+
CONFIG: { x: -2200, y: 2500, z: 1500 },
|
|
37
|
+
UTIL: { x: 0, y: 200, z: 0 },
|
|
36
38
|
};
|
|
37
39
|
|
|
38
40
|
// ═══════════════════════════════════════════
|
|
@@ -54,18 +56,23 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
54
56
|
|
|
55
57
|
// Periodic server health check (catches server down even without SSE)
|
|
56
58
|
let healthCheckTimer = null;
|
|
59
|
+
let healthFailCount = 0;
|
|
60
|
+
const HEALTH_FAIL_THRESHOLD = 3; // Show offline only after 3 consecutive failures
|
|
57
61
|
function startHealthCheck() {
|
|
58
62
|
if (healthCheckTimer) return;
|
|
59
63
|
healthCheckTimer = setInterval(async () => {
|
|
60
64
|
try {
|
|
61
|
-
const res = await fetch("/api/project-info", { signal: AbortSignal.timeout(
|
|
65
|
+
const res = await fetch("/api/project-info", { signal: AbortSignal.timeout(10000) });
|
|
62
66
|
if (res.ok) {
|
|
63
|
-
|
|
67
|
+
healthFailCount = 0; // Reset on success
|
|
64
68
|
}
|
|
65
69
|
} catch (e) {
|
|
66
|
-
|
|
70
|
+
healthFailCount++;
|
|
71
|
+
if (healthFailCount >= HEALTH_FAIL_THRESHOLD) {
|
|
72
|
+
showServerOffline();
|
|
73
|
+
}
|
|
67
74
|
}
|
|
68
|
-
},
|
|
75
|
+
}, 30000); // Check every 30 seconds (not 10)
|
|
69
76
|
}
|
|
70
77
|
|
|
71
78
|
// ═══════════════════════════════════════════
|
|
@@ -103,20 +110,62 @@ async function loadGraph() {
|
|
|
103
110
|
}
|
|
104
111
|
hideWelcomeOverlay();
|
|
105
112
|
|
|
113
|
+
const isReload = Graph !== null;
|
|
114
|
+
|
|
115
|
+
// Spawn point for new nodes (top-right corner of 3D space)
|
|
116
|
+
const SPAWN = { x: 5000, y: 4000, z: -2000 };
|
|
117
|
+
|
|
118
|
+
// Preserve existing node positions on reload
|
|
119
|
+
const currentPositions = {};
|
|
120
|
+
if (isReload) {
|
|
121
|
+
const cur = Graph.graphData();
|
|
122
|
+
cur.nodes.forEach(n => {
|
|
123
|
+
currentPositions[n.id] = { x: n.x, y: n.y, z: n.z };
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
106
127
|
const nodes = raw.nodes.map(n => {
|
|
107
128
|
const layer = n.data.layer || "UTIL";
|
|
108
129
|
const c = LAYER_CENTERS[layer] || LAYER_CENTERS.UTIL;
|
|
130
|
+
const isNew = knownNodeIds.size > 0 && !knownNodeIds.has(n.data.id);
|
|
131
|
+
|
|
132
|
+
// For existing nodes on reload: keep their current simulated position
|
|
133
|
+
const targetPos = currentPositions[n.data.id] || {
|
|
134
|
+
x: c.x + (Math.random() - 0.5) * 600,
|
|
135
|
+
y: c.y + (Math.random() - 0.5) * 600,
|
|
136
|
+
z: c.z + (Math.random() - 0.5) * 600,
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
// New node: spawn from far corner, will animate to target
|
|
140
|
+
if (isNew) {
|
|
141
|
+
console.log("[SYKE] ★ Star birth:", n.data.id);
|
|
142
|
+
birthAnimations.set(n.data.id, {
|
|
143
|
+
startTime: Date.now(),
|
|
144
|
+
spawnPos: { ...SPAWN },
|
|
145
|
+
targetPos: { ...targetPos },
|
|
146
|
+
duration: 2500, // 2.5s flight time
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
109
150
|
return {
|
|
110
151
|
id: n.data.id, label: n.data.label, fullPath: n.data.fullPath,
|
|
111
152
|
riskLevel: n.data.riskLevel, dependentCount: n.data.dependentCount,
|
|
112
153
|
lineCount: n.data.lineCount || 0, importsCount: n.data.importsCount || 0,
|
|
113
154
|
depth: n.data.depth || 0, group: n.data.group,
|
|
114
155
|
layer, action: n.data.action || "X", env: n.data.env || "PROD",
|
|
115
|
-
x:
|
|
116
|
-
y:
|
|
117
|
-
z:
|
|
156
|
+
x: isNew ? SPAWN.x : targetPos.x,
|
|
157
|
+
y: isNew ? SPAWN.y : targetPos.y,
|
|
158
|
+
z: isNew ? SPAWN.z : targetPos.z,
|
|
159
|
+
// Pin new nodes at SPAWN so simulation doesn't skip their animation
|
|
160
|
+
fx: isNew ? SPAWN.x : undefined,
|
|
161
|
+
fy: isNew ? SPAWN.y : undefined,
|
|
162
|
+
fz: isNew ? SPAWN.z : undefined,
|
|
163
|
+
_isNew: isNew,
|
|
118
164
|
};
|
|
119
165
|
});
|
|
166
|
+
|
|
167
|
+
// Update known node IDs
|
|
168
|
+
knownNodeIds = new Set(nodes.map(n => n.id));
|
|
120
169
|
const links = raw.edges.map(e => ({ source: e.data.source, target: e.data.target }));
|
|
121
170
|
graphData = { nodes, links };
|
|
122
171
|
|
|
@@ -128,6 +177,15 @@ async function loadGraph() {
|
|
|
128
177
|
const highRisk = nodes.filter(n => n.riskLevel === "HIGH").length;
|
|
129
178
|
document.getElementById("stat-high").textContent = highRisk;
|
|
130
179
|
|
|
180
|
+
// ── RELOAD: just update data, no graph re-creation ──
|
|
181
|
+
if (isReload) {
|
|
182
|
+
Graph.graphData(graphData);
|
|
183
|
+
buildLegend(layerCounts);
|
|
184
|
+
console.log("[SYKE] Graph updated (reload), birth animations:", birthAnimations.size);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// ── FIRST LOAD: create new Graph instance ──
|
|
131
189
|
const container = document.getElementById("3d-graph");
|
|
132
190
|
|
|
133
191
|
Graph = ForceGraph3D()(container)
|
|
@@ -140,7 +198,7 @@ async function loadGraph() {
|
|
|
140
198
|
.nodeColor(node => getNodeColor(node))
|
|
141
199
|
.nodeVal(node => {
|
|
142
200
|
if (!isNodeVisible(node)) return 0.001;
|
|
143
|
-
const base = Math.max(
|
|
201
|
+
const base = Math.max(8, Math.sqrt(node.lineCount) * 0.8);
|
|
144
202
|
const hb = heartbeatNodes.get(node.id);
|
|
145
203
|
if (hb) {
|
|
146
204
|
const elapsed = Date.now() - hb.startTime;
|
|
@@ -153,6 +211,14 @@ async function loadGraph() {
|
|
|
153
211
|
const pulse = 0.5 + 0.5 * Math.sin(Date.now() / 200);
|
|
154
212
|
return base * (1 + pulse * 0.4);
|
|
155
213
|
}
|
|
214
|
+
// Star birth: start large, shrink to normal
|
|
215
|
+
const birth = birthAnimations.get(node.id);
|
|
216
|
+
if (birth) {
|
|
217
|
+
const t = Math.min(1, (Date.now() - birth.startTime) / birth.duration);
|
|
218
|
+
if (t >= 1) birthAnimations.delete(node.id);
|
|
219
|
+
const scale = 1 + (3 - 1) * (1 - t) * (1 - t); // ease-out: 3x → 1x
|
|
220
|
+
return base * scale;
|
|
221
|
+
}
|
|
156
222
|
return base;
|
|
157
223
|
})
|
|
158
224
|
.nodeOpacity(1.0)
|
|
@@ -215,19 +281,44 @@ async function loadGraph() {
|
|
|
215
281
|
node.fz = undefined;
|
|
216
282
|
})
|
|
217
283
|
|
|
218
|
-
|
|
219
|
-
.
|
|
220
|
-
|
|
221
|
-
|
|
284
|
+
// Star birth: animate pinned position from SPAWN → target on each tick
|
|
285
|
+
.onEngineTick(() => {
|
|
286
|
+
if (birthAnimations.size === 0) return;
|
|
287
|
+
const now = Date.now();
|
|
288
|
+
for (const [nodeId, anim] of birthAnimations) {
|
|
289
|
+
const node = graphData.nodes.find(n => n.id === nodeId);
|
|
290
|
+
if (!node) continue;
|
|
291
|
+
const t = Math.min(1, (now - anim.startTime) / anim.duration);
|
|
292
|
+
if (t < 1) {
|
|
293
|
+
// Ease-out cubic: fast start, gentle landing
|
|
294
|
+
const ease = 1 - Math.pow(1 - t, 3);
|
|
295
|
+
node.fx = anim.spawnPos.x + (anim.targetPos.x - anim.spawnPos.x) * ease;
|
|
296
|
+
node.fy = anim.spawnPos.y + (anim.targetPos.y - anim.spawnPos.y) * ease;
|
|
297
|
+
node.fz = anim.spawnPos.z + (anim.targetPos.z - anim.spawnPos.z) * ease;
|
|
298
|
+
} else {
|
|
299
|
+
// Animation done: unpin, let simulation fine-tune position
|
|
300
|
+
node.fx = undefined;
|
|
301
|
+
node.fy = undefined;
|
|
302
|
+
node.fz = undefined;
|
|
303
|
+
birthAnimations.delete(nodeId);
|
|
304
|
+
console.log("[SYKE] ★ Star birth complete:", nodeId);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
})
|
|
308
|
+
|
|
309
|
+
.d3AlphaDecay(0.008)
|
|
310
|
+
.d3VelocityDecay(0.3)
|
|
311
|
+
.warmupTicks(300)
|
|
312
|
+
.cooldownTicks(800)
|
|
222
313
|
.enablePointerInteraction(true);
|
|
223
314
|
|
|
224
|
-
Graph.d3Force("cluster", clusterForce(0.
|
|
225
|
-
Graph.d3Force("charge").strength(-
|
|
315
|
+
Graph.d3Force("cluster", clusterForce(0.015));
|
|
316
|
+
Graph.d3Force("charge").strength(-800);
|
|
226
317
|
Graph.d3Force("link")
|
|
227
|
-
.distance(l => srcLayer(l) === tgtLayer(l) ?
|
|
228
|
-
.strength(l => srcLayer(l) === tgtLayer(l) ? 0.
|
|
318
|
+
.distance(l => srcLayer(l) === tgtLayer(l) ? 250 : 900)
|
|
319
|
+
.strength(l => srcLayer(l) === tgtLayer(l) ? 0.2 : 0.05);
|
|
229
320
|
|
|
230
|
-
Graph.cameraPosition({ x: 0, y: 0, z:
|
|
321
|
+
Graph.cameraPosition({ x: 0, y: 0, z: 3500 });
|
|
231
322
|
|
|
232
323
|
setTimeout(() => {
|
|
233
324
|
try {
|
|
@@ -257,6 +348,12 @@ async function loadGraph() {
|
|
|
257
348
|
buildLegend(layerCounts);
|
|
258
349
|
createNodeLabels();
|
|
259
350
|
updateLabelsLoop();
|
|
351
|
+
|
|
352
|
+
// ── Auto-start code crawl with highest-hub file on initial load ──
|
|
353
|
+
setTimeout(() => {
|
|
354
|
+
const topNode = nodes.slice().sort((a, b) => b.dependentCount - a.dependentCount)[0];
|
|
355
|
+
if (topNode) startCodeCrawl(topNode.id);
|
|
356
|
+
}, 2000);
|
|
260
357
|
}
|
|
261
358
|
|
|
262
359
|
// ═══════════════════════════════════════════
|
|
@@ -366,6 +463,26 @@ function getNodeColor(node) {
|
|
|
366
463
|
return `rgb(${r},${g},${b})`;
|
|
367
464
|
}
|
|
368
465
|
|
|
466
|
+
// Star birth: bright white → cyan → normal layer color
|
|
467
|
+
const birth = birthAnimations.get(node.id);
|
|
468
|
+
if (birth) {
|
|
469
|
+
const t = Math.min(1, (Date.now() - birth.startTime) / birth.duration);
|
|
470
|
+
const layerHex = LAYER_HEX[node.layer] || "#ff69b4";
|
|
471
|
+
const lr = parseInt(layerHex.slice(1,3),16);
|
|
472
|
+
const lg = parseInt(layerHex.slice(3,5),16);
|
|
473
|
+
const lb = parseInt(layerHex.slice(5,7),16);
|
|
474
|
+
if (t < 0.3) {
|
|
475
|
+
// Phase 1: white → cyan flash
|
|
476
|
+
const p = t / 0.3;
|
|
477
|
+
return `rgb(${Math.round(255*(1-p))},${255},${255})`;
|
|
478
|
+
} else {
|
|
479
|
+
// Phase 2: cyan → normal layer color (smooth blend)
|
|
480
|
+
const p = (t - 0.3) / 0.7;
|
|
481
|
+
const ease = p * p; // ease-in for gentle arrival
|
|
482
|
+
return `rgb(${Math.round(lr*ease)},${Math.round(255+(lg-255)*ease)},${Math.round(255+(lb-255)*ease)})`;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
369
486
|
// AI is modifying this node → bright pulsing white/orange
|
|
370
487
|
if (modifyingNodes.has(node.id)) {
|
|
371
488
|
const t = Date.now() / 200;
|
|
@@ -604,7 +721,7 @@ function handleBackgroundClick() {
|
|
|
604
721
|
document.getElementById("code-content").innerHTML = '<p class="placeholder">Select a node to preview source code</p>';
|
|
605
722
|
document.getElementById("sim-content").innerHTML = '<p class="placeholder">Select a node, then switch to SIM tab</p>';
|
|
606
723
|
document.getElementById("btn-ai-analyze").disabled = true;
|
|
607
|
-
|
|
724
|
+
// Don't stop code crawl — keep it always visible
|
|
608
725
|
refreshGraph();
|
|
609
726
|
}
|
|
610
727
|
|
|
@@ -1578,10 +1695,52 @@ function stopCodeCrawl() {
|
|
|
1578
1695
|
if (crawlUserTimer) { clearTimeout(crawlUserTimer); crawlUserTimer = null; }
|
|
1579
1696
|
const vp = document.getElementById("crawl-viewport");
|
|
1580
1697
|
if (vp) { vp.removeEventListener("wheel", pauseAutoScroll); vp.removeEventListener("mousedown", pauseAutoScroll); vp.removeEventListener("touchstart", pauseAutoScroll); }
|
|
1581
|
-
|
|
1698
|
+
// Code crawl stays always visible — never hide
|
|
1582
1699
|
crawlData = null;
|
|
1583
1700
|
}
|
|
1584
1701
|
|
|
1702
|
+
// ═══════════════════════════════════════════
|
|
1703
|
+
// CODE CRAWL: DRAG SUPPORT
|
|
1704
|
+
// ═══════════════════════════════════════════
|
|
1705
|
+
(function initCrawlDrag() {
|
|
1706
|
+
let isDragging = false, startX, startY, startLeft, startTop;
|
|
1707
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
1708
|
+
const crawlEl = document.getElementById("code-crawl");
|
|
1709
|
+
const header = document.getElementById("crawl-header");
|
|
1710
|
+
if (!crawlEl || !header) return;
|
|
1711
|
+
|
|
1712
|
+
header.addEventListener("mousedown", (e) => {
|
|
1713
|
+
if (e.target.closest("#crawl-viewport")) return;
|
|
1714
|
+
isDragging = true;
|
|
1715
|
+
crawlEl.classList.add("dragging");
|
|
1716
|
+
const rect = crawlEl.getBoundingClientRect();
|
|
1717
|
+
const parent = crawlEl.offsetParent.getBoundingClientRect();
|
|
1718
|
+
startX = e.clientX;
|
|
1719
|
+
startY = e.clientY;
|
|
1720
|
+
startLeft = rect.left - parent.left;
|
|
1721
|
+
startTop = rect.top - parent.top;
|
|
1722
|
+
crawlEl.style.left = startLeft + "px";
|
|
1723
|
+
crawlEl.style.top = startTop + "px";
|
|
1724
|
+
crawlEl.style.bottom = "auto";
|
|
1725
|
+
e.preventDefault();
|
|
1726
|
+
});
|
|
1727
|
+
|
|
1728
|
+
document.addEventListener("mousemove", (e) => {
|
|
1729
|
+
if (!isDragging) return;
|
|
1730
|
+
const dx = e.clientX - startX;
|
|
1731
|
+
const dy = e.clientY - startY;
|
|
1732
|
+
crawlEl.style.left = (startLeft + dx) + "px";
|
|
1733
|
+
crawlEl.style.top = (startTop + dy) + "px";
|
|
1734
|
+
});
|
|
1735
|
+
|
|
1736
|
+
document.addEventListener("mouseup", () => {
|
|
1737
|
+
if (!isDragging) return;
|
|
1738
|
+
isDragging = false;
|
|
1739
|
+
crawlEl.classList.remove("dragging");
|
|
1740
|
+
});
|
|
1741
|
+
});
|
|
1742
|
+
})();
|
|
1743
|
+
|
|
1585
1744
|
// ═══════════════════════════════════════════
|
|
1586
1745
|
// REAL-TIME DIFF VIEWER (SSE-driven)
|
|
1587
1746
|
// ═══════════════════════════════════════════
|
|
@@ -1683,6 +1842,9 @@ function showRealtimeDiff(data) {
|
|
|
1683
1842
|
|
|
1684
1843
|
contentEl.innerHTML = html;
|
|
1685
1844
|
|
|
1845
|
+
// ── Mirror diff to CODE tab (VS Code style) ──
|
|
1846
|
+
renderCodeTabDiff(data, html);
|
|
1847
|
+
|
|
1686
1848
|
// ── Animated diff scroll: sweep through file, pause on changes ──
|
|
1687
1849
|
if (diffScrollAnim) cancelAnimationFrame(diffScrollAnim);
|
|
1688
1850
|
viewport.scrollTop = 0;
|
|
@@ -1768,6 +1930,97 @@ function updateDiffWithAnalysis(analysis) {
|
|
|
1768
1930
|
}
|
|
1769
1931
|
|
|
1770
1932
|
// ═══════════════════════════════════════════
|
|
1933
|
+
// CODE TAB: VS Code-style real-time diff viewer
|
|
1934
|
+
// ═══════════════════════════════════════════
|
|
1935
|
+
let codeTabScrollAnim = null;
|
|
1936
|
+
|
|
1937
|
+
function renderCodeTabDiff(data, diffHtml) {
|
|
1938
|
+
const codeEl = document.getElementById("code-content");
|
|
1939
|
+
if (!codeEl) return;
|
|
1940
|
+
|
|
1941
|
+
// Build VS Code-style header
|
|
1942
|
+
const fileName = data.file.split("/").pop();
|
|
1943
|
+
const ext = fileName.split(".").pop();
|
|
1944
|
+
const langLabel = { ts: "TypeScript", tsx: "TypeScript React", js: "JavaScript", jsx: "JSX",
|
|
1945
|
+
dart: "Dart", py: "Python", go: "Go", rs: "Rust", java: "Java", cpp: "C++", rb: "Ruby",
|
|
1946
|
+
css: "CSS", html: "HTML", json: "JSON", md: "Markdown", yaml: "YAML", yml: "YAML" }[ext] || ext.toUpperCase();
|
|
1947
|
+
const changeIcon = data.type === "added" ? "A" : data.type === "deleted" ? "D" : "M";
|
|
1948
|
+
const changeCls = data.type === "added" ? "vsc-added" : data.type === "deleted" ? "vsc-deleted" : "vsc-modified";
|
|
1949
|
+
|
|
1950
|
+
let html = `<div class="vsc-editor">`;
|
|
1951
|
+
// Tab bar (like VS Code file tab)
|
|
1952
|
+
html += `<div class="vsc-tab-bar">`;
|
|
1953
|
+
html += `<div class="vsc-tab active">`;
|
|
1954
|
+
html += `<span class="vsc-tab-change ${changeCls}">${changeIcon}</span>`;
|
|
1955
|
+
html += `<span class="vsc-tab-name">${fileName}</span>`;
|
|
1956
|
+
html += `<span class="vsc-tab-lang">${langLabel}</span>`;
|
|
1957
|
+
html += `</div>`;
|
|
1958
|
+
html += `</div>`;
|
|
1959
|
+
// Breadcrumb path
|
|
1960
|
+
html += `<div class="vsc-breadcrumb">${data.file}</div>`;
|
|
1961
|
+
// Diff content area (reuse crawl-line classes)
|
|
1962
|
+
html += `<div class="vsc-diff-body">${diffHtml}</div>`;
|
|
1963
|
+
// Status bar
|
|
1964
|
+
const lineCount = data.newContent ? data.newContent.length : 0;
|
|
1965
|
+
const diffCount = data.diffCount || 0;
|
|
1966
|
+
html += `<div class="vsc-status-bar">`;
|
|
1967
|
+
html += `<span class="vsc-status-item">${langLabel}</span>`;
|
|
1968
|
+
html += `<span class="vsc-status-item">Ln ${lineCount}</span>`;
|
|
1969
|
+
html += `<span class="vsc-status-item vsc-status-diff">+${diffCount} changes</span>`;
|
|
1970
|
+
html += `<span class="vsc-status-item">${new Date().toLocaleTimeString()}</span>`;
|
|
1971
|
+
html += `</div>`;
|
|
1972
|
+
html += `</div>`;
|
|
1973
|
+
|
|
1974
|
+
codeEl.innerHTML = html;
|
|
1975
|
+
|
|
1976
|
+
// Auto-switch to CODE tab
|
|
1977
|
+
switchTab("code");
|
|
1978
|
+
|
|
1979
|
+
// Auto-scroll to first change
|
|
1980
|
+
if (codeTabScrollAnim) cancelAnimationFrame(codeTabScrollAnim);
|
|
1981
|
+
setTimeout(() => {
|
|
1982
|
+
const diffBody = codeEl.querySelector(".vsc-diff-body");
|
|
1983
|
+
const firstChange = diffBody?.querySelector(".crawl-line.added, .crawl-line.changed, .crawl-line.removed");
|
|
1984
|
+
if (firstChange && diffBody) {
|
|
1985
|
+
firstChange.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
1986
|
+
firstChange.classList.add("diff-flash-active");
|
|
1987
|
+
setTimeout(() => firstChange.classList.remove("diff-flash-active"), 1200);
|
|
1988
|
+
}
|
|
1989
|
+
}, 300);
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1992
|
+
function updateCodeTabAnalysis(analysis) {
|
|
1993
|
+
const statusBar = document.querySelector(".vsc-status-bar");
|
|
1994
|
+
if (!statusBar) return;
|
|
1995
|
+
|
|
1996
|
+
const riskColors = { CRITICAL: "#f85149", HIGH: "#ff6b6b", MEDIUM: "#e3b341", LOW: "#3fb950", SAFE: "#3fb950" };
|
|
1997
|
+
const color = riskColors[analysis.riskLevel] || "#888";
|
|
1998
|
+
statusBar.style.background = analysis.riskLevel === "CRITICAL" || analysis.riskLevel === "HIGH" ? "#6e1b1b" : "#007acc";
|
|
1999
|
+
|
|
2000
|
+
// Add risk badge to status bar
|
|
2001
|
+
const existing = statusBar.querySelector(".vsc-risk-badge");
|
|
2002
|
+
if (existing) existing.remove();
|
|
2003
|
+
const badge = document.createElement("span");
|
|
2004
|
+
badge.className = "vsc-risk-badge";
|
|
2005
|
+
badge.style.cssText = `background:${color};color:#fff;padding:1px 6px;border-radius:2px;font-weight:bold;font-size:10px;`;
|
|
2006
|
+
badge.textContent = analysis.riskLevel;
|
|
2007
|
+
statusBar.prepend(badge);
|
|
2008
|
+
|
|
2009
|
+
// Add AI summary banner to diff body
|
|
2010
|
+
const diffBody = document.querySelector(".vsc-diff-body");
|
|
2011
|
+
if (diffBody && analysis.summary) {
|
|
2012
|
+
const banner = document.createElement("div");
|
|
2013
|
+
banner.className = "vsc-ai-banner";
|
|
2014
|
+
banner.style.cssText = `padding:8px 16px;background:rgba(0,122,204,0.15);border-left:3px solid ${color};color:${color};font-size:11px;margin:0;`;
|
|
2015
|
+
let text = `AI: ${analysis.summary}`;
|
|
2016
|
+
if (analysis.suggestion) text += ` — ${analysis.suggestion}`;
|
|
2017
|
+
banner.textContent = text;
|
|
2018
|
+
const firstBanner = diffBody.querySelector(".diff-banner");
|
|
2019
|
+
if (firstBanner) firstBanner.after(banner);
|
|
2020
|
+
else diffBody.prepend(banner);
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
|
|
1771
2024
|
// ═══════════════════════════════════════════
|
|
1772
2025
|
// AUDIT RESULT (shown after analysis completes)
|
|
1773
2026
|
// ═══════════════════════════════════════════
|
|
@@ -1798,14 +2051,7 @@ function showAuditResult(analysis) {
|
|
|
1798
2051
|
<div class="audit-meta">${analysis.affectedNodes?.length || 0} files checked · ${analysis.analysisMs}ms</div>
|
|
1799
2052
|
</div>`;
|
|
1800
2053
|
|
|
1801
|
-
//
|
|
1802
|
-
setTimeout(() => {
|
|
1803
|
-
crawlEl.classList.remove("active");
|
|
1804
|
-
contentEl.innerHTML = "";
|
|
1805
|
-
headerName.textContent = "";
|
|
1806
|
-
headerStatus.textContent = "";
|
|
1807
|
-
headerStatus.style.color = "";
|
|
1808
|
-
}, 8000);
|
|
2054
|
+
// Keep showing audit result — don't hide code crawl
|
|
1809
2055
|
} else {
|
|
1810
2056
|
// ── WARNING: issues found — keep showing ──
|
|
1811
2057
|
headerName.textContent = analysis.file;
|
|
@@ -1962,6 +2208,7 @@ async function initSSE() {
|
|
|
1962
2208
|
sseSource.addEventListener("connected", (e) => {
|
|
1963
2209
|
const data = JSON.parse(e.data);
|
|
1964
2210
|
console.log("[SYKE:SSE] Connected, cache:", data.cacheSize, "files");
|
|
2211
|
+
healthFailCount = 0; // Reset health failures on SSE connect
|
|
1965
2212
|
updateSSEStatus("LIVE", "connected");
|
|
1966
2213
|
});
|
|
1967
2214
|
|
|
@@ -2055,6 +2302,8 @@ async function initSSE() {
|
|
|
2055
2302
|
|
|
2056
2303
|
// Update diff view with analysis risk badge
|
|
2057
2304
|
updateDiffWithAnalysis(analysis);
|
|
2305
|
+
// Also update CODE tab with analysis result
|
|
2306
|
+
updateCodeTabAnalysis(analysis);
|
|
2058
2307
|
|
|
2059
2308
|
addRealtimeEvent({
|
|
2060
2309
|
type: "result",
|
|
@@ -2133,24 +2382,34 @@ async function initSSE() {
|
|
|
2133
2382
|
updateSSEStatus("PROJECT LOADED", "connected");
|
|
2134
2383
|
});
|
|
2135
2384
|
|
|
2385
|
+
let sseRetryCount = 0;
|
|
2136
2386
|
sseSource.onerror = async () => {
|
|
2137
|
-
console.warn("[SYKE:SSE] Connection error");
|
|
2138
|
-
updateSSEStatus("OFFLINE", "offline");
|
|
2387
|
+
console.warn("[SYKE:SSE] Connection error, retry #" + (sseRetryCount + 1));
|
|
2139
2388
|
sseSource.close();
|
|
2140
2389
|
sseSource = null;
|
|
2141
|
-
if (sseBlocked) return;
|
|
2390
|
+
if (sseBlocked) return;
|
|
2142
2391
|
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2392
|
+
sseRetryCount++;
|
|
2393
|
+
updateSSEStatus("RECONNECTING...", "warning");
|
|
2394
|
+
|
|
2395
|
+
// Only show offline after 5 consecutive SSE failures
|
|
2396
|
+
if (sseRetryCount >= 5) {
|
|
2397
|
+
try {
|
|
2398
|
+
const probe = await fetch("/api/project-info", { signal: AbortSignal.timeout(8000) });
|
|
2399
|
+
if (!probe.ok) throw new Error("not ok");
|
|
2400
|
+
// Server alive but SSE failing — just keep retrying
|
|
2401
|
+
sseRetryCount = 0;
|
|
2402
|
+
} catch (e) {
|
|
2403
|
+
showServerOffline();
|
|
2404
|
+
}
|
|
2150
2405
|
}
|
|
2151
2406
|
|
|
2407
|
+
// Exponential backoff: 2s, 4s, 8s, 16s, max 30s
|
|
2408
|
+
const delay = Math.min(2000 * Math.pow(2, sseRetryCount - 1), 30000);
|
|
2152
2409
|
if (sseReconnectTimer) clearTimeout(sseReconnectTimer);
|
|
2153
|
-
sseReconnectTimer = setTimeout(
|
|
2410
|
+
sseReconnectTimer = setTimeout(() => {
|
|
2411
|
+
initSSE();
|
|
2412
|
+
}, delay);
|
|
2154
2413
|
};
|
|
2155
2414
|
}
|
|
2156
2415
|
|
|
@@ -2423,7 +2682,7 @@ async function loadProjectInfo() {
|
|
|
2423
2682
|
updateLicenseBadge(info.plan, info.expiresAt);
|
|
2424
2683
|
} catch (e) {
|
|
2425
2684
|
console.warn("[SYKE] Failed to load project info:", e);
|
|
2426
|
-
|
|
2685
|
+
// Don't immediately show offline — let health check handle it
|
|
2427
2686
|
}
|
|
2428
2687
|
}
|
|
2429
2688
|
|
|
@@ -660,9 +660,11 @@ main {
|
|
|
660
660
|
|
|
661
661
|
#code-content {
|
|
662
662
|
flex: 1;
|
|
663
|
-
overflow
|
|
663
|
+
overflow: hidden;
|
|
664
664
|
font-size: 11px;
|
|
665
665
|
line-height: 1.6;
|
|
666
|
+
display: flex;
|
|
667
|
+
flex-direction: column;
|
|
666
668
|
}
|
|
667
669
|
|
|
668
670
|
.code-block {
|
|
@@ -689,6 +691,148 @@ main {
|
|
|
689
691
|
opacity: 0.5;
|
|
690
692
|
}
|
|
691
693
|
|
|
694
|
+
/* ── VS Code-style Code Editor (CODE tab) ── */
|
|
695
|
+
.vsc-editor {
|
|
696
|
+
display: flex;
|
|
697
|
+
flex-direction: column;
|
|
698
|
+
height: 100%;
|
|
699
|
+
background: #1e1e1e;
|
|
700
|
+
border: 1px solid #333;
|
|
701
|
+
border-radius: 4px;
|
|
702
|
+
overflow: hidden;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
.vsc-tab-bar {
|
|
706
|
+
display: flex;
|
|
707
|
+
align-items: center;
|
|
708
|
+
background: #252526;
|
|
709
|
+
border-bottom: 1px solid #333;
|
|
710
|
+
min-height: 36px;
|
|
711
|
+
padding: 0 4px;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
.vsc-tab {
|
|
715
|
+
display: flex;
|
|
716
|
+
align-items: center;
|
|
717
|
+
gap: 6px;
|
|
718
|
+
padding: 6px 12px;
|
|
719
|
+
background: #1e1e1e;
|
|
720
|
+
border-top: 2px solid #007acc;
|
|
721
|
+
border-right: 1px solid #333;
|
|
722
|
+
font-size: 12px;
|
|
723
|
+
color: #ccc;
|
|
724
|
+
white-space: nowrap;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
.vsc-tab-change {
|
|
728
|
+
font-size: 10px;
|
|
729
|
+
font-weight: bold;
|
|
730
|
+
padding: 1px 4px;
|
|
731
|
+
border-radius: 2px;
|
|
732
|
+
}
|
|
733
|
+
.vsc-tab-change.vsc-modified { color: #e2c08d; }
|
|
734
|
+
.vsc-tab-change.vsc-added { color: #73c991; }
|
|
735
|
+
.vsc-tab-change.vsc-deleted { color: #f44747; }
|
|
736
|
+
|
|
737
|
+
.vsc-tab-name { color: #fff; }
|
|
738
|
+
.vsc-tab-lang { color: #666; font-size: 10px; }
|
|
739
|
+
|
|
740
|
+
.vsc-breadcrumb {
|
|
741
|
+
padding: 4px 12px;
|
|
742
|
+
font-size: 11px;
|
|
743
|
+
color: #888;
|
|
744
|
+
background: #1e1e1e;
|
|
745
|
+
border-bottom: 1px solid #2a2a2a;
|
|
746
|
+
white-space: nowrap;
|
|
747
|
+
overflow: hidden;
|
|
748
|
+
text-overflow: ellipsis;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
.vsc-diff-body {
|
|
752
|
+
flex: 1;
|
|
753
|
+
overflow-y: auto;
|
|
754
|
+
overflow-x: auto;
|
|
755
|
+
background: #1e1e1e;
|
|
756
|
+
padding: 4px 0;
|
|
757
|
+
font-family: 'Consolas', 'SF Mono', 'Fira Code', monospace;
|
|
758
|
+
font-size: 12px;
|
|
759
|
+
line-height: 20px;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
/* Scrollbar styling for diff body */
|
|
763
|
+
.vsc-diff-body::-webkit-scrollbar { width: 10px; }
|
|
764
|
+
.vsc-diff-body::-webkit-scrollbar-track { background: #1e1e1e; }
|
|
765
|
+
.vsc-diff-body::-webkit-scrollbar-thumb { background: #424242; border-radius: 4px; }
|
|
766
|
+
.vsc-diff-body::-webkit-scrollbar-thumb:hover { background: #555; }
|
|
767
|
+
|
|
768
|
+
/* Override crawl-line in diff body for VS Code look */
|
|
769
|
+
.vsc-diff-body .crawl-line {
|
|
770
|
+
padding: 0 16px 0 0;
|
|
771
|
+
min-height: 20px;
|
|
772
|
+
display: flex;
|
|
773
|
+
align-items: center;
|
|
774
|
+
border: none;
|
|
775
|
+
margin: 0;
|
|
776
|
+
font-size: 12px;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
.vsc-diff-body .crawl-line .cl-num {
|
|
780
|
+
min-width: 48px;
|
|
781
|
+
padding-right: 12px;
|
|
782
|
+
text-align: right;
|
|
783
|
+
color: #5a5a5a;
|
|
784
|
+
user-select: none;
|
|
785
|
+
font-size: 12px;
|
|
786
|
+
background: #1e1e1e;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
.vsc-diff-body .crawl-line.added {
|
|
790
|
+
background: rgba(35, 134, 54, 0.2);
|
|
791
|
+
border-left: 3px solid #28a745;
|
|
792
|
+
}
|
|
793
|
+
.vsc-diff-body .crawl-line.added .cl-num { background: rgba(35, 134, 54, 0.15); color: #73c991; }
|
|
794
|
+
|
|
795
|
+
.vsc-diff-body .crawl-line.removed {
|
|
796
|
+
background: rgba(248, 81, 73, 0.15);
|
|
797
|
+
border-left: 3px solid #f85149;
|
|
798
|
+
}
|
|
799
|
+
.vsc-diff-body .crawl-line.removed .cl-num { background: rgba(248, 81, 73, 0.1); color: #f85149; }
|
|
800
|
+
|
|
801
|
+
.vsc-diff-body .crawl-line.changed {
|
|
802
|
+
background: rgba(227, 179, 65, 0.12);
|
|
803
|
+
border-left: 3px solid #e3b341;
|
|
804
|
+
}
|
|
805
|
+
.vsc-diff-body .crawl-line.changed .cl-num { background: rgba(227, 179, 65, 0.08); color: #e3b341; }
|
|
806
|
+
|
|
807
|
+
.vsc-diff-body .diff-banner {
|
|
808
|
+
padding: 6px 16px;
|
|
809
|
+
font-size: 11px;
|
|
810
|
+
margin: 0;
|
|
811
|
+
border-radius: 0;
|
|
812
|
+
border-left: 3px solid;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
.vsc-diff-body .diff-marker {
|
|
816
|
+
min-width: 16px;
|
|
817
|
+
text-align: center;
|
|
818
|
+
font-weight: bold;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
.vsc-status-bar {
|
|
822
|
+
display: flex;
|
|
823
|
+
align-items: center;
|
|
824
|
+
gap: 16px;
|
|
825
|
+
padding: 2px 12px;
|
|
826
|
+
background: #007acc;
|
|
827
|
+
color: #fff;
|
|
828
|
+
font-size: 11px;
|
|
829
|
+
min-height: 24px;
|
|
830
|
+
flex-shrink: 0;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
.vsc-status-item { white-space: nowrap; }
|
|
834
|
+
.vsc-status-diff { font-weight: bold; }
|
|
835
|
+
|
|
692
836
|
/* ── Simulation Panel ── */
|
|
693
837
|
#sim-panel {
|
|
694
838
|
flex: 1;
|
|
@@ -1192,52 +1336,134 @@ main {
|
|
|
1192
1336
|
STAR WARS CODE CRAWL
|
|
1193
1337
|
═══════════════════════════════════════════ */
|
|
1194
1338
|
|
|
1339
|
+
/* ── FBI/CIA Code Crawl Overlay ── */
|
|
1195
1340
|
#code-crawl {
|
|
1196
1341
|
position: absolute;
|
|
1197
|
-
|
|
1198
|
-
|
|
1342
|
+
left: 20px;
|
|
1343
|
+
bottom: 20px;
|
|
1199
1344
|
width: 500px;
|
|
1200
|
-
|
|
1345
|
+
height: 500px;
|
|
1201
1346
|
pointer-events: auto;
|
|
1202
|
-
z-index:
|
|
1347
|
+
z-index: 10;
|
|
1203
1348
|
overflow: hidden;
|
|
1204
|
-
display:
|
|
1205
|
-
border-radius:
|
|
1349
|
+
display: block;
|
|
1350
|
+
border-radius: 6px;
|
|
1351
|
+
background: rgba(2, 8, 20, 0.92);
|
|
1352
|
+
border: 1px solid rgba(0, 212, 255, 0.25);
|
|
1353
|
+
box-shadow:
|
|
1354
|
+
0 0 30px rgba(0, 212, 255, 0.15),
|
|
1355
|
+
0 0 60px rgba(0, 212, 255, 0.05),
|
|
1356
|
+
inset 0 0 80px rgba(0, 0, 0, 0.5);
|
|
1357
|
+
backdrop-filter: blur(8px);
|
|
1358
|
+
cursor: grab;
|
|
1359
|
+
transition: box-shadow 0.3s ease;
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
#code-crawl:hover {
|
|
1363
|
+
box-shadow:
|
|
1364
|
+
0 0 40px rgba(0, 212, 255, 0.25),
|
|
1365
|
+
0 0 80px rgba(0, 212, 255, 0.08),
|
|
1366
|
+
inset 0 0 80px rgba(0, 0, 0, 0.5);
|
|
1206
1367
|
}
|
|
1207
1368
|
|
|
1208
1369
|
#code-crawl.active {
|
|
1209
|
-
|
|
1370
|
+
animation: crawl-open 0.4s ease-out;
|
|
1210
1371
|
}
|
|
1211
1372
|
|
|
1212
|
-
|
|
1373
|
+
@keyframes crawl-open {
|
|
1374
|
+
0% { opacity: 0; transform: scale(0.85); }
|
|
1375
|
+
100% { opacity: 1; transform: scale(1); }
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
/* Scan line sweep effect */
|
|
1379
|
+
#code-crawl::before {
|
|
1380
|
+
content: '';
|
|
1213
1381
|
position: absolute;
|
|
1214
|
-
top: 0;
|
|
1215
|
-
|
|
1216
|
-
|
|
1382
|
+
top: 0; left: 0; right: 0;
|
|
1383
|
+
height: 2px;
|
|
1384
|
+
background: linear-gradient(90deg, transparent, var(--accent), transparent);
|
|
1385
|
+
z-index: 5;
|
|
1386
|
+
animation: crawl-scanline 3s linear infinite;
|
|
1387
|
+
opacity: 0.6;
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
@keyframes crawl-scanline {
|
|
1391
|
+
0% { top: 0; opacity: 0.8; }
|
|
1392
|
+
100% { top: 100%; opacity: 0; }
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
/* Corner brackets (FBI terminal style) */
|
|
1396
|
+
#code-crawl::after {
|
|
1397
|
+
content: '';
|
|
1398
|
+
position: absolute;
|
|
1399
|
+
inset: 4px;
|
|
1400
|
+
border: 1px solid rgba(0, 212, 255, 0.12);
|
|
1401
|
+
border-radius: 3px;
|
|
1402
|
+
pointer-events: none;
|
|
1403
|
+
z-index: 1;
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
/* Dragging state */
|
|
1407
|
+
#code-crawl.dragging {
|
|
1408
|
+
cursor: grabbing;
|
|
1409
|
+
opacity: 0.9;
|
|
1410
|
+
box-shadow:
|
|
1411
|
+
0 0 50px rgba(0, 212, 255, 0.3),
|
|
1412
|
+
0 0 100px rgba(0, 212, 255, 0.1);
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
#crawl-header {
|
|
1416
|
+
position: relative;
|
|
1417
|
+
padding: 8px 12px;
|
|
1217
1418
|
display: flex;
|
|
1218
1419
|
justify-content: space-between;
|
|
1219
1420
|
align-items: center;
|
|
1220
|
-
z-index:
|
|
1221
|
-
background: linear-gradient(180deg, rgba(
|
|
1421
|
+
z-index: 3;
|
|
1422
|
+
background: linear-gradient(180deg, rgba(0, 15, 35, 0.95) 0%, rgba(0, 10, 25, 0.8) 100%);
|
|
1423
|
+
border-bottom: 1px solid rgba(0, 212, 255, 0.15);
|
|
1424
|
+
cursor: grab;
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
#crawl-header::before {
|
|
1428
|
+
content: '◉ LIVE INTERCEPT';
|
|
1429
|
+
font-size: 8px;
|
|
1430
|
+
letter-spacing: 3px;
|
|
1431
|
+
color: var(--risk-high);
|
|
1432
|
+
position: absolute;
|
|
1433
|
+
top: -1px;
|
|
1434
|
+
left: 12px;
|
|
1435
|
+
animation: intercept-blink 1.5s ease-in-out infinite;
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
@keyframes intercept-blink {
|
|
1439
|
+
0%, 100% { opacity: 1; }
|
|
1440
|
+
50% { opacity: 0.3; }
|
|
1222
1441
|
}
|
|
1223
1442
|
|
|
1224
1443
|
#crawl-file-name {
|
|
1225
|
-
font-size:
|
|
1444
|
+
font-size: 10px;
|
|
1226
1445
|
color: var(--accent);
|
|
1227
|
-
letter-spacing:
|
|
1446
|
+
letter-spacing: 1.5px;
|
|
1228
1447
|
font-weight: 600;
|
|
1229
1448
|
text-transform: uppercase;
|
|
1230
|
-
opacity: 0.
|
|
1449
|
+
opacity: 0.8;
|
|
1450
|
+
margin-top: 10px;
|
|
1451
|
+
white-space: nowrap;
|
|
1452
|
+
overflow: hidden;
|
|
1453
|
+
text-overflow: ellipsis;
|
|
1454
|
+
max-width: 320px;
|
|
1231
1455
|
}
|
|
1232
1456
|
|
|
1233
1457
|
#crawl-status {
|
|
1234
1458
|
font-size: 9px;
|
|
1235
1459
|
letter-spacing: 2px;
|
|
1236
1460
|
color: var(--risk-low);
|
|
1461
|
+
margin-top: 10px;
|
|
1237
1462
|
}
|
|
1238
1463
|
|
|
1239
1464
|
#crawl-status.modifying {
|
|
1240
1465
|
color: var(--risk-high);
|
|
1466
|
+
text-shadow: 0 0 8px rgba(255, 45, 85, 0.6);
|
|
1241
1467
|
animation: status-blink 0.6s ease-in-out infinite;
|
|
1242
1468
|
}
|
|
1243
1469
|
|
|
@@ -1248,24 +1474,80 @@ main {
|
|
|
1248
1474
|
|
|
1249
1475
|
#crawl-viewport {
|
|
1250
1476
|
position: absolute;
|
|
1251
|
-
top:
|
|
1477
|
+
top: 42px; left: 0; right: 0; bottom: 0;
|
|
1252
1478
|
overflow-y: auto;
|
|
1253
1479
|
overflow-x: hidden;
|
|
1254
1480
|
}
|
|
1255
1481
|
|
|
1482
|
+
/* Scrollbar */
|
|
1483
|
+
#crawl-viewport::-webkit-scrollbar { width: 6px; }
|
|
1484
|
+
#crawl-viewport::-webkit-scrollbar-track { background: transparent; }
|
|
1485
|
+
#crawl-viewport::-webkit-scrollbar-thumb { background: rgba(0, 212, 255, 0.2); border-radius: 3px; }
|
|
1486
|
+
|
|
1487
|
+
/* Top/bottom fade for cinema effect */
|
|
1488
|
+
#crawl-viewport::before,
|
|
1489
|
+
#crawl-viewport::after {
|
|
1490
|
+
content: '';
|
|
1491
|
+
position: sticky;
|
|
1492
|
+
display: block;
|
|
1493
|
+
height: 40px;
|
|
1494
|
+
z-index: 2;
|
|
1495
|
+
pointer-events: none;
|
|
1496
|
+
}
|
|
1497
|
+
#crawl-viewport::before {
|
|
1498
|
+
top: 0;
|
|
1499
|
+
background: linear-gradient(180deg, rgba(2, 8, 20, 0.95) 0%, transparent 100%);
|
|
1500
|
+
}
|
|
1501
|
+
#crawl-viewport::after {
|
|
1502
|
+
bottom: 0;
|
|
1503
|
+
background: linear-gradient(0deg, rgba(2, 8, 20, 0.95) 0%, transparent 100%);
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1256
1506
|
#crawl-content {
|
|
1257
|
-
padding:
|
|
1507
|
+
padding: 8px 10px;
|
|
1258
1508
|
}
|
|
1259
1509
|
|
|
1260
|
-
/* Individual crawl line —
|
|
1510
|
+
/* Individual crawl line — FBI terminal style */
|
|
1261
1511
|
.crawl-line {
|
|
1262
1512
|
font-family: 'Consolas', 'SF Mono', 'Fira Code', monospace;
|
|
1263
1513
|
font-size: 11px;
|
|
1264
|
-
line-height: 1.
|
|
1514
|
+
line-height: 1.6;
|
|
1265
1515
|
white-space: pre;
|
|
1266
|
-
color:
|
|
1516
|
+
color: rgba(200, 214, 229, 0.5);
|
|
1267
1517
|
overflow: hidden;
|
|
1268
1518
|
text-overflow: ellipsis;
|
|
1519
|
+
padding: 0 4px;
|
|
1520
|
+
transition: color 0.3s, background 0.3s;
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
/* Non-changed lines are dim / ghostly */
|
|
1524
|
+
.crawl-line:not(.added):not(.removed):not(.changed) {
|
|
1525
|
+
color: rgba(200, 214, 229, 0.25);
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
/* Changed lines glow bright */
|
|
1529
|
+
.crawl-line.added, .crawl-line.removed, .crawl-line.changed {
|
|
1530
|
+
color: #d4d4d4;
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
/* Staggered fade-in for each line */
|
|
1534
|
+
#code-crawl.active .crawl-line {
|
|
1535
|
+
animation: line-materialize 0.5s ease-out both;
|
|
1536
|
+
}
|
|
1537
|
+
#code-crawl.active .crawl-line:nth-child(1) { animation-delay: 0.02s; }
|
|
1538
|
+
#code-crawl.active .crawl-line:nth-child(2) { animation-delay: 0.04s; }
|
|
1539
|
+
#code-crawl.active .crawl-line:nth-child(3) { animation-delay: 0.06s; }
|
|
1540
|
+
#code-crawl.active .crawl-line:nth-child(4) { animation-delay: 0.08s; }
|
|
1541
|
+
#code-crawl.active .crawl-line:nth-child(5) { animation-delay: 0.10s; }
|
|
1542
|
+
#code-crawl.active .crawl-line:nth-child(6) { animation-delay: 0.12s; }
|
|
1543
|
+
#code-crawl.active .crawl-line:nth-child(7) { animation-delay: 0.14s; }
|
|
1544
|
+
#code-crawl.active .crawl-line:nth-child(8) { animation-delay: 0.16s; }
|
|
1545
|
+
#code-crawl.active .crawl-line:nth-child(9) { animation-delay: 0.18s; }
|
|
1546
|
+
#code-crawl.active .crawl-line:nth-child(10) { animation-delay: 0.20s; }
|
|
1547
|
+
|
|
1548
|
+
@keyframes line-materialize {
|
|
1549
|
+
0% { opacity: 0; transform: translateX(8px); filter: blur(2px); }
|
|
1550
|
+
100% { opacity: 1; transform: translateX(0); filter: blur(0); }
|
|
1269
1551
|
}
|
|
1270
1552
|
|
|
1271
1553
|
.crawl-line .cl-num {
|
|
@@ -1577,12 +1859,6 @@ main {
|
|
|
1577
1859
|
100% { background: transparent; }
|
|
1578
1860
|
}
|
|
1579
1861
|
|
|
1580
|
-
/* Crawl status when modifying */
|
|
1581
|
-
#crawl-status.modifying {
|
|
1582
|
-
color: var(--risk-medium);
|
|
1583
|
-
animation: sse-pulse 0.5s infinite;
|
|
1584
|
-
}
|
|
1585
|
-
|
|
1586
1862
|
/* ═══════════════════════════════════════════ */
|
|
1587
1863
|
/* Audit Result (post-analysis) */
|
|
1588
1864
|
/* ═══════════════════════════════════════════ */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@syke1/mcp-server",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.11",
|
|
4
4
|
"mcpName": "io.github.khalomsky/syke",
|
|
5
5
|
"description": "AI code impact analysis MCP server — dependency graphs, cascade detection, and a mandatory build gate for AI coding agents",
|
|
6
6
|
"main": "dist/index.js",
|