Two pieces of the "port v4's exact UI onto v5's backend" plan: - core/contract.py: new optional ModuleSpec.category field (closed set: continuity, guidance, conditioning, sampling, post, system), validated in core/schema.py, threaded through core/registry.py's FIELDS and to_manifest(). Presentation grouping, independent of STAGE (execution order) -- a module can be stage="sampling" and category="guidance" at once. Only the 4 existing modules that actually render a settings panel (alg, sharpen, appearance, full_control) got a category assigned; the other 12 have none to categorize (no SETTINGS block) and stay uncategorised, which is valid. - v4's frontend (movie_editor/frontend/*, ~29k lines, 61 files) copied verbatim into app/legacy/. core/routes.py's "/" route now serves legacy/index.html instead of the composer entry point; a <base> tag in that file resolves its own relative script/link tags against /funpack/app/legacy/ regardless of what URL it's served at, so every file moved as a unit with zero per-file path edits. Verified live: all 61 assets load 200 OK, the real v4 onboarding screen renders pixel-for- pixel (it's the actual DOM/CSS, not a reimplementation), and several v4 API calls (/api/media, /api/projects, /api/git/status) already succeed against v5's real backend by coincidence of shared route naming -- the rest 404/405 as expected, since api.js's ~90 functions haven't been rewired to v5's actual routes yet (next piece of this plan). The old composer entry point (app/index.html, app/composer/**) stays in the tree, unreferenced, until a later cleanup pass once nothing imports it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
33 lines
1.4 KiB
JavaScript
33 lines
1.4 KiB
JavaScript
// Entry: wire splitters, boot the store (view modules self-mount on load).
|
|
(function () {
|
|
function makeSplitter(splitterId, targetVar, axis, min, max, invert) {
|
|
const sp = document.getElementById(splitterId);
|
|
if (!sp) return;
|
|
let dragging = false;
|
|
const root = document.documentElement;
|
|
sp.addEventListener("pointerdown", (e) => {
|
|
dragging = true; sp.setPointerCapture(e.pointerId);
|
|
document.body.classList.add("dragging");
|
|
});
|
|
sp.addEventListener("pointermove", (e) => {
|
|
if (!dragging) return;
|
|
let v = axis === "x"
|
|
? (invert ? window.innerWidth - e.clientX : e.clientX)
|
|
: (invert ? window.innerHeight - e.clientY : e.clientY);
|
|
v = Math.max(min, Math.min(max(), v));
|
|
root.style.setProperty(targetVar, v + "px");
|
|
});
|
|
sp.addEventListener("pointerup", (e) => {
|
|
dragging = false; try { sp.releasePointerCapture(e.pointerId); } catch (_) {}
|
|
document.body.classList.remove("dragging");
|
|
});
|
|
}
|
|
|
|
window.addEventListener("DOMContentLoaded", () => {
|
|
// Assets (left) | Preview (center, flex) | Properties (right) — over Timeline (bottom).
|
|
makeSplitter("split-left", "--media-w", "x", 180, () => window.innerWidth * 0.45, false);
|
|
makeSplitter("split-right", "--properties-w", "x", 220, () => window.innerWidth * 0.5, true);
|
|
makeSplitter("split-bottom", "--timeline-h", "y", 200, () => window.innerHeight * 0.7, true);
|
|
window.Store.init();
|
|
});
|
|
})(); |