Files
rustui-playground/style/tailwind.css
zhangheng a018609a28 refactor(terminal): explicit history buffer + virtual scrolling
Replace the spacer-estimate scrollback model with an explicit history
row buffer owned by TerminalCore, rendered with absolute-positioned
virtual scrolling so huge outputs (cat of 50k-line files) stay smooth.

core.rs:
- TerminalCore owns history_rows + a cached screen, captured after each
  process() via a bounded "conveyor belt" read of vt100 scrollback
- feed bytes in newline-bounded safe slices so a capture never reads
  deeper than viewport_rows (works around vt100 0.15 visible_rows
  subtract-overflow panic)
- handle clear (ESC[3J) by wiping our own history buffer
- TerminalSegment carries grid column count for exact-width rendering
- collect_window() clones only the visible slice

component.rs:
- TerminalCore lives in an RwSignal; render driven by an rAF-coalesced
  render_tick so packet bursts collapse into one frame
- virtual_window pins to the bottom in live mode, follows scroll_top in
  history mode; absolute-positioned rows in a fixed-height sizer keep
  scroll geometry constant (fixes the "keeps drifting up" bounce)
- position-based programmatic-scroll detection replaces the racy
  ignore-next-scroll flag
- alt-screen wheel events translate to arrow keys (vim/less paging)
- try_* signal access guards against disposal during hydration

style: lock row height to 18px, segment width to Nch (exact grid so
fallback-font glyphs like btm braille can't push the row sideways),
absolute-position the virtual rows layer.

Adds 12 unit tests (capture, clear, alt-screen, window slicing, wheel
mapping) and TERMINAL_ROADMAP.md tracking xterm.js parity work.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 19:51:06 +08:00

1316 lines
28 KiB
CSS

@import "tailwindcss";
@source "../app/src/**/*.rs";
@source "../../ui/app_crates/registry/src/ui/button.rs";
@source "../../ui/crates/icons/src/**/*.rs";
:root {
--cowa-page-bg: #f7f8fa;
--cowa-surface: #ffffff;
--cowa-surface-subtle: #f2f3f5;
--cowa-control-bg: #f7f8fa;
--cowa-border: #e5e6eb;
--cowa-border-strong: #c9cdd4;
--cowa-text-primary: #1d2129;
--cowa-text-secondary: #4e5969;
--cowa-text-tertiary: #86909c;
--cowa-primary: #1874ff;
--cowa-primary-hover: #0f66e8;
--cowa-primary-soft: #e7efff;
--cowa-success-bg: #e8ffea;
--cowa-success-text: #00b42a;
--cowa-danger-bg: #ffece8;
--cowa-danger-text: #f53f3f;
--cowa-radius: 4px;
--cowa-control-h: 32px;
--cowa-row-h: 40px;
--cowa-topbar-h: 60px;
--cowa-sidebar-w: 196px;
--cowa-header-brand-w: 190px;
--radius: var(--cowa-radius);
--background: var(--cowa-page-bg);
--foreground: var(--cowa-text-primary);
--card: var(--cowa-surface);
--muted: var(--cowa-surface-subtle);
--muted-foreground: var(--cowa-text-tertiary);
--primary: var(--cowa-primary);
--primary-foreground: #ffffff;
--secondary: #ffffff;
--secondary-foreground: var(--cowa-text-secondary);
--accent: var(--cowa-primary-soft);
--accent-foreground: var(--cowa-primary);
--border: var(--cowa-border);
--ring: var(--cowa-primary);
}
@theme inline {
--radius-md: var(--radius);
--radius-lg: var(--radius);
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-border: var(--border);
--color-ring: var(--ring);
}
@layer base {
* {
@apply box-border border-border;
}
html,
body {
@apply m-0 min-h-screen bg-background text-foreground antialiased;
font-family:
"Inter", "PingFang SC", "Microsoft YaHei", "Noto Sans CJK SC",
system-ui, sans-serif;
font-size: 14px;
letter-spacing: 0;
}
button,
input {
font: inherit;
}
a {
color: inherit;
text-decoration: none;
}
}
@layer components {
.cowa-shell {
@apply min-h-screen bg-background text-[14px] text-foreground;
min-height: 100dvh;
padding-top: var(--cowa-topbar-h);
}
.cowa-shell-collapsed {
--cowa-sidebar-w: 48px;
}
.cowa-sidebar {
@apply fixed left-0 z-20 flex flex-col border-r bg-white;
top: var(--cowa-topbar-h);
width: var(--cowa-sidebar-w);
height: calc(100vh - var(--cowa-topbar-h));
height: calc(100dvh - var(--cowa-topbar-h));
border-color: var(--cowa-border);
transition: width 0.2s ease;
}
.cowa-brand {
@apply flex h-[60px] items-center;
gap: 10px;
padding: 0 16px;
width: var(--cowa-header-brand-w);
color: #0b1e43;
font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
font-size: 20px;
font-weight: 600;
line-height: 28px;
white-space: nowrap;
}
.cowa-brand-logo {
@apply block shrink-0;
width: 32px;
height: 32px;
}
.cowa-nav {
@apply flex flex-1 flex-col gap-1 px-2 py-2;
overflow: visible;
}
.cowa-nav-group {
@apply relative w-full;
}
.cowa-nav-item,
.cowa-nav-subitem,
.cowa-nav-flyout-item {
@apply flex h-10 items-center rounded text-sm font-medium leading-[22px] transition-colors;
color: var(--cowa-text-secondary);
}
.cowa-nav-item {
@apply w-full gap-2 overflow-hidden border-0 bg-transparent px-3 text-left;
}
.cowa-nav-label {
@apply min-w-0 flex-1 truncate;
}
.cowa-nav-caret {
width: 14px;
height: 14px;
flex-shrink: 0;
color: var(--cowa-text-tertiary);
transition: transform 0.2s ease;
}
.cowa-nav-item[aria-expanded="true"] .cowa-nav-caret {
transform: rotate(180deg);
}
.cowa-nav-sublist {
@apply mt-1 flex flex-col gap-1;
}
.cowa-nav-subitem {
@apply ml-7 px-2 font-medium;
color: var(--cowa-text-secondary);
}
.cowa-nav-item:hover,
.cowa-nav-subitem:hover,
.cowa-nav-flyout-item:hover {
background: var(--cowa-surface-subtle);
}
.cowa-nav-item-active,
.cowa-nav-subitem-active,
.cowa-nav-flyout-item-active {
background: var(--cowa-primary-soft);
color: var(--cowa-primary);
}
.cowa-nav-item-has-active-child {
color: var(--cowa-primary);
}
.cowa-nav-flyout {
position: absolute;
top: 0;
left: 100%;
z-index: 40;
min-width: 160px;
margin-left: 8px;
padding: 6px;
border: 1px solid var(--cowa-border);
border-radius: 4px;
background: #fff;
box-shadow: 0 8px 24px rgb(29 33 41 / 12%);
opacity: 0;
visibility: hidden;
pointer-events: none;
transition:
opacity 0.12s ease,
visibility 0s linear 0.12s;
}
/* Transparent bridge that fills the gap between the icon and the flyout so
the cursor never leaves the hover zone while moving onto the menu. */
.cowa-nav-flyout::before {
content: "";
position: absolute;
top: 0;
left: -16px;
width: 16px;
height: 100%;
}
.cowa-nav-flyout-title {
@apply px-2 pb-1 text-xs font-medium;
color: var(--cowa-text-tertiary);
}
.cowa-nav-flyout-item {
@apply px-2 font-medium;
}
.cowa-shell-collapsed .cowa-nav {
@apply items-center px-1;
}
.cowa-shell-collapsed .cowa-nav-group {
@apply flex justify-center;
}
.cowa-shell-collapsed .cowa-nav-item {
@apply h-10 w-9 justify-center px-0;
}
.cowa-shell-collapsed .cowa-nav-label,
.cowa-shell-collapsed .cowa-nav-caret {
display: none;
}
.cowa-shell-collapsed .cowa-nav-group:hover .cowa-nav-flyout,
.cowa-shell-collapsed .cowa-nav-group:focus-within .cowa-nav-flyout {
opacity: 1;
visibility: visible;
pointer-events: auto;
transition:
opacity 0.12s ease,
visibility 0s;
}
.cowa-sidebar-footer {
@apply flex h-12 items-center justify-end border-t px-2 text-xs;
border-color: var(--cowa-border);
color: var(--cowa-text-tertiary);
}
.cowa-sidebar-toggle {
@apply inline-flex h-9 w-9 items-center justify-center rounded border-0 bg-transparent p-0 transition-colors;
color: var(--cowa-text-secondary);
}
.cowa-sidebar-toggle:hover {
background: var(--cowa-surface-subtle);
color: var(--cowa-primary);
}
.cowa-sidebar-toggle-icon {
width: 18px;
height: 18px;
}
.cowa-shell-collapsed .cowa-sidebar-footer {
@apply justify-center px-1;
}
.cowa-main {
@apply min-w-0 overflow-hidden;
margin-left: var(--cowa-sidebar-w);
height: calc(100vh - var(--cowa-topbar-h));
height: calc(100dvh - var(--cowa-topbar-h));
min-height: 0;
transition: margin-left 0.2s ease;
}
.cowa-topbar {
@apply fixed left-0 right-0 top-0 z-30 grid h-[60px] items-center border-b bg-white;
grid-template-columns: var(--cowa-header-brand-w) minmax(0, 1fr);
border-color: var(--cowa-border);
}
.cowa-topbar-main {
@apply flex min-w-0 items-center justify-between gap-4;
padding: 0 16px;
}
.cowa-module-tabs {
@apply flex min-w-0 flex-1 items-center gap-2 overflow-x-auto;
scrollbar-width: none;
}
.cowa-module-tabs::-webkit-scrollbar {
display: none;
}
.cowa-module-tab {
@apply inline-flex shrink-0 items-center justify-center font-medium transition-colors;
height: 40px;
gap: 6px;
padding: 0 14px;
border-radius: 4px;
color: var(--cowa-text-primary);
font-size: 16px;
line-height: 22px;
}
.cowa-module-tab-icon,
.cowa-nav-icon {
width: 14px;
height: 14px;
flex-shrink: 0;
}
.cowa-breadcrumb-icon,
.cowa-control-icon,
.cowa-page-control-icon {
width: 12px;
height: 12px;
flex-shrink: 0;
}
.cowa-module-tab:hover {
background: var(--cowa-surface-subtle);
}
.cowa-module-tab-active {
background: var(--cowa-primary);
color: #fff;
}
.cowa-user-menu {
@apply relative flex shrink-0 items-center;
}
.cowa-user-trigger {
@apply flex h-10 items-center gap-3 rounded border-0 bg-transparent p-0 transition-colors;
color: var(--cowa-text-tertiary);
cursor: pointer;
}
.cowa-user-trigger:hover {
color: var(--cowa-primary);
}
.cowa-avatar {
@apply flex shrink-0 items-center justify-center rounded-full text-sm font-medium text-white;
width: 32px;
height: 32px;
background: linear-gradient(135deg, #1874ff 0%, #00b2ff 100%);
}
.cowa-user-meta {
@apply flex flex-col items-start gap-px;
}
.cowa-user-name {
color: var(--cowa-text-primary);
font-size: 14px;
font-weight: 500;
line-height: 18px;
}
.cowa-user-city {
color: var(--cowa-text-tertiary);
font-size: 12px;
line-height: 14px;
}
.cowa-user-chevron {
width: 12px;
height: 12px;
color: var(--cowa-text-tertiary);
}
.cowa-user-dropdown {
position: absolute;
top: calc(100% + 8px);
right: 0;
z-index: 50;
width: 220px;
padding: 4px 0;
border: 1px solid var(--cowa-border);
border-radius: 4px;
background: #fff;
box-shadow: 0 8px 24px rgb(29 33 41 / 14%);
opacity: 0;
pointer-events: none;
transform: translateY(-4px);
transition:
opacity 0.16s ease,
transform 0.16s ease;
}
.cowa-user-menu:hover .cowa-user-dropdown,
.cowa-user-menu:focus-within .cowa-user-dropdown {
opacity: 1;
pointer-events: auto;
transform: translateY(0);
}
.cowa-user-dropdown-label {
@apply px-3 pb-1 pt-2 text-xs font-medium;
color: var(--cowa-text-tertiary);
}
.cowa-user-dropdown-info {
@apply px-3 py-1 text-sm font-medium;
color: var(--cowa-text-primary);
word-break: break-all;
}
.cowa-user-dropdown-divider {
@apply my-1 h-px;
background: var(--cowa-border);
}
.cowa-user-dropdown-item {
@apply flex h-9 w-full items-center gap-2 border-0 bg-transparent px-3 text-left text-sm transition-colors;
color: var(--cowa-text-primary);
}
.cowa-user-dropdown-item:hover {
background: var(--cowa-surface-subtle);
}
.cowa-user-dropdown-icon {
width: 14px;
height: 14px;
flex-shrink: 0;
color: var(--cowa-text-secondary);
}
.cowa-modal-backdrop {
@apply fixed inset-0 z-[60] flex items-center justify-center;
background: rgb(29 33 41 / 45%);
}
.cowa-password-modal {
width: 440px;
max-width: calc(100vw - 32px);
border-radius: 4px;
background: #fff;
box-shadow: 0 16px 48px rgb(29 33 41 / 20%);
}
.cowa-password-modal h2 {
@apply m-0 border-b px-5 py-4 text-base font-medium;
border-color: var(--cowa-border);
color: var(--cowa-text-primary);
}
.cowa-password-modal p {
@apply m-0 px-5 pt-4 text-sm;
color: var(--cowa-text-secondary);
}
.cowa-password-form {
@apply flex flex-col gap-3 px-5 pb-5 pt-4;
}
.cowa-password-form label {
@apply flex flex-col gap-1 text-sm;
color: var(--cowa-text-primary);
}
.cowa-password-form input {
@apply h-8 rounded border px-3 outline-none transition-colors;
border-color: var(--cowa-border);
background: #fff;
}
.cowa-password-form input:focus {
border-color: var(--cowa-primary);
}
.cowa-password-actions {
@apply mt-1 flex justify-end gap-3;
}
.cowa-password-cancel,
.cowa-password-submit {
@apply h-8 rounded border px-3 text-sm transition-colors;
}
.cowa-password-cancel {
border-color: var(--cowa-border);
background: #fff;
color: var(--cowa-text-secondary);
}
.cowa-password-submit {
border-color: var(--cowa-primary);
background: var(--cowa-primary);
color: #fff;
}
.cowa-content {
@apply flex h-full min-h-0 flex-col overflow-hidden p-4;
}
.cowa-breadcrumb {
@apply mb-2 flex h-6 items-center gap-1 text-xs;
color: var(--cowa-text-tertiary);
}
.cowa-breadcrumb strong {
@apply font-medium;
color: var(--cowa-text-secondary);
}
.cowa-work-surface {
@apply flex flex-1 flex-col bg-white px-4 pt-4;
min-height: 0;
}
.terminal-page-shell {
@apply overflow-hidden rounded border;
border-color: var(--cowa-border);
padding-right: 4px;
padding-bottom: 4px;
}
.terminal-shell-card {
@apply flex flex-1 flex-col gap-4 rounded border bg-white;
border-color: var(--cowa-border);
min-height: 0;
height: 100%;
padding: 18px;
box-shadow: 0 12px 32px rgb(29 33 41 / 6%);
}
.terminal-header {
@apply flex flex-wrap items-start justify-between gap-3;
}
.terminal-title {
margin: 0;
color: #0f172a;
font-family: "IBM Plex Sans", "PingFang SC", "Microsoft YaHei", sans-serif;
font-size: 20px;
font-weight: 600;
line-height: 28px;
}
.terminal-subtitle {
margin: 4px 0 0;
color: var(--cowa-text-secondary);
font-size: 13px;
line-height: 20px;
}
.terminal-badge {
@apply inline-flex items-center rounded-full;
min-height: 28px;
padding: 0 12px;
background: #eef6ff;
color: #1456cc;
font-size: 12px;
font-weight: 600;
line-height: 18px;
}
.terminal-status-stack {
@apply flex flex-col items-end gap-2;
}
.terminal-metrics {
color: var(--cowa-text-tertiary);
font-size: 12px;
line-height: 16px;
}
.terminal-screen {
@apply relative min-h-0 flex-1 overflow-y-auto overflow-x-hidden rounded;
padding: 16px;
border: 1px solid #17202f;
background:
radial-gradient(circle at top right, rgb(56 189 248 / 11%), transparent 28%),
linear-gradient(180deg, #0b1220 0%, #0f172a 100%);
box-shadow: inset 0 1px 0 rgb(255 255 255 / 4%);
outline: none;
color: #dbeafe;
font-family:
"JetBrains Mono", "Fira Code", "Cascadia Code", "SFMono-Regular",
"Consolas", monospace;
font-size: 13px;
line-height: 18px;
white-space: pre;
letter-spacing: 0;
scrollbar-gutter: stable;
scrollbar-width: thin;
scrollbar-color: rgb(148 163 184 / 60%) transparent;
}
.terminal-screen:focus {
border-color: #38bdf8;
box-shadow:
inset 0 1px 0 rgb(255 255 255 / 4%),
0 0 0 3px rgb(56 189 248 / 15%);
}
.terminal-text {
margin: 0;
min-height: 100%;
color: #dbeafe;
font-family:
"JetBrains Mono", "Fira Code", "Cascadia Code", "SFMono-Regular",
"Consolas", monospace;
font-size: 13px;
line-height: 18px;
white-space: pre-wrap;
word-break: break-word;
}
.terminal-grid {
position: relative;
display: block;
min-height: 100%;
}
.terminal-row {
height: 18px;
min-height: 18px;
max-height: 18px;
overflow: hidden;
white-space: pre;
}
.terminal-segment {
display: inline-block;
overflow: hidden;
vertical-align: top;
white-space: pre;
}
.terminal-cursor {
background: #dbeafe !important;
color: #0f172a !important;
box-shadow: 0 0 0 1px rgb(219 234 254 / 30%);
}
/* Absolute-positioning virtual scroll: the sizer holds the full content
height so the scroll geometry is constant; the rows layer is translated
to the visible window's offset. */
.terminal-virtual-sizer {
display: block;
width: 100%;
pointer-events: none;
}
.terminal-virtual-rows {
position: absolute;
top: 0;
left: 0;
right: 0;
will-change: transform;
}
.terminal-measure {
position: absolute;
left: -9999px;
top: 0;
white-space: pre;
pointer-events: none;
opacity: 0;
}
.terminal-screen::-webkit-scrollbar {
width: 10px;
}
.terminal-screen::-webkit-scrollbar-track {
background: transparent;
}
.terminal-screen::-webkit-scrollbar-thumb {
border: 2px solid transparent;
border-radius: 999px;
background: rgb(148 163 184 / 55%);
background-clip: padding-box;
}
.cowa-page-heading {
@apply flex items-start justify-between gap-4 pb-3;
}
.cowa-page-heading h1 {
@apply m-0 text-base font-medium;
color: var(--cowa-text-primary);
line-height: 24px;
}
.cowa-page-heading p {
@apply m-0 pt-1 text-sm;
color: var(--cowa-text-secondary);
}
.cowa-page-heading > span {
@apply shrink-0 rounded px-2 py-1 text-xs;
background: var(--cowa-primary-soft);
color: var(--cowa-primary);
}
.cowa-summary-grid {
@apply mb-4 grid gap-3;
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.cowa-summary-card {
@apply flex h-16 flex-col justify-center rounded border px-4;
border-color: var(--cowa-border);
background: #fff;
}
.cowa-summary-card span {
@apply text-xs;
color: var(--cowa-text-tertiary);
}
.cowa-summary-card strong {
@apply pt-1 text-xl font-semibold;
color: var(--cowa-text-primary);
line-height: 26px;
}
.cowa-summary-card-blue {
border-left: 3px solid var(--cowa-primary);
}
.cowa-summary-card-green {
border-left: 3px solid var(--cowa-success-text);
}
.cowa-summary-card-orange {
border-left: 3px solid #ff7d00;
}
.cowa-query {
@apply grid items-center gap-x-9 gap-y-3;
grid-template-columns: repeat(4, minmax(0, 1fr));
min-height: 52px;
}
.cowa-form-item {
@apply flex h-8 min-w-0 items-center;
}
.cowa-form-label {
@apply shrink-0 text-sm font-normal;
width: 80px;
color: var(--cowa-text-primary);
}
.cowa-control {
@apply h-8 min-w-0 flex-1 rounded border px-3 text-sm outline-none transition-colors;
width: 240px;
max-width: 240px;
border-color: var(--cowa-border);
background: var(--cowa-control-bg);
color: var(--cowa-text-primary);
}
.cowa-control::placeholder {
color: var(--cowa-text-tertiary);
}
.cowa-control:focus {
border-color: var(--cowa-primary);
background: #fff;
}
.cowa-select {
@apply inline-flex items-center justify-between text-left;
color: var(--cowa-text-tertiary);
}
.cowa-query-actions {
@apply flex h-8 items-center justify-end gap-5;
}
.cowa-link-action {
@apply h-8 border-0 bg-transparent p-0 text-sm font-normal;
color: var(--cowa-primary);
}
.cowa-btn {
@apply h-8 rounded px-3 py-0 text-sm font-normal shadow-none transition-colors;
}
.cowa-btn-primary {
background: var(--cowa-primary);
color: #fff;
}
.cowa-btn-primary:hover {
background: var(--cowa-primary-hover);
}
.cowa-btn-secondary {
border-color: var(--cowa-border);
background: #fff;
color: var(--cowa-text-secondary);
}
.cowa-divider {
@apply mt-3 h-px w-full;
background: var(--cowa-border);
}
.cowa-toolbar {
@apply flex h-14 items-center;
}
.cowa-toolbar-between {
@apply justify-between;
}
.cowa-list-title {
@apply flex items-center gap-2;
}
.cowa-list-title strong {
@apply text-base font-medium;
color: var(--cowa-text-primary);
}
.cowa-list-title span {
@apply text-xs;
color: var(--cowa-text-tertiary);
}
.cowa-project-query {
align-items: start;
}
.cowa-project-list {
@apply flex min-h-0 flex-1 flex-col overflow-hidden;
max-height: 100%;
}
.cowa-project-list-meta {
@apply mb-3 flex items-center justify-between text-xs;
color: var(--cowa-text-tertiary);
}
.cowa-project-banner {
@apply mb-3 flex items-start gap-3 rounded border px-4 py-3 text-sm;
border-color: color-mix(in srgb, var(--cowa-danger-text) 18%, var(--cowa-border));
background: color-mix(in srgb, var(--cowa-danger-bg) 68%, white);
color: var(--cowa-danger-text);
}
.cowa-project-banner span {
@apply text-xs;
color: var(--cowa-text-secondary);
}
.cowa-project-scroll {
@apply min-h-0 flex-1 overflow-y-auto pr-1;
max-height: 100%;
scrollbar-gutter: stable;
scrollbar-width: thin;
scrollbar-color: var(--cowa-border-strong) transparent;
}
.cowa-project-scroll::-webkit-scrollbar {
width: 8px;
}
.cowa-project-scroll::-webkit-scrollbar-track {
background: transparent;
}
.cowa-project-scroll::-webkit-scrollbar-thumb {
border: 2px solid #fff;
border-radius: 999px;
background: var(--cowa-border-strong);
}
.cowa-project-grid {
@apply grid gap-3;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
padding-bottom: 4px;
}
.cowa-project-card {
@apply flex min-h-[144px] flex-col justify-between gap-1.5 rounded border bg-white p-3 transition-colors;
border-color: var(--cowa-border);
box-shadow: none;
}
.cowa-project-card:hover {
border-color: #c7dcff;
background: #fcfdff;
}
.cowa-project-card-top {
@apply flex items-start gap-2.5;
}
.cowa-project-cover {
@apply flex h-[68px] w-[68px] shrink-0 items-center justify-center overflow-hidden rounded;
}
.cowa-project-cover-image {
@apply block h-full w-full;
}
.cowa-project-card-main {
@apply min-w-0 flex-1;
}
.cowa-project-title-row {
@apply flex min-w-0 items-center gap-1.5;
}
.cowa-project-title {
@apply min-w-0 flex-1 truncate text-sm font-medium;
color: var(--cowa-primary);
}
.cowa-project-title:hover {
color: var(--cowa-primary-hover);
}
.cowa-project-status {
@apply inline-flex h-5 max-w-[92px] shrink-0 items-center truncate rounded-full px-2.5 text-xs font-medium;
color: #fff;
}
.cowa-project-status-info {
background: #1874ff;
}
.cowa-project-status-prelabel {
background: #15aabf;
}
.cowa-project-status-success {
background: #40c057;
}
.cowa-project-status-warning {
background: #fab005;
}
.cowa-project-status-danger {
background: #fa5252;
}
.cowa-project-status-terminate {
background: rgb(250 82 82 / 10%);
color: #fa5252;
}
.cowa-project-meta {
@apply mt-1 flex items-center gap-1.5 text-sm;
color: var(--cowa-text-secondary);
}
.cowa-project-meta-icon {
@apply h-3.5 w-3.5 shrink-0;
color: var(--cowa-text-tertiary);
}
.cowa-project-meta strong {
@apply min-w-0 truncate font-normal;
color: var(--cowa-text-secondary);
}
.cowa-project-remark {
@apply m-0 mt-1 block truncate text-xs;
color: var(--cowa-text-tertiary);
line-height: 20px;
}
.cowa-project-embedding-row {
@apply mt-1 flex items-center justify-between gap-3;
}
.cowa-project-embedding-label {
@apply shrink-0 text-xs font-medium;
color: var(--cowa-text-secondary);
}
.cowa-project-embedding-badge {
@apply inline-flex h-6 shrink-0 items-center rounded-full px-2.5 text-xs font-medium;
}
.cowa-project-embedding-success {
background: var(--cowa-success-bg);
color: var(--cowa-success-text);
}
.cowa-project-embedding-muted {
background: var(--cowa-surface-subtle);
color: var(--cowa-text-tertiary);
}
.cowa-project-embedding-info {
background: var(--cowa-primary-soft);
color: var(--cowa-primary);
}
.cowa-project-embedding-warning {
background: #fef5e7;
color: #ff7d00;
}
.cowa-project-actions {
@apply mt-1 flex items-center justify-between gap-1.5 border-t pt-1.5;
border-color: var(--cowa-border);
}
.cowa-project-actions a {
@apply flex h-7 flex-1 items-center justify-center rounded;
color: var(--cowa-primary);
}
.cowa-project-actions a:hover {
background: var(--cowa-primary-soft);
}
.cowa-project-actions a.is-disabled {
pointer-events: none;
color: var(--cowa-border-strong);
}
.cowa-project-action-icon {
@apply h-4 w-4;
}
.cowa-project-pagination {
@apply mt-3 flex h-12 shrink-0 items-center justify-between border-t px-4 text-xs;
border-color: var(--cowa-border);
color: var(--cowa-text-secondary);
}
.cowa-project-empty,
.cowa-project-state {
@apply flex min-h-[220px] flex-col items-center justify-center gap-2 rounded border text-sm;
border-color: var(--cowa-border);
color: var(--cowa-text-tertiary);
}
.cowa-project-state-error {
color: var(--cowa-danger-text);
}
.cowa-project-state-error span {
@apply max-w-[720px] text-center text-xs;
color: var(--cowa-text-secondary);
}
.cowa-project-spinner {
@apply h-5 w-5 rounded-full border-2;
border-color: var(--cowa-border);
border-top-color: var(--cowa-primary);
animation: cowa-spin 0.8s linear infinite;
}
@keyframes cowa-spin {
to {
transform: rotate(360deg);
}
}
.cowa-table-wrap {
@apply w-full overflow-hidden;
border: 1px solid var(--cowa-border);
border-bottom: 0;
}
.cowa-table {
@apply w-full border-collapse bg-white text-left text-sm;
table-layout: fixed;
}
.cowa-table thead tr {
height: var(--cowa-row-h);
background: var(--cowa-surface-subtle);
}
.cowa-table th {
@apply h-10 border-b px-4 text-sm font-medium;
border-color: var(--cowa-border);
color: var(--cowa-text-primary);
background: var(--cowa-surface-subtle);
}
.cowa-table tbody tr {
height: var(--cowa-row-h);
transition: background-color 0.2s ease;
}
.cowa-table tbody tr:hover {
background: #fafbfc;
}
.cowa-table td {
@apply h-10 border-b px-4 align-middle text-sm;
border-color: var(--cowa-border);
color: var(--cowa-text-primary);
}
.cowa-table th:nth-child(1),
.cowa-table td:nth-child(1) {
width: 18%;
}
.cowa-table th:nth-child(2),
.cowa-table td:nth-child(2) {
width: 10%;
}
.cowa-table th:nth-child(3),
.cowa-table td:nth-child(3) {
width: 15%;
}
.cowa-table th:nth-child(4),
.cowa-table td:nth-child(4) {
width: 18%;
}
.cowa-table th:nth-child(5),
.cowa-table td:nth-child(5) {
width: 17%;
}
.cowa-table th:nth-child(6),
.cowa-table td:nth-child(6) {
width: 14%;
}
.cowa-table th:nth-child(7),
.cowa-table td:nth-child(7) {
width: 16%;
}
.cowa-table-action-head {
@apply text-center;
}
.cowa-table td:nth-child(7) {
@apply whitespace-nowrap;
}
.cowa-ellipsis {
@apply truncate;
}
.cowa-status {
@apply inline-flex h-6 items-center rounded px-2 text-xs font-normal;
}
.cowa-status-enabled {
background: var(--cowa-success-bg);
color: var(--cowa-success-text);
}
.cowa-status-disabled {
background: var(--cowa-danger-bg);
color: var(--cowa-danger-text);
}
.cowa-row-actions {
@apply flex items-center justify-center whitespace-nowrap text-xs;
}
.cowa-row-actions a {
@apply px-2 leading-none;
color: var(--cowa-primary);
}
.cowa-row-actions a + a {
border-left: 1px solid var(--cowa-border);
}
.cowa-row-actions .danger {
color: var(--cowa-danger-text);
}
.cowa-pagination {
@apply flex h-14 items-center justify-between border-x border-b bg-white px-4 text-xs;
border-color: var(--cowa-border);
color: var(--cowa-text-secondary);
}
.cowa-page-controls {
@apply flex items-center gap-2;
}
.cowa-page-icon,
.cowa-page-number {
@apply flex h-8 min-w-8 items-center justify-center rounded border-0 bg-transparent px-2 text-xs transition-colors;
color: var(--cowa-text-secondary);
}
.cowa-page-icon:disabled,
.cowa-page-number:disabled {
color: var(--cowa-border-strong);
cursor: not-allowed;
}
.cowa-page-icon:hover,
.cowa-page-number:hover {
background: var(--cowa-surface-subtle);
}
.cowa-page-number.active {
background: var(--cowa-primary-soft);
color: var(--cowa-primary);
}
.cowa-page-more {
@apply px-1;
color: var(--cowa-text-tertiary);
}
.cowa-page-size {
@apply ml-1 flex h-8 items-center gap-1 rounded border bg-white px-2 text-xs;
border-color: var(--cowa-border);
color: var(--cowa-text-secondary);
}
.cowa-page-size-select {
@apply h-7 rounded border bg-white px-2 text-xs outline-none;
border-color: var(--cowa-border);
color: var(--cowa-text-secondary);
}
.cowa-empty {
@apply flex flex-col items-start gap-3 py-10;
}
.cowa-empty h1 {
@apply m-0 text-lg font-medium;
}
.cowa-empty p {
@apply m-0 text-sm;
color: var(--cowa-text-secondary);
}
}
@media (max-width: 1100px) {
.cowa-sidebar {
@apply hidden;
}
.cowa-main {
margin-left: 0;
}
.cowa-topbar {
grid-template-columns: minmax(0, 1fr);
}
.cowa-brand {
@apply hidden;
}
.cowa-query {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (max-width: 720px) {
.cowa-topbar-main {
@apply gap-2 px-3;
}
.cowa-module-tabs {
@apply min-w-0 flex-1 gap-2 overflow-x-auto;
}
.cowa-module-tab {
@apply shrink-0 px-3;
}
.cowa-user-menu {
@apply hidden;
}
.cowa-content {
@apply p-3;
}
.cowa-query {
grid-template-columns: 1fr;
}
.cowa-query-actions {
@apply justify-start;
}
}