mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-04 14:39:42 +02:00
feat(split-pane): add SplitTerminalPane class for Pane B
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
aa0521602d
commit
97a1238c85
@@ -3721,6 +3721,7 @@
|
||||
<script defer src="app.js"></script>
|
||||
<script defer src="tab-rail-resize.js"></script>
|
||||
<script defer src="terminal-ui.js"></script>
|
||||
<script defer src="terminal-split.js"></script>
|
||||
<script defer src="respawn-ui.js"></script>
|
||||
<script defer src="ralph-panel.js"></script>
|
||||
<script defer src="orchestrator-panel.js"></script>
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
// src/web/public/terminal-split.js
|
||||
|
||||
/**
|
||||
* @fileoverview SplitTerminalPane — a second, independent live terminal pane
|
||||
* ("Pane B") for split-view sessions. Deliberately plainer than the primary
|
||||
* pane (this.terminal/this._ws in terminal-ui.js): no local-echo overlay, no
|
||||
* CJK IME, no touch/mobile handlers, no keyboard accessory bar. Desktop-only
|
||||
* feature by nature — see docs/superpowers/specs/2026-09-15-split-pane-sessions-design.md.
|
||||
*
|
||||
* @dependency vendor/xterm.js, vendor/xterm-addon-fit.js
|
||||
* @dependency terminal-ui.js (window.CodemanTerminalFont, codemanCurrentXtermTheme, codemanCurrentSkinIsLight)
|
||||
* @loadorder 7.5 of 16 — loaded after terminal-ui.js, before respawn-ui.js
|
||||
*/
|
||||
|
||||
(function (global) {
|
||||
class SplitTerminalPane {
|
||||
constructor(sessionId, mountEl) {
|
||||
this.sessionId = sessionId;
|
||||
this.mountEl = mountEl;
|
||||
this.terminal = null;
|
||||
this.fitAddon = null;
|
||||
this.ws = null;
|
||||
this._wsReady = false;
|
||||
}
|
||||
|
||||
connect() {
|
||||
this.terminal = new Terminal({
|
||||
theme: { ...global.codemanCurrentXtermTheme() },
|
||||
fontFamily: global.CodemanTerminalFont.resolve(),
|
||||
...global.CodemanTerminalFont.resolveWeights({}),
|
||||
fontSize: 14,
|
||||
lineHeight: 1.2,
|
||||
cursorBlink: false,
|
||||
cursorStyle: 'block',
|
||||
minimumContrastRatio: global.codemanCurrentSkinIsLight() ? 4.5 : 1,
|
||||
scrollback: 5000,
|
||||
allowTransparency: true,
|
||||
allowProposedApi: true,
|
||||
});
|
||||
|
||||
this.fitAddon = new FitAddon.FitAddon();
|
||||
this.terminal.loadAddon(this.fitAddon);
|
||||
this.terminal.open(this.mountEl);
|
||||
this.fitAddon.fit();
|
||||
|
||||
this.terminal.onData((data) => {
|
||||
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
||||
this.ws.send(JSON.stringify({ t: 'i', d: data }));
|
||||
}
|
||||
});
|
||||
|
||||
const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const url = `${proto}//${location.host}${window.CodemanBase.base}/ws/sessions/${this.sessionId}/terminal`;
|
||||
this.ws = new WebSocket(url);
|
||||
|
||||
this.ws.onopen = () => {
|
||||
this._wsReady = true;
|
||||
this._sendResize();
|
||||
};
|
||||
|
||||
this.ws.onmessage = (event) => {
|
||||
try {
|
||||
const msg = JSON.parse(event.data);
|
||||
if (msg.t === 'o') {
|
||||
this.terminal.write(msg.d);
|
||||
} else if (msg.t === 'c') {
|
||||
this.terminal.clear();
|
||||
}
|
||||
} catch {
|
||||
/* Malformed frame — ignore, matches primary pane's tolerance. */
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fit() {
|
||||
if (!this.fitAddon) return;
|
||||
this.fitAddon.fit();
|
||||
this._sendResize();
|
||||
}
|
||||
|
||||
_sendResize() {
|
||||
if (!this._wsReady || !this.fitAddon) return;
|
||||
const dims = this.fitAddon.proposeDimensions();
|
||||
if (!dims) return;
|
||||
const cols = Math.max(dims.cols, 40);
|
||||
const rows = Math.max(dims.rows, 10);
|
||||
this.ws.send(JSON.stringify({ t: 'z', c: cols, r: rows, v: 'desktop' }));
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if (this.ws) {
|
||||
this.ws.onopen = null;
|
||||
this.ws.onmessage = null;
|
||||
this.ws.close();
|
||||
this.ws = null;
|
||||
}
|
||||
if (this.terminal) {
|
||||
this.terminal.dispose();
|
||||
this.terminal = null;
|
||||
}
|
||||
this.fitAddon = null;
|
||||
}
|
||||
}
|
||||
|
||||
global.SplitTerminalPane = SplitTerminalPane;
|
||||
})(window);
|
||||
Reference in New Issue
Block a user