539e43195f
Full-app UI/UX refresh committing to a tech-instrument / studio-console aesthetic inspired by hardware synths, Eurorack panels, and DAW layouts. Design tokens and fonts: - Embed Manrope (body), JetBrains Mono (labels/metrics), Big Shoulders Display (numeric readouts) as local .woff2 variable fonts with latin + latin-ext + cyrillic + cyrillic-ext subsets via unicode-range. - New Lumenworks token layer in base.css: --lux-bg-0..3, --lux-line(-bold), --lux-ink(-dim/-mute/-faint), --ch-signal/-cyan/-magenta/-amber/-coral/ -violet channel palette, --lux-signal-glow, --lux-shadow-rack, all theme-aware for dark + light. Existing tokens untouched for compat. Shell (header + sidebar): - Header rebuilt as a 3-column CSS-grid transport bar (brand | center | toolbar) with a glowing LED brand mark rendered via pseudo-elements on .header-title. Gradient channel-color rule under the bottom border. - New sidebar.css introduces a vertical channel-strip nav. Active tab gets a glowing left stripe + radial tint + LED pip. .sidebar-foot contains a live CPU/FPS meter plate. - Sidebar collapses to a 56 px icon rail at <=1100 px and hides via display:contents at <=600 px so mobile.css's fixed bottom tab-bar flows through unchanged. Cards and dashboard: - .card gets channel stripe (data-card-type + .ch-* utilities auto-map from data-target-id / data-stream-id / data-automation-id etc.), corner bracket, gradient background, subtle rack shadow. - .card-running replaces the old @property --border-angle conic-gradient rotating border with a lightweight signalFlow linear-gradient strip on the bottom edge (cheaper paint, no GPU layer compositing per card). - Skeleton loaders rewritten: left hairline + corner bracket + gradient shimmer instead of the old text-color opacity pulse. - .dashboard-target rows pick up the same channel-stripe + signalFlow treatment. Section headers use mono micro-caps with a channel-green underline accent consistent across the app. - .perf-chart-card: channel stripe replaces old border-top; per-metric accents moved to the channel palette (CPU=coral, RAM=violet, GPU=green, temp=amber). Metric values use tabular-nums + a soft glow. Live bindings (no new endpoints): - _updateSidebarMeter: binds the sidebar Load + FPS bars to the existing /system/performance poll. - _updateTransportStatus: toggles the transport chip between "Ready" and "Armed - N live" whenever the dashboard's running-target set is recomputed. Tree-nav + sub-tabs: - tree-nav.css trigger pill gets a channel-stripe left edge that glows when open; panel has a gradient channel-accent rule across the top; group headers use silkscreened micro-caps; active leaf has a pulsing LED pip + channel tint. - .stream-tab-btn / .subtab-section-header adopt the same mono-caps + channel-underline language for consistency. - Graph editor toolbar gets gradient + hairline + rack shadow + backdrop blur. Canvas and nodes untouched. Modals (40+ modals share modal.css): - Radial-dim + 6 px blur backdrop. Content gets a gradient background, hairline border, deep rack shadow, top channel-accent rule driven by --modal-ch, bottom-right corner bracket (hidden on mobile fullscreen). - Per-modal-ID channel lanes: target editors = green, source/input editors = cyan, audio = magenta, automation/scene/game = violet, settings/auth = amber, confirm = coral. - Modal headers: vertical channel stripe left of the title + hairline divider. Modal footers: hairline top border + subtle gradient wash. Forms: - Inputs use hairline borders; number inputs switch to mono + tabular-nums for column alignment. Focus state: channel-green ring + soft glow. - Buttons use mono-uppercase type with signal-glow on primary and coral- glow on danger. Mobile (<=600 px): - Fixed bottom .tab-bar gets the full Lumenworks treatment: gradient fill, top channel-accent rule matching the transport bar, backdrop blur. Active tab has an LED pip above the icon + channel tint + icon recolor. - Fullscreen modals: corner bracket hidden, header stripe slimmed. Microcopy (en / ru / zh): - "Targets" -> "Channels" / "Каналы" / "通道" - "Sources" -> "Inputs" / "Входы" / "输入" - Internal tab keys (dashboard/automations/targets/streams/integrations/ graph) kept stable so no JS or localStorage migration is needed. - Added: sidebar.workspaces, sidebar.load, sidebar.fps, transport.status.ready, transport.status.armed. Compatibility: - All existing class hooks preserved (.tab-bar, .tab-btn, .card, .card-running, .tree-dd-*, .cs-*, .perf-chart-card, .modal-content, .dashboard-target, etc.). No JS or API changes required for the new look to take effect. - Tour selectors survive (header .header-title, #tab-btn-*, onclick markers on theme/settings/search, #cp-wrap-accent, etc.). - Mobile <=600 px bottom tab-bar keeps working via display:contents fall-through in the new sidebar. Build: tsc --noEmit clean; npm run build clean. CSS bundle grew from ~177 KB to ~201 KB for the full new visual system. Fonts loaded lazily per unicode-range subset (~98 KB critical path for English). Phased plan + deferred follow-ups (dashboard hero strip, legacy-token cleanup) recorded at the top of TODO.md. Reference mockup: server/docs/ui-redesign-mockup.html.
1379 lines
47 KiB
HTML
1379 lines
47 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en" data-theme="dark">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>LUMENWORKS — LedGrab Redesign Mockup</title>
|
||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||
<link href="https://fonts.googleapis.com/css2?family=Big+Shoulders+Display:wght@500;700;900&family=JetBrains+Mono:wght@300;400;500;600;700&family=Manrope:wght@300;400;500;600;700;800&family=Orbitron:wght@600;800;900&display=swap" rel="stylesheet">
|
||
<style>
|
||
/* ============================================================
|
||
LUMENWORKS — proposed studio-instrument aesthetic for LedGrab
|
||
============================================================ */
|
||
:root {
|
||
--bg-0: #0a0b0d;
|
||
--bg-1: #101216;
|
||
--bg-2: #15181d;
|
||
--bg-3: #1c2027;
|
||
--line: #232831;
|
||
--line-bold: #2e3440;
|
||
--ink: #e6ebf2;
|
||
--ink-dim: #8b95a5;
|
||
--ink-mute: #5b6473;
|
||
--ink-faint: #3a414c;
|
||
|
||
/* Channel/lane palette — assigned to entity types like patch cables */
|
||
--signal: #00ff7a; /* primary — capture/playback */
|
||
--signal-dim: #00b85a;
|
||
--ch-cyan: #00d8ff; /* data / network */
|
||
--ch-magenta: #ff4ade; /* audio */
|
||
--ch-amber: #ffb800; /* warning / autostart */
|
||
--ch-coral: #ff5e5e; /* alarm / stop */
|
||
--ch-violet: #8b7eff; /* graph / nodes */
|
||
|
||
--signal-glow: 0 0 14px color-mix(in srgb, var(--signal) 40%, transparent);
|
||
--shadow-rack: 0 1px 0 rgba(255,255,255,0.03), 0 8px 24px rgba(0,0,0,0.5);
|
||
|
||
--font-display: 'Big Shoulders Display', 'Orbitron', sans-serif;
|
||
--font-brand: 'Orbitron', sans-serif;
|
||
--font-mono: 'JetBrains Mono', 'IBM Plex Mono', ui-monospace, monospace;
|
||
--font-body: 'Manrope', system-ui, sans-serif;
|
||
|
||
--r-sm: 3px;
|
||
--r-md: 6px;
|
||
--r-lg: 10px;
|
||
}
|
||
|
||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||
|
||
html, body {
|
||
background: var(--bg-0);
|
||
color: var(--ink);
|
||
font-family: var(--font-body);
|
||
font-feature-settings: "ss01", "cv11";
|
||
-webkit-font-smoothing: antialiased;
|
||
min-height: 100%;
|
||
}
|
||
|
||
body {
|
||
background:
|
||
radial-gradient(900px 600px at 12% -5%, color-mix(in srgb, var(--signal) 7%, transparent), transparent 60%),
|
||
radial-gradient(700px 500px at 95% 8%, color-mix(in srgb, var(--ch-cyan) 5%, transparent), transparent 60%),
|
||
radial-gradient(800px 600px at 50% 110%, color-mix(in srgb, var(--ch-magenta) 4%, transparent), transparent 70%),
|
||
var(--bg-0);
|
||
position: relative;
|
||
overflow-x: hidden;
|
||
}
|
||
|
||
/* Dotted grid texture across the entire app */
|
||
body::before {
|
||
content: '';
|
||
position: fixed;
|
||
inset: 0;
|
||
background-image: radial-gradient(rgba(255,255,255,0.04) 1px, transparent 1px);
|
||
background-size: 16px 16px;
|
||
background-position: 0 0;
|
||
z-index: 0;
|
||
pointer-events: none;
|
||
}
|
||
|
||
/* Subtle scanlines overlay */
|
||
body::after {
|
||
content: '';
|
||
position: fixed;
|
||
inset: 0;
|
||
background: repeating-linear-gradient(
|
||
0deg,
|
||
transparent 0,
|
||
transparent 2px,
|
||
rgba(255,255,255,0.012) 2px,
|
||
rgba(255,255,255,0.012) 3px
|
||
);
|
||
z-index: 1;
|
||
pointer-events: none;
|
||
mix-blend-mode: overlay;
|
||
}
|
||
|
||
/* ─── Layout shell ─────────────────────────────────────────── */
|
||
.shell {
|
||
position: relative;
|
||
z-index: 2;
|
||
display: grid;
|
||
grid-template-columns: 248px 1fr;
|
||
grid-template-rows: 64px 1fr;
|
||
min-height: 100vh;
|
||
}
|
||
|
||
/* ─── Top transport bar ────────────────────────────────────── */
|
||
.transport {
|
||
grid-column: 1 / -1;
|
||
display: grid;
|
||
grid-template-columns: 248px 1fr auto;
|
||
align-items: center;
|
||
border-bottom: 1px solid var(--line-bold);
|
||
background: linear-gradient(180deg, var(--bg-1) 0%, var(--bg-0) 100%);
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 50;
|
||
backdrop-filter: blur(8px);
|
||
}
|
||
|
||
.brand {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
padding: 0 20px;
|
||
height: 100%;
|
||
border-right: 1px solid var(--line-bold);
|
||
}
|
||
|
||
.brand-mark {
|
||
width: 28px;
|
||
height: 28px;
|
||
background: var(--signal);
|
||
border-radius: 4px;
|
||
position: relative;
|
||
box-shadow: var(--signal-glow), inset 0 0 0 1px rgba(0,0,0,0.2);
|
||
display: grid;
|
||
place-items: center;
|
||
}
|
||
.brand-mark::after {
|
||
content: '';
|
||
width: 12px;
|
||
height: 12px;
|
||
background: var(--bg-0);
|
||
border-radius: 1px;
|
||
box-shadow: 0 0 0 2px var(--signal);
|
||
display: block;
|
||
}
|
||
.brand-text {
|
||
display: flex;
|
||
flex-direction: column;
|
||
line-height: 1;
|
||
gap: 2px;
|
||
}
|
||
.brand-name {
|
||
font-family: var(--font-brand);
|
||
font-weight: 900;
|
||
font-size: 1rem;
|
||
letter-spacing: 0.18em;
|
||
background: linear-gradient(90deg, var(--ink), var(--signal) 50%, var(--ink));
|
||
background-size: 200% 100%;
|
||
-webkit-background-clip: text;
|
||
background-clip: text;
|
||
color: transparent;
|
||
animation: shimmer 8s linear infinite;
|
||
}
|
||
.brand-tag {
|
||
font-family: var(--font-mono);
|
||
font-size: 0.55rem;
|
||
letter-spacing: 0.25em;
|
||
color: var(--ink-mute);
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
@keyframes shimmer {
|
||
to { background-position: -200% 0; }
|
||
}
|
||
|
||
.transport-center {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
padding: 0 24px;
|
||
}
|
||
.transport-btn {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
padding: 6px 12px;
|
||
background: var(--bg-2);
|
||
border: 1px solid var(--line);
|
||
border-radius: var(--r-sm);
|
||
color: var(--ink-dim);
|
||
font-family: var(--font-mono);
|
||
font-size: 0.7rem;
|
||
letter-spacing: 0.08em;
|
||
text-transform: uppercase;
|
||
cursor: pointer;
|
||
transition: all 0.15s ease;
|
||
}
|
||
.transport-btn:hover {
|
||
color: var(--ink);
|
||
border-color: var(--line-bold);
|
||
background: var(--bg-3);
|
||
}
|
||
.transport-btn.is-armed {
|
||
color: var(--signal);
|
||
border-color: color-mix(in srgb, var(--signal) 40%, transparent);
|
||
background: color-mix(in srgb, var(--signal) 8%, transparent);
|
||
box-shadow: inset 0 0 12px color-mix(in srgb, var(--signal) 12%, transparent);
|
||
}
|
||
.transport-btn .dot {
|
||
width: 6px; height: 6px; border-radius: 50%; background: var(--signal);
|
||
box-shadow: var(--signal-glow);
|
||
animation: pulse 1.4s ease-in-out infinite;
|
||
}
|
||
@keyframes pulse {
|
||
0%, 100% { opacity: 1; transform: scale(1); }
|
||
50% { opacity: 0.45; transform: scale(0.85); }
|
||
}
|
||
|
||
.transport-meta {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 18px;
|
||
padding: 0 20px;
|
||
font-family: var(--font-mono);
|
||
font-size: 0.7rem;
|
||
color: var(--ink-mute);
|
||
}
|
||
.meta-cell { display: flex; flex-direction: column; gap: 2px; line-height: 1; align-items: flex-end; }
|
||
.meta-cell .k { font-size: 0.55rem; letter-spacing: 0.2em; text-transform: uppercase; color: var(--ink-faint); }
|
||
.meta-cell .v { color: var(--ink); font-weight: 500; font-variant-numeric: tabular-nums; }
|
||
.meta-sep { width: 1px; height: 22px; background: var(--line); }
|
||
|
||
.icon-btn {
|
||
width: 28px; height: 28px;
|
||
display: grid; place-items: center;
|
||
background: transparent;
|
||
border: 1px solid var(--line);
|
||
border-radius: var(--r-sm);
|
||
color: var(--ink-dim);
|
||
cursor: pointer;
|
||
transition: all 0.15s ease;
|
||
}
|
||
.icon-btn:hover { color: var(--ink); border-color: var(--line-bold); background: var(--bg-2); }
|
||
.icon-btn svg { width: 14px; height: 14px; stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
|
||
|
||
/* ─── Sidebar (channel strip nav) ──────────────────────────── */
|
||
.sidebar {
|
||
border-right: 1px solid var(--line-bold);
|
||
background: linear-gradient(180deg, var(--bg-1) 0%, var(--bg-0) 100%);
|
||
padding: 20px 0;
|
||
position: sticky;
|
||
top: 64px;
|
||
height: calc(100vh - 64px);
|
||
overflow-y: auto;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.sidebar-section { padding: 0 14px 18px; }
|
||
.sidebar-label {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
font-family: var(--font-mono);
|
||
font-size: 0.55rem;
|
||
letter-spacing: 0.25em;
|
||
text-transform: uppercase;
|
||
color: var(--ink-mute);
|
||
padding: 0 6px 8px;
|
||
border-bottom: 1px dashed var(--line);
|
||
margin-bottom: 8px;
|
||
}
|
||
.sidebar-label::before { content: '['; color: var(--ink-faint); }
|
||
.sidebar-label::after { content: ']'; color: var(--ink-faint); margin-left: auto; }
|
||
|
||
.nav-item {
|
||
display: grid;
|
||
grid-template-columns: 18px 1fr auto;
|
||
gap: 12px;
|
||
align-items: center;
|
||
padding: 8px 8px;
|
||
font-family: var(--font-mono);
|
||
font-size: 0.78rem;
|
||
color: var(--ink-dim);
|
||
cursor: pointer;
|
||
border-radius: var(--r-sm);
|
||
margin-bottom: 1px;
|
||
position: relative;
|
||
transition: all 0.15s ease;
|
||
letter-spacing: 0.04em;
|
||
}
|
||
.nav-item:hover { color: var(--ink); background: var(--bg-2); }
|
||
.nav-item.active {
|
||
color: var(--ink);
|
||
background: linear-gradient(90deg, color-mix(in srgb, var(--signal) 12%, transparent), transparent);
|
||
box-shadow: inset 2px 0 0 var(--signal);
|
||
}
|
||
.nav-item svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 1.7; }
|
||
.nav-item.active svg { color: var(--signal); }
|
||
.nav-item .nav-count {
|
||
font-size: 0.6rem;
|
||
background: var(--bg-3);
|
||
color: var(--ink-mute);
|
||
padding: 1px 5px;
|
||
border-radius: 8px;
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
.nav-item.active .nav-count { background: var(--signal); color: var(--bg-0); }
|
||
|
||
.sidebar-bottom {
|
||
margin-top: auto;
|
||
padding: 16px 14px;
|
||
border-top: 1px dashed var(--line);
|
||
}
|
||
.cpu-meter {
|
||
font-family: var(--font-mono);
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
}
|
||
.cpu-meter-row { display: flex; justify-content: space-between; font-size: 0.6rem; color: var(--ink-mute); letter-spacing: 0.1em; text-transform: uppercase; }
|
||
.cpu-meter-row b { color: var(--ink); font-weight: 500; font-variant-numeric: tabular-nums; }
|
||
.cpu-bar { height: 4px; background: var(--bg-3); border-radius: 2px; overflow: hidden; position: relative; }
|
||
.cpu-bar > i {
|
||
display: block;
|
||
height: 100%;
|
||
background: linear-gradient(90deg, var(--signal), var(--ch-cyan));
|
||
box-shadow: 0 0 8px color-mix(in srgb, var(--signal) 50%, transparent);
|
||
}
|
||
|
||
/* ─── Main rack area ───────────────────────────────────────── */
|
||
.rack {
|
||
padding: 28px 36px 60px;
|
||
position: relative;
|
||
min-width: 0;
|
||
}
|
||
|
||
.rack-header {
|
||
display: flex;
|
||
align-items: flex-end;
|
||
justify-content: space-between;
|
||
padding-bottom: 22px;
|
||
margin-bottom: 28px;
|
||
border-bottom: 1px solid var(--line);
|
||
position: relative;
|
||
}
|
||
.rack-header::after {
|
||
content: '';
|
||
position: absolute;
|
||
left: 0; bottom: -1px;
|
||
width: 80px;
|
||
height: 1px;
|
||
background: var(--signal);
|
||
box-shadow: var(--signal-glow);
|
||
}
|
||
.rack-title {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 6px;
|
||
}
|
||
.rack-title .crumb {
|
||
font-family: var(--font-mono);
|
||
font-size: 0.6rem;
|
||
letter-spacing: 0.25em;
|
||
text-transform: uppercase;
|
||
color: var(--ink-mute);
|
||
}
|
||
.rack-title .crumb b { color: var(--signal); font-weight: 500; }
|
||
.rack-title h1 {
|
||
font-family: var(--font-display);
|
||
font-weight: 900;
|
||
font-size: 2.4rem;
|
||
line-height: 1;
|
||
letter-spacing: -0.01em;
|
||
color: var(--ink);
|
||
}
|
||
.rack-title h1 em {
|
||
font-style: normal;
|
||
color: var(--ink-mute);
|
||
font-weight: 500;
|
||
font-size: 1.6rem;
|
||
margin-left: 8px;
|
||
}
|
||
.rack-actions { display: flex; align-items: center; gap: 8px; }
|
||
|
||
/* ─── Section banner ───────────────────────────────────────── */
|
||
.section {
|
||
margin-bottom: 36px;
|
||
}
|
||
.section-head {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16px;
|
||
margin-bottom: 14px;
|
||
position: relative;
|
||
}
|
||
.section-head h2 {
|
||
font-family: var(--font-mono);
|
||
font-size: 0.7rem;
|
||
font-weight: 600;
|
||
letter-spacing: 0.3em;
|
||
text-transform: uppercase;
|
||
color: var(--ink-dim);
|
||
display: flex;
|
||
align-items: baseline;
|
||
gap: 10px;
|
||
}
|
||
.section-head h2::before {
|
||
content: '◆';
|
||
color: var(--signal);
|
||
font-size: 0.6rem;
|
||
}
|
||
.section-head .count {
|
||
font-family: var(--font-mono);
|
||
font-size: 0.6rem;
|
||
color: var(--ink-mute);
|
||
letter-spacing: 0.15em;
|
||
}
|
||
.section-head .rule {
|
||
flex: 1;
|
||
height: 1px;
|
||
background: linear-gradient(90deg, var(--line) 0%, transparent 100%);
|
||
}
|
||
.section-head .actions { display: flex; gap: 6px; }
|
||
|
||
/* ─── Hero strip — system overview ─────────────────────────── */
|
||
.hero {
|
||
display: grid;
|
||
grid-template-columns: 1.2fr 1fr 1fr 1fr;
|
||
gap: 1px;
|
||
margin-bottom: 36px;
|
||
background: var(--line);
|
||
border: 1px solid var(--line);
|
||
border-radius: var(--r-md);
|
||
overflow: hidden;
|
||
}
|
||
.hero-cell {
|
||
background: var(--bg-1);
|
||
padding: 22px 22px 18px;
|
||
position: relative;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
overflow: hidden;
|
||
}
|
||
.hero-cell::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 12px; right: 12px;
|
||
width: 28px; height: 14px;
|
||
border: 1px solid var(--line-bold);
|
||
border-bottom-color: transparent;
|
||
border-left-color: transparent;
|
||
}
|
||
.hero-cell .label {
|
||
font-family: var(--font-mono);
|
||
font-size: 0.58rem;
|
||
letter-spacing: 0.25em;
|
||
text-transform: uppercase;
|
||
color: var(--ink-mute);
|
||
}
|
||
.hero-cell .big {
|
||
font-family: var(--font-display);
|
||
font-size: 3.4rem;
|
||
font-weight: 900;
|
||
line-height: 0.95;
|
||
color: var(--ink);
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
.hero-cell .big .unit { color: var(--ink-mute); font-size: 1.1rem; margin-left: 6px; font-weight: 500; }
|
||
.hero-cell .sub { font-family: var(--font-mono); font-size: 0.7rem; color: var(--ink-dim); display: flex; gap: 10px; }
|
||
.hero-cell .sub .delta { color: var(--signal); }
|
||
.hero-cell .sub .delta.bad { color: var(--ch-coral); }
|
||
|
||
/* Hero — primary cell with running channels */
|
||
.hero-cell.primary {
|
||
background:
|
||
radial-gradient(420px 280px at 110% 110%, color-mix(in srgb, var(--signal) 10%, transparent), transparent 60%),
|
||
var(--bg-1);
|
||
}
|
||
.channels-running {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 6px;
|
||
margin-top: 6px;
|
||
}
|
||
.ch-row {
|
||
display: grid;
|
||
grid-template-columns: 6px 1fr auto;
|
||
align-items: center;
|
||
gap: 10px;
|
||
font-family: var(--font-mono);
|
||
font-size: 0.72rem;
|
||
}
|
||
.ch-row .stripe { width: 4px; height: 20px; background: var(--signal); border-radius: 2px; box-shadow: 0 0 6px currentColor; }
|
||
.ch-row .stripe.cyan { background: var(--ch-cyan); }
|
||
.ch-row .stripe.magenta { background: var(--ch-magenta); }
|
||
.ch-row .name { color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||
.ch-row .fps { color: var(--signal); font-variant-numeric: tabular-nums; }
|
||
|
||
/* Mini sparkline for hero numerics */
|
||
.spark { height: 28px; width: 100%; margin-top: 8px; }
|
||
.spark path.line { fill: none; stroke: var(--signal); stroke-width: 1.5; }
|
||
.spark path.fill { fill: url(#spark-fade); opacity: 0.4; }
|
||
|
||
/* ─── Module grid (the cards) ──────────────────────────────── */
|
||
.modules {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
|
||
gap: 14px;
|
||
}
|
||
|
||
.module {
|
||
--ch: var(--signal);
|
||
position: relative;
|
||
background:
|
||
linear-gradient(180deg, var(--bg-1) 0%, var(--bg-2) 100%);
|
||
border: 1px solid var(--line);
|
||
border-radius: var(--r-md);
|
||
padding: 18px 20px 16px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 14px;
|
||
overflow: hidden;
|
||
transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
|
||
}
|
||
.module:hover {
|
||
transform: translateY(-2px);
|
||
border-color: var(--line-bold);
|
||
box-shadow: var(--shadow-rack);
|
||
}
|
||
|
||
/* Channel stripe — left edge color tag */
|
||
.module::before {
|
||
content: '';
|
||
position: absolute;
|
||
left: 0; top: 0; bottom: 0;
|
||
width: 3px;
|
||
background: var(--ch);
|
||
box-shadow: 0 0 12px color-mix(in srgb, var(--ch) 50%, transparent);
|
||
}
|
||
/* Corner brackets — silkscreened panel feel */
|
||
.module::after {
|
||
content: '';
|
||
position: absolute;
|
||
top: 8px; right: 8px;
|
||
width: 14px; height: 14px;
|
||
border-top: 1px solid var(--line-bold);
|
||
border-right: 1px solid var(--line-bold);
|
||
pointer-events: none;
|
||
}
|
||
|
||
.module.is-running {
|
||
border-color: color-mix(in srgb, var(--ch) 35%, var(--line));
|
||
box-shadow: 0 0 0 1px color-mix(in srgb, var(--ch) 25%, transparent), 0 6px 24px rgba(0,0,0,0.35);
|
||
}
|
||
|
||
.mod-head {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
justify-content: space-between;
|
||
gap: 14px;
|
||
}
|
||
.mod-id {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
}
|
||
.mod-id .badge {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
font-family: var(--font-mono);
|
||
font-size: 0.55rem;
|
||
letter-spacing: 0.2em;
|
||
text-transform: uppercase;
|
||
color: var(--ch);
|
||
padding: 2px 6px;
|
||
border: 1px solid color-mix(in srgb, var(--ch) 35%, var(--line));
|
||
border-radius: 3px;
|
||
background: color-mix(in srgb, var(--ch) 8%, transparent);
|
||
align-self: flex-start;
|
||
}
|
||
.mod-id h3 {
|
||
font-family: var(--font-body);
|
||
font-weight: 700;
|
||
font-size: 1.05rem;
|
||
color: var(--ink);
|
||
letter-spacing: -0.01em;
|
||
}
|
||
.mod-id .meta {
|
||
font-family: var(--font-mono);
|
||
font-size: 0.66rem;
|
||
color: var(--ink-mute);
|
||
letter-spacing: 0.06em;
|
||
}
|
||
|
||
/* LED status cluster (top-right) */
|
||
.leds {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
padding: 5px 7px;
|
||
background: var(--bg-0);
|
||
border: 1px solid var(--line);
|
||
border-radius: var(--r-sm);
|
||
}
|
||
.led {
|
||
width: 6px; height: 6px;
|
||
border-radius: 50%;
|
||
background: var(--ink-faint);
|
||
box-shadow: inset 0 0 0 1px rgba(0,0,0,0.5);
|
||
}
|
||
.led.on { background: var(--ch); box-shadow: 0 0 6px color-mix(in srgb, var(--ch) 80%, transparent); }
|
||
.led.amber { background: var(--ch-amber); box-shadow: 0 0 6px color-mix(in srgb, var(--ch-amber) 80%, transparent); }
|
||
.led.blink {
|
||
animation: blink 1.2s ease-in-out infinite;
|
||
}
|
||
.led.blink:nth-child(2) { animation-delay: 0.2s; }
|
||
.led.blink:nth-child(3) { animation-delay: 0.4s; }
|
||
@keyframes blink {
|
||
0%, 100% { opacity: 1; }
|
||
50% { opacity: 0.25; }
|
||
}
|
||
|
||
/* Metric grid inside a module */
|
||
.mod-metrics {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 0;
|
||
background: var(--bg-0);
|
||
border: 1px solid var(--line);
|
||
border-radius: var(--r-sm);
|
||
overflow: hidden;
|
||
}
|
||
.metric {
|
||
padding: 10px 12px;
|
||
border-right: 1px solid var(--line);
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 2px;
|
||
position: relative;
|
||
min-width: 0;
|
||
}
|
||
.metric:last-child { border-right: none; }
|
||
.metric .k {
|
||
font-family: var(--font-mono);
|
||
font-size: 0.55rem;
|
||
letter-spacing: 0.18em;
|
||
text-transform: uppercase;
|
||
color: var(--ink-mute);
|
||
}
|
||
.metric .v {
|
||
font-family: var(--font-display);
|
||
font-size: 1.5rem;
|
||
font-weight: 700;
|
||
line-height: 1;
|
||
color: var(--ink);
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
.metric .v.signal { color: var(--ch); text-shadow: 0 0 14px color-mix(in srgb, var(--ch) 60%, transparent); }
|
||
.metric .v small { font-size: 0.65rem; color: var(--ink-mute); margin-left: 2px; font-weight: 500; }
|
||
|
||
/* Sparkline inside a metric cell */
|
||
.metric svg.spark-sm { width: 100%; height: 22px; margin-top: 2px; }
|
||
.metric svg.spark-sm path { fill: none; stroke: var(--ch); stroke-width: 1.4; }
|
||
.metric svg.spark-sm .area { fill: var(--ch); opacity: 0.12; stroke: none; }
|
||
|
||
/* Module footer — playback row + patch points */
|
||
.mod-foot {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
.mod-foot .patch {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
font-family: var(--font-mono);
|
||
font-size: 0.6rem;
|
||
color: var(--ink-mute);
|
||
letter-spacing: 0.1em;
|
||
margin-right: auto;
|
||
}
|
||
.patch-dot {
|
||
width: 8px; height: 8px;
|
||
border-radius: 50%;
|
||
background: var(--bg-0);
|
||
border: 1px solid var(--ch);
|
||
position: relative;
|
||
}
|
||
.patch-dot.is-live::after {
|
||
content: '';
|
||
position: absolute;
|
||
inset: 1px;
|
||
border-radius: 50%;
|
||
background: var(--ch);
|
||
box-shadow: 0 0 6px var(--ch);
|
||
}
|
||
|
||
.btn {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
font-family: var(--font-mono);
|
||
font-size: 0.7rem;
|
||
font-weight: 600;
|
||
letter-spacing: 0.08em;
|
||
text-transform: uppercase;
|
||
padding: 7px 14px;
|
||
border: 1px solid var(--line-bold);
|
||
border-radius: var(--r-sm);
|
||
background: var(--bg-2);
|
||
color: var(--ink-dim);
|
||
cursor: pointer;
|
||
transition: all 0.15s ease;
|
||
}
|
||
.btn:hover { color: var(--ink); border-color: color-mix(in srgb, var(--signal) 40%, var(--line-bold)); }
|
||
.btn-go {
|
||
background: var(--ch);
|
||
color: var(--bg-0);
|
||
border-color: var(--ch);
|
||
box-shadow: 0 0 16px color-mix(in srgb, var(--ch) 35%, transparent);
|
||
}
|
||
.btn-go:hover { color: var(--bg-0); filter: brightness(1.08); }
|
||
.btn-stop {
|
||
background: transparent;
|
||
color: var(--ch-coral);
|
||
border-color: color-mix(in srgb, var(--ch-coral) 40%, transparent);
|
||
}
|
||
.btn-stop:hover { background: color-mix(in srgb, var(--ch-coral) 12%, transparent); }
|
||
.btn .glyph { width: 10px; height: 10px; display: inline-grid; place-items: center; }
|
||
|
||
/* Channel color variants */
|
||
.module.ch-cyan { --ch: var(--ch-cyan); }
|
||
.module.ch-magenta { --ch: var(--ch-magenta); }
|
||
.module.ch-amber { --ch: var(--ch-amber); }
|
||
.module.ch-violet { --ch: var(--ch-violet); }
|
||
|
||
/* ─── "PATCHED" — animated signal flow indicator on running modules ─── */
|
||
.module.is-running .signal-flow {
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 3px;
|
||
right: 0;
|
||
height: 2px;
|
||
background: linear-gradient(
|
||
90deg,
|
||
transparent 0%,
|
||
var(--ch) 50%,
|
||
transparent 100%
|
||
);
|
||
background-size: 30% 100%;
|
||
background-repeat: no-repeat;
|
||
animation: patchflow 2.4s linear infinite;
|
||
opacity: 0.55;
|
||
pointer-events: none;
|
||
}
|
||
@keyframes patchflow {
|
||
0% { background-position: -30% 0; }
|
||
100% { background-position: 130% 0; }
|
||
}
|
||
|
||
/* ─── Footer rack info plate ──────────────────────────────── */
|
||
.plate {
|
||
margin-top: 48px;
|
||
padding: 14px 20px;
|
||
border: 1px dashed var(--line);
|
||
border-radius: var(--r-md);
|
||
display: grid;
|
||
grid-template-columns: auto 1fr auto;
|
||
align-items: center;
|
||
gap: 24px;
|
||
font-family: var(--font-mono);
|
||
font-size: 0.65rem;
|
||
color: var(--ink-mute);
|
||
letter-spacing: 0.12em;
|
||
text-transform: uppercase;
|
||
}
|
||
.plate .stamp {
|
||
font-family: var(--font-brand);
|
||
font-weight: 800;
|
||
font-size: 0.7rem;
|
||
letter-spacing: 0.3em;
|
||
color: var(--signal);
|
||
padding: 4px 8px;
|
||
border: 1px solid color-mix(in srgb, var(--signal) 40%, transparent);
|
||
border-radius: 3px;
|
||
}
|
||
.plate .dots { display: flex; gap: 5px; }
|
||
.plate .dots i { width: 5px; height: 5px; border-radius: 50%; background: var(--ink-faint); display: block; }
|
||
.plate .dots i:nth-child(1), .plate .dots i:nth-child(3), .plate .dots i:nth-child(5) { background: var(--signal); box-shadow: 0 0 4px var(--signal); }
|
||
|
||
/* ─── Color-picker / settings cluster (top right of transport) ─── */
|
||
.swatches { display: flex; gap: 6px; }
|
||
.swatch { width: 18px; height: 18px; border-radius: 4px; border: 1px solid rgba(255,255,255,0.1); cursor: pointer; transition: transform 0.15s; }
|
||
.swatch:hover { transform: scale(1.18); }
|
||
.swatch.s1 { background: var(--signal); box-shadow: 0 0 8px var(--signal); }
|
||
.swatch.s2 { background: var(--ch-cyan); }
|
||
.swatch.s3 { background: var(--ch-magenta); }
|
||
.swatch.s4 { background: var(--ch-amber); }
|
||
.swatch.s5 { background: var(--ch-violet); }
|
||
|
||
/* ─── Detail / diagonal callout for the mockup file ─── */
|
||
.tag-mock {
|
||
position: fixed;
|
||
top: 14px; right: 14px;
|
||
z-index: 999;
|
||
font-family: var(--font-mono);
|
||
font-size: 0.55rem;
|
||
letter-spacing: 0.25em;
|
||
text-transform: uppercase;
|
||
color: var(--bg-0);
|
||
background: var(--ch-amber);
|
||
padding: 4px 8px;
|
||
border-radius: 2px;
|
||
box-shadow: 0 0 12px color-mix(in srgb, var(--ch-amber) 50%, transparent);
|
||
}
|
||
|
||
@media (max-width: 1100px) {
|
||
.shell { grid-template-columns: 1fr; }
|
||
.sidebar { display: none; }
|
||
.transport { grid-template-columns: 1fr auto auto; }
|
||
.brand { border-right: none; padding: 0 14px; }
|
||
.hero { grid-template-columns: 1fr 1fr; }
|
||
}
|
||
@media (max-width: 700px) {
|
||
.hero { grid-template-columns: 1fr; }
|
||
.modules { grid-template-columns: 1fr; }
|
||
.rack { padding: 18px; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<div class="tag-mock">PROTOTYPE · LUMENWORKS v0.1</div>
|
||
|
||
<svg width="0" height="0" style="position:absolute">
|
||
<defs>
|
||
<linearGradient id="spark-fade" x1="0" y1="0" x2="0" y2="1">
|
||
<stop offset="0%" stop-color="#00ff7a" stop-opacity="0.6"/>
|
||
<stop offset="100%" stop-color="#00ff7a" stop-opacity="0"/>
|
||
</linearGradient>
|
||
</defs>
|
||
</svg>
|
||
|
||
<div class="shell">
|
||
|
||
<!-- ─── TRANSPORT BAR ───────────────────────────────── -->
|
||
<header class="transport">
|
||
<div class="brand">
|
||
<div class="brand-mark"></div>
|
||
<div class="brand-text">
|
||
<div class="brand-name">LUMENWORKS</div>
|
||
<div class="brand-tag">LED · GRAB · v0.4.2</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="transport-center">
|
||
<button class="transport-btn is-armed"><span class="dot"></span> Armed · 3 channels live</button>
|
||
<button class="transport-btn">⏯ Master Stop</button>
|
||
<button class="transport-btn">⌘ K — Search</button>
|
||
</div>
|
||
|
||
<div class="transport-meta">
|
||
<div class="meta-cell"><span class="k">Uptime</span><span class="v">04:21:08</span></div>
|
||
<span class="meta-sep"></span>
|
||
<div class="meta-cell"><span class="k">CPU</span><span class="v">14%</span></div>
|
||
<div class="meta-cell"><span class="k">Mem</span><span class="v">182MB</span></div>
|
||
<span class="meta-sep"></span>
|
||
<div class="swatches">
|
||
<span class="swatch s1" title="Signal Green"></span>
|
||
<span class="swatch s2"></span>
|
||
<span class="swatch s3"></span>
|
||
<span class="swatch s4"></span>
|
||
<span class="swatch s5"></span>
|
||
</div>
|
||
<span class="meta-sep"></span>
|
||
<button class="icon-btn" title="Theme"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41"/></svg></button>
|
||
<button class="icon-btn" title="Settings"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg></button>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- ─── SIDEBAR (channel-strip nav) ─────────────────── -->
|
||
<aside class="sidebar">
|
||
<div class="sidebar-section">
|
||
<div class="sidebar-label">Workspaces</div>
|
||
<div class="nav-item active">
|
||
<svg viewBox="0 0 24 24"><rect width="7" height="9" x="3" y="3" rx="1"/><rect width="7" height="5" x="14" y="3" rx="1"/><rect width="7" height="9" x="14" y="12" rx="1"/><rect width="7" height="5" x="3" y="16" rx="1"/></svg>
|
||
<span>Dashboard</span>
|
||
<span class="nav-count">3</span>
|
||
</div>
|
||
<div class="nav-item">
|
||
<svg viewBox="0 0 24 24"><rect width="8" height="4" x="8" y="2" rx="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/><path d="M12 11h4M12 16h4M8 11h.01M8 16h.01"/></svg>
|
||
<span>Automations</span>
|
||
<span class="nav-count">7</span>
|
||
</div>
|
||
<div class="nav-item">
|
||
<svg viewBox="0 0 24 24"><path d="M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z"/></svg>
|
||
<span>Targets</span>
|
||
<span class="nav-count">12</span>
|
||
</div>
|
||
<div class="nav-item">
|
||
<svg viewBox="0 0 24 24"><path d="m17 2-5 5-5-5"/><rect width="20" height="15" x="2" y="7" rx="2"/></svg>
|
||
<span>Sources</span>
|
||
<span class="nav-count">8</span>
|
||
</div>
|
||
<div class="nav-item">
|
||
<svg viewBox="0 0 24 24"><path d="M12 22v-5M15 8V2"/><path d="M17 8a1 1 0 0 1 1 1v4a4 4 0 0 1-4 4h-4a4 4 0 0 1-4-4V9a1 1 0 0 1 1-1z"/><path d="M9 8V2"/></svg>
|
||
<span>Integrations</span>
|
||
<span class="nav-count">4</span>
|
||
</div>
|
||
<div class="nav-item">
|
||
<svg viewBox="0 0 24 24"><circle cx="5" cy="6" r="3"/><circle cx="19" cy="6" r="3"/><circle cx="12" cy="18" r="3"/><path d="M7.5 7.5L10.5 16M16.5 7.5L13.5 16"/></svg>
|
||
<span>Graph</span>
|
||
<span class="nav-count">2</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="sidebar-section">
|
||
<div class="sidebar-label">Live Targets</div>
|
||
<div class="nav-item">
|
||
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M12 1v6M12 17v6"/></svg>
|
||
<span>Living Room</span>
|
||
<span class="nav-count" style="background:var(--signal);color:var(--bg-0)">●</span>
|
||
</div>
|
||
<div class="nav-item">
|
||
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/></svg>
|
||
<span>Desk Strip</span>
|
||
<span class="nav-count" style="background:var(--ch-cyan);color:var(--bg-0)">●</span>
|
||
</div>
|
||
<div class="nav-item">
|
||
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/></svg>
|
||
<span>Bedroom Halo</span>
|
||
<span class="nav-count" style="background:var(--ch-magenta);color:var(--bg-0)">●</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="sidebar-bottom">
|
||
<div class="cpu-meter">
|
||
<div class="cpu-meter-row"><span>System Load</span><b>14.2%</b></div>
|
||
<div class="cpu-bar"><i style="width:14.2%"></i></div>
|
||
<div class="cpu-meter-row"><span>Capture FPS</span><b>59.7</b></div>
|
||
<div class="cpu-bar"><i style="width:88%;background:linear-gradient(90deg,var(--ch-cyan),var(--ch-magenta))"></i></div>
|
||
</div>
|
||
</div>
|
||
</aside>
|
||
|
||
<!-- ─── RACK (main content) ─────────────────────────── -->
|
||
<main class="rack">
|
||
|
||
<div class="rack-header">
|
||
<div class="rack-title">
|
||
<div class="crumb">Workspace · <b>Dashboard</b> · Studio A</div>
|
||
<h1>Live Channels<em>3 of 12 patched</em></h1>
|
||
</div>
|
||
<div class="rack-actions">
|
||
<button class="btn">+ New Target</button>
|
||
<button class="btn">▤ Layout</button>
|
||
<button class="btn-go btn">◉ Capture All</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ─── HERO STRIP — system overview ─── -->
|
||
<div class="hero">
|
||
<div class="hero-cell primary">
|
||
<span class="label">Active Patches</span>
|
||
<div class="big">03<span class="unit">/ 12</span></div>
|
||
<div class="channels-running">
|
||
<div class="ch-row"><span class="stripe"></span><span class="name">Living Room WLED</span><span class="fps">59.4 FPS</span></div>
|
||
<div class="ch-row"><span class="stripe cyan"></span><span class="name">Desk Strip</span><span class="fps">29.8 FPS</span></div>
|
||
<div class="ch-row"><span class="stripe magenta"></span><span class="name">Bedroom Halo</span><span class="fps">44.2 FPS</span></div>
|
||
</div>
|
||
</div>
|
||
<div class="hero-cell">
|
||
<span class="label">Throughput</span>
|
||
<div class="big">133<span class="unit">FPS</span></div>
|
||
<div class="sub"><span class="delta">▲ 4.2</span><span>vs last min</span></div>
|
||
<svg class="spark" viewBox="0 0 200 28" preserveAspectRatio="none">
|
||
<path class="line" d="M0,18 L20,14 L40,16 L60,9 L80,12 L100,7 L120,11 L140,5 L160,8 L180,4 L200,6"/>
|
||
<path class="fill" d="M0,18 L20,14 L40,16 L60,9 L80,12 L100,7 L120,11 L140,5 L160,8 L180,4 L200,6 L200,28 L0,28 Z"/>
|
||
</svg>
|
||
</div>
|
||
<div class="hero-cell">
|
||
<span class="label">CPU · Capture Pipe</span>
|
||
<div class="big">14<span class="unit">%</span></div>
|
||
<div class="sub"><span class="delta">▼ 1.1</span><span>nominal</span></div>
|
||
<svg class="spark" viewBox="0 0 200 28" preserveAspectRatio="none">
|
||
<path class="line" style="stroke:var(--ch-cyan)" d="M0,16 L20,18 L40,15 L60,17 L80,13 L100,16 L120,11 L140,14 L160,12 L180,15 L200,13"/>
|
||
</svg>
|
||
</div>
|
||
<div class="hero-cell">
|
||
<span class="label">Latency · WLED Net</span>
|
||
<div class="big">12<span class="unit">ms</span></div>
|
||
<div class="sub"><span class="delta bad">▲ 0.4</span><span>p95 stable</span></div>
|
||
<svg class="spark" viewBox="0 0 200 28" preserveAspectRatio="none">
|
||
<path class="line" style="stroke:var(--ch-amber)" d="M0,18 L20,16 L40,18 L60,14 L80,16 L100,12 L120,15 L140,11 L160,13 L180,9 L200,12"/>
|
||
</svg>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ─── SECTION: Capture targets ─── -->
|
||
<section class="section">
|
||
<div class="section-head">
|
||
<h2>Capture Targets <span class="count">03 LIVE / 12 TOTAL</span></h2>
|
||
<div class="rule"></div>
|
||
<div class="actions">
|
||
<button class="icon-btn"><svg viewBox="0 0 24 24"><path d="M3 6h18M3 12h18M3 18h18"/></svg></button>
|
||
<button class="icon-btn"><svg viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/></svg></button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="modules">
|
||
|
||
<!-- MODULE 1 — running (signal green) -->
|
||
<div class="module is-running">
|
||
<div class="signal-flow"></div>
|
||
<div class="mod-head">
|
||
<div class="mod-id">
|
||
<span class="badge">CH·01 · WLED</span>
|
||
<h3>Living Room Strip</h3>
|
||
<div class="meta">192.168.1.42 · 144 LEDs · DDP</div>
|
||
</div>
|
||
<div class="leds">
|
||
<span class="led on blink"></span>
|
||
<span class="led on blink"></span>
|
||
<span class="led on blink"></span>
|
||
</div>
|
||
</div>
|
||
<div class="mod-metrics">
|
||
<div class="metric">
|
||
<span class="k">FPS</span>
|
||
<span class="v signal">59<small>.4</small></span>
|
||
<svg class="spark-sm" viewBox="0 0 100 22" preserveAspectRatio="none">
|
||
<path class="area" d="M0,18 L10,12 L20,14 L30,8 L40,11 L50,6 L60,10 L70,4 L80,9 L90,5 L100,7 L100,22 L0,22 Z"/>
|
||
<path d="M0,18 L10,12 L20,14 L30,8 L40,11 L50,6 L60,10 L70,4 L80,9 L90,5 L100,7"/>
|
||
</svg>
|
||
</div>
|
||
<div class="metric">
|
||
<span class="k">Latency</span>
|
||
<span class="v">8<small>ms</small></span>
|
||
<svg class="spark-sm" viewBox="0 0 100 22" preserveAspectRatio="none">
|
||
<path d="M0,16 L10,14 L20,15 L30,12 L40,14 L50,11 L60,13 L70,10 L80,12 L90,9 L100,11" style="stroke:var(--signal-dim)"/>
|
||
</svg>
|
||
</div>
|
||
<div class="metric">
|
||
<span class="k">Source</span>
|
||
<span class="v" style="font-size:0.85rem;font-family:var(--font-mono)">SCREEN<br><small>2560×1440</small></span>
|
||
</div>
|
||
</div>
|
||
<div class="mod-foot">
|
||
<div class="patch">
|
||
<span class="patch-dot is-live"></span>
|
||
<span>PATCHED · IN→OUT</span>
|
||
</div>
|
||
<button class="btn btn-stop">■ Stop</button>
|
||
<button class="btn">⚙</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- MODULE 2 — running (cyan / data) -->
|
||
<div class="module ch-cyan is-running">
|
||
<div class="signal-flow"></div>
|
||
<div class="mod-head">
|
||
<div class="mod-id">
|
||
<span class="badge">CH·02 · ARTNET</span>
|
||
<h3>Desk Underglow</h3>
|
||
<div class="meta">10.0.0.88 · 60 LEDs · ArtNet U1</div>
|
||
</div>
|
||
<div class="leds">
|
||
<span class="led on blink"></span>
|
||
<span class="led on"></span>
|
||
<span class="led"></span>
|
||
</div>
|
||
</div>
|
||
<div class="mod-metrics">
|
||
<div class="metric">
|
||
<span class="k">FPS</span>
|
||
<span class="v signal">29<small>.8</small></span>
|
||
<svg class="spark-sm" viewBox="0 0 100 22" preserveAspectRatio="none">
|
||
<path class="area" d="M0,12 L10,14 L20,11 L30,13 L40,10 L50,15 L60,9 L70,12 L80,8 L90,11 L100,10 L100,22 L0,22 Z"/>
|
||
<path d="M0,12 L10,14 L20,11 L30,13 L40,10 L50,15 L60,9 L70,12 L80,8 L90,11 L100,10"/>
|
||
</svg>
|
||
</div>
|
||
<div class="metric">
|
||
<span class="k">Latency</span>
|
||
<span class="v">14<small>ms</small></span>
|
||
</div>
|
||
<div class="metric">
|
||
<span class="k">Effect</span>
|
||
<span class="v" style="font-size:0.85rem;font-family:var(--font-mono)">GRAD<br><small>fire+sparkle</small></span>
|
||
</div>
|
||
</div>
|
||
<div class="mod-foot">
|
||
<div class="patch">
|
||
<span class="patch-dot is-live"></span>
|
||
<span>PATCHED · CSS→RGB</span>
|
||
</div>
|
||
<button class="btn btn-stop">■ Stop</button>
|
||
<button class="btn">⚙</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- MODULE 3 — running (magenta / audio) -->
|
||
<div class="module ch-magenta is-running">
|
||
<div class="signal-flow"></div>
|
||
<div class="mod-head">
|
||
<div class="mod-id">
|
||
<span class="badge">CH·03 · AUDIO</span>
|
||
<h3>Bedroom Halo (FFT)</h3>
|
||
<div class="meta">192.168.1.55 · 90 LEDs · E1.31</div>
|
||
</div>
|
||
<div class="leds">
|
||
<span class="led on blink"></span>
|
||
<span class="led on blink"></span>
|
||
<span class="led on blink"></span>
|
||
</div>
|
||
</div>
|
||
<div class="mod-metrics">
|
||
<div class="metric">
|
||
<span class="k">FPS</span>
|
||
<span class="v signal">44<small>.2</small></span>
|
||
<svg class="spark-sm" viewBox="0 0 100 22" preserveAspectRatio="none">
|
||
<path class="area" d="M0,16 L8,4 L16,18 L24,6 L32,15 L40,3 L48,16 L56,8 L64,14 L72,5 L80,17 L88,9 L96,12 L100,7 L100,22 L0,22 Z"/>
|
||
<path d="M0,16 L8,4 L16,18 L24,6 L32,15 L40,3 L48,16 L56,8 L64,14 L72,5 L80,17 L88,9 L96,12 L100,7"/>
|
||
</svg>
|
||
</div>
|
||
<div class="metric">
|
||
<span class="k">Peak</span>
|
||
<span class="v">−6<small>dB</small></span>
|
||
</div>
|
||
<div class="metric">
|
||
<span class="k">Source</span>
|
||
<span class="v" style="font-size:0.85rem;font-family:var(--font-mono)">MIC<br><small>loopback</small></span>
|
||
</div>
|
||
</div>
|
||
<div class="mod-foot">
|
||
<div class="patch">
|
||
<span class="patch-dot is-live"></span>
|
||
<span>PATCHED · FFT→COLOR</span>
|
||
</div>
|
||
<button class="btn btn-stop">■ Stop</button>
|
||
<button class="btn">⚙</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- MODULE 4 — idle (amber / autostart) -->
|
||
<div class="module ch-amber">
|
||
<div class="mod-head">
|
||
<div class="mod-id">
|
||
<span class="badge">CH·04 · WLED</span>
|
||
<h3>Kitchen Counter</h3>
|
||
<div class="meta">192.168.1.61 · 30 LEDs · DDP · idle</div>
|
||
</div>
|
||
<div class="leds">
|
||
<span class="led amber"></span>
|
||
<span class="led"></span>
|
||
<span class="led"></span>
|
||
</div>
|
||
</div>
|
||
<div class="mod-metrics">
|
||
<div class="metric">
|
||
<span class="k">FPS</span>
|
||
<span class="v" style="color:var(--ink-mute)">--</span>
|
||
</div>
|
||
<div class="metric">
|
||
<span class="k">Last Run</span>
|
||
<span class="v" style="font-size:0.85rem;font-family:var(--font-mono);color:var(--ink-dim)">2h<br><small>ago</small></span>
|
||
</div>
|
||
<div class="metric">
|
||
<span class="k">Autostart</span>
|
||
<span class="v" style="font-size:0.85rem;font-family:var(--font-mono);color:var(--ch-amber)">ARMED</span>
|
||
</div>
|
||
</div>
|
||
<div class="mod-foot">
|
||
<div class="patch">
|
||
<span class="patch-dot"></span>
|
||
<span>STANDBY</span>
|
||
</div>
|
||
<button class="btn btn-go" style="--ch:var(--ch-amber)">▶ Start</button>
|
||
<button class="btn">⚙</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- MODULE 5 — offline (coral / alarm) -->
|
||
<div class="module" style="--ch:var(--ch-coral)">
|
||
<div class="mod-head">
|
||
<div class="mod-id">
|
||
<span class="badge">CH·05 · WLED</span>
|
||
<h3>Hallway Run</h3>
|
||
<div class="meta">192.168.1.77 · OFFLINE · last seen 3d</div>
|
||
</div>
|
||
<div class="leds">
|
||
<span class="led" style="background:var(--ch-coral);box-shadow:0 0 6px var(--ch-coral)"></span>
|
||
<span class="led"></span>
|
||
<span class="led"></span>
|
||
</div>
|
||
</div>
|
||
<div class="mod-metrics">
|
||
<div class="metric">
|
||
<span class="k">FPS</span>
|
||
<span class="v" style="color:var(--ink-mute)">--</span>
|
||
</div>
|
||
<div class="metric">
|
||
<span class="k">Health</span>
|
||
<span class="v" style="color:var(--ch-coral);font-size:0.95rem">OFFLINE</span>
|
||
</div>
|
||
<div class="metric">
|
||
<span class="k">Action</span>
|
||
<span class="v" style="font-size:0.75rem;font-family:var(--font-mono);color:var(--ink-dim)">PING<br><small>required</small></span>
|
||
</div>
|
||
</div>
|
||
<div class="mod-foot">
|
||
<div class="patch">
|
||
<span class="patch-dot" style="border-color:var(--ch-coral)"></span>
|
||
<span>UNREACHABLE</span>
|
||
</div>
|
||
<button class="btn">↻ Ping</button>
|
||
<button class="btn">⚙</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- MODULE 6 — graph node (violet) -->
|
||
<div class="module ch-violet">
|
||
<div class="mod-head">
|
||
<div class="mod-id">
|
||
<span class="badge">CH·06 · GRAPH</span>
|
||
<h3>Sunset Scene Composer</h3>
|
||
<div class="meta">3 inputs · 2 outputs · scheduled</div>
|
||
</div>
|
||
<div class="leds">
|
||
<span class="led on" style="background:var(--ch-violet);box-shadow:0 0 6px var(--ch-violet)"></span>
|
||
<span class="led on" style="background:var(--ch-violet);box-shadow:0 0 6px var(--ch-violet)"></span>
|
||
<span class="led"></span>
|
||
</div>
|
||
</div>
|
||
<div class="mod-metrics">
|
||
<div class="metric">
|
||
<span class="k">Nodes</span>
|
||
<span class="v signal">14</span>
|
||
</div>
|
||
<div class="metric">
|
||
<span class="k">Schedule</span>
|
||
<span class="v" style="font-size:0.85rem;font-family:var(--font-mono);color:var(--ink-dim)">19:42<br><small>−12m</small></span>
|
||
</div>
|
||
<div class="metric">
|
||
<span class="k">Targets</span>
|
||
<span class="v" style="font-size:1.1rem">Living + Bedroom</span>
|
||
</div>
|
||
</div>
|
||
<div class="mod-foot">
|
||
<div class="patch">
|
||
<span class="patch-dot"></span>
|
||
<span>SCHEDULED</span>
|
||
</div>
|
||
<button class="btn">▶ Run Now</button>
|
||
<button class="btn">⚙</button>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ─── SECTION: Sources panel preview ─── -->
|
||
<section class="section">
|
||
<div class="section-head">
|
||
<h2>Active Sources <span class="count">04 STREAMING</span></h2>
|
||
<div class="rule"></div>
|
||
<div class="actions">
|
||
<button class="btn">+ Add Source</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="modules">
|
||
|
||
<div class="module ch-cyan is-running">
|
||
<div class="signal-flow"></div>
|
||
<div class="mod-head">
|
||
<div class="mod-id">
|
||
<span class="badge">SRC·01 · SCREEN</span>
|
||
<h3>Display 2 — 4K HDR</h3>
|
||
<div class="meta">3840×2160 · 60 Hz · BGRA · DXGI</div>
|
||
</div>
|
||
<div class="leds">
|
||
<span class="led on blink" style="background:var(--ch-cyan);box-shadow:0 0 6px var(--ch-cyan)"></span>
|
||
<span class="led on" style="background:var(--ch-cyan);box-shadow:0 0 6px var(--ch-cyan)"></span>
|
||
</div>
|
||
</div>
|
||
<div class="mod-metrics">
|
||
<div class="metric">
|
||
<span class="k">Capture</span>
|
||
<span class="v signal">60.0<small>fps</small></span>
|
||
</div>
|
||
<div class="metric">
|
||
<span class="k">CPU</span>
|
||
<span class="v">3.2<small>%</small></span>
|
||
</div>
|
||
<div class="metric">
|
||
<span class="k">Patches</span>
|
||
<span class="v">2</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="module ch-magenta is-running">
|
||
<div class="signal-flow"></div>
|
||
<div class="mod-head">
|
||
<div class="mod-id">
|
||
<span class="badge">SRC·02 · AUDIO</span>
|
||
<h3>Stereo Mix · 48kHz</h3>
|
||
<div class="meta">WASAPI loopback · 1024 FFT · log scale</div>
|
||
</div>
|
||
<div class="leds">
|
||
<span class="led on blink" style="background:var(--ch-magenta);box-shadow:0 0 6px var(--ch-magenta)"></span>
|
||
<span class="led on blink" style="background:var(--ch-magenta);box-shadow:0 0 6px var(--ch-magenta)"></span>
|
||
</div>
|
||
</div>
|
||
<div class="mod-metrics">
|
||
<div class="metric">
|
||
<span class="k">Peak L</span>
|
||
<span class="v signal">−4<small>dB</small></span>
|
||
</div>
|
||
<div class="metric">
|
||
<span class="k">Peak R</span>
|
||
<span class="v signal">−6<small>dB</small></span>
|
||
</div>
|
||
<div class="metric">
|
||
<span class="k">Patches</span>
|
||
<span class="v">1</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="module ch-amber">
|
||
<div class="mod-head">
|
||
<div class="mod-id">
|
||
<span class="badge">SRC·03 · CSS</span>
|
||
<h3>Gradient Fire</h3>
|
||
<div class="meta">linear-gradient · 12 stops · animated 4s</div>
|
||
</div>
|
||
<div class="leds">
|
||
<span class="led on" style="background:var(--ch-amber);box-shadow:0 0 6px var(--ch-amber)"></span>
|
||
</div>
|
||
</div>
|
||
<div class="mod-metrics">
|
||
<div class="metric">
|
||
<span class="k">Refresh</span>
|
||
<span class="v">30<small>Hz</small></span>
|
||
</div>
|
||
<div class="metric">
|
||
<span class="k">CPU</span>
|
||
<span class="v">0.4<small>%</small></span>
|
||
</div>
|
||
<div class="metric">
|
||
<span class="k">Patches</span>
|
||
<span class="v">0</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</section>
|
||
|
||
<div class="plate">
|
||
<span class="stamp">SYSTEM NOMINAL</span>
|
||
<div>BUILD 0.4.2 · KERNEL READY · 3 CHANNELS PATCHED · 04:21:08 UPTIME · NO ERRORS</div>
|
||
<div class="dots"><i></i><i></i><i></i><i></i><i></i></div>
|
||
</div>
|
||
|
||
</main>
|
||
</div>
|
||
|
||
</body>
|
||
</html>
|