A pipeline with no length or rate input hides those controls in the Project and Scene panels. A preset can carry project numbers it goes with. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
58 lines
3.9 KiB
JavaScript
58 lines
3.9 KiB
JavaScript
// Project Setup Wizard: look, a name and a model's starting point, then the tour offer. Opens from File ▸ Project Setup Wizard.
|
|
import { composer as c } from "../../composer/composer.js";
|
|
|
|
export default {
|
|
id: "wizard",
|
|
mount: "menubar.menus",
|
|
needs: ["project", "api", "pipeline", "theme"],
|
|
setup({ app }) {
|
|
let win = null;
|
|
const ctx = { name: "Untitled montage", preset: "", tour: true };
|
|
let presets = [], step = 0;
|
|
|
|
const STEPS = [
|
|
{ title: "Choose your look", sub: "You can change this any time in Settings ▸ Appearance.", body: () => c.segmented.md({ label: "Colour scheme", value: app.theme.get(), onChange: (v) => app.theme.apply(v),
|
|
options: [{ value: "dark", label: "Dark" }, { value: "light", label: "Light" }, { value: "auto", label: "Auto" }] }) },
|
|
{ title: "Name it, and pick a model", sub: "The model decides which nodes and which files this project needs.", body: () => c.region.stack({ gap: "sm", children: [
|
|
c.field.default({ label: "Project name", control: c.input.md({ label: "Project name", value: ctx.name, onInput: (v) => { ctx.name = v; } }) }),
|
|
c.field.default({ label: "Start from", hint: "A model's own pipeline, ready to fill with files. “Keep the current pipeline” leaves Models & Pipeline as it is.",
|
|
control: c.select.md({ label: "Start from", value: ctx.preset, onChange: (v) => { ctx.preset = v; },
|
|
options: [{ value: "", label: "Keep the current pipeline" }, ...presets.map((p) => ({ value: p.id, label: p.title }))] }) })] }) },
|
|
{ title: "Want the guided tour?", sub: "A walk through the screen, one part at a time. It changes nothing; Help ▸ Start tour runs it again.", body: () => c.toggle.default({ label: "Show me around when I finish", checked: ctx.tour, onChange: (v) => { ctx.tour = v; } }) },
|
|
];
|
|
|
|
let busy = false;
|
|
async function finish() {
|
|
if (busy) return;
|
|
busy = true;
|
|
const pick = presets.find((p) => p.id === ctx.preset);
|
|
try {
|
|
await app.project.newProject((ctx.name || "").trim() || "Untitled montage");
|
|
if (pick) { const r = await app.pipeline.restore({ slots: pick.slots, removed: [], unwired: {} }); if (r.refused.length) c.toast.warn({ text: `The model's pipeline was not loaded: ${r.refused[0]}` }); else { Object.entries(pick.prefs || {}).forEach(([k, v]) => app.project.setPref(k, v)); Object.entries(pick.project || {}).forEach(([k, v]) => app.project.setField(k, v)); } }
|
|
} catch (err) { busy = false; return c.toast.warn({ text: `Could not make the project: ${err.message}` }); }
|
|
busy = false;
|
|
close();
|
|
if (ctx.tour) setTimeout(() => app.say("tour.start"), 300);
|
|
}
|
|
const close = () => { if (win) { const w = win; win = null; w.close("done"); } };
|
|
function draw() {
|
|
const s = STEPS[step], last = step === STEPS.length - 1;
|
|
body(s);
|
|
win.setFooter({ actions: [step ? c.button.sm({ label: "Back", tone: "ghost", onClick: () => { step -= 1; draw(); } }) : null,
|
|
c.button.sm({ label: last ? "Create project" : "Continue", tone: "primary", onClick: () => (last ? finish() : (step += 1, draw())) })].filter(Boolean) });
|
|
}
|
|
const slot = c.region.stack({ gap: "md" });
|
|
const body = (s) => (slot.set([c.label.section({ text: `${step + 1} / ${STEPS.length} · ${s.title}` }), c.hint.default({ text: s.sub }), s.body()]), slot);
|
|
async function open() {
|
|
if (win) return;
|
|
try { presets = (await app.api.pipelinePresets()).presets || []; } catch { presets = []; }
|
|
step = 0; ctx.name = "Untitled montage"; ctx.preset = ""; ctx.tour = true;
|
|
win = c.modal.generic({ title: "Project Setup", size: "md", closeOnOutside: false, body: body(STEPS[0]), onClose: () => { win = null; } });
|
|
draw();
|
|
}
|
|
app.has.add("wizard");
|
|
const off = app.on((what) => { if (what === "wizard.open") open(); });
|
|
return () => { off(); app.has.delete("wizard"); };
|
|
},
|
|
};
|