Skip to content

feat(ui): show the VapourSynth calls each pass makes - #78

Merged
StuartCameronCode merged 1 commit into
mainfrom
feat/implementation-readout
Aug 19, 2026
Merged

feat(ui): show the VapourSynth calls each pass makes#78
StuartCameronCode merged 1 commit into
mainfrom
feat/implementation-readout

Conversation

@StuartCameronCode

Copy link
Copy Markdown
Owner

Experts coming from Hybrid, StaxRip or AviSynth recognise FixChromaBleedingMod or MSRCP instantly, and couldn't tell what a VapourBox pass was doing from its labels. Each pass now prints its calls.

Advanced mode only — a plugin name isn't actionable for someone who hasn't asked for that level of detail, and advanced mode is the app-wide lever for exactly that judgement.

Two shapes, because filters come in two shapes

One call per method — the readout names the selected method's own function:

VAPOURSYNTH
core.zsmooth.CCD

This data already existed on all 49 methods and was already parsed by the Dart model. It had simply never been rendered anywhere, so for these passes this was a display gap, not a data one.

Composite passes — Colour Correction, Chroma Fixes and Crop & Resize aren't one call. They now declare an implementation list of the whole repertoire, and the readout emphasises what's running:

VAPOURSYNTH
core.std.Levels        — automatic levels, measured per frame with core.std.PlaneStats
core.std.Expr          — automatic white balance, measured per frame with core.std.PlaneStats
adjust.Tweak           — brightness, contrast, saturation, hue
core.std.Levels        — levels
haf.SmoothLevels       — levels, dithered and limited as it maps
core.retinex.MSRCP     — shadow detail, on the luma plane only

Showing the inactive calls is the point: it says what the pass could do, not only what it's doing now.

Colour Correction was the reported case and is the sharpest one — it has no method dropdown at all (the inert one went in the panel audit), so nothing anywhere in the UI named any of those six.

activeWhen takes more than one key, and that's load-bearing

applyLevels chooses between std.Levels and haf.SmoothLevels, so each is gated on the pair {applyLevels, smoothLevels} and precisely one is ever emphasised. Gating both on applyLevels alone would claim the pass runs two levels operations. Chroma Fixes has the same shape: automatic and manual alignment are both core.resize.Spline36, told apart only by their role text, and automatic supersedes manual exactly as the generator does.

It reuses _checkVisibleWhen rather than adding a second matcher, so the two can't drift.

Placeholders fixed rather than displayed

spotlessspotless.SpotLess, qtgmc_internalhaf.QTGMC (NoiseProcess), whisperwhisper-cli, and havsfunc.ChangeFPShaf.ChangeFPS (a prefix used nowhere else). custom stays as bookkeeping on the three composite passes but is never shown — asserted.

One judgement call to flag

Crop & Resize's scaling entry reads core.resize.* rather than a concrete kernel, because the kernel is a seven-way enum already visible as its own dropdown directly above; listing all seven greyed out seemed like noise. It's the one entry that isn't a literal callable name. Straightforward to expand into seven activeWhen-gated entries if exactness is preferred.

Testing

820 Flutter tests pass. New:

  • 8 widget tests on the readout — advanced-mode gating, following the method rather than the filter, composite emphasis, the two-key applyLevels case in both directions, the automatic/manual Spline36 pair, and placeholder suppression.
  • 4 schema lints per filter, each covering a silent failure: blank function, custom without an implementation list, activeWhen naming a non-existent parameter (the call would read as permanently inactive — same failure mode as a visibleWhen naming a missing parameter), and a list where nothing can ever be active.

Documented in docs/FILTER_SCHEMA.md and CLAUDE.md.

Experts coming from Hybrid, StaxRip or AviSynth recognise
FixChromaBleedingMod or MSRCP instantly, and could not tell what a VapourBox
pass was doing from its labels. Each pass now prints its calls.

Advanced mode only, and only there: a plugin name is not actionable for
someone who has not asked for that level of detail, and advanced mode is the
app-wide lever for exactly that judgement.

Most of this was a display gap rather than a data one. Every method already
declared its own `function` and the Dart model already parsed it — it had never
been rendered anywhere. For those passes the readout simply names the selected
method.

Composite passes are the real work. Colour Correction was the reported case and
is the sharpest one: it has no method dropdown at all, so nothing in the UI
named any of the six things it can run. Such a pass now declares an
`implementation` list of its whole repertoire, each entry optionally gated by an
`activeWhen`, and the readout emphasises what is running while still showing
what is available. Seeing the inactive calls is the point — it says what the
pass could do, not only what it is doing now.

activeWhen reuses the visibleWhen matcher rather than adding a second one, so
the two cannot drift. It takes more than one key, and that is load-bearing:
applyLevels chooses *between* std.Levels and haf.SmoothLevels, so each is gated
on the pair {applyLevels, smoothLevels} and precisely one is ever emphasised.
Gating both on applyLevels alone would claim the pass runs two levels
operations. Chroma Fixes has the same shape, where automatic and manual chroma
alignment are both core.resize.Spline36 and automatic supersedes manual.

Four placeholder function names are fixed rather than displayed: spotless ->
spotless.SpotLess, qtgmc_internal -> haf.QTGMC (NoiseProcess), whisper ->
whisper-cli, and havsfunc.ChangeFPS -> haf.ChangeFPS, a prefix used nowhere
else. "custom" stays as bookkeeping on the three composite passes but is never
shown, asserted.

Crop & Resize's scaling entry reads core.resize.* rather than a concrete
kernel: the kernel is a seven-way enum already visible as its own dropdown
directly above, so listing all seven greyed out would be noise. It is the one
entry that is not a literal callable name.

Four schema lints per filter, all of which fail silently otherwise: a blank
function, a "custom" method with no implementation list, an activeWhen naming
a parameter that does not exist (the call would read as permanently inactive,
the same failure as a visibleWhen naming a missing parameter), and a list where
nothing can ever be active.
@StuartCameronCode
StuartCameronCode merged commit 9c39c79 into main Aug 19, 2026
4 checks passed
@StuartCameronCode
StuartCameronCode deleted the feat/implementation-readout branch August 19, 2026 07:10
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