Skip to content

Fix conditional visibility, panel grouping, and the settings they silently dropped - #75

Merged
StuartCameronCode merged 4 commits into
mainfrom
fix/panel-grouping-and-visibility
Aug 18, 2026
Merged

Fix conditional visibility, panel grouping, and the settings they silently dropped#75
StuartCameronCode merged 4 commits into
mainfrom
fix/panel-grouping-and-visibility

Conversation

@StuartCameronCode

Copy link
Copy Markdown
Owner

Started as "the Chroma Fixes parameters aren't intuitive" and turned out to be four defects, three of them affecting most of the app.

What was wrong

visibleWhen was being ignored. It is declared only on ParameterUiConfig, so a schema that writes it one level up has it dropped at parse time and the control is always visible. That was 35 parameters across 8 schemas, plus 12 sections (UiSection has no visibleWhen either) — all written during the 2026-08-17 build-out. Beyond Chroma Fixes: Noise Reduction showed mClean and TemporalDegrain2 knobs under SMDegrain, SpotLess showed RemoveDirt's, Chroma Denoise showed CCD's under Cnr4, Deflicker showed both methods' at once.

Sections were not headings. The compact panel printed a title only for an advancedOnly section in advanced mode, so every ordinary section ran together into one flat list and a title was in practice a comment.

Three settings applied on top of corrections already made, and one switch was never read. Automatic chroma alignment measures the misalignment and corrects it, then the manual Y/C shift was applied on top. Automatic levels places black and white, then the manual input/output points graded the already-graded picture. And apply_levels — the panel's own Levels switch — never reached the worker: the block was emitted whenever a value differed from its default, so unticking it left the adjustment running with nothing on screen able to stop it.

copyWith silently reset 26 fields in six models. togglePass rebuilds a pass through copyWith, so flicking a pass switch discarded them — including the DeDot that VHS Cleanup turns on and the automatic alignment that DV Camcorder Tape turns on.

What changed

  • Conditions moved to where the model reads them, section conditions pushed down onto their parameters; the panel prints section titles whenever a schema has more than one.
  • Chroma Fixes regrouped into its five actual repairs (alignment, bleeding, dot crawl, rainbowing, chroma combing): a switch, the controls you would reach for, and an advancedOnly tuning section per filter. Default panel went from ~25 always-on controls to 8 switches.
  • Color Correction pairs each automatic control with the manual one it supersedes, instead of collecting both in a separate "Automatic" group. Automatic levels hides the manual points it replaces but keeps gamma, which it never touches; automatic white balance leaves temperature and tint alone, because an offset still means what its label says after a correction while a mapping does not.
  • Two more inert Method dropdowns removed. Color Correction (tweak/white_balance) and Crop & Resize (standard/nnedi3_2x/eedi3_2x) each declared methods that gated nothing — no parameter conditional on the choice, no model field, the converter hardcoding one value or emitting none. Crop & Resize was the sharper case: its real upscaler choice is the upscaleMethod parameter, so the dropdown was an inert duplicate of a working control.
  • deblock.quant1 was ungated while DCTFilter, which does not take it, was one of the three methods offered.
  • Headings that read as comments renamed after the filter whose controls they hold; three dead sections holding only method removed.
  • Precedence lives in one derivation per pair (effective_apply_chroma_shift, effective_levels_points, effective_apply_levels, and their Dart twins), so the panel, the script and the pass summary cannot disagree. None clears the stored value, so switching the automatic side off restores what was set by hand.
  • Pass summaries name every repair they will perform — Chroma Fixes' omitted four, Colour Correction's omitted four.

Behaviour changes worth flagging

  1. A job or preset with both automatic and manual alignment set now applies only the automatic correction (previously both, i.e. a double correction).
  2. A job with level values but applyLevels unticked no longer applies them. The switch is authoritative now; no built-in preset is affected.
  3. Automatic levels + manual points now applies only gamma from the manual side.

