Files
olivv-cs-ComfyUI-FunPack/app/ui/features/wheel/index.js
T

48 lines
3.5 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";
// Per browser, not per project: the wheel is a habit of the hand. enabled null = everything offered.
const KEY = "funpack_wheel";
export function readPrefs() {
try { const p = JSON.parse(localStorage.getItem(KEY)) || {}; return { shape: p.shape === "half" ? "half" : "full", side: p.side === "left" ? "left" : "right", enabled: Array.isArray(p.enabled) ? p.enabled : null }; }
catch { return { shape: "full", side: "right", enabled: null }; }
}
const writePrefs = (p) => { try { localStorage.setItem(KEY, JSON.stringify(p)); } catch { /* private window: lasts until reload */ } };
export default {
id: "wheel",
mount: "menubar.menus",
needs: ["project"],
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 onDown = (e) => {
if (e.button !== 1 || wheel || document.querySelector('[role="dialog"]')) return; // nothing behind a dialog
e.preventDefault();
const prefs = readPrefs(), on = (a) => !prefs.enabled || prefs.enabled.includes(a.id);
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(), set = (patch) => writePrefs({ ...readPrefs(), ...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 || offered.map((o) => o.value), onChange: (v) => set({ enabled: v }) })] });
});
document.addEventListener("mousedown", onDown, true);
return () => { document.removeEventListener("mousedown", onDown, true); if (wheel) wheel.close && wheel.close(); offs.forEach((f) => f()); };
},
};