codesynapt 0.0.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/CHANGELOG.md +17 -0
- package/LICENSE +686 -0
- package/LICENSES.md +141 -0
- package/README.md +331 -0
- package/electron/main.cjs +2849 -0
- package/electron/plugin-loader.cjs +184 -0
- package/electron/preload.cjs +108 -0
- package/package.json +216 -0
- package/packages/core/bin/codesynapt-mcp.cjs +611 -0
- package/packages/core/bin/codesynapt.cjs +1933 -0
- package/packages/core/legacy.js +300 -0
- package/packages/core/lib/control-server.cjs +1539 -0
- package/packages/core/lib/embedding.cjs +89 -0
- package/packages/core/lib/logger.cjs +63 -0
- package/packages/core/lib/search-cache.cjs +140 -0
- package/packages/core/lib/search-worker.cjs +255 -0
- package/packages/core/lib/search.cjs +211 -0
- package/packages/core/lib/symbol-graph.cjs +402 -0
- package/packages/core/lib/symbol-parser-js.cjs +542 -0
- package/packages/core/lib/symbol-parser-misc.cjs +394 -0
- package/packages/core/lib/symbol-parser-py.cjs +215 -0
- package/packages/core/lib/symbol-parser-treesitter.cjs +658 -0
- package/packages/core/lib/symbol-parser-tsc.cjs +332 -0
- package/packages/core/monorepo.js +310 -0
- package/packages/core/parser.js +2234 -0
- package/packages/core/scanner.js +623 -0
- package/plugin-api/LICENSE +21 -0
- package/plugin-api/README.md +114 -0
- package/plugin-api/docs/01-getting-started.md +197 -0
- package/plugin-api/docs/02-concepts.md +269 -0
- package/plugin-api/docs/api-reference.md +463 -0
- package/plugin-api/docs/troubleshooting.md +332 -0
- package/plugin-api/docs/types/exporter.md +377 -0
- package/plugin-api/docs/types/theme.md +312 -0
- package/plugin-api/examples/hello-world-plugin/README.md +70 -0
- package/plugin-api/examples/hello-world-plugin/main.js +36 -0
- package/plugin-api/examples/hello-world-plugin/manifest.json +12 -0
- package/plugin-api/examples/mermaid-exporter/README.md +125 -0
- package/plugin-api/examples/mermaid-exporter/main.js +58 -0
- package/plugin-api/examples/mermaid-exporter/manifest.json +12 -0
- package/plugin-api/examples/rust-parser/README.md +71 -0
- package/plugin-api/examples/rust-parser/main.js +123 -0
- package/plugin-api/examples/rust-parser/manifest.json +12 -0
- package/plugin-api/examples/sunset-theme/README.md +95 -0
- package/plugin-api/examples/sunset-theme/manifest.json +12 -0
- package/plugin-api/examples/sunset-theme/theme.css +31 -0
- package/plugin-api/package.json +20 -0
- package/plugin-api/types.d.ts +395 -0
- package/public/app.js +6837 -0
- package/public/backend.js +285 -0
- package/public/index.html +647 -0
- package/public/plugin-host.js +321 -0
- package/public/style.css +4359 -0
- package/public/vendor/three.module.js +53044 -0
- package/scripts/competitor-watch.mjs +144 -0
- package/scripts/copy-vendor.js +21 -0
- package/scripts/download-bundled-node.cjs +53 -0
- package/scripts/fuses-after-pack.cjs +34 -0
- package/scripts/license-check.js +119 -0
- package/scripts/perf-test.js +200 -0
- package/server.js +132 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════
|
|
2
|
+
// backend.js — physics backend dispatcher
|
|
3
|
+
//
|
|
4
|
+
// Three modes:
|
|
5
|
+
// 'gpu' → WebGPU compute (when available, not contended)
|
|
6
|
+
// 'cpu' → CPU stepCPU() in app.js (always available, scales OK)
|
|
7
|
+
// 'auto' → Start GPU if available; if GPU becomes saturated
|
|
8
|
+
// (sustained >60% of frame budget), transparently
|
|
9
|
+
// switch to CPU. If GPU recovers later, switch back.
|
|
10
|
+
//
|
|
11
|
+
// Public surface:
|
|
12
|
+
// init({ onStatusChange }) — async, sets up GPU if possible
|
|
13
|
+
// setMode('gpu' | 'cpu' | 'auto') — user choice from settings UI
|
|
14
|
+
// getStatus() — { mode, active, gpuAvailable,
|
|
15
|
+
// gpuTimeMs, reason }
|
|
16
|
+
// runStep(dt, stepCPU, stepGPU) — dispatched per frame from
|
|
17
|
+
// app.js render loop
|
|
18
|
+
//
|
|
19
|
+
// Persists user preference in localStorage so the toggle survives
|
|
20
|
+
// app restarts.
|
|
21
|
+
//
|
|
22
|
+
// GPU contention detection:
|
|
23
|
+
// Each GPU step ends with await device.queue.onSubmittedWorkDone(),
|
|
24
|
+
// timed via performance.now(). If the average of the last
|
|
25
|
+
// SAMPLE_WINDOW frames exceeds CONTENTION_MS, we declare the GPU
|
|
26
|
+
// contended (likely a heavy app like Wan 2.2 inference is using
|
|
27
|
+
// it) and fall back to CPU. We keep probing every PROBE_INTERVAL_MS
|
|
28
|
+
// by running one GPU step and checking its timing.
|
|
29
|
+
// ═══════════════════════════════════════════════════════════════
|
|
30
|
+
|
|
31
|
+
const STORAGE_KEY = 'codesynapt:backend_mode'
|
|
32
|
+
const SAMPLE_WINDOW = 30 // frames to average for contention
|
|
33
|
+
const CONTENTION_MS = 10 // GPU step >10ms avg = contended
|
|
34
|
+
const RECOVERY_MS = 4 // probe shows <4ms = healthy again
|
|
35
|
+
const PROBE_INTERVAL_MS = 3000 // re-test GPU every 3s in fallback
|
|
36
|
+
|
|
37
|
+
const state = {
|
|
38
|
+
mode: 'auto', // user preference: 'auto'|'gpu'|'cpu'
|
|
39
|
+
active: 'cpu', // what's actually running this frame
|
|
40
|
+
gpuAvailable: false, // navigator.gpu and adapter exist
|
|
41
|
+
gpuInitError: null, // string describing init failure
|
|
42
|
+
device: null, // GPUDevice, populated on init
|
|
43
|
+
adapter: null, // GPUAdapter
|
|
44
|
+
gpuTimeMs: 0, // smoothed GPU step time
|
|
45
|
+
reason: 'not initialized', // human-readable status
|
|
46
|
+
sampleBuffer: new Float32Array(SAMPLE_WINDOW),
|
|
47
|
+
sampleIdx: 0,
|
|
48
|
+
sampleCount: 0,
|
|
49
|
+
lastProbeAt: 0,
|
|
50
|
+
listeners: [],
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function loadPreference() {
|
|
54
|
+
try {
|
|
55
|
+
const v = localStorage.getItem(STORAGE_KEY)
|
|
56
|
+
if (v === 'auto' || v === 'gpu' || v === 'cpu') state.mode = v
|
|
57
|
+
} catch { /* localStorage may be blocked */ }
|
|
58
|
+
}
|
|
59
|
+
function savePreference() {
|
|
60
|
+
try { localStorage.setItem(STORAGE_KEY, state.mode) } catch { /* ignore */ }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function emit() {
|
|
64
|
+
const snapshot = getStatus()
|
|
65
|
+
for (const fn of state.listeners) fn(snapshot)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function recordSample(ms) {
|
|
69
|
+
state.sampleBuffer[state.sampleIdx] = ms
|
|
70
|
+
state.sampleIdx = (state.sampleIdx + 1) % SAMPLE_WINDOW
|
|
71
|
+
if (state.sampleCount < SAMPLE_WINDOW) state.sampleCount++
|
|
72
|
+
// Exponential moving average for display
|
|
73
|
+
state.gpuTimeMs = state.gpuTimeMs * 0.9 + ms * 0.1
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function averageGpuTime() {
|
|
77
|
+
if (state.sampleCount === 0) return 0
|
|
78
|
+
let sum = 0
|
|
79
|
+
for (let i = 0; i < state.sampleCount; i++) sum += state.sampleBuffer[i]
|
|
80
|
+
return sum / state.sampleCount
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function clearSamples() {
|
|
84
|
+
state.sampleCount = 0
|
|
85
|
+
state.sampleIdx = 0
|
|
86
|
+
state.gpuTimeMs = 0
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ─── Public API ─────────────────────────────────────────────────
|
|
90
|
+
|
|
91
|
+
export async function init({ onStatusChange } = {}) {
|
|
92
|
+
if (onStatusChange) state.listeners.push(onStatusChange)
|
|
93
|
+
loadPreference()
|
|
94
|
+
|
|
95
|
+
if (!('gpu' in navigator)) {
|
|
96
|
+
state.gpuInitError = 'WebGPU not available in this browser/build'
|
|
97
|
+
state.reason = state.gpuInitError
|
|
98
|
+
state.active = 'cpu'
|
|
99
|
+
emit()
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
const adapter = await navigator.gpu.requestAdapter({
|
|
105
|
+
powerPreference: 'high-performance',
|
|
106
|
+
})
|
|
107
|
+
if (!adapter) {
|
|
108
|
+
state.gpuInitError = 'No WebGPU adapter found'
|
|
109
|
+
state.reason = state.gpuInitError
|
|
110
|
+
state.active = 'cpu'
|
|
111
|
+
emit()
|
|
112
|
+
return
|
|
113
|
+
}
|
|
114
|
+
const device = await adapter.requestDevice({
|
|
115
|
+
// Ask for the limits we need for big graphs
|
|
116
|
+
requiredLimits: {
|
|
117
|
+
maxBufferSize: Math.min(adapter.limits.maxBufferSize, 256 * 1024 * 1024),
|
|
118
|
+
maxStorageBufferBindingSize: Math.min(
|
|
119
|
+
adapter.limits.maxStorageBufferBindingSize,
|
|
120
|
+
256 * 1024 * 1024,
|
|
121
|
+
),
|
|
122
|
+
},
|
|
123
|
+
})
|
|
124
|
+
device.lost.then((info) => {
|
|
125
|
+
console.warn('WebGPU device lost:', info.message)
|
|
126
|
+
state.gpuAvailable = false
|
|
127
|
+
state.device = null
|
|
128
|
+
state.active = 'cpu'
|
|
129
|
+
state.reason = `GPU device lost: ${info.message}`
|
|
130
|
+
emit()
|
|
131
|
+
})
|
|
132
|
+
state.adapter = adapter
|
|
133
|
+
state.device = device
|
|
134
|
+
state.gpuAvailable = true
|
|
135
|
+
state.reason = 'GPU ready'
|
|
136
|
+
|
|
137
|
+
// Initial active backend based on preference
|
|
138
|
+
if (state.mode === 'cpu') {
|
|
139
|
+
state.active = 'cpu'
|
|
140
|
+
state.reason = 'CPU mode selected by user'
|
|
141
|
+
} else {
|
|
142
|
+
state.active = 'gpu'
|
|
143
|
+
state.reason = 'GPU active'
|
|
144
|
+
}
|
|
145
|
+
} catch (err) {
|
|
146
|
+
state.gpuInitError = err.message || String(err)
|
|
147
|
+
state.reason = `GPU init failed: ${state.gpuInitError}`
|
|
148
|
+
state.active = 'cpu'
|
|
149
|
+
}
|
|
150
|
+
emit()
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function setMode(mode) {
|
|
154
|
+
if (mode !== 'auto' && mode !== 'gpu' && mode !== 'cpu') return
|
|
155
|
+
state.mode = mode
|
|
156
|
+
savePreference()
|
|
157
|
+
clearSamples()
|
|
158
|
+
if (mode === 'cpu') {
|
|
159
|
+
state.active = 'cpu'
|
|
160
|
+
state.reason = 'CPU mode selected'
|
|
161
|
+
} else if (mode === 'gpu') {
|
|
162
|
+
if (state.gpuAvailable) {
|
|
163
|
+
state.active = 'gpu'
|
|
164
|
+
state.reason = 'GPU mode forced'
|
|
165
|
+
} else {
|
|
166
|
+
state.active = 'cpu'
|
|
167
|
+
state.reason = state.gpuInitError || 'GPU not available'
|
|
168
|
+
}
|
|
169
|
+
} else {
|
|
170
|
+
// auto
|
|
171
|
+
state.active = state.gpuAvailable ? 'gpu' : 'cpu'
|
|
172
|
+
state.reason = state.gpuAvailable
|
|
173
|
+
? 'auto: GPU active'
|
|
174
|
+
: (state.gpuInitError || 'auto: GPU unavailable, using CPU')
|
|
175
|
+
}
|
|
176
|
+
emit()
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function getStatus() {
|
|
180
|
+
return {
|
|
181
|
+
mode: state.mode,
|
|
182
|
+
active: state.active,
|
|
183
|
+
gpuAvailable: state.gpuAvailable,
|
|
184
|
+
gpuTimeMs: state.gpuTimeMs,
|
|
185
|
+
reason: state.reason,
|
|
186
|
+
gpuInitError: state.gpuInitError,
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function subscribe(fn) {
|
|
191
|
+
state.listeners.push(fn)
|
|
192
|
+
fn(getStatus())
|
|
193
|
+
return () => {
|
|
194
|
+
const i = state.listeners.indexOf(fn)
|
|
195
|
+
if (i >= 0) state.listeners.splice(i, 1)
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// ─── Per-frame dispatch ─────────────────────────────────────────
|
|
200
|
+
//
|
|
201
|
+
// app.js calls runStep(dt, stepCPU, stepGPU) each frame. We
|
|
202
|
+
// decide which backend to use based on state.active, then measure
|
|
203
|
+
// GPU time if applicable, and consider switching modes for 'auto'.
|
|
204
|
+
//
|
|
205
|
+
// stepGPU is OPTIONAL — until v0.6 implements actual compute
|
|
206
|
+
// shaders, it can be null. In that case any 'gpu' request silently
|
|
207
|
+
// runs CPU and updates state.reason.
|
|
208
|
+
//
|
|
209
|
+
export function runStep(dt, stepCPU, stepGPU) {
|
|
210
|
+
// No GPU implementation yet? Always CPU.
|
|
211
|
+
if (!stepGPU || !state.device) {
|
|
212
|
+
if (state.active === 'gpu') {
|
|
213
|
+
state.active = 'cpu'
|
|
214
|
+
state.reason = 'GPU backend not implemented yet (CPU active)'
|
|
215
|
+
emit()
|
|
216
|
+
}
|
|
217
|
+
stepCPU(dt)
|
|
218
|
+
return
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (state.active === 'cpu') {
|
|
222
|
+
stepCPU(dt)
|
|
223
|
+
if (state.mode === 'auto' && state.gpuAvailable) {
|
|
224
|
+
maybeProbeGpu(stepGPU)
|
|
225
|
+
}
|
|
226
|
+
return
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// GPU path
|
|
230
|
+
const t0 = performance.now()
|
|
231
|
+
const promise = stepGPU(dt, state.device)
|
|
232
|
+
// stepGPU may be sync (just queue.submit) or return a Promise that
|
|
233
|
+
// resolves when GPU work is done. We don't await — we measure
|
|
234
|
+
// submission-to-resolve in the background.
|
|
235
|
+
if (promise && typeof promise.then === 'function') {
|
|
236
|
+
promise.then(() => {
|
|
237
|
+
const ms = performance.now() - t0
|
|
238
|
+
recordSample(ms)
|
|
239
|
+
considerAutoSwitch()
|
|
240
|
+
}).catch((err) => {
|
|
241
|
+
console.warn('GPU step failed, falling back to CPU:', err)
|
|
242
|
+
state.active = 'cpu'
|
|
243
|
+
state.reason = `GPU error: ${err.message || err}`
|
|
244
|
+
emit()
|
|
245
|
+
})
|
|
246
|
+
} else {
|
|
247
|
+
const ms = performance.now() - t0
|
|
248
|
+
recordSample(ms)
|
|
249
|
+
considerAutoSwitch()
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function considerAutoSwitch() {
|
|
254
|
+
if (state.mode !== 'auto') return
|
|
255
|
+
if (state.sampleCount < SAMPLE_WINDOW / 2) return
|
|
256
|
+
const avg = averageGpuTime()
|
|
257
|
+
if (state.active === 'gpu' && avg > CONTENTION_MS) {
|
|
258
|
+
state.active = 'cpu'
|
|
259
|
+
state.reason = `auto: GPU contended (${avg.toFixed(1)}ms avg), using CPU`
|
|
260
|
+
state.lastProbeAt = performance.now()
|
|
261
|
+
emit()
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function maybeProbeGpu(stepGPU) {
|
|
266
|
+
// While in CPU fallback under 'auto', occasionally try a single
|
|
267
|
+
// GPU step to check if the GPU has freed up.
|
|
268
|
+
if (state.mode !== 'auto') return
|
|
269
|
+
const now = performance.now()
|
|
270
|
+
if (now - state.lastProbeAt < PROBE_INTERVAL_MS) return
|
|
271
|
+
state.lastProbeAt = now
|
|
272
|
+
const t0 = performance.now()
|
|
273
|
+
const r = stepGPU(0, state.device) // dt=0 == probe-only, no-op step
|
|
274
|
+
const finish = () => {
|
|
275
|
+
const ms = performance.now() - t0
|
|
276
|
+
if (ms < RECOVERY_MS) {
|
|
277
|
+
state.active = 'gpu'
|
|
278
|
+
state.reason = `auto: GPU recovered (${ms.toFixed(1)}ms probe)`
|
|
279
|
+
clearSamples()
|
|
280
|
+
emit()
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
if (r && typeof r.then === 'function') r.then(finish).catch(() => {})
|
|
284
|
+
else finish()
|
|
285
|
+
}
|