mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-09-30 12:39:42 +02:00
fix: correct claudeSessionId for resumed sessions and clean up DEC sync dead code
Use resumeSessionId for Claude conversation ID when resuming sessions, increase default font size to 14, extract shared history fetch logic, and remove unused DEC 2026 sync constants/functions (xterm.js 6.0 handles natively). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+4
-6
@@ -959,9 +959,8 @@ export class Session extends EventEmitter {
|
||||
}
|
||||
);
|
||||
|
||||
// Set claudeSessionId immediately since we passed --session-id to Claude
|
||||
// The mux manager passes --session-id ${sessionId} to Claude
|
||||
this._claudeSessionId = this.id;
|
||||
// Set claudeSessionId — when resuming, the Claude conversation ID is the resumed one.
|
||||
this._claudeSessionId = this._resumeSessionId || this.id;
|
||||
} catch (spawnErr) {
|
||||
console.error('[Session] Failed to spawn PTY for mux attachment:', spawnErr);
|
||||
this.emit('error', `Failed to attach to mux session: ${spawnErr}`);
|
||||
@@ -1046,9 +1045,8 @@ export class Session extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
// Set the claudeSessionId immediately since we passed --session-id
|
||||
// This ensures subagent matching works without waiting for JSON messages
|
||||
this._claudeSessionId = this.id;
|
||||
// Set claudeSessionId — when resuming, the Claude conversation ID is the resumed one.
|
||||
this._claudeSessionId = this._resumeSessionId || this.id;
|
||||
|
||||
this._pid = this.ptyProcess.pid;
|
||||
console.log('[Session] Interactive PTY spawned with PID:', this._pid);
|
||||
|
||||
+33
-43
@@ -63,7 +63,7 @@
|
||||
* @class CodemanApp
|
||||
* @globals {CodemanApp} app - Singleton instance (also on window.app)
|
||||
*
|
||||
* @dependency constants.js (SSE_EVENTS, timing constants, escapeHtml, extractSyncSegments, DEC sync markers)
|
||||
* @dependency constants.js (SSE_EVENTS, timing constants, escapeHtml, DEC_SYNC_STRIP_RE)
|
||||
* @dependency mobile-handlers.js (MobileDetection, KeyboardHandler, SwipeHandler)
|
||||
* @dependency voice-input.js (VoiceInput, DeepgramProvider)
|
||||
* @dependency notification-manager.js (NotificationManager class)
|
||||
@@ -600,7 +600,7 @@ class CodemanApp {
|
||||
},
|
||||
fontFamily: '"Fira Code", "Cascadia Code", "JetBrains Mono", "SF Mono", Monaco, monospace',
|
||||
// Use smaller font on mobile to fit more columns (prevents wrapping of Claude's status line)
|
||||
fontSize: MobileDetection.getDeviceType() === 'mobile' ? 10 : 12,
|
||||
fontSize: MobileDetection.getDeviceType() === 'mobile' ? 10 : 14,
|
||||
lineHeight: 1.2,
|
||||
cursorBlink: false,
|
||||
cursorStyle: 'block',
|
||||
@@ -1181,35 +1181,41 @@ class CodemanApp {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch and deduplicate history sessions (up to 2 per dir, max `limit` total).
|
||||
* @returns {Promise<Array>} deduplicated session list, sorted by lastModified desc
|
||||
*/
|
||||
async _fetchHistorySessions(limit = 12) {
|
||||
const res = await fetch('/api/history/sessions');
|
||||
const data = await res.json();
|
||||
const sessions = data.sessions || [];
|
||||
if (sessions.length === 0) return [];
|
||||
|
||||
const byDir = new Map();
|
||||
for (const s of sessions) {
|
||||
if (!byDir.has(s.workingDir)) byDir.set(s.workingDir, []);
|
||||
byDir.get(s.workingDir).push(s);
|
||||
}
|
||||
const items = [];
|
||||
for (const [, group] of byDir) {
|
||||
items.push(...group.slice(0, 2));
|
||||
}
|
||||
items.sort((a, b) => new Date(b.lastModified) - new Date(a.lastModified));
|
||||
return items.slice(0, limit);
|
||||
}
|
||||
|
||||
async loadHistorySessions() {
|
||||
const container = document.getElementById('historySessions');
|
||||
const list = document.getElementById('historyList');
|
||||
if (!container || !list) return;
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/history/sessions');
|
||||
const data = await res.json();
|
||||
const sessions = data.sessions || [];
|
||||
if (sessions.length === 0) {
|
||||
const display = await this._fetchHistorySessions(12);
|
||||
if (display.length === 0) {
|
||||
container.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
// Deduplicate: keep only the most recent session per workingDir
|
||||
const byDir = new Map();
|
||||
for (const s of sessions) {
|
||||
if (!byDir.has(s.workingDir)) byDir.set(s.workingDir, []);
|
||||
byDir.get(s.workingDir).push(s);
|
||||
}
|
||||
|
||||
// Flatten: show up to 2 most recent per dir, max 12 total
|
||||
const items = [];
|
||||
for (const [, group] of byDir) {
|
||||
items.push(...group.slice(0, 2));
|
||||
}
|
||||
items.sort((a, b) => new Date(b.lastModified) - new Date(a.lastModified));
|
||||
const display = items.slice(0, 12);
|
||||
|
||||
// Build DOM safely (no innerHTML with user data)
|
||||
list.replaceChildren();
|
||||
for (const s of display) {
|
||||
@@ -4263,28 +4269,12 @@ class CodemanApp {
|
||||
container.innerHTML = '<div class="run-mode-hist-empty">Loading...</div>';
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/history/sessions');
|
||||
const data = await res.json();
|
||||
const sessions = data.sessions || [];
|
||||
|
||||
if (sessions.length === 0) {
|
||||
const display = await this._fetchHistorySessions(10);
|
||||
if (display.length === 0) {
|
||||
container.innerHTML = '<div class="run-mode-hist-empty">No history</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Deduplicate: up to 2 per dir, max 10 total
|
||||
const byDir = new Map();
|
||||
for (const s of sessions) {
|
||||
if (!byDir.has(s.workingDir)) byDir.set(s.workingDir, []);
|
||||
byDir.get(s.workingDir).push(s);
|
||||
}
|
||||
const items = [];
|
||||
for (const [, group] of byDir) {
|
||||
items.push(...group.slice(0, 2));
|
||||
}
|
||||
items.sort((a, b) => new Date(b.lastModified) - new Date(a.lastModified));
|
||||
const display = items.slice(0, 10);
|
||||
|
||||
// Build items using DOM API for reliable mobile touch handling
|
||||
container.replaceChildren();
|
||||
for (const s of display) {
|
||||
@@ -5211,13 +5201,13 @@ class CodemanApp {
|
||||
}
|
||||
|
||||
increaseFontSize() {
|
||||
const current = this.terminal.options.fontSize || 12;
|
||||
const current = this.terminal.options.fontSize || 14;
|
||||
this.setFontSize(Math.min(current + 2, 24));
|
||||
}
|
||||
|
||||
decreaseFontSize() {
|
||||
const current = this.terminal.options.fontSize || 12;
|
||||
this.setFontSize(Math.max(current - 2, 8));
|
||||
const current = this.terminal.options.fontSize || 14;
|
||||
this.setFontSize(Math.max(current - 2, 10));
|
||||
}
|
||||
|
||||
setFontSize(size) {
|
||||
@@ -5233,7 +5223,7 @@ class CodemanApp {
|
||||
const saved = localStorage.getItem('codeman-font-size');
|
||||
if (saved) {
|
||||
const size = parseInt(saved, 10);
|
||||
if (size >= 8 && size <= 24) {
|
||||
if (size >= 10 && size <= 24) {
|
||||
this.terminal.options.fontSize = size;
|
||||
document.getElementById('fontSizeDisplay').textContent = size;
|
||||
}
|
||||
|
||||
@@ -2,13 +2,12 @@
|
||||
* @fileoverview Shared constants, utility functions, and SSE event type registry for all frontend modules.
|
||||
*
|
||||
* This is the first script loaded in index.html. Every other frontend module depends on the
|
||||
* globals defined here: timing constants, Z-index layers, DEC 2026 sync markers, respawn
|
||||
* preset definitions, the SSE_EVENTS registry, and shared utilities (escapeHtml, extractSyncSegments,
|
||||
* globals defined here: timing constants, Z-index layers, respawn
|
||||
* preset definitions, the SSE_EVENTS registry, and shared utilities (escapeHtml,
|
||||
* getEventCoords, scheduleBackground, urlBase64ToUint8Array).
|
||||
*
|
||||
* @globals {function} urlBase64ToUint8Array - VAPID key conversion for Web Push
|
||||
* @globals {function} scheduleBackground - scheduler.postTask wrapper (background priority)
|
||||
* @globals {function} extractSyncSegments - DEC 2026 terminal sync marker parser
|
||||
* @globals {function} getEventCoords - Unified mouse/touch coordinate extractor
|
||||
* @globals {function} escapeHtml - XSS-safe HTML escaping
|
||||
* @globals {object} SSE_EVENTS - Centralized SSE event type constants (~73 event types)
|
||||
@@ -79,14 +78,8 @@ function scheduleBackground(fn) {
|
||||
else { requestAnimationFrame(fn); }
|
||||
}
|
||||
|
||||
// DEC mode 2026 - Synchronized Output (xterm.js 6.0+ handles natively)
|
||||
// Wrap terminal writes with these markers to prevent partial-frame flicker.
|
||||
// Terminal buffers all output between markers and renders atomically.
|
||||
// Supported by: WezTerm, Kitty, Ghostty, iTerm2 3.5+, Windows Terminal, VSCode terminal
|
||||
// xterm.js 6.0+ supports DEC 2026 natively. Constants kept for reference/stripping.
|
||||
const DEC_SYNC_START = '\x1b[?2026h';
|
||||
const DEC_SYNC_END = '\x1b[?2026l';
|
||||
// Pre-compiled regex for stripping DEC 2026 markers (single pass instead of two replaceAll calls)
|
||||
// DEC mode 2026 marker stripping — xterm.js 6.0 handles sync natively,
|
||||
// but server-sent terminal buffers may still contain markers from Claude CLI.
|
||||
const DEC_SYNC_STRIP_RE = /\x1b\[\?2026[hl]/g;
|
||||
|
||||
// Built-in respawn configuration presets
|
||||
@@ -303,59 +296,6 @@ function getEventCoords(e) {
|
||||
return { clientX: e.clientX, clientY: e.clientY };
|
||||
}
|
||||
|
||||
/**
|
||||
* Process data containing DEC 2026 sync markers.
|
||||
* Strips markers and returns segments that should be written atomically.
|
||||
* Each returned segment represents content between SYNC_START and SYNC_END.
|
||||
* Content outside sync blocks is returned as-is.
|
||||
*
|
||||
* @param {string} data - Raw terminal data with potential sync markers
|
||||
* @returns {string[]} - Array of content segments to write (markers stripped)
|
||||
*/
|
||||
function extractSyncSegments(data) {
|
||||
const segments = [];
|
||||
let remaining = data;
|
||||
|
||||
while (remaining.length > 0) {
|
||||
const startIdx = remaining.indexOf(DEC_SYNC_START);
|
||||
|
||||
if (startIdx === -1) {
|
||||
// No more sync blocks, return rest as-is
|
||||
if (remaining.length > 0) {
|
||||
segments.push(remaining);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Content before sync block (if any)
|
||||
if (startIdx > 0) {
|
||||
segments.push(remaining.slice(0, startIdx));
|
||||
}
|
||||
|
||||
// Find matching end marker
|
||||
const afterStart = remaining.slice(startIdx + DEC_SYNC_START.length);
|
||||
const endIdx = afterStart.indexOf(DEC_SYNC_END);
|
||||
|
||||
if (endIdx === -1) {
|
||||
// No end marker found - sync block continues in next chunk
|
||||
// Include the start marker so it can be handled when more data arrives
|
||||
segments.push(remaining.slice(startIdx));
|
||||
break;
|
||||
}
|
||||
|
||||
// Extract synchronized content (without markers)
|
||||
const syncContent = afterStart.slice(0, endIdx);
|
||||
if (syncContent.length > 0) {
|
||||
segments.push(syncContent);
|
||||
}
|
||||
|
||||
// Continue with content after end marker
|
||||
remaining = afterStart.slice(endIdx + DEC_SYNC_END.length);
|
||||
}
|
||||
|
||||
return segments;
|
||||
}
|
||||
|
||||
// HTML escape utility (shared by NotificationManager, CodemanApp, and ralph-wizard.js)
|
||||
const _htmlEscapeMap = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' };
|
||||
const _htmlEscapePattern = /[&<>"']/g;
|
||||
|
||||
@@ -123,13 +123,6 @@ import {
|
||||
INACTIVITY_TIMEOUT_MS,
|
||||
} from '../config/server-timing.js';
|
||||
|
||||
// DEC mode 2026 - Synchronized Output
|
||||
// When terminal supports this, it buffers all output between start/end markers
|
||||
// and renders atomically, eliminating partial-frame flicker from Ink redraws.
|
||||
// Supported by: WezTerm, Kitty, Ghostty, iTerm2 3.5+, Windows Terminal, VSCode terminal
|
||||
// DEC 2026 sync markers no longer added server-side — xterm.js 6.0 handles
|
||||
// them natively and Claude CLI already emits its own markers via Ink.
|
||||
|
||||
// SSE padding for Cloudflare tunnel buffer flushing.
|
||||
// Cloudflare quick tunnels buffer small SSE responses, causing lag for real-time events.
|
||||
// Appending SSE comment padding (ignored by EventSource) forces the proxy to flush.
|
||||
|
||||
Reference in New Issue
Block a user