Files
AgentHub/client/src/index.css
T
nntrivi2001 c7e934b4b4 fix(run): stop the terminal wheel from typing arrow keys, re-attach dropped sockets
Three separate reasons the Workspace terminal looked unscrollable:

- xterm falls back, on an alt-screen buffer whose program has not enabled
  mouse tracking, to translating each wheel notch into a cursor-key press.
  Inside a `claude` pane that reads as arrow up/down, so scrolling walked the
  prompt history. A custom wheel handler returning false suppresses only that
  emulation branch; real mouse reports come from a separate listener xterm
  registers when the program does request wheel events.
- TerminalView never reconnected, so a server restart left the pane frozen on
  its last painted frame — still live-looking, but swallowing every keystroke
  and mouse report. It now re-attaches 1.5s after an unexpected close and
  stops only on the server's `exit` frame; tmux keeps the session, so the
  re-attach repaints in full.
- tmux repaints the whole pane, so xterm's own scrollback is always empty and
  its viewport scrollbar renders as a groove with no thumb that cannot move.
  Hidden, since scrolling there goes through mouse reports, not the DOM.
2026-08-19 12:02:01 +07:00

207 lines
6.9 KiB
CSS

/**
* @file index.css
* @description Global styles and Tailwind CSS configuration for the application.
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
*/
@tailwind base;
@tailwind components;
@tailwind utilities;
/* Palette sourced from Radix Colors (radix-ui.com/colors), an accessible
* 12-step system built for UI, contrast-checked with APCA — chosen after
* three rounds of hand-picked values that kept overshooting (too flat, then
* too dark, then too glaring). Radix's own role guidance for a 12-step scale:
* 1-2 app/chrome background 6-8 borders (6 subtle, 7-8 stronger)
* 3-5 component background 9-10 solid (buttons, vibrant fills)
* 11-12 text (11 secondary, 12 primary)
* `slate` supplies surface/border/fg here; `blue` supplies accent; `green`/
* `red`/`amber` step 11 supplies the status tokens — the same step used for
* fg-secondary, since 11 is Radix's guaranteed-readable-text step. Values are
* the literal Radix hex constants (steps 1/2/3/4/5/6/7/9/11/12), converted to
* RGB triplets for Tailwind's `<alpha-value>` opacity support.
*/
:root {
/* Light theme — the CSS default, so it applies with no class on <html>.
Radix `slate` (light), steps 1/2/3/4/5/6/7. */
--surface-0: 252 252 253; /* slate-1 #fcfcfd */
--surface-1: 249 249 251; /* slate-2 #f9f9fb */
--surface-2: 240 240 243; /* slate-3 #f0f0f3 */
--surface-3: 232 232 236; /* slate-4 #e8e8ec */
--surface-4: 224 225 230; /* slate-5 #e0e1e6 */
--surface-5: 217 217 224; /* slate-6 #d9d9e0 */
--border: 217 217 224; /* slate-6 #d9d9e0 */
--border-light: 205 206 214; /* slate-7 #cdced6 */
--accent: 0 144 255; /* blue-9 #0090ff */
--accent-hover: 5 136 240; /* blue-10 #0588f0 */
--fg-primary: 28 32 36; /* slate-12 #1c2024 */
--fg-secondary: 96 100 108; /* slate-11 #60646c */
--fg-muted: 139 141 152; /* slate-9 #8b8d98 */
--shadow-card: 0 1px 2px rgb(15 23 42 / 0.06), 0 1px 3px rgb(15 23 42 / 0.08);
/* Status: green/red/amber step 11 (light) — the same "readable text" step
as fg-secondary, so success/danger/warning sit at the same visual weight
as ordinary secondary text, not shouting over it. */
--status-success: 33 131 88; /* green-11 #218358 */
--status-danger: 206 44 49; /* red-11 #ce2c31 */
--status-warning: 171 100 0; /* amber-11 #ab6400 */
}
.dark {
/* Radix `slate` (dark), steps 1/2/3/4/5/6/7. The scale is built in the
opposite direction on purpose — step 6 is LIGHTER than step 3/4 here,
which is what makes borders rise off a panel instead of sinking into it
(the bug in the previous, hand-picked dark border). */
--surface-0: 17 17 19; /* slate-1 #111113 */
--surface-1: 24 25 27; /* slate-2 #18191b */
--surface-2: 33 34 37; /* slate-3 #212225 */
--surface-3: 39 42 45; /* slate-4 #272a2d */
--surface-4: 46 49 53; /* slate-5 #2e3135 */
--surface-5: 54 58 63; /* slate-6 #363a3f */
--border: 54 58 63; /* slate-6 #363a3f */
--border-light: 67 72 78; /* slate-7 #43484e */
--accent: 0 144 255; /* blue-9 #0090ff — identical value both themes, Radix's brand anchor */
--accent-hover: 59 158 255; /* blue-10 #3b9eff */
--fg-primary: 237 238 240; /* slate-12 #edeef0 */
--fg-secondary: 176 180 186; /* slate-11 #b0b4ba */
--fg-muted: 105 110 119; /* slate-9 #696e77 */
/* Dark surfaces already separate by lightness step; a shadow adds nothing
visible against a dark page and reads as a stray dark smudge. */
--shadow-card: none;
--status-success: 61 214 140; /* green-11 dark #3dd68c */
--status-danger: 255 149 146; /* red-11 dark #ff9592 */
--status-warning: 255 202 22; /* amber-11 dark #ffca16 */
}
@layer base {
* {
scrollbar-width: thin;
scrollbar-color: rgb(var(--border)) rgb(var(--surface-2));
}
*::-webkit-scrollbar {
width: 6px;
height: 6px;
}
*::-webkit-scrollbar-track {
background: rgb(var(--surface-2));
}
*::-webkit-scrollbar-thumb {
background: rgb(var(--border));
border-radius: 3px;
}
*::-webkit-scrollbar-thumb:hover {
background: rgb(var(--border-light));
}
::selection {
background: rgb(var(--accent) / 0.3);
color: rgb(var(--fg-primary));
}
::-webkit-calendar-picker-indicator {
filter: invert(1) brightness(0.8);
cursor: pointer;
opacity: 0.7;
transition: opacity 0.2s;
}
::-webkit-calendar-picker-indicator:hover {
opacity: 1;
}
.time-input-custom::-webkit-calendar-picker-indicator {
display: none;
-webkit-appearance: none;
}
}
@layer components {
.card {
@apply bg-surface-3 border border-border rounded-xl;
box-shadow: var(--shadow-card);
}
.card-hover {
@apply card transition-all duration-200 hover:border-border-light hover:bg-surface-4;
}
.btn-primary {
@apply inline-flex items-center gap-2 px-4 py-2 bg-accent hover:bg-accent-hover text-white text-sm font-medium rounded-lg transition-colors duration-150;
}
.btn-ghost {
@apply inline-flex items-center gap-2 px-3 py-1.5 text-sm text-fg-secondary hover:text-fg-primary hover:bg-surface-4 rounded-lg transition-colors duration-150;
}
.badge {
@apply inline-flex items-center gap-1.5 px-2.5 py-1 text-xs font-medium rounded-full border whitespace-nowrap shrink-0;
}
.input {
@apply bg-surface-2 border border-border rounded-lg px-3 py-2 text-sm text-fg-primary placeholder-fg-muted focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent/30 transition-colors;
}
}
@layer utilities {
/* Hide the scrollbar on horizontally scrollable strips (e.g. the Settings
TOC) where chevron affordances replace it. */
.no-scrollbar {
scrollbar-width: none;
}
.no-scrollbar::-webkit-scrollbar {
display: none;
}
/* The run console's terminal attaches to tmux, which repaints the whole pane
on every frame — xterm's own scrollback stays empty forever, so its
viewport scrollbar renders as a groove with no thumb that never moves.
Scrolling there goes through mouse reports to the pane's program, not the
DOM, so hide the dead affordance. */
.xterm-viewport {
scrollbar-width: none;
}
.xterm-viewport::-webkit-scrollbar {
display: none;
}
/* Visually hidden until focused — first Tab lands here before the sidebar. */
.skip-to-content {
position: absolute;
left: 0.75rem;
top: 0.75rem;
z-index: 100;
padding: 0.5rem 0.75rem;
border-radius: 0.5rem;
border: 1px solid rgb(var(--border-light));
background: rgb(var(--surface-2));
color: rgb(var(--fg-primary));
font-size: 0.8125rem;
font-weight: 500;
text-decoration: none;
transform: translateY(-150%);
transition: transform 0.15s ease;
}
.skip-to-content:focus {
transform: translateY(0);
outline: 2px solid rgb(var(--accent));
outline-offset: 2px;
}
}
@keyframes pulse-dot {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.4;
}
}
.animate-pulse-dot {
animation: pulse-dot 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}