feat(premiere-panel): icon-rail redesign with hover tooltips
Replace the text-heavy panel layout with a minimal icon-first UI: a VS Code-style vertical activity rail for view switching plus a contextual icon dock for clip actions. Every control carries a [data-tip] label surfaced on hover via a JS-positioned tooltip bubble (UXP's CSS engine can't be trusted with content: attr()). All existing main.js element IDs and the JS wiring contract are preserved; the dropped advanced section was already guarded. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
1fcb927d26
commit
6a161c7133
3 changed files with 611 additions and 558 deletions
|
|
@ -9,7 +9,7 @@
|
|||
<div id="root">
|
||||
|
||||
<!-- ── Connect Pane ─────────────────────────────────────────────── -->
|
||||
<section id="connect-pane" class="pane">
|
||||
<section id="connect-pane" class="pane pane-connect">
|
||||
<div class="brand">
|
||||
<div class="brand-icon">
|
||||
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
||||
|
|
@ -28,110 +28,121 @@
|
|||
<div id="connect-status" class="status-msg muted"></div>
|
||||
</section>
|
||||
|
||||
<!-- ── Library Pane ─────────────────────────────────────────────── -->
|
||||
<!-- ── Library Pane (app-shell: statusbar · rail · main · dock) ──── -->
|
||||
<section id="library-pane" class="pane hidden">
|
||||
<div class="app">
|
||||
|
||||
<!-- Connection status strip (v2.1.8): dot + identity + ⋯ menu.
|
||||
Disconnect lives inside the menu so it's not always visible. -->
|
||||
<header class="status-strip">
|
||||
<span class="signal-dot"></span>
|
||||
<span id="connected-host" class="connected-host"></span>
|
||||
<span id="panel-version" class="panel-version" title="Plugin version"></span>
|
||||
<button id="menu-btn" class="btn-ghost" title="More" aria-label="More">⋯</button>
|
||||
<div id="status-menu" class="menu hidden" role="menu">
|
||||
<button id="disconnect-btn" class="menu-item" role="menuitem">Disconnect</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Tabs -->
|
||||
<div class="tab-nav">
|
||||
<button id="tab-library" class="tab-btn active">
|
||||
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/>
|
||||
</svg>
|
||||
Library
|
||||
</button>
|
||||
<button id="tab-growing" class="tab-btn">
|
||||
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M23 7l-7 5 7 5V7z"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/>
|
||||
</svg>
|
||||
Growing
|
||||
<span id="growing-count" class="badge" style="display:none">0</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Search + Project filter -->
|
||||
<div class="search-row">
|
||||
<input id="search-input" type="search" placeholder="Search assets…" />
|
||||
<select id="project-filter" title="Filter by project">
|
||||
<option value="all">All Projects</option>
|
||||
</select>
|
||||
<button id="refresh-btn" class="btn btn-icon" title="Refresh">↻</button>
|
||||
</div>
|
||||
|
||||
<!-- Active sequence info bar -->
|
||||
<div id="seq-info-bar" class="seq-info-bar hidden">
|
||||
<span class="chip chip-ok">SEQ</span>
|
||||
<span id="seq-info-name" class="seq-info-name"></span>
|
||||
</div>
|
||||
|
||||
<!-- Asset grid (library tab) -->
|
||||
<div id="library-container" class="grid-container">
|
||||
<div id="asset-grid" class="asset-grid">
|
||||
<div class="empty muted">Loading…</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Growing grid -->
|
||||
<div id="growing-container" class="grid-container hidden">
|
||||
<div id="growing-grid" class="asset-grid">
|
||||
<div class="empty muted">No growing files</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- (v2.2.0) Asset details panel dropped — card meta already carries
|
||||
name / codec / duration. If we need richer detail later, surface
|
||||
it on the card hover state rather than reserving permanent space. -->
|
||||
<div id="details-panel" class="hidden"></div>
|
||||
|
||||
<!-- Action buttons -->
|
||||
<footer class="actions">
|
||||
<div class="action-row">
|
||||
<button id="import-proxy-btn" class="btn btn-primary" disabled>Import Proxy</button>
|
||||
<button id="import-hires-btn" class="btn btn-secondary" disabled>Hi-Res</button>
|
||||
</div>
|
||||
<div class="action-row">
|
||||
<button id="mount-live-btn" class="btn btn-secondary" disabled title="Open live file from SMB share">Mount Live</button>
|
||||
<button id="relink-btn" class="btn btn-secondary" disabled title="Relink proxy → hi-res original">Relink Hi-Res</button>
|
||||
</div>
|
||||
<div class="action-row">
|
||||
<button id="import-all-btn" class="btn btn-secondary" disabled>Import All</button>
|
||||
<button id="export-timeline-btn" class="btn btn-secondary" disabled>Export Timeline ↑</button>
|
||||
</div>
|
||||
|
||||
<!-- Progress -->
|
||||
<div id="progress-row" class="progress-row hidden">
|
||||
<div class="progress-bar"><div id="progress-fill"></div></div>
|
||||
<div id="progress-label" class="progress-label">…</div>
|
||||
</div>
|
||||
|
||||
<!-- Toast -->
|
||||
<div id="toast" class="toast hidden"></div>
|
||||
</footer>
|
||||
|
||||
<!-- Advanced section — collapsed by default; click the row to expand. -->
|
||||
<div class="advanced-section">
|
||||
<button id="advanced-toggle" class="advanced-toggle" type="button" aria-expanded="false">
|
||||
<span class="advanced-caret">▸</span>
|
||||
<span class="advanced-title">Advanced</span>
|
||||
</button>
|
||||
<div id="advanced-body" class="advanced-body hidden">
|
||||
<div class="action-row">
|
||||
<button id="export-conform-btn" class="btn btn-secondary" disabled>Export & Conform</button>
|
||||
<button id="fetch-relink-btn" class="btn btn-secondary" disabled>Fetch & Relink All</button>
|
||||
<!-- Connection collapsed to a status line: dot + host + version + ⋯ -->
|
||||
<header class="statusbar">
|
||||
<span class="signal-dot"></span>
|
||||
<span id="connected-host" class="connected-host"></span>
|
||||
<span id="panel-version" class="panel-version" title="Plugin version"></span>
|
||||
<button id="menu-btn" class="iconbtn iconbtn--sm" data-tip="More" data-tip-pos="down-left" aria-label="More">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="5" cy="12" r="1.4" fill="currentColor" stroke="none"/><circle cx="12" cy="12" r="1.4" fill="currentColor" stroke="none"/><circle cx="19" cy="12" r="1.4" fill="currentColor" stroke="none"/></svg>
|
||||
</button>
|
||||
<div id="status-menu" class="menu hidden" role="menu">
|
||||
<button id="disconnect-btn" class="menu-item" role="menuitem">Disconnect</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="workspace">
|
||||
|
||||
<!-- Vertical icon rail: views on top, global actions below -->
|
||||
<nav class="rail">
|
||||
<button id="tab-library" class="rail-btn active" data-tip="Library" data-tip-pos="right">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg>
|
||||
</button>
|
||||
<button id="tab-growing" class="rail-btn" data-tip="Growing" data-tip-pos="right">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M23 7l-7 5 7 5V7z"/><rect x="1" y="5" width="15" height="14" rx="2"/></svg>
|
||||
<span id="growing-count" class="rail-count" style="display:none">0</span>
|
||||
</button>
|
||||
|
||||
<span class="rail-spacer"></span>
|
||||
|
||||
<button id="export-timeline-btn" class="rail-btn rail-btn--accent" data-tip="Export Timeline" data-tip-pos="right">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9"><path d="M12 19V6"/><path d="m5 12 7-7 7 7"/><path d="M5 21h14"/></svg>
|
||||
</button>
|
||||
<button id="refresh-btn" class="rail-btn" data-tip="Refresh" data-tip-pos="right">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M21 12a9 9 0 1 1-2.64-6.36"/><path d="M21 3v5h-5"/></svg>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<!-- Main column -->
|
||||
<div class="main">
|
||||
|
||||
<!-- Search + project filter -->
|
||||
<div class="toolbar">
|
||||
<label class="search">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></svg>
|
||||
<input id="search-input" type="search" placeholder="Search assets" />
|
||||
</label>
|
||||
<select id="project-filter" class="filter-select" title="Filter by project">
|
||||
<option value="all">All Projects</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Active sequence info bar -->
|
||||
<div id="seq-info-bar" class="seq-info-bar hidden">
|
||||
<span class="chip chip-ok">SEQ</span>
|
||||
<span id="seq-info-name" class="seq-info-name"></span>
|
||||
</div>
|
||||
|
||||
<!-- Asset grid (library tab) -->
|
||||
<div id="library-container" class="grid-container">
|
||||
<div id="asset-grid" class="asset-grid">
|
||||
<div class="empty muted">Loading…</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Growing grid -->
|
||||
<div id="growing-container" class="grid-container hidden">
|
||||
<div id="growing-grid" class="asset-grid">
|
||||
<div class="empty muted">No growing files</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Details panel retired (v2.2.0) — card meta carries name/codec/duration. -->
|
||||
<div id="details-panel" class="hidden"></div>
|
||||
|
||||
<!-- Progress + toast sit just above the action dock -->
|
||||
<div id="progress-row" class="progress-row hidden">
|
||||
<div class="progress-bar"><div id="progress-fill"></div></div>
|
||||
<div id="progress-label" class="progress-label">…</div>
|
||||
</div>
|
||||
<div id="toast" class="toast hidden"></div>
|
||||
|
||||
<!-- Contextual action dock: text buttons replaced by icon buttons
|
||||
with hover labels. Per-asset actions left, batch actions right. -->
|
||||
<footer class="dock">
|
||||
<button id="import-proxy-btn" class="iconbtn iconbtn--primary" data-tip="Import Proxy" data-tip-pos="up" disabled>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9"><path d="M12 3v13"/><path d="m7 11 5 5 5-5"/><path d="M5 21h14"/></svg>
|
||||
</button>
|
||||
<button id="import-hires-btn" class="iconbtn" data-tip="Import Hi-Res" data-tip-pos="up" disabled>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><path d="M12 2 2 7l10 5 10-5z"/><path d="m2 17 10 5 10-5"/><path d="m2 12 10 5 10-5"/></svg>
|
||||
</button>
|
||||
<button id="mount-live-btn" class="iconbtn" data-tip="Mount Live" data-tip-pos="up" disabled>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="12" r="2.5"/><path d="M6.3 6.3a8 8 0 0 0 0 11.4M17.7 6.3a8 8 0 0 1 0 11.4"/></svg>
|
||||
</button>
|
||||
<button id="relink-btn" class="iconbtn" data-tip="Relink Hi-Res" data-tip-pos="up" disabled>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M10 13a5 5 0 0 0 7 0l2-2a5 5 0 0 0-7-7l-1 1"/><path d="M14 11a5 5 0 0 0-7 0l-2 2a5 5 0 0 0 7 7l1-1"/></svg>
|
||||
</button>
|
||||
|
||||
<span class="dock-sep"></span>
|
||||
|
||||
<button id="import-all-btn" class="iconbtn" data-tip="Import All" data-tip-pos="up-left" disabled>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><path d="m3.27 6.96 8.73 5.05 8.73-5.05"/><path d="M12 22.08V12"/></svg>
|
||||
</button>
|
||||
<button id="export-conform-btn" class="iconbtn" data-tip="Export & Conform" data-tip-pos="up-left" disabled>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><line x1="4" y1="21" x2="4" y2="14"/><line x1="4" y1="10" x2="4" y2="3"/><line x1="12" y1="21" x2="12" y2="12"/><line x1="12" y1="8" x2="12" y2="3"/><line x1="20" y1="21" x2="20" y2="16"/><line x1="20" y1="12" x2="20" y2="3"/><line x1="1" y1="14" x2="7" y2="14"/><line x1="9" y1="8" x2="15" y2="8"/><line x1="17" y1="16" x2="23" y2="16"/></svg>
|
||||
</button>
|
||||
<button id="fetch-relink-btn" class="iconbtn" data-tip="Fetch & Relink All" data-tip-pos="up-left" disabled>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M21 2v6h-6"/><path d="M3 12a9 9 0 0 1 15-6.7L21 8"/><path d="M3 22v-6h6"/><path d="M21 12a9 9 0 0 1-15 6.7L3 16"/></svg>
|
||||
</button>
|
||||
</footer>
|
||||
|
||||
</div><!-- /main -->
|
||||
</div><!-- /workspace -->
|
||||
</div><!-- /app -->
|
||||
</section>
|
||||
|
||||
<!-- ── Export Timeline Slide Panel ──────────────────────────────── -->
|
||||
|
|
@ -231,6 +242,7 @@
|
|||
<script src="src/library.js"></script>
|
||||
<script src="src/import-flow.js"></script>
|
||||
<script src="src/timeline.js"></script>
|
||||
<script src="src/tooltip.js"></script>
|
||||
<script src="src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
73
services/premiere-plugin-uxp/src/tooltip.js
Normal file
73
services/premiere-plugin-uxp/src/tooltip.js
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
// Hover tooltips — v1
|
||||
// Icon-first UI: every actionable control carries a [data-tip] label that
|
||||
// surfaces on hover. UXP's CSS engine can't be trusted with
|
||||
// `content: attr(data-tip)` on ::after, so we position a single floating
|
||||
// bubble with plain DOM + getBoundingClientRect (both well supported).
|
||||
|
||||
(function () {
|
||||
let bubble = null;
|
||||
let timer = null;
|
||||
|
||||
function ensure() {
|
||||
if (bubble) return bubble;
|
||||
bubble = document.createElement('div');
|
||||
bubble.className = 'tip-bubble';
|
||||
document.body.appendChild(bubble);
|
||||
return bubble;
|
||||
}
|
||||
|
||||
function show(el) {
|
||||
const text = el.getAttribute('data-tip');
|
||||
if (!text) return;
|
||||
const tip = ensure();
|
||||
tip.textContent = text;
|
||||
tip.style.display = 'block';
|
||||
tip.style.opacity = '0';
|
||||
|
||||
const r = el.getBoundingClientRect();
|
||||
const t = tip.getBoundingClientRect();
|
||||
const gap = 7;
|
||||
const pos = el.getAttribute('data-tip-pos') || 'down';
|
||||
let x, y;
|
||||
|
||||
if (pos === 'right') {
|
||||
x = r.right + gap; y = r.top + (r.height - t.height) / 2;
|
||||
} else if (pos === 'up') {
|
||||
x = r.left + (r.width - t.width) / 2; y = r.top - t.height - gap;
|
||||
} else if (pos === 'up-left') {
|
||||
x = r.right - t.width; y = r.top - t.height - gap;
|
||||
} else if (pos === 'down-left') {
|
||||
x = r.right - t.width; y = r.bottom + gap;
|
||||
} else {
|
||||
x = r.left + (r.width - t.width) / 2; y = r.bottom + gap;
|
||||
}
|
||||
|
||||
const vw = window.innerWidth, vh = window.innerHeight;
|
||||
x = Math.max(4, Math.min(x, vw - t.width - 4));
|
||||
y = Math.max(4, Math.min(y, vh - t.height - 4));
|
||||
tip.style.left = x + 'px';
|
||||
tip.style.top = y + 'px';
|
||||
tip.style.opacity = '1';
|
||||
}
|
||||
|
||||
function hide() {
|
||||
clearTimeout(timer);
|
||||
if (bubble) { bubble.style.opacity = '0'; bubble.style.display = 'none'; }
|
||||
}
|
||||
|
||||
function bind(el) {
|
||||
el.addEventListener('mouseenter', () => {
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(() => show(el), 240);
|
||||
});
|
||||
el.addEventListener('mouseleave', hide);
|
||||
el.addEventListener('click', hide);
|
||||
}
|
||||
|
||||
function init() {
|
||||
document.querySelectorAll('[data-tip]').forEach(bind);
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
|
||||
else init();
|
||||
})();
|
||||
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue