From 4f18eefa69fe5fcfb14ebc2979c315d37cdc334f Mon Sep 17 00:00:00 2001
From: digital-garbage <133010426+digital-garbage@users.noreply.github.com>
Date: Wed, 16 Sep 2026 06:25:12 +0300
Subject: [PATCH] feat: picker wheel -- customizable radial menu, replaces v4's
pinned buttons
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
[[project_v5_ui_requirements]]: "any action in the UI can be assigned to a
slot... full wheel or half wheel... half wheel positioned left or right...
both draggable... default: middle mouse button, anywhere."
app/legacy/actions.js: ported the composer tree's tiny action registry
(app/shell/actions.js, an ES module, unreferenced from boot) as a classic
global window.FunPackActions -- offerAction({id,label,icon,run}), offered(),
run(id). Whatever announces itself is what the wheel (or anything later --
a command palette, a keymap) can show.
app/legacy/wheel.js: middle-mouse (anywhere) opens a radial menu of
whatever's currently on it. Full wheel spaces items evenly around 360°;
half wheel spaces them over a 180° arc on the chosen side, with drag
locked to the vertical axis only (full wheel drags both axes) per the
spec's "half wheel moves up/down, full wheel up/down and left/right" line.
Dragging repositions the CURRENTLY OPEN wheel only, not persisted -- read
"draggable" as "don't get stuck somewhere awkward this time" rather than a
remembered dock position, since the spec doesn't say which and this is the
simpler reading. Customize dialog (reuses pinned_buttons.js's existing
.pin-modal/.pin-slot CSS) toggles shape, side, and which offered actions
are on the wheel. pinned_buttons.js is NOT removed -- the spec says this
supersedes it, but retiring a working control felt like a separate
decision from adding a new one; both coexist for now, confirmed to not
conflict (separate storage keys, no shared DOM, never both open at once).
app/legacy/wheel_actions_seed.js: seeds 9 starter actions (Generate,
Generate selected, Render, Auto Montage, Undo, Redo, Models & Pipeline,
Engine settings, Sampler quick-access) so the wheel isn't empty on first
use.
Two rounds of extensive_testing, plus a bug I found myself in live
testing before either round: the half wheel's fixed radius, sized for a
full wheel's 360° spread, packed the same item count into half the
angular room and produced heavily overlapping items -- fixed by growing
the radius only as far as needed to keep adjacent items from touching,
confirmed visually before/after.
Round 1: no functional break found in outside-click-vs-item-click
ordering, listener leaks on repeated opens, or coexistence with
pinned_buttons.js.
Round 2: found the real defect -- enabledIds()'s first-call snapshot
("default to everything offered so far") could run in the gap between
wheel.js executing and wheel_actions_seed.js finishing (classic
+