termdock 1.4.98 → 1.4.99
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/.desktop-dist/main.js +211 -34
- package/dist/client/assets/{ModelPreview-Drqv0OTg.js → ModelPreview-CJK3Nmg4.js} +1 -1
- package/dist/client/assets/{abnfDiagram-VRR7QNED-Cl8CT45X.js → abnfDiagram-VRR7QNED-C-QKkcSr.js} +1 -1
- package/dist/client/assets/{arc-2Sc638oe.js → arc-BpjFutHZ.js} +1 -1
- package/dist/client/assets/{architectureDiagram-ZJ3FMSHR-CNH9w9eN.js → architectureDiagram-ZJ3FMSHR-DeeuzGsk.js} +1 -1
- package/dist/client/assets/{blockDiagram-677ZJIJ3-ClvE_yP1.js → blockDiagram-677ZJIJ3-BkDiKR3-.js} +1 -1
- package/dist/client/assets/{c4Diagram-LMCZKHZV-CxIMU8l9.js → c4Diagram-LMCZKHZV-BX6424vk.js} +1 -1
- package/dist/client/assets/channel-BiaGwVDJ.js +1 -0
- package/dist/client/assets/{chunk-2Q5K7J3B-CdvXmc6Z.js → chunk-2Q5K7J3B-DwNMfRvw.js} +1 -1
- package/dist/client/assets/{chunk-32BRIVSS-B1dA4l5A.js → chunk-32BRIVSS-D5g3chKu.js} +1 -1
- package/dist/client/assets/{chunk-5VM5RSS4-CVk_pzpf.js → chunk-5VM5RSS4-B2rw8Ldu.js} +1 -1
- package/dist/client/assets/{chunk-EX3LRPZG-B46zhCa0.js → chunk-EX3LRPZG-j3CVdb45.js} +1 -1
- package/dist/client/assets/{chunk-JWPE2WC7-1wRPE3eO.js → chunk-JWPE2WC7-iHTR9CAX.js} +1 -1
- package/dist/client/assets/{chunk-MOJQB5TN-BzPrhNIS.js → chunk-MOJQB5TN-DVD1wXze.js} +1 -1
- package/dist/client/assets/{chunk-RYQCIY6F-D0sDGnBf.js → chunk-RYQCIY6F-zZRopnRd.js} +1 -1
- package/dist/client/assets/{chunk-V7JOEXUC-BRo0103H.js → chunk-V7JOEXUC-h4EZpc4k.js} +1 -1
- package/dist/client/assets/{chunk-VR4S4FIN-CW-wSkDE.js → chunk-VR4S4FIN-C9MZH2Oy.js} +1 -1
- package/dist/client/assets/{chunk-XXDRQBXY-BuWiQ0uq.js → chunk-XXDRQBXY-BNPDO5fQ.js} +1 -1
- package/dist/client/assets/classDiagram-OUVF2IWQ-DYljVZD_.js +1 -0
- package/dist/client/assets/classDiagram-v2-EOCWNBFH-DYljVZD_.js +1 -0
- package/dist/client/assets/{cose-bilkent-JH36ORCC-WzKVd03Q.js → cose-bilkent-JH36ORCC-DlqULumX.js} +1 -1
- package/dist/client/assets/{cynefin-VYW2F7L2-CfXYAqnq.js → cynefin-VYW2F7L2-D6pWZSwi.js} +1 -1
- package/dist/client/assets/{cynefinDiagram-TSTJHNR4-BnJGle_X.js → cynefinDiagram-TSTJHNR4-B6SI7XOe.js} +1 -1
- package/dist/client/assets/{dagre-VKFMJZFB-CDDlGCQy.js → dagre-VKFMJZFB-DXnKJ4XD.js} +1 -1
- package/dist/client/assets/{diagram-FQU43EPY-t8DNZS7P.js → diagram-FQU43EPY-B5NTAhw6.js} +1 -1
- package/dist/client/assets/{diagram-G47NLZAW-CZpQd91k.js → diagram-G47NLZAW-Dihaal0B.js} +1 -1
- package/dist/client/assets/{diagram-NH7WQ7WH-OET0EFlt.js → diagram-NH7WQ7WH-Be987DPJ.js} +1 -1
- package/dist/client/assets/{diagram-OA4YK3LP-COgju4NX.js → diagram-OA4YK3LP-CfgTeXAJ.js} +1 -1
- package/dist/client/assets/{diagram-WEI45ONY-6gNSoIek.js → diagram-WEI45ONY-BgrK8rP2.js} +1 -1
- package/dist/client/assets/{ebnfDiagram-CCIWWBDH-CTlIMc8W.js → ebnfDiagram-CCIWWBDH-BPsH1_QO.js} +1 -1
- package/dist/client/assets/{erDiagram-Q63AITRT-BDsEvZvq.js → erDiagram-Q63AITRT-F3s7Ytrc.js} +1 -1
- package/dist/client/assets/{flowDiagram-23GEKE2U-ggl4zaY9.js → flowDiagram-23GEKE2U-ByL-8svD.js} +1 -1
- package/dist/client/assets/{ganttDiagram-NO4QXBWP-D9Ca5oWS.js → ganttDiagram-NO4QXBWP-Dtv-fi06.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-IHSO6WYX-THPUH6Bg.js → gitGraphDiagram-IHSO6WYX-CeD48POl.js} +1 -1
- package/dist/client/assets/{index-BuMMW0wS.js → index-C82Uc6Vy.js} +4 -4
- package/dist/client/assets/{infoDiagram-FWYZ7A6U-CKF-vSTz.js → infoDiagram-FWYZ7A6U-BxvLe33p.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-FXEZZL3T-CBFlmgI3.js → ishikawaDiagram-FXEZZL3T-XPW1aoJK.js} +1 -1
- package/dist/client/assets/{journeyDiagram-5HDEW3XC-lbXIqX5a.js → journeyDiagram-5HDEW3XC-Bhiae04Z.js} +1 -1
- package/dist/client/assets/{kanban-definition-HUTT4EX6-BUuO_e54.js → kanban-definition-HUTT4EX6-D5iOY3Zc.js} +1 -1
- package/dist/client/assets/{linear-D_uoNzR8.js → linear-PCgLsFTe.js} +1 -1
- package/dist/client/assets/{mermaid.core-D4Yy5OxZ.js → mermaid.core-DWuc-xG5.js} +4 -4
- package/dist/client/assets/{mindmap-definition-LN4V7U3C-B0Ml-jaq.js → mindmap-definition-LN4V7U3C-C0EFK91v.js} +1 -1
- package/dist/client/assets/{pegDiagram-2B236MQR-CKF9cuqx.js → pegDiagram-2B236MQR-ClTz8aHa.js} +1 -1
- package/dist/client/assets/{pieDiagram-ENE6RG2P-DL3XisAD.js → pieDiagram-ENE6RG2P-BuTv1DGN.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-ABIIQ3AL-DrsHXXqd.js → quadrantDiagram-ABIIQ3AL-FLZC-cqI.js} +1 -1
- package/dist/client/assets/{railroadDiagram-RFXS5EU6-DMAz8hLg.js → railroadDiagram-RFXS5EU6-DK68FlFh.js} +1 -1
- package/dist/client/assets/{requirementDiagram-TGXJPOKE-CCHretnQ.js → requirementDiagram-TGXJPOKE-B_701AJ1.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-HTMAVEWB-Btb7ZNwU.js → sankeyDiagram-HTMAVEWB-Bk87L7JG.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-DBY2YBRQ-DcZk1PdX.js → sequenceDiagram-DBY2YBRQ-Biy5tPTS.js} +1 -1
- package/dist/client/assets/{sizeCapture-X5ZJPWSS-vMYb_VZU.js → sizeCapture-X5ZJPWSS-__4zzUuh.js} +1 -1
- package/dist/client/assets/{stateDiagram-2N3HPSRC-CltexqJj.js → stateDiagram-2N3HPSRC-B_400cf9.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-6OUMAXLB-CK77br3V.js +1 -0
- package/dist/client/assets/{swimlanes-5IMT3BWC-DPnwlpXX.js → swimlanes-5IMT3BWC-CxWZz0n0.js} +2 -2
- package/dist/client/assets/swimlanesDiagram-G3AALYLV-DbYe8ax3.js +8 -0
- package/dist/client/assets/{timeline-definition-FHXFAJF6-l78vfKln.js → timeline-definition-FHXFAJF6-DSrIUp8H.js} +1 -1
- package/dist/client/assets/{vennDiagram-L72KCM5P-BggNEe_f.js → vennDiagram-L72KCM5P-CdwaUmpL.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-EHGQE667-v04USz2e.js → wardleyDiagram-EHGQE667-DwxlF0Xk.js} +1 -1
- package/dist/client/assets/{xychartDiagram-FW5EYKEG-CInHBOpK.js → xychartDiagram-FW5EYKEG-CQNmEc_n.js} +1 -1
- package/dist/client/index.html +1 -1
- package/dist/client/sw.js +1 -1
- package/dist/server/agent/sessionSearchStore.js +38 -10
- package/dist/server/routes/terminal.js +206 -30
- package/package.json +3 -2
- package/runtime-manifest.json +2 -2
- package/dist/client/assets/channel-DPt818Jx.js +0 -1
- package/dist/client/assets/classDiagram-OUVF2IWQ-C2hZbTnO.js +0 -1
- package/dist/client/assets/classDiagram-v2-EOCWNBFH-C2hZbTnO.js +0 -1
- package/dist/client/assets/stateDiagram-v2-6OUMAXLB-BslbFNw3.js +0 -1
- package/dist/client/assets/swimlanesDiagram-G3AALYLV-B0V1XVQb.js +0 -8
|
@@ -43,6 +43,7 @@ const TERMDOCK_DIR = `${os.homedir()}/.termdock`;
|
|
|
43
43
|
const automationStore = new AutomationStore(`${TERMDOCK_DIR}/automations.json`);
|
|
44
44
|
const collaborationStore = new CollaborationStore(`${TERMDOCK_DIR}/collaboration-groups.json`);
|
|
45
45
|
const sessionSearchStore = new SessionSearchStore(`${TERMDOCK_DIR}/session-search`);
|
|
46
|
+
let atomicJsonWriteSequence = 0;
|
|
46
47
|
async function readJsonFileIfExists(filePath) {
|
|
47
48
|
try {
|
|
48
49
|
const raw = await fs.promises.readFile(filePath, 'utf-8');
|
|
@@ -56,7 +57,21 @@ async function readJsonFileIfExists(filePath) {
|
|
|
56
57
|
}
|
|
57
58
|
async function writeJsonFile(filePath, value) {
|
|
58
59
|
await fs.promises.mkdir(path.dirname(filePath), { recursive: true });
|
|
59
|
-
|
|
60
|
+
const temporaryPath = `${filePath}.${process.pid}.${++atomicJsonWriteSequence}.tmp`;
|
|
61
|
+
let handle = null;
|
|
62
|
+
try {
|
|
63
|
+
handle = await fs.promises.open(temporaryPath, 'w');
|
|
64
|
+
await handle.writeFile(JSON.stringify(value, null, 2), 'utf-8');
|
|
65
|
+
await handle.sync();
|
|
66
|
+
await handle.close();
|
|
67
|
+
handle = null;
|
|
68
|
+
await fs.promises.rename(temporaryPath, filePath);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
await handle?.close().catch(() => undefined);
|
|
72
|
+
await fs.promises.rm(temporaryPath, { force: true }).catch(() => undefined);
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
60
75
|
}
|
|
61
76
|
// Termdock metadata constants used to populate tmux user options
|
|
62
77
|
// (`@termdock-*`) so external tools (e.g. `termdock --tls`) can identify
|
|
@@ -636,6 +651,8 @@ const CLEANUP_INTERVAL = TERMINAL.cleanupInterval;
|
|
|
636
651
|
const RECONNECT_SCROLLBACK = TERMINAL.reconnectScrollback;
|
|
637
652
|
const TMUX_POLL_INTERVAL = parseInt(process.env.TMUX_POLL_INTERVAL || '500', 10);
|
|
638
653
|
const ACTIVE_PROGRAM_POLL_INTERVAL = parseInt(process.env.TERMINAL_ACTIVE_PROGRAM_POLL_INTERVAL || '1200', 10);
|
|
654
|
+
const TMUX_LAYOUT_CACHE_TTL_MS = Math.max(TMUX_POLL_INTERVAL, 1000);
|
|
655
|
+
const PROCESS_SNAPSHOT_CACHE_TTL_MS = Math.max(ACTIVE_PROGRAM_POLL_INTERVAL, 2500);
|
|
639
656
|
const FLOW_CONTROL_PAUSE_LEASE_MS = TERMINAL.flowControlPauseLeaseMs;
|
|
640
657
|
const TMUX_DELIMITER = '\x1f';
|
|
641
658
|
const TERMDOCK_TMUX_HISTORY_LIMIT = TMUX.historyLimit;
|
|
@@ -2954,6 +2971,51 @@ try {
|
|
|
2954
2971
|
}
|
|
2955
2972
|
}
|
|
2956
2973
|
catch { /* plugin loading must never block startup */ }
|
|
2974
|
+
let processSnapshot = null;
|
|
2975
|
+
let processSnapshotPromise = null;
|
|
2976
|
+
function parseProcessSnapshot(stdout) {
|
|
2977
|
+
return stdout
|
|
2978
|
+
.split('\n')
|
|
2979
|
+
.map((line) => line.trim())
|
|
2980
|
+
.filter(Boolean)
|
|
2981
|
+
.map((line) => {
|
|
2982
|
+
// ps -o format: TTY PID PPID PGID TPGID STAT COMM ARGS
|
|
2983
|
+
const match = line.match(/^(\S+)\s+(\d+)\s+(\d+)\s+(-?\d+)\s+(-?\d+)\s+(\S+)\s+(\S+)\s+(.+)$/);
|
|
2984
|
+
if (!match)
|
|
2985
|
+
return null;
|
|
2986
|
+
return {
|
|
2987
|
+
tty: match[1] || '',
|
|
2988
|
+
pid: Number.parseInt(match[2] || '0', 10),
|
|
2989
|
+
ppid: Number.parseInt(match[3] || '0', 10),
|
|
2990
|
+
pgid: Number.parseInt(match[4] || '0', 10),
|
|
2991
|
+
tpgid: Number.parseInt(match[5] || '0', 10),
|
|
2992
|
+
stat: match[6] || '',
|
|
2993
|
+
comm: match[7] || '',
|
|
2994
|
+
args: match[8]?.trim() || '',
|
|
2995
|
+
};
|
|
2996
|
+
})
|
|
2997
|
+
.filter((row) => row !== null);
|
|
2998
|
+
}
|
|
2999
|
+
async function getProcessSnapshot() {
|
|
3000
|
+
const now = Date.now();
|
|
3001
|
+
if (processSnapshot && now - processSnapshot.fetchedAt < PROCESS_SNAPSHOT_CACHE_TTL_MS) {
|
|
3002
|
+
return processSnapshot.rows;
|
|
3003
|
+
}
|
|
3004
|
+
if (processSnapshotPromise)
|
|
3005
|
+
return processSnapshotPromise;
|
|
3006
|
+
processSnapshotPromise = execFileAsync('ps', [
|
|
3007
|
+
'-e',
|
|
3008
|
+
'-o',
|
|
3009
|
+
'tty=,pid=,ppid=,pgid=,tpgid=,stat=,comm=,args=',
|
|
3010
|
+
], { timeout: 3000, maxBuffer: 4 * 1024 * 1024 }).then(({ stdout }) => {
|
|
3011
|
+
const rows = parseProcessSnapshot(stdout);
|
|
3012
|
+
processSnapshot = { rows, fetchedAt: Date.now() };
|
|
3013
|
+
return rows;
|
|
3014
|
+
}).finally(() => {
|
|
3015
|
+
processSnapshotPromise = null;
|
|
3016
|
+
});
|
|
3017
|
+
return processSnapshotPromise;
|
|
3018
|
+
}
|
|
2957
3019
|
async function resolveTmuxPaneProgram(pane) {
|
|
2958
3020
|
// If pane command is a known shell, try to find a child foreground process
|
|
2959
3021
|
const command = normalizeProgramName(pane.command);
|
|
@@ -2969,31 +3031,36 @@ async function resolveTmuxPaneProgram(pane) {
|
|
|
2969
3031
|
return { command, source: 'tmux-pane', rawArgs: null };
|
|
2970
3032
|
}
|
|
2971
3033
|
try {
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
3034
|
+
let rows;
|
|
3035
|
+
if (process.platform === 'linux' && pane.tty) {
|
|
3036
|
+
const paneTty = pane.tty.replace(/^\/dev\//, '');
|
|
3037
|
+
rows = (await getProcessSnapshot()).filter((row) => row.tty === paneTty);
|
|
3038
|
+
}
|
|
3039
|
+
else {
|
|
3040
|
+
const psArgs = pane.tty
|
|
3041
|
+
? ['-t', pane.tty.replace(/^\/dev\//, ''), '-o', 'pid=,ppid=,pgid=,tpgid=,stat=,comm=,args=']
|
|
3042
|
+
: ['-o', 'pid=,ppid=,pgid=,tpgid=,stat=,comm=,args='];
|
|
3043
|
+
const { stdout } = await execFileAsync('ps', psArgs, { timeout: 3000, maxBuffer: 512 * 1024 });
|
|
3044
|
+
rows = stdout
|
|
3045
|
+
.split('\n')
|
|
3046
|
+
.map((line) => line.trim())
|
|
3047
|
+
.filter(Boolean)
|
|
3048
|
+
.map((line) => {
|
|
3049
|
+
const match = line.match(/^(\d+)\s+(\d+)\s+(-?\d+)\s+(-?\d+)\s+(\S+)\s+(\S+)\s+(.+)$/);
|
|
3050
|
+
if (!match)
|
|
3051
|
+
return null;
|
|
3052
|
+
return {
|
|
3053
|
+
pid: Number.parseInt(match[1] || '0', 10),
|
|
3054
|
+
ppid: Number.parseInt(match[2] || '0', 10),
|
|
3055
|
+
pgid: Number.parseInt(match[3] || '0', 10),
|
|
3056
|
+
tpgid: Number.parseInt(match[4] || '0', 10),
|
|
3057
|
+
stat: match[5] || '',
|
|
3058
|
+
comm: match[6] || '',
|
|
3059
|
+
args: match[7]?.trim() || '',
|
|
3060
|
+
};
|
|
3061
|
+
})
|
|
3062
|
+
.filter((row) => row !== null);
|
|
3063
|
+
}
|
|
2997
3064
|
const selected = selectTmuxForegroundProgram({
|
|
2998
3065
|
panePid: pane.pid,
|
|
2999
3066
|
rows,
|
|
@@ -3463,6 +3530,115 @@ async function getTmuxLayout(sessionName) {
|
|
|
3463
3530
|
inCopyMode: paneInMode === '1',
|
|
3464
3531
|
};
|
|
3465
3532
|
}
|
|
3533
|
+
let tmuxLayoutsSnapshot = null;
|
|
3534
|
+
let tmuxLayoutsSnapshotPromise = null;
|
|
3535
|
+
async function buildTmuxLayoutsSnapshot() {
|
|
3536
|
+
const [sessionsRaw, windowsRaw, panesRaw] = await Promise.all([
|
|
3537
|
+
runTmux([
|
|
3538
|
+
'list-sessions',
|
|
3539
|
+
'-F',
|
|
3540
|
+
`#{session_id}${TMUX_DELIMITER}#{session_name}${TMUX_DELIMITER}#{window_id}${TMUX_DELIMITER}#{pane_id}${TMUX_DELIMITER}#{pane_in_mode}`,
|
|
3541
|
+
]),
|
|
3542
|
+
runTmux([
|
|
3543
|
+
'list-windows',
|
|
3544
|
+
'-a',
|
|
3545
|
+
'-F',
|
|
3546
|
+
`#{session_name}${TMUX_DELIMITER}#{window_id}${TMUX_DELIMITER}#{window_name}${TMUX_DELIMITER}#{window_index}${TMUX_DELIMITER}#{window_active}`,
|
|
3547
|
+
]),
|
|
3548
|
+
runTmux([
|
|
3549
|
+
'list-panes',
|
|
3550
|
+
'-a',
|
|
3551
|
+
'-F',
|
|
3552
|
+
`#{session_name}${TMUX_DELIMITER}#{window_id}${TMUX_DELIMITER}#{pane_id}${TMUX_DELIMITER}#{pane_index}${TMUX_DELIMITER}#{pane_active}${TMUX_DELIMITER}#{pane_width}${TMUX_DELIMITER}#{pane_height}${TMUX_DELIMITER}#{pane_top}${TMUX_DELIMITER}#{pane_left}${TMUX_DELIMITER}#{pane_current_command}${TMUX_DELIMITER}#{pane_pid}${TMUX_DELIMITER}#{pane_tty}${TMUX_DELIMITER}#{pane_title}${TMUX_DELIMITER}#{pane_current_path}`,
|
|
3553
|
+
]),
|
|
3554
|
+
]);
|
|
3555
|
+
const layouts = new Map();
|
|
3556
|
+
for (const line of sessionsRaw.trim().split('\n')) {
|
|
3557
|
+
if (!line)
|
|
3558
|
+
continue;
|
|
3559
|
+
const row = parseDelimitedRow(line, 5);
|
|
3560
|
+
if (!row)
|
|
3561
|
+
continue;
|
|
3562
|
+
const [sessionId, sessionName, activeWindowId, activePaneId, paneInMode] = row;
|
|
3563
|
+
layouts.set(sessionName, {
|
|
3564
|
+
sessionId,
|
|
3565
|
+
sessionName,
|
|
3566
|
+
windows: [],
|
|
3567
|
+
activeWindowId,
|
|
3568
|
+
activePaneId,
|
|
3569
|
+
inCopyMode: paneInMode === '1',
|
|
3570
|
+
});
|
|
3571
|
+
}
|
|
3572
|
+
const windowsById = new Map();
|
|
3573
|
+
for (const line of windowsRaw.trim().split('\n')) {
|
|
3574
|
+
if (!line)
|
|
3575
|
+
continue;
|
|
3576
|
+
const row = parseDelimitedRow(line, 5);
|
|
3577
|
+
if (!row)
|
|
3578
|
+
continue;
|
|
3579
|
+
const [sessionName, windowId, windowName, windowIndexRaw, windowActiveRaw] = row;
|
|
3580
|
+
const layout = layouts.get(sessionName);
|
|
3581
|
+
if (!layout)
|
|
3582
|
+
continue;
|
|
3583
|
+
const window = {
|
|
3584
|
+
id: windowId,
|
|
3585
|
+
name: windowName || '',
|
|
3586
|
+
index: parseInt(windowIndexRaw || '0', 10),
|
|
3587
|
+
active: windowActiveRaw === '1',
|
|
3588
|
+
panes: [],
|
|
3589
|
+
};
|
|
3590
|
+
layout.windows.push(window);
|
|
3591
|
+
windowsById.set(`${sessionName}\0${windowId}`, window);
|
|
3592
|
+
}
|
|
3593
|
+
for (const line of panesRaw.trim().split('\n')) {
|
|
3594
|
+
if (!line)
|
|
3595
|
+
continue;
|
|
3596
|
+
const row = parseDelimitedRow(line, 14);
|
|
3597
|
+
if (!row)
|
|
3598
|
+
continue;
|
|
3599
|
+
const [sessionName, windowId, paneId, paneIndexRaw, paneActiveRaw, widthRaw, heightRaw, topRaw, leftRaw, command, pidRaw, tty, title, currentPath] = row;
|
|
3600
|
+
const window = windowsById.get(`${sessionName}\0${windowId}`);
|
|
3601
|
+
if (!window)
|
|
3602
|
+
continue;
|
|
3603
|
+
window.panes.push({
|
|
3604
|
+
id: paneId,
|
|
3605
|
+
index: parseInt(paneIndexRaw || '0', 10),
|
|
3606
|
+
active: paneActiveRaw === '1',
|
|
3607
|
+
width: parseInt(widthRaw || '0', 10),
|
|
3608
|
+
height: parseInt(heightRaw || '0', 10),
|
|
3609
|
+
top: parseInt(topRaw || '0', 10),
|
|
3610
|
+
left: parseInt(leftRaw || '0', 10),
|
|
3611
|
+
command: command || '',
|
|
3612
|
+
pid: parseInt(pidRaw || '0', 10) || 0,
|
|
3613
|
+
tty: tty || '',
|
|
3614
|
+
title: title || '',
|
|
3615
|
+
currentPath: currentPath || '',
|
|
3616
|
+
});
|
|
3617
|
+
}
|
|
3618
|
+
return layouts;
|
|
3619
|
+
}
|
|
3620
|
+
async function getCachedTmuxLayout(sessionName) {
|
|
3621
|
+
if (tmuxLayoutsSnapshot && Date.now() - tmuxLayoutsSnapshot.fetchedAt < TMUX_LAYOUT_CACHE_TTL_MS) {
|
|
3622
|
+
const cached = tmuxLayoutsSnapshot.layouts.get(sessionName);
|
|
3623
|
+
if (cached)
|
|
3624
|
+
return cached;
|
|
3625
|
+
}
|
|
3626
|
+
if (!tmuxLayoutsSnapshotPromise) {
|
|
3627
|
+
tmuxLayoutsSnapshotPromise = buildTmuxLayoutsSnapshot().then((layouts) => {
|
|
3628
|
+
tmuxLayoutsSnapshot = { layouts, fetchedAt: Date.now() };
|
|
3629
|
+
return layouts;
|
|
3630
|
+
}).finally(() => {
|
|
3631
|
+
tmuxLayoutsSnapshotPromise = null;
|
|
3632
|
+
});
|
|
3633
|
+
}
|
|
3634
|
+
const layouts = await tmuxLayoutsSnapshotPromise;
|
|
3635
|
+
const layout = layouts.get(sessionName);
|
|
3636
|
+
if (layout)
|
|
3637
|
+
return layout;
|
|
3638
|
+
// A just-created session can race the global snapshot. Keep that rare path
|
|
3639
|
+
// correct without returning stale metadata for the new terminal.
|
|
3640
|
+
return getTmuxLayout(sessionName);
|
|
3641
|
+
}
|
|
3466
3642
|
async function getRestoreHistory(sessionId, session) {
|
|
3467
3643
|
// In tmux mode, scrollback belongs to tmux itself rather than the app layer,
|
|
3468
3644
|
// so capture the active pane to rebuild the visible screen on refresh.
|
|
@@ -5650,7 +5826,7 @@ router.get('/:sessionId/stream', async (req, res) => {
|
|
|
5650
5826
|
session.activeProgram = ap;
|
|
5651
5827
|
}
|
|
5652
5828
|
else if (session.mode === 'tmux' && session.tmuxSessionName) {
|
|
5653
|
-
const layout = await
|
|
5829
|
+
const layout = await getCachedTmuxLayout(session.tmuxSessionName);
|
|
5654
5830
|
const activePane = getActivePaneFromLayout(layout);
|
|
5655
5831
|
const ap = activePane
|
|
5656
5832
|
? await resolveTmuxPaneProgram(activePane)
|
|
@@ -5727,7 +5903,7 @@ router.get('/:sessionId/stream', async (req, res) => {
|
|
|
5727
5903
|
return;
|
|
5728
5904
|
}
|
|
5729
5905
|
try {
|
|
5730
|
-
const layout = await
|
|
5906
|
+
const layout = await getCachedTmuxLayout(session.tmuxSessionName);
|
|
5731
5907
|
// Resolve the active program — try ps-based detection for generic commands
|
|
5732
5908
|
const activePane = getActivePaneFromLayout(layout);
|
|
5733
5909
|
if (activePane) {
|
|
@@ -6336,7 +6512,7 @@ export function handleTerminalWebSocket(ws, sessionId, clientId, options = {}) {
|
|
|
6336
6512
|
session.activeProgram = ap;
|
|
6337
6513
|
}
|
|
6338
6514
|
else if (session.mode === 'tmux' && session.tmuxSessionName) {
|
|
6339
|
-
const layout = await
|
|
6515
|
+
const layout = await getCachedTmuxLayout(session.tmuxSessionName);
|
|
6340
6516
|
const ap = getActiveProgramFromTmuxLayout(layout);
|
|
6341
6517
|
if (ap)
|
|
6342
6518
|
session.activeProgram = ap;
|
|
@@ -6419,7 +6595,7 @@ export function handleTerminalWebSocket(ws, sessionId, clientId, options = {}) {
|
|
|
6419
6595
|
if (ws.readyState !== ws.OPEN)
|
|
6420
6596
|
return;
|
|
6421
6597
|
try {
|
|
6422
|
-
const layout = await
|
|
6598
|
+
const layout = await getCachedTmuxLayout(session.tmuxSessionName);
|
|
6423
6599
|
// Update active program — try ps-based detection for generic commands
|
|
6424
6600
|
const activePane = getActivePaneFromLayout(layout);
|
|
6425
6601
|
let ap = null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "termdock",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.99",
|
|
4
4
|
"description": "A complete web-based terminal application with modern UI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": ".desktop-dist/main.js",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"dev:client": "vite",
|
|
25
25
|
"dev:server": "cross-env PORT=9835 tsx watch src/server/entry.ts",
|
|
26
26
|
"build": "npm run clean && npm run build:client && npm run build:server && npm run build:runtime-manifest",
|
|
27
|
-
"build:desktop": "node scripts/prepare-desktop-tokens.mjs && tsc -p tsconfig.desktop.json",
|
|
27
|
+
"build:desktop": "node scripts/prepare-desktop-tokens.mjs && tsc -p tsconfig.desktop.json && esbuild desktop/preload.cts --bundle --platform=node --format=cjs --external:electron --outfile=.desktop-dist/preload.cjs",
|
|
28
28
|
"desktop:dev": "npm run build && npm run build:desktop && electron-forge start",
|
|
29
29
|
"desktop:prepare": "npm run build && npm run build:desktop && node scripts/prepare-desktop-runtime.mjs",
|
|
30
30
|
"desktop:make": "node scripts/ensure-macos-appdmg.mjs && npm run desktop:prepare && electron-forge make",
|
|
@@ -111,6 +111,7 @@
|
|
|
111
111
|
"concurrently": "^8.2.0",
|
|
112
112
|
"cross-env": "^10.1.0",
|
|
113
113
|
"electron": "^43.2.0",
|
|
114
|
+
"esbuild": "^0.27.7",
|
|
114
115
|
"eslint": "^9.39.2",
|
|
115
116
|
"jsdom": "^24.0.0",
|
|
116
117
|
"postcss": "^8.4.0",
|
package/runtime-manifest.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"packageName": "termdock",
|
|
4
|
-
"version": "1.4.
|
|
4
|
+
"version": "1.4.99",
|
|
5
5
|
"runtimeProtocolVersion": 1,
|
|
6
6
|
"minimumDesktopVersion": "1.4.46",
|
|
7
7
|
"nodeMajor": 22,
|
|
8
8
|
"dependencyHash": "sha256-emuXnf9w0kJF3ksMJVvdI0u8oLHKwZJ2/OzHBAXulZs=",
|
|
9
|
-
"serverBundleHash": "sha256-
|
|
9
|
+
"serverBundleHash": "sha256-HoYNZvRYLVItcMdTryy29alYQdle/LCxvw2bx4YzWfk=",
|
|
10
10
|
"entrypoint": "dist/server/cli.js",
|
|
11
11
|
"clientEntrypoint": "dist/client/index.html"
|
|
12
12
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{U as a,C as n}from"./mermaid.core-D4Yy5OxZ.js";const t=(r,o)=>a.lang.round(n.parse(r)[o]);export{t as c};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as a,a as s,c as t,C as e}from"./chunk-V7JOEXUC-BRo0103H.js";import{b as i}from"./mermaid.core-D4Yy5OxZ.js";import"./chunk-5VM5RSS4-CVk_pzpf.js";import"./chunk-XXDRQBXY-BuWiQ0uq.js";import"./chunk-VR4S4FIN-CW-wSkDE.js";import"./chunk-32BRIVSS-B1dA4l5A.js";import"./purify.es-CC4Brkr_.js";import"./index-BuMMW0wS.js";import"./react-XDCjal2T.js";import"./dnd-BCFAPC0L.js";import"./terminal-C7X98Amf.js";var g={parser:t,get db(){return new e},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{g as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as a,a as s,c as t,C as e}from"./chunk-V7JOEXUC-BRo0103H.js";import{b as i}from"./mermaid.core-D4Yy5OxZ.js";import"./chunk-5VM5RSS4-CVk_pzpf.js";import"./chunk-XXDRQBXY-BuWiQ0uq.js";import"./chunk-VR4S4FIN-CW-wSkDE.js";import"./chunk-32BRIVSS-B1dA4l5A.js";import"./purify.es-CC4Brkr_.js";import"./index-BuMMW0wS.js";import"./react-XDCjal2T.js";import"./dnd-BCFAPC0L.js";import"./terminal-C7X98Amf.js";var g={parser:t,get db(){return new e},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{g as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{b as r,a as e,s as a,S as i}from"./chunk-EX3LRPZG-B46zhCa0.js";import{b as s}from"./mermaid.core-D4Yy5OxZ.js";import"./chunk-XXDRQBXY-BuWiQ0uq.js";import"./chunk-VR4S4FIN-CW-wSkDE.js";import"./chunk-32BRIVSS-B1dA4l5A.js";import"./purify.es-CC4Brkr_.js";import"./index-BuMMW0wS.js";import"./react-XDCjal2T.js";import"./dnd-BCFAPC0L.js";import"./terminal-C7X98Amf.js";var g={parser:a,get db(){return new i(2)},renderer:e,styles:r,init:s(t=>{t.state||(t.state={}),t.state.arrowMarkerAbsolute=t.arrowMarkerAbsolute},"init")};export{g as diagram};
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import{c as r,s as e}from"./flowDiagram-23GEKE2U-ggl4zaY9.js";import{b as o}from"./mermaid.core-D4Yy5OxZ.js";import"./chunk-5VM5RSS4-CVk_pzpf.js";import"./chunk-XXDRQBXY-BuWiQ0uq.js";import"./chunk-VR4S4FIN-CW-wSkDE.js";import"./chunk-32BRIVSS-B1dA4l5A.js";import"./purify.es-CC4Brkr_.js";import"./channel-DPt818Jx.js";import"./index-BuMMW0wS.js";import"./react-XDCjal2T.js";import"./dnd-BCFAPC0L.js";import"./terminal-C7X98Amf.js";var a=o(t=>`${e(t)}
|
|
2
|
-
.swimlane.cluster rect {
|
|
3
|
-
stroke: ${t.clusterBorder} !important;
|
|
4
|
-
}
|
|
5
|
-
[data-look="neo"].cluster rect {
|
|
6
|
-
filter: none;
|
|
7
|
-
}
|
|
8
|
-
`,"getStyles"),m=a,v=r({defaultLayout:"swimlane",styles:m});export{v as diagram};
|