pr-monitor/web/src/styles/app.css
2026-07-28 14:44:55 +00:00

647 lines
10 KiB
CSS

* {
box-sizing: border-box;
}
html,
body,
#root {
margin: 0;
height: 100%;
}
body {
font-family: system-ui, -apple-system, 'Segoe UI', sans-serif;
background: var(--page);
color: var(--ink);
font-size: 14px;
line-height: 1.45;
}
a {
color: inherit;
}
button {
font: inherit;
color: inherit;
background: none;
border: none;
padding: 0;
cursor: pointer;
}
.app {
display: flex;
flex-direction: column;
height: 100%;
}
/* ---------- header ---------- */
.header {
display: flex;
align-items: center;
gap: 12px;
padding: 10px 16px;
border-bottom: 1px solid var(--hairline);
background: var(--surface);
position: sticky;
top: 0;
z-index: 20;
}
.header h1 {
font-size: 15px;
font-weight: 650;
margin: 0;
}
.header .spacer {
flex: 1;
}
.sync-status {
display: flex;
align-items: center;
gap: 6px;
color: var(--ink-secondary);
font-size: 12px;
white-space: nowrap;
}
.sync-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--status-pass);
}
.sync-dot.stale {
background: var(--status-running);
}
.sync-dot.error {
background: var(--status-fail);
}
.error-banner {
background: var(--chip-fail-bg);
color: var(--status-fail-text);
padding: 6px 16px;
font-size: 12px;
border-bottom: 1px solid var(--hairline);
overflow-wrap: anywhere;
}
.icon-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 30px;
height: 30px;
border-radius: 7px;
border: 1px solid var(--border);
background: var(--surface-raised);
font-size: 14px;
}
.icon-btn:hover {
border-color: var(--ink-muted);
}
/* ---------- layout ---------- */
.columns {
display: flex;
flex: 1;
min-height: 0;
}
.pane {
overflow-y: auto;
overflow-x: hidden;
min-width: 0;
-webkit-overflow-scrolling: touch;
}
.pane-prs {
flex: 3;
border-right: 1px solid var(--hairline);
}
.pane-timeline {
flex: 2;
background: var(--surface);
}
.mobile-tabs {
display: none;
}
@media (max-width: 899px) {
.columns .pane {
display: none;
flex: 1;
}
.columns .pane.active {
display: block;
}
.pane-prs {
border-right: none;
}
.mobile-tabs {
display: flex;
border-top: 1px solid var(--hairline);
background: var(--surface);
padding-bottom: env(safe-area-inset-bottom);
}
.mobile-tabs button {
flex: 1;
padding: 10px 0 8px;
font-size: 13px;
color: var(--ink-muted);
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
}
.mobile-tabs button.active {
color: var(--accent);
font-weight: 600;
}
.tab-badge {
position: absolute;
transform: translate(14px, -2px);
background: var(--unread);
color: #fff;
font-size: 10px;
font-weight: 600;
border-radius: 8px;
min-width: 16px;
height: 16px;
line-height: 16px;
padding: 0 4px;
}
}
/* ---------- filter tabs ---------- */
.filter-tabs {
display: flex;
gap: 6px;
padding: 10px 16px 4px;
flex-wrap: wrap;
}
.filter-tabs button {
padding: 4px 10px;
border-radius: 14px;
border: 1px solid var(--border);
color: var(--ink-secondary);
font-size: 12px;
background: var(--surface);
}
.filter-tabs button.active {
background: var(--ink);
color: var(--page);
border-color: var(--ink);
}
/* ---------- PR cards ---------- */
.pr-list {
padding: 8px 16px 24px;
display: flex;
flex-direction: column;
gap: 8px;
}
.repo-heading {
margin: 10px 0 2px;
font-size: 11px;
font-weight: 650;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--ink-muted);
}
.pr-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 10px;
padding: 10px 12px;
}
.pr-card .title-row {
display: flex;
align-items: baseline;
gap: 8px;
}
.pr-card .pr-number {
color: var(--ink-muted);
font-size: 12px;
white-space: nowrap;
}
.pr-card .pr-title {
font-weight: 600;
text-decoration: none;
overflow-wrap: anywhere;
}
.pr-card .pr-title:hover {
text-decoration: underline;
}
.pr-card .meta-row {
display: flex;
align-items: center;
gap: 8px;
margin-top: 4px;
color: var(--ink-muted);
font-size: 12px;
flex-wrap: wrap;
}
.draft-tag {
border: 1px solid var(--border);
border-radius: 4px;
padding: 0 5px;
font-size: 11px;
color: var(--ink-secondary);
}
.branch {
font-family: ui-monospace, monospace;
font-size: 11px;
overflow: hidden;
text-overflow: ellipsis;
max-width: 220px;
white-space: nowrap;
}
.unread-pill {
background: var(--unread);
color: #fff;
border-radius: 9px;
font-size: 11px;
font-weight: 600;
padding: 0 6px;
line-height: 17px;
}
.chips-row {
display: flex;
align-items: center;
gap: 6px;
margin-top: 8px;
flex-wrap: wrap;
}
/* ---------- category chips + dropdown ---------- */
.chip-wrap {
position: relative;
}
.chip {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 3px 9px;
border-radius: 13px;
font-size: 12px;
font-weight: 600;
border: 1px solid transparent;
}
.chip .count {
font-weight: 500;
opacity: 0.85;
}
.chip-pass {
background: var(--chip-pass-bg);
color: var(--status-pass-text);
}
.chip-fail {
background: var(--chip-fail-bg);
color: var(--status-fail-text);
}
.chip-running {
background: var(--chip-running-bg);
color: var(--status-running-text);
}
.chip-running .chip-icon {
animation: pulse 1.4s ease-in-out infinite;
}
.chip-pending {
background: var(--chip-pending-bg);
color: var(--ink-secondary);
}
.chip-skipped {
background: var(--chip-skipped-bg);
color: var(--ink-muted);
}
@keyframes pulse {
50% {
opacity: 0.35;
}
}
.check-dropdown {
display: none;
position: absolute;
top: calc(100% + 4px);
left: 0;
z-index: 30;
min-width: 260px;
max-width: min(340px, 90vw);
max-height: 300px;
overflow-y: auto;
background: var(--surface-raised);
border: 1px solid var(--border);
border-radius: 10px;
box-shadow: var(--shadow);
padding: 6px;
}
.chip-wrap.align-right .check-dropdown {
left: auto;
right: 0;
}
.chip-wrap.open .check-dropdown {
display: block;
}
@media (hover: hover) {
.chip-wrap:hover .check-dropdown {
display: block;
}
}
.check-row {
display: flex;
align-items: center;
gap: 7px;
padding: 4px 6px;
border-radius: 6px;
font-size: 12px;
color: var(--ink);
text-decoration: none;
}
a.check-row:hover {
background: var(--chip-pending-bg);
}
.check-row .check-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.check-icon {
font-size: 11px;
width: 14px;
text-align: center;
flex: none;
}
.check-icon.pass {
color: var(--status-pass-text);
}
.check-icon.fail {
color: var(--status-fail-text);
}
.check-icon.running {
color: var(--status-running-text);
animation: pulse 1.4s ease-in-out infinite;
}
.check-icon.pending,
.check-icon.skipped {
color: var(--ink-muted);
}
/* ---------- review badge ---------- */
.review-badge {
display: inline-flex;
align-items: center;
gap: 5px;
font-size: 12px;
padding: 3px 9px;
border-radius: 13px;
border: 1px solid var(--border);
color: var(--ink-secondary);
}
.review-badge.outstanding {
border-color: var(--status-running);
color: var(--status-running-text);
}
.review-badge.addressed {
border-color: var(--status-pass);
color: var(--status-pass-text);
}
.verdict {
display: inline-flex;
align-items: center;
gap: 3px;
font-size: 11px;
padding: 2px 7px;
border-radius: 11px;
}
.verdict.approved {
background: var(--chip-pass-bg);
color: var(--status-pass-text);
}
.verdict.changes {
background: var(--chip-fail-bg);
color: var(--status-fail-text);
}
/* ---------- timeline ---------- */
.timeline-head {
display: flex;
align-items: center;
gap: 8px;
padding: 10px 14px 6px;
position: sticky;
top: 0;
background: var(--surface);
z-index: 10;
}
.timeline-head h2 {
font-size: 13px;
font-weight: 650;
margin: 0;
flex: 1;
}
.timeline-head button {
font-size: 12px;
color: var(--ink-secondary);
border: 1px solid var(--border);
border-radius: 12px;
padding: 3px 9px;
}
.timeline-head button.active {
background: var(--ink);
color: var(--page);
border-color: var(--ink);
}
.timeline-list {
padding: 0 8px 24px;
}
.tl-group {
margin: 10px 6px 0;
}
.tl-group-head {
display: flex;
align-items: baseline;
gap: 6px;
font-size: 12px;
color: var(--ink-secondary);
padding: 0 6px 2px;
flex-wrap: wrap;
}
.tl-group-head .actor {
font-weight: 650;
color: var(--ink);
}
.tl-group-head a {
color: var(--ink-secondary);
text-decoration: none;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 100%;
}
.tl-group-head a:hover {
text-decoration: underline;
}
.tl-item {
display: flex;
gap: 8px;
padding: 6px 6px;
border-radius: 8px;
cursor: pointer;
align-items: flex-start;
}
.tl-item:hover {
background: var(--chip-pending-bg);
}
.tl-item .dot {
flex: none;
width: 8px;
height: 8px;
border-radius: 50%;
margin-top: 6px;
background: transparent;
border: 1px solid transparent;
}
.tl-item.unread .dot {
background: var(--unread);
}
.tl-reply-ctx {
font-size: 12px;
color: var(--ink-muted);
border-left: 2px solid var(--hairline);
padding-left: 7px;
margin: 2px 0;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
overflow-wrap: anywhere;
}
.tl-reply-ctx .actor {
font-weight: 600;
}
.tl-item .body {
color: var(--ink-secondary);
font-size: 13px;
overflow-wrap: anywhere;
}
.tl-item.unread .body {
color: var(--ink);
}
.tl-kind {
font-size: 11px;
color: var(--ink-muted);
}
.tl-kind .path {
font-family: ui-monospace, monospace;
}
.tl-review-state {
font-weight: 600;
}
.tl-review-state.approved {
color: var(--status-pass-text);
}
.tl-review-state.changes {
color: var(--status-fail-text);
}
.load-more {
display: block;
margin: 12px auto;
padding: 5px 14px;
border: 1px solid var(--border);
border-radius: 14px;
color: var(--ink-secondary);
font-size: 12px;
}
.empty-note {
color: var(--ink-muted);
text-align: center;
padding: 32px 16px;
font-size: 13px;
}