feat: Claude Code Monitor — lanes, pipelines and a merged workspace

Internal SmartGift build of a Claude Code monitoring dashboard.

Lanes: a durable unit of parallel agent work, one per working directory,
tracked across session restarts. Managed lanes are git worktrees the
dashboard provisions and can reset or remove behind a three-check destroy
guard and a counted preflight; adopted lanes are directories you already
own and are never destroyable.

Pipelines: a lane moves through pipeline stages. A stage the agent declares
with evidence renders green; a stage inferred from the tool-event stream
renders dashed amber and never counts as done. Detection is forward-only
within a 30-minute window, and never writes the declared stage.

Workspace: one page at /run with a lane grid, the selected lane's pipeline,
and a full Claude console behind a disclosure.
This commit is contained in:
2026-07-29 17:07:45 +07:00
commit 57dc91585d
783 changed files with 221743 additions and 0 deletions
+129
View File
@@ -0,0 +1,129 @@
/**
* @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;
@layer base {
* {
scrollbar-width: thin;
scrollbar-color: #2a2a3d #0c0c14;
}
*::-webkit-scrollbar {
width: 6px;
height: 6px;
}
*::-webkit-scrollbar-track {
background: #0c0c14;
}
*::-webkit-scrollbar-thumb {
background: #2a2a3d;
border-radius: 3px;
}
*::-webkit-scrollbar-thumb:hover {
background: #363650;
}
::selection {
background: rgba(99, 102, 241, 0.3);
color: #e4e4ed;
}
::-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;
}
.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-gray-400 hover:text-gray-200 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-gray-200 placeholder-gray-500 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;
}
/* 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 #363650;
background: #15151f;
color: #e4e4ed;
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 #6366f1;
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;
}