mirror of
https://github.com/garrytan/gstack.git
synced 2026-05-09 06:45:46 +02:00
feat(extension): Terminal-only sidebar — auth fix, UX polish, chat rip
The chat queue path is gone. The Chrome side panel is now just an
interactive claude PTY in xterm.js. Activity / Refs / Inspector still
exist behind the `debug` toggle in the footer.
Three threads of change, all from dogfood iteration on top of
cc-pty-import:
1. fix(server): cross-port WS auth via Sec-WebSocket-Protocol
- Browsers can't set Authorization on a WebSocket upgrade. We had
been minting an HttpOnly gstack_pty cookie via /pty-session, but
SameSite=Strict cookies don't survive the cross-port jump from
server.ts:34567 to the agent's random port from a chrome-extension
origin. The WS opened then immediately closed → "Session ended."
- /pty-session now also returns ptySessionToken in the JSON body.
- Extension calls `new WebSocket(url, [`gstack-pty.<token>`])`.
Browser sends Sec-WebSocket-Protocol on the upgrade.
- Agent reads the protocol header, validates against validTokens,
and MUST echo the protocol back (Chromium closes the connection
immediately if a server doesn't pick one of the offered protocols).
- Cookie path is kept as a fallback for non-browser callers (curl,
integration tests).
- New integration test exercises the full protocol-auth round-trip
via raw fetch+Upgrade so a future regression of this exact class
fails in CI.
2. fix(extension): UX polish on the Terminal pane
- Eager auto-connect when the sidebar opens — no "Press any key to
start" friction every reload.
- Always-visible ↻ Restart button in the terminal toolbar (not
gated on the ENDED state) so the user can force a fresh claude
mid-session.
- MutationObserver on #tab-terminal's class attribute drives a
fitAddon.fit() + term.refresh() when the pane becomes visible
again — xterm doesn't auto-redraw after display:none → display:flex.
3. feat(extension): rip the chat tab + sidebar-agent.ts
- Sidebar is Terminal-only. No more Terminal | Chat primary nav.
- sidebar-agent.ts deleted. /sidebar-command, /sidebar-chat,
/sidebar-agent/event, /sidebar-tabs* and friends all deleted.
- The pickSidebarModel router (sonnet vs opus) is gone — the live
PTY uses whatever model the user's `claude` CLI is configured with.
- Quick-actions (🧹 Cleanup / 📸 Screenshot / 🍪 Cookies) survive
in the Terminal toolbar. Cleanup now injects its prompt into the
live PTY via window.gstackInjectToTerminal — no more
/sidebar-command POST. The Inspector "Send to Code" action uses
the same injection path.
- clear-chat button removed from the footer.
- sidepanel.js shed ~900 lines of chat polling, optimistic UI,
stop-agent, etc.
Net diff: -3.4k lines across 16 files. CLAUDE.md, TODOS.md, and
docs/designs/SIDEBAR_MESSAGE_FLOW.md rewritten to match. The sidebar
regression test (browse/test/sidebar-tabs.test.ts) is rewritten as 27
structural assertions locking the new layout — Terminal sole pane,
no chat input, quick-actions in toolbar, eager-connect, MutationObserver
repaint, restart helper.
This commit is contained in:
+15
-80
@@ -25,57 +25,28 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Security event banner — fires on prompt injection detection.
|
||||
Variant A from /plan-design-review 2026-04-19: centered alert-heavy,
|
||||
big red error icon, mono layer scores in expandable details. -->
|
||||
<div class="security-banner" id="security-banner" role="alert" aria-live="assertive" style="display:none">
|
||||
<button class="security-banner-close" id="security-banner-close" aria-label="Dismiss">×</button>
|
||||
<div class="security-banner-icon" aria-hidden="true">
|
||||
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
<line x1="12" y1="8" x2="12" y2="12"></line>
|
||||
<line x1="12" y1="16" x2="12.01" y2="16"></line>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="security-banner-title" id="security-banner-title">Session terminated</div>
|
||||
<div class="security-banner-subtitle" id="security-banner-subtitle">prompt injection detected</div>
|
||||
<button class="security-banner-expand" id="security-banner-expand" aria-expanded="false" aria-controls="security-banner-details">
|
||||
<span>What happened</span>
|
||||
<svg class="security-banner-chevron" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="6 9 12 15 18 9"></polyline>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="security-banner-details" id="security-banner-details" hidden>
|
||||
<div class="security-banner-section-label">SECURITY LAYERS</div>
|
||||
<div class="security-banner-layers" id="security-banner-layers"></div>
|
||||
<div class="security-banner-section-label" id="security-banner-suspect-label" hidden>SUSPECTED TEXT</div>
|
||||
<pre class="security-banner-suspect" id="security-banner-suspect" hidden></pre>
|
||||
</div>
|
||||
<div class="security-banner-actions" id="security-banner-actions" hidden>
|
||||
<button type="button" class="security-banner-btn security-banner-btn-block" id="security-banner-btn-block">Block session</button>
|
||||
<button type="button" class="security-banner-btn security-banner-btn-allow" id="security-banner-btn-allow">Allow and continue</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Browser tab bar -->
|
||||
<div class="browser-tabs" id="browser-tabs" style="display:none"></div>
|
||||
|
||||
<!-- Primary surface tabs: Terminal (default) | Chat. Activity / Refs /
|
||||
Inspector still exist as a separate debug-tabs strip below. The
|
||||
Terminal tab is default-active per /plan-eng-review Issue 1B
|
||||
(subsequently informed by codex's spawn-waste finding: PTY only
|
||||
spawns when the user types, so default-active is cheap). -->
|
||||
<nav class="primary-tabs" id="primary-tabs" role="tablist">
|
||||
<button class="primary-tab active" role="tab" data-pane="terminal" aria-selected="true">Terminal</button>
|
||||
<button class="primary-tab" role="tab" data-pane="chat" aria-selected="false">Chat</button>
|
||||
</nav>
|
||||
|
||||
<!-- Terminal Tab (default-active) -->
|
||||
<!-- Terminal pane is now the sole primary surface. Activity / Refs /
|
||||
Inspector still exist behind the `debug` toggle in the footer. -->
|
||||
<main id="tab-terminal" class="tab-content active" role="tabpanel" aria-label="Terminal">
|
||||
<!-- Toolbar with browser quick-actions on the left, Restart on the right.
|
||||
Restart is always visible so the user can force a fresh claude any
|
||||
time, not just from the ENDED state. -->
|
||||
<div class="terminal-toolbar" id="terminal-toolbar">
|
||||
<div class="terminal-toolbar-actions">
|
||||
<button id="chat-cleanup-btn" class="terminal-toolbar-btn" title="Remove ads, banners, popups">🧹 Cleanup</button>
|
||||
<button id="chat-screenshot-btn" class="terminal-toolbar-btn" title="Take a screenshot">📸 Screenshot</button>
|
||||
<button id="chat-cookies-btn" class="terminal-toolbar-btn" title="Import cookies from your browser">🍪 Cookies</button>
|
||||
</div>
|
||||
<button class="terminal-toolbar-btn" id="terminal-restart-now" title="Restart Claude Code session">↻ Restart</button>
|
||||
</div>
|
||||
<div class="terminal-bootstrap" id="terminal-bootstrap">
|
||||
<div class="terminal-bootstrap-icon">▸</div>
|
||||
<p id="terminal-bootstrap-status">Press any key to start Claude Code.</p>
|
||||
<p id="terminal-bootstrap-status">Starting Claude Code...</p>
|
||||
<p class="muted" id="terminal-bootstrap-hint">Real PTY. Real terminal. Real claude.</p>
|
||||
<pre id="loading-debug" class="muted" style="font-size:11px; font-family:'JetBrains Mono',monospace; white-space:pre-wrap; margin-top:8px; color:#71717A;"></pre>
|
||||
</div>
|
||||
<div class="terminal-install-card" id="terminal-install-card" style="display:none">
|
||||
<p><strong>Claude Code not found</strong></p>
|
||||
@@ -89,22 +60,6 @@
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Chat Tab (the existing claude -p one-shot chat path; preserved verbatim) -->
|
||||
<main id="tab-chat" class="tab-content" role="tabpanel" aria-label="Chat">
|
||||
<div class="chat-messages" id="chat-messages">
|
||||
<div class="chat-loading" id="chat-loading">
|
||||
<div class="chat-loading-spinner"></div>
|
||||
<p id="loading-status">Looking for browse server...</p>
|
||||
<pre id="loading-debug" class="muted" style="font-size:11px; font-family:'JetBrains Mono',monospace; white-space:pre-wrap; margin-top:8px; color:#71717A;"></pre>
|
||||
</div>
|
||||
<div class="chat-welcome" id="chat-welcome" style="display:none">
|
||||
<div class="chat-welcome-icon">G</div>
|
||||
<p>Send a message to Claude Code.</p>
|
||||
<p class="muted">Your agent will see it and act on it.</p>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Debug: Activity Tab (hidden by default) -->
|
||||
<main id="tab-activity" class="tab-content" role="log" aria-live="polite">
|
||||
<div class="empty-state" id="empty-state">
|
||||
@@ -204,30 +159,10 @@
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Experimental chat banner (shown when chatEnabled) -->
|
||||
<div id="experimental-banner" class="experimental-banner" style="display: none;">
|
||||
Browser co-pilot — controls this browser, reports back to your workspace
|
||||
</div>
|
||||
|
||||
<!-- Quick Actions Toolbar -->
|
||||
<div class="quick-actions" id="quick-actions">
|
||||
<button id="chat-cleanup-btn" class="quick-action-btn" title="Remove ads, banners, popups">🧹 Cleanup</button>
|
||||
<button id="chat-screenshot-btn" class="quick-action-btn" title="Take a screenshot">📸 Screenshot</button>
|
||||
<button id="chat-cookies-btn" class="quick-action-btn" title="Import cookies from your browser">🍪 Cookies</button>
|
||||
</div>
|
||||
|
||||
<!-- Command Bar -->
|
||||
<div class="command-bar">
|
||||
<button class="stop-btn" id="stop-agent-btn" title="Stop agent" style="display: none;">■</button>
|
||||
<input type="text" class="command-input" id="command-input" placeholder="Ask about this page..." autocomplete="off" spellcheck="false">
|
||||
<button class="send-btn" id="send-btn" title="Send">↑</button>
|
||||
</div>
|
||||
|
||||
<!-- Footer with connection + debug toggle -->
|
||||
<footer>
|
||||
<div class="footer-left">
|
||||
<button class="debug-toggle" id="debug-toggle" title="Toggle debug panels">debug</button>
|
||||
<button class="footer-btn" id="clear-chat" title="Clear chat">clear</button>
|
||||
<button class="footer-btn" id="reload-sidebar" title="Reload sidebar">reload</button>
|
||||
</div>
|
||||
<div class="footer-right">
|
||||
|
||||
Reference in New Issue
Block a user