myrlin-workbook 0.9.23 → 0.9.24

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myrlin-workbook",
3
- "version": "0.9.23",
3
+ "version": "0.9.24",
4
4
  "description": "Browser-based project manager for Claude Code sessions - session discovery, multi-terminal, cost tracking, docs, and kanban board",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -9063,30 +9063,9 @@ class CWMApp {
9063
9063
  * Preserves session info in terminalPanes[] for layout saves.
9064
9064
  * Click reconnects via openTerminalInPane().
9065
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;">&#x1F50C;</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
- }
9066
+ // _showDisconnectedPlaceholder removed in v0.9.23 - caused more issues than it solved
9067
+ // (wrong pane on reconnect, unclosable panes, broken layout). Panes now connect
9068
+ // directly on restore and close cleanly on fatal error.
9090
9069
 
9091
9070
  openTerminalInPane(slotIdx, sessionId, sessionName, spawnOpts) {
9092
9071
  // Check localStorage for a previously saved name for this session
@@ -9095,12 +9074,6 @@ class CWMApp {
9095
9074
  sessionName = savedTitle;
9096
9075
  }
9097
9076
  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
-
9104
9077
  // If the target slot already has an active terminal, find the next empty slot
9105
9078
  if (this.terminalPanes[slotIdx]) {
9106
9079
  const emptySlot = this.terminalPanes.findIndex(p => p === null);
@@ -9146,33 +9119,21 @@ class CWMApp {
9146
9119
  });
9147
9120
  };
9148
9121
 
9149
- // On fatal connection error, show disconnected state but PRESERVE pane info.
9150
- // The session ID, name, and spawnOpts stay in the layout so the user can
9151
- // reconnect by clicking, and layout saves don't lose the session mapping.
9122
+ // On fatal connection error, close the pane cleanly.
9152
9123
  tp.onFatalError = (failedSessionId) => {
9153
9124
  const idx = this.terminalPanes.indexOf(tp);
9154
9125
  if (idx === -1) return;
9155
- // Stash session info before disposing so we can offer reconnect
9156
- const sid = tp.sessionId;
9157
- const sName = tp.sessionName;
9158
- const opts = { ...(tp.spawnOpts || {}) };
9159
9126
  tp.dispose();
9160
- // Replace the TerminalPane with a lightweight placeholder that preserves
9161
- // the session mapping for layout saves (saveCurrentGroupPanes reads these).
9162
- this.terminalPanes[idx] = { sessionId: sid, sessionName: sName, spawnOpts: opts, _disconnected: true };
9163
- const paneEl = document.getElementById(`term-pane-${idx}`);
9164
- if (!paneEl) return;
9165
- const container = document.getElementById(`term-container-${idx}`);
9166
- if (container) {
9167
- 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">
9168
- <span style="font-size:20px;">&#x26A0;</span>
9169
- <span>${this.escapeHtml(sName || sid)}</span>
9170
- <span style="font-size:11px;opacity:0.7;">Disconnected. Click to reconnect.</span>
9171
- </div>`;
9172
- container.querySelector('.reconnect-prompt').addEventListener('click', () => {
9173
- this.openTerminalInPane(idx, sid, sName, opts);
9174
- });
9127
+ this.terminalPanes[idx] = null;
9128
+ const deadPane = document.getElementById(`term-pane-${idx}`);
9129
+ if (deadPane) {
9130
+ deadPane.classList.add('terminal-pane-empty');
9131
+ const header = deadPane.querySelector('.terminal-pane-title');
9132
+ if (header) header.textContent = 'Drop a session here';
9133
+ const closeBtn2 = deadPane.querySelector('.terminal-pane-close');
9134
+ if (closeBtn2) closeBtn2.hidden = true;
9175
9135
  }
9136
+ this.updateTerminalGridLayout();
9176
9137
  };
9177
9138
 
9178
9139
  // Enable auto-trust if the setting is on
@@ -12088,8 +12049,9 @@ class CWMApp {
12088
12049
  if (cached.domFragments[i]) {
12089
12050
  const termContainer = document.getElementById(`term-container-${i}`);
12090
12051
  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);
12052
+ } else if (cached.panes[i].sessionId) {
12053
+ // Cached pane had no DOM fragment (was disconnected), reconnect directly
12054
+ this.openTerminalInPane(i, cached.panes[i].sessionId, cached.panes[i].sessionName, cached.panes[i].spawnOpts);
12093
12055
  }
12094
12056
  }
12095
12057
  }
@@ -12109,15 +12071,12 @@ class CWMApp {
12109
12071
  }
12110
12072
  });
12111
12073
  } else {
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.
12074
+ // No cache, create fresh connections (first time opening this group)
12116
12075
  const group = this._tabGroups.find(g => g.id === groupId);
12117
12076
  if (group && group.panes) {
12118
12077
  group.panes.forEach(p => {
12119
12078
  if (p.sessionId && !this.terminalPanes[p.slot]) {
12120
- this._showDisconnectedPlaceholder(p.slot, p.sessionId, p.sessionName || 'Terminal', p.spawnOpts || {});
12079
+ this.openTerminalInPane(p.slot, p.sessionId, p.sessionName || 'Terminal', p.spawnOpts || {});
12121
12080
  }
12122
12081
  });
12123
12082
  }
@@ -12147,9 +12106,7 @@ class CWMApp {
12147
12106
  group.panes = [];
12148
12107
  for (let i = 0; i < CWMApp.MAX_PANES; i++) {
12149
12108
  const tp = this.terminalPanes[i];
12150
- // Save both live TerminalPanes and disconnected placeholders.
12151
- // Disconnected placeholders have { sessionId, sessionName, spawnOpts, _disconnected: true }
12152
- // and MUST be preserved so layout restores don't lose session mappings.
12109
+ // Save live TerminalPanes for layout restore.
12153
12110
  if (tp && tp.sessionId) {
12154
12111
  group.panes.push({
12155
12112
  slot: i,