feat(editor): show fps/codec/resolution/duration in media panel asset list

- Add two-line layout to media panel items: name on top, metadata below
- fmtMs() converts duration_ms to MM:SS or HH:MM:SS for display
- Meta line shows resolution · codec · fps · duration, skipping null fields
- Assets with no extracted metadata (no proxy yet) show name only
- Active item meta line inherits accent color at reduced opacity
This commit is contained in:
Zac Gaetano 2026-05-18 23:37:56 -04:00
parent 508cf8d41b
commit 660afb94bb

View file

@ -120,19 +120,43 @@
.media-asset-item {
display: flex;
align-items: center;
align-items: flex-start;
gap: var(--sp-2);
padding: var(--sp-2) var(--sp-3);
font-size: var(--text-xs);
color: var(--text-secondary);
cursor: pointer;
transition: background var(--t-fast), color var(--t-fast);
white-space: nowrap;
overflow: hidden;
}
.media-asset-item svg { flex-shrink: 0; margin-top: 2px; }
.media-asset-item:hover { background: var(--bg-hover); color: var(--text-primary); }
.media-asset-item.active { background: var(--accent-subtle); color: var(--accent); }
.media-asset-item span { overflow: hidden; text-overflow: ellipsis; }
.media-asset-info {
display: flex;
flex-direction: column;
min-width: 0;
flex: 1;
gap: 2px;
}
.media-asset-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.media-asset-meta {
font-size: 10px;
color: var(--text-tertiary);
font-family: var(--font-mono);
letter-spacing: 0.02em;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.media-asset-item.active .media-asset-meta { color: var(--accent); opacity: 0.7; }
/* ── Timeline panel ────────────────────────────────────────── */
.timeline-panel {
@ -481,18 +505,43 @@ async function loadMediaAssets() {
renderMediaList();
}
function fmtMs(ms) {
if (!ms || ms <= 0) return null;
const s = Math.floor(ms / 1000);
const h = Math.floor(s / 3600);
const m = Math.floor((s % 3600) / 60);
const sc = s % 60;
if (h > 0) return [h, m, sc].map(v => String(v).padStart(2,'0')).join(':');
return [m, sc].map(v => String(v).padStart(2,'0')).join(':');
}
function renderMediaList() {
const list = document.getElementById('mediaAssetList');
list.innerHTML = '';
state.assets.forEach(function(asset) {
const el = document.createElement('div');
el.className = 'media-asset-item';
const metaParts = [
asset.resolution || null,
asset.codec || null,
asset.fps ? asset.fps + ' fps' : null,
fmtMs(asset.duration_ms),
].filter(Boolean);
const metaStr = metaParts.join(' · ');
el.innerHTML =
'<svg viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.3" width="12" height="12">' +
'<rect x="1" y="3" width="8" height="8" rx="1"/>' +
'<path d="M9 6l4-2v6l-4-2"/>' +
'</svg>' +
'<span title="' + esc(asset.display_name || asset.filename) + '">' + esc(asset.display_name || asset.filename) + '</span>';
'<div class="media-asset-info">' +
'<span class="media-asset-name" title="' + esc(asset.display_name || asset.filename) + '">' +
esc(asset.display_name || asset.filename) +
'</span>' +
(metaStr ? '<span class="media-asset-meta">' + esc(metaStr) + '</span>' : '') +
'</div>';
el.ondblclick = function() { loadSourceAsset(asset); };
el.onclick = function() {
list.querySelectorAll('.media-asset-item').forEach(function(e) { e.classList.remove('active'); });