myrlin-workbook 0.9.22 → 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/logs/server.pid CHANGED
@@ -1 +1 @@
1
- 67796
1
+ 68900
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myrlin-workbook",
3
- "version": "0.9.22",
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
@@ -9101,7 +9080,7 @@ class CWMApp {
9101
9080
  if (emptySlot !== -1) {
9102
9081
  slotIdx = emptySlot;
9103
9082
  } else {
9104
- // All slots full - replace the target slot
9083
+ // All slots full, replace the target slot
9105
9084
  this.terminalPanes[slotIdx].dispose();
9106
9085
  this.terminalPanes[slotIdx] = null;
9107
9086
  }
@@ -9140,33 +9119,21 @@ class CWMApp {
9140
9119
  });
9141
9120
  };
9142
9121
 
9143
- // On fatal connection error, show disconnected state but PRESERVE pane info.
9144
- // The session ID, name, and spawnOpts stay in the layout so the user can
9145
- // reconnect by clicking, and layout saves don't lose the session mapping.
9122
+ // On fatal connection error, close the pane cleanly.
9146
9123
  tp.onFatalError = (failedSessionId) => {
9147
9124
  const idx = this.terminalPanes.indexOf(tp);
9148
9125
  if (idx === -1) return;
9149
- // Stash session info before disposing so we can offer reconnect
9150
- const sid = tp.sessionId;
9151
- const sName = tp.sessionName;
9152
- const opts = { ...(tp.spawnOpts || {}) };
9153
9126
  tp.dispose();
9154
- // Replace the TerminalPane with a lightweight placeholder that preserves
9155
- // the session mapping for layout saves (saveCurrentGroupPanes reads these).
9156
- this.terminalPanes[idx] = { sessionId: sid, sessionName: sName, spawnOpts: opts, _disconnected: true };
9157
- const paneEl = document.getElementById(`term-pane-${idx}`);
9158
- if (!paneEl) return;
9159
- const container = document.getElementById(`term-container-${idx}`);
9160
- if (container) {
9161
- 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">
9162
- <span style="font-size:20px;">&#x26A0;</span>
9163
- <span>${this.escapeHtml(sName || sid)}</span>
9164
- <span style="font-size:11px;opacity:0.7;">Disconnected. Click to reconnect.</span>
9165
- </div>`;
9166
- container.querySelector('.reconnect-prompt').addEventListener('click', () => {
9167
- this.openTerminalInPane(idx, sid, sName, opts);
9168
- });
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;
9169
9135
  }
9136
+ this.updateTerminalGridLayout();
9170
9137
  };
9171
9138
 
9172
9139
  // Enable auto-trust if the setting is on
@@ -12082,8 +12049,9 @@ class CWMApp {
12082
12049
  if (cached.domFragments[i]) {
12083
12050
  const termContainer = document.getElementById(`term-container-${i}`);
12084
12051
  if (termContainer) termContainer.appendChild(cached.domFragments[i]);
12085
- } else if (cached.panes[i]._disconnected) {
12086
- 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);
12087
12055
  }
12088
12056
  }
12089
12057
  }
@@ -12103,15 +12071,12 @@ class CWMApp {
12103
12071
  }
12104
12072
  });
12105
12073
  } else {
12106
- // No cache: show disconnected placeholders instead of spawning all PTYs.
12107
- // Each PTY spawns a Claude process (~150MB), so eagerly connecting all
12108
- // panes across all tab groups would consume gigabytes of system memory.
12109
- // Users click a pane to connect on demand.
12074
+ // No cache, create fresh connections (first time opening this group)
12110
12075
  const group = this._tabGroups.find(g => g.id === groupId);
12111
12076
  if (group && group.panes) {
12112
12077
  group.panes.forEach(p => {
12113
12078
  if (p.sessionId && !this.terminalPanes[p.slot]) {
12114
- this._showDisconnectedPlaceholder(p.slot, p.sessionId, p.sessionName || 'Terminal', p.spawnOpts || {});
12079
+ this.openTerminalInPane(p.slot, p.sessionId, p.sessionName || 'Terminal', p.spawnOpts || {});
12115
12080
  }
12116
12081
  });
12117
12082
  }
@@ -12141,9 +12106,7 @@ class CWMApp {
12141
12106
  group.panes = [];
12142
12107
  for (let i = 0; i < CWMApp.MAX_PANES; i++) {
12143
12108
  const tp = this.terminalPanes[i];
12144
- // Save both live TerminalPanes and disconnected placeholders.
12145
- // Disconnected placeholders have { sessionId, sessionName, spawnOpts, _disconnected: true }
12146
- // and MUST be preserved so layout restores don't lose session mappings.
12109
+ // Save live TerminalPanes for layout restore.
12147
12110
  if (tp && tp.sessionId) {
12148
12111
  group.panes.push({
12149
12112
  slot: i,