mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-09-30 12:39:42 +02:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5814947d6 | ||
|
|
b620511d0e |
@@ -1,5 +1,11 @@
|
||||
# aicodeman
|
||||
|
||||
## 0.3.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Re-enable WebGL renderer with 48KB/frame flush cap protection against GPU stalls
|
||||
|
||||
## 0.3.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -52,7 +52,7 @@ When user says "COM":
|
||||
4. **Sync CLAUDE.md version**: Update the `**Version**` line below to match the new version from `package.json`
|
||||
5. **Commit and deploy**: `git add -A && git commit -m "chore: version packages" && git push && npm run build && systemctl --user restart codeman-web`
|
||||
|
||||
**Version**: 0.3.5 (must match `package.json`)
|
||||
**Version**: 0.3.6 (must match `package.json`)
|
||||
|
||||
## Project Overview
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "aicodeman",
|
||||
"version": "0.3.5",
|
||||
"version": "0.3.6",
|
||||
"description": "The missing control plane for AI coding agents - run 20 autonomous agents with real-time monitoring and session persistence",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
|
||||
+47
-19
@@ -618,14 +618,13 @@ class CodemanApp {
|
||||
const container = document.getElementById('terminalContainer');
|
||||
this.terminal.open(container);
|
||||
|
||||
// WebGL renderer disabled — canvas renderer used instead.
|
||||
// xterm.js WebGL addon causes synchronous GPU ReadPixels calls during large
|
||||
// terminal writes (buffer loads, heavy Ink output) that block Chrome's main
|
||||
// thread for 10+ seconds, triggering "page unresponsive" crashes.
|
||||
// Canvas renderer handles the same workloads without GPU stalls.
|
||||
// Re-enable with ?webgl=1 URL param for testing.
|
||||
// WebGL renderer for GPU-accelerated terminal rendering.
|
||||
// Previously caused "page unresponsive" crashes from synchronous GPU stalls,
|
||||
// but the 48KB/frame flush cap in flushPendingWrites() now prevents
|
||||
// oversized terminal.write() calls that triggered the stalls.
|
||||
// Disable with ?nowebgl URL param if GPU issues return.
|
||||
this._webglAddon = null;
|
||||
if (new URLSearchParams(location.search).has('webgl') && typeof WebglAddon !== 'undefined') {
|
||||
if (!new URLSearchParams(location.search).has('nowebgl') && typeof WebglAddon !== 'undefined') {
|
||||
try {
|
||||
this._webglAddon = new WebglAddon.WebglAddon();
|
||||
this._webglAddon.onContextLoss(() => {
|
||||
@@ -1381,21 +1380,50 @@ class CodemanApp {
|
||||
|
||||
const segments = extractSyncSegments(joined);
|
||||
|
||||
// Write all segments in a single batch (atomic within this frame).
|
||||
// xterm.js internally batches multiple write() calls within same frame.
|
||||
// Never discard content from incomplete sync blocks — xterm.js doesn't support
|
||||
// DEC 2026 natively anyway, so strip the marker and write content regardless.
|
||||
// Discarding causes real data loss (including Ink's erase-line escapes).
|
||||
for (const segment of segments) {
|
||||
if (segment) {
|
||||
const content = segment.startsWith(DEC_SYNC_START)
|
||||
? segment.slice(DEC_SYNC_START.length)
|
||||
: segment;
|
||||
if (content) this.terminal.write(content);
|
||||
// Write segments respecting a per-frame byte budget.
|
||||
// Each DEC 2026 sync segment is a complete Ink redraw — writing whole segments
|
||||
// preserves atomicity (no flicker). But when total data exceeds 48KB, defer
|
||||
// remaining segments to the next frame to prevent terminal.write() from blocking
|
||||
// the main thread. 141KB single-frame writes have been observed to freeze Chrome
|
||||
// for 2+ minutes even with the canvas renderer.
|
||||
const MAX_FRAME_BYTES = 49152; // 48KB budget per frame
|
||||
let bytesThisFrame = 0;
|
||||
let deferred = false;
|
||||
|
||||
for (let i = 0; i < segments.length; i++) {
|
||||
const segment = segments[i];
|
||||
if (!segment) continue;
|
||||
const content = segment.startsWith(DEC_SYNC_START)
|
||||
? segment.slice(DEC_SYNC_START.length)
|
||||
: segment;
|
||||
if (!content) continue;
|
||||
|
||||
// If we'd exceed the budget, defer this and all remaining segments
|
||||
if (bytesThisFrame > 0 && bytesThisFrame + content.length > MAX_FRAME_BYTES) {
|
||||
// Re-queue remaining segments as raw content for next flush
|
||||
const remaining = segments.slice(i).map(s => {
|
||||
if (!s) return '';
|
||||
return s.startsWith(DEC_SYNC_START) ? s.slice(DEC_SYNC_START.length) : s;
|
||||
}).filter(Boolean).join('');
|
||||
if (remaining) {
|
||||
this.pendingWrites.push(remaining);
|
||||
if (!this.writeFrameScheduled) {
|
||||
this.writeFrameScheduled = true;
|
||||
requestAnimationFrame(() => {
|
||||
this.flushPendingWrites();
|
||||
this.writeFrameScheduled = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
deferred = true;
|
||||
break;
|
||||
}
|
||||
|
||||
this.terminal.write(content);
|
||||
bytesThisFrame += content.length;
|
||||
}
|
||||
const _dt = performance.now() - _t0;
|
||||
if (_dt > 100) console.warn(`[CRASH-DIAG] flushPendingWrites took ${_dt.toFixed(0)}ms (${_joinedLen} bytes)`);
|
||||
if (_dt > 100 || deferred) console.warn(`[CRASH-DIAG] flushPendingWrites: ${_dt.toFixed(0)}ms, ${(bytesThisFrame/1024).toFixed(0)}KB written${deferred ? ', rest deferred' : ''} (total ${(_joinedLen/1024).toFixed(0)}KB)`);
|
||||
|
||||
// Sticky scroll: if user was at bottom, keep them there after new output
|
||||
if (this._wasAtBottomBeforeWrite) {
|
||||
|
||||
Reference in New Issue
Block a user