Say what the application is doing, in one bar and one list
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>
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
id: blacks_whites
|
||||
label: op.blacks_whites
|
||||
order: 50
|
||||
|
||||
doc: |
|
||||
Blacks and whites — the endpoints, where the image clips.
|
||||
|
||||
Narrow where [`highlights_shadows`](highlights_shadows.yaml) is broad:
|
||||
whites act only in the top quarter, blacks only in the bottom quarter. That
|
||||
difference in reach is the whole distinction between the two pairs.
|
||||
|
||||
placement: |
|
||||
After the broad recovery controls: the endpoints are placed once the tonal
|
||||
range they bound has been settled.
|
||||
|
||||
params:
|
||||
blacks:
|
||||
label: param.blacks
|
||||
kind: amount
|
||||
whites:
|
||||
label: param.whites
|
||||
kind: amount
|
||||
|
||||
uniforms:
|
||||
white_amount: whites / 100
|
||||
black_amount:
|
||||
value: blacks / 100 * 0.02
|
||||
doc: |
|
||||
A small linear offset. Scene-referred black sits near zero, so the
|
||||
useful range here is far smaller than a stop — 0.02 is already a
|
||||
visible lift on a dark frame.
|
||||
|
||||
helpers: [luminance, tone_position, apply_tone_gain]
|
||||
|
||||
wgsl: |
|
||||
let luma = luminance(c);
|
||||
let pos = tone_position(luma);
|
||||
|
||||
// Narrow weights concentrated at each end — this is what separates these
|
||||
// controls from highlights/shadows, which are broad. Whites act only in the
|
||||
// top quarter, blacks only in the bottom quarter.
|
||||
let white_w = smoothstep(0.75, 1.0, pos);
|
||||
let black_w = 1.0 - smoothstep(0.0, 0.25, pos);
|
||||
|
||||
// Whites scale the top end multiplicatively, moving the clipping point.
|
||||
let white_gain = exp2(white_amount * white_w);
|
||||
c = apply_tone_gain(c, white_gain);
|
||||
|
||||
// Blacks shift the floor. This one is deliberately *additive*: the point of
|
||||
// a blacks control is to set where the image reaches zero, and a multiply
|
||||
// can never bring a non-zero value to zero nor lift a true black off it.
|
||||
c = c + vec3<f32>(black_amount * black_w);
|
||||
|
||||
// The subtractive direction can push below zero, which is not light.
|
||||
c = max(c, vec3<f32>(0.0));
|
||||
|
||||
tests:
|
||||
- name: it_starts_neutral
|
||||
expect_active: false
|
||||
|
||||
- name: one_parameter_is_enough_to_activate
|
||||
set: { whites: 20 }
|
||||
expect_active: true
|
||||
|
||||
- name: the_blacks_offset_stays_small
|
||||
why: |
|
||||
Scene-referred black is near zero; a full-stop control here would be
|
||||
unusable, moving the image to grey at a fraction of its travel.
|
||||
set: { blacks: 100 }
|
||||
expect_range: { black_amount: [0.0, 0.05] }
|
||||
|
||||
- name: blacks_are_additive_not_multiplicative
|
||||
why: |
|
||||
A multiply can never bring a non-zero value to zero nor lift a true
|
||||
black off it, which is precisely what this control is for.
|
||||
expect_wgsl: ["c = c + vec3<f32>(black_amount * black_w);"]
|
||||
|
||||
- name: the_result_cannot_go_below_zero
|
||||
why: A negative radiance is not light, and it poisons everything downstream.
|
||||
expect_wgsl: ["c = max(c, vec3<f32>(0.0));"]
|
||||
|
||||
- name: these_endpoints_are_narrower_than_the_recovery_controls
|
||||
why: |
|
||||
The one property distinguishing this node from highlights_shadows. If
|
||||
these weights widened to match, the two would be the same control twice.
|
||||
expect_wgsl: ["smoothstep(0.75, 1.0, pos)", "smoothstep(0.0, 0.25, pos)"]
|
||||
Reference in New Issue
Block a user