myrlin-workbook 0.9.21 → 0.9.23
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/logs/server.pid +1 -1
- package/package.json +1 -1
- package/src/web/public/app.js +45 -4
package/logs/server.pid
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
68900
|
package/package.json
CHANGED
package/src/web/public/app.js
CHANGED
|
@@ -9058,6 +9058,36 @@ class CWMApp {
|
|
|
9058
9058
|
TERMINAL GRID VIEW
|
|
9059
9059
|
═══════════════════════════════════════════════════════════ */
|
|
9060
9060
|
|
|
9061
|
+
/**
|
|
9062
|
+
* Show a disconnected placeholder in a terminal pane slot.
|
|
9063
|
+
* Preserves session info in terminalPanes[] for layout saves.
|
|
9064
|
+
* Click reconnects via openTerminalInPane().
|
|
9065
|
+
*/
|
|
9066
|
+
_showDisconnectedPlaceholder(slotIdx, sessionId, sessionName, spawnOpts) {
|
|
9067
|
+
// Store placeholder so saveCurrentGroupPanes preserves the mapping
|
|
9068
|
+
this.terminalPanes[slotIdx] = { sessionId, sessionName, spawnOpts: spawnOpts || {}, _disconnected: true };
|
|
9069
|
+
const paneEl = document.getElementById(`term-pane-${slotIdx}`);
|
|
9070
|
+
if (!paneEl) return;
|
|
9071
|
+
paneEl.hidden = false;
|
|
9072
|
+
paneEl.classList.remove('terminal-pane-empty');
|
|
9073
|
+
const titleEl = paneEl.querySelector('.terminal-pane-title');
|
|
9074
|
+
if (titleEl) titleEl.textContent = sessionName || sessionId;
|
|
9075
|
+
const closeBtn = paneEl.querySelector('.terminal-pane-close');
|
|
9076
|
+
if (closeBtn) closeBtn.hidden = false;
|
|
9077
|
+
const container = document.getElementById(`term-container-${slotIdx}`);
|
|
9078
|
+
if (container) {
|
|
9079
|
+
container.innerHTML = `<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:8px;color:var(--overlay0);font-size:13px;cursor:pointer;" class="reconnect-prompt">
|
|
9080
|
+
<span style="font-size:20px;">🔌</span>
|
|
9081
|
+
<span>${this.escapeHtml(sessionName || sessionId)}</span>
|
|
9082
|
+
<span style="font-size:11px;opacity:0.7;">Click to connect</span>
|
|
9083
|
+
</div>`;
|
|
9084
|
+
container.querySelector('.reconnect-prompt').addEventListener('click', () => {
|
|
9085
|
+
this.openTerminalInPane(slotIdx, sessionId, sessionName, spawnOpts);
|
|
9086
|
+
});
|
|
9087
|
+
}
|
|
9088
|
+
this.updateTerminalGridLayout();
|
|
9089
|
+
}
|
|
9090
|
+
|
|
9061
9091
|
openTerminalInPane(slotIdx, sessionId, sessionName, spawnOpts) {
|
|
9062
9092
|
// Check localStorage for a previously saved name for this session
|
|
9063
9093
|
const savedTitle = this.getProjectSessionTitle(sessionId);
|
|
@@ -9065,13 +9095,19 @@ class CWMApp {
|
|
|
9065
9095
|
sessionName = savedTitle;
|
|
9066
9096
|
}
|
|
9067
9097
|
console.log('[DnD] openTerminalInPane slot:', slotIdx, 'session:', sessionId, 'name:', sessionName);
|
|
9098
|
+
// If the target slot has a disconnected placeholder, clear it so we reuse the slot.
|
|
9099
|
+
// Without this, "Click to connect" opens in a different pane instead of replacing.
|
|
9100
|
+
if (this.terminalPanes[slotIdx] && this.terminalPanes[slotIdx]._disconnected) {
|
|
9101
|
+
this.terminalPanes[slotIdx] = null;
|
|
9102
|
+
}
|
|
9103
|
+
|
|
9068
9104
|
// If the target slot already has an active terminal, find the next empty slot
|
|
9069
9105
|
if (this.terminalPanes[slotIdx]) {
|
|
9070
9106
|
const emptySlot = this.terminalPanes.findIndex(p => p === null);
|
|
9071
9107
|
if (emptySlot !== -1) {
|
|
9072
9108
|
slotIdx = emptySlot;
|
|
9073
9109
|
} else {
|
|
9074
|
-
// All slots full
|
|
9110
|
+
// All slots full, replace the target slot
|
|
9075
9111
|
this.terminalPanes[slotIdx].dispose();
|
|
9076
9112
|
this.terminalPanes[slotIdx] = null;
|
|
9077
9113
|
}
|
|
@@ -12048,10 +12084,12 @@ class CWMApp {
|
|
|
12048
12084
|
const uploadBtn = paneEl.querySelector('.terminal-pane-upload');
|
|
12049
12085
|
if (uploadBtn) uploadBtn.hidden = false;
|
|
12050
12086
|
}
|
|
12051
|
-
// Reattach xterm DOM
|
|
12087
|
+
// Reattach xterm DOM, or re-render placeholder for disconnected panes
|
|
12052
12088
|
if (cached.domFragments[i]) {
|
|
12053
12089
|
const termContainer = document.getElementById(`term-container-${i}`);
|
|
12054
12090
|
if (termContainer) termContainer.appendChild(cached.domFragments[i]);
|
|
12091
|
+
} else if (cached.panes[i]._disconnected) {
|
|
12092
|
+
this._showDisconnectedPlaceholder(i, cached.panes[i].sessionId, cached.panes[i].sessionName, cached.panes[i].spawnOpts);
|
|
12055
12093
|
}
|
|
12056
12094
|
}
|
|
12057
12095
|
}
|
|
@@ -12071,12 +12109,15 @@ class CWMApp {
|
|
|
12071
12109
|
}
|
|
12072
12110
|
});
|
|
12073
12111
|
} else {
|
|
12074
|
-
// No cache
|
|
12112
|
+
// No cache: show disconnected placeholders instead of spawning all PTYs.
|
|
12113
|
+
// Each PTY spawns a Claude process (~150MB), so eagerly connecting all
|
|
12114
|
+
// panes across all tab groups would consume gigabytes of system memory.
|
|
12115
|
+
// Users click a pane to connect on demand.
|
|
12075
12116
|
const group = this._tabGroups.find(g => g.id === groupId);
|
|
12076
12117
|
if (group && group.panes) {
|
|
12077
12118
|
group.panes.forEach(p => {
|
|
12078
12119
|
if (p.sessionId && !this.terminalPanes[p.slot]) {
|
|
12079
|
-
this.
|
|
12120
|
+
this._showDisconnectedPlaceholder(p.slot, p.sessionId, p.sessionName || 'Terminal', p.spawnOpts || {});
|
|
12080
12121
|
}
|
|
12081
12122
|
});
|
|
12082
12123
|
}
|