collections_ui.rs had grown to 4,591 lines covering the sidebar controller, the click/drag selection policy, tree refresh, the drag gesture, the trash worker, twelve wiring functions, and the row's rename/create/context menu, all in one file. Split into collections_ui/ with one module per area, the way develop/ and library/ were already split on this branch: - controller.rs: CollectionsController and the pure drop/delete/release decisions (decide_drop, decide_delete, decide_release, menu_detail, delete_warning) that a test can drive without a window. - press.rs: PressUndo and the click-and-release selection policy (apply_press, select_row, commit_press, cancel_press). - tree_sync.rs: rebuilding the sidebar from the catalog and pushing catalog-derived state into the grid (refresh_tree, offline_state, sync_lifted/sync_selection/sync_reorderable/sync_badges, refresh_membership, direct_holdings). - drag.rs: the cursor bitmap (compose_drag_image, blit_scaled) and the hold/spring timers (arm_hold, arm_spring, should_spring, collapse_spring_opened) plus their delay constants. - trash.rs: the soft delete (start_trash, start_restore, drain_trash, stop_trash, refresh_trash, format_bytes). - wiring_grid.rs / wiring_tree.rs: the wire() entry point and its twelve wire_* functions, split in two because together they were the largest single piece (grid-facing selection/drag/trash vs. sidebar-facing navigation/create/rename/row-drag/menu/membership). - rename_menu.rs: creating, naming and renaming collections, and the row's context menu (apply_rename, create_child, unique_name, open_row_menu, close_row_menu, close_rename). mod.rs carries the module's own top-level doc comment, the `pub use` re-exports for the eight items the rest of the crate reaches by `collections_ui::` path (CollectionsController, wire, refresh_tree, sync_badges, sync_selection, select_row, commit_press, cancel_press), and a shared `test_support` for the one fixture (`ids`) more than one file's tests needed. Every item that only crossed a boundary within this module, not out of it, was narrowed to `pub(super)` rather than kept at the crate-wide `pub` a single file gave it for free. Extracted with a brace-aware pass that kept each item's own leading doc comment and attributes attached to it, and tests moved with the code they exercise; every TRACES/GESTURE comment lands on the same code it did before. No file outside the new directory changed — lib.rs's `mod collections_ui;` resolves to the directory automatically, and every outside caller's `collections_ui::` path still resolves through mod.rs's re-exports.
Documentation
Two audiences, two folders. Most people want the first table and never the second.
Using DarkRoom
| The manual | Every feature, pictured from the application itself — opening a library, rating and filing, developing, local masks, repair, film, panoramas, export |
| How it is driven | Every gesture and shortcut, by screen. Generated from the code, so it cannot describe one the application does not have |
The top-level README says what DarkRoom is, how to get it on each platform, and what is still missing.
Changing DarkRoom
Everything under dev/ is for someone working on the code. Start with
CONTRIBUTING.md, which says how to land a first change
without reading the rest.
The register and the record. What must be built, how it is built, and how far along it is.
| requirements.md | What the software must do — the numbered register, the decisions (D-numbers) and the spikes (S-numbers) |
| architecture.md | How it is built — crates, the GPU pipeline, the data model, sync |
| traceability.md | Generated: which requirement is claimed by which file. Never edited by hand |
| outstanding.md | What is specified and not built, and whether that is a decision or a gap |
| technical-debt.md | Compromises taken deliberately, each with the condition that retires it |
| code-health.md | What a contribution costs, per seam, measured |
Designs, one per subsystem. Each is the specification the code was built to, kept current as the code moved.
| catalog.md | The index, the library view, incremental scan, the job queue |
| storage.md | Storage backends: the seam a folder, a sync client and a Nextcloud account share |
| faces.md | Face detection, identity, clustering and the eye-state models |
| segmentation.md | How the application finds the regions a local mask snaps to |
| mask-editing.md | Painting, erasing and combining masks |
| spot-removal.md | Clone and heal as parameters in the edit graph |
| panorama.md | Alignment, projection, the chunked composite and the border fill |
| inference.md | The neural runtime and model chosen per device, with the measurements |
| display-and-extension.md | The display contract, and why the fused pipeline is already most of a plugin format |
| view-composition.md | A controller for the display layer |
| ui-navigation.md | Finding things in the interface once there are many |
Measurements. Numbers committed so a regression is a diff rather than a recollection.
| benchmarks.md | The per-commit suite: what it covers, what it does not, how to read a failure |
| bench-baseline.json | The committed numbers the suite checks against |
| frame-budget.md | What a frame costs on each device, and the decision those figures settled |
Platforms and distribution.
| distribution.md | Which channels v1 targets and what each one constrains |
| windows.md | The Windows installer, cross-built from the Linux CI |
| android-signing.md | Which key signs the APK, and keeping it |
Archive. Kept as the record of what was asked for, not as plans.
| milestone-v0.1.md | The first milestone, delivered 2026-08-30 and superseded |
| ui-refinement.md | How the interface should look; succeeded by ui-navigation.md |
Conventions
Two files here are generated and must not be edited by hand:
gestures.md and dev/traceability.md. Both come from
cargo run -p traceability and the pre-commit hook keeps them in step with
the tree. The manual's pictures are recorded by
tools/manual and live in LFS.
A design document links to the requirements it satisfies and to the code
that satisfies them. When the code moves, the link moves with it; a document
that has stopped being true goes to dev/archive/ with a note saying what
replaced it, rather than being deleted.