Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 99 additions & 16 deletions STRUCTURE.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"version": "1.0",
"description": "Claude Code IDE - Module structure and IPC communication map",
"lastUpdated": "2026-03-26",
"lastUpdated": "2026-03-31",
"architecture": {
"type": "electron",
"mainProcess": "src/main/index.js",
Expand Down Expand Up @@ -1618,12 +1618,14 @@
"ipc": {
"listens": [
"TERMINAL_CREATED",
"TERMINALS_RESTORED",
"AVAILABLE_SHELLS_DATA",
"TERMINAL_OUTPUT_ID",
"TERMINAL_DESTROYED"
],
"emits": [
"TERMINAL_CREATE",
"TERMINALS_RESTORE",
"GET_AVAILABLE_SHELLS",
"TERMINAL_INPUT_ID",
"TERMINAL_DESTROY",
Expand Down Expand Up @@ -1804,6 +1806,7 @@
"exports": [
"init",
"createTerminal",
"restoreTerminals",
"writeToTerminal",
"resizeTerminal",
"destroyTerminal",
Expand All @@ -1820,57 +1823,62 @@
"node-pty",
"shared/ipcChannels",
"promptLogger",
"terminalPersistence",
"child_process",
"fs"
],
"functions": {
"init": {
"line": 19,
"line": 20,
"params": [
"window"
],
"purpose": "Initialize PTY manager with window reference"
},
"getDefaultShell": {
"line": 26,
"line": 27,
"purpose": "Get default shell based on platform"
},
"getAvailableShells": {
"line": 43,
"line": 44,
"purpose": "Get available shells on the system"
},
"createTerminal": {
"line": 129,
"line": 130,
"params": [
"workingDir = null",
"projectPath = null",
"shellPath = null"
]
},
"restoreTerminals": {
"line": 192,
"purpose": "Restore terminals from persisted sessions (plain PTY, no tmux)"
},
"getTerminalsByProject": {
"line": 191,
"line": 255,
"params": [
"projectPath"
],
"purpose": "Get terminals for a specific project"
},
"getTerminalInfo": {
"line": 206,
"line": 270,
"params": [
"terminalId"
],
"purpose": "Get terminal info"
},
"writeToTerminal": {
"line": 217,
"line": 281,
"params": [
"terminalId",
"data"
],
"purpose": "Write data to specific terminal"
},
"resizeTerminal": {
"line": 227,
"line": 291,
"params": [
"terminalId",
"cols",
Expand All @@ -1879,33 +1887,33 @@
"purpose": "Resize specific terminal"
},
"destroyTerminal": {
"line": 237,
"line": 301,
"params": [
"terminalId"
],
"purpose": "Destroy specific terminal"
},
"destroyAll": {
"line": 249,
"line": 314,
"purpose": "Destroy all terminals"
},
"getTerminalCount": {
"line": 260,
"line": 325,
"purpose": "Get terminal count"
},
"getTerminalIds": {
"line": 267,
"line": 332,
"purpose": "Get all terminal IDs"
},
"hasTerminal": {
"line": 274,
"line": 339,
"params": [
"terminalId"
],
"purpose": "Check if terminal exists"
},
"setupIPC": {
"line": 281,
"line": 346,
"params": [
"ipcMain"
],
Expand All @@ -1918,9 +1926,12 @@
"TERMINAL_CREATE",
"TERMINAL_DESTROY",
"TERMINAL_INPUT_ID",
"TERMINAL_RESIZE_ID"
"TERMINAL_RESIZE_ID",
"TERMINALS_RESTORE"
],
"emits": [
"TERMINAL_OUTPUT_ID",
"TERMINAL_DESTROYED",
"TERMINAL_OUTPUT_ID",
"TERMINAL_DESTROYED"
]
Expand Down Expand Up @@ -3499,6 +3510,61 @@
"LOAD_PROMPT_HISTORY"
]
}
},
"main/terminalPersistence": {
"file": "src/main/terminalPersistence.js",
"description": "Terminal Persistence Module",
"exports": [
"load",
"save",
"add",
"remove",
"update"
],
"depends": [
"path",
"fs",
"electron"
],
"functions": {
"getStoragePath": {
"line": 10
},
"load": {
"line": 18,
"purpose": "Load all persisted terminal sessions"
},
"save": {
"line": 34,
"params": [
"sessions"
],
"purpose": "Persist all terminal sessions to disk"
},
"add": {
"line": 47,
"params": [
"terminalId",
"data"
],
"purpose": "Add or update a terminal session entry"
},
"remove": {
"line": 56,
"params": [
"terminalId"
],
"purpose": "Remove a terminal session entry"
},
"update": {
"line": 67,
"params": [
"terminalId",
"patch"
],
"purpose": "Update a specific field of a terminal session"
}
}
}
},
"ipcChannels": {
Expand Down Expand Up @@ -3975,6 +4041,16 @@
"name": "clone-github-repo-result",
"direction": "",
"description": ""
},
"TERMINALS_RESTORE": {
"name": "terminals-restore",
"direction": "",
"description": ""
},
"TERMINALS_RESTORED": {
"name": "terminals-restored",
"direction": "",
"description": ""
}
}
},
Expand Down Expand Up @@ -4362,6 +4438,13 @@
"description": "Terminal Tab Bar Module"
}
],
"terminal-persistence": [
{
"module": "main/terminalPersistence",
"file": "src/main/terminalPersistence.js",
"description": "Terminal Persistence Module"
}
],
"workspace": [
{
"module": "main/workspace",
Expand Down
77 changes: 77 additions & 0 deletions src/main/ptyManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
const pty = require('node-pty');
const { IPC } = require('../shared/ipcChannels');
const promptLogger = require('./promptLogger');
const terminalPersistence = require('./terminalPersistence');

// Store multiple PTY instances
const ptyInstances = new Map(); // Map<terminalId, {pty, cwd, projectPath}>
Expand Down Expand Up @@ -178,11 +179,74 @@ function createTerminal(workingDir = null, projectPath = null, shellPath = null)
});

ptyInstances.set(terminalId, { pty: ptyProcess, cwd, projectPath });
terminalPersistence.add(terminalId, { cwd, projectPath: projectPath || null, customName: null });
console.log(`Created terminal ${terminalId} in ${cwd} (project: ${projectPath || 'global'})`);

return terminalId;
}

/**
* Restore terminals from persisted sessions (plain PTY, no tmux)
* @returns {Array<{terminalId, cwd, projectPath, customName}>} Restored terminal metadata
*/
function restoreTerminals() {
const sessions = terminalPersistence.load();
const restored = [];

for (const [terminalId, data] of Object.entries(sessions)) {
const { cwd, projectPath, customName } = data;

// Update counter so new terminals don't collide with restored IDs
const match = terminalId.match(/term-(\d+)/);
if (match) {
const num = parseInt(match[1], 10);
if (num > terminalCounter) terminalCounter = num;
}

const shell = getDefaultShell();
let shellArgs = [];
if (process.platform !== 'win32') {
const shellName = shell.split('/').pop();
if (shellName === 'fish') {
shellArgs = ['-i'];
} else if (shellName === 'nu') {
shellArgs = ['-l'];
} else {
shellArgs = ['-i', '-l'];
}
}

const ptyProcess = pty.spawn(shell, shellArgs, {
name: 'xterm-256color',
cols: 80,
rows: 24,
cwd,
env: { ...process.env, TERM: 'xterm-256color', COLORTERM: 'truecolor' }
});

ptyProcess.onData((d) => {
if (mainWindow && !mainWindow.isDestroyed()) {
mainWindow.webContents.send(IPC.TERMINAL_OUTPUT_ID, { terminalId, data: d });
}
});

ptyProcess.onExit(({ exitCode }) => {
console.log(`Terminal ${terminalId} (restored) exited:`, exitCode);
ptyInstances.delete(terminalId);
terminalPersistence.remove(terminalId);
if (mainWindow && !mainWindow.isDestroyed()) {
mainWindow.webContents.send(IPC.TERMINAL_DESTROYED, { terminalId, exitCode });
}
});

ptyInstances.set(terminalId, { pty: ptyProcess, cwd, projectPath: projectPath || null });
restored.push({ terminalId, cwd, projectPath: projectPath || null, customName: customName || null });
console.log(`Restored terminal ${terminalId} in ${cwd}`);
}

return restored;
}

/**
* Get terminals for a specific project
* @param {string|null} projectPath - Project path or null for global
Expand Down Expand Up @@ -239,6 +303,7 @@ function destroyTerminal(terminalId) {
if (instance) {
instance.pty.kill();
ptyInstances.delete(terminalId);
terminalPersistence.remove(terminalId);
console.log(`Destroyed terminal ${terminalId}`);
}
}
Expand Down Expand Up @@ -329,11 +394,23 @@ function setupIPC(ipcMain) {
ipcMain.on(IPC.TERMINAL_RESIZE_ID, (event, { terminalId, cols, rows }) => {
resizeTerminal(terminalId, cols, rows);
});

// Restore persisted terminals
ipcMain.on(IPC.TERMINALS_RESTORE, (event) => {
try {
const restored = restoreTerminals();
event.reply(IPC.TERMINALS_RESTORED, { success: true, terminals: restored });
} catch (error) {
console.error('[ptyManager] Failed to restore terminals:', error);
event.reply(IPC.TERMINALS_RESTORED, { success: false, terminals: [], error: error.message });
}
});
}

module.exports = {
init,
createTerminal,
restoreTerminals,
writeToTerminal,
resizeTerminal,
destroyTerminal,
Expand Down
Loading