Skip to content

Commit f671979

Browse files
authored
feat(ui): per-pane start prompt (Claude/Codex) overlay in each split\n\nchore(release): v2.16.0 (#37)
1 parent cb8cc87 commit f671979

5 files changed

Lines changed: 74 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,3 +221,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
221221

222222
### Fixed
223223
- Start‑prompt (Claude/Codex) overlay now appears in multi‑pane mode: terminal container is kept available for overlays even when panes are active.
224+
## [2.16.0] - 2025-09-13
225+
226+
### Added
227+
- Per‑pane start prompt overlay: when a session is attached to a pane and hasn’t produced output yet, the pane shows a local dialog to pick the assistant (Claude/Codex), including dangerous variants.
228+
229+
### Changed
230+
- Overlays no longer rely on the single‑pane terminal; the per‑pane overlay sits within each split.
231+
232+
### Notes
233+
- UI‑only; no server/CLI changes.

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "claude-code-web",
3-
"version": "2.15.1",
3+
"version": "2.16.0",
44
"description": "Web-based interface for Claude Code CLI accessible via browser",
55
"main": "src/server.js",
66
"bin": {

src/public/panes.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ class ClaudePane {
88
this.socket = null;
99
this.sessionId = null;
1010
this.container = document.getElementById(`tileTerminal${index}`);
11+
this.hadOutput = false;
12+
this.startOverlayEl = null;
1113
}
1214

1315
async setSession(sessionId) {
@@ -17,6 +19,10 @@ class ClaudePane {
1719
if (!sessionId) return;
1820
this.ensureTerminal();
1921
await this.connect();
22+
// If newly attached and no output yet, show per-pane start overlay
23+
setTimeout(() => {
24+
if (!this.hadOutput) this.showStartOverlay();
25+
}, 50);
2026
}
2127

2228
ensureTerminal() {
@@ -61,6 +67,10 @@ class ClaudePane {
6167
if (msg.type === 'output') {
6268
const filtered = msg.data.replace(/\x1b\[\[?[IO]/g, '');
6369
this.terminal.write(filtered);
70+
if (filtered) {
71+
this.hadOutput = true;
72+
this.hideStartOverlay();
73+
}
6474
}
6575
};
6676
this.socket.onclose = () => {};
@@ -83,6 +93,50 @@ class ClaudePane {
8393
try { this.socket?.close(); } catch (_) {}
8494
this.socket = null;
8595
try { this.terminal?.clear(); } catch (_) {}
96+
this.hadOutput = false;
97+
this.hideStartOverlay();
98+
}
99+
100+
showStartOverlay() {
101+
// Build a minimal per-pane start overlay (Claude/Codex)
102+
if (!this.container) return;
103+
this.hideStartOverlay();
104+
const ov = document.createElement('div');
105+
ov.className = 'pane-start-overlay';
106+
ov.innerHTML = `
107+
<div class="pane-start-card">
108+
<h3>Select Assistant</h3>
109+
<p>Start an assistant in this session.</p>
110+
<div class="pane-start-actions">
111+
<button class="btn btn-primary" data-kind="claude">Start ${this.app?.getAlias?.('claude') || 'Claude'}</button>
112+
<button class="btn btn-danger" data-kind="claude" data-danger>Dangerous ${this.app?.getAlias?.('claude') || 'Claude'}</button>
113+
<button class="btn btn-primary" data-kind="codex">Start ${this.app?.getAlias?.('codex') || 'Codex'}</button>
114+
<button class="btn btn-danger" data-kind="codex" data-danger>Dangerous ${this.app?.getAlias?.('codex') || 'Codex'}</button>
115+
</div>
116+
</div>`;
117+
ov.querySelectorAll('button').forEach(btn => {
118+
btn.addEventListener('click', () => {
119+
const kind = btn.getAttribute('data-kind');
120+
const dangerous = btn.hasAttribute('data-danger');
121+
this.startAssistant(kind, { dangerouslySkipPermissions: dangerous });
122+
});
123+
});
124+
this.container.appendChild(ov);
125+
this.startOverlayEl = ov;
126+
}
127+
128+
hideStartOverlay() {
129+
if (this.startOverlayEl && this.startOverlayEl.parentNode) {
130+
this.startOverlayEl.remove();
131+
this.startOverlayEl = null;
132+
}
133+
}
134+
135+
startAssistant(kind = 'claude', options = {}) {
136+
if (!this.socket || this.socket.readyState !== WebSocket.OPEN) return;
137+
const type = kind === 'codex' ? 'start_codex' : 'start_claude';
138+
this.socket.send(JSON.stringify({ type, options }));
139+
this.hideStartOverlay();
86140
}
87141
}
88142

src/public/style.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -942,6 +942,13 @@ body {
942942
.pane-session-sep { height:1px; background: var(--border); margin:6px 0; }
943943
.pane-session-action { padding:8px 10px; color: var(--accent); cursor:pointer; border-radius:6px; }
944944
.pane-session-action:hover { background: var(--bg-tertiary); }
945+
946+
/* Per-pane start overlay */
947+
.pane-start-overlay { position:absolute; inset:0; background: rgba(0,0,0,.55); display:flex; align-items:center; justify-content:center; z-index: 5; }
948+
.pane-start-card { background: var(--bg-secondary); border:1px solid var(--border); border-radius:12px; padding:16px 20px; width: 90%; max-width: 420px; text-align:center; box-shadow: 0 12px 40px rgba(0,0,0,.4); }
949+
.pane-start-card h3 { margin: 0 0 6px 0; font-size: 16px; }
950+
.pane-start-card p { margin: 0 0 14px 0; color: var(--text-secondary); font-size: 13px; }
951+
.pane-start-actions { display:flex; flex-wrap: wrap; gap:8px; align-items:center; justify-content:center; }
945952
.tile-session-select { background: var(--bg-tertiary); color: var(--text-primary); border:1px solid var(--border); border-radius:6px; padding:6px 8px; font-family: var(--font-mono); font-size:12px; }
946953
.tile-close { background: transparent; border:1px solid var(--border); border-radius:4px; color: var(--text-secondary); width:26px; height:26px; display:flex; align-items:center; justify-content:center; cursor:pointer; }
947954
.tile-close:hover { background: var(--bg-tertiary); color: var(--text-primary); }

0 commit comments

Comments
 (0)