Two things, both invisible in code review and obvious on screen. 1. Eight controls in the project detail had class names but no CSS rule at all, so they rendered as raw user-agent widgets — white input boxes and grey buttons sitting inside a dark panel: View Diff, the new-branch input, Create Worktree, and the five fan-out fields. Gave them the skin the panel already established, with two button roles: filled accent for the control that commits a form (matching Fetch), outline for one that only reveals or toggles (matching Open). Layout stays in each element's existing rule; this block sets only the shared skin, so no property is declared twice. 2. The w6 CSS referenced --fg, --fg-dim, --fg-faint, --bg-sunk and --mono, none of which exist in :root. Every one of those 19 references was silently falling through to its hardcoded fallback, which happened to match the default amber theme — so it looked correct while being wired to nothing. Under any other theme the sync band would have kept the default colours while the rest of the app changed. Rewired to the real tokens: --text, --text-dim, --text-faint, --surface-1, --on-accent, and the mono stack the file already uses. The fallback values are what hid this: a var() fallback turns a typo into a silent success. Worth remembering when reaching for one. Verified: tsc clean, npm test green (78 files / 2161, e2e 27), bundle rebuilt.
2924 lines
62 KiB
CSS
2924 lines
62 KiB
CSS
/* Web Terminal — modern dark UI */
|
||
|
||
:root {
|
||
/* palette (design tokens) — Amber 琥珀金 theme (warm-neutral base + gold accent) */
|
||
--bg: #100f0d; /* terminal / deepest surface — warm near-neutral */
|
||
--surface-1: #181613; /* tab bar, key bar */
|
||
--surface-2: #1f1c17; /* tabs, buttons, cards */
|
||
--surface-3: #2a2620; /* hover / active */
|
||
--border: rgba(255, 255, 255, 0.07);
|
||
--border-strong: rgba(255, 255, 255, 0.14);
|
||
--text: #ece9e3;
|
||
--text-dim: #a8a299;
|
||
--text-faint: #6f6a61;
|
||
--accent: #e3a64a; /* amber gold */
|
||
--accent-2: #c9892f; /* deeper gold (gradients / hover) */
|
||
--accent-soft: rgba(227, 166, 74, 0.15);
|
||
--on-accent: #1a1305; /* text/icon color on a gold accent background (contrast) */
|
||
--green: #46d07f; /* connection-status: connected */
|
||
--amber: #f5b14c; /* connection-status: connecting */
|
||
--red: #ff6b6b; /* connection-status: disconnected / error */
|
||
--shadow: 0 10px 40px rgba(0, 0, 0, 0.55);
|
||
--radius: 9px;
|
||
--radius-lg: 14px;
|
||
|
||
--tabbar-h: 40px;
|
||
--keybar-h: 46px;
|
||
|
||
/* iOS notch / home-indicator clearance. index.html sets viewport-fit=cover,
|
||
* so the layout spans the physical screen edges; without these the top bar
|
||
* hides under the status bar and the bottom key-bar covers terminal output.
|
||
* Falls back to 0px on platforms without safe areas. */
|
||
--safe-t: env(safe-area-inset-top, 0px);
|
||
--safe-b: env(safe-area-inset-bottom, 0px);
|
||
|
||
--ui-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||
}
|
||
|
||
@media (pointer: coarse) {
|
||
:root {
|
||
--tabbar-h: 48px;
|
||
--keybar-h: 60px;
|
||
}
|
||
}
|
||
|
||
html,
|
||
body {
|
||
margin: 0;
|
||
padding: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background-color: var(--bg);
|
||
color: var(--text);
|
||
font-family: var(--ui-font);
|
||
overflow: hidden;
|
||
}
|
||
|
||
* {
|
||
scrollbar-width: thin;
|
||
scrollbar-color: var(--surface-3) transparent;
|
||
}
|
||
|
||
/* ── Tab bar ─────────────────────────────────────────────────────── */
|
||
#tabbar {
|
||
position: fixed;
|
||
inset: 0 0 auto 0;
|
||
box-sizing: border-box;
|
||
height: calc(var(--tabbar-h) + var(--safe-t));
|
||
padding-top: var(--safe-t);
|
||
background: linear-gradient(180deg, #181a22, var(--surface-1));
|
||
border-bottom: 1px solid var(--border);
|
||
display: flex;
|
||
align-items: stretch;
|
||
z-index: 1001;
|
||
}
|
||
|
||
#tabs {
|
||
flex: 1 1 auto;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 2px;
|
||
padding: 0 4px;
|
||
overflow-x: auto;
|
||
overflow-y: hidden;
|
||
}
|
||
#tabs::-webkit-scrollbar {
|
||
height: 0;
|
||
}
|
||
|
||
/* tabs as floating rounded chips */
|
||
.tab {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 2px;
|
||
height: calc(var(--tabbar-h) - 12px);
|
||
padding: 0 4px 0 10px;
|
||
border-radius: 8px;
|
||
color: var(--text-dim);
|
||
background: transparent;
|
||
cursor: pointer;
|
||
white-space: nowrap;
|
||
font-size: 13px;
|
||
user-select: none;
|
||
max-width: 220px;
|
||
transition:
|
||
background 0.12s ease,
|
||
color 0.12s ease;
|
||
}
|
||
.tab:hover {
|
||
background: var(--surface-2);
|
||
color: var(--text);
|
||
}
|
||
.tab.active {
|
||
background: var(--surface-3);
|
||
color: #fff;
|
||
box-shadow: inset 0 0 0 1px var(--border-strong);
|
||
}
|
||
|
||
.tab-dot {
|
||
width: 7px;
|
||
height: 7px;
|
||
border-radius: 50%;
|
||
margin-right: 8px;
|
||
flex: none;
|
||
background: var(--text-faint);
|
||
}
|
||
.dot-connected {
|
||
background: var(--green);
|
||
box-shadow: 0 0 6px rgba(70, 208, 127, 0.5);
|
||
}
|
||
.dot-connecting,
|
||
.dot-reconnecting {
|
||
background: var(--amber);
|
||
}
|
||
.dot-exited {
|
||
background: var(--red);
|
||
}
|
||
|
||
.tab-label {
|
||
padding-right: 4px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.tab-rename {
|
||
width: 120px;
|
||
margin: 0 4px;
|
||
padding: 3px 6px;
|
||
font: inherit;
|
||
font-size: 13px;
|
||
color: #fff;
|
||
background: var(--bg);
|
||
border: 1px solid var(--accent);
|
||
border-radius: 6px;
|
||
outline: none;
|
||
}
|
||
|
||
/* unread / Claude-waiting accents */
|
||
.tab.unread .tab-dot {
|
||
box-shadow: 0 0 0 3px var(--accent-soft);
|
||
}
|
||
.tab.unread .tab-label {
|
||
color: #fff;
|
||
font-weight: 600;
|
||
}
|
||
.tab-claude {
|
||
margin-right: 4px;
|
||
font-size: 12px;
|
||
flex: none;
|
||
}
|
||
.tab.claude-waiting {
|
||
background: rgba(245, 177, 76, 0.14);
|
||
box-shadow: inset 0 0 0 1px rgba(245, 177, 76, 0.5);
|
||
}
|
||
|
||
.tab.dragging {
|
||
opacity: 0.4;
|
||
}
|
||
.tab.drag-over {
|
||
box-shadow: inset 3px 0 0 var(--accent);
|
||
}
|
||
|
||
.tab-close {
|
||
background: none;
|
||
border: none;
|
||
color: var(--text-faint);
|
||
cursor: pointer;
|
||
font-size: 16px;
|
||
line-height: 1;
|
||
padding: 3px 6px;
|
||
border-radius: 6px;
|
||
flex: none;
|
||
}
|
||
.tab-close:hover {
|
||
background: rgba(255, 255, 255, 0.1);
|
||
color: #fff;
|
||
}
|
||
@media (pointer: fine) {
|
||
.tab:not(.active):not(:hover) .tab-close {
|
||
opacity: 0;
|
||
}
|
||
}
|
||
|
||
.tab-add {
|
||
background: none;
|
||
border: none;
|
||
color: var(--text-dim);
|
||
cursor: pointer;
|
||
font-size: 18px;
|
||
padding: 0 10px;
|
||
flex: none;
|
||
border-radius: 7px;
|
||
}
|
||
.tab-add:hover {
|
||
color: #fff;
|
||
background: var(--surface-2);
|
||
}
|
||
|
||
/* v0.6: Home (⌂) button — opens the Sessions/Projects chooser over open tabs. */
|
||
.tab-home {
|
||
background: none;
|
||
border: none;
|
||
color: var(--text-dim);
|
||
cursor: pointer;
|
||
font-size: 17px;
|
||
padding: 0 10px;
|
||
flex: none;
|
||
border-radius: 7px;
|
||
}
|
||
.tab-home:hover {
|
||
color: #fff;
|
||
background: var(--surface-2);
|
||
}
|
||
.tab-home.active {
|
||
color: var(--on-accent, #1a1408);
|
||
background: var(--accent);
|
||
}
|
||
|
||
/* toolbar */
|
||
#toolbar {
|
||
flex: 0 0 auto;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 2px;
|
||
padding: 0 6px;
|
||
border-left: 1px solid var(--border);
|
||
}
|
||
.toolbtn {
|
||
position: relative; /* for the hover tooltip */
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: none;
|
||
border: none;
|
||
color: var(--text-dim);
|
||
cursor: pointer;
|
||
padding: 7px 9px;
|
||
border-radius: 8px;
|
||
line-height: 1;
|
||
transition: background 0.12s ease, color 0.12s ease;
|
||
}
|
||
.toolbtn svg {
|
||
display: block;
|
||
width: 18px;
|
||
height: 18px;
|
||
}
|
||
.toolbtn:hover {
|
||
color: var(--accent);
|
||
background: var(--surface-2);
|
||
}
|
||
/* Hover tooltip (right-anchored so it never clips the screen edge). */
|
||
.toolbtn[data-tip]::after {
|
||
content: attr(data-tip);
|
||
position: absolute;
|
||
top: calc(100% + 6px);
|
||
right: 0;
|
||
white-space: nowrap;
|
||
background: var(--surface-3);
|
||
color: var(--text);
|
||
border: 1px solid var(--border-strong);
|
||
padding: 4px 8px;
|
||
border-radius: 6px;
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transition: opacity 0.12s ease;
|
||
z-index: 60;
|
||
}
|
||
.toolbtn[data-tip]:hover::after {
|
||
opacity: 1;
|
||
}
|
||
|
||
/* ── Terminal area ───────────────────────────────────────────────── */
|
||
#term {
|
||
position: absolute;
|
||
inset: calc(var(--tabbar-h) + var(--safe-t)) 0 calc(var(--keybar-h) + var(--safe-b)) 0;
|
||
background: var(--bg);
|
||
overflow: hidden;
|
||
}
|
||
/* Each pane lives in a .term-cell wrapper (header + terminal + optional inline
|
||
* approve). In single mode the cell fills #term absolutely (only the focused one
|
||
* is shown) so the classic one-pane look is unchanged; split layouts turn #term
|
||
* into a CSS grid and the cells flow into tracks. */
|
||
.term-cell {
|
||
position: absolute;
|
||
inset: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
min-width: 0;
|
||
min-height: 0;
|
||
overflow: hidden;
|
||
}
|
||
.term-pane {
|
||
position: relative;
|
||
flex: 1 1 auto;
|
||
min-height: 0;
|
||
padding: 6px 8px 0;
|
||
box-sizing: border-box;
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* Cell header + inline-approve footer are hidden in single mode. */
|
||
.cell-head,
|
||
.cell-approve {
|
||
display: none;
|
||
}
|
||
|
||
/* ── Split-grid layouts (desktop watch board) ────────────────────────── */
|
||
/* Shared cell chrome keys off the single `grid` marker so it is independent of
|
||
* how many concrete layouts exist; each `lay-*` only sets the grid template. */
|
||
#term.grid {
|
||
display: grid;
|
||
gap: 6px;
|
||
padding: 6px;
|
||
}
|
||
#term.lay-split-2 {
|
||
grid-template-columns: 1fr 1fr;
|
||
grid-template-rows: 1fr;
|
||
}
|
||
#term.lay-row-3 {
|
||
grid-template-columns: 1fr 1fr 1fr;
|
||
grid-template-rows: 1fr;
|
||
}
|
||
#term.lay-grid-4 {
|
||
grid-template-columns: 1fr 1fr;
|
||
grid-template-rows: 1fr 1fr;
|
||
}
|
||
#term.lay-grid-6 {
|
||
grid-template-columns: 1fr 1fr 1fr;
|
||
grid-template-rows: 1fr 1fr;
|
||
}
|
||
#term.grid .term-cell {
|
||
position: relative;
|
||
inset: auto;
|
||
border: 1px solid var(--border);
|
||
border-radius: var(--radius);
|
||
background: var(--bg);
|
||
}
|
||
/* Keep the last terminal row clear of the cell border/focus ring in a grid.
|
||
* Root cause (measured): the base .term-pane is box-sizing:border-box, so
|
||
* getComputedStyle(pane).height — which xterm's FitAddon reads to compute rows —
|
||
* returns the PADDING-box height and double-counts the pane's own padding, packing
|
||
* one extra row that overruns into the border (overflow +6px). content-box makes
|
||
* that height report the CONTENT box, so FitAddon drops the phantom row and the 6px
|
||
* padding becomes real clearance. (Single mode keeps padding 0 so the terminal
|
||
* still fills to the key-bar.) */
|
||
#term.grid .term-pane {
|
||
box-sizing: content-box;
|
||
padding-bottom: 6px;
|
||
}
|
||
/* Maximized quadrant fills the whole grid as an ABSOLUTE overlay (taken out of
|
||
* grid flow so the siblings keep their placement — spanning grid tracks instead
|
||
* would shove auto-placed siblings into implicit rows, resizing their live PTYs).
|
||
* #term is a positioned containing block, so inset:0 covers the whole area. */
|
||
#term.grid .term-cell.maximized {
|
||
position: absolute;
|
||
inset: 0;
|
||
z-index: 4;
|
||
}
|
||
/* A tab dragged from the tab bar is hovering this quadrant. */
|
||
#term.grid .term-cell.drag-target {
|
||
outline: 2px dashed var(--accent);
|
||
outline-offset: -3px;
|
||
}
|
||
|
||
/* Draggable splitter handles overlaying the track boundaries (v3). */
|
||
.grid-gutter {
|
||
position: absolute;
|
||
z-index: 3;
|
||
touch-action: none;
|
||
}
|
||
.grid-gutter::after {
|
||
content: '';
|
||
position: absolute;
|
||
background: var(--border-strong);
|
||
border-radius: 2px;
|
||
opacity: 0;
|
||
transition: opacity 0.15s;
|
||
}
|
||
.grid-gutter:hover::after,
|
||
.grid-gutter:active::after {
|
||
opacity: 1;
|
||
background: var(--accent);
|
||
}
|
||
.grid-gutter-col {
|
||
top: 0;
|
||
bottom: 0;
|
||
width: 12px;
|
||
transform: translateX(-50%);
|
||
cursor: col-resize;
|
||
}
|
||
.grid-gutter-col::after {
|
||
top: 15%;
|
||
bottom: 15%;
|
||
left: 50%;
|
||
width: 3px;
|
||
transform: translateX(-50%);
|
||
}
|
||
.grid-gutter-row {
|
||
left: 0;
|
||
right: 0;
|
||
height: 12px;
|
||
transform: translateY(-50%);
|
||
cursor: row-resize;
|
||
}
|
||
.grid-gutter-row::after {
|
||
left: 15%;
|
||
right: 15%;
|
||
top: 50%;
|
||
height: 3px;
|
||
transform: translateY(-50%);
|
||
}
|
||
#term.grid .cell-head {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding: 5px 9px;
|
||
background: linear-gradient(180deg, var(--surface-2), var(--surface-1));
|
||
border-bottom: 1px solid var(--border);
|
||
font-size: 12px;
|
||
cursor: pointer;
|
||
user-select: none;
|
||
}
|
||
.cell-max {
|
||
border: 0;
|
||
background: transparent;
|
||
color: var(--text-faint);
|
||
cursor: pointer;
|
||
font-size: 13px;
|
||
line-height: 1;
|
||
padding: 2px 4px;
|
||
border-radius: 5px;
|
||
}
|
||
.cell-max:hover {
|
||
color: var(--text);
|
||
background: var(--surface-3);
|
||
}
|
||
.cell-keep {
|
||
border: 0;
|
||
background: transparent;
|
||
color: var(--text-faint);
|
||
cursor: pointer;
|
||
font-size: 13px;
|
||
line-height: 1;
|
||
padding: 2px 4px;
|
||
border-radius: 5px;
|
||
}
|
||
.cell-keep:hover {
|
||
color: var(--text);
|
||
background: var(--surface-3);
|
||
}
|
||
.cell-monitor-btn {
|
||
border: 0;
|
||
background: transparent;
|
||
color: var(--text-faint);
|
||
cursor: pointer;
|
||
font-size: 12px;
|
||
line-height: 1;
|
||
padding: 2px 4px;
|
||
border-radius: 5px;
|
||
opacity: 0.7;
|
||
}
|
||
.cell-monitor-btn:hover {
|
||
color: var(--text);
|
||
background: var(--surface-3);
|
||
opacity: 1;
|
||
}
|
||
.cell-monitor-btn.active {
|
||
color: var(--accent);
|
||
opacity: 1;
|
||
}
|
||
/* Read-only monitor preview fills the cell below the header. */
|
||
.cell-monitor {
|
||
flex: 1 1 auto;
|
||
min-height: 0;
|
||
overflow: hidden;
|
||
position: relative;
|
||
padding: 6px 8px 0;
|
||
box-sizing: border-box;
|
||
}
|
||
.cell-name {
|
||
font-weight: 600;
|
||
color: var(--text);
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
.cell-status {
|
||
margin-left: auto;
|
||
font-size: 11px;
|
||
color: var(--text-dim);
|
||
white-space: nowrap;
|
||
}
|
||
.cell-status.cs-working {
|
||
color: var(--amber);
|
||
}
|
||
.cell-status.cs-waiting {
|
||
color: var(--accent);
|
||
}
|
||
.cell-status.cs-idle {
|
||
color: var(--green);
|
||
}
|
||
.cell-status.cs-stuck {
|
||
color: var(--red);
|
||
}
|
||
|
||
/* Focus ring — the quadrant that owns keyboard / keybar / voice / approvals. */
|
||
#term.grid .term-cell.focused {
|
||
border-color: var(--accent);
|
||
box-shadow:
|
||
0 0 0 1px var(--accent),
|
||
var(--shadow);
|
||
}
|
||
/* Pending glow — a non-focused quadrant is waiting for approval. */
|
||
#term.grid .term-cell.pending:not(.focused) {
|
||
border-color: var(--amber);
|
||
animation: cell-pending 2.4s ease-in-out infinite;
|
||
}
|
||
@keyframes cell-pending {
|
||
0%,
|
||
100% {
|
||
box-shadow: 0 0 0 1px rgba(245, 177, 76, 0.35);
|
||
}
|
||
50% {
|
||
box-shadow:
|
||
0 0 0 1px rgba(245, 177, 76, 0.7),
|
||
0 0 26px -8px rgba(245, 177, 76, 0.6);
|
||
}
|
||
}
|
||
@media (prefers-reduced-motion: reduce) {
|
||
#term.grid .term-cell.pending {
|
||
animation: none;
|
||
}
|
||
}
|
||
|
||
/* Inline per-quadrant approve footer. */
|
||
#term.grid .cell-approve {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding: 6px 9px;
|
||
background: rgba(245, 177, 76, 0.08);
|
||
border-top: 1px solid rgba(245, 177, 76, 0.35);
|
||
font-size: 11.5px;
|
||
}
|
||
.cell-approve-label {
|
||
color: var(--amber);
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
.cell-approve-btns {
|
||
margin-left: auto;
|
||
display: flex;
|
||
gap: 6px;
|
||
}
|
||
.cell-approve-yes,
|
||
.cell-approve-no {
|
||
border: 0;
|
||
border-radius: 6px;
|
||
padding: 3px 11px;
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
}
|
||
.cell-approve-yes {
|
||
background: var(--green);
|
||
color: #06231a;
|
||
}
|
||
.cell-approve-no {
|
||
background: var(--surface-3);
|
||
color: var(--text-dim);
|
||
}
|
||
.cell-approve-yes:hover {
|
||
filter: brightness(1.1);
|
||
}
|
||
.cell-approve-no:hover {
|
||
color: var(--text);
|
||
}
|
||
|
||
/* Empty-slot placeholder (fewer sessions than the layout holds). */
|
||
.term-cell.slot-empty {
|
||
border: 1px dashed var(--border-strong);
|
||
background: repeating-linear-gradient(
|
||
-45deg,
|
||
transparent,
|
||
transparent 10px,
|
||
rgba(255, 255, 255, 0.02) 10px,
|
||
rgba(255, 255, 255, 0.02) 20px
|
||
);
|
||
align-items: center;
|
||
justify-content: center;
|
||
cursor: pointer;
|
||
}
|
||
.term-cell.slot-empty:hover {
|
||
border-color: var(--accent);
|
||
}
|
||
.slot-new {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 6px;
|
||
background: none;
|
||
border: 0;
|
||
color: var(--text-faint);
|
||
font-size: 12px;
|
||
font-family: var(--ui-font);
|
||
cursor: pointer;
|
||
}
|
||
.slot-new b {
|
||
font-size: 26px;
|
||
font-weight: 300;
|
||
line-height: 1;
|
||
color: var(--text-dim);
|
||
}
|
||
.term-cell.slot-empty:hover .slot-new {
|
||
color: var(--text-dim);
|
||
}
|
||
|
||
/* ── Grid-toggle segmented control (toolbar) ─────────────────────────── */
|
||
.grid-toggle {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 2px;
|
||
margin-left: 4px;
|
||
padding: 2px;
|
||
background: var(--surface-2);
|
||
border: 1px solid var(--border);
|
||
border-radius: 8px;
|
||
}
|
||
.grid-toggle-btn {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 28px;
|
||
height: 26px;
|
||
padding: 0;
|
||
border: 0;
|
||
border-radius: 6px;
|
||
background: transparent;
|
||
color: var(--text-faint);
|
||
cursor: pointer;
|
||
}
|
||
.grid-toggle-btn:hover {
|
||
color: var(--text);
|
||
background: var(--surface-3);
|
||
}
|
||
.grid-toggle-btn[aria-pressed='true'] {
|
||
color: var(--on-accent);
|
||
background: var(--accent);
|
||
}
|
||
.grid-glyph {
|
||
display: grid;
|
||
gap: 1.5px;
|
||
width: 14px;
|
||
height: 12px;
|
||
}
|
||
.grid-glyph.gl-single {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
.grid-glyph.gl-split-2 {
|
||
grid-template-columns: 1fr 1fr;
|
||
}
|
||
.grid-glyph.gl-row-3 {
|
||
grid-template-columns: 1fr 1fr 1fr;
|
||
}
|
||
.grid-glyph.gl-grid-4 {
|
||
grid-template-columns: 1fr 1fr;
|
||
grid-template-rows: 1fr 1fr;
|
||
}
|
||
.grid-glyph.gl-grid-6 {
|
||
grid-template-columns: 1fr 1fr 1fr;
|
||
grid-template-rows: 1fr 1fr;
|
||
}
|
||
.grid-glyph i {
|
||
background: currentColor;
|
||
border-radius: 1px;
|
||
}
|
||
/* Touch tablets clear the 1024px width gate but need bigger tap targets — mirror
|
||
* the project's existing coarse-pointer bump for the tab/key bars. */
|
||
@media (pointer: coarse) {
|
||
.grid-toggle {
|
||
gap: 3px;
|
||
}
|
||
.grid-toggle-btn {
|
||
width: 36px;
|
||
height: 34px;
|
||
}
|
||
.cell-max,
|
||
.cell-monitor-btn {
|
||
padding: 6px 9px;
|
||
font-size: 15px;
|
||
}
|
||
}
|
||
|
||
/* ── Key bar (rounded chips) ─────────────────────────────────────── */
|
||
#keybar {
|
||
position: fixed;
|
||
inset: auto 0 0 0;
|
||
box-sizing: border-box;
|
||
height: calc(var(--keybar-h) + var(--safe-b));
|
||
background: var(--surface-1);
|
||
border-top: 1px solid var(--border);
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 5px;
|
||
padding: 0 6px var(--safe-b);
|
||
overflow-x: auto;
|
||
overflow-y: hidden;
|
||
z-index: 1000;
|
||
}
|
||
#keybar::-webkit-scrollbar {
|
||
height: 0;
|
||
}
|
||
#keybar button {
|
||
flex: 1 0 auto;
|
||
min-width: 50px;
|
||
height: calc(var(--keybar-h) - 12px);
|
||
padding: 0 10px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 1px;
|
||
border: 1px solid var(--border);
|
||
background: var(--surface-2);
|
||
color: var(--text);
|
||
font-family: var(--ui-font);
|
||
cursor: pointer;
|
||
border-radius: 8px;
|
||
transition:
|
||
background 0.1s ease,
|
||
transform 0.05s ease;
|
||
user-select: none;
|
||
-webkit-user-select: none;
|
||
-webkit-touch-callout: none;
|
||
}
|
||
#keybar .kb-key {
|
||
font-size: 13px;
|
||
line-height: 1.1;
|
||
}
|
||
#keybar .kb-cap {
|
||
font-size: 9px;
|
||
line-height: 1;
|
||
color: var(--text-faint);
|
||
letter-spacing: 0.02em;
|
||
}
|
||
#keybar button:hover {
|
||
background: var(--surface-3);
|
||
}
|
||
#keybar button:active {
|
||
transform: translateY(1px);
|
||
background: var(--surface-3);
|
||
}
|
||
#keybar button.keybar-btn-primary {
|
||
background: var(--accent-soft);
|
||
color: #f0d49a;
|
||
border-color: rgba(227, 166, 74, 0.4);
|
||
font-weight: 600;
|
||
min-width: 54px;
|
||
}
|
||
#keybar button.keybar-btn-primary:hover {
|
||
background: rgba(227, 166, 74, 0.26);
|
||
}
|
||
#keybar button.keybar-btn-primary .kb-cap {
|
||
color: rgba(240, 212, 154, 0.7);
|
||
}
|
||
|
||
/* ── Shared: overlays, cards, inputs, buttons ────────────────────── */
|
||
.overlay {
|
||
position: fixed;
|
||
inset: 0;
|
||
z-index: 1200;
|
||
display: flex;
|
||
background: rgba(8, 9, 12, 0.6);
|
||
-webkit-backdrop-filter: blur(6px);
|
||
backdrop-filter: blur(6px);
|
||
}
|
||
|
||
#searchbox {
|
||
position: fixed;
|
||
top: calc(var(--tabbar-h) + var(--safe-t) + 8px);
|
||
right: 10px;
|
||
z-index: 1100;
|
||
display: flex;
|
||
gap: 5px;
|
||
padding: 7px;
|
||
background: var(--surface-2);
|
||
border: 1px solid var(--border-strong);
|
||
border-radius: 10px;
|
||
box-shadow: var(--shadow);
|
||
}
|
||
.search-input {
|
||
width: 190px;
|
||
padding: 5px 8px;
|
||
font: inherit;
|
||
font-size: 13px;
|
||
color: #fff;
|
||
background: var(--bg);
|
||
border: 1px solid var(--border);
|
||
border-radius: 7px;
|
||
outline: none;
|
||
}
|
||
.search-input:focus {
|
||
border-color: var(--accent);
|
||
}
|
||
#searchbox button {
|
||
background: var(--surface-3);
|
||
border: 1px solid var(--border);
|
||
color: var(--text);
|
||
cursor: pointer;
|
||
border-radius: 7px;
|
||
padding: 0 10px;
|
||
}
|
||
#searchbox button:hover {
|
||
background: var(--accent-soft);
|
||
}
|
||
|
||
/* QR + share modals */
|
||
#qrmodal,
|
||
#sharemodal {
|
||
position: fixed;
|
||
inset: 0;
|
||
z-index: 1200;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: rgba(8, 9, 12, 0.6);
|
||
-webkit-backdrop-filter: blur(6px);
|
||
backdrop-filter: blur(6px);
|
||
}
|
||
.qr-card {
|
||
background: var(--surface-2);
|
||
color: var(--text);
|
||
border: 1px solid var(--border-strong);
|
||
border-radius: var(--radius-lg);
|
||
padding: 22px;
|
||
text-align: center;
|
||
max-width: 90vw;
|
||
box-shadow: var(--shadow);
|
||
}
|
||
.qr-card canvas {
|
||
border-radius: 10px;
|
||
background: #fff;
|
||
padding: 8px;
|
||
}
|
||
.qr-title {
|
||
font-weight: 600;
|
||
margin-bottom: 14px;
|
||
}
|
||
.qr-url {
|
||
margin-top: 12px;
|
||
font-size: 13px;
|
||
color: var(--accent);
|
||
word-break: break-all;
|
||
}
|
||
.qr-note {
|
||
margin-top: 8px;
|
||
font-size: 12px;
|
||
color: var(--text-dim);
|
||
max-width: 240px;
|
||
}
|
||
.qr-close {
|
||
margin-top: 16px;
|
||
padding: 8px 20px;
|
||
border: 1px solid var(--border-strong);
|
||
border-radius: 8px;
|
||
background: var(--surface-3);
|
||
color: #fff;
|
||
cursor: pointer;
|
||
font: inherit;
|
||
}
|
||
.qr-close:hover {
|
||
background: var(--accent-soft);
|
||
}
|
||
|
||
/* Settings panel */
|
||
#settingspanel {
|
||
position: fixed;
|
||
top: calc(var(--tabbar-h) + var(--safe-t) + 8px);
|
||
right: 10px;
|
||
z-index: 1100;
|
||
background: var(--surface-2);
|
||
border: 1px solid var(--border-strong);
|
||
border-radius: 12px;
|
||
padding: 12px;
|
||
min-width: 220px;
|
||
box-shadow: var(--shadow);
|
||
color: var(--text);
|
||
}
|
||
.settings-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
margin: 8px 0;
|
||
}
|
||
.settings-label {
|
||
width: 48px;
|
||
color: var(--text-dim);
|
||
font-size: 13px;
|
||
}
|
||
.settings-opt {
|
||
background: var(--surface-3);
|
||
border: 1px solid var(--border);
|
||
color: var(--text);
|
||
border-radius: 7px;
|
||
padding: 5px 10px;
|
||
cursor: pointer;
|
||
font: inherit;
|
||
font-size: 12px;
|
||
transition: background 0.1s ease;
|
||
}
|
||
.settings-opt:hover {
|
||
background: var(--surface-1);
|
||
}
|
||
.settings-opt.active {
|
||
background: var(--accent);
|
||
color: var(--on-accent);
|
||
border-color: var(--accent);
|
||
}
|
||
.settings-size {
|
||
min-width: 22px;
|
||
text-align: center;
|
||
}
|
||
|
||
/* Dashboard + history + shortcuts share the centered-card look */
|
||
#dashboard,
|
||
#historymodal,
|
||
#shortcutsmodal {
|
||
position: fixed;
|
||
inset: 0;
|
||
z-index: 1200;
|
||
display: flex;
|
||
align-items: flex-start;
|
||
justify-content: center;
|
||
padding-top: 60px;
|
||
background: rgba(8, 9, 12, 0.6);
|
||
-webkit-backdrop-filter: blur(6px);
|
||
backdrop-filter: blur(6px);
|
||
}
|
||
.dash-card,
|
||
.hist-card,
|
||
.sc-card {
|
||
background: var(--surface-2);
|
||
border: 1px solid var(--border-strong);
|
||
border-radius: var(--radius-lg);
|
||
min-width: 340px;
|
||
max-width: 92vw;
|
||
max-height: 72vh;
|
||
overflow-y: auto;
|
||
box-shadow: var(--shadow);
|
||
}
|
||
.sc-card {
|
||
width: 540px;
|
||
}
|
||
.dash-title,
|
||
.hist-title,
|
||
.sc-title {
|
||
padding: 14px 18px;
|
||
font-weight: 600;
|
||
color: #fff;
|
||
border-bottom: 1px solid var(--border);
|
||
position: sticky;
|
||
top: 0;
|
||
background: var(--surface-2);
|
||
}
|
||
|
||
/* Shortcut cheat-sheet */
|
||
.sc-group {
|
||
padding: 12px 18px 4px;
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
letter-spacing: 0.06em;
|
||
text-transform: uppercase;
|
||
color: var(--accent);
|
||
}
|
||
.sc-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
padding: 6px 18px;
|
||
}
|
||
.sc-key {
|
||
flex: none;
|
||
min-width: 110px;
|
||
font-family: Menlo, Consolas, monospace;
|
||
font-size: 12px;
|
||
color: #fff;
|
||
background: var(--bg);
|
||
border: 1px solid var(--border-strong);
|
||
border-radius: 6px;
|
||
padding: 3px 8px;
|
||
text-align: center;
|
||
}
|
||
.sc-desc {
|
||
flex: 1 1 auto;
|
||
color: var(--text-dim);
|
||
font-size: 13px;
|
||
}
|
||
.sc-tag {
|
||
flex: none;
|
||
font-size: 10px;
|
||
color: var(--accent);
|
||
background: var(--accent-soft);
|
||
border-radius: 5px;
|
||
padding: 2px 6px;
|
||
}
|
||
.dash-row,
|
||
.hist-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
padding: 11px 18px;
|
||
cursor: pointer;
|
||
color: var(--text);
|
||
border-bottom: 1px solid var(--border);
|
||
transition: background 0.1s ease;
|
||
}
|
||
.dash-row:hover,
|
||
.hist-row:hover {
|
||
background: var(--surface-3);
|
||
}
|
||
.dash-row.active {
|
||
box-shadow: inset 3px 0 0 var(--accent);
|
||
}
|
||
.dash-name {
|
||
flex: 1;
|
||
}
|
||
.dash-claude {
|
||
color: var(--text-dim);
|
||
font-size: 13px;
|
||
}
|
||
.dash-claude.pending {
|
||
color: var(--amber);
|
||
font-weight: 600;
|
||
}
|
||
|
||
.hist-empty {
|
||
padding: 18px;
|
||
color: var(--text-dim);
|
||
}
|
||
.hist-row {
|
||
cursor: default;
|
||
}
|
||
.hist-main {
|
||
flex: 1 1 auto;
|
||
min-width: 0;
|
||
}
|
||
.hist-proj {
|
||
color: #fff;
|
||
font-size: 13px;
|
||
}
|
||
.hist-prev {
|
||
color: var(--text-dim);
|
||
font-size: 12px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
.hist-resume {
|
||
flex: none;
|
||
background: var(--accent);
|
||
color: var(--on-accent);
|
||
border: none;
|
||
border-radius: 8px;
|
||
padding: 7px 15px;
|
||
cursor: pointer;
|
||
font: inherit;
|
||
font-weight: 500;
|
||
transition: background 0.1s ease;
|
||
}
|
||
.hist-resume:hover {
|
||
background: var(--accent-2);
|
||
}
|
||
|
||
/* Approval banner (H3) */
|
||
#approvalbar {
|
||
position: fixed;
|
||
inset: auto 0 calc(var(--keybar-h) + var(--safe-b)) 0;
|
||
z-index: 1050;
|
||
display: flex;
|
||
flex-wrap: wrap; /* W1: a preview row wraps below the label + buttons */
|
||
align-items: center;
|
||
gap: 10px;
|
||
padding: 10px 14px;
|
||
background: linear-gradient(180deg, rgba(245, 177, 76, 0.16), rgba(245, 177, 76, 0.08));
|
||
border-top: 1px solid rgba(245, 177, 76, 0.5);
|
||
color: #ffe0ad;
|
||
font-size: 14px;
|
||
}
|
||
.approval-label {
|
||
flex: 1 1 auto;
|
||
}
|
||
/* W1: approval preview (command/diff) — its own full-width row, scrollable. */
|
||
.approval-preview {
|
||
flex: 1 1 100%;
|
||
order: 3; /* below the label + buttons regardless of insertion order */
|
||
max-height: 30vh;
|
||
overflow: auto;
|
||
overflow-x: auto;
|
||
border: 1px solid rgba(245, 177, 76, 0.35);
|
||
border-radius: 8px;
|
||
background: rgba(0, 0, 0, 0.35);
|
||
padding: 8px 10px;
|
||
}
|
||
.approval-cmd {
|
||
margin: 0;
|
||
white-space: pre-wrap;
|
||
word-break: break-word;
|
||
font-family: Menlo, Consolas, monospace;
|
||
font-size: 13px;
|
||
color: #f4f5f7;
|
||
}
|
||
.approval-truncated {
|
||
margin-top: 6px;
|
||
font-size: 12px;
|
||
opacity: 0.7;
|
||
}
|
||
#approvalbar button {
|
||
flex: none;
|
||
border: none;
|
||
border-radius: 8px;
|
||
padding: 8px 16px;
|
||
cursor: pointer;
|
||
font: inherit;
|
||
font-weight: 600;
|
||
}
|
||
.approval-yes {
|
||
background: var(--green);
|
||
color: #06210f;
|
||
}
|
||
.approval-no {
|
||
background: var(--red);
|
||
color: #2a0808;
|
||
}
|
||
|
||
/* ── Launcher (home session chooser, shown when no tab is open) ──── */
|
||
.launcher {
|
||
position: absolute;
|
||
inset: 52px 0 0 0; /* mobile: reserve a row for the stacked .home-seg (desktop overrides to 0) */
|
||
overflow-y: auto;
|
||
padding: 22px 18px 40px;
|
||
box-sizing: border-box;
|
||
background: var(--bg);
|
||
}
|
||
.launcher-head {
|
||
display: flex;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
gap: 10px 16px;
|
||
margin-bottom: 18px;
|
||
}
|
||
.launcher-title {
|
||
font-size: 20px;
|
||
font-weight: 700;
|
||
color: #fff;
|
||
}
|
||
.launcher-sub {
|
||
flex: 1 1 auto;
|
||
color: var(--text-dim);
|
||
font-size: 13px;
|
||
}
|
||
.launcher-actions {
|
||
display: flex;
|
||
gap: 8px;
|
||
}
|
||
/* "New session" tile — leads the session grid, matching the session cards. */
|
||
.mg-new-card {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 8px;
|
||
min-height: 200px;
|
||
background: none;
|
||
border: 2px dashed var(--border-strong);
|
||
border-radius: 12px;
|
||
color: var(--text-dim);
|
||
cursor: pointer;
|
||
font: inherit;
|
||
transition: border-color 0.12s ease, color 0.12s ease, background 0.12s ease;
|
||
}
|
||
.mg-new-card:hover {
|
||
border-color: var(--accent);
|
||
color: var(--accent);
|
||
background: var(--accent-soft);
|
||
}
|
||
.mg-new-plus {
|
||
font-size: 34px;
|
||
font-weight: 300;
|
||
line-height: 1;
|
||
}
|
||
.mg-new-label {
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
/* ── Session Manager page (manage.html) ──────────────────────────── */
|
||
#manage-root {
|
||
max-width: 1200px;
|
||
margin: 0 auto;
|
||
padding: 24px 16px 60px;
|
||
height: 100%;
|
||
overflow-y: auto;
|
||
box-sizing: border-box;
|
||
}
|
||
.mg-header {
|
||
margin-bottom: 18px;
|
||
}
|
||
.mg-title {
|
||
font-size: 22px;
|
||
font-weight: 700;
|
||
color: #fff;
|
||
}
|
||
.mg-sub {
|
||
color: var(--text-dim);
|
||
font-size: 13px;
|
||
margin-top: 2px;
|
||
}
|
||
.mg-bar {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
margin-top: 14px;
|
||
}
|
||
.mg-btn {
|
||
background: var(--surface-2);
|
||
border: 1px solid var(--border-strong);
|
||
color: var(--text);
|
||
border-radius: 8px;
|
||
padding: 8px 14px;
|
||
cursor: pointer;
|
||
font: inherit;
|
||
font-size: 13px;
|
||
text-decoration: none;
|
||
transition: background 0.1s ease;
|
||
}
|
||
.mg-btn:hover {
|
||
background: var(--surface-3);
|
||
}
|
||
.mg-btn.warn {
|
||
border-color: rgba(245, 177, 76, 0.5);
|
||
color: var(--amber);
|
||
}
|
||
.mg-btn.danger {
|
||
border-color: rgba(255, 107, 107, 0.5);
|
||
color: var(--red);
|
||
}
|
||
.mg-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(330px, 1fr));
|
||
gap: 14px;
|
||
}
|
||
.mg-card {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 10px;
|
||
padding: 12px;
|
||
background: var(--surface-2);
|
||
border: 1px solid var(--border);
|
||
border-radius: 12px;
|
||
}
|
||
.mg-card-head {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
.mg-name {
|
||
flex: 1 1 auto;
|
||
min-width: 0;
|
||
color: #fff;
|
||
font-weight: 600;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
/* Live preview thumbnail (scaled read-only xterm) */
|
||
.mg-thumb {
|
||
position: relative;
|
||
width: 100%;
|
||
min-height: 90px;
|
||
background: #0e0f13;
|
||
border: 1px solid var(--border);
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
cursor: pointer;
|
||
}
|
||
.mg-thumb:hover {
|
||
border-color: var(--accent);
|
||
}
|
||
.mg-thumb-inner {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
transform-origin: top left;
|
||
padding: 4px;
|
||
}
|
||
.mg-watch {
|
||
font-size: 11px;
|
||
color: var(--text-faint);
|
||
background: var(--surface-3);
|
||
border-radius: 5px;
|
||
padding: 1px 6px;
|
||
}
|
||
.mg-watch.live {
|
||
color: var(--green);
|
||
background: rgba(70, 208, 127, 0.14);
|
||
}
|
||
.mg-status {
|
||
font-size: 11px;
|
||
color: var(--text-dim);
|
||
}
|
||
.mg-status.mg-waiting {
|
||
color: var(--amber);
|
||
font-weight: 600;
|
||
}
|
||
.mg-status.mg-working {
|
||
color: var(--accent);
|
||
}
|
||
.mg-meta {
|
||
color: var(--text-faint);
|
||
font-size: 11px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
font-family: Menlo, Consolas, monospace;
|
||
}
|
||
.mg-actions {
|
||
display: flex;
|
||
gap: 6px;
|
||
}
|
||
.mg-actions .mg-open {
|
||
flex: 1 1 auto;
|
||
text-align: center;
|
||
}
|
||
.mg-open {
|
||
background: var(--accent);
|
||
color: var(--on-accent);
|
||
border-radius: 8px;
|
||
padding: 7px 13px;
|
||
text-decoration: none;
|
||
font-size: 13px;
|
||
}
|
||
.mg-open:hover {
|
||
background: var(--accent-2);
|
||
}
|
||
.mg-kill {
|
||
background: transparent;
|
||
border: 1px solid rgba(255, 107, 107, 0.4);
|
||
color: var(--red);
|
||
border-radius: 8px;
|
||
padding: 7px 13px;
|
||
cursor: pointer;
|
||
font: inherit;
|
||
font-size: 13px;
|
||
}
|
||
.mg-kill:hover {
|
||
background: rgba(255, 107, 107, 0.14);
|
||
}
|
||
.mg-empty {
|
||
color: var(--text-dim);
|
||
padding: 30px 0;
|
||
text-align: center;
|
||
}
|
||
|
||
/* ── v0.6 Projects panel ─────────────────────────────────────────────────── */
|
||
|
||
/* Panel wrapper (same host as .launcher; toggled via display) */
|
||
.proj-panel {
|
||
position: absolute;
|
||
inset: 52px 0 0 0; /* mobile: reserve a row for the stacked .home-seg (desktop overrides to 0) */
|
||
overflow-y: auto;
|
||
padding: 22px 18px 40px;
|
||
box-sizing: border-box;
|
||
background: var(--bg);
|
||
}
|
||
|
||
/* Search box */
|
||
.proj-search-wrap {
|
||
margin-bottom: 16px;
|
||
}
|
||
.proj-search {
|
||
width: 100%;
|
||
max-width: 320px;
|
||
padding: 8px 12px;
|
||
font: inherit;
|
||
font-size: 14px;
|
||
color: var(--text);
|
||
background: var(--surface-2);
|
||
border: 1px solid var(--border-strong);
|
||
border-radius: 8px;
|
||
outline: none;
|
||
box-sizing: border-box;
|
||
}
|
||
.proj-search:focus {
|
||
border-color: var(--accent);
|
||
box-shadow: 0 0 0 2px var(--accent-soft);
|
||
}
|
||
|
||
/* Outer container is now a vertical stack of collapsible namespace sections. */
|
||
.proj-grid {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 22px;
|
||
}
|
||
|
||
/* The actual card grid — one per group (and the flat fallback). Mirrors .mg-grid. */
|
||
.proj-grid-cards {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||
gap: 14px;
|
||
}
|
||
|
||
/* ── Namespace group section ─────────────────────────────────────────────── */
|
||
.proj-group {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
}
|
||
.proj-group-head {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 9px;
|
||
padding: 4px 2px;
|
||
position: sticky; /* keep the namespace visible while scrolling a long group */
|
||
top: 0;
|
||
z-index: 1;
|
||
background: var(--bg);
|
||
user-select: none;
|
||
}
|
||
.proj-group-head[role='button'] {
|
||
cursor: pointer;
|
||
border-radius: 7px;
|
||
}
|
||
.proj-group-head[role='button']:hover {
|
||
background: var(--surface-1);
|
||
}
|
||
.proj-group-head[role='button']:focus-visible {
|
||
outline: 2px solid var(--accent);
|
||
outline-offset: 1px;
|
||
}
|
||
.proj-group-caret {
|
||
color: var(--text-faint);
|
||
font-size: 11px;
|
||
width: 12px;
|
||
flex: none;
|
||
text-align: center;
|
||
}
|
||
.proj-group-label {
|
||
color: var(--text);
|
||
font-weight: 600;
|
||
font-size: 13px;
|
||
letter-spacing: 0.01em;
|
||
}
|
||
.proj-group-count {
|
||
color: var(--text-faint);
|
||
font-size: 12px;
|
||
font-family: Menlo, Consolas, monospace;
|
||
}
|
||
/* "N active" badge on a namespace/other header — never lose running work behind a caret. */
|
||
.proj-group-active {
|
||
color: var(--green);
|
||
font-size: 11px;
|
||
margin-left: 2px;
|
||
}
|
||
/* "Active now" header — pinned, accent-tinted, non-collapsible. */
|
||
.proj-group-head.proj-group-active .proj-group-caret {
|
||
color: var(--green);
|
||
font-size: 9px;
|
||
}
|
||
.proj-group-head.proj-group-active .proj-group-label {
|
||
color: var(--accent);
|
||
}
|
||
.proj-group.collapsed {
|
||
gap: 0;
|
||
}
|
||
|
||
/* Project card */
|
||
.proj-card {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
padding: 14px;
|
||
background: var(--surface-2);
|
||
border: 1px solid var(--border);
|
||
border-radius: 12px;
|
||
transition: border-color 0.12s ease;
|
||
}
|
||
.proj-card:hover {
|
||
border-color: var(--border-strong);
|
||
}
|
||
|
||
/* Card header row */
|
||
.proj-card-head {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
/* Repo name */
|
||
.proj-name {
|
||
flex: 1 1 auto;
|
||
color: #fff;
|
||
font-weight: 600;
|
||
font-size: 14px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
/* Branch chip */
|
||
.proj-branch {
|
||
font-size: 11px;
|
||
color: var(--accent);
|
||
background: var(--accent-soft);
|
||
border-radius: 5px;
|
||
padding: 2px 7px;
|
||
white-space: nowrap;
|
||
flex: none;
|
||
}
|
||
|
||
/* Dirty indicator (● dot) */
|
||
.proj-dirty {
|
||
font-size: 10px;
|
||
color: var(--amber);
|
||
flex: none;
|
||
}
|
||
|
||
/* W3(a): ahead/behind sync chip (mirrors the .proj-branch chip look). */
|
||
.proj-sync {
|
||
font-size: 11px;
|
||
color: var(--amber);
|
||
background: var(--accent-soft);
|
||
border-radius: 5px;
|
||
padding: 2px 7px;
|
||
white-space: nowrap;
|
||
flex: none;
|
||
}
|
||
|
||
/* ── w6/G3: the project detail sync band ──────────────────────────────────────
|
||
Four labelled cells, per docs/mockups/project-detail-git.html. The captions
|
||
and footnotes are load-bearing, not decoration: they are what tells a reader
|
||
WHY a zero cannot be trusted, which is the whole reason this band exists.
|
||
Semantic colours are deliberately NOT the accent; --ok reads "verified,
|
||
ignore this" and must stay unreachable from a stale or unknowable state. */
|
||
.proj-syncband {
|
||
display: flex;
|
||
align-items: stretch;
|
||
margin: 10px 0 4px;
|
||
background: var(--surface-1);
|
||
border: 1px solid var(--border);
|
||
border-radius: 9px;
|
||
overflow: hidden;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.proj-sync-cell {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 3px;
|
||
padding: 11px 18px;
|
||
min-width: 0;
|
||
border-right: 1px solid var(--border);
|
||
}
|
||
|
||
.proj-sync-cell:last-child {
|
||
border-right: 0;
|
||
}
|
||
|
||
.proj-sync-actions {
|
||
margin-left: auto;
|
||
justify-content: center;
|
||
}
|
||
|
||
.proj-sync-k {
|
||
font-size: 10.5px;
|
||
letter-spacing: 0.13em;
|
||
text-transform: uppercase;
|
||
color: var(--text-faint);
|
||
}
|
||
|
||
.proj-sync-v {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 7px;
|
||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||
font-size: 15px;
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
|
||
.proj-sync-big {
|
||
font-size: 19px;
|
||
font-weight: 600;
|
||
letter-spacing: -0.02em;
|
||
}
|
||
|
||
.proj-sync-sub {
|
||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||
font-size: 11px;
|
||
color: var(--text-faint);
|
||
}
|
||
|
||
.proj-sync-sub-bad {
|
||
color: #cf6b4f;
|
||
}
|
||
|
||
.proj-sync-upstream {
|
||
color: var(--text-dim);
|
||
}
|
||
|
||
.proj-sync-chip {
|
||
font-size: 11.5px;
|
||
font-variant-numeric: tabular-nums;
|
||
border-radius: 5px;
|
||
padding: 2px 8px;
|
||
white-space: nowrap;
|
||
flex: none;
|
||
border: 1px solid transparent;
|
||
}
|
||
|
||
.proj-sync-ahead {
|
||
color: var(--accent);
|
||
}
|
||
|
||
.proj-sync-behind {
|
||
color: var(--text-faint);
|
||
}
|
||
|
||
/* The one reassuring state. Reachable only with an upstream AND a fresh fetch. */
|
||
.proj-sync-ok {
|
||
color: #7d9b76;
|
||
background: rgba(125, 155, 118, 0.13);
|
||
border-color: rgba(125, 155, 118, 0.3);
|
||
}
|
||
|
||
/* "This number may be lying" — never applied to ahead, which needs no fetch. */
|
||
.proj-sync-stale,
|
||
.proj-sync-noupstream,
|
||
.proj-sync-detached {
|
||
color: #cf6b4f;
|
||
}
|
||
|
||
.proj-sync-chip.proj-sync-stale,
|
||
.proj-sync-noupstream,
|
||
.proj-sync-detached {
|
||
background: rgba(207, 107, 79, 0.14);
|
||
border-color: rgba(207, 107, 79, 0.36);
|
||
}
|
||
|
||
.proj-sync-dirty {
|
||
color: var(--amber);
|
||
background: rgba(224, 151, 90, 0.13);
|
||
border: 1px solid rgba(224, 151, 90, 0.3);
|
||
border-radius: 5px;
|
||
padding: 2px 8px;
|
||
font-size: 11.5px;
|
||
white-space: nowrap;
|
||
flex: none;
|
||
}
|
||
|
||
.proj-sync-fetch {
|
||
font: inherit;
|
||
font-size: 12.5px;
|
||
font-weight: 560;
|
||
cursor: pointer;
|
||
padding: 7px 15px;
|
||
border-radius: 7px;
|
||
border: 0;
|
||
background: var(--accent);
|
||
color: var(--on-accent);
|
||
}
|
||
|
||
.proj-sync-fetch:hover:not(:disabled) {
|
||
filter: brightness(1.08);
|
||
}
|
||
|
||
.proj-sync-fetch:disabled {
|
||
opacity: 0.45;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.proj-sync-fetch:focus-visible {
|
||
outline: 2px solid var(--text);
|
||
outline-offset: 2px;
|
||
}
|
||
|
||
@media (max-width: 720px) {
|
||
.proj-syncband {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.proj-sync-cell {
|
||
border-right: 0;
|
||
border-bottom: 1px solid var(--border);
|
||
}
|
||
|
||
.proj-sync-actions {
|
||
margin-left: 0;
|
||
}
|
||
}
|
||
|
||
/* ── w6: unpushed count beside the commit-list heading ───────────────────── */
|
||
.proj-commitlog-count {
|
||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||
font-size: 11.5px;
|
||
font-variant-numeric: tabular-nums;
|
||
color: var(--accent);
|
||
text-transform: none;
|
||
letter-spacing: 0;
|
||
margin-left: 10px;
|
||
}
|
||
|
||
/* ── w6/G5: worktree rows as two-line cards ──────────────────────────────── */
|
||
/* NOTE: the row's own display/grid lives with the ORIGINAL .proj-wt-row rule
|
||
further down. Declaring it twice let the later copy silently win and the
|
||
Open button never reached the right edge. One rule per selector. */
|
||
.proj-wt-row-current {
|
||
border-color: rgba(227, 166, 74, 0.36);
|
||
background: linear-gradient(90deg, var(--accent-soft), transparent 46%);
|
||
}
|
||
|
||
.proj-wt-main {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 5px;
|
||
min-width: 0;
|
||
}
|
||
|
||
.proj-wt-top {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 9px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.proj-wt-right {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
flex: none;
|
||
}
|
||
|
||
.proj-wt-open {
|
||
font: inherit;
|
||
font-size: 11.5px;
|
||
cursor: pointer;
|
||
padding: 4px 12px;
|
||
border-radius: 6px;
|
||
border: 1px solid var(--border-strong);
|
||
background: transparent;
|
||
color: var(--text);
|
||
}
|
||
|
||
.proj-wt-open:hover {
|
||
border-color: var(--accent);
|
||
color: var(--accent);
|
||
}
|
||
|
||
.proj-wt-open:focus-visible {
|
||
outline: 2px solid var(--text);
|
||
outline-offset: 2px;
|
||
}
|
||
|
||
/* ── w6/G4: unpushed commits + the upstream boundary ───────────────────────── */
|
||
.proj-commit-unpushed {
|
||
position: relative;
|
||
background: linear-gradient(90deg, var(--accent-soft), transparent 55%);
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.proj-commit-unpushed::before {
|
||
content: '';
|
||
position: absolute;
|
||
left: 0;
|
||
top: 0;
|
||
bottom: 0;
|
||
width: 2px;
|
||
border-radius: 2px;
|
||
background: var(--accent);
|
||
}
|
||
|
||
.proj-commit-mark {
|
||
color: var(--accent);
|
||
font-size: 11px;
|
||
flex: none;
|
||
width: 12px;
|
||
text-align: center;
|
||
}
|
||
|
||
/* Where the upstream actually sits in this history — never drawn without one. */
|
||
.proj-commit-boundary {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
margin: 7px 0 5px;
|
||
}
|
||
|
||
.proj-commit-boundary-label {
|
||
font-size: 10.5px;
|
||
color: var(--text-dim);
|
||
border: 1px solid var(--border);
|
||
border-radius: 999px;
|
||
padding: 2px 10px;
|
||
white-space: nowrap;
|
||
flex: none;
|
||
}
|
||
|
||
.proj-commit-boundary-rule {
|
||
flex: 1;
|
||
height: 1px;
|
||
background: var(--border-strong);
|
||
}
|
||
|
||
/* w6/G7: running sessions attributed to one worktree row. */
|
||
.proj-wt-sessions {
|
||
font-size: 11px;
|
||
font-variant-numeric: tabular-nums;
|
||
color: var(--accent);
|
||
background: var(--accent-soft);
|
||
border-radius: 5px;
|
||
padding: 2px 7px;
|
||
white-space: nowrap;
|
||
flex: none;
|
||
}
|
||
|
||
/* W3(b): cost chip in the per-tab telemetry gauge, warn-styled over budget. */
|
||
.tg-cost-warn {
|
||
color: var(--red);
|
||
font-weight: 600;
|
||
}
|
||
|
||
/* W3(d): recent-commit list in the project detail. */
|
||
.proj-commitlog {
|
||
margin: 4px 0 10px;
|
||
}
|
||
.proj-commitlog-loading {
|
||
font-size: 12px;
|
||
color: var(--text-faint);
|
||
}
|
||
.proj-commitlog-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 2px;
|
||
}
|
||
.proj-commit-row {
|
||
display: flex;
|
||
align-items: baseline;
|
||
gap: 8px;
|
||
font-size: 12px;
|
||
min-width: 0;
|
||
}
|
||
.proj-commit-hash {
|
||
font-family: Menlo, Consolas, monospace;
|
||
color: var(--accent);
|
||
flex: none;
|
||
}
|
||
.proj-commit-time {
|
||
color: var(--text-faint);
|
||
flex: none;
|
||
white-space: nowrap;
|
||
}
|
||
.proj-commit-subject {
|
||
color: var(--text);
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
min-width: 0;
|
||
}
|
||
.proj-commit-more {
|
||
font-size: 11px;
|
||
color: var(--text-faint);
|
||
margin-top: 4px;
|
||
}
|
||
|
||
/* W3(c): "while you were away" reconnect banner (compact, dismissible top bar). */
|
||
.wya-banner {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
z-index: 1000;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
padding: 8px 14px;
|
||
font-size: 13px;
|
||
color: var(--text);
|
||
background: var(--accent-soft);
|
||
border-bottom: 1px solid var(--accent);
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
|
||
}
|
||
.wya-title {
|
||
font-weight: 600;
|
||
color: var(--accent);
|
||
flex: none;
|
||
}
|
||
.wya-summary {
|
||
flex: 1;
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
.wya-cost {
|
||
color: var(--text-faint);
|
||
flex: none;
|
||
}
|
||
.wya-dismiss {
|
||
flex: none;
|
||
border: none;
|
||
background: transparent;
|
||
color: var(--text-faint);
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
line-height: 1;
|
||
padding: 2px 6px;
|
||
}
|
||
.wya-dismiss:hover {
|
||
color: var(--text);
|
||
}
|
||
|
||
/* W3: PR + CI status chip (mirrors the .proj-branch chip look). */
|
||
.proj-pr-host {
|
||
display: inline-flex;
|
||
flex: none;
|
||
}
|
||
.proj-pr-chip {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
font-size: 11px;
|
||
border-radius: 5px;
|
||
padding: 2px 7px;
|
||
white-space: nowrap;
|
||
max-width: 260px;
|
||
color: var(--text-faint);
|
||
background: var(--accent-soft);
|
||
}
|
||
.proj-pr-chip .proj-pr-title {
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
color: var(--text-faint);
|
||
}
|
||
.proj-pr-loading {
|
||
opacity: 0.6;
|
||
}
|
||
/* PR-state modifiers */
|
||
.proj-pr-open .proj-pr-label {
|
||
color: var(--green);
|
||
}
|
||
.proj-pr-draft .proj-pr-label {
|
||
color: var(--text-faint);
|
||
}
|
||
.proj-pr-merged .proj-pr-label {
|
||
color: var(--accent);
|
||
}
|
||
.proj-pr-closed .proj-pr-label {
|
||
color: var(--red);
|
||
}
|
||
.proj-pr-none,
|
||
.proj-pr-unavailable {
|
||
color: var(--text-faint);
|
||
background: transparent;
|
||
}
|
||
/* Checks-rollup modifiers (colour the whole chip label) */
|
||
.proj-pr-checks-ok .proj-pr-label {
|
||
color: var(--green);
|
||
}
|
||
.proj-pr-checks-fail .proj-pr-label {
|
||
color: var(--red);
|
||
}
|
||
.proj-pr-checks-pending .proj-pr-label {
|
||
color: var(--amber);
|
||
}
|
||
/* Mergeable = conflicting */
|
||
.proj-pr-conflict {
|
||
box-shadow: inset 0 0 0 1px var(--red);
|
||
}
|
||
|
||
/* Meta line (last-active time) */
|
||
.proj-meta {
|
||
font-size: 11px;
|
||
color: var(--text-faint);
|
||
font-family: Menlo, Consolas, monospace;
|
||
}
|
||
|
||
/* Favourite star toggle */
|
||
.proj-fav {
|
||
background: none;
|
||
border: none;
|
||
color: var(--text-faint);
|
||
cursor: pointer;
|
||
font-size: 15px;
|
||
padding: 0 2px;
|
||
line-height: 1;
|
||
flex: none;
|
||
transition: color 0.12s ease;
|
||
}
|
||
.proj-fav:hover {
|
||
color: var(--accent);
|
||
}
|
||
.proj-fav.on {
|
||
color: var(--accent);
|
||
}
|
||
|
||
/* Sessions list (1:N) */
|
||
.proj-sessions-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
}
|
||
|
||
/* One running-session row */
|
||
.proj-session-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding: 6px 10px;
|
||
border-radius: 8px;
|
||
background: var(--surface-3);
|
||
cursor: pointer;
|
||
font-size: 13px;
|
||
color: var(--text);
|
||
transition: background 0.1s ease;
|
||
}
|
||
.proj-session-row:hover {
|
||
background: rgba(255, 255, 255, 0.08);
|
||
}
|
||
|
||
/* Status dot on a session row — colours from THEME.md v0.6 note */
|
||
.proj-session-dot {
|
||
width: 8px;
|
||
height: 8px;
|
||
border-radius: 50%;
|
||
flex: none;
|
||
background: var(--text-faint);
|
||
}
|
||
.proj-dot-working {
|
||
background: var(--accent);
|
||
box-shadow: 0 0 5px var(--accent-soft);
|
||
}
|
||
.proj-dot-waiting {
|
||
background: var(--amber);
|
||
}
|
||
.proj-dot-idle {
|
||
background: #8b8a86;
|
||
}
|
||
.proj-dot-unknown {
|
||
background: var(--text-faint);
|
||
}
|
||
|
||
/* Session title */
|
||
.proj-session-title {
|
||
flex: 1 1 auto;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
/* Kill (✕) on a session row — close the session straight from the card. */
|
||
.proj-session-kill {
|
||
flex: none;
|
||
width: 20px;
|
||
height: 20px;
|
||
line-height: 1;
|
||
padding: 0;
|
||
border: none;
|
||
border-radius: 5px;
|
||
background: none;
|
||
color: var(--text-faint);
|
||
cursor: pointer;
|
||
font-size: 11px;
|
||
transition: background 0.1s ease, color 0.1s ease;
|
||
}
|
||
.proj-session-kill:hover {
|
||
background: var(--red, #e5484d);
|
||
color: #fff;
|
||
}
|
||
|
||
/* Client-count badge (👁 N) */
|
||
.proj-session-badge {
|
||
font-size: 11px;
|
||
color: var(--text-dim);
|
||
background: var(--surface-2);
|
||
border-radius: 4px;
|
||
padding: 1px 5px;
|
||
flex: none;
|
||
}
|
||
|
||
/* "+ New" / "+ start claude here" action button */
|
||
/* Launcher row — Claude · Codex · VS Code, each a brand-logo icon button. */
|
||
.proj-launchers {
|
||
display: flex;
|
||
gap: 8px;
|
||
margin-top: 2px;
|
||
}
|
||
.proj-launch {
|
||
position: relative; /* for the active count badge */
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 5px;
|
||
padding: 9px 4px;
|
||
background: var(--surface-2);
|
||
border: 1px solid var(--border);
|
||
border-radius: 9px;
|
||
cursor: pointer;
|
||
font: inherit;
|
||
color: var(--text-dim); /* drives the logo colour (currentColor) by default */
|
||
transition: background 0.12s ease, border-color 0.12s ease, transform 0.08s ease;
|
||
}
|
||
.proj-launch svg {
|
||
display: block;
|
||
}
|
||
.proj-launch-cap {
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
color: var(--text-dim);
|
||
}
|
||
.proj-launch:hover {
|
||
background: var(--surface-3);
|
||
border-color: var(--border-strong);
|
||
transform: translateY(-1px);
|
||
}
|
||
.proj-launch:active {
|
||
transform: translateY(0);
|
||
}
|
||
.proj-launch:hover .proj-launch-cap {
|
||
color: var(--text);
|
||
}
|
||
/* Brand colours on the logos (currentColor) */
|
||
.proj-launch-claude {
|
||
color: #d97757; /* Claude coral */
|
||
}
|
||
.proj-launch-codex {
|
||
color: #e7e8ec; /* OpenAI mark — near-white on dark */
|
||
}
|
||
.proj-launch-vscode {
|
||
color: #3aa0e3; /* VS Code blue */
|
||
}
|
||
|
||
/* Active state — a matching session is running (Claude logo when a Claude
|
||
* session is live). Coral ring + faint tint draw the eye without shouting. */
|
||
.proj-launch-active {
|
||
background: rgba(217, 119, 87, 0.14);
|
||
border-color: #d97757;
|
||
box-shadow: 0 0 0 1px rgba(217, 119, 87, 0.45) inset;
|
||
}
|
||
.proj-launch-active .proj-launch-cap {
|
||
color: #e9b9a6;
|
||
}
|
||
.proj-launch-badge {
|
||
position: absolute;
|
||
top: 4px;
|
||
right: 6px;
|
||
min-width: 15px;
|
||
height: 15px;
|
||
padding: 0 3px;
|
||
box-sizing: border-box;
|
||
border-radius: 8px;
|
||
background: #d97757;
|
||
color: #1a1408;
|
||
font-size: 9px;
|
||
font-weight: 700;
|
||
line-height: 15px;
|
||
text-align: center;
|
||
}
|
||
|
||
/* ── Project detail view ─────────────────────────────────────────── */
|
||
.proj-name-link {
|
||
cursor: pointer;
|
||
}
|
||
.proj-name-link:hover {
|
||
text-decoration: underline;
|
||
text-underline-offset: 2px;
|
||
}
|
||
.proj-detail-inner {
|
||
max-width: 900px;
|
||
margin: 0 auto;
|
||
}
|
||
.proj-back {
|
||
background: none;
|
||
border: 1px solid var(--border);
|
||
color: var(--text-dim);
|
||
border-radius: 8px;
|
||
padding: 6px 12px;
|
||
font: inherit;
|
||
font-size: 13px;
|
||
cursor: pointer;
|
||
margin-bottom: 16px;
|
||
transition: background 0.12s ease, color 0.12s ease;
|
||
}
|
||
.proj-back:hover {
|
||
background: var(--surface-2);
|
||
color: var(--text);
|
||
}
|
||
.proj-detail-head {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
flex-wrap: wrap;
|
||
}
|
||
.proj-detail-name {
|
||
margin: 0;
|
||
font-size: 22px;
|
||
font-weight: 700;
|
||
color: #fff;
|
||
}
|
||
.proj-detail-path {
|
||
margin: 4px 0 8px;
|
||
font-family: Menlo, Consolas, monospace;
|
||
font-size: 12px;
|
||
color: var(--text-faint);
|
||
word-break: break-all;
|
||
}
|
||
.proj-section-title {
|
||
margin: 22px 0 10px;
|
||
font-size: 12px;
|
||
font-weight: 700;
|
||
letter-spacing: 0.06em;
|
||
text-transform: uppercase;
|
||
color: var(--text-faint);
|
||
}
|
||
/* W5 fan-out form — sibling of the New Worktree form. */
|
||
.proj-fanout-form {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
}
|
||
.proj-fanout-prompt {
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
resize: vertical;
|
||
min-height: 44px;
|
||
font: inherit;
|
||
}
|
||
.proj-fanout-row {
|
||
display: flex;
|
||
gap: 8px;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
}
|
||
.proj-fanout-lanes {
|
||
width: 72px;
|
||
}
|
||
.proj-fanout-branch {
|
||
flex: 1 1 160px;
|
||
min-width: 120px;
|
||
}
|
||
.proj-fanout-error {
|
||
color: var(--danger, #e5534b);
|
||
font-size: 12px;
|
||
}
|
||
.proj-fanout-submit {
|
||
align-self: flex-start;
|
||
}
|
||
.proj-fanout-submit:disabled {
|
||
opacity: 0.5;
|
||
cursor: not-allowed;
|
||
}
|
||
/* ── Projects panel controls ──────────────────────────────────────────────────
|
||
These eight controls shipped with class names but no visual rules, so they
|
||
rendered as raw user-agent widgets: white boxes and grey buttons in a dark
|
||
panel. Layout stays with each element's own rule above; this block only sets
|
||
the shared skin, so there is no duplicate declaration of the same property.
|
||
|
||
Two button roles, matching what the panel already established: filled accent
|
||
for the action that commits the form (like Fetch), outline for anything that
|
||
only reveals or toggles (like Open). */
|
||
.proj-wt-branch-input,
|
||
.proj-fanout-prompt,
|
||
.proj-fanout-lanes,
|
||
.proj-fanout-branch,
|
||
.proj-fanout-mode {
|
||
font: inherit;
|
||
font-size: 13px;
|
||
color: var(--text);
|
||
background: var(--surface-2);
|
||
border: 1px solid var(--border-strong);
|
||
border-radius: 8px;
|
||
padding: 8px 11px;
|
||
outline: none;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.proj-wt-branch-input::placeholder,
|
||
.proj-fanout-prompt::placeholder,
|
||
.proj-fanout-branch::placeholder {
|
||
color: var(--text-faint);
|
||
}
|
||
|
||
.proj-wt-branch-input:focus,
|
||
.proj-fanout-prompt:focus,
|
||
.proj-fanout-lanes:focus,
|
||
.proj-fanout-branch:focus,
|
||
.proj-fanout-mode:focus {
|
||
border-color: var(--accent);
|
||
}
|
||
|
||
/* A branch name is an identifier, so it reads as one. */
|
||
.proj-wt-branch-input,
|
||
.proj-fanout-branch {
|
||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||
font-size: 12.5px;
|
||
}
|
||
|
||
.proj-fanout-lanes {
|
||
text-align: center;
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
|
||
.proj-fanout-mode {
|
||
cursor: pointer;
|
||
}
|
||
|
||
.proj-wt-submit,
|
||
.proj-fanout-submit {
|
||
font: inherit;
|
||
font-size: 12.5px;
|
||
font-weight: 560;
|
||
cursor: pointer;
|
||
padding: 8px 15px;
|
||
border: 0;
|
||
border-radius: 8px;
|
||
background: var(--accent);
|
||
color: var(--on-accent);
|
||
}
|
||
|
||
.proj-wt-submit:hover:not(:disabled),
|
||
.proj-fanout-submit:hover:not(:disabled) {
|
||
filter: brightness(1.08);
|
||
}
|
||
|
||
.proj-diff-toggle {
|
||
font: inherit;
|
||
font-size: 12.5px;
|
||
cursor: pointer;
|
||
padding: 7px 14px;
|
||
border-radius: 8px;
|
||
border: 1px solid var(--border-strong);
|
||
background: transparent;
|
||
color: var(--text);
|
||
}
|
||
|
||
.proj-diff-toggle:hover {
|
||
border-color: var(--accent);
|
||
color: var(--accent);
|
||
}
|
||
|
||
.proj-wt-submit:focus-visible,
|
||
.proj-fanout-submit:focus-visible,
|
||
.proj-diff-toggle:focus-visible {
|
||
outline: 2px solid var(--text);
|
||
outline-offset: 2px;
|
||
}
|
||
|
||
/* W5 fan-out status/error banner — fixed, dismissible, textContent only. */
|
||
.fanout-banner {
|
||
position: fixed;
|
||
left: 50%;
|
||
bottom: 16px;
|
||
transform: translateX(-50%);
|
||
max-width: min(680px, 92vw);
|
||
z-index: 60;
|
||
padding: 10px 14px;
|
||
border-radius: 8px;
|
||
background: var(--surface-3, #2a2c33);
|
||
color: var(--text, #e7e8ec);
|
||
border: 1px solid var(--border, #3a3d46);
|
||
box-shadow: 0 6px 24px rgba(0, 0, 0, 0.35);
|
||
font-size: 13px;
|
||
cursor: pointer;
|
||
}
|
||
/* Section header with an inline action (CLAUDE.md generate/update). */
|
||
.proj-section-row {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 12px;
|
||
margin: 22px 0 10px;
|
||
}
|
||
.proj-section-row .proj-section-title {
|
||
margin: 0;
|
||
}
|
||
.proj-claudemd-btn {
|
||
flex: none;
|
||
background: var(--surface-2);
|
||
border: 1px solid var(--border-strong);
|
||
color: var(--accent);
|
||
border-radius: 7px;
|
||
padding: 6px 12px;
|
||
font: inherit;
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
transition: background 0.12s ease, border-color 0.12s ease;
|
||
}
|
||
.proj-claudemd-btn:hover {
|
||
background: var(--accent-soft);
|
||
border-color: var(--accent);
|
||
}
|
||
.proj-claudemd {
|
||
margin: 0;
|
||
max-height: 320px;
|
||
overflow: auto;
|
||
padding: 12px 14px;
|
||
background: var(--surface-2);
|
||
border: 1px solid var(--border);
|
||
border-radius: 10px;
|
||
font-family: Menlo, Consolas, monospace;
|
||
font-size: 12px;
|
||
line-height: 1.5;
|
||
color: var(--text);
|
||
white-space: pre-wrap;
|
||
word-break: break-word;
|
||
}
|
||
/* Worktrees */
|
||
.proj-wt-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 6px;
|
||
}
|
||
.proj-wt-row {
|
||
/* w6: two-line card — content column, then the actions rail pinned right. */
|
||
display: grid;
|
||
grid-template-columns: 1fr auto;
|
||
align-items: center;
|
||
gap: 14px;
|
||
padding: 11px 14px;
|
||
background: var(--surface-2);
|
||
border: 1px solid var(--border);
|
||
border-radius: 8px;
|
||
}
|
||
.proj-wt-branch {
|
||
font-weight: 600;
|
||
color: var(--text);
|
||
}
|
||
.proj-wt-tag {
|
||
font-size: 10px;
|
||
font-weight: 700;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.04em;
|
||
padding: 2px 7px;
|
||
border-radius: 999px;
|
||
background: var(--surface-3);
|
||
color: var(--text-dim);
|
||
}
|
||
.proj-wt-tag-current {
|
||
background: var(--accent);
|
||
color: var(--on-accent);
|
||
}
|
||
.proj-wt-path {
|
||
margin-left: auto;
|
||
font-family: Menlo, Consolas, monospace;
|
||
font-size: 11px;
|
||
color: var(--text-faint);
|
||
word-break: break-all;
|
||
}
|
||
/* Detailed session rows */
|
||
.proj-detail-sessions {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
}
|
||
.proj-dsession {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
padding: 10px 12px;
|
||
background: var(--surface-2);
|
||
border: 1px solid var(--border);
|
||
border-radius: 8px;
|
||
}
|
||
.proj-dsession-main {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 2px;
|
||
min-width: 0;
|
||
flex: 1;
|
||
}
|
||
.proj-dsession-title {
|
||
font-weight: 600;
|
||
color: var(--text);
|
||
}
|
||
.proj-dsession-meta {
|
||
font-size: 12px;
|
||
color: var(--text-dim);
|
||
}
|
||
.proj-dsession-open {
|
||
background: var(--accent);
|
||
color: var(--on-accent);
|
||
border: none;
|
||
border-radius: 7px;
|
||
padding: 6px 14px;
|
||
font: inherit;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
}
|
||
.proj-dsession-open:hover {
|
||
background: var(--accent-2);
|
||
}
|
||
.proj-dsession-kill {
|
||
background: none;
|
||
border: 1px solid var(--border);
|
||
color: var(--text-dim);
|
||
border-radius: 7px;
|
||
width: 30px;
|
||
height: 30px;
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
}
|
||
.proj-dsession-kill:hover {
|
||
background: var(--red, #e5484d);
|
||
border-color: var(--red, #e5484d);
|
||
color: #fff;
|
||
}
|
||
|
||
/* Empty state */
|
||
.proj-empty {
|
||
color: var(--text-dim);
|
||
padding: 30px 0;
|
||
text-align: center;
|
||
}
|
||
|
||
/* ── Home segmented control (P6 builds the DOM; styles defined here per spec) */
|
||
|
||
/* Horizontal pill container, centered above the home grid */
|
||
/* Segmented control: a single rounded "track" holding two pill buttons, with
|
||
* the active one filled (iOS-style). Centred with fit-content + margin auto. */
|
||
.home-seg {
|
||
width: fit-content;
|
||
margin: 10px auto 12px;
|
||
display: flex;
|
||
gap: 3px;
|
||
padding: 3px;
|
||
background: var(--surface-2);
|
||
border: 1px solid var(--border);
|
||
border-radius: 999px;
|
||
}
|
||
|
||
/* Inactive segment */
|
||
.home-seg-btn {
|
||
background: transparent;
|
||
border: none;
|
||
color: var(--text-dim);
|
||
cursor: pointer;
|
||
font: inherit;
|
||
font-size: 13px;
|
||
font-weight: 500;
|
||
padding: 6px 22px;
|
||
border-radius: 999px;
|
||
transition: background 0.12s ease, color 0.12s ease;
|
||
}
|
||
.home-seg-btn:not(.active):hover {
|
||
color: var(--text);
|
||
background: var(--surface-3);
|
||
}
|
||
|
||
/* Active segment — Amber accent; text uses --on-accent for contrast */
|
||
.home-seg-btn.active {
|
||
background: var(--accent);
|
||
color: var(--on-accent);
|
||
font-weight: 600;
|
||
}
|
||
.home-seg-btn.active:hover {
|
||
background: var(--accent-2);
|
||
}
|
||
|
||
/* On the home chooser (Sessions/Projects), there is no active terminal, so the
|
||
* bottom key-bar is just noise — hide it. Toggled by TabApp.updateHomeView(). */
|
||
body.home-open #keybar {
|
||
display: none;
|
||
}
|
||
/* Home chooser hides the key-bar, so reclaim its reserved space at the bottom
|
||
* (keeping the home-indicator clearance so launcher content stays reachable). */
|
||
body.home-open #term {
|
||
bottom: var(--safe-b);
|
||
}
|
||
|
||
/* Desktop: float the Sessions/Projects toggle top-right, on the SAME row as the
|
||
* panel's filter/header — instead of a dedicated row that left a big empty band.
|
||
* The panels fill from the top; the launcher header reserves room for the pill.
|
||
* (Mobile keeps the stacked, centred toggle — see .home-seg above — to avoid
|
||
* overlapping the full-width filter box on narrow screens.) */
|
||
@media (min-width: 769px) {
|
||
.home-seg {
|
||
position: absolute;
|
||
top: 16px;
|
||
right: 20px;
|
||
margin: 0;
|
||
z-index: 6;
|
||
}
|
||
.launcher,
|
||
.proj-panel {
|
||
inset: 0;
|
||
}
|
||
.launcher-head {
|
||
padding-right: 240px; /* clear the absolutely-positioned .home-seg */
|
||
}
|
||
}
|
||
|
||
/* v0.7: timeline + push toggle reuse the minimal .toolbtn look. */
|
||
.toolbtn.active {
|
||
color: var(--accent);
|
||
}
|
||
.toolbtn.push-bell-disabled {
|
||
opacity: 0.45;
|
||
cursor: default;
|
||
}
|
||
.toolbtn.push-bell-disabled:hover {
|
||
background: none;
|
||
color: var(--text-dim);
|
||
}
|
||
|
||
/* v0.7 fix: timeline + push toggle render as minimal line-icon buttons that
|
||
match the .toolbtn toolbar style (transparent, monochrome, accent on
|
||
hover/active). !important defeats the earlier boxed default for these classes. */
|
||
.tab-timeline,
|
||
.push-toggle-btn,
|
||
.push-toggle-disabled {
|
||
background: none !important;
|
||
border: none !important;
|
||
box-shadow: none !important;
|
||
color: var(--text-dim);
|
||
cursor: pointer;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
padding: 7px 9px;
|
||
border-radius: 8px;
|
||
font: inherit;
|
||
font-size: 12px;
|
||
line-height: 1;
|
||
transition: background 0.12s ease, color 0.12s ease;
|
||
}
|
||
.tab-timeline svg,
|
||
.push-toggle-btn svg,
|
||
.push-bell svg {
|
||
width: 18px;
|
||
height: 18px;
|
||
display: block;
|
||
}
|
||
.tab-timeline:hover,
|
||
.push-toggle-btn:hover {
|
||
color: var(--accent);
|
||
background: var(--surface-2) !important;
|
||
}
|
||
.tab-timeline.active,
|
||
.push-toggle-btn.push-toggle-on {
|
||
color: var(--accent);
|
||
}
|
||
.push-toggle-disabled {
|
||
color: var(--text-faint);
|
||
opacity: 0.6;
|
||
cursor: default;
|
||
}
|
||
.push-hint {
|
||
font-size: 11px;
|
||
color: var(--text-faint);
|
||
}
|
||
|
||
/* v0.7: styled hover tooltip on the timeline + push buttons (like .toolbtn).
|
||
Left-anchored — these sit on the left side of the tab bar. */
|
||
.tab-timeline,
|
||
.push-toggle-btn {
|
||
position: relative;
|
||
}
|
||
.tab-timeline[data-tip]::after,
|
||
.push-toggle-btn[data-tip]::after {
|
||
content: attr(data-tip);
|
||
position: absolute;
|
||
top: calc(100% + 6px);
|
||
left: 0;
|
||
white-space: nowrap;
|
||
background: var(--surface-3);
|
||
color: var(--text);
|
||
border: 1px solid var(--border-strong);
|
||
padding: 4px 8px;
|
||
border-radius: 6px;
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transition: opacity 0.12s ease;
|
||
z-index: 60;
|
||
}
|
||
.tab-timeline[data-tip]:hover::after,
|
||
.push-toggle-btn[data-tip]:hover::after {
|
||
opacity: 1;
|
||
}
|
||
|
||
/* ── Saved layout presets dropdown (v3) ──────────────────────────────── */
|
||
.grid-presets {
|
||
position: relative;
|
||
display: inline-flex;
|
||
margin-left: 2px;
|
||
}
|
||
.grid-presets-btn {
|
||
border: 1px solid var(--border);
|
||
background: var(--surface-2);
|
||
color: var(--text-dim);
|
||
width: 30px;
|
||
height: 30px;
|
||
border-radius: 8px;
|
||
cursor: pointer;
|
||
font-size: 13px;
|
||
}
|
||
.grid-presets-btn:hover {
|
||
color: var(--text);
|
||
background: var(--surface-3);
|
||
}
|
||
.grid-presets-menu {
|
||
position: absolute;
|
||
top: calc(100% + 6px);
|
||
right: 0;
|
||
min-width: 220px;
|
||
background: var(--surface-1);
|
||
border: 1px solid var(--border-strong);
|
||
border-radius: 10px;
|
||
box-shadow: var(--shadow);
|
||
padding: 6px;
|
||
z-index: 40;
|
||
}
|
||
.grid-presets-menu[hidden] {
|
||
display: none;
|
||
}
|
||
.gp-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 2px;
|
||
max-height: 240px;
|
||
overflow-y: auto;
|
||
}
|
||
.gp-empty {
|
||
color: var(--text-faint);
|
||
font-size: 12px;
|
||
padding: 8px;
|
||
text-align: center;
|
||
}
|
||
.gp-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
}
|
||
.gp-apply {
|
||
flex: 1;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 8px;
|
||
border: 0;
|
||
background: transparent;
|
||
color: var(--text);
|
||
padding: 6px 8px;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
font-size: 12.5px;
|
||
text-align: left;
|
||
}
|
||
.gp-apply:hover {
|
||
background: var(--surface-2);
|
||
}
|
||
.gp-apply-layout {
|
||
color: var(--text-faint);
|
||
font-size: 11px;
|
||
}
|
||
.gp-del {
|
||
border: 0;
|
||
background: transparent;
|
||
color: var(--text-faint);
|
||
width: 22px;
|
||
height: 22px;
|
||
border-radius: 5px;
|
||
cursor: pointer;
|
||
font-size: 15px;
|
||
line-height: 1;
|
||
}
|
||
.gp-del:hover {
|
||
color: var(--red);
|
||
background: var(--surface-2);
|
||
}
|
||
.gp-save {
|
||
display: flex;
|
||
gap: 4px;
|
||
margin-top: 6px;
|
||
border-top: 1px solid var(--border);
|
||
padding-top: 6px;
|
||
}
|
||
.gp-name {
|
||
flex: 1;
|
||
min-width: 0;
|
||
background: var(--bg);
|
||
border: 1px solid var(--border);
|
||
border-radius: 6px;
|
||
color: var(--text);
|
||
padding: 5px 8px;
|
||
font-size: 12px;
|
||
font-family: var(--ui-font);
|
||
}
|
||
.gp-name:focus {
|
||
outline: none;
|
||
border-color: var(--accent);
|
||
}
|
||
.gp-save-btn {
|
||
border: 0;
|
||
background: var(--accent);
|
||
color: var(--on-accent);
|
||
border-radius: 6px;
|
||
padding: 5px 10px;
|
||
cursor: pointer;
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
}
|
||
.gp-save-btn:hover {
|
||
filter: brightness(1.05);
|
||
}
|
||
|
||
/* ── W4 git write: per-file Stage/Unstage toggle + commit/push bar ─────────── */
|
||
.df-file-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
.df-file-stage {
|
||
margin-left: auto;
|
||
flex: 0 0 auto;
|
||
padding: 3px 10px;
|
||
font-size: 12px;
|
||
border: 1px solid var(--border-strong);
|
||
border-radius: 6px;
|
||
background: var(--surface-2);
|
||
color: var(--text);
|
||
cursor: pointer;
|
||
}
|
||
.df-file-stage:hover {
|
||
background: var(--surface-3);
|
||
border-color: var(--accent);
|
||
}
|
||
.df-commitbar {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
gap: 8px;
|
||
margin-top: 12px;
|
||
padding: 10px;
|
||
border-top: 1px solid var(--border);
|
||
background: var(--surface-1);
|
||
}
|
||
.df-commit-msg {
|
||
flex: 1 1 220px;
|
||
min-width: 0;
|
||
resize: vertical;
|
||
padding: 8px;
|
||
font: inherit;
|
||
color: var(--text);
|
||
background: var(--surface-2);
|
||
border: 1px solid var(--border-strong);
|
||
border-radius: 6px;
|
||
}
|
||
.df-commit-msg:focus {
|
||
outline: none;
|
||
border-color: var(--accent);
|
||
box-shadow: 0 0 0 3px var(--accent-soft);
|
||
}
|
||
.df-commit-btn,
|
||
.df-push-btn {
|
||
flex: 0 0 auto;
|
||
padding: 8px 16px;
|
||
font: inherit;
|
||
font-weight: 600;
|
||
border-radius: 6px;
|
||
border: 1px solid var(--accent);
|
||
background: var(--accent);
|
||
color: #1a1712;
|
||
cursor: pointer;
|
||
}
|
||
.df-push-btn {
|
||
background: var(--surface-2);
|
||
color: var(--text);
|
||
border-color: var(--border-strong);
|
||
}
|
||
.df-commit-btn:disabled,
|
||
.df-push-btn:disabled {
|
||
opacity: 0.5;
|
||
cursor: not-allowed;
|
||
}
|
||
.df-op-status {
|
||
flex: 1 1 100%;
|
||
font-size: 13px;
|
||
word-break: break-word;
|
||
}
|
||
.df-op-error {
|
||
color: var(--red);
|
||
}
|
||
.df-op-notice {
|
||
color: var(--text-dim);
|
||
}
|
||
.df-op-busy {
|
||
opacity: 0.7;
|
||
}
|