Files
web-terminal/public/style.css
Yaojia Wang 9a6150c355 feat(v0.3): H3 — remote approve/reject (no typing)
- types/protocol: ClientMessage approve/reject; ServerMessage status.pending
- server: POST /hook/permission HELD until the client decides; approve/reject
  ws messages resolve it with {hookSpecificOutput.decision.behavior allow|deny};
  5-min timeout + release-on-disconnect fall back to Claude's own prompt
- manager.handleHookEvent threads pending flag
- setup-hooks: PermissionRequest uses the held curl (writes decision to stdout);
  status events stay fire-and-forget
- FE: terminal-session approve()/reject() + pending state; tabs approval banner
  (Approve/Reject) for the active tab's held request
- Tests: protocol approve/reject; integration ⑧ held→approve→allow. 207 green.
- Browser-verified: banner 'Claude wants to use Bash' → Approve → resolves allow.
2026-06-17 19:13:07 +02:00

393 lines
7.2 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Web Terminal Styling */
:root {
/* Key bar is shown on ALL devices (clickable shortcut buttons). Touch
devices get a taller bar with bigger tap targets. */
--tabbar-h: 36px;
--keybar-h: 42px;
}
/* Touch devices: bigger tap targets for tabs and the key bar. */
@media (pointer: coarse) {
:root {
--tabbar-h: 44px;
--keybar-h: 58px;
}
}
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
background-color: #1a1a1a;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
overflow: hidden;
}
/* ── Tab bar (top) ───────────────────────────────────────────────── */
#tabbar {
position: fixed;
top: 0;
left: 0;
right: 0;
height: var(--tabbar-h);
background-color: #222;
border-bottom: 1px solid #3a3a3a;
display: flex;
align-items: stretch;
z-index: 1001;
}
/* tabs: scrollable; toolbar: fixed cluster on the right */
#tabs {
flex: 1 1 auto;
display: flex;
align-items: stretch;
overflow-x: auto;
overflow-y: hidden;
scrollbar-width: thin;
}
#toolbar {
flex: 0 0 auto;
display: flex;
align-items: center;
border-left: 1px solid #3a3a3a;
}
.toolbtn {
background: none;
border: none;
color: #aaa;
cursor: pointer;
font-size: 15px;
padding: 0 12px;
height: 100%;
}
.toolbtn:hover {
color: #fff;
background-color: #2a2a2a;
}
/* Search box overlay (M1) */
#searchbox {
position: fixed;
top: calc(var(--tabbar-h) + 6px);
right: 8px;
z-index: 1100;
display: flex;
gap: 4px;
padding: 6px;
background-color: #2a2a2a;
border: 1px solid #3a3a3a;
border-radius: 6px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
}
.search-input {
width: 180px;
padding: 4px 6px;
font: inherit;
font-size: 13px;
color: #fff;
background: #111;
border: 1px solid #444;
border-radius: 3px;
outline: none;
}
#searchbox button {
background: #333;
border: 1px solid #444;
color: #ddd;
cursor: pointer;
border-radius: 3px;
padding: 0 8px;
}
#searchbox button:hover {
background: #555;
}
/* QR connect modal (M5) */
#qrmodal {
position: fixed;
inset: 0;
z-index: 1200;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.6);
}
.qr-card {
background: #fff;
color: #111;
border-radius: 12px;
padding: 20px;
text-align: center;
max-width: 90vw;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
}
.qr-title {
font-weight: 600;
margin-bottom: 12px;
}
.qr-url {
margin-top: 10px;
font-size: 13px;
color: #333;
word-break: break-all;
}
.qr-note {
margin-top: 8px;
font-size: 12px;
color: #666;
max-width: 240px;
}
.qr-close {
margin-top: 14px;
padding: 6px 18px;
border: none;
border-radius: 6px;
background: #1a1a1a;
color: #fff;
cursor: pointer;
font: inherit;
}
.tab {
display: flex;
align-items: center;
padding: 0 4px 0 12px;
color: #aaa;
background-color: #2a2a2a;
border-right: 1px solid #1a1a1a;
cursor: pointer;
white-space: nowrap;
font-size: 13px;
user-select: none;
max-width: 200px;
}
.tab.active {
background-color: #1a1a1a;
color: #fff;
box-shadow: inset 0 -2px 0 #4a9eff; /* active underline accent */
}
/* Status dot — color reflects connection state. */
.tab-dot {
width: 8px;
height: 8px;
border-radius: 50%;
margin-right: 8px;
flex: none;
background-color: #666;
}
.dot-connected {
background-color: #3fb950; /* green */
}
.dot-connecting,
.dot-reconnecting {
background-color: #d29922; /* amber */
}
.dot-exited {
background-color: #f85149; /* red */
}
/* Unread: an inactive tab got new output — blue ring + brighter label. */
.tab.unread .tab-dot {
box-shadow: 0 0 0 2px rgba(74, 158, 255, 0.6);
}
.tab.unread .tab-label {
color: #fff;
font-weight: 600;
}
/* Claude Code activity glyph (H2/H4) */
.tab-claude {
margin-right: 6px;
font-size: 12px;
flex: none;
}
/* A background tab that needs approval gets an amber outline. */
.tab.claude-waiting {
background-color: #3a2a10;
box-shadow: inset 0 -2px 0 #d29922;
}
/* Approve/reject banner (H3) — sits just above the key bar. */
#approvalbar {
position: fixed;
left: 0;
right: 0;
bottom: var(--keybar-h);
z-index: 1050;
display: flex;
align-items: center;
gap: 10px;
padding: 8px 12px;
background: #3a2a10;
border-top: 1px solid #d29922;
color: #ffcf8f;
font-size: 14px;
}
.approval-label {
flex: 1 1 auto;
}
#approvalbar button {
flex: none;
border: none;
border-radius: 5px;
padding: 7px 16px;
cursor: pointer;
font: inherit;
font-weight: 600;
}
.approval-yes {
background: #2ea043;
color: #fff;
}
.approval-no {
background: #c93c37;
color: #fff;
}
/* Drag-to-reorder feedback. */
.tab.dragging {
opacity: 0.4;
}
.tab.drag-over {
box-shadow: inset 3px 0 0 #4a9eff; /* drop-position indicator */
}
.tab-label {
padding-right: 8px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Inline rename input (double-click a tab) */
.tab-rename {
width: 110px;
margin: 0 4px;
padding: 2px 4px;
font: inherit;
font-size: 13px;
color: #fff;
background: #111;
border: 1px solid #4a9eff;
border-radius: 3px;
outline: none;
}
.tab-close {
background: none;
border: none;
color: #888;
cursor: pointer;
font-size: 15px;
line-height: 1;
padding: 2px 6px;
border-radius: 3px;
flex: none;
}
.tab-close:hover {
background-color: #444;
color: #fff;
}
/* Desktop (mouse): reveal × on hover/active to reduce clutter.
Touch devices keep × always visible (no hover). */
@media (pointer: fine) {
.tab:not(.active):not(:hover) .tab-close {
opacity: 0;
}
}
.tab-add {
background: none;
border: none;
color: #aaa;
cursor: pointer;
font-size: 18px;
padding: 0 14px;
user-select: none;
flex: none;
}
.tab-add:hover {
color: #fff;
}
/* ── Terminal area (holds one .term-pane per tab) ────────────────── */
#term {
position: absolute;
top: var(--tabbar-h);
left: 0;
right: 0;
bottom: var(--keybar-h);
background-color: #1a1a1a;
overflow: hidden;
}
.term-pane {
position: absolute;
inset: 0;
overflow: hidden;
}
/* ── Shortcut key bar (bottom) — shown on ALL devices ────────────── */
#keybar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: var(--keybar-h);
background-color: #2a2a2a;
border-top: 1px solid #3a3a3a;
display: flex;
flex-direction: row;
overflow-x: auto;
overflow-y: hidden;
z-index: 1000;
gap: 0;
scrollbar-width: thin;
}
#keybar button {
flex: 1 0 auto;
min-width: 48px;
height: var(--keybar-h);
padding: 0 8px;
border: 1px solid #444;
border-top: none;
border-bottom: none;
background-color: #333;
color: #ddd;
font-family: inherit;
font-size: 14px;
cursor: pointer;
border-radius: 0;
transition: background-color 0.12s;
-webkit-user-select: none;
user-select: none;
-webkit-touch-callout: none;
}
#keybar button:active {
background-color: #555;
}
/* Esc is the most-used (interrupt) — make it prominent. */
#keybar button.keybar-btn-primary {
background-color: #5a3a12;
color: #ffcf8f;
font-weight: 600;
min-width: 56px;
}
#keybar button.keybar-btn-primary:active {
background-color: #7a4f18;
}