Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tw9fjYBYFyV8YG5YDfYPRb
76 lines
6.3 KiB
JavaScript
76 lines
6.3 KiB
JavaScript
// The picker wheel: press the middle mouse button anywhere and every offered action fans out under the pointer; release on one to run it.
|
|
import { composer as c } from "../../composer/composer.js";
|
|
import { offer } from "../../shell/actions.js";
|
|
import { allReferences, clearReferences } from "../../shell/edits.js";
|
|
|
|
// Saved with the project (a new rental has an empty browser); the browser copy is only the starting point for a project that has no choice yet. enabled null = everything offered except the opt-in actions.
|
|
const KEY = "funui_wheel";
|
|
const clean = (p) => ({ shape: p.shape === "half" ? "half" : "full", side: p.side === "left" ? "left" : "right", enabled: Array.isArray(p.enabled) ? p.enabled : null });
|
|
const fromBrowser = () => { try { return JSON.parse(localStorage.getItem(KEY)) || {}; } catch { return {}; } };
|
|
export function readPrefs(app) {
|
|
const saved = app && app.project && app.project.pref ? app.project.pref("wheel", null) : null;
|
|
return clean(saved && typeof saved === "object" ? saved : fromBrowser());
|
|
}
|
|
function writePrefs(app, p) {
|
|
if (app.project && app.project.setPref) app.project.setPref("wheel", p);
|
|
try { localStorage.setItem(KEY, JSON.stringify(p)); } catch { /* private window: lasts until reload */ }
|
|
}
|
|
|
|
export default {
|
|
id: "wheel",
|
|
mount: "menubar.menus",
|
|
needs: ["project", "dom"],
|
|
setup({ app }) {
|
|
let wheel = null;
|
|
const offs = [
|
|
offer(app, { id: "undo", label: "Undo", icon: "↶", run: () => app.project.undo() }),
|
|
offer(app, { id: "redo", label: "Redo", icon: "↷", run: () => app.project.redo() }),
|
|
offer(app, { id: "models", label: "Models & Pipeline", icon: "▤", when: () => Boolean(app.openSettings), run: () => app.openSettings("models") }),
|
|
offer(app, { id: "engine", label: "Engine settings", icon: "⚙", when: () => Boolean(app.openSettings), run: () => app.openSettings("engine") }),
|
|
];
|
|
const p = () => app.project, pipe = () => app.pipeline;
|
|
const say = (text) => c.toast.info({ text });
|
|
const toggles = [
|
|
offer(app, { id: "all-off", optIn: true, icon: "⊘", get label() { return pipe().allOff() ? "Enable enhancements" : "Disable all enhancements"; },
|
|
when: () => Boolean(pipe() && pipe().allOff && pipe().setAllOff),
|
|
run: async () => { const on = !pipe().allOff(); await pipe().setAllOff(on); say(on ? "All enhancements disabled: runs are plain" : "Enhancements enabled again"); } }),
|
|
offer(app, { id: "guidance-off", optIn: true, icon: "◌", get label() { return pipe().guidanceOff?.() ? "Enable guidance enhancements" : "Disable all guidance enhancements"; },
|
|
when: () => Boolean(pipe() && pipe().guidanceOff && pipe().setGuidanceOff),
|
|
run: async () => { const on = !pipe().guidanceOff(); await pipe().setGuidanceOff(on); say(on ? "Guidance enhancements disabled" : "Guidance enhancements enabled again"); } }),
|
|
offer(app, { id: "camera-off", optIn: true, icon: "◎", get label() { return pipe().cameraOff?.() ? "Enable camera move enhancements" : "Disable camera move enhancements"; },
|
|
when: () => Boolean(pipe() && pipe().cameraOff && pipe().setCameraOff && pipe().modulesById?.().conditioning_shot_camera),
|
|
run: async () => { const on = !pipe().cameraOff(); await pipe().setCameraOff(on); say(on ? "Camera move enhancements disabled" : "Camera move enhancements enabled again"); } }),
|
|
offer(app, { id: "quick-ab", optIn: true, label: "Quick A/B", icon: "⇄",
|
|
when: () => Boolean(app.runner && app.runner.quickAB && pipe() && pipe().setAllOff),
|
|
run: () => app.runner.quickAB() }),
|
|
offer(app, { id: "clear-refs", optIn: true, label: "Clear references", icon: "⌧",
|
|
when: () => Boolean(p() && p().project && allReferences(p().project).size),
|
|
run: async () => { p().edit(clearReferences); say("References cleared for every scene"); } }),
|
|
offer(app, { id: "fix-seed", optIn: true, icon: "⚄", get label() { return p().pref("random_seed", true) === false ? "Fresh seed each run" : "Fix the seed"; },
|
|
when: () => Boolean(p() && p().pref),
|
|
run: () => { const fix = p().pref("random_seed", true) !== false; p().setPref("random_seed", !fix); say(fix ? "Seed fixed: the seed node's own number is used" : "A fresh seed on every Generate"); } }),
|
|
];
|
|
const onDown = (e) => {
|
|
if (e.button !== 1 || wheel || app.dom.dialogOpen()) return; // nothing behind a dialog
|
|
e.preventDefault();
|
|
const prefs = readPrefs(app), on = (a) => prefs.enabled ? prefs.enabled.includes(a.id) : !a.optIn;
|
|
const items = (app.actions || []).filter((a) => on(a) && (!a.when || a.when())).slice(0, 12).map((a) => ({ id: a.id, label: a.label, icon: a.icon }));
|
|
if (!items.length) return;
|
|
const shared = { items, onClose: () => { wheel = null; },
|
|
onPick: (item) => { const a = (app.actions || []).find((x) => x.id === item.id); if (a) Promise.resolve().then(() => a.run()).catch((err) => c.toast.warn({ text: err.message })); } };
|
|
wheel = prefs.shape === "half" ? c.wheel.half({ ...shared, edge: prefs.side, at: e.clientY }) : c.wheel.picker({ ...shared, x: e.clientX, y: e.clientY });
|
|
};
|
|
app.editorSettings.push(() => {
|
|
const prefs = readPrefs(app), set = (patch) => writePrefs(app, { ...readPrefs(app), ...patch });
|
|
const offered = (app.actions || []).map((a) => ({ value: a.id, label: `${a.icon || ""} ${a.label}`.trim() }));
|
|
return c.region.stack({ gap: "sm", children: [c.label.section({ text: "Picker wheel" }),
|
|
c.hint.default({ text: "Hold the middle mouse button anywhere, flick toward an action and let go." }),
|
|
c.segmented.md({ label: "Shape", value: prefs.shape, onChange: (v) => set({ shape: v }), options: [{ value: "full", label: "Full wheel" }, { value: "half", label: "Half wheel" }] }),
|
|
c.segmented.md({ label: "A half wheel opens", value: prefs.side, onChange: (v) => set({ side: v }), options: [{ value: "left", label: "Left" }, { value: "right", label: "Right" }] }),
|
|
c.checklist.default({ label: "On the wheel", items: offered, values: prefs.enabled || (app.actions || []).filter((a) => !a.optIn).map((a) => a.id), onChange: (v) => set({ enabled: v }) })] });
|
|
});
|
|
const offDown = app.dom.onPage("mousedown", onDown, true);
|
|
return () => { offDown(); if (wheel) wheel.close && wheel.close(); offs.concat(toggles).forEach((f) => f()); };
|
|
},
|
|
};
|