diff --git a/docs/architecture.md b/docs/architecture.md index 23adcf4..67e10ae 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1086,6 +1086,7 @@ Full rationale in [requirements.md §8](requirements.md). Summary: | D12 | Scope versus pace | **Open** | | D13 | Face inference runtime and model licensing | **Runtime answered**, licensing open | | D14 | Segmentation source for local masking | Decided — arm C (docs/segmentation.md §14) | +| D15 | Target devices — 12-inch tablet and desktop, no phone | Decided (requirements D15) | --- diff --git a/docs/requirements.md b/docs/requirements.md index 35c7a07..5d42ce3 100644 --- a/docs/requirements.md +++ b/docs/requirements.md @@ -1192,6 +1192,30 @@ both a smaller build and a usable one — it needs no develop chain. Resolving D12 sets D3 and [architecture.md §10](architecture.md)'s Phase 2. +### D15 — target devices · **DECIDED 2026-08-22** + +**A 12-inch tablet and a desktop. No phone.** + +Recorded because it is load-bearing for the interface and invisible in the +code. Every phone-shaped answer — a bottom tool strip, a one-tool-at-a-time +sheet, thumb-reach zones — is designing for hardware this project does not +target, and each would have cost a second layout to keep in step with the +first. + +What survives the decision is the *input* difference rather than the size one: +a 12-inch tablet is touched, and NFR/FR-UI-7's position already covers it — +hit regions grow to the modality, layout does not move. The practical rules +that fall out are in `docs/ui-navigation.md` D-N2: no hover-only affordance +and no modifier key may be the sole route to anything, because a tablet has +neither. + +`EXPANDED_MIN_WIDTH` is 820 logical pixels and a 12-inch tablet is ~1024 +across in portrait, so **both orientations of both targets are the expanded +layout**. The compact class remains as graceful degradation for a narrowed +desktop window, not as a second interface. + +--- + ### D13 — face inference runtime and model licensing · **RUNTIME ANSWERED, LICENSING OPEN** > **Updated 2026-08-21.** The runtime half of this decision is settled, and by a route the table diff --git a/docs/ui-navigation.md b/docs/ui-navigation.md index 6d9b6cb..bf35cfe 100644 --- a/docs/ui-navigation.md +++ b/docs/ui-navigation.md @@ -88,7 +88,7 @@ overlay; Capture One makes layers a persistent selector at the top of the adjustments tab. Nobody makes a mask a panel that silently rewires a different panel — which is what DarkRoom currently does. -### On a phone, one family +### On a phone, one family — and why it does not apply here Lightroom Mobile, Photomator and VSCO all converge on the same shape: **a horizontal strip of tool icons along the bottom**, and tapping one replaces a @@ -105,6 +105,11 @@ The convergence is not fashion. Three physical facts drive it: - **There is no hover.** Disclosure triangles and hover-revealed affordances are worth less; a control is either visible or gone. +**None of the first two apply to DarkRoom's targets**, which are a 12-inch +tablet and a desktop (§3, D-N2). Nobody thumbs a 12-inch tablet one-handed, +and its narrow dimension is not narrow. The third does apply, and is handled +already — see D-N2. + --- ## 3. The decisions @@ -142,35 +147,50 @@ describe what is being looked at — and without it the instrument and the controls disagree about what they are measuring. Costs a mask term in the histogram reduction, which already runs per frame over the displayed frame. -### D-N2 — Diverge on **width**, not on platform · **RECOMMENDED** +### D-N2 — One layout, because both targets are wide · **DECIDED** -The question was whether desktop and Android should diverge. They should -diverge, but not along that line. +The question was whether desktop and Android should diverge. The answer turns +out to be that **neither the platform nor the width axis separates DarkRoom's +targets**, so there is no divergence to build. -**Platform is the wrong axis.** A tablet in landscape wants what a desktop -wants. A desktop window dragged to a third of the screen wants what a phone -wants. Splitting on `cfg(target_os)` would give the same physical situation two -answers depending on which binary it happened to be. +**The targets are a 12-inch tablet and a desktop.** No phone, decided +2026-08-22. A 12-inch tablet is roughly 1024 logical pixels across in portrait +and 1400 in landscape; `EXPANDED_MIN_WIDTH` is 820. **Both orientations of both +targets are the expanded class.** The compact class now fires only when a +desktop window is dragged under 820px, which is a case to degrade gracefully +into, not a second interface to design. -**Width is the right axis, and it already exists.** `apply_layout_class` in -`lib.rs` already classifies the window as `expanded` or `compact` on width -alone, already remembers a user override per class, and already drives panel -visibility. The divergence proposed here is a second consumer of a decision -the app is already making. +**Platform would have been the wrong axis anyway**, and it is worth recording +why so it is not proposed again. A tablet in landscape wants what a desktop +wants; a desktop window dragged narrow wants what a small screen wants. +Splitting on `cfg(target_os)` gives one *physical* situation two answers +depending on which binary it happens to be. `apply_layout_class` already says +this in its own comment — "logical pixels, not a device check" — and it was +right. -So: +**What actually differs between the two targets is input, not size**, and the +architecture has already decided that too. `WidgetDemand::precise_pointing` +exists for a frontend driving a television with a remote, and its own +documentation states the position: *touch is fine, since hit regions grow to +the modality* (FR-UI-7). Touch changes **hit regions, not layout**. A control +is drawn where it belongs and its target grows past its own bounds — which +`Check` and the mask rows already do. -| | **expanded** | **compact** | -|---|---|---| -| Modes | strip at the top of the canvas | strip along the bottom | -| Tools | side column, collapsible stack | bottom sheet, one group at a time | -| Mask stack | inline in the column | its own sheet, reached from the strip | +So: **one develop layout, tuned for a wide viewport, with touch targets +throughout.** The consequences worth stating: -**What must not diverge is the controls.** Both layouts consume the same -`ParamRow` model, the same generated capability list and the same widgets. -Only the container differs. That is what keeps FR-DEV-3a intact — a new -operation appears in both layouts with no UI edit, because neither layout -knows what an operation is. +- **A guaranteed-wide viewport is an asset.** The extent problem (§1.2) can be + solved by pinning rather than by hiding — see N4. +- **No hover-only affordance may carry meaning.** Hover may *emphasise*; it may + never be the only way to discover a control. The mask rows already obey this + — the eye and the delete target are drawn, not revealed. +- **No modifier key may be required.** A tablet has no shift. Local masking + already lost its shift-click extend for this reason, and nothing should + reintroduce one as the only route to a feature. +- **Anything dragged needs a finger-sized target.** This is the live one: + gradient masks have no on-canvas handles yet, and when they arrive their + handles are the first control in the app designed to be dragged on a + photograph rather than in a panel. ### D-N3 — Collapsible panels, not tool tabs · **OPEN** @@ -251,25 +271,42 @@ local mode restores the frame's. **Depends on** `ui-refinement.md` A. Extends C. -**Deliverable.** Each panel in the develop column collapses to its header; -headers carry the modified dot C defines; state is keyed by panel identity and -survives a slider drag. Default: image and histogram open, the rest collapsed -until touched. +**Deliverable, two halves.** -**Done when.** A fresh develop view fits without scrolling at 900px tall, and -the histogram is reachable without scrolling past the sliders it reports on. +*Collapse.* Each panel collapses to its header; headers carry the modified dot +C defines; state is keyed by panel identity and survives a slider drag. +Default: histogram open, the rest collapsed until touched. -### N5 — Compact layout +*Pin.* The histogram and the scope header do **not** scroll. They sit above the +scrolling region, always visible. -**Depends on** N1, N4. +Pinning is the half a guaranteed-wide viewport buys, and it addresses §1.2 +directly rather than obliquely. The complaint is not that the column is long — +it is that the instrument every tonal control is judged against scrolls away +from the controls it reports on. Collapsing panels shortens the scroll; +pinning removes the problem. Together they cost about 260px of fixed height, +which a target that is never under 820px wide and rarely under 1000 tall can +afford. -**Deliverable.** In the compact class the column becomes a bottom sheet and the -mode strip moves to the bottom. Tool groups are reached from the strip, one at -a time. Same models, same widgets, different container. +**Done when.** The histogram is visible while any tonal slider is being +dragged, at every window size the targets produce, without the user having +scrolled to arrange it. -**Done when.** A phone-width window shows the photograph with a thumb-reachable -strip and no side column, and every control reachable in expanded is reachable -here. +### N5 — Compact degrades, rather than diverges + +**Depends on** N4. + +Not a second interface. Below `EXPANDED_MIN_WIDTH` the develop column already +overlays rather than sits beside the canvas, and `apply_layout_class` already +remembers the user's override per class. With N4's collapse in place a narrow +window is a one-panel-at-a-time column by consequence rather than by design. + +**Deliverable.** Confirm the narrow case is usable and fix what is not. No +bottom sheet, no second layout, no tool strip along the bottom — those solve a +phone, and there is no phone. + +**Done when.** A desktop window dragged to 700px shows the photograph and a +usable column, and nothing is unreachable that was reachable at 1400px. --- @@ -292,7 +329,9 @@ rearranges everything. layouts with no UI edit. No file under `ui/` may name an operation. - **ARCH §4.3a.** Composition is the frontend's decision. The core may declare what an operation *is*; it may not declare where the panel puts it. -- **FR-UI-3.** Touch targets survive the compact layout — it is the layout - most likely to be touched. +- **FR-UI-3 / FR-UI-7.** Touch changes hit regions, not layout. Every control + keeps a finger-sized target wherever it is drawn, and no hover-only + affordance and no modifier key may be the sole route to anything — a 12-inch + tablet has neither. - **FR-UI-5.** Escape and the Android back gesture leave the innermost state first. Every mode added here joins that order.