Guards added

  • filter_schema_curation_test: nothing may declare visibleWhen outside a parameter's ui; no section may carry one; every condition must name a parameter that exists (a condition that can never be satisfied hides a control forever — the worse failure); a schema with 2+ methods must gate at least one parameter on the choice; a parameter used by a subset of methods must name exactly those; no section may hold only method.
  • chroma_fixes_panel_test, color_correction_panel_test: pump the real schemas through the real panel — the only level that shows what is actually on screen.
  • parameter_copy_with_test: two passes, behavioural and source-level. The behavioural one cannot perturb a string or enum (an invented value would not decode), which is exactly the shape of the SpotLess method and Subtitles burnInPath bugs.
  • preset_visibility_test: walks every built-in preset's enabled passes through the real converter and fails if a value differing from the schema default is not reachable in that preset's own state. Values hidden only by a method condition are exempt (builtin-fast selects Bwdif and sets a QTGMC preset deliberately); three advanced-only QTGMC values pass through a named allowlist, with a companion test that fails if the list goes stale.
  • test_150, test_151 in the Rust suite, with Dart counterparts.

Each new guard was verified to fail when its fix is removed.

Testing

  • flutter test --exclude-tags heavy — 746 pass
  • cargo test — green apart from subtitle_integration_test, which needs the whisper small model that is not installed locally (unrelated to this branch)
  • Ran the debug app throughout: checked the regrouped panels, the automatic/manual switching, and the headings now appearing in Deinterlace, Noise Reduction and Crop & Resize

Not in this PR

  • Deinterlace's fourteen section titles — established, documented and referenced throughout CLAUDE.md; renaming them is a bigger decision than a heading tidy.
  • Parameter labels mix Title Case and sentence case, sometimes within one panel, and headings make it more obvious. Several hundred strings across 21 files, so it deserves its own change.

🤖 Generated with Claude Code

Chroma Fixes was reported as unintuitive: every repair's tuning sliders were on
screen whether the repair was switched on or not. The grouping was not the cause
— none of the conditions were being read.

`visibleWhen` is declared only on ParameterUiConfig, so a schema that writes it
one level up has it dropped at parse time and the control is always visible.
That was 35 parameters across 8 schemas, plus 12 sections (UiSection has no
visibleWhen either), all written during the 2026-08-17 build-out. Beyond Chroma
Fixes it meant Noise Reduction showed mClean and TemporalDegrain2 knobs under
SMDegrain, SpotLess showed RemoveDirt's, Chroma Denoise showed CCD's under Cnr4,
and Deflicker showed both methods' at once. Section conditions are pushed down
onto their member parameters, which is the only place they work.

DynamicFilterPanelCompact also printed a heading only for an advancedOnly section
in advanced mode, so every ordinary section ran together into one flat list and a
`title` was in practice a comment. It now prints titles whenever a schema
declares more than one section; a single-section schema still gets none, since
there is nothing to distinguish and most of them call it "Settings".

With headings on screen for the first time, all 21 schemas were audited:

  - Color Correction and Crop & Resize each declared methods that gated nothing.
    No parameter was conditional on the choice, neither model has a `method`
    field, and the converter hardcoded one value or emitted none — the dropdown
    rendered, responded and did nothing. Crop & Resize was the sharper case: its
    real upscaler choice is the `upscaleMethod` parameter, so the dropdown was an
    inert duplicate of a working control. Both are single-method now.
  - deblock's `quant1` was ungated while DCTFilter, which does not take it, was
    one of the three methods on offer.
  - Three schemas held a section containing only `method`, which renders nothing.
    Titles that read as comments rather than headings are named after the filter
    whose controls they hold ("CCD settings", "SpotLess settings", "SMDegrain").

Chroma Fixes is regrouped into its five actual repairs — a switch, the controls
you would reach for, and an advancedOnly tuning section per filter — and Color
Correction pairs each automatic control with the manual one it supersedes instead
of collecting them in a separate "Automatic" group.

filter_schema_curation_test lints all of it: nothing may declare visibleWhen
outside a parameter's ui, no section may carry one, every condition must name a
parameter that exists (a condition that can never be satisfied hides a control
forever, which is the worse failure), a schema with two or more methods must gate
at least one parameter on the choice, and a parameter used by a subset of methods
must name exactly those. Two panel tests pump the real schemas through the real
panel, which is the only level that can show what is on screen.
Three settings applied on top of a correction that had already been made, and one
switch the worker never read. All silent.

