563 lines
29 KiB
HTML
563 lines
29 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
|
<title>Home — Dragonflight</title>
|
|
<link rel="stylesheet" href="/dist/app.css">
|
|
<style>
|
|
/* Page-only layout. Sidebar + all chrome is from /dist/app.css primitives.
|
|
Hero / cards / SVG art are bespoke to the home landing page. */
|
|
body { margin: 0; }
|
|
.wd-shell { display: flex; min-height: 100vh; }
|
|
|
|
.home-main {
|
|
flex: 1;
|
|
overflow: auto;
|
|
position: relative;
|
|
background:
|
|
radial-gradient(ellipse 70% 50% at 50% 0%, oklch(35% 0.16 32 / 0.55), transparent 65%),
|
|
radial-gradient(ellipse 80% 60% at 30% 90%, oklch(40% 0.20 32 / 0.45), transparent 70%),
|
|
radial-gradient(ellipse 60% 50% at 80% 80%, oklch(45% 0.18 20 / 0.30), transparent 65%),
|
|
linear-gradient(135deg, oklch(20% 0.05 30), var(--bg-base) 100%);
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.home-stage {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: safe center;
|
|
padding: 56px 32px 32px;
|
|
gap: 36px;
|
|
}
|
|
|
|
.home-brandmark {
|
|
display: flex; flex-direction: column; align-items: center; gap: 14px;
|
|
}
|
|
.home-portrait {
|
|
width: 120px; height: 120px;
|
|
border-radius: 50%;
|
|
overflow: hidden; position: relative;
|
|
background:
|
|
radial-gradient(ellipse at 40% 30%, oklch(35% 0.18 32 / 0.4), transparent 65%),
|
|
linear-gradient(135deg, var(--bg-surface), var(--bg-deep));
|
|
border: 2px solid var(--accent-border);
|
|
box-shadow:
|
|
0 20px 50px -10px oklch(62% 0.22 32 / 0.4),
|
|
inset 0 1px 0 oklch(100% 0 0 / 0.08);
|
|
}
|
|
.home-portrait img {
|
|
position: absolute; inset: 0;
|
|
width: 100%; height: 100%;
|
|
object-fit: cover;
|
|
object-position: 50% 30%;
|
|
}
|
|
.home-portrait-dot {
|
|
position: absolute; bottom: 4px; right: 4px;
|
|
width: 18px; height: 18px;
|
|
background: var(--signal-bad);
|
|
border: 3px solid var(--bg-panel);
|
|
border-radius: 50%;
|
|
box-shadow: 0 0 12px var(--signal-bad);
|
|
animation: live-pulse 1.6s ease-in-out infinite;
|
|
}
|
|
@keyframes live-pulse {
|
|
0%, 100% { opacity: 0.85; transform: scale(1); }
|
|
50% { opacity: 1; transform: scale(1.1); }
|
|
}
|
|
.home-wordmark-svg {
|
|
width: min(520px, 80vw);
|
|
height: auto;
|
|
filter: drop-shadow(0 16px 30px oklch(50% 0.22 32 / 0.45));
|
|
margin-bottom: -10px;
|
|
}
|
|
|
|
.home-tagline {
|
|
display: flex; align-items: center; gap: 12px;
|
|
font: 500 17px/1 var(--font);
|
|
color: oklch(75% 0.10 32);
|
|
letter-spacing: 0.005em;
|
|
}
|
|
.home-tagline::before {
|
|
content: ''; width: 3px; height: 18px;
|
|
background: var(--accent-bright);
|
|
border-radius: 2px;
|
|
}
|
|
|
|
.home-cards {
|
|
display: flex;
|
|
gap: 18px;
|
|
width: 100%;
|
|
max-width: 1680px;
|
|
padding: 0 24px;
|
|
flex-wrap: wrap;
|
|
justify-content: center;
|
|
}
|
|
.home-card {
|
|
flex: 1 1 0;
|
|
min-width: 200px;
|
|
max-width: 220px;
|
|
background: oklch(12% 0.020 30 / 0.85);
|
|
border: 1px solid var(--border-faint);
|
|
border-radius: 10px;
|
|
overflow: hidden;
|
|
text-decoration: none;
|
|
color: var(--text-primary);
|
|
display: flex;
|
|
flex-direction: column;
|
|
cursor: pointer;
|
|
backdrop-filter: blur(6px);
|
|
transition: transform var(--dur-slide) var(--ease-out-quart),
|
|
border-color var(--dur-slide) var(--ease-out-quart),
|
|
box-shadow var(--dur-slide) var(--ease-out-quart);
|
|
}
|
|
.home-card:hover {
|
|
transform: translateY(-4px);
|
|
border-color: var(--accent-border);
|
|
box-shadow: 0 24px 50px -16px oklch(62% 0.22 32 / 0.4);
|
|
}
|
|
.home-card-title {
|
|
padding: 12px 16px 10px;
|
|
font: 500 15px/1.2 var(--font);
|
|
letter-spacing: 0.005em;
|
|
color: var(--text-primary);
|
|
}
|
|
.home-card-preview {
|
|
position: relative;
|
|
aspect-ratio: 16/10;
|
|
background: linear-gradient(135deg, var(--bg-surface), var(--bg-base));
|
|
overflow: hidden;
|
|
border-top: 1px solid var(--border-faint);
|
|
border-bottom: 1px solid var(--border-faint);
|
|
}
|
|
.home-card-preview svg.preview-art {
|
|
position: absolute; inset: 0;
|
|
width: 100%; height: 100%;
|
|
}
|
|
.home-card-stats {
|
|
position: absolute; bottom: 8px; left: 10px;
|
|
display: inline-flex; align-items: center; gap: 6px;
|
|
background: var(--overlay);
|
|
backdrop-filter: blur(6px);
|
|
padding: 3px 8px;
|
|
border-radius: 999px;
|
|
border: 1px solid var(--border-faint);
|
|
font: 400 10px/1 var(--font-mono);
|
|
letter-spacing: 0.04em;
|
|
color: var(--text-secondary);
|
|
}
|
|
.home-card-stats b { color: var(--text-primary); font-weight: 600; }
|
|
.home-card-stats-dot {
|
|
width: 6px; height: 6px; border-radius: 50%;
|
|
background: var(--signal-good); box-shadow: 0 0 6px var(--signal-good);
|
|
}
|
|
.home-card-desc {
|
|
padding: 12px 16px 16px;
|
|
font: 400 11.5px/1.5 var(--font);
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
.home-footer {
|
|
padding: 24px;
|
|
display: flex; align-items: center; gap: 10px;
|
|
justify-content: center;
|
|
color: var(--text-tertiary);
|
|
font: 400 11px/1 var(--font);
|
|
letter-spacing: 0.08em;
|
|
text-transform: uppercase;
|
|
}
|
|
.home-footer-mark {
|
|
display: inline-flex; align-items: center; gap: 8px;
|
|
font-weight: 700;
|
|
color: var(--text-secondary);
|
|
}
|
|
.home-footer-mark-dot {
|
|
width: 6px; height: 6px; border-radius: 50%;
|
|
background: var(--accent-bright);
|
|
}
|
|
|
|
@media (max-width: 900px) {
|
|
.home-stage { padding: 32px 16px; gap: 24px; }
|
|
.home-portrait { width: 96px; height: 96px; }
|
|
.home-cards { gap: 12px; padding: 0 12px; }
|
|
.home-card { min-width: 150px; }
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<div class="wd-shell">
|
|
<nav class="wd-sidebar" aria-label="Main navigation">
|
|
<div class="wd-sidebar-header">
|
|
<img src="img/dragon-logo.png?v=1" alt="Dragonflight" style="width:18px;height:18px;">
|
|
<span class="wd-sidebar-brand">Dragonflight</span>
|
|
</div>
|
|
<div class="wd-sidebar-nav">
|
|
<a href="home.html" class="wd-nav-item is-active">
|
|
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M2 7l6-5 6 5v7a1 1 0 0 1-1 1h-3v-5H6v5H3a1 1 0 0 1-1-1z"/></svg>
|
|
Home
|
|
</a>
|
|
<a href="index.html" class="wd-nav-item">
|
|
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="1" y="1" width="6" height="6" rx="1"/><rect x="9" y="1" width="6" height="6" rx="1"/><rect x="1" y="9" width="6" height="6" rx="1"/><rect x="9" y="9" width="6" height="6" rx="1"/></svg>
|
|
Library
|
|
</a>
|
|
<a href="projects.html" class="wd-nav-item">
|
|
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M1 4a1 1 0 0 1 1-1h4l2 2h5a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1z"/></svg>
|
|
Projects
|
|
</a>
|
|
<a href="upload.html" class="wd-nav-item">
|
|
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M8 11V3M5 6l3-3 3 3"/><path d="M2 13h12"/></svg>
|
|
Ingest
|
|
</a>
|
|
<a href="recorders.html" class="wd-nav-item">
|
|
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="1" y="4" width="10" height="8" rx="1"/><path d="M11 7l4-2v6l-4-2"/></svg>
|
|
Recorders
|
|
</a>
|
|
<a href="capture.html" class="wd-nav-item">
|
|
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="8" cy="8" r="3"/><circle cx="8" cy="8" r="6.5"/></svg>
|
|
Capture
|
|
</a>
|
|
<a href="jobs.html" class="wd-nav-item">
|
|
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M2 4h12M2 8h8M2 12h5"/></svg>
|
|
Jobs
|
|
</a>
|
|
<a href="editor.html" class="wd-nav-item">
|
|
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M2 13l1.5-3.5L11 2l3 3-7.5 7.5L3 14zM10 3l3 3"/></svg>
|
|
Editor
|
|
</a>
|
|
<div class="wd-sidebar-section">Admin</div>
|
|
<a href="users.html" class="wd-nav-item">
|
|
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="6" cy="5" r="2.5"/><path d="M1 13c0-2.8 2.2-5 5-5s5 2.2 5 5"/><circle cx="12" cy="5" r="2"/><path d="M15 12c0-1.9-1.3-3.5-3-4"/></svg>
|
|
Users
|
|
</a>
|
|
<a href="tokens.html" class="wd-nav-item">
|
|
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="6" cy="10" r="3.5"/><path d="M8.7 7.3L13 3M11.5 3.5l1.5 1.5M13.5 2.5l1 1"/></svg>
|
|
Tokens
|
|
</a>
|
|
<a href="containers.html" class="wd-nav-item">
|
|
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="1" y="5" width="14" height="4" rx="1"/><rect x="1" y="10" width="14" height="4" rx="1"/><path d="M4 7h1M4 12h1"/></svg>
|
|
Containers
|
|
</a>
|
|
<a href="cluster.html" class="wd-nav-item">
|
|
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="8" cy="8" r="2"/><circle cx="2" cy="3" r="1.5"/><circle cx="14" cy="3" r="1.5"/><circle cx="2" cy="13" r="1.5"/><circle cx="14" cy="13" r="1.5"/><path d="M3.1 4.1L6.5 6.5M12.9 4.1L9.5 6.5M3.1 11.9L6.5 9.5M12.9 11.9L9.5 9.5"/></svg>
|
|
Cluster
|
|
</a>
|
|
<a href="settings.html" class="wd-nav-item">
|
|
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="8" cy="8" r="2.5"/><path d="M8 1v1.5M8 13.5V15M1 8h1.5M13.5 8H15M2.9 2.9l1.1 1.1M12 12l1.1 1.1M2.9 13.1L4 12M12 4l1.1-1.1"/></svg>
|
|
Settings
|
|
</a>
|
|
</div>
|
|
<div class="wd-sidebar-footer">
|
|
<div class="wd-sidebar-user">
|
|
<div class="wd-sidebar-user-avatar" id="userAvatar">?</div>
|
|
<div class="wd-sidebar-user-info">
|
|
<div class="wd-sidebar-user-name" id="userName">—</div>
|
|
<div class="wd-sidebar-user-role" id="userRole"></div>
|
|
</div>
|
|
<button class="wd-btn wd-btn--ghost wd-btn--sm wd-btn--icon wd-sidebar-user-logout" id="logoutBtn" title="Sign out">
|
|
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" width="12" height="12"><path d="M10 8H3M6 5l-3 3 3 3"/><path d="M7 3h5a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1H7"/></svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
<div class="home-main">
|
|
<div class="home-stage">
|
|
<div class="home-brandmark">
|
|
<div class="home-portrait">
|
|
<img src="img/ampp-safe.png?v=hardhat3" alt="Zac in hardhat">
|
|
<span class="home-portrait-dot" title="On duty"></span>
|
|
</div>
|
|
<svg class="home-wordmark-svg" viewBox="0 0 760 132" xmlns="http://www.w3.org/2000/svg" aria-label="Dragonflight">
|
|
<defs>
|
|
<linearGradient id="dfwm" x1="0" y1="0" x2="0" y2="1">
|
|
<stop offset="0" stop-color="oklch(88% 0.16 48)"/>
|
|
<stop offset="0.5" stop-color="oklch(68% 0.22 32)"/>
|
|
<stop offset="1" stop-color="oklch(42% 0.18 22)"/>
|
|
</linearGradient>
|
|
</defs>
|
|
<text x="0" y="108" font-family="Inter, system-ui, sans-serif" font-weight="900" font-size="120" letter-spacing="-6" fill="url(#dfwm)">Dragonflight</text>
|
|
</svg>
|
|
</div>
|
|
|
|
<div class="home-tagline">Please select an option below to get started</div>
|
|
|
|
<div class="home-cards">
|
|
|
|
<a href="index.html" class="home-card">
|
|
<div class="home-card-title">Library</div>
|
|
<div class="home-card-preview">
|
|
<svg class="preview-art" viewBox="0 0 200 125" xmlns="http://www.w3.org/2000/svg">
|
|
<rect x="0" y="0" width="200" height="125" fill="oklch(13% 0.02 30)"/>
|
|
<g fill="oklch(25% 0.05 30)" stroke="oklch(45% 0.10 32 / 0.4)" stroke-width="0.5">
|
|
<rect x="12" y="14" width="40" height="28" rx="2"/>
|
|
<rect x="58" y="14" width="40" height="28" rx="2"/>
|
|
<rect x="104" y="14" width="40" height="28" rx="2"/>
|
|
<rect x="150" y="14" width="40" height="28" rx="2"/>
|
|
<rect x="12" y="48" width="40" height="28" rx="2"/>
|
|
<rect x="58" y="48" width="40" height="28" rx="2"/>
|
|
<rect x="104" y="48" width="40" height="28" rx="2"/>
|
|
<rect x="150" y="48" width="40" height="28" rx="2"/>
|
|
<rect x="12" y="82" width="40" height="28" rx="2"/>
|
|
<rect x="58" y="82" width="40" height="28" rx="2"/>
|
|
<rect x="104" y="82" width="40" height="28" rx="2"/>
|
|
<rect x="150" y="82" width="40" height="28" rx="2"/>
|
|
</g>
|
|
<g fill="oklch(62% 0.22 32 / 0.8)"><circle cx="32" cy="28" r="3"/><circle cx="124" cy="62" r="3"/><circle cx="170" cy="96" r="3"/></g>
|
|
</svg>
|
|
<span class="home-card-stats"><span class="home-card-stats-dot"></span><b id="assetCount">--</b> assets</span>
|
|
</div>
|
|
<div class="home-card-desc">Browse, organize, and preview every asset across all projects.</div>
|
|
</a>
|
|
|
|
<a href="projects.html" class="home-card">
|
|
<div class="home-card-title">Projects</div>
|
|
<div class="home-card-preview">
|
|
<svg class="preview-art" viewBox="0 0 200 125" xmlns="http://www.w3.org/2000/svg">
|
|
<rect x="0" y="0" width="200" height="125" fill="oklch(13% 0.02 30)"/>
|
|
<g fill="oklch(20% 0.04 30)" stroke="oklch(45% 0.10 32 / 0.4)" stroke-width="0.5">
|
|
<path d="M12 24 L12 100 L188 100 L188 36 L92 36 L80 24 Z" />
|
|
<path d="M28 56 L184 56" stroke-dasharray="2,3"/>
|
|
<path d="M28 76 L184 76" stroke-dasharray="2,3"/>
|
|
</g>
|
|
<g fill="oklch(62% 0.22 32 / 0.6)">
|
|
<rect x="32" y="46" width="6" height="6" rx="1"/>
|
|
<rect x="32" y="66" width="6" height="6" rx="1"/>
|
|
<rect x="32" y="86" width="6" height="6" rx="1"/>
|
|
</g>
|
|
</svg>
|
|
<span class="home-card-stats"><span class="home-card-stats-dot"></span><b id="projectCount">--</b> projects</span>
|
|
</div>
|
|
<div class="home-card-desc">Create projects, organize bins, and manage who owns what footage.</div>
|
|
</a>
|
|
|
|
<a href="upload.html" class="home-card">
|
|
<div class="home-card-title">Ingest</div>
|
|
<div class="home-card-preview">
|
|
<svg class="preview-art" viewBox="0 0 200 125" xmlns="http://www.w3.org/2000/svg">
|
|
<rect x="0" y="0" width="200" height="125" fill="oklch(13% 0.02 30)"/>
|
|
<g stroke="oklch(62% 0.22 32 / 0.6)" stroke-width="2" fill="none">
|
|
<path d="M100 80 V 30 M85 45 L 100 30 L 115 45" stroke-linecap="round" stroke-linejoin="round"/>
|
|
<line x1="30" y1="100" x2="170" y2="100" stroke-dasharray="3,3" stroke-width="1"/>
|
|
</g>
|
|
<rect x="40" y="92" width="120" height="6" rx="3" fill="oklch(25% 0.05 30)"/>
|
|
<rect x="40" y="92" width="68" height="6" rx="3" fill="oklch(62% 0.22 32 / 0.9)"/>
|
|
</svg>
|
|
<span class="home-card-stats"><span class="home-card-stats-dot"></span><b id="ingestCount">--</b> processing</span>
|
|
</div>
|
|
<div class="home-card-desc">Upload finished files. MOV, MP4, MXF, ProRes, drop them, we proxy them.</div>
|
|
</a>
|
|
|
|
<a href="recorders.html" class="home-card">
|
|
<div class="home-card-title">Recorders</div>
|
|
<div class="home-card-preview">
|
|
<svg class="preview-art" viewBox="0 0 200 125" xmlns="http://www.w3.org/2000/svg">
|
|
<rect x="0" y="0" width="200" height="125" fill="oklch(13% 0.02 30)"/>
|
|
<rect x="20" y="22" width="120" height="80" rx="4" fill="oklch(8% 0.01 30)" stroke="oklch(45% 0.10 32 / 0.4)" stroke-width="0.5"/>
|
|
<g opacity="0.5"><path d="M30 50 q15 -12 30 0 t30 0 t30 0 t30 0" stroke="oklch(62% 0.22 32)" stroke-width="1" fill="none"/><path d="M30 70 q15 -8 30 0 t30 0 t30 0 t30 0" stroke="oklch(62% 0.22 32)" stroke-width="1" fill="none"/></g>
|
|
<path d="M140 62 L182 42 L182 82 Z" fill="oklch(20% 0.04 30)" stroke="oklch(45% 0.10 32 / 0.4)" stroke-width="0.5"/>
|
|
<circle cx="160" cy="62" r="6" fill="oklch(62% 0.22 25)"><animate attributeName="opacity" values="1;0.5;1" dur="1.6s" repeatCount="indefinite"/></circle>
|
|
</svg>
|
|
<span class="home-card-stats"><span class="home-card-stats-dot" style="background:var(--signal-bad);box-shadow:0 0 6px var(--signal-bad)"></span><b id="recorderCount">--</b> live</span>
|
|
</div>
|
|
<div class="home-card-desc">Pull SRT, RTMP, and SDI feeds straight to ProRes with a live HLS preview.</div>
|
|
</a>
|
|
|
|
<a href="capture.html" class="home-card">
|
|
<div class="home-card-title">Capture</div>
|
|
<div class="home-card-preview">
|
|
<svg class="preview-art" viewBox="0 0 200 125" xmlns="http://www.w3.org/2000/svg">
|
|
<rect x="0" y="0" width="200" height="125" fill="oklch(13% 0.02 30)"/>
|
|
<circle cx="100" cy="62" r="34" fill="none" stroke="oklch(45% 0.10 32 / 0.3)" stroke-width="0.5"/>
|
|
<circle cx="100" cy="62" r="22" fill="none" stroke="oklch(45% 0.10 32 / 0.5)" stroke-width="0.5"/>
|
|
<circle cx="100" cy="62" r="12" fill="oklch(20% 0.04 30)" stroke="oklch(62% 0.22 32 / 0.7)" stroke-width="1"/>
|
|
<circle cx="100" cy="62" r="4" fill="oklch(76% 0.20 32)"/>
|
|
</svg>
|
|
<span class="home-card-stats"><span class="home-card-stats-dot"></span><b id="captureStatus">Idle</b></span>
|
|
</div>
|
|
<div class="home-card-desc">DeckLink SDI capture with manual scene control and per-device routing.</div>
|
|
</a>
|
|
|
|
<a href="editor.html" class="home-card">
|
|
<div class="home-card-title">Editor</div>
|
|
<div class="home-card-preview">
|
|
<svg class="preview-art" viewBox="0 0 200 125" xmlns="http://www.w3.org/2000/svg">
|
|
<rect x="0" y="0" width="200" height="125" fill="oklch(13% 0.02 30)"/>
|
|
<rect x="14" y="14" width="172" height="44" rx="3" fill="oklch(8% 0.01 30)" stroke="oklch(45% 0.10 32 / 0.4)" stroke-width="0.5"/>
|
|
<g stroke="oklch(45% 0.10 32 / 0.4)" stroke-width="0.5">
|
|
<line x1="14" y1="74" x2="186" y2="74"/>
|
|
<line x1="14" y1="92" x2="186" y2="92"/>
|
|
<line x1="14" y1="110" x2="186" y2="110"/>
|
|
</g>
|
|
<rect x="22" y="68" width="60" height="12" rx="2" fill="oklch(62% 0.22 32 / 0.8)"/>
|
|
<rect x="90" y="68" width="38" height="12" rx="2" fill="oklch(62% 0.22 32 / 0.5)"/>
|
|
<rect x="40" y="86" width="90" height="12" rx="2" fill="oklch(76% 0.20 32 / 0.6)"/>
|
|
<rect x="30" y="104" width="80" height="12" rx="2" fill="oklch(62% 0.22 32 / 0.7)"/>
|
|
</svg>
|
|
<span class="home-card-stats"><span class="home-card-stats-dot"></span>Web editor ↗</span>
|
|
</div>
|
|
<div class="home-card-desc">Pull a clip from the library into the in-browser editor. Trim, cut, export.</div>
|
|
</a>
|
|
|
|
<a href="jobs.html" class="home-card">
|
|
<div class="home-card-title">Jobs</div>
|
|
<div class="home-card-preview">
|
|
<svg class="preview-art" viewBox="0 0 200 125" xmlns="http://www.w3.org/2000/svg">
|
|
<rect x="0" y="0" width="200" height="125" fill="oklch(13% 0.02 30)"/>
|
|
<g stroke="oklch(45% 0.10 32 / 0.4)" stroke-width="0.5">
|
|
<line x1="14" y1="28" x2="186" y2="28"/>
|
|
<line x1="14" y1="46" x2="186" y2="46"/>
|
|
<line x1="14" y1="64" x2="186" y2="64"/>
|
|
<line x1="14" y1="82" x2="186" y2="82"/>
|
|
<line x1="14" y1="100" x2="186" y2="100"/>
|
|
</g>
|
|
<rect x="22" y="20" width="38" height="6" rx="3" fill="oklch(45% 0.16 145 / 0.8)"/>
|
|
<rect x="22" y="38" width="80" height="6" rx="3" fill="oklch(45% 0.16 145 / 0.6)"/>
|
|
<rect x="22" y="56" width="120" height="6" rx="3" fill="oklch(70% 0.18 80 / 0.8)"/>
|
|
<rect x="22" y="74" width="60" height="6" rx="3" fill="oklch(45% 0.16 145 / 0.7)"/>
|
|
<rect x="22" y="92" width="100" height="6" rx="3" fill="oklch(45% 0.16 145 / 0.5)"/>
|
|
</svg>
|
|
<span class="home-card-stats"><span class="home-card-stats-dot"></span><b id="jobCount">--</b> active</span>
|
|
</div>
|
|
<div class="home-card-desc">Track proxy generation, thumbnails, and folder sync as they run.</div>
|
|
</a>
|
|
|
|
<a href="containers.html" class="home-card">
|
|
<div class="home-card-title">Containers</div>
|
|
<div class="home-card-preview">
|
|
<svg class="preview-art" viewBox="0 0 200 125" xmlns="http://www.w3.org/2000/svg">
|
|
<rect x="0" y="0" width="200" height="125" fill="oklch(13% 0.02 30)"/>
|
|
<g fill="oklch(20% 0.04 30)" stroke="oklch(45% 0.10 32 / 0.45)" stroke-width="0.6">
|
|
<rect x="20" y="18" width="160" height="22" rx="3"/>
|
|
<rect x="20" y="46" width="160" height="22" rx="3"/>
|
|
<rect x="20" y="74" width="160" height="22" rx="3"/>
|
|
<rect x="20" y="102" width="160" height="16" rx="3"/>
|
|
</g>
|
|
<circle cx="34" cy="29" r="3.5" fill="oklch(62% 0.22 145)"/>
|
|
<circle cx="34" cy="57" r="3.5" fill="oklch(62% 0.22 145)"/>
|
|
<circle cx="34" cy="85" r="3.5" fill="oklch(62% 0.22 145)"/>
|
|
<circle cx="34" cy="110" r="3.5" fill="oklch(62% 0.22 25)"/>
|
|
<rect x="46" y="25" width="60" height="5" rx="2" fill="oklch(50% 0.12 32 / 0.7)"/>
|
|
<rect x="46" y="53" width="80" height="5" rx="2" fill="oklch(50% 0.12 32 / 0.7)"/>
|
|
<rect x="46" y="81" width="50" height="5" rx="2" fill="oklch(50% 0.12 32 / 0.7)"/>
|
|
<rect x="46" y="107" width="70" height="4" rx="2" fill="oklch(40% 0.10 32 / 0.5)"/>
|
|
</svg>
|
|
<span class="home-card-stats"><span class="home-card-stats-dot"></span><b id="containerCount">--</b> running</span>
|
|
</div>
|
|
<div class="home-card-desc">Manage Docker Compose services, start, stop, and restart containers.</div>
|
|
</a>
|
|
|
|
<a href="cluster.html" class="home-card">
|
|
<div class="home-card-title">Cluster</div>
|
|
<div class="home-card-preview">
|
|
<svg class="preview-art" viewBox="0 0 200 125" xmlns="http://www.w3.org/2000/svg">
|
|
<rect x="0" y="0" width="200" height="125" fill="oklch(13% 0.02 30)"/>
|
|
<circle cx="100" cy="62" r="10" fill="oklch(20% 0.04 30)" stroke="oklch(62% 0.22 32 / 0.7)" stroke-width="1"/>
|
|
<circle cx="100" cy="62" r="4" fill="oklch(76% 0.20 32)"/>
|
|
<line x1="100" y1="62" x2="36" y2="22" stroke="oklch(45% 0.12 32 / 0.4)" stroke-width="1"/>
|
|
<line x1="100" y1="62" x2="164" y2="22" stroke="oklch(45% 0.12 32 / 0.4)" stroke-width="1"/>
|
|
<line x1="100" y1="62" x2="24" y2="80" stroke="oklch(45% 0.12 32 / 0.4)" stroke-width="1"/>
|
|
<line x1="100" y1="62" x2="176" y2="80" stroke="oklch(45% 0.12 32 / 0.4)" stroke-width="1"/>
|
|
<line x1="100" y1="62" x2="100" y2="108" stroke="oklch(45% 0.12 32 / 0.4)" stroke-width="1"/>
|
|
<circle cx="36" cy="22" r="7" fill="oklch(18% 0.03 30)" stroke="oklch(45% 0.10 32 / 0.5)" stroke-width="0.6"/>
|
|
<circle cx="164" cy="22" r="7" fill="oklch(18% 0.03 30)" stroke="oklch(45% 0.10 32 / 0.5)" stroke-width="0.6"/>
|
|
<circle cx="24" cy="80" r="7" fill="oklch(18% 0.03 30)" stroke="oklch(45% 0.10 32 / 0.5)" stroke-width="0.6"/>
|
|
<circle cx="176" cy="80" r="7" fill="oklch(18% 0.03 30)" stroke="oklch(45% 0.10 32 / 0.5)" stroke-width="0.6"/>
|
|
<circle cx="100" cy="108" r="7" fill="oklch(18% 0.03 30)" stroke="oklch(45% 0.10 32 / 0.5)" stroke-width="0.6"/>
|
|
<circle cx="36" cy="22" r="2.5" fill="oklch(62% 0.22 145)"/>
|
|
<circle cx="164" cy="22" r="2.5" fill="oklch(62% 0.22 145)"/>
|
|
<circle cx="24" cy="80" r="2.5" fill="oklch(62% 0.22 145)"/>
|
|
<circle cx="176" cy="80" r="2.5" fill="oklch(55% 0.18 80)"/>
|
|
<circle cx="100" cy="108" r="2.5" fill="oklch(45% 0.10 32 / 0.4)"/>
|
|
</svg>
|
|
<span class="home-card-stats"><span class="home-card-stats-dot"></span><b id="nodeCount">--</b> nodes</span>
|
|
</div>
|
|
<div class="home-card-desc">Multi-server cluster registry. Monitor, federate, and scale Dragonflight nodes.</div>
|
|
</a>
|
|
|
|
<a href="tokens.html" class="home-card" title="Just kidding">
|
|
<div class="home-card-title">Tokens</div>
|
|
<div class="home-card-preview">
|
|
<svg class="preview-art" viewBox="0 0 200 125" xmlns="http://www.w3.org/2000/svg">
|
|
<rect x="0" y="0" width="200" height="125" fill="oklch(13% 0.02 30)"/>
|
|
<g stroke="oklch(45% 0.10 32 / 0.5)" stroke-width="0.5"><line x1="14" y1="100" x2="186" y2="100"/><line x1="14" y1="80" x2="186" y2="80" stroke-dasharray="2,3"/><line x1="14" y1="60" x2="186" y2="60" stroke-dasharray="2,3"/></g>
|
|
<polyline points="14,90 38,72 62,80 86,55 110,62 134,40 158,48 186,28" fill="none" stroke="oklch(70% 0.18 200)" stroke-width="2"/>
|
|
<polyline points="14,95 38,86 62,90 86,76 110,80 134,68 158,72 186,58" fill="none" stroke="oklch(62% 0.15 145)" stroke-width="2"/>
|
|
<circle cx="186" cy="28" r="3" fill="oklch(62% 0.22 25)"/>
|
|
</svg>
|
|
<span class="home-card-stats"><span class="home-card-stats-dot" style="background:var(--signal-bad);box-shadow:0 0 6px var(--signal-bad)"></span><b id="tokenBurn">—</b> burning</span>
|
|
</div>
|
|
<div class="home-card-desc">Token-metered pricing parody. Click for a giggle. (You actually pay $0.)</div>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="home-footer">
|
|
<span class="home-footer-mark"><span class="home-footer-mark-dot"></span>Wild Dragon</span>
|
|
<span>·</span>
|
|
<span id="systemBuild">Dragonflight Operator Console</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="js/api.js?v=6"></script>
|
|
<script src="js/topbar-strip.js?v=1"></script>
|
|
<script>
|
|
async function loadStats() {
|
|
const setText = (id, val) => { const el = document.getElementById(id); if (el) el.textContent = val; };
|
|
try {
|
|
const [aRes, pRes, rRes, jRes, cRes, nRes] = await Promise.allSettled([
|
|
fetch('/api/v1/assets?limit=1', { credentials: 'include' }),
|
|
fetch('/api/v1/projects', { credentials: 'include' }),
|
|
fetch('/api/v1/recorders', { credentials: 'include' }),
|
|
fetch('/api/v1/jobs?status=active', { credentials: 'include' }),
|
|
fetch('/api/v1/system/containers', { credentials: 'include' }),
|
|
fetch('/api/v1/cluster', { credentials: 'include' }),
|
|
]);
|
|
|
|
if (aRes.status === 'fulfilled' && aRes.value.ok) {
|
|
const j = await aRes.value.json();
|
|
setText('assetCount', j.total ?? (j.assets?.length ?? '--'));
|
|
}
|
|
if (pRes.status === 'fulfilled' && pRes.value.ok) {
|
|
const j = await pRes.value.json();
|
|
const arr = Array.isArray(j) ? j : (j.projects ?? []);
|
|
setText('projectCount', arr.length);
|
|
}
|
|
if (rRes.status === 'fulfilled' && rRes.value.ok) {
|
|
const j = await rRes.value.json();
|
|
const arr = Array.isArray(j) ? j : [];
|
|
const recording = arr.filter(r => r.status === 'recording').length;
|
|
setText('recorderCount', recording);
|
|
}
|
|
if (jRes.status === 'fulfilled' && jRes.value.ok) {
|
|
const j = await jRes.value.json();
|
|
const arr = Array.isArray(j) ? j : (j.jobs ?? []);
|
|
setText('jobCount', arr.length);
|
|
setText('ingestCount', arr.filter(x => (x.type || '').toLowerCase().includes('proxy')).length || arr.length);
|
|
}
|
|
if (cRes.status === 'fulfilled' && cRes.value.ok) {
|
|
const j = await cRes.value.json();
|
|
const arr = Array.isArray(j) ? j : (j.containers ?? []);
|
|
const running = arr.filter(c => c.state === 'running').length;
|
|
setText('containerCount', running);
|
|
}
|
|
if (nRes.status === 'fulfilled' && nRes.value.ok) {
|
|
const j = await nRes.value.json();
|
|
const arr = Array.isArray(j) ? j : (j.nodes ?? []);
|
|
const online = arr.filter(n => n.online).length;
|
|
setText('nodeCount', arr.length > 0 ? online + '/' + arr.length : '--');
|
|
}
|
|
const tb = document.getElementById('tokenBurn'); if (tb) { tb.textContent = (14000 + Math.round(Math.random() * 8000)).toLocaleString(); }
|
|
} catch (_) { /* leave dashes */ }
|
|
}
|
|
loadStats();
|
|
setInterval(loadStats, 15000);
|
|
</script>
|
|
<script src="js/auth-guard.js"></script>
|
|
</body>
|
|
</html>
|