@syke1/mcp-server 1.3.19 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/web/public/app.js +513 -113
- package/dist/web/public/index.html +101 -4
- package/dist/web/public/style.css +248 -37
- package/package.json +1 -1
package/dist/web/public/app.js
CHANGED
|
@@ -20,12 +20,378 @@ let heartbeatNodes = new Map(); // nodeId → { riskLevel, startTime, interval }
|
|
|
20
20
|
let diffScrollAnim = null; // animation for diff scroll
|
|
21
21
|
let knownNodeIds = new Set(); // track existing nodes for star-birth detection
|
|
22
22
|
let birthAnimations = new Map(); // nodeId → { startTime, spawnPos, targetPos }
|
|
23
|
+
let searchActive = false; // true when search input has text
|
|
24
|
+
let _searchRAF = null; // RAF loop for search glow animation
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
let LAYER_HEX = {
|
|
25
27
|
FE: "#00d4ff", BE: "#c084fc", DB: "#ff6b35",
|
|
26
28
|
API: "#00ffaa", CONFIG: "#ffd700", UTIL: "#ff69b4",
|
|
27
29
|
};
|
|
28
30
|
|
|
31
|
+
// ═══════════════════════════════════════════
|
|
32
|
+
// SETTINGS SYSTEM
|
|
33
|
+
// ═══════════════════════════════════════════
|
|
34
|
+
const SETTINGS_DEFAULTS = {
|
|
35
|
+
nodes: {
|
|
36
|
+
sizeMin: 30,
|
|
37
|
+
sizeMultiplier: 3,
|
|
38
|
+
opacity: 1.0,
|
|
39
|
+
resolution: 16,
|
|
40
|
+
selectedColor: "#ffffff",
|
|
41
|
+
},
|
|
42
|
+
links: {
|
|
43
|
+
normalWidth: 1.5,
|
|
44
|
+
highlightWidth: 4.0,
|
|
45
|
+
opacity: 0.9,
|
|
46
|
+
curvatureBase: 0.12,
|
|
47
|
+
normalAlpha: 0.25,
|
|
48
|
+
highlightColor: "#ff2d55",
|
|
49
|
+
},
|
|
50
|
+
particles: {
|
|
51
|
+
normalCount: 6,
|
|
52
|
+
highlightCount: 14,
|
|
53
|
+
normalWidth: 2.0,
|
|
54
|
+
highlightWidth: 4.5,
|
|
55
|
+
normalSpeed: 0.006,
|
|
56
|
+
highlightSpeed: 0.004,
|
|
57
|
+
highlightColor: "#ffffff",
|
|
58
|
+
},
|
|
59
|
+
colors: {
|
|
60
|
+
FE: "#00d4ff",
|
|
61
|
+
BE: "#c084fc",
|
|
62
|
+
DB: "#ff6b35",
|
|
63
|
+
API: "#00ffaa",
|
|
64
|
+
CONFIG: "#ffd700",
|
|
65
|
+
UTIL: "#ff69b4",
|
|
66
|
+
},
|
|
67
|
+
arrows: {
|
|
68
|
+
length: 4,
|
|
69
|
+
position: 1,
|
|
70
|
+
},
|
|
71
|
+
scene: {
|
|
72
|
+
background: "#050a18",
|
|
73
|
+
ambientIntensity: 8,
|
|
74
|
+
pointIntensity: 3,
|
|
75
|
+
pointDistance: 5000,
|
|
76
|
+
fogDensity: 0.00012,
|
|
77
|
+
scanlineOpacity: 1.0,
|
|
78
|
+
},
|
|
79
|
+
camera: {
|
|
80
|
+
initialZ: 3500,
|
|
81
|
+
autoRotateSpeed: 0.0005,
|
|
82
|
+
autoRotateRadius: 1600,
|
|
83
|
+
resetDistance: 1600,
|
|
84
|
+
},
|
|
85
|
+
animation: {
|
|
86
|
+
birthDuration: 2500,
|
|
87
|
+
birthScale: 3,
|
|
88
|
+
spawnX: 5000,
|
|
89
|
+
spawnY: 4000,
|
|
90
|
+
spawnZ: -2000,
|
|
91
|
+
},
|
|
92
|
+
physics: {
|
|
93
|
+
alphaDecay: 0.008,
|
|
94
|
+
velocityDecay: 0.3,
|
|
95
|
+
chargeStrength: -800,
|
|
96
|
+
sameLayerDistance: 250,
|
|
97
|
+
crossLayerDistance: 900,
|
|
98
|
+
clusterStrength: 0.015,
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
function deepMerge(defaults, override) {
|
|
103
|
+
const result = {};
|
|
104
|
+
for (const key of Object.keys(defaults)) {
|
|
105
|
+
if (typeof defaults[key] === "object" && defaults[key] !== null && !Array.isArray(defaults[key])) {
|
|
106
|
+
result[key] = deepMerge(defaults[key], override?.[key] || {});
|
|
107
|
+
} else {
|
|
108
|
+
result[key] = override?.[key] !== undefined ? override[key] : defaults[key];
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function loadSettings() {
|
|
115
|
+
try {
|
|
116
|
+
const stored = JSON.parse(localStorage.getItem("syke-dashboard-settings") || "{}");
|
|
117
|
+
return deepMerge(SETTINGS_DEFAULTS, stored);
|
|
118
|
+
} catch (e) {
|
|
119
|
+
console.warn("[SYKE] Failed to load settings, using defaults", e);
|
|
120
|
+
return deepMerge(SETTINGS_DEFAULTS, {});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const SETTINGS = loadSettings();
|
|
125
|
+
|
|
126
|
+
// Apply colors from settings on startup
|
|
127
|
+
Object.assign(LAYER_HEX, SETTINGS.colors);
|
|
128
|
+
|
|
129
|
+
function saveSettings() {
|
|
130
|
+
try {
|
|
131
|
+
localStorage.setItem("syke-dashboard-settings", JSON.stringify(SETTINGS));
|
|
132
|
+
} catch (e) {
|
|
133
|
+
console.warn("[SYKE] Failed to save settings", e);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function resetSettingsGroup(group) {
|
|
138
|
+
if (SETTINGS_DEFAULTS[group]) {
|
|
139
|
+
SETTINGS[group] = deepMerge(SETTINGS_DEFAULTS[group], {});
|
|
140
|
+
saveSettings();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function resetAllSettings() {
|
|
145
|
+
for (const group of Object.keys(SETTINGS_DEFAULTS)) {
|
|
146
|
+
SETTINGS[group] = deepMerge(SETTINGS_DEFAULTS[group], {});
|
|
147
|
+
}
|
|
148
|
+
Object.assign(LAYER_HEX, SETTINGS.colors);
|
|
149
|
+
saveSettings();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function applySettings(group) {
|
|
153
|
+
if (!Graph) return;
|
|
154
|
+
switch (group) {
|
|
155
|
+
case "nodes":
|
|
156
|
+
refreshGraph();
|
|
157
|
+
break;
|
|
158
|
+
case "links":
|
|
159
|
+
Graph.linkCurvature(link => SETTINGS.links.curvatureBase + (hc(getSrcId(link) + getTgtId(link)) % 20) * 0.01);
|
|
160
|
+
Graph.linkOpacity(SETTINGS.links.opacity);
|
|
161
|
+
refreshGraph();
|
|
162
|
+
break;
|
|
163
|
+
case "particles":
|
|
164
|
+
refreshGraph();
|
|
165
|
+
break;
|
|
166
|
+
case "colors":
|
|
167
|
+
Object.assign(LAYER_HEX, SETTINGS.colors);
|
|
168
|
+
if (graphData) {
|
|
169
|
+
const layerCounts = {};
|
|
170
|
+
graphData.nodes.forEach(n => { layerCounts[n.layer] = (layerCounts[n.layer] || 0) + 1; });
|
|
171
|
+
buildLegend(layerCounts);
|
|
172
|
+
}
|
|
173
|
+
refreshGraph();
|
|
174
|
+
break;
|
|
175
|
+
case "arrows":
|
|
176
|
+
Graph.linkDirectionalArrowLength(SETTINGS.arrows.length);
|
|
177
|
+
Graph.linkDirectionalArrowRelPos(SETTINGS.arrows.position);
|
|
178
|
+
break;
|
|
179
|
+
case "scene":
|
|
180
|
+
Graph.backgroundColor(SETTINGS.scene.background);
|
|
181
|
+
try {
|
|
182
|
+
const scene = Graph.scene();
|
|
183
|
+
if (scene) {
|
|
184
|
+
scene.children.forEach(c => {
|
|
185
|
+
if (c.isAmbientLight) c.intensity = SETTINGS.scene.ambientIntensity;
|
|
186
|
+
if (c.isPointLight) { c.intensity = SETTINGS.scene.pointIntensity; c.distance = SETTINGS.scene.pointDistance; }
|
|
187
|
+
});
|
|
188
|
+
if (scene.fog) scene.fog.density = SETTINGS.scene.fogDensity;
|
|
189
|
+
}
|
|
190
|
+
} catch(e) {}
|
|
191
|
+
const scanline = document.getElementById("scanline");
|
|
192
|
+
if (scanline) scanline.style.opacity = SETTINGS.scene.scanlineOpacity;
|
|
193
|
+
break;
|
|
194
|
+
case "physics":
|
|
195
|
+
Graph.d3AlphaDecay(SETTINGS.physics.alphaDecay);
|
|
196
|
+
Graph.d3VelocityDecay(SETTINGS.physics.velocityDecay);
|
|
197
|
+
try {
|
|
198
|
+
Graph.d3Force("charge").strength(SETTINGS.physics.chargeStrength);
|
|
199
|
+
Graph.d3Force("link")
|
|
200
|
+
.distance(l => srcLayer(l) === tgtLayer(l) ? SETTINGS.physics.sameLayerDistance : SETTINGS.physics.crossLayerDistance);
|
|
201
|
+
Graph.d3Force("cluster", clusterForce(SETTINGS.physics.clusterStrength));
|
|
202
|
+
} catch(e) {}
|
|
203
|
+
Graph.d3ReheatSimulation();
|
|
204
|
+
break;
|
|
205
|
+
case "camera":
|
|
206
|
+
// Camera values are read live in autoRotate loop, no action needed
|
|
207
|
+
break;
|
|
208
|
+
case "animation":
|
|
209
|
+
// Animation values are read when new nodes appear, no action needed
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function setupSettings() {
|
|
215
|
+
const CTRL_MAP = [
|
|
216
|
+
// [group, key, selector, type]
|
|
217
|
+
["nodes", "sizeMin", "#set-node-sizeMin", "range"],
|
|
218
|
+
["nodes", "sizeMultiplier", "#set-node-sizeMultiplier", "range"],
|
|
219
|
+
["nodes", "opacity", "#set-node-opacity", "range"],
|
|
220
|
+
["nodes", "resolution", "#set-node-resolution", "range"],
|
|
221
|
+
["nodes", "selectedColor", "#set-node-selectedColor", "color"],
|
|
222
|
+
["links", "normalWidth", "#set-link-normalWidth", "range"],
|
|
223
|
+
["links", "highlightWidth", "#set-link-highlightWidth", "range"],
|
|
224
|
+
["links", "opacity", "#set-link-opacity", "range"],
|
|
225
|
+
["links", "curvatureBase", "#set-link-curvatureBase", "range"],
|
|
226
|
+
["links", "normalAlpha", "#set-link-normalAlpha", "range"],
|
|
227
|
+
["links", "highlightColor", "#set-link-highlightColor", "color"],
|
|
228
|
+
["particles", "normalCount", "#set-part-normalCount", "range"],
|
|
229
|
+
["particles", "highlightCount", "#set-part-highlightCount", "range"],
|
|
230
|
+
["particles", "normalWidth", "#set-part-normalWidth", "range"],
|
|
231
|
+
["particles", "highlightWidth", "#set-part-highlightWidth", "range"],
|
|
232
|
+
["particles", "normalSpeed", "#set-part-normalSpeed", "range"],
|
|
233
|
+
["particles", "highlightSpeed", "#set-part-highlightSpeed", "range"],
|
|
234
|
+
["particles", "highlightColor", "#set-part-highlightColor", "color"],
|
|
235
|
+
["colors", "FE", "#set-color-FE", "color"],
|
|
236
|
+
["colors", "BE", "#set-color-BE", "color"],
|
|
237
|
+
["colors", "DB", "#set-color-DB", "color"],
|
|
238
|
+
["colors", "API", "#set-color-API", "color"],
|
|
239
|
+
["colors", "CONFIG", "#set-color-CONFIG", "color"],
|
|
240
|
+
["colors", "UTIL", "#set-color-UTIL", "color"],
|
|
241
|
+
["arrows", "length", "#set-arrow-length", "range"],
|
|
242
|
+
["arrows", "position", "#set-arrow-position", "range"],
|
|
243
|
+
["scene", "background", "#set-scene-background", "color"],
|
|
244
|
+
["scene", "ambientIntensity", "#set-scene-ambientIntensity", "range"],
|
|
245
|
+
["scene", "pointIntensity", "#set-scene-pointIntensity", "range"],
|
|
246
|
+
["scene", "pointDistance", "#set-scene-pointDistance", "range"],
|
|
247
|
+
["scene", "fogDensity", "#set-scene-fogDensity", "range"],
|
|
248
|
+
["scene", "scanlineOpacity", "#set-scene-scanlineOpacity", "range"],
|
|
249
|
+
["camera", "initialZ", "#set-cam-initialZ", "range"],
|
|
250
|
+
["camera", "autoRotateSpeed", "#set-cam-autoRotateSpeed", "range"],
|
|
251
|
+
["camera", "autoRotateRadius", "#set-cam-autoRotateRadius", "range"],
|
|
252
|
+
["camera", "resetDistance", "#set-cam-resetDistance", "range"],
|
|
253
|
+
["physics", "alphaDecay", "#set-phys-alphaDecay", "range"],
|
|
254
|
+
["physics", "velocityDecay", "#set-phys-velocityDecay", "range"],
|
|
255
|
+
["physics", "chargeStrength", "#set-phys-chargeStrength", "range"],
|
|
256
|
+
["physics", "sameLayerDistance", "#set-phys-sameLayerDistance", "range"],
|
|
257
|
+
["physics", "crossLayerDistance", "#set-phys-crossLayerDistance", "range"],
|
|
258
|
+
["physics", "clusterStrength", "#set-phys-clusterStrength", "range"],
|
|
259
|
+
["animation", "birthDuration", "#set-anim-birthDuration", "range"],
|
|
260
|
+
["animation", "birthScale", "#set-anim-birthScale", "range"],
|
|
261
|
+
["animation", "spawnX", "#set-anim-spawnX", "range"],
|
|
262
|
+
["animation", "spawnY", "#set-anim-spawnY", "range"],
|
|
263
|
+
["animation", "spawnZ", "#set-anim-spawnZ", "range"],
|
|
264
|
+
];
|
|
265
|
+
|
|
266
|
+
// Init values and bind events
|
|
267
|
+
for (const [group, key, selector, type] of CTRL_MAP) {
|
|
268
|
+
const el = document.querySelector(selector);
|
|
269
|
+
if (!el) continue;
|
|
270
|
+
const valEl = el.parentElement?.querySelector(".set-val");
|
|
271
|
+
el.value = SETTINGS[group][key];
|
|
272
|
+
if (valEl) valEl.textContent = formatSetVal(SETTINGS[group][key], type);
|
|
273
|
+
|
|
274
|
+
el.addEventListener("input", () => {
|
|
275
|
+
const v = type === "color" ? el.value : parseFloat(el.value);
|
|
276
|
+
SETTINGS[group][key] = v;
|
|
277
|
+
if (valEl) valEl.textContent = formatSetVal(v, type);
|
|
278
|
+
applySettings(group);
|
|
279
|
+
saveSettings();
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Collapsible sections
|
|
284
|
+
document.querySelectorAll(".set-section-hdr").forEach(hdr => {
|
|
285
|
+
hdr.addEventListener("click", () => {
|
|
286
|
+
const body = hdr.nextElementSibling;
|
|
287
|
+
const arrow = hdr.querySelector(".set-arrow");
|
|
288
|
+
if (body) body.classList.toggle("collapsed");
|
|
289
|
+
if (arrow) arrow.classList.toggle("open");
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
// Reset per group
|
|
294
|
+
document.querySelectorAll(".set-rst-btn").forEach(btn => {
|
|
295
|
+
btn.addEventListener("click", (e) => {
|
|
296
|
+
e.stopPropagation();
|
|
297
|
+
const group = btn.dataset.group;
|
|
298
|
+
resetSettingsGroup(group);
|
|
299
|
+
applySettings(group);
|
|
300
|
+
// Refresh UI inputs
|
|
301
|
+
for (const [g, key, selector, type] of CTRL_MAP) {
|
|
302
|
+
if (g !== group) continue;
|
|
303
|
+
const el = document.querySelector(selector);
|
|
304
|
+
if (!el) continue;
|
|
305
|
+
el.value = SETTINGS[group][key];
|
|
306
|
+
const valEl = el.parentElement?.querySelector(".set-val");
|
|
307
|
+
if (valEl) valEl.textContent = formatSetVal(SETTINGS[group][key], type);
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
// Reset all
|
|
313
|
+
const resetAllBtn = document.getElementById("set-reset-all");
|
|
314
|
+
if (resetAllBtn) {
|
|
315
|
+
resetAllBtn.addEventListener("click", () => {
|
|
316
|
+
resetAllSettings();
|
|
317
|
+
for (const group of Object.keys(SETTINGS_DEFAULTS)) applySettings(group);
|
|
318
|
+
// Refresh all UI inputs
|
|
319
|
+
for (const [group, key, selector, type] of CTRL_MAP) {
|
|
320
|
+
const el = document.querySelector(selector);
|
|
321
|
+
if (!el) continue;
|
|
322
|
+
el.value = SETTINGS[group][key];
|
|
323
|
+
const valEl = el.parentElement?.querySelector(".set-val");
|
|
324
|
+
if (valEl) valEl.textContent = formatSetVal(SETTINGS[group][key], type);
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Export
|
|
330
|
+
const exportBtn = document.getElementById("set-export");
|
|
331
|
+
if (exportBtn) {
|
|
332
|
+
exportBtn.addEventListener("click", () => {
|
|
333
|
+
const blob = new Blob([JSON.stringify(SETTINGS, null, 2)], { type: "application/json" });
|
|
334
|
+
const url = URL.createObjectURL(blob);
|
|
335
|
+
const a = document.createElement("a");
|
|
336
|
+
a.href = url;
|
|
337
|
+
a.download = "syke-settings.json";
|
|
338
|
+
a.click();
|
|
339
|
+
URL.revokeObjectURL(url);
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Import
|
|
344
|
+
const importBtn = document.getElementById("set-import");
|
|
345
|
+
if (importBtn) {
|
|
346
|
+
importBtn.addEventListener("click", () => {
|
|
347
|
+
const input = document.createElement("input");
|
|
348
|
+
input.type = "file";
|
|
349
|
+
input.accept = ".json";
|
|
350
|
+
input.addEventListener("change", () => {
|
|
351
|
+
const file = input.files[0];
|
|
352
|
+
if (!file) return;
|
|
353
|
+
const reader = new FileReader();
|
|
354
|
+
reader.onload = () => {
|
|
355
|
+
try {
|
|
356
|
+
const imported = JSON.parse(reader.result);
|
|
357
|
+
const merged = deepMerge(SETTINGS_DEFAULTS, imported);
|
|
358
|
+
for (const group of Object.keys(merged)) {
|
|
359
|
+
SETTINGS[group] = merged[group];
|
|
360
|
+
}
|
|
361
|
+
Object.assign(LAYER_HEX, SETTINGS.colors);
|
|
362
|
+
saveSettings();
|
|
363
|
+
for (const group of Object.keys(SETTINGS_DEFAULTS)) applySettings(group);
|
|
364
|
+
// Refresh all UI inputs
|
|
365
|
+
for (const [group, key, selector, type] of CTRL_MAP) {
|
|
366
|
+
const el = document.querySelector(selector);
|
|
367
|
+
if (!el) continue;
|
|
368
|
+
el.value = SETTINGS[group][key];
|
|
369
|
+
const valEl = el.parentElement?.querySelector(".set-val");
|
|
370
|
+
if (valEl) valEl.textContent = formatSetVal(SETTINGS[group][key], type);
|
|
371
|
+
}
|
|
372
|
+
} catch(e) {
|
|
373
|
+
console.error("[SYKE] Import failed:", e);
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
reader.readAsText(file);
|
|
377
|
+
});
|
|
378
|
+
input.click();
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Apply scanline opacity on startup
|
|
383
|
+
const scanline = document.getElementById("scanline");
|
|
384
|
+
if (scanline) scanline.style.opacity = SETTINGS.scene.scanlineOpacity;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function formatSetVal(v, type) {
|
|
388
|
+
if (type === "color") return v;
|
|
389
|
+
if (Number.isInteger(v)) return v.toString();
|
|
390
|
+
if (Math.abs(v) < 0.01) return v.toFixed(5);
|
|
391
|
+
if (Math.abs(v) < 1) return v.toFixed(3);
|
|
392
|
+
return v.toFixed(1);
|
|
393
|
+
}
|
|
394
|
+
|
|
29
395
|
const LAYER_KEYS = ["FE", "BE", "DB", "API", "CONFIG", "UTIL"];
|
|
30
396
|
|
|
31
397
|
const LAYER_CENTERS = {
|
|
@@ -49,6 +415,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
49
415
|
setupKeyboardShortcuts();
|
|
50
416
|
setupContextMenu();
|
|
51
417
|
setupTabs();
|
|
418
|
+
setupSettings();
|
|
52
419
|
setupProjectModal();
|
|
53
420
|
initSSE();
|
|
54
421
|
startHealthCheck();
|
|
@@ -113,7 +480,7 @@ async function loadGraph() {
|
|
|
113
480
|
const isReload = Graph !== null;
|
|
114
481
|
|
|
115
482
|
// Spawn point for new nodes (top-right corner of 3D space)
|
|
116
|
-
const SPAWN = { x:
|
|
483
|
+
const SPAWN = { x: SETTINGS.animation.spawnX, y: SETTINGS.animation.spawnY, z: SETTINGS.animation.spawnZ };
|
|
117
484
|
|
|
118
485
|
// Preserve existing node positions on reload
|
|
119
486
|
const currentPositions = {};
|
|
@@ -143,7 +510,7 @@ async function loadGraph() {
|
|
|
143
510
|
startTime: Date.now(),
|
|
144
511
|
spawnPos: { ...SPAWN },
|
|
145
512
|
targetPos: { ...targetPos },
|
|
146
|
-
duration:
|
|
513
|
+
duration: SETTINGS.animation.birthDuration,
|
|
147
514
|
});
|
|
148
515
|
}
|
|
149
516
|
|
|
@@ -209,7 +576,7 @@ async function loadGraph() {
|
|
|
209
576
|
// Phase 3: Zoom out to overview (at 3.5s, 1.5s transition)
|
|
210
577
|
setTimeout(() => {
|
|
211
578
|
Graph.cameraPosition(
|
|
212
|
-
{ x: 0, y: 0, z:
|
|
579
|
+
{ x: 0, y: 0, z: SETTINGS.camera.initialZ },
|
|
213
580
|
{ x: 0, y: 0, z: 0 },
|
|
214
581
|
1500
|
|
215
582
|
);
|
|
@@ -231,13 +598,19 @@ async function loadGraph() {
|
|
|
231
598
|
.width(window.innerWidth - 380)
|
|
232
599
|
.height(window.innerHeight - 100)
|
|
233
600
|
.graphData(graphData)
|
|
234
|
-
.backgroundColor(
|
|
601
|
+
.backgroundColor(SETTINGS.scene.background)
|
|
235
602
|
.showNavInfo(false)
|
|
236
603
|
|
|
237
604
|
.nodeColor(node => getNodeColor(node))
|
|
238
605
|
.nodeVal(node => {
|
|
239
606
|
if (!isNodeVisible(node)) return 0.001;
|
|
240
|
-
const base = Math.max(
|
|
607
|
+
const base = Math.max(SETTINGS.nodes.sizeMin, Math.sqrt(node.lineCount) * SETTINGS.nodes.sizeMultiplier);
|
|
608
|
+
// Search mode: shrink non-matches, boost matches
|
|
609
|
+
if (searchActive) {
|
|
610
|
+
if (!highlightNodes.has(node.id)) return base * 0.3;
|
|
611
|
+
const pulse = 0.9 + 0.1 * Math.sin(Date.now() / 400);
|
|
612
|
+
return base * 1.4 * pulse;
|
|
613
|
+
}
|
|
241
614
|
const hb = heartbeatNodes.get(node.id);
|
|
242
615
|
if (hb) {
|
|
243
616
|
const elapsed = Date.now() - hb.startTime;
|
|
@@ -255,13 +628,13 @@ async function loadGraph() {
|
|
|
255
628
|
if (birth) {
|
|
256
629
|
const t = Math.min(1, (Date.now() - birth.startTime) / birth.duration);
|
|
257
630
|
if (t >= 1) birthAnimations.delete(node.id);
|
|
258
|
-
const scale = 1 + (
|
|
631
|
+
const scale = 1 + (SETTINGS.animation.birthScale - 1) * (1 - t) * (1 - t); // ease-out: Nx → 1x
|
|
259
632
|
return base * scale;
|
|
260
633
|
}
|
|
261
634
|
return base;
|
|
262
635
|
})
|
|
263
|
-
.nodeOpacity(
|
|
264
|
-
.nodeResolution(
|
|
636
|
+
.nodeOpacity(SETTINGS.nodes.opacity)
|
|
637
|
+
.nodeResolution(SETTINGS.nodes.resolution)
|
|
265
638
|
.nodeVisibility(node => isNodeVisible(node))
|
|
266
639
|
.nodeLabel(node => {
|
|
267
640
|
const c = LAYER_HEX[node.layer] || "#ccc";
|
|
@@ -272,34 +645,36 @@ async function loadGraph() {
|
|
|
272
645
|
})
|
|
273
646
|
|
|
274
647
|
.linkColor(link => getLinkColor(link))
|
|
275
|
-
.linkWidth(link => highlightLinks.has(link) ?
|
|
276
|
-
.linkOpacity(
|
|
648
|
+
.linkWidth(link => highlightLinks.has(link) ? SETTINGS.links.highlightWidth : SETTINGS.links.normalWidth)
|
|
649
|
+
.linkOpacity(SETTINGS.links.opacity)
|
|
277
650
|
.linkVisibility(link => isLinkVisible(link))
|
|
278
|
-
.linkCurvature(link =>
|
|
651
|
+
.linkCurvature(link => SETTINGS.links.curvatureBase + (hc(getSrcId(link) + getTgtId(link)) % 20) * 0.01)
|
|
279
652
|
.linkCurveRotation(link => (hc(getTgtId(link) + getSrcId(link)) % 628) / 100)
|
|
280
653
|
|
|
281
654
|
.linkDirectionalParticles(link => {
|
|
282
|
-
if (highlightLinks.has(link)
|
|
655
|
+
if (searchActive) return highlightLinks.has(link) ? SETTINGS.particles.highlightCount : 0;
|
|
656
|
+
if (highlightLinks.has(link)) return SETTINGS.particles.highlightCount;
|
|
283
657
|
const isActive = modifyingNodes.size > 0 || heartbeatNodes.size > 0;
|
|
284
|
-
return isActive ?
|
|
658
|
+
return isActive ? Math.round(SETTINGS.particles.normalCount * 0.67) : SETTINGS.particles.normalCount;
|
|
285
659
|
})
|
|
286
660
|
.linkDirectionalParticleWidth(link => {
|
|
287
|
-
if (highlightLinks.has(link)) return
|
|
661
|
+
if (highlightLinks.has(link)) return SETTINGS.particles.highlightWidth;
|
|
288
662
|
const isActive = modifyingNodes.size > 0 || heartbeatNodes.size > 0;
|
|
289
|
-
return isActive ?
|
|
663
|
+
return isActive ? SETTINGS.particles.normalWidth * 0.75 : SETTINGS.particles.normalWidth;
|
|
290
664
|
})
|
|
291
|
-
.linkDirectionalParticleSpeed(link => highlightLinks.has(link) ?
|
|
665
|
+
.linkDirectionalParticleSpeed(link => highlightLinks.has(link) ? SETTINGS.particles.highlightSpeed : SETTINGS.particles.normalSpeed)
|
|
292
666
|
.linkDirectionalParticleColor(link => {
|
|
293
|
-
if (highlightLinks.has(link)) return "#
|
|
667
|
+
if (searchActive && highlightLinks.has(link)) return LAYER_HEX[srcLayer(link)] || "#00d4ff";
|
|
668
|
+
if (highlightLinks.has(link)) return SETTINGS.particles.highlightColor;
|
|
294
669
|
const isActive = modifyingNodes.size > 0 || heartbeatNodes.size > 0;
|
|
295
670
|
if (isActive) return "rgba(150,180,220,0.6)";
|
|
296
671
|
return LAYER_HEX[srcLayer(link)] || "#ff69b4";
|
|
297
672
|
})
|
|
298
673
|
|
|
299
|
-
.linkDirectionalArrowLength(
|
|
300
|
-
.linkDirectionalArrowRelPos(
|
|
674
|
+
.linkDirectionalArrowLength(SETTINGS.arrows.length)
|
|
675
|
+
.linkDirectionalArrowRelPos(SETTINGS.arrows.position)
|
|
301
676
|
.linkDirectionalArrowColor(link => {
|
|
302
|
-
if (highlightLinks.has(link)) return
|
|
677
|
+
if (highlightLinks.has(link)) return SETTINGS.links.highlightColor;
|
|
303
678
|
return rgba(LAYER_HEX[srcLayer(link)] || "#ff69b4", 0.4);
|
|
304
679
|
})
|
|
305
680
|
|
|
@@ -345,27 +720,28 @@ async function loadGraph() {
|
|
|
345
720
|
}
|
|
346
721
|
})
|
|
347
722
|
|
|
348
|
-
.d3AlphaDecay(
|
|
349
|
-
.d3VelocityDecay(
|
|
723
|
+
.d3AlphaDecay(SETTINGS.physics.alphaDecay)
|
|
724
|
+
.d3VelocityDecay(SETTINGS.physics.velocityDecay)
|
|
350
725
|
.warmupTicks(300)
|
|
351
726
|
.cooldownTicks(800)
|
|
352
727
|
.enablePointerInteraction(true);
|
|
353
728
|
|
|
354
|
-
Graph.d3Force("cluster", clusterForce(
|
|
355
|
-
Graph.d3Force("charge").strength(
|
|
729
|
+
Graph.d3Force("cluster", clusterForce(SETTINGS.physics.clusterStrength));
|
|
730
|
+
Graph.d3Force("charge").strength(SETTINGS.physics.chargeStrength);
|
|
356
731
|
Graph.d3Force("link")
|
|
357
|
-
.distance(l => srcLayer(l) === tgtLayer(l) ?
|
|
732
|
+
.distance(l => srcLayer(l) === tgtLayer(l) ? SETTINGS.physics.sameLayerDistance : SETTINGS.physics.crossLayerDistance)
|
|
358
733
|
.strength(l => srcLayer(l) === tgtLayer(l) ? 0.2 : 0.05);
|
|
359
734
|
|
|
360
|
-
Graph.cameraPosition({ x: 0, y: 0, z:
|
|
735
|
+
Graph.cameraPosition({ x: 0, y: 0, z: SETTINGS.camera.initialZ });
|
|
361
736
|
|
|
362
737
|
setTimeout(() => {
|
|
363
738
|
try {
|
|
364
739
|
const scene = Graph.scene();
|
|
365
740
|
if (!scene) return;
|
|
366
|
-
scene.add(new THREE.AmbientLight(0xffffff,
|
|
367
|
-
|
|
368
|
-
scene.
|
|
741
|
+
scene.add(new THREE.AmbientLight(0xffffff, SETTINGS.scene.ambientIntensity));
|
|
742
|
+
const ptLight = new THREE.PointLight(0xffffff, SETTINGS.scene.pointIntensity, SETTINGS.scene.pointDistance);
|
|
743
|
+
scene.add(ptLight);
|
|
744
|
+
scene.fog = new THREE.FogExp2(parseInt(SETTINGS.scene.background.replace("#",""), 16), SETTINGS.scene.fogDensity);
|
|
369
745
|
console.log("[SYKE] Scene ready");
|
|
370
746
|
} catch(e) { console.warn(e); }
|
|
371
747
|
}, 500);
|
|
@@ -528,13 +904,24 @@ function getNodeColor(node) {
|
|
|
528
904
|
const pulse = 0.5 + 0.5 * Math.sin(t);
|
|
529
905
|
return `rgb(255,${Math.round(180 + pulse * 75)},${Math.round(50 + pulse * 50)})`;
|
|
530
906
|
}
|
|
531
|
-
if (node.id === selectedNodeId) return
|
|
907
|
+
if (node.id === selectedNodeId) return SETTINGS.nodes.selectedColor;
|
|
532
908
|
const base = LAYER_HEX[node.layer] || "#ff69b4";
|
|
533
909
|
if (highlightNodes.size > 0 && !highlightNodes.has(node.id)) {
|
|
534
|
-
|
|
910
|
+
if (searchActive) {
|
|
911
|
+
// Search mode: near-invisible ghost nodes
|
|
912
|
+
return dimHex(base, 0.06);
|
|
913
|
+
}
|
|
535
914
|
const isActive = modifyingNodes.size > 0 || heartbeatNodes.size > 0;
|
|
536
915
|
return dimHex(base, isActive ? 0.12 : 0.35);
|
|
537
916
|
}
|
|
917
|
+
// Search match: pulsing bright glow
|
|
918
|
+
if (searchActive && highlightNodes.has(node.id)) {
|
|
919
|
+
const pulse = 0.85 + 0.15 * Math.sin(Date.now() / 300);
|
|
920
|
+
const r = Math.min(255, Math.round(parseInt(base.slice(1,3),16) * pulse + 40));
|
|
921
|
+
const g = Math.min(255, Math.round(parseInt(base.slice(3,5),16) * pulse + 40));
|
|
922
|
+
const b = Math.min(255, Math.round(parseInt(base.slice(5,7),16) * pulse + 40));
|
|
923
|
+
return `rgb(${r},${g},${b})`;
|
|
924
|
+
}
|
|
538
925
|
return base;
|
|
539
926
|
}
|
|
540
927
|
|
|
@@ -599,12 +986,17 @@ function dimHex(hex, factor) {
|
|
|
599
986
|
}
|
|
600
987
|
|
|
601
988
|
function getLinkColor(link) {
|
|
602
|
-
if (highlightLinks.has(link))
|
|
989
|
+
if (searchActive && highlightLinks.has(link)) {
|
|
990
|
+
// Search mode: matched links glow with source layer color
|
|
991
|
+
return LAYER_HEX[srcLayer(link)] || "#00d4ff";
|
|
992
|
+
}
|
|
993
|
+
if (highlightLinks.has(link)) return SETTINGS.links.highlightColor;
|
|
603
994
|
if (highlightNodes.size > 0 && !highlightLinks.has(link)) {
|
|
995
|
+
if (searchActive) return "rgba(30,40,60,0.02)"; // near-invisible
|
|
604
996
|
const isActive = modifyingNodes.size > 0 || heartbeatNodes.size > 0;
|
|
605
997
|
return isActive ? "rgba(60,70,90,0.03)" : "rgba(100,120,150,0.06)";
|
|
606
998
|
}
|
|
607
|
-
return rgba(LAYER_HEX[srcLayer(link)] || "#ff69b4",
|
|
999
|
+
return rgba(LAYER_HEX[srcLayer(link)] || "#ff69b4", SETTINGS.links.normalAlpha);
|
|
608
1000
|
}
|
|
609
1001
|
|
|
610
1002
|
// ═══════════════════════════════════════════
|
|
@@ -689,11 +1081,11 @@ function buildLegend(counts) {
|
|
|
689
1081
|
let rotAngle = 0, rotRAF = null;
|
|
690
1082
|
function startAutoRotate() {
|
|
691
1083
|
if (!autoRotate || !Graph) return;
|
|
692
|
-
rotAngle +=
|
|
1084
|
+
rotAngle += SETTINGS.camera.autoRotateSpeed;
|
|
693
1085
|
Graph.cameraPosition({
|
|
694
|
-
x:
|
|
1086
|
+
x: SETTINGS.camera.autoRotateRadius * Math.sin(rotAngle),
|
|
695
1087
|
y: 200 + Math.sin(rotAngle * 0.3) * 100,
|
|
696
|
-
z:
|
|
1088
|
+
z: SETTINGS.camera.autoRotateRadius * Math.cos(rotAngle),
|
|
697
1089
|
});
|
|
698
1090
|
rotRAF = requestAnimationFrame(startAutoRotate);
|
|
699
1091
|
}
|
|
@@ -783,7 +1175,12 @@ async function showImpact(fileId, nd) {
|
|
|
783
1175
|
document.getElementById("impact-content").innerHTML = '<div class="loading"><div class="spinner"></div>TRACING...</div>';
|
|
784
1176
|
|
|
785
1177
|
try {
|
|
786
|
-
const res = await fetch("/api/impact/" + fileId);
|
|
1178
|
+
const res = await fetch("/api/impact/" + fileId.split("/").map(encodeURIComponent).join("/"));
|
|
1179
|
+
const ct = res.headers.get("content-type") || "";
|
|
1180
|
+
if (!ct.includes("application/json")) {
|
|
1181
|
+
document.getElementById("impact-content").innerHTML = `<p class="placeholder">ERROR: Server returned non-JSON (${res.status})</p>`;
|
|
1182
|
+
return;
|
|
1183
|
+
}
|
|
787
1184
|
const impact = await res.json();
|
|
788
1185
|
if (!res.ok) { document.getElementById("impact-content").innerHTML = `<p class="placeholder">ERROR: ${impact.error}</p>`; return; }
|
|
789
1186
|
|
|
@@ -876,7 +1273,12 @@ async function loadCodePreview(fileId) {
|
|
|
876
1273
|
const el = document.getElementById("code-content");
|
|
877
1274
|
el.innerHTML = '<div class="loading"><div class="spinner"></div>LOADING...</div>';
|
|
878
1275
|
try {
|
|
879
|
-
const res = await fetch("/api/file-content/" + fileId);
|
|
1276
|
+
const res = await fetch("/api/file-content/" + fileId.split("/").map(encodeURIComponent).join("/"));
|
|
1277
|
+
const ct = res.headers.get("content-type") || "";
|
|
1278
|
+
if (!ct.includes("application/json")) {
|
|
1279
|
+
el.innerHTML = `<p class="placeholder">ERROR: Server returned non-JSON (${res.status}). File: ${fileId}</p>`;
|
|
1280
|
+
return;
|
|
1281
|
+
}
|
|
880
1282
|
const data = await res.json();
|
|
881
1283
|
if (!res.ok) { el.innerHTML = `<p class="placeholder">ERROR: ${data.error}</p>`; return; }
|
|
882
1284
|
|
|
@@ -901,7 +1303,12 @@ async function loadSimulation(fileId) {
|
|
|
901
1303
|
const el = document.getElementById("sim-content");
|
|
902
1304
|
el.innerHTML = '<div class="loading"><div class="spinner"></div>SIMULATING...</div>';
|
|
903
1305
|
try {
|
|
904
|
-
const res = await fetch("/api/simulate-delete/" + fileId);
|
|
1306
|
+
const res = await fetch("/api/simulate-delete/" + fileId.split("/").map(encodeURIComponent).join("/"));
|
|
1307
|
+
const ct = res.headers.get("content-type") || "";
|
|
1308
|
+
if (!ct.includes("application/json")) {
|
|
1309
|
+
el.innerHTML = `<p class="placeholder">ERROR: Server returned non-JSON (${res.status})</p>`;
|
|
1310
|
+
return;
|
|
1311
|
+
}
|
|
905
1312
|
const data = await res.json();
|
|
906
1313
|
if (!res.ok) { el.innerHTML = `<p class="placeholder">ERROR: ${data.error}</p>`; return; }
|
|
907
1314
|
|
|
@@ -1495,7 +1902,7 @@ function resetView() {
|
|
|
1495
1902
|
if (pathMode) exitPathMode();
|
|
1496
1903
|
stopCodeCrawl();
|
|
1497
1904
|
refreshGraph();
|
|
1498
|
-
Graph.cameraPosition({ x:0,y:0,z:
|
|
1905
|
+
Graph.cameraPosition({ x:0,y:0,z:SETTINGS.camera.resetDistance }, { x:0,y:0,z:0 }, 1000);
|
|
1499
1906
|
}
|
|
1500
1907
|
|
|
1501
1908
|
function toggleAutoRotate() {
|
|
@@ -1548,22 +1955,67 @@ function setupEventListeners() {
|
|
|
1548
1955
|
document.getElementById("btn-cancel-path").addEventListener("click", exitPathMode);
|
|
1549
1956
|
|
|
1550
1957
|
document.getElementById("search-input").addEventListener("input", e => {
|
|
1551
|
-
const q = e.target.value.toLowerCase();
|
|
1958
|
+
const q = e.target.value.toLowerCase().trim();
|
|
1552
1959
|
highlightNodes.clear(); highlightLinks.clear(); selectedNodeId = null;
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1960
|
+
|
|
1961
|
+
if (!q) {
|
|
1962
|
+
// ── Clear search: restore full view ──
|
|
1963
|
+
searchActive = false;
|
|
1964
|
+
if (_searchRAF) { cancelAnimationFrame(_searchRAF); _searchRAF = null; }
|
|
1965
|
+
refreshGraph();
|
|
1966
|
+
// Smooth zoom-out to overview
|
|
1967
|
+
if (Graph) {
|
|
1968
|
+
Graph.cameraPosition(
|
|
1969
|
+
{ x: 0, y: 0, z: SETTINGS.camera.resetDistance },
|
|
1970
|
+
{ x: 0, y: 0, z: 0 }, 800
|
|
1971
|
+
);
|
|
1972
|
+
}
|
|
1973
|
+
return;
|
|
1974
|
+
}
|
|
1975
|
+
|
|
1976
|
+
// ── Active search: find matching nodes ──
|
|
1977
|
+
searchActive = true;
|
|
1978
|
+
graphData.nodes.forEach(n => {
|
|
1979
|
+
if (n.fullPath.toLowerCase().includes(q) || n.layer.toLowerCase() === q || n.label.toLowerCase().includes(q)) {
|
|
1980
|
+
highlightNodes.add(n.id);
|
|
1981
|
+
}
|
|
1982
|
+
});
|
|
1983
|
+
|
|
1984
|
+
// Also highlight links between matched nodes
|
|
1985
|
+
if (highlightNodes.size > 0) {
|
|
1986
|
+
graphData.links.forEach(l => {
|
|
1987
|
+
const sid = getSrcId(l), tid = getTgtId(l);
|
|
1988
|
+
if (highlightNodes.has(sid) || highlightNodes.has(tid)) highlightLinks.add(l);
|
|
1556
1989
|
});
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1990
|
+
|
|
1991
|
+
// Camera: frame all matched nodes (centroid)
|
|
1992
|
+
let cx = 0, cy = 0, cz = 0, cnt = 0;
|
|
1993
|
+
graphData.nodes.forEach(n => {
|
|
1994
|
+
if (highlightNodes.has(n.id) && n.x != null) {
|
|
1995
|
+
cx += n.x; cy += (n.y || 0); cz += (n.z || 0); cnt++;
|
|
1563
1996
|
}
|
|
1997
|
+
});
|
|
1998
|
+
if (cnt > 0) {
|
|
1999
|
+
cx /= cnt; cy /= cnt; cz /= cnt;
|
|
2000
|
+
stopUser();
|
|
2001
|
+
const dist = cnt === 1 ? 600 : Math.min(2500, 800 + cnt * 80);
|
|
2002
|
+
Graph.cameraPosition(
|
|
2003
|
+
{ x: cx + dist * 0.3, y: cy + dist * 0.2, z: cz + dist },
|
|
2004
|
+
{ x: cx, y: cy, z: cz }, 600
|
|
2005
|
+
);
|
|
1564
2006
|
}
|
|
1565
2007
|
}
|
|
2008
|
+
|
|
1566
2009
|
refreshGraph();
|
|
2010
|
+
// Start glow animation loop for search matches
|
|
2011
|
+
if (!_searchRAF && highlightNodes.size > 0) {
|
|
2012
|
+
function searchGlow() {
|
|
2013
|
+
if (!searchActive) { _searchRAF = null; return; }
|
|
2014
|
+
if (Graph) Graph.nodeColor(Graph.nodeColor());
|
|
2015
|
+
_searchRAF = requestAnimationFrame(searchGlow);
|
|
2016
|
+
}
|
|
2017
|
+
_searchRAF = requestAnimationFrame(searchGlow);
|
|
2018
|
+
}
|
|
1567
2019
|
});
|
|
1568
2020
|
|
|
1569
2021
|
document.getElementById("btn-toggle-hub").addEventListener("click", () => {
|
|
@@ -2587,82 +3039,30 @@ function setupResizeHandle() {
|
|
|
2587
3039
|
// ═══════════════════════════════════════════
|
|
2588
3040
|
// PROJECT SELECTOR
|
|
2589
3041
|
// ═══════════════════════════════════════════
|
|
2590
|
-
let licenseTimerInterval = null;
|
|
2591
|
-
|
|
2592
3042
|
function updateLicenseBadge(plan, expiresAt) {
|
|
2593
3043
|
const badge = document.getElementById("license-badge");
|
|
2594
3044
|
if (!badge) return;
|
|
2595
3045
|
|
|
2596
|
-
const planEl = badge.querySelector(".license-plan");
|
|
2597
|
-
const timerEl = badge.querySelector(".license-timer");
|
|
2598
|
-
|
|
2599
|
-
// Clear previous timer
|
|
2600
|
-
if (licenseTimerInterval) {
|
|
2601
|
-
clearInterval(licenseTimerInterval);
|
|
2602
|
-
licenseTimerInterval = null;
|
|
2603
|
-
}
|
|
2604
|
-
|
|
2605
3046
|
badge.className = "license-badge";
|
|
2606
3047
|
|
|
2607
3048
|
if (plan === "pro" && expiresAt) {
|
|
2608
|
-
const
|
|
2609
|
-
const now = new Date();
|
|
2610
|
-
const diffMs = expiry - now;
|
|
2611
|
-
|
|
3049
|
+
const diffMs = new Date(expiresAt) - new Date();
|
|
2612
3050
|
if (diffMs <= 0) {
|
|
2613
|
-
// Expired
|
|
2614
3051
|
badge.classList.add("expired");
|
|
2615
|
-
|
|
2616
|
-
|
|
3052
|
+
badge.textContent = "EXPIRED";
|
|
3053
|
+
} else if (Math.ceil(diffMs / (1000 * 60 * 60 * 24)) <= 14) {
|
|
3054
|
+
badge.classList.add("trial");
|
|
3055
|
+
badge.textContent = "TRIAL";
|
|
2617
3056
|
} else {
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
if (totalDays <= 3) {
|
|
2621
|
-
// Trial or near-expiry
|
|
2622
|
-
badge.classList.add("trial-urgent");
|
|
2623
|
-
planEl.textContent = "TRIAL";
|
|
2624
|
-
} else if (totalDays <= 14) {
|
|
2625
|
-
badge.classList.add("trial");
|
|
2626
|
-
planEl.textContent = "TRIAL";
|
|
2627
|
-
} else {
|
|
2628
|
-
badge.classList.add("pro");
|
|
2629
|
-
planEl.textContent = "PRO";
|
|
2630
|
-
}
|
|
2631
|
-
|
|
2632
|
-
// Live countdown
|
|
2633
|
-
function tick() {
|
|
2634
|
-
const remaining = new Date(expiresAt) - new Date();
|
|
2635
|
-
if (remaining <= 0) {
|
|
2636
|
-
timerEl.textContent = "EXPIRED";
|
|
2637
|
-
badge.className = "license-badge expired";
|
|
2638
|
-
planEl.textContent = "EXPIRED";
|
|
2639
|
-
clearInterval(licenseTimerInterval);
|
|
2640
|
-
return;
|
|
2641
|
-
}
|
|
2642
|
-
const d = Math.floor(remaining / (1000 * 60 * 60 * 24));
|
|
2643
|
-
const h = Math.floor((remaining % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
|
|
2644
|
-
const m = Math.floor((remaining % (1000 * 60 * 60)) / (1000 * 60));
|
|
2645
|
-
const s = Math.floor((remaining % (1000 * 60)) / 1000);
|
|
2646
|
-
|
|
2647
|
-
if (d > 0) {
|
|
2648
|
-
timerEl.textContent = `D-${d} ${String(h).padStart(2,"0")}:${String(m).padStart(2,"0")}:${String(s).padStart(2,"0")}`;
|
|
2649
|
-
} else {
|
|
2650
|
-
timerEl.textContent = `${String(h).padStart(2,"0")}:${String(m).padStart(2,"0")}:${String(s).padStart(2,"0")}`;
|
|
2651
|
-
}
|
|
2652
|
-
}
|
|
2653
|
-
tick();
|
|
2654
|
-
licenseTimerInterval = setInterval(tick, 1000);
|
|
3057
|
+
badge.classList.add("pro");
|
|
3058
|
+
badge.textContent = "PRO";
|
|
2655
3059
|
}
|
|
2656
3060
|
} else if (plan === "pro") {
|
|
2657
|
-
// Pro without expiry (permanent or subscription)
|
|
2658
3061
|
badge.classList.add("pro");
|
|
2659
|
-
|
|
2660
|
-
timerEl.textContent = "";
|
|
3062
|
+
badge.textContent = "PRO";
|
|
2661
3063
|
} else {
|
|
2662
|
-
// Free
|
|
2663
3064
|
badge.classList.add("free");
|
|
2664
|
-
|
|
2665
|
-
timerEl.textContent = "";
|
|
3065
|
+
badge.textContent = "FREE";
|
|
2666
3066
|
}
|
|
2667
3067
|
}
|
|
2668
3068
|
|
|
@@ -2731,7 +3131,7 @@ async function updateBottomBar() {
|
|
|
2731
3131
|
const el = document.getElementById("bottom-info");
|
|
2732
3132
|
if (!el) return;
|
|
2733
3133
|
try {
|
|
2734
|
-
const r = await fetch("https://registry.npmjs.org/@syke1/mcp-server");
|
|
3134
|
+
const r = await fetch("https://registry.npmjs.org/@syke1/mcp-server", { cache: "no-store" });
|
|
2735
3135
|
const data = await r.json();
|
|
2736
3136
|
const version = data["dist-tags"]?.latest || "?";
|
|
2737
3137
|
const publishDate = data.time?.[version]?.slice(0, 10) || "?";
|
|
@@ -19,15 +19,12 @@
|
|
|
19
19
|
<span class="logo-text">SYKE</span>
|
|
20
20
|
<span class="logo-sub">CODE IMPACT RADAR</span>
|
|
21
21
|
<span id="sse-status" class="sse-indicator offline">OFFLINE</span>
|
|
22
|
+
<span id="license-badge" class="license-badge free">FREE</span>
|
|
22
23
|
</div>
|
|
23
24
|
<div class="project-selector">
|
|
24
25
|
<span id="current-project" class="project-path">Loading...</span>
|
|
25
26
|
<button id="btn-change-project" class="top-btn" title="Switch project">OPEN</button>
|
|
26
27
|
</div>
|
|
27
|
-
<div id="license-badge" class="license-badge free">
|
|
28
|
-
<span class="license-plan">FREE</span>
|
|
29
|
-
<span class="license-timer"></span>
|
|
30
|
-
</div>
|
|
31
28
|
<div class="top-controls">
|
|
32
29
|
<button id="btn-cycles" class="top-btn" title="Detect circular dependencies">CYCLES</button>
|
|
33
30
|
<button id="btn-stats" class="top-btn" title="Toggle statistics panel">STATS</button>
|
|
@@ -134,6 +131,7 @@
|
|
|
134
131
|
<button class="panel-tab" data-tab="code">CODE</button>
|
|
135
132
|
<button class="panel-tab" data-tab="simulate">SIM</button>
|
|
136
133
|
<button class="panel-tab" data-tab="live">LIVE</button>
|
|
134
|
+
<button class="panel-tab" data-tab="settings">SET</button>
|
|
137
135
|
</div>
|
|
138
136
|
|
|
139
137
|
<!-- Tab: Info (default) -->
|
|
@@ -192,6 +190,105 @@
|
|
|
192
190
|
</div>
|
|
193
191
|
</section>
|
|
194
192
|
</div>
|
|
193
|
+
|
|
194
|
+
<!-- Tab: Settings -->
|
|
195
|
+
<div id="tab-settings" class="tab-content">
|
|
196
|
+
<div class="set-panel">
|
|
197
|
+
<!-- Top action bar -->
|
|
198
|
+
<div class="set-actions">
|
|
199
|
+
<button id="set-reset-all" class="set-action-btn set-danger">RESET ALL</button>
|
|
200
|
+
<button id="set-export" class="set-action-btn">EXPORT</button>
|
|
201
|
+
<button id="set-import" class="set-action-btn">IMPORT</button>
|
|
202
|
+
</div>
|
|
203
|
+
|
|
204
|
+
<div class="set-scroll">
|
|
205
|
+
<!-- NODES -->
|
|
206
|
+
<div class="set-section-hdr"><span class="set-arrow open">▶</span> NODES <button class="set-rst-btn" data-group="nodes">RST</button></div>
|
|
207
|
+
<div class="set-section-body">
|
|
208
|
+
<div class="set-row"><label>Size Min</label><input type="range" id="set-node-sizeMin" min="5" max="100" step="1"><span class="set-val"></span></div>
|
|
209
|
+
<div class="set-row"><label>Size Multiplier</label><input type="range" id="set-node-sizeMultiplier" min="0.5" max="10" step="0.1"><span class="set-val"></span></div>
|
|
210
|
+
<div class="set-row"><label>Opacity</label><input type="range" id="set-node-opacity" min="0.1" max="1" step="0.05"><span class="set-val"></span></div>
|
|
211
|
+
<div class="set-row"><label>Resolution</label><input type="range" id="set-node-resolution" min="4" max="32" step="2"><span class="set-val"></span></div>
|
|
212
|
+
<div class="set-row"><label>Selected Color</label><input type="color" id="set-node-selectedColor"><span class="set-val"></span></div>
|
|
213
|
+
</div>
|
|
214
|
+
|
|
215
|
+
<!-- LINKS -->
|
|
216
|
+
<div class="set-section-hdr"><span class="set-arrow open">▶</span> LINKS <button class="set-rst-btn" data-group="links">RST</button></div>
|
|
217
|
+
<div class="set-section-body">
|
|
218
|
+
<div class="set-row"><label>Normal Width</label><input type="range" id="set-link-normalWidth" min="0.1" max="8" step="0.1"><span class="set-val"></span></div>
|
|
219
|
+
<div class="set-row"><label>Highlight Width</label><input type="range" id="set-link-highlightWidth" min="1" max="12" step="0.5"><span class="set-val"></span></div>
|
|
220
|
+
<div class="set-row"><label>Opacity</label><input type="range" id="set-link-opacity" min="0.1" max="1" step="0.05"><span class="set-val"></span></div>
|
|
221
|
+
<div class="set-row"><label>Curvature</label><input type="range" id="set-link-curvatureBase" min="0" max="0.5" step="0.01"><span class="set-val"></span></div>
|
|
222
|
+
<div class="set-row"><label>Normal Alpha</label><input type="range" id="set-link-normalAlpha" min="0.05" max="1" step="0.05"><span class="set-val"></span></div>
|
|
223
|
+
<div class="set-row"><label>Highlight Color</label><input type="color" id="set-link-highlightColor"><span class="set-val"></span></div>
|
|
224
|
+
</div>
|
|
225
|
+
|
|
226
|
+
<!-- PARTICLES -->
|
|
227
|
+
<div class="set-section-hdr"><span class="set-arrow open">▶</span> PARTICLES <button class="set-rst-btn" data-group="particles">RST</button></div>
|
|
228
|
+
<div class="set-section-body">
|
|
229
|
+
<div class="set-row"><label>Normal Count</label><input type="range" id="set-part-normalCount" min="0" max="20" step="1"><span class="set-val"></span></div>
|
|
230
|
+
<div class="set-row"><label>Highlight Count</label><input type="range" id="set-part-highlightCount" min="0" max="30" step="1"><span class="set-val"></span></div>
|
|
231
|
+
<div class="set-row"><label>Normal Width</label><input type="range" id="set-part-normalWidth" min="0.5" max="6" step="0.1"><span class="set-val"></span></div>
|
|
232
|
+
<div class="set-row"><label>Highlight Width</label><input type="range" id="set-part-highlightWidth" min="1" max="10" step="0.5"><span class="set-val"></span></div>
|
|
233
|
+
<div class="set-row"><label>Normal Speed</label><input type="range" id="set-part-normalSpeed" min="0.001" max="0.02" step="0.001"><span class="set-val"></span></div>
|
|
234
|
+
<div class="set-row"><label>Highlight Speed</label><input type="range" id="set-part-highlightSpeed" min="0.001" max="0.02" step="0.001"><span class="set-val"></span></div>
|
|
235
|
+
<div class="set-row"><label>Highlight Color</label><input type="color" id="set-part-highlightColor"><span class="set-val"></span></div>
|
|
236
|
+
</div>
|
|
237
|
+
|
|
238
|
+
<!-- LAYER COLORS -->
|
|
239
|
+
<div class="set-section-hdr"><span class="set-arrow open">▶</span> LAYER COLORS <button class="set-rst-btn" data-group="colors">RST</button></div>
|
|
240
|
+
<div class="set-section-body">
|
|
241
|
+
<div class="set-row"><label>FE (Frontend)</label><input type="color" id="set-color-FE"><span class="set-val"></span></div>
|
|
242
|
+
<div class="set-row"><label>BE (Backend)</label><input type="color" id="set-color-BE"><span class="set-val"></span></div>
|
|
243
|
+
<div class="set-row"><label>DB (Database)</label><input type="color" id="set-color-DB"><span class="set-val"></span></div>
|
|
244
|
+
<div class="set-row"><label>API</label><input type="color" id="set-color-API"><span class="set-val"></span></div>
|
|
245
|
+
<div class="set-row"><label>CONFIG</label><input type="color" id="set-color-CONFIG"><span class="set-val"></span></div>
|
|
246
|
+
<div class="set-row"><label>UTIL</label><input type="color" id="set-color-UTIL"><span class="set-val"></span></div>
|
|
247
|
+
</div>
|
|
248
|
+
|
|
249
|
+
<!-- SCENE -->
|
|
250
|
+
<div class="set-section-hdr"><span class="set-arrow open">▶</span> SCENE <button class="set-rst-btn" data-group="scene">RST</button></div>
|
|
251
|
+
<div class="set-section-body">
|
|
252
|
+
<div class="set-row"><label>Background</label><input type="color" id="set-scene-background"><span class="set-val"></span></div>
|
|
253
|
+
<div class="set-row"><label>Ambient Light</label><input type="range" id="set-scene-ambientIntensity" min="0" max="20" step="0.5"><span class="set-val"></span></div>
|
|
254
|
+
<div class="set-row"><label>Point Light</label><input type="range" id="set-scene-pointIntensity" min="0" max="10" step="0.5"><span class="set-val"></span></div>
|
|
255
|
+
<div class="set-row"><label>Light Distance</label><input type="range" id="set-scene-pointDistance" min="1000" max="20000" step="500"><span class="set-val"></span></div>
|
|
256
|
+
<div class="set-row"><label>Fog Density</label><input type="range" id="set-scene-fogDensity" min="0" max="0.001" step="0.00001"><span class="set-val"></span></div>
|
|
257
|
+
<div class="set-row"><label>Scanline</label><input type="range" id="set-scene-scanlineOpacity" min="0" max="1" step="0.05"><span class="set-val"></span></div>
|
|
258
|
+
</div>
|
|
259
|
+
|
|
260
|
+
<!-- CAMERA -->
|
|
261
|
+
<div class="set-section-hdr"><span class="set-arrow open">▶</span> CAMERA <button class="set-rst-btn" data-group="camera">RST</button></div>
|
|
262
|
+
<div class="set-section-body">
|
|
263
|
+
<div class="set-row"><label>Initial Distance</label><input type="range" id="set-cam-initialZ" min="500" max="8000" step="100"><span class="set-val"></span></div>
|
|
264
|
+
<div class="set-row"><label>Rotate Speed</label><input type="range" id="set-cam-autoRotateSpeed" min="0.0001" max="0.003" step="0.0001"><span class="set-val"></span></div>
|
|
265
|
+
<div class="set-row"><label>Orbit Radius</label><input type="range" id="set-cam-autoRotateRadius" min="400" max="5000" step="100"><span class="set-val"></span></div>
|
|
266
|
+
<div class="set-row"><label>Reset Distance</label><input type="range" id="set-cam-resetDistance" min="500" max="5000" step="100"><span class="set-val"></span></div>
|
|
267
|
+
</div>
|
|
268
|
+
|
|
269
|
+
<!-- ADVANCED (collapsed by default) -->
|
|
270
|
+
<div class="set-section-hdr"><span class="set-arrow">▶</span> ADVANCED <button class="set-rst-btn" data-group="physics">RST</button></div>
|
|
271
|
+
<div class="set-section-body collapsed">
|
|
272
|
+
<div class="set-sub-title">PHYSICS</div>
|
|
273
|
+
<div class="set-row"><label>Alpha Decay</label><input type="range" id="set-phys-alphaDecay" min="0.001" max="0.05" step="0.001"><span class="set-val"></span></div>
|
|
274
|
+
<div class="set-row"><label>Velocity Decay</label><input type="range" id="set-phys-velocityDecay" min="0.05" max="0.8" step="0.01"><span class="set-val"></span></div>
|
|
275
|
+
<div class="set-row"><label>Charge Strength</label><input type="range" id="set-phys-chargeStrength" min="-3000" max="-100" step="50"><span class="set-val"></span></div>
|
|
276
|
+
<div class="set-row"><label>Same-Layer Dist</label><input type="range" id="set-phys-sameLayerDistance" min="50" max="800" step="10"><span class="set-val"></span></div>
|
|
277
|
+
<div class="set-row"><label>Cross-Layer Dist</label><input type="range" id="set-phys-crossLayerDistance" min="200" max="3000" step="50"><span class="set-val"></span></div>
|
|
278
|
+
<div class="set-row"><label>Cluster Force</label><input type="range" id="set-phys-clusterStrength" min="0" max="0.1" step="0.001"><span class="set-val"></span></div>
|
|
279
|
+
<div class="set-sub-title">ANIMATION</div>
|
|
280
|
+
<div class="set-row"><label>Birth Duration</label><input type="range" id="set-anim-birthDuration" min="500" max="5000" step="100"><span class="set-val"></span></div>
|
|
281
|
+
<div class="set-row"><label>Birth Scale</label><input type="range" id="set-anim-birthScale" min="1" max="8" step="0.5"><span class="set-val"></span></div>
|
|
282
|
+
<div class="set-row"><label>Spawn X</label><input type="range" id="set-anim-spawnX" min="-10000" max="10000" step="500"><span class="set-val"></span></div>
|
|
283
|
+
<div class="set-row"><label>Spawn Y</label><input type="range" id="set-anim-spawnY" min="-10000" max="10000" step="500"><span class="set-val"></span></div>
|
|
284
|
+
<div class="set-row"><label>Spawn Z</label><input type="range" id="set-anim-spawnZ" min="-10000" max="10000" step="500"><span class="set-val"></span></div>
|
|
285
|
+
<div class="set-sub-title">ARROWS</div>
|
|
286
|
+
<div class="set-row"><label>Arrow Length</label><input type="range" id="set-arrow-length" min="0" max="20" step="1"><span class="set-val"></span></div>
|
|
287
|
+
<div class="set-row"><label>Arrow Position</label><input type="range" id="set-arrow-position" min="0" max="1" step="0.05"><span class="set-val"></span></div>
|
|
288
|
+
</div>
|
|
289
|
+
</div>
|
|
290
|
+
</div>
|
|
291
|
+
</div>
|
|
195
292
|
</aside>
|
|
196
293
|
</main>
|
|
197
294
|
|
|
@@ -123,17 +123,15 @@ body {
|
|
|
123
123
|
text-transform: uppercase;
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
/* ── License Badge ── */
|
|
126
|
+
/* ── License Badge (inline, next to SSE indicator) ── */
|
|
127
127
|
.license-badge {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
border
|
|
133
|
-
|
|
128
|
+
font-size: 9px;
|
|
129
|
+
letter-spacing: 1.5px;
|
|
130
|
+
padding: 2px 8px;
|
|
131
|
+
border-radius: 3px;
|
|
132
|
+
border: 1px solid var(--border);
|
|
133
|
+
margin-left: 8px;
|
|
134
134
|
font-weight: 700;
|
|
135
|
-
letter-spacing: 2px;
|
|
136
|
-
border: 1px solid;
|
|
137
135
|
transition: all 0.3s;
|
|
138
136
|
cursor: default;
|
|
139
137
|
}
|
|
@@ -141,50 +139,22 @@ body {
|
|
|
141
139
|
.license-badge.free {
|
|
142
140
|
color: #8899aa;
|
|
143
141
|
border-color: rgba(136,153,170,0.4);
|
|
144
|
-
background: rgba(136,153,170,0.12);
|
|
145
142
|
}
|
|
146
143
|
|
|
147
144
|
.license-badge.pro {
|
|
148
145
|
color: #ffd700;
|
|
149
146
|
border-color: rgba(255,215,0,0.4);
|
|
150
|
-
background: rgba(255,215,0,0.08);
|
|
151
147
|
text-shadow: 0 0 8px rgba(255,215,0,0.3);
|
|
152
148
|
}
|
|
153
149
|
|
|
154
150
|
.license-badge.trial {
|
|
155
151
|
color: var(--risk-medium);
|
|
156
152
|
border-color: rgba(255,159,10,0.4);
|
|
157
|
-
background: rgba(255,159,10,0.08);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
.license-badge.trial-urgent {
|
|
161
|
-
color: var(--risk-high);
|
|
162
|
-
border-color: rgba(255,45,85,0.4);
|
|
163
|
-
background: rgba(255,45,85,0.08);
|
|
164
|
-
animation: license-urgent 1.5s ease-in-out infinite;
|
|
165
153
|
}
|
|
166
154
|
|
|
167
155
|
.license-badge.expired {
|
|
168
156
|
color: var(--risk-high);
|
|
169
157
|
border-color: rgba(255,45,85,0.4);
|
|
170
|
-
background: rgba(255,45,85,0.1);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
@keyframes license-urgent {
|
|
174
|
-
0%, 100% { opacity: 1; }
|
|
175
|
-
50% { opacity: 0.6; }
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
.license-plan {
|
|
179
|
-
font-weight: 700;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
.license-timer {
|
|
183
|
-
font-size: 9px;
|
|
184
|
-
font-weight: 400;
|
|
185
|
-
letter-spacing: 1px;
|
|
186
|
-
opacity: 0.85;
|
|
187
|
-
font-variant-numeric: tabular-nums;
|
|
188
158
|
}
|
|
189
159
|
|
|
190
160
|
.top-controls {
|
|
@@ -2342,3 +2312,244 @@ main {
|
|
|
2342
2312
|
z-index: 50;
|
|
2343
2313
|
backdrop-filter: blur(8px);
|
|
2344
2314
|
}
|
|
2315
|
+
|
|
2316
|
+
/* ═══════════════════════════════════════════ */
|
|
2317
|
+
/* SETTINGS PANEL */
|
|
2318
|
+
/* ═══════════════════════════════════════════ */
|
|
2319
|
+
#tab-settings {
|
|
2320
|
+
overflow: hidden;
|
|
2321
|
+
}
|
|
2322
|
+
|
|
2323
|
+
.set-panel {
|
|
2324
|
+
display: flex;
|
|
2325
|
+
flex-direction: column;
|
|
2326
|
+
height: 100%;
|
|
2327
|
+
overflow: hidden;
|
|
2328
|
+
}
|
|
2329
|
+
|
|
2330
|
+
.set-actions {
|
|
2331
|
+
display: flex;
|
|
2332
|
+
gap: 6px;
|
|
2333
|
+
padding: 10px 14px;
|
|
2334
|
+
border-bottom: 1px solid var(--border);
|
|
2335
|
+
flex-shrink: 0;
|
|
2336
|
+
}
|
|
2337
|
+
|
|
2338
|
+
.set-action-btn {
|
|
2339
|
+
flex: 1;
|
|
2340
|
+
padding: 6px 0;
|
|
2341
|
+
background: transparent;
|
|
2342
|
+
color: var(--text-secondary);
|
|
2343
|
+
border: 1px solid var(--border);
|
|
2344
|
+
border-radius: 2px;
|
|
2345
|
+
cursor: pointer;
|
|
2346
|
+
font-size: 9px;
|
|
2347
|
+
font-family: inherit;
|
|
2348
|
+
font-weight: 600;
|
|
2349
|
+
letter-spacing: 2px;
|
|
2350
|
+
transition: all 0.2s;
|
|
2351
|
+
}
|
|
2352
|
+
|
|
2353
|
+
.set-action-btn:hover {
|
|
2354
|
+
color: var(--accent);
|
|
2355
|
+
border-color: var(--accent);
|
|
2356
|
+
box-shadow: var(--glow-cyan);
|
|
2357
|
+
}
|
|
2358
|
+
|
|
2359
|
+
.set-action-btn.set-danger:hover {
|
|
2360
|
+
color: var(--risk-high);
|
|
2361
|
+
border-color: var(--risk-high);
|
|
2362
|
+
box-shadow: var(--glow-red);
|
|
2363
|
+
}
|
|
2364
|
+
|
|
2365
|
+
.set-scroll {
|
|
2366
|
+
flex: 1;
|
|
2367
|
+
overflow-y: auto;
|
|
2368
|
+
padding: 4px 0;
|
|
2369
|
+
}
|
|
2370
|
+
|
|
2371
|
+
.set-scroll::-webkit-scrollbar { width: 4px; }
|
|
2372
|
+
.set-scroll::-webkit-scrollbar-track { background: transparent; }
|
|
2373
|
+
.set-scroll::-webkit-scrollbar-thumb { background: var(--accent-dim); border-radius: 2px; }
|
|
2374
|
+
|
|
2375
|
+
/* Section header */
|
|
2376
|
+
.set-section-hdr {
|
|
2377
|
+
display: flex;
|
|
2378
|
+
align-items: center;
|
|
2379
|
+
gap: 8px;
|
|
2380
|
+
padding: 8px 14px;
|
|
2381
|
+
cursor: pointer;
|
|
2382
|
+
font-size: 10px;
|
|
2383
|
+
font-weight: 700;
|
|
2384
|
+
letter-spacing: 2px;
|
|
2385
|
+
color: var(--accent);
|
|
2386
|
+
border-bottom: 1px solid rgba(26,45,77,0.3);
|
|
2387
|
+
user-select: none;
|
|
2388
|
+
transition: background 0.2s;
|
|
2389
|
+
}
|
|
2390
|
+
|
|
2391
|
+
.set-section-hdr:hover {
|
|
2392
|
+
background: rgba(0,212,255,0.04);
|
|
2393
|
+
}
|
|
2394
|
+
|
|
2395
|
+
.set-arrow {
|
|
2396
|
+
display: inline-block;
|
|
2397
|
+
font-size: 8px;
|
|
2398
|
+
transition: transform 0.25s ease;
|
|
2399
|
+
color: var(--text-secondary);
|
|
2400
|
+
}
|
|
2401
|
+
|
|
2402
|
+
.set-arrow.open {
|
|
2403
|
+
transform: rotate(90deg);
|
|
2404
|
+
}
|
|
2405
|
+
|
|
2406
|
+
.set-rst-btn {
|
|
2407
|
+
margin-left: auto;
|
|
2408
|
+
padding: 2px 8px;
|
|
2409
|
+
background: transparent;
|
|
2410
|
+
color: var(--text-secondary);
|
|
2411
|
+
border: 1px solid var(--border);
|
|
2412
|
+
border-radius: 2px;
|
|
2413
|
+
cursor: pointer;
|
|
2414
|
+
font-size: 8px;
|
|
2415
|
+
font-family: inherit;
|
|
2416
|
+
font-weight: 600;
|
|
2417
|
+
letter-spacing: 1px;
|
|
2418
|
+
transition: all 0.2s;
|
|
2419
|
+
}
|
|
2420
|
+
|
|
2421
|
+
.set-rst-btn:hover {
|
|
2422
|
+
color: var(--risk-medium);
|
|
2423
|
+
border-color: var(--risk-medium);
|
|
2424
|
+
}
|
|
2425
|
+
|
|
2426
|
+
/* Section body */
|
|
2427
|
+
.set-section-body {
|
|
2428
|
+
padding: 6px 14px;
|
|
2429
|
+
border-bottom: 1px solid rgba(26,45,77,0.2);
|
|
2430
|
+
}
|
|
2431
|
+
|
|
2432
|
+
.set-section-body.collapsed {
|
|
2433
|
+
display: none;
|
|
2434
|
+
}
|
|
2435
|
+
|
|
2436
|
+
.set-sub-title {
|
|
2437
|
+
font-size: 9px;
|
|
2438
|
+
font-weight: 600;
|
|
2439
|
+
letter-spacing: 2px;
|
|
2440
|
+
color: var(--text-secondary);
|
|
2441
|
+
margin: 8px 0 4px;
|
|
2442
|
+
padding-top: 6px;
|
|
2443
|
+
border-top: 1px solid rgba(26,45,77,0.3);
|
|
2444
|
+
}
|
|
2445
|
+
|
|
2446
|
+
.set-sub-title:first-child {
|
|
2447
|
+
margin-top: 0;
|
|
2448
|
+
padding-top: 0;
|
|
2449
|
+
border-top: none;
|
|
2450
|
+
}
|
|
2451
|
+
|
|
2452
|
+
/* Individual control row */
|
|
2453
|
+
.set-row {
|
|
2454
|
+
display: flex;
|
|
2455
|
+
align-items: center;
|
|
2456
|
+
gap: 8px;
|
|
2457
|
+
padding: 3px 0;
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2460
|
+
.set-row label {
|
|
2461
|
+
font-size: 10px;
|
|
2462
|
+
color: var(--text-secondary);
|
|
2463
|
+
letter-spacing: 0.5px;
|
|
2464
|
+
min-width: 100px;
|
|
2465
|
+
flex-shrink: 0;
|
|
2466
|
+
}
|
|
2467
|
+
|
|
2468
|
+
.set-row .set-val {
|
|
2469
|
+
font-size: 9px;
|
|
2470
|
+
color: var(--accent);
|
|
2471
|
+
min-width: 48px;
|
|
2472
|
+
text-align: right;
|
|
2473
|
+
font-variant-numeric: tabular-nums;
|
|
2474
|
+
flex-shrink: 0;
|
|
2475
|
+
}
|
|
2476
|
+
|
|
2477
|
+
/* Range slider styling */
|
|
2478
|
+
.set-row input[type="range"] {
|
|
2479
|
+
-webkit-appearance: none;
|
|
2480
|
+
appearance: none;
|
|
2481
|
+
flex: 1;
|
|
2482
|
+
height: 3px;
|
|
2483
|
+
background: var(--border);
|
|
2484
|
+
border-radius: 2px;
|
|
2485
|
+
outline: none;
|
|
2486
|
+
cursor: pointer;
|
|
2487
|
+
}
|
|
2488
|
+
|
|
2489
|
+
.set-row input[type="range"]::-webkit-slider-thumb {
|
|
2490
|
+
-webkit-appearance: none;
|
|
2491
|
+
appearance: none;
|
|
2492
|
+
width: 12px;
|
|
2493
|
+
height: 12px;
|
|
2494
|
+
border-radius: 50%;
|
|
2495
|
+
background: var(--accent);
|
|
2496
|
+
border: 2px solid var(--bg-primary);
|
|
2497
|
+
box-shadow: 0 0 6px rgba(0,212,255,0.5);
|
|
2498
|
+
cursor: pointer;
|
|
2499
|
+
transition: box-shadow 0.2s, transform 0.15s;
|
|
2500
|
+
}
|
|
2501
|
+
|
|
2502
|
+
.set-row input[type="range"]::-webkit-slider-thumb:hover {
|
|
2503
|
+
box-shadow: 0 0 12px rgba(0,212,255,0.8);
|
|
2504
|
+
transform: scale(1.2);
|
|
2505
|
+
}
|
|
2506
|
+
|
|
2507
|
+
.set-row input[type="range"]:active::-webkit-slider-thumb {
|
|
2508
|
+
box-shadow: 0 0 16px rgba(0,212,255,1);
|
|
2509
|
+
transform: scale(1.3);
|
|
2510
|
+
}
|
|
2511
|
+
|
|
2512
|
+
.set-row input[type="range"]::-moz-range-thumb {
|
|
2513
|
+
width: 12px;
|
|
2514
|
+
height: 12px;
|
|
2515
|
+
border-radius: 50%;
|
|
2516
|
+
background: var(--accent);
|
|
2517
|
+
border: 2px solid var(--bg-primary);
|
|
2518
|
+
box-shadow: 0 0 6px rgba(0,212,255,0.5);
|
|
2519
|
+
cursor: pointer;
|
|
2520
|
+
}
|
|
2521
|
+
|
|
2522
|
+
.set-row input[type="range"]::-moz-range-track {
|
|
2523
|
+
height: 3px;
|
|
2524
|
+
background: var(--border);
|
|
2525
|
+
border-radius: 2px;
|
|
2526
|
+
border: none;
|
|
2527
|
+
}
|
|
2528
|
+
|
|
2529
|
+
/* Color picker styling */
|
|
2530
|
+
.set-row input[type="color"] {
|
|
2531
|
+
-webkit-appearance: none;
|
|
2532
|
+
appearance: none;
|
|
2533
|
+
width: 28px;
|
|
2534
|
+
height: 20px;
|
|
2535
|
+
border: 1px solid var(--border);
|
|
2536
|
+
border-radius: 2px;
|
|
2537
|
+
background: transparent;
|
|
2538
|
+
cursor: pointer;
|
|
2539
|
+
padding: 1px;
|
|
2540
|
+
transition: border-color 0.2s, box-shadow 0.2s;
|
|
2541
|
+
}
|
|
2542
|
+
|
|
2543
|
+
.set-row input[type="color"]:hover {
|
|
2544
|
+
border-color: var(--accent);
|
|
2545
|
+
box-shadow: 0 0 8px rgba(0,212,255,0.4);
|
|
2546
|
+
}
|
|
2547
|
+
|
|
2548
|
+
.set-row input[type="color"]::-webkit-color-swatch-wrapper {
|
|
2549
|
+
padding: 0;
|
|
2550
|
+
}
|
|
2551
|
+
|
|
2552
|
+
.set-row input[type="color"]::-webkit-color-swatch {
|
|
2553
|
+
border: none;
|
|
2554
|
+
border-radius: 1px;
|
|
2555
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@syke1/mcp-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
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",
|