mirror of
https://github.com/garrytan/gstack.git
synced 2026-05-02 03:35:09 +02:00
59e0635eb5
Small "SEC" badge in the top-right of the sidepanel that reflects the
security module's current state. Three states drive color:
protected green — all layers ok (TestSavantAI + transcript + canary)
degraded amber — one+ ML layer offline but canary + arch controls active
inactive red — security module crashed, arch controls only
Consumes /health.security (surfaced in commit 7e9600ff). Updated once on
connection bootstrap. Shield stays hidden until /health arrives so the user
never sees a flickering "unknown" state.
Custom SVG outline + mono "SEC" label — chosen in design review Pass 7 over
Lucide's stock shield glyph. Matches the industrial/CLI brand voice in
DESIGN.md ("monospace as personality font").
Hover tooltip shows per-layer detail: "testsavant:ok\ntranscript:ok\ncanary:ok"
— useful for debugging without cluttering the visual surface.
Known v1 limitation: only updates at connection bootstrap. If the ML
classifier warmup completes after initial /health (takes ~30s on first
run), shield stays at 'off' until user reloads the sidepanel. Follow-up
TODO: extend /sidebar-chat polling to refresh security state.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
215 lines
10 KiB
HTML
215 lines
10 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<link rel="stylesheet" href="sidepanel.css">
|
|
</head>
|
|
<body>
|
|
<!-- Security shield — reflects ~/.gstack/security/session-state.json status.
|
|
Hidden until the sidebar knows its state (avoids flicker on first load).
|
|
Consumes /health.security — see browse/src/security.ts getStatus(). -->
|
|
<div class="security-shield" id="security-shield" role="status" aria-label="Security status: unknown" style="display:none" title="Security">
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
|
|
</svg>
|
|
<span class="security-shield-label" id="security-shield-label">SEC</span>
|
|
</div>
|
|
|
|
<!-- Connection status banner -->
|
|
<div class="conn-banner" id="conn-banner" style="display:none">
|
|
<span class="conn-banner-text" id="conn-banner-text">Reconnecting...</span>
|
|
<div class="conn-banner-actions" id="conn-banner-actions" style="display:none">
|
|
<button class="conn-btn" id="conn-reconnect">Reconnect</button>
|
|
<button class="conn-btn conn-copy" id="conn-copy" title="Copy command">/open-gstack-browser</button>
|
|
</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>
|
|
</div>
|
|
|
|
<!-- Browser tab bar -->
|
|
<div class="browser-tabs" id="browser-tabs" style="display:none"></div>
|
|
|
|
<!-- Chat Tab (default, full height) -->
|
|
<main id="tab-chat" class="tab-content active">
|
|
<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">
|
|
<p>Waiting for commands...</p>
|
|
<p class="muted">Run a browse command to see activity here.</p>
|
|
</div>
|
|
<div id="activity-feed"></div>
|
|
</main>
|
|
|
|
<!-- Debug: Refs Tab (hidden by default) -->
|
|
<main id="tab-refs" class="tab-content">
|
|
<div class="empty-state" id="refs-empty">
|
|
<p>No refs yet</p>
|
|
<p class="muted">Run <code>snapshot</code> to see element refs.</p>
|
|
</div>
|
|
<div id="refs-list"></div>
|
|
<div class="refs-footer" id="refs-footer"></div>
|
|
</main>
|
|
|
|
<!-- Debug: Inspector Tab (hidden by default) -->
|
|
<main id="tab-inspector" class="tab-content">
|
|
<!-- Toolbar: always visible -->
|
|
<div class="inspector-toolbar" id="inspector-toolbar">
|
|
<button class="inspector-pick-btn" id="inspector-pick-btn" title="Pick an element (click, then click any element on the page)">
|
|
<span class="inspector-pick-icon">✛</span> Pick
|
|
</button>
|
|
<span class="inspector-selected" id="inspector-selected"></span>
|
|
<span class="inspector-mode-badge" id="inspector-mode-badge" style="display:none"></span>
|
|
<div style="flex:1"></div>
|
|
<button id="inspector-cleanup-btn" class="inspector-action-btn" title="Remove ads, banners, popups">🧹</button>
|
|
<button id="inspector-screenshot-btn" class="inspector-action-btn" title="Take a screenshot">📸</button>
|
|
</div>
|
|
|
|
<!-- Inspector content area -->
|
|
<div class="inspector-content" id="inspector-content">
|
|
<!-- Empty state (before first pick) -->
|
|
<div class="inspector-empty" id="inspector-empty">
|
|
<div class="inspector-empty-icon">✛</div>
|
|
<p>Pick an element to inspect</p>
|
|
<p class="muted">Click the button above, then click any element on the page</p>
|
|
</div>
|
|
|
|
<!-- Loading state -->
|
|
<div class="inspector-loading" id="inspector-loading" style="display:none">
|
|
<div class="inspector-loading-text">Inspecting...</div>
|
|
<div class="inspector-skeleton">
|
|
<div class="inspector-skeleton-bar"></div>
|
|
<div class="inspector-skeleton-bar"></div>
|
|
<div class="inspector-skeleton-bar"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Error state -->
|
|
<div class="inspector-error" id="inspector-error" style="display:none"></div>
|
|
|
|
<!-- Inspector data panels -->
|
|
<div class="inspector-panels" id="inspector-panels" style="display:none">
|
|
<!-- Box Model -->
|
|
<div class="inspector-section" id="inspector-boxmodel-section">
|
|
<div class="inspector-section-header">Box Model</div>
|
|
<div class="inspector-boxmodel" id="inspector-boxmodel"></div>
|
|
</div>
|
|
|
|
<!-- Matched Rules -->
|
|
<div class="inspector-section" id="inspector-rules-section">
|
|
<button class="inspector-section-toggle" data-section="rules" aria-expanded="true">
|
|
<span class="inspector-toggle-arrow">▼</span>
|
|
<span>Matched Rules</span>
|
|
<span class="inspector-rule-count" id="inspector-rule-count"></span>
|
|
</button>
|
|
<div class="inspector-section-body" id="inspector-rules" role="tree"></div>
|
|
</div>
|
|
|
|
<!-- Computed Styles -->
|
|
<div class="inspector-section" id="inspector-computed-section">
|
|
<button class="inspector-section-toggle collapsed" data-section="computed" aria-expanded="false">
|
|
<span class="inspector-toggle-arrow">▶</span>
|
|
<span>Computed</span>
|
|
</button>
|
|
<div class="inspector-section-body collapsed" id="inspector-computed"></div>
|
|
</div>
|
|
|
|
<!-- Quick Edit -->
|
|
<div class="inspector-section" id="inspector-quickedit-section">
|
|
<button class="inspector-section-toggle collapsed" data-section="quickedit" aria-expanded="false">
|
|
<span class="inspector-toggle-arrow">▶</span>
|
|
<span>Quick Edit</span>
|
|
</button>
|
|
<div class="inspector-section-body collapsed" id="inspector-quickedit"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Send to Agent: sticky bottom -->
|
|
<div class="inspector-send" id="inspector-send" style="display:none">
|
|
<button class="inspector-send-btn" id="inspector-send-btn">Send to Agent</button>
|
|
</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">
|
|
<span class="dot" id="footer-dot"></span>
|
|
<span class="footer-port" id="footer-port" title="Click to change port"></span>
|
|
<input type="text" class="port-input" id="port-input" placeholder="34567" autocomplete="off" style="display:none">
|
|
</div>
|
|
</footer>
|
|
|
|
<!-- Debug tab bar (hidden by default) -->
|
|
<nav class="tabs debug-tabs" id="debug-tabs" role="tablist" style="display:none">
|
|
<button class="tab" role="tab" data-tab="activity">Activity</button>
|
|
<button class="tab" role="tab" data-tab="refs">Refs</button>
|
|
<button class="tab" role="tab" data-tab="inspector">Inspector</button>
|
|
<button class="tab close-debug" id="close-debug" title="Close debug">×</button>
|
|
</nav>
|
|
|
|
<script src="sidepanel.js"></script>
|
|
</body>
|
|
</html>
|