Move Generate/Render to timeline header; rename menu to Settings
Renames the top Generate menu to Settings (engine, models, pipeline slots). Removes the standalone Models menu. Adds Generate, Selected, and Render buttons to the timeline zone header.
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
// Main transport actions: Generate / Generate selection / Render (timeline header).
|
||||
(function () {
|
||||
const { el, clear } = window.dom;
|
||||
const S = window.Store;
|
||||
const mount = document.getElementById("transport-actions");
|
||||
if (!mount) return;
|
||||
|
||||
function selCount() { return S.selectedSceneCount ? S.selectedSceneCount() : (S.get().selectedSceneId ? 1 : 0); }
|
||||
function hasProject() { return !!S.get().project; }
|
||||
function busy(st) { return ["queuing", "running", "pending"].includes(st.gen?.state); }
|
||||
|
||||
function render(st) {
|
||||
clear(mount);
|
||||
const genAll = el("button", "btn primary compact", "▶ Generate");
|
||||
genAll.title = "Generate the whole montage";
|
||||
genAll.disabled = !hasProject() || busy(st);
|
||||
genAll.onclick = () => S.generate(null);
|
||||
mount.append(genAll);
|
||||
|
||||
const n = selCount();
|
||||
const selLabel = n > 1 ? `Selected (${n})` : "Selected";
|
||||
const genSel = el("button", "btn ghost compact", selLabel);
|
||||
genSel.title = n > 1
|
||||
? `Generate ${n} selected scenes (one chain run per segment)`
|
||||
: "Generate the selected scene";
|
||||
genSel.disabled = n === 0 || busy(st);
|
||||
genSel.onclick = () => S.generateSelected();
|
||||
mount.append(genSel);
|
||||
|
||||
const renderBtn = el("button", "btn render compact", "⧉ Render");
|
||||
renderBtn.title = "Stitch generated clips into a final video";
|
||||
renderBtn.disabled = !hasProject() || busy(st);
|
||||
renderBtn.onclick = () => S.renderFinal();
|
||||
mount.append(renderBtn);
|
||||
}
|
||||
|
||||
S.subscribe(render);
|
||||
render(S.get());
|
||||
})();
|
||||
@@ -87,6 +87,7 @@
|
||||
<section class="zone timeline" id="timeline-zone">
|
||||
<div class="zone-head">
|
||||
<span class="zone-title">Timeline</span>
|
||||
<div class="transport-actions" id="transport-actions" aria-label="Generate and render"></div>
|
||||
<div class="zone-head-right">
|
||||
<span id="timeline-meta"></span>
|
||||
<nav class="dock-tabs" id="dock-tabs" aria-label="Workspace panels"></nav>
|
||||
@@ -104,6 +105,7 @@
|
||||
<script src="store.js"></script>
|
||||
<script src="models.js"></script>
|
||||
<script src="menubar.js"></script>
|
||||
<script src="actionbar.js"></script>
|
||||
<script src="layout.js"></script>
|
||||
<script src="mediabrowser.js"></script>
|
||||
<script src="log.js"></script>
|
||||
|
||||
@@ -145,7 +145,7 @@
|
||||
const selN = S.selectedSceneCount ? S.selectedSceneCount() : 1;
|
||||
if (selN > 1) {
|
||||
const mh = el("div", "insp-hint");
|
||||
mh.textContent = `${selN} clips selected — Generate → Generate Selected Scenes runs each chain segment that includes a selected clip (⌘/Ctrl-click toggle · Shift-click range).`;
|
||||
mh.textContent = `${selN} clips selected — use Selected in the timeline header to generate each chain segment that includes a selected clip (⌘/Ctrl-click toggle · Shift-click range).`;
|
||||
body.append(mh);
|
||||
}
|
||||
if (unitScenes.length > 1) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Top menu bar: File / Edit / View / Generate / FunPack + status chips.
|
||||
// Top menu bar: File / Edit / View / Settings / FunPack + status chips.
|
||||
(function () {
|
||||
const { el, clear } = window.dom;
|
||||
const S = window.Store;
|
||||
@@ -7,7 +7,6 @@
|
||||
let openName = null;
|
||||
|
||||
function sel() { return S.get().selectedSceneId; }
|
||||
function selCount() { return S.selectedSceneCount ? S.selectedSceneCount() : (sel() ? 1 : 0); }
|
||||
function hasProject() { return !!S.get().project; }
|
||||
|
||||
// hidden file input for importing a project file
|
||||
@@ -72,25 +71,17 @@
|
||||
{ sep: true },
|
||||
{ label: "Reset Layout", action: () => { const r = document.documentElement; r.style.removeProperty("--media-w"); r.style.removeProperty("--timeline-h"); } },
|
||||
],
|
||||
Generate: [
|
||||
Settings: [
|
||||
{ label: "Engine settings…", disabled: !hasProject(), action: () => window.EngineSettingsModal.open() },
|
||||
{ sep: true },
|
||||
{ label: "Generate Whole Montage", hint: "▶", disabled: !hasProject(), action: () => S.generate(null) },
|
||||
{ label: selCount() > 1 ? `Generate Selected Scenes (${selCount()})` : "Generate Selected Scene",
|
||||
hint: selCount() > 1 ? "⌘-click" : undefined,
|
||||
disabled: selCount() === 0, action: () => S.generateSelected() },
|
||||
{ sep: true },
|
||||
{ label: "Render Final Video", hint: "⧉", disabled: !hasProject(), action: () => S.renderFinal() },
|
||||
{ label: "Models…", action: () => window.ModelsModal.open() },
|
||||
{ label: "Refresh model list", hint: "R", action: async () => { try { await window.MovieEditorAPI.refreshModels(); } catch (_) {} } },
|
||||
{ sep: true },
|
||||
{ label: `Conditioning: ${_roleLabel(st.project?.conditioning_slot, "FunPack Studio")}`, disabled: !hasProject(),
|
||||
action: () => _pickRole("conditioning_slot", "Conditioning node", st) },
|
||||
{ label: `Sampler: ${_roleLabel(st.project?.sampler_slot, "FunPack Chain Sampler")}`, disabled: !hasProject(),
|
||||
action: () => _pickRole("sampler_slot", "Sampler node", st) },
|
||||
],
|
||||
Models: [
|
||||
{ label: "Settings…", action: () => window.ModelsModal.open() },
|
||||
{ label: "Refresh model list", hint: "R", action: async () => { try { await window.MovieEditorAPI.refreshModels(); } catch (_) {} } },
|
||||
],
|
||||
FunPack: [
|
||||
{ label: st.resetSessionArmed ? "Reset Studio session ✓ armed — click to cancel" : "Reset Studio session",
|
||||
disabled: !hasProject(), action: () => S.resetStudioSession() },
|
||||
|
||||
@@ -513,7 +513,7 @@
|
||||
const empty = el("div", "program-empty");
|
||||
empty.append(el("div", "reel", "🎬"));
|
||||
empty.append(el("div", null, p ? "No render yet" : "Open or create a project"));
|
||||
empty.append(el("div", "pj-meta", "Generate from the menu or timeline"));
|
||||
empty.append(el("div", "pj-meta", "Use Generate in the timeline header"));
|
||||
canvas.append(empty);
|
||||
}
|
||||
// generation progress overlay (visible even when there's already media). Built once per
|
||||
|
||||
@@ -179,6 +179,14 @@ button, input, select, textarea { font-family: inherit; color: var(--text); }
|
||||
font-family: var(--font-mono); font-size: 11px; letter-spacing: 2px; text-transform: uppercase; color: var(--muted);
|
||||
}
|
||||
.zone-title::before { content: ""; display: inline-block; width: 6px; height: 6px; margin-right: 9px; border-radius: 1px; background: var(--amber); transform: translateY(-1px); box-shadow: 0 0 7px var(--amber-soft); }
|
||||
.transport-actions { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }
|
||||
.transport-actions .btn.compact { padding: 3px 10px; font-size: 12px; font-family: var(--font-mono); line-height: 1.35; }
|
||||
.transport-actions .btn:disabled { opacity: 0.42; pointer-events: none; cursor: default; }
|
||||
.transport-actions .btn.render {
|
||||
background: linear-gradient(#3ab3a8, #2d9a90); color: #0a1a18; border-color: #3ab3a8;
|
||||
font-weight: 600; box-shadow: 0 4px 14px -6px color-mix(in srgb, var(--teal) 55%, transparent);
|
||||
}
|
||||
.transport-actions .btn.render:hover { filter: brightness(1.08); }
|
||||
.zone-head-right { margin-left: auto; font-family: var(--font-mono); font-size: 11px; color: var(--faint); display: flex; gap: 10px; align-items: center; flex-wrap: wrap; justify-content: flex-end; }
|
||||
.zone-body { flex: 1; overflow: auto; padding: 12px; min-height: 0; }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user