Every background job reported into a window property of its own — library-thumbs-done, library-pin-total, library-syncing — which only the grid ever read. A pin download that outlived the view it was started from drew nothing at all once the user opened an image, and there was no answer anywhere to "what is this busy with", because the answer was spread across eight properties nothing collected. They report to one register now (ui/dr-ui/src/activity.rs). It publishes an aggregate, which draws a three-pixel bar across the top of the shell in every view, and a row per job, which the settings page lists: scans, thumbnail batches, pin and open downloads, sidecar uploads, the sync and the trash. Failures stay on the list until they are cleared; routine successes do not, or a scroll would bury them. The handle removes a still-running job when it drops, so a worker that dies mid-transfer takes its row with it rather than leaving the bar sweeping for the rest of the session. Also carries in-flight work from a parallel session — the drawn icon set and the dr-pipeline ops split. dr-pipeline's build script does not compile at this commit; ui/dr-ui does, with clippy clean and its tests passing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
82 lines
2.6 KiB
YAML
82 lines
2.6 KiB
YAML
id: highlights_shadows
|
|
label: op.highlights_shadows
|
|
order: 40
|
|
|
|
doc: |
|
|
Highlights and shadows — broad, overlapping recovery at both ends.
|
|
|
|
Weights are built from smoothstep rather than a hard threshold: a sharp
|
|
boundary produces visible banding on a gradient — a sky is the worst case,
|
|
and it is also the most common subject for these controls.
|
|
|
|
Broad where [`blacks_whites`](blacks_whites.yaml) is narrow. These are the
|
|
controls used to tame a contrasty scene; those are the ones used to place
|
|
the endpoints.
|
|
|
|
placement: |
|
|
After the broad tonal statement contrast makes, so these refine it.
|
|
|
|
params:
|
|
highlights:
|
|
label: param.highlights
|
|
kind: amount
|
|
doc: |
|
|
Negative recovers highlights, the overwhelmingly common direction,
|
|
matching the convention every other developer uses.
|
|
shadows:
|
|
label: param.shadows
|
|
kind: amount
|
|
|
|
uniforms:
|
|
hi_amount:
|
|
value: highlights / 100
|
|
doc: |
|
|
A full stop at the extreme; enough to recover a bright sky without
|
|
inverting the tonal relationship.
|
|
lo_amount: shadows / 100
|
|
|
|
helpers: [luminance, tone_position, apply_tone_gain]
|
|
|
|
wgsl: |
|
|
let luma = luminance(c);
|
|
let pos = tone_position(luma);
|
|
|
|
// Broad, overlapping weights. Highlights ramp in over the upper half,
|
|
// shadows out over the lower half, so a mid-tone is barely touched by
|
|
// either and the two controls blend rather than fighting at the join.
|
|
let hi_w = smoothstep(0.5, 1.0, pos);
|
|
let lo_w = 1.0 - smoothstep(0.0, 0.5, pos);
|
|
|
|
// Each control contributes up to a stop of gain at full deflection.
|
|
// exp2 keeps the effect symmetric: -100 and +100 are inverse.
|
|
let hi_gain = exp2(hi_amount * hi_w);
|
|
let lo_gain = exp2(lo_amount * lo_w);
|
|
|
|
c = apply_tone_gain(c, hi_gain * lo_gain);
|
|
|
|
tests:
|
|
- name: it_starts_neutral
|
|
expect_active: false
|
|
|
|
- name: one_parameter_is_enough_to_activate
|
|
set: { highlights: -50 }
|
|
expect_active: true
|
|
|
|
- name: highlight_recovery_is_the_negative_direction
|
|
why: The convention users expect — dragging left recovers. Full travel is one stop.
|
|
set: { highlights: -100 }
|
|
expect: { hi_amount: -1.0 }
|
|
|
|
- name: tonal_amounts_are_symmetric
|
|
why: |
|
|
exp2 of equal and opposite exponents multiplies to 1, so +100 and -100
|
|
have to be exact negations for the two directions to cancel.
|
|
set: { shadows: 100 }
|
|
expect: { lo_amount: 1.0 }
|
|
|
|
- name: the_weights_are_smooth_not_thresholded
|
|
why: |
|
|
A hard boundary bands visibly on a gradient, and a sky is both the worst
|
|
case and the most common subject for this control.
|
|
expect_wgsl: ["smoothstep(0.5, 1.0, pos)", "smoothstep(0.0, 0.5, pos)"]
|