dragonflight/services/web-ui/public/edit.html

397 lines
15 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>Editor — Z-AMPP</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=Inter:wght@400;500;600&display=swap" rel="stylesheet">
<link rel="stylesheet" href="css/common.css">
<style>
/* ── Editor layout ── */
.edit-shell { display: flex; flex: 1; overflow: hidden; min-height: 0; }
/* Left rail: asset library */
.edit-assets {
width: 280px;
flex-shrink: 0;
border-right: 1px solid var(--border);
display: flex; flex-direction: column;
background: oklch(11% 0.018 250 / 0.7);
min-height: 0;
}
.edit-assets-head {
padding: 12px 14px;
border-bottom: 1px solid var(--border);
display: flex; flex-direction: column; gap: 8px;
}
.edit-assets-title {
font-size: 11px; font-weight: 600;
letter-spacing: 0.16em; text-transform: uppercase;
color: var(--text-tertiary);
}
.edit-assets-search input {
width: 100%; padding: 7px 10px;
background: oklch(15% 0.020 250);
border: 1px solid var(--border);
border-radius: var(--r-sm);
color: var(--text-primary); font-size: 13px;
}
.edit-assets-list {
flex: 1; overflow: auto; padding: 6px;
}
.edit-asset {
display: flex; gap: 10px; align-items: center;
padding: 6px;
border-radius: var(--r-sm);
cursor: grab;
border: 1px solid transparent;
}
.edit-asset:hover { background: oklch(15% 0.020 250 / 0.7); border-color: var(--border); }
.edit-asset:active { cursor: grabbing; }
.edit-asset-thumb {
width: 56px; height: 32px;
background: #000; border-radius: 3px;
object-fit: cover; flex-shrink: 0;
}
.edit-asset-name {
font-size: 12px; line-height: 1.3;
color: var(--text-primary);
overflow: hidden; text-overflow: ellipsis;
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
/* Middle: preview + timeline */
.edit-stage {
flex: 1; display: flex; flex-direction: column;
min-width: 0; min-height: 0;
background: var(--bg-base);
}
.edit-preview-wrap {
flex: 1; display: flex; flex-direction: column;
padding: 18px 24px 0;
min-height: 0;
}
.edit-preview {
flex: 1;
background: #000;
border-radius: 8px;
overflow: hidden;
position: relative;
display: flex; align-items: center; justify-content: center;
border: 1px solid oklch(28% 0.04 260 / 0.4);
min-height: 200px;
}
.edit-preview video {
width: 100%; height: 100%;
object-fit: contain;
background: #000;
}
.edit-preview-empty {
color: var(--text-tertiary); font-size: 13px;
display: flex; flex-direction: column; align-items: center; gap: 10px;
padding: 24px;
}
.edit-transport {
display: flex; align-items: center; gap: 10px;
padding: 12px 0 14px;
flex-wrap: wrap;
}
.edit-transport-btn {
width: 32px; height: 32px;
display: inline-flex; align-items: center; justify-content: center;
background: oklch(15% 0.020 250);
border: 1px solid var(--border);
border-radius: var(--r-sm);
color: var(--text-primary);
cursor: pointer;
}
.edit-transport-btn:hover { background: oklch(20% 0.030 260); border-color: oklch(45% 0.20 266 / 0.5); }
.edit-transport-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.edit-scrubber {
flex: 1; min-width: 200px;
position: relative;
height: 32px;
display: flex; align-items: center;
}
.edit-scrubber input[type=range] {
width: 100%;
-webkit-appearance: none; appearance: none;
background: transparent;
height: 6px;
}
.edit-scrubber input[type=range]::-webkit-slider-runnable-track {
height: 6px;
background: linear-gradient(90deg,
oklch(25% 0.05 260) 0%,
oklch(25% 0.05 260) var(--in-pct, 0%),
oklch(55% 0.20 266) var(--in-pct, 0%),
oklch(55% 0.20 266) var(--out-pct, 100%),
oklch(25% 0.05 260) var(--out-pct, 100%),
oklch(25% 0.05 260) 100%);
border-radius: 3px;
}
.edit-scrubber input[type=range]::-webkit-slider-thumb {
-webkit-appearance: none; appearance: none;
width: 14px; height: 14px;
background: var(--text-primary);
border-radius: 50%;
border: 2px solid oklch(70% 0.18 266);
margin-top: -4px;
cursor: pointer;
}
.edit-time {
font-family: var(--font-mono); font-size: 12px;
color: var(--text-secondary);
min-width: 110px; text-align: right;
letter-spacing: 0.04em;
}
.edit-marker-btn {
padding: 6px 12px;
background: oklch(15% 0.020 250);
border: 1px solid var(--border);
border-radius: var(--r-sm);
color: var(--text-primary);
font-size: 12px; font-weight: 500;
cursor: pointer;
}
.edit-marker-btn:hover { border-color: oklch(45% 0.20 266 / 0.6); }
.edit-marker-btn.in { color: oklch(70% 0.18 266); }
.edit-marker-btn.out { color: oklch(70% 0.18 266); }
/* Timeline strip */
.edit-timeline {
flex-shrink: 0;
height: 140px;
padding: 12px 24px 18px;
border-top: 1px solid var(--border);
background: oklch(10% 0.015 250 / 0.6);
display: flex; flex-direction: column; gap: 8px;
overflow: hidden;
}
.edit-timeline-head {
display: flex; justify-content: space-between; align-items: baseline;
}
.edit-timeline-title {
font-size: 11px; font-weight: 600;
letter-spacing: 0.16em; text-transform: uppercase;
color: var(--text-tertiary);
}
.edit-timeline-total {
font-family: var(--font-mono); font-size: 11px;
color: var(--text-secondary); letter-spacing: 0.04em;
}
.edit-track {
flex: 1;
display: flex; gap: 4px;
overflow-x: auto;
align-items: stretch;
padding: 4px 0;
}
.edit-track-empty {
flex: 1;
display: flex; align-items: center; justify-content: center;
color: var(--text-tertiary); font-size: 12px;
border: 1px dashed var(--border);
border-radius: var(--r-sm);
transition: background 120ms ease, border-color 120ms ease;
}
.edit-track-empty.drag-over {
background: oklch(20% 0.05 266 / 0.4);
border-color: oklch(55% 0.20 266 / 0.8);
color: var(--accent-strong);
}
.edit-clip {
position: relative;
flex-shrink: 0;
display: flex; flex-direction: column;
width: 140px;
background: oklch(15% 0.025 250);
border: 1px solid oklch(28% 0.04 260 / 0.5);
border-radius: var(--r-sm);
overflow: hidden;
cursor: pointer;
transition: border-color 100ms ease;
}
.edit-clip:hover { border-color: oklch(45% 0.20 266 / 0.5); }
.edit-clip.active {
border-color: oklch(70% 0.18 266);
box-shadow: 0 0 0 1px oklch(70% 0.18 266 / 0.4);
}
.edit-clip-thumb {
width: 100%; height: 64px;
background: #000;
object-fit: cover;
}
.edit-clip-bar {
position: relative;
height: 6px;
background: oklch(20% 0.04 260);
margin: 4px 6px 0;
border-radius: 2px;
overflow: hidden;
}
.edit-clip-bar-fill {
position: absolute;
top: 0; bottom: 0;
background: oklch(55% 0.20 266);
}
.edit-clip-meta {
padding: 4px 8px 6px;
font-size: 10px; font-family: var(--font-mono);
color: var(--text-tertiary);
display: flex; justify-content: space-between;
letter-spacing: 0.02em;
}
.edit-clip-name {
padding: 2px 8px;
font-size: 11px;
color: var(--text-primary);
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.edit-clip-remove {
position: absolute; top: 4px; right: 4px;
width: 18px; height: 18px;
background: rgba(0,0,0,0.6); color: #fff;
border: 0; border-radius: 50%;
font-size: 11px; line-height: 1;
cursor: pointer; display: none;
align-items: center; justify-content: center;
}
.edit-clip:hover .edit-clip-remove { display: flex; }
/* Right: clip inspector */
.edit-inspector {
width: 280px;
flex-shrink: 0;
border-left: 1px solid var(--border);
background: oklch(11% 0.018 250 / 0.7);
padding: 14px;
display: flex; flex-direction: column; gap: 14px;
overflow: auto;
}
.edit-inspector-empty {
color: var(--text-tertiary); font-size: 13px;
text-align: center; padding: 32px 12px;
}
.edit-inspector-section {
display: flex; flex-direction: column; gap: 8px;
}
.edit-inspector-label {
font-size: 10px; font-weight: 600;
letter-spacing: 0.16em; text-transform: uppercase;
color: var(--text-tertiary);
}
.edit-inspector-clipname {
font-size: 14px; font-weight: 500;
color: var(--text-primary);
word-break: break-all;
}
.edit-inspector-stats {
display: grid; grid-template-columns: auto 1fr;
gap: 4px 12px;
font-family: var(--font-mono); font-size: 12px;
}
.edit-inspector-stats dt {
color: var(--text-tertiary); letter-spacing: 0.04em;
text-transform: uppercase; font-size: 10px;
align-self: center;
}
.edit-inspector-stats dd { color: var(--text-primary); margin: 0; }
.edit-inspector-actions {
display: flex; gap: 6px; flex-wrap: wrap;
}
</style>
</head>
<body>
<div class="shell">
<nav class="sidebar" aria-label="Main navigation">
<div class="sidebar-brand">
<img src="img/dragon-logo.png?v=1" alt="Wild Dragon" class="sidebar-logo">
<span class="sidebar-brand-name">Z-AMPP</span>
</div>
<nav class="sidebar-nav">
<a href="home.html" class="nav-item"><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="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="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="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="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="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="edit.html" class="nav-item active"><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>
<a href="jobs.html" class="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>
</nav>
</nav>
<div class="main">
<header class="topbar">
<div class="topbar-left">
<span class="page-title">Editor</span>
<span class="topbar-sep">/</span>
<span class="text-sm" style="color:var(--text-tertiary)">Phase A · single-track</span>
</div>
<div class="topbar-right">
<button class="btn btn-ghost btn-sm" onclick="saveEDL()">Save draft</button>
<button class="btn btn-primary btn-sm" disabled title="Phase C">Export</button>
</div>
</header>
<div class="edit-shell">
<!-- Left: asset library -->
<aside class="edit-assets">
<div class="edit-assets-head">
<span class="edit-assets-title">Library</span>
<div class="edit-assets-search"><input type="text" id="assetSearch" placeholder="Search assets…" /></div>
</div>
<div class="edit-assets-list" id="assetList">
<div class="edit-inspector-empty">Loading…</div>
</div>
</aside>
<!-- Middle: preview + timeline -->
<section class="edit-stage">
<div class="edit-preview-wrap">
<div class="edit-preview" id="previewWrap">
<div class="edit-preview-empty" id="previewEmpty">
<svg viewBox="0 0 32 32" fill="none" stroke="currentColor" stroke-width="1" width="36" height="36" style="opacity:0.4"><polygon points="11,8 24,16 11,24"/></svg>
<div>Drag a clip onto the timeline below, then click it to preview.</div>
</div>
<video id="previewVideo" style="display:none" playsinline></video>
</div>
<div class="edit-transport">
<button class="edit-transport-btn" id="btnPlay" title="Play / pause (space)" disabled>
<svg viewBox="0 0 16 16" fill="currentColor" width="12" height="12"><polygon points="4,3 13,8 4,13"/></svg>
</button>
<div class="edit-scrubber"><input type="range" id="scrubber" min="0" max="100" value="0" step="0.01" disabled></div>
<span class="edit-time" id="timeDisplay">--:--.-- / --:--.--</span>
<button class="edit-marker-btn in" id="btnSetIn" title="Set In (I)" disabled>I IN</button>
<button class="edit-marker-btn out" id="btnSetOut" title="Set Out (O)" disabled>O OUT</button>
</div>
</div>
<div class="edit-timeline">
<div class="edit-timeline-head">
<span class="edit-timeline-title">Timeline · Track 1</span>
<span class="edit-timeline-total" id="timelineTotal">0 clips · 00:00.00</span>
</div>
<div class="edit-track" id="track">
<div class="edit-track-empty" id="trackEmpty">Drag clips here from the Library</div>
</div>
</div>
</section>
<!-- Right: inspector -->
<aside class="edit-inspector" id="inspector">
<div class="edit-inspector-empty">Select a clip on the timeline to inspect.</div>
</aside>
</div>
</div>
</div>
<script src="js/api.js"></script>
<script src="js/topbar-strip.js"></script>
<script src="js/edit.js?v=1"></script>
</body>
</html>