Chroma Fixes: `_auto_chroma_fix` measures the chroma misalignment and corrects
it, and its block runs before the manual Y/C shift — so with both set the picture
was shifted by a measured amount and then again by a number the user guessed.

Color Correction: automatic levels measures the picture and places black and
white itself, again running first, so manual input/output points on top graded an
already-graded picture with numbers measured against the original. Gamma is
deliberately kept, because automatic levels never touches the midtones and the
Levels group is the only place in the app to reach them.

Color Correction again: `apply_levels` — the panel's switch — was never read.
`has_levels` was true whenever any level differed from its default, so unticking
the switch left the adjustment running with nothing on screen able to stop it,
since the sliders hide with the switch.

White balance is the mirror case and stays composable: automatic white balance
neutralises the cast and temperature/tint then offset the result deliberately. An
offset still means what its label says after a correction; a mapping does not.
pass_advice says which order they happen in rather than hiding anything.

Precedence lives in one derivation per pair — effective_apply_chroma_shift,
effective_levels_points, effective_apply_levels, and their Dart twins — so the
panel, the generated script and the pass summary cannot disagree. None of them
clears the stored value, so switching the automatic side off restores what the
user set by hand.

Covered by test_150/test_151 in the Rust suite and their Dart counterparts in
integration_filter_parameters_test, which is the only side that can catch a
@jsonvalue drifting from a serde name.
ProcessingPipeline.togglePass rebuilds a pass through copyWith, so a field the
method never gained is a field that reverts to its default the moment the user
flicks the pass switch. It compiles, it runs, and the pass then does something
other than what the panel says.

All 26 were added during the 2026-08-17 build-out:

  - Colour Correction: all six automatic levels / automatic white balance fields
  - Chroma Fixes: automatic alignment and DeDot, with their tuning — which VHS
    Cleanup and DV Camcorder Tape turn on, so those presets lost settings to a
    single click
  - Noise Reduction: thirteen, including every mClean and TemporalDegrain2 value
  - SpotLess: `method`, so RemoveDirt silently reverted to SpotLess
  - QTGMC: bwdifEdeint
  - Subtitles: burnInPath

parameter_copy_with_test checks it two ways, and needs both: the behavioural pass
fills every field through fromJson and compares after copyWith(), but it cannot
perturb a string or enum value — an invented one would not decode — which is
exactly the shape of the SpotLess and Subtitles bugs. So a second pass reads the
model source and asserts every final field of the parameter class appears in the
copyWith body. Both were verified against the actual defects.

preset_visibility_test covers the other direction: a preset is the main way
settings arrive without anyone touching a control, so it is the main way a hidden
setting arrives. It walks every built-in's enabled passes through the real
converter and fails if a value differing from the schema default is not reachable
in that preset's own state. The nine built-ins pass. Values hidden only by a
`method` condition are exempt — builtin-fast selects Bwdif and still sets a QTGMC
preset on purpose, so that switching method in the UI lands somewhere sensible —
and three advanced-only QTGMC values are allowed through a named list, with a
companion test that fails if the list gains a stale entry.
CLAUDE.md gains the four findings and what they cost: visibleWhen being read only
inside `ui`, sections becoming visible headings, Color Correction's automatic and
manual halves belonging in one group, and copyWith resetting what it forgets. Also
records that the nine built-in presets map cleanly, and why — most of it luck, in
that no built-in uses Colour Correction at all.

FILTER_SCHEMA.md states where visibleWhen belongs and what happens when it is
written elsewhere, that a section is a heading only where there is more than one,
and that a method which gates nothing should not exist. Notes the two schemas that
are now worked examples: chroma_fixes for progressive disclosure, color_correction
for pairing an automatic control with the manual one it supersedes.

Left deliberately unchanged and noted as such: deinterlace's fourteen section
titles, and the mixed Title Case / sentence case of parameter labels, which is now
more visible under headings but is several hundred strings and should be its own
change.
@StuartCameronCode
StuartCameronCode merged commit 8eb44de into main Aug 18, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant