27 Commits
Author SHA1 Message Date
Claude 19c6525d19 Old scene Source values open as the default; Generate refused while an upscale model runs; preset decodes with None without the Sol-H3 VAE; tour callout follows a redrawn button
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tw9fjX7o2YfeGQE18MEtPu
2026-11-08 21:05:34 +00:00
digital-garbageandClaude Sonnet 5.5 aa31b95ea5 Timeline header on one line; kit literals into tokens; Composer fits; running-state viewport test; remove Auto Montage
- Header: view toggles (Assets/Preview/Properties/Auto-hide) move to the top bar; progress, the prompt warning and scene actions (rate, upscale, interpolate) move to the row under the toolbar; the timeline head never wraps from 1000px up and never clips its controls. The top bar grows a line instead of spilling when it wraps.
- Kit: every literal pixel size in element CSS is a token (the ratchet test is now zero); Composer's text box follows the viewport; the catalogue shows the component sizes.
- test:viewport adds a fake ComfyUI queue and socket for the render-running state and asserts the header is one row.
- Auto Montage removed (button, window, code, README/docs, tests). Saved projects holding pieces it made still load: the reused flag is kept.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-06 14:28:45 +02:00
Claude 06692cf79c Media bin: pin a picture as the aspect ratio source for all scenes (new scenes inherit it), and add a reference to all scenes
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tw9fjXTnZcw5grg6FUn3DV
2026-11-02 13:17:44 +00:00
Claude 5df050a7f2 Project settings: Resolution is the Short side / Long side / Pixel count; H3 sizes with FunUIAspectSize, old projects keep Pixel count
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tw9fjYcwzhZeJZAD6ujMjA
2026-10-29 11:47:13 +00:00
Claude a4eaabc20e Video input: prepare reference videos (size, crop, rate, skip, cut, every Nth, cap) per project or per scene
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BzJLJWVu7RZnbRqm3nYAnX
2026-10-27 01:04:58 +00:00
Claude 3adebb731f Rename FunPack to FunUI (5.0.3 Classy Chopper): /fun endpoint, wink logo, load-time migration of old names
Old project/workflow names and browser settings migrate on load; /funpack redirects to /fun.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016acRss69eH1235eRdFTZHY
2026-10-25 13:11:03 +00:00
digital-garbageandClaude Sonnet 5.5 1e26876bdb fix: Auto Montage pieces are their own reused units (Generate no longer doubles the clip they copy; deleting the original no longer scrambles them); pieces round down to the frame grid; honest title and hint
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 13:51:29 +03:00
digital-garbageandClaude Sonnet 5.5 ef8c9cfb43 feat: templates no longer carry variables; variables belong to the project alone
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 11:37:05 +03:00
digital-garbageandClaude Haiku 5.5 2a902a5b78 feat: a reference's pin takes the number it has now and stays for every scene until unticked
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-13 04:24:52 +03:00
digital-garbageandClaude Haiku 5.5 976a5bb90c feat: fixed reference slots: a reference pinned to a slot number goes into every scene at that number until unpinned
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-13 04:14:44 +03:00
digital-garbageandClaude Haiku 5.5 b61e0b7def feat: pin a reference to its slot number, so switching off R1 keeps R2 as R2
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-13 03:50:56 +03:00
digital-garbageandClaude Opus 5.5 57c74268cd fix: ratings set the odds (each like up, each dislike down, a run of dislikes much more); a redraw never commits mid-swap
A rating keeps the prompt that made the render and when; matcher replays the expander exactly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 00:13:50 +03:00
digital-garbageandClaude Opus 5.5 15c4a27009 feat: a rating keeps the scene text it was given to; Build learns from that, not a later rewrite
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 17:31:17 +03:00
digital-garbageandClaude Sonnet 5.5 972a5e97b4 fix(import): bypassed/muted subgraphs, primitives through reroutes, one control to every node of a kind, no binding to a lookalike, scoped Set/Get, widget alignment from the export, 400 on bad shape
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 12:35:22 +03:00
digital-garbageandClaude Sonnet 5.5 a799846df0 feat: final render is made at a clip's own resolution (first clip, a chosen clip, or the project's), never forced to the project size
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-03 02:49:03 +03:00
digital-garbageandClaude Sonnet 5.5 fd236a6e97 fix: phrase probe re-installs when its switch flips (cache key), sits innermost so other features' extra forwards are not measured, panel keeps peaks; enhancer reads Qwen-style tokenizers, chat comments key on the typed prompt; scenes get eff_frames/eff_fps
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-02 15:35:37 +03:00
digital-garbageandClaude Sonnet 5.5 98c95fc0de fix: projects keep the whole editor state (timeline edits, audio/overlay lanes, renders, pipeline, client scene ids), validated per field; v4 project round-trips
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-02 15:28:26 +03:00
digital-garbageandClaude Sonnet 5.5 a9ef7d5c44 fix: enhancer works with ComfyUI's real generate/tokenizer signatures (seed, no unknown args, says when no picture/template), chat comments stay with their prompt, enhancer skips combined multi-scene prompts, revolver draws are atomic, template apply is undoable and says what v4 templates lose
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-02 15:21:06 +03:00
digital-garbageandClaude Sonnet 5.5 dd825979f6 feat: prompt templates and editor settings persist with the project, shortcut revolver (peek vs commit), shortcut ideas stats
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-02 14:56:23 +03:00
digital-garbage 3931677507 feat: per-scene resolution source + references, picked from the Media bin
Pieces B and C: Scene gains source_image (aspect-ratio reference only --
resolution itself stays a project.video setting, ImageTransformKJ's
"total_pixels" keep_proportion fits the picture's aspect into that budget)
and references (media ids, in order). Inspector's Scene tab gets pick/clear/
remove controls; picking routes through the existing Media bin via a small
pending-pick handshake in boot.js (one bin, one onPick, so "which field
asked" has to be tracked between the click that asked and the click that
answered).

References wire into MiniMaxH3ReferenceToVideo's ref_images autogrow input
dynamically, per run, only for what a scene actually has -- an unwired slot,
not an empty image, is what "not in use" means to that node. The target is
data on the role itself (wireTo), not a slot id boot.js would otherwise have
to know, keeping the "core never names an implementation" contract intact
one layer up into the app.

Verifying this exposed a second real core gap (after the MatchType one):
core/graph.py had no idea what an Autogrow (V3's io.Autogrow) input even
is -- "ref_images" reported as one opaque COMFY_AUTOGROW_V3 slot, its real
per-instance sockets (ref_image_0, ref_image_1, ...) invisible to
declared()/build(), so a wire naming one was refused as an input the node
does not have. Fixed generically in comfy_types.py (autogrow_instances())
and graph.py, confirmed against ComfyUI's own _io.py (finalize_prefix's
dotted join, build_nested_inputs's runtime un-nesting) -- not guessed, and
proven end to end with a real override()+build() test using the exact edit
boot.js makes.
2026-09-11 12:00:16 +03:00
digital-garbage 2e384c48b2 core, app: shortcuts, autocomplete, and prompt-craft expansion
A trigger->replacement library (core/shortcuts.py, a close port of v4's
apply_prompt_shortcuts/resolve_variables), a Settings section to manage it,
autocomplete on the real prompt textarea (composer.autocomplete.default, so
it outranks the Constructor modal it lives inside), and a stateless
/api/prompt/expand that combines anchor + shortcuts + $variables + postfix
at the moment a run is queued -- scene.text itself stays exactly what was
typed. Project gained anchor/postfix/variables editing UI on its own tab.

Along the way: a real layout bug in the Properties column (every composer
stack can flex-shrink below its own content, and once the Project tab grew
past a couple of rows the column started squeezing its own children's
content into each other and, at the outer level, into the sibling Generation
section below) -- fixed with flex-shrink:0 at each level that needs it,
caught by a Playwright regression test.
2026-09-08 11:49:49 +03:00
digital-garbageandClaude Sonnet 5 e9fcfa247b core: anchor and postfix, the other two project-level prompt fields
Alongside the existing negative prompt: anchor (prepended to every scene)
and postfix + postfix_enabled (appended, with its own on/off switch --
v4 proved a postfix worth keeping needs to be turned off for a quick test
without losing it, which clearing the field would do).

Data layer only, deliberately: unlike negative, which maps 1:1 onto an
existing CLIPTextEncode "text" input via a role, anchor and postfix need
to be COMBINED with each scene's own prompt text, and nothing in the
pipeline does that yet -- that is Studio's job, not built. Wiring a UI
field to a mechanism that does not exist yet is the "half-assed port"
this project has already been steered away from once this session.

Also backfilled round-trip and never-trust-a-file tests for the existing
negative field, which had none at the core/projects.py level despite
video already having them.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-08 11:00:48 +03:00
digital-garbage 0746e31544 core, app: the negative prompt is a real per-project field, not ephemeral state
Found by the adversarial pass on the commit that moved it to the Project
tab: it had no persistence and no per-project sync at all. width/height/
length (project.video) get both -- a real dict field on Project, and
syncVideo() re-reading it into the controls on every project switch.
Negative got neither: it lived only in the pipeline-override controls'
own in-memory state, so it silently carried over from whichever project
was open last. Typed for project A, it was sent as project B's negative
prompt the moment someone switched -- with nothing on screen to say so,
and the Project tab's own copy ("Size and length live in the
Constructor") actively implying every field there was project-scoped.

Project.negative is now a real field (core/projects.py, round-tripped
through the existing whole-project PUT with no route changes needed),
and boot.js gained syncNegative(), wired everywhere syncVideo() already
is: onOpen, and after the pipeline changes underneath. Reproduced the
exact leak end-to-end before fixing it, and the new project_video.spec.js
test is that same reproduction, now asserting it doesn't happen.
2026-09-07 14:05:22 +03:00
digital-garbage aceb06803d app, core: a rating is liked or it is not
dev went binary on H3 and said why: the steering reads the SIGN of a rating
and never its size, so a ten-point scale was the UI claiming a precision
nothing downstream used -- and at the sample sizes this actually runs at, a
mediocre middle rating only diluted the direction. v5 had invented a
four-way scale of its own; it now stores what dev stores.

Pressing the one that is on takes it back. Without a way out a mis-click is
a permanent opinion, and "no opinion" is a real answer nothing should have
to guess at.
2026-09-06 12:52:13 +03:00
digital-garbage faaa465d68 app, core: the inspector, the wheel, ratings, a ruler, and a way in
The surfaces v4 has that v5 did not. Each announces itself and each can be
absent without the rest noticing, which is the point of the exercise: the
frontend should read as v4, and nothing in it should be load-bearing for
anything else.

* SCENE AND PROJECT, in the column v4 keeps them in, which now says which
  scene it is about. Length on the timeline is the scene's own CROP and says
  outright that a regenerate reads the project's -- the rule that mixing the
  two is how a per-scene value quietly becomes everyone's.
* RATINGS on a scene, and a clip carries its rating as a colour on its edge.
  Stored, not yet read: nothing in v5 learns from them yet.
* A RULER, and clips as wide as they are long, so the strip reads as time
  rather than a row of equal boxes. Both ends bounded -- one long clip must
  not become the whole strip, and one short one must stay hittable.
* THE PICKER WHEEL, on the middle button anywhere. It shows whatever
  announced itself to actions.js, so a part of the app that grew a new action
  is in the wheel without the wheel being told, and a part that failed to
  load is simply not among them. v4's three pinned buttons had a fixed idea
  of what could go in them.
* THE WAY IN, and it closes -- on Escape, on the backdrop, on its own button.
  v4's could only be completed: a page refresh was the only way out, which
  makes it a trap rather than a wizard. Dismissing is a real answer.
* File > rename, and the listing moves with it rather than going stale until
  a reload.

Two things the kit refused, correctly, and now nobody has to know: a wheel of
fewer than two items throws (there is nothing to aim between), so the shell
does not offer one; and a strip cell with no ceiling took the whole strip.
2026-09-06 12:30:59 +03:00
digital-garbage 9a4fb4eca0 app, core: what the project is generated at
Width, height and length belong to the PROJECT, not to a scene: a scene
cropped on the timeline and regenerated comes back at the project's size,
because the crop was a timeline decision and a regenerate is a new scene.
They sit in the Constructor beside the prompt -- the two things settled in
one sitting -- and travel to the graph on the path the prompt already uses.

No new mechanism: the pipeline declares roles at `project.video` the same
way it declares the prompt, so core still names no node and no setting. A
role whose value is a number gets a number control instead of a box; that
is the whole of the UI change. What the pipeline does not offer is not
shown, so a graph with no length input simply has no Length.

Bounds are the NODE's and stay there -- it already refuses a value outside
them with a reason, beside Generate. A project file is the one thing here
that outlives the code that wrote it, so everything read back out of one is
checked: a whole positive number, or gone.

Also, four faults the browser suite was already red with before this:

* Six tests still clicked a rail toggle that moved into the timeline head.
* At 420px the panel OVERLAYS the centre -- and the named toggles are in
  the centre, so the panel covered the control that closes it. That is the
  v4 trap this project forbids. They are not offered at that width now; the
  View menu, which nothing can cover, is the way in and out.
* A 768px window laid out 860px wide and scrolled sideways. Root cause, and
  it was never about one component: a flex item's automatic minimum is its
  CONTENT, so every stack, split, panel body and frame region in the app
  handed its content's width upward. A scroller that cannot shrink below
  what it holds is not a scroller -- the strip of scenes made the WINDOW as
  wide as the strip. Floored at the five places every layout routes through.
* A bin test counted `.cx-cell-glyph` across the whole page, so the timeline
  drawing cells with the same glyph in them broke a test about the bin.
2026-09-05 22:16:01 +03:00
digital-garbageandClaude Opus 5 9dcd94c156 app, core: a project of scenes, and the timeline that draws it
The app could run a pipeline and had nowhere to keep what it was making. A
project is an ordered list of scenes, one JSON file each, and it is the only
part of the app that has to survive a reload.

A scene owns its prompt text and the result it produced, and nothing else. v4's
Scene grew three dozen fields; each belongs to the feature that needs it. Per
scene length and fps are deliberately absent — a regenerate uses the project's
values, so a scene cropped on the timeline comes back whole.

The prompt box edits the current scene rather than holding its own copy, so
what is on screen and what a run uses cannot disagree.

app: the region toggles are named buttons in the timeline bar

v4's arrangement. The kit's fallback is one glyph on a rail at each edge of the
window, which is right for a kit that cannot know what its panels are called and
wrong here, where they have names and a bar to put them in.

A pressed toggle was styled only inside a button group, so a standalone one
carried aria-pressed and looked identical either way.

gallery.strip is selectable like its siblings: a filmstrip is the one shape
whose whole job is saying which of these you are on.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 12:03:19 +03:00