Repository navigation
Inspector parameters persist forever with no way to reset them, so the pre-#27 wheel-scroll damage is permanent #56
Description
Context
#27 fixed the cause of the wheel-scroll bug: until then, scrolling the inspector panel changed the
value of whatever field the pointer passed over. #40's Notes recorded the damage from a single
such session — an edge treatment nobody chose, 11 fps, and a 0.4 % alpha threshold.
The cause is fixed. The damage is not. Inspector parameters are persisted in QSettings and there
is no way for a user to put them back. Anyone who scrolled past a field before #27 keeps those
values in every future session, on every future clip, with nothing in the UI to undo it.
Found 2026-09-05 on Windows while manually verifying #40 — the values below are still live on
that machine, months after the session that set them.
Current State (verified 2026-09-05)
Live contents of HKEY_CURRENT_USER\Software\MatteLoop\MatteLoop\parameters on a machine
affected by the pre-#27 bug, next to the defaults in
src/matteloop/core/parameters.py:48-62:
| Key | Persisted | Default | |
|---|---|---|---|
fps |
11 |
15 |
scrolled |
alpha_threshold |
0.4 |
2.0 |
scrolled |
stretch_x |
1.2 |
1.0 |
scrolled |
padding |
1 |
0 |
scrolled |
edge_mode |
decontaminate_colors |
standard |
set deliberately during #40 |
model_id |
birefnet-portrait |
birefnet-portrait |
default |
execution_provider |
CPUExecutionProvider |
CPUExecutionProvider |
default |
The full persisted set (_KEYS in src/matteloop/ui/preferences.py) is model_id, edge_mode,
execution_provider, fps, trim, alpha_threshold, padding, stretch_x,
output_directory, output_filename and max_mib. The transform is not persisted.
There is no reset. Searched the whole UI for a reset, restore, defaults or clear path:
| Looked for | Result |
|---|---|
| Reset/Defaults button for parameters | none |
| Menu bar or context menu | the app has neither |
| Preferences dialog | none |
| CLI flag | only --version, --providers, --smoke-test (src/matteloop/app.py:253-265) |
| Settings schema version that would discard old values | none |
The two "Reset" buttons that do exist are scoped elsewhere and do not touch these values:
src/matteloop/ui/inspector.py:500 ("Reset Crop") and src/matteloop/ui/timeline.py:248
("Reset Range").
Validation does not rescue the user. parameters_from_values
(src/matteloop/core/parameters.py:351-376) does range-check every persisted value —
fps to 1..240, alpha_threshold to 0..100, padding to >= 0, stretch_x to > 0 — and falls
back to the default per key when a value is invalid. Every scrolled value above is inside those
ranges. They are legal values that the user did not choose, which is exactly the case validation
cannot detect.
The only escape today is deleting the registry key by hand. That is not something to ask a user
to do, and it is not discoverable.
Why This Matters
The persisted set is not cosmetic. fps and alpha_threshold change what is rendered; a
0.4 % alpha threshold keeps nearly every faintly-transparent pixel, and stretch_x 1.2 distorts
the output geometry. A user in this state gets subtly wrong results forever and has no reason to
suspect the settings — they never chose them, so they have no memory of setting them.
It also makes every future bug report from an affected user harder to read: their output differs
from a clean install for reasons neither side can see.
Proposed Change
Add a user-reachable reset for the inspector parameters.
- A "Reset to defaults" action in the inspector, scoped to the persisted parameter set
(model_id,edge_mode,fps,trim,alpha_threshold,padding,stretch_x,max_mib),
restoring theParameterStatedefaults and writing them through to QSettings. Placement should
match the existing "Reset Crop" / "Reset Range" affordances so it reads as the same kind of
control. - Do not reset what the user is working on: the loaded source, the crop, the timeline range
and the transform are separate concerns with their own controls. This action is about the
inspector parameters only. execution_provideris excluded from the reset. It is persisted with the same set, but it
is a machine capability choice rather than a creative parameter: resetting it to the
recommendation would move a user back onto hardware they deliberately left. On the machine
where this was found, that means being put back on DirectML and a GPU that hangs on the
default model. The UI must state that the provider is not affected.output_directoryandoutput_filenameare excluded too. They are a button and a line
edit, so the wheel could never have changed them, and they are file choices the user made on
purpose. Their "default" (next to the source, named after it) is reachable by clearing them.
Acceptance Criteria
- A user can restore the inspector parameters to their defaults without leaving the app.
- After the reset, the persisted QSettings values match
ParameterState()'s defaults for every
key in scope — verified by reading the settings back, not just the widgets. - The reset does not change the loaded source, crop, timeline range, transform, output
directory or output filename. - The reset is reflected in the inspector widgets immediately, without a restart.
- The reset leaves
execution_provideruntouched, and the UI says so. - A user with out-of-range persisted values still gets per-key fallback as today — this change
does not replaceparameters_from_values' validation. uv run ruff check . && uv run mypy src && QT_QPA_PLATFORM=offscreen uv run pytest -qgreen,
plusuv run python scripts/check_guardrails.py.
Testing Plan
| Layer | What | Count |
|---|---|---|
| Unit | reducer event restores every in-scope key to its default and leaves the rest alone | +2 |
| Integration | persist -> reset -> re-read QSettings yields defaults | +1 |
| UI | the action is reachable, updates the widgets, and does not disturb crop/range/transform | +2 |
Files Reference
| File | Change |
|---|---|
src/matteloop/core/parameters.py |
a ParametersReset event in the ParameterEvent union (:151-164), reduced by reduce_parameters (:167) from the ParameterState defaults (:48-62), invalidating the preview like the other parameter events |
src/matteloop/ui/inspector.py |
the button itself, alongside the existing reset affordances (see :500), emitting the event through command_requested like "Reset Crop" |
No change to src/matteloop/ui/preferences.py or src/matteloop/ui/controller.py:
Controller._dispatch_parameter (controller.py:462-480) already routes every ParameterEvent
to the store and calls persist_parameters with the full resulting state, so a reset event is
persisted the moment it is reduced. (_dispatch_parameter also re-applies _working_provider
when the persisted provider is the one that failed — the reset must not touch that path, which
follows from leaving execution_provider alone.)
Out of Scope
- Migrating or versioning the settings schema. Worth considering separately; it would not help
the affected users here, because their stored values are valid. - Any change to the wheel-scroll behaviour itself. That was Transform stage on a finished cut: trim, output crop, resize, and a looping result preview #27.
- Resetting crop, timeline range, transform, output directory or output filename.
Rollback
Revert the PR. The change adds an action; it does not alter the stored format, so a revert leaves
existing settings readable.
Effort Estimate
~1 h reducer event + ~1 h UI wiring + ~1 h tests.
Related
- Transform stage on a finished cut: trim, output crop, resize, and a looping result preview #27 — fixed the wheel-scroll cause. This issue is about the state it left behind.
- Edge treatment "Decontaminate colors" fails every preview and render: the segmentation result is read-only #40 — the manual verification during which this was found.
- There is no application-level settings surface at all, and the language switch needs one #50 — the application-level settings surface. If that lands, a second entry point there is
fine; this issue does not wait for it, because the action belongs next to the fields it resets,
the way "Reset Crop" and "Reset Range" sit next to theirs.
Activity