township: an app shell, not a longer page (v0.2.41)

The 0.2.40 "restyle" rewrote the stylesheet into tokens and kept every class and
every piece of markup, so it rendered as the same page in a slightly different
grey. It also could not have shown: the markup carried 270 inline style
attributes, 32 of which re-shrank a button below the type scale it had just been
given, and 47 of which hardcoded a colour. This changes the structure.

THE SHELL. Three regions that do not move, instead of one 1180px column:

- A left sidebar with the six sections, each one an icon plus a label, and the
  active one carried by aria-current. Under ~900px it becomes a strip of icons
  above the content rather than eating a third of a phone.
- A sticky top bar holding the page title and, on the Run page, the controls that
  start, stop and persist a run. Starting a render used to mean scrolling past 66
  settings to reach the button. The status pill sits with them.
- A log dock fixed to the bottom. Watching a render used to mean scrolling to the
  end of the page and staying there. It remembers whether it was open, and opens
  itself the moment a run starts, which is the one time it is worth reading.

Templates and the per-stage buttons move from under the settings to a card above
them, in the order they are used: pick a template, see it in the form, run a
stage. The Refresh links on Characters/Environments/Matches move to the top bar,
and the per-page <h1> goes with them, since the bar already names the page.

THE FORM. Above 1220px the cards flow in two columns, which roughly halves the
height of a 66-setting form; the four wide cards span both. Each card is a
container query root, so a row inside a half-width card collapses to the card's
width rather than the window's. A label that wraps to two lines no longer pushes
its field out of line with the one beside it.

A BUG THIS SURFACED. The wardrobe inputs declared no type=, and [type=text] does
not match a missing attribute, so every outfit field had been rendering as a
white browser-default box on a dark page. The selector now covers them.

The inline styles are mostly gone: 46 button size attributes folded into .btn-sm
/.btn-xs, and 144 hardcoded hex values replaced by tokens, including four hues
(violet, sage, tan, deep green) that the markup had been spelling out by hand and
that are now declared once.

THE STARTERS, answering what they left out. Both only carried video settings, so
switching to LongCat left the outcome stings at Wan's 96-150 frames — neither
4n+1 nor segment-aligned, so the server had to round them. Both now also set the
outcome range (LongCat: 93, one whole segment, to 173), frame interpolation (off
for LongCat, which does not need it at 15fps), the upscale factor, the keyframe
size and the match count (3 for LongCat, where a scene is ~4x the frames). Both
cover the same keys, so switching between them is deterministic.

What they still do not set, deliberately: the character and environment stages.
Those are image-side work — the video model never sees the pool — so a template
that pinned the pool size or the reference-image budget would reset an operator's
setup for no reason.

All six pages rendered and screenshotted at 1600px and 820px. 53 tests.
Co-Authored-By: 's avatarClaude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0182DhR1eFNPQyGmDrpHbedY
parent a3888a0d
......@@ -16,7 +16,7 @@
# Canonical product version for CoderAI — single source of truth. Both the API
# metadata and the admin web UI read from here.
__version__ = "0.2.40"
__version__ = "0.2.41"
__tagline__ = "Complete orchestration, distribution and escalation of remotizable advanced inference."
# Configure the CUDA caching allocator BEFORE torch is imported anywhere.
......
......@@ -289,3 +289,40 @@ def test_a_starter_is_a_partial_not_a_full_capture():
for name, cfg in TW.BUILTIN_TEMPLATES.items():
assert 0 < len(cfg) < len(TW.CONFIG_FIELDS) / 2, name
assert set(cfg) <= set(TW.CONFIG_FIELDS), name
def test_both_starters_cover_the_same_options():
"""A starter that set a key its twin did not would leave that setting at whatever
the previous template put there — switching templates has to be deterministic."""
wan = set(TW.BUILTIN_TEMPLATES["Wan - many short scenes"])
lc = set(TW.BUILTIN_TEMPLATES["LongCat - few long scenes"])
assert wan == lc, wan ^ lc
def test_the_starters_cover_the_outcome_clips_too():
"""Outcome stings go through the same video model as the scenes, so a template
that only sized the scenes would leave them at the other model's frame counts."""
for cfg in TW.BUILTIN_TEMPLATES.values():
assert "outcome_min_frames" in cfg and "outcome_max_frames" in cfg
assert cfg["outcome_min_frames"] <= cfg["outcome_max_frames"]
def test_the_longcat_outcomes_obey_the_vae_rule_as_well():
lc = TW.BUILTIN_TEMPLATES["LongCat - few long scenes"]
for key in ("outcome_min_frames", "outcome_max_frames"):
assert (lc[key] - 1) % 4 == 0, key
# 93 is one whole LongCat segment: the shortest honest ask.
assert lc["outcome_min_frames"] == 93
def test_the_starters_leave_the_image_side_stages_alone():
"""Characters and environments are drawn by the IMAGE model; the video model never
sees the pool. A starter that pinned those would reset an operator's pool for no
reason. keyframe_size is the one exception — a keyframe is the frame the video
model is conditioned on, so it has to match the clip."""
image_side = {"num_fighters", "num_environments", "char_refs", "env_refs",
"keyframe_steps", "image_model", "text_model", "lora_steps",
"lora_rank", "lora_weight", "character_strength"}
for name, cfg in TW.BUILTIN_TEMPLATES.items():
assert not (set(cfg) & image_side), name
assert cfg["keyframe_size"] == cfg["video_size"], name
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment