The manual existed only as docs/manual/README.md, which the forge renders and nothing else does. An installed copy of the application, on a laptop with no network or on a tablet, had no manual it could open. `traces manual` renders the README to docs/manual/index.html with pulldown-cmark (already in the tree as Slint's Markdown parser, so this adds a dependency edge and no crate). The page is one file with an inline stylesheet that follows the system's light or dark preference, a contents list of every section and subsection, and the pictures by their relative media/ paths. Each heading carries the id the forge gives it, so README.md#rating-and-flagging and index.html#rating-and-flagging are the same link. A picture alone in its paragraph becomes a figure whose alt text is shown as the caption, and every picture reserves its 16:11 box before it loads, so a jump into the middle of the page lands where it aimed rather than a screenful above. Links to design documents, which the installed page has no copy of, point at the forge. The page is committed rather than rendered at build time, as the gesture book is: it is user-facing text reviewed in the diff, and the three packagers then only copy it. `traces manual-check` fails in CI when the committed page is not the render of the README, and the pre-commit hook regenerates it when the README is staged.
306 lines
20 KiB
HTML
306 lines
20 KiB
HTML
<!DOCTYPE html>
|
|
<!-- GENERATED FILE — do not edit by hand. -->
|
|
<!-- Source: docs/manual/README.md. Regenerate: cargo run -p traceability -- manual -->
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<meta name="color-scheme" content="light dark">
|
|
<title>DarkRoom, shown</title>
|
|
<style>
|
|
:root {
|
|
--bg: #fbfaf8;
|
|
--ink: #1d1c1a;
|
|
--ink-dim: #5c5955;
|
|
--rule: #dedad4;
|
|
--accent: #8a4b12;
|
|
--panel: #f1eee9;
|
|
--mark: #f6e3c7;
|
|
}
|
|
@media (prefers-color-scheme: dark) {
|
|
:root {
|
|
--bg: #161514;
|
|
--ink: #e9e6e1;
|
|
--ink-dim: #a39e97;
|
|
--rule: #34312d;
|
|
--accent: #e8a25c;
|
|
--panel: #201e1c;
|
|
--mark: #43321f;
|
|
}
|
|
}
|
|
* { box-sizing: border-box; }
|
|
body {
|
|
margin: 0;
|
|
background: var(--bg);
|
|
color: var(--ink);
|
|
font: 17px/1.6 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
|
}
|
|
.page {
|
|
display: grid;
|
|
grid-template-columns: 15rem minmax(0, 46rem);
|
|
gap: 3rem;
|
|
justify-content: center;
|
|
padding: 2rem 1.5rem 4rem;
|
|
}
|
|
.toc {
|
|
position: sticky;
|
|
top: 1.5rem;
|
|
align-self: start;
|
|
max-height: calc(100vh - 3rem);
|
|
overflow-y: auto;
|
|
font-size: 0.9rem;
|
|
}
|
|
.toc-title {
|
|
margin: 0 0 0.5rem;
|
|
color: var(--ink-dim);
|
|
font-size: 0.75rem;
|
|
font-weight: 700;
|
|
letter-spacing: 0.08em;
|
|
text-transform: uppercase;
|
|
}
|
|
.toc ul { list-style: none; margin: 0; padding: 0; }
|
|
.toc ul ul { padding-left: 0.9rem; }
|
|
.toc li { margin: 0.2rem 0; }
|
|
.toc a { color: var(--ink-dim); text-decoration: none; }
|
|
.toc a:hover { color: var(--accent); }
|
|
main { min-width: 0; }
|
|
h1, h2, h3 { line-height: 1.25; scroll-margin-top: 1rem; }
|
|
h1 { font-size: 2.1rem; margin: 0 0 1rem; }
|
|
h2 { font-size: 1.5rem; margin: 2.6rem 0 0.8rem; padding-top: 1rem; border-top: 1px solid var(--rule); }
|
|
h3 { font-size: 1.15rem; margin: 1.8rem 0 0.6rem; }
|
|
h2:target, h3:target { background: var(--mark); border-radius: 4px; padding-left: 0.3rem; margin-left: -0.3rem; }
|
|
a { color: var(--accent); }
|
|
code {
|
|
font: 0.88em ui-monospace, "Cascadia Mono", "DejaVu Sans Mono", monospace;
|
|
background: var(--panel);
|
|
border: 1px solid var(--rule);
|
|
border-radius: 4px;
|
|
padding: 0.05em 0.3em;
|
|
}
|
|
figure { margin: 1.4rem 0; }
|
|
figure img, main img {
|
|
display: block;
|
|
width: 100%;
|
|
height: auto;
|
|
aspect-ratio: auto 16 / 11;
|
|
border-radius: 6px;
|
|
border: 1px solid var(--rule);
|
|
}
|
|
figcaption { margin-top: 0.4rem; color: var(--ink-dim); font-size: 0.9rem; }
|
|
table { border-collapse: collapse; width: 100%; font-size: 0.95rem; }
|
|
th, td { text-align: left; padding: 0.4rem 0.6rem; border-bottom: 1px solid var(--rule); vertical-align: top; }
|
|
th { color: var(--ink-dim); font-weight: 600; }
|
|
@media (max-width: 52rem) {
|
|
.page { grid-template-columns: minmax(0, 1fr); gap: 1rem; padding: 1rem 16px 3rem; }
|
|
.toc { position: static; max-height: none; border: 1px solid var(--rule); border-radius: 6px; padding: 0.8rem 1rem; background: var(--panel); }
|
|
body { font-size: 16px; }
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="page">
|
|
<nav class="toc" aria-label="Contents">
|
|
<p class="toc-title">Contents</p>
|
|
<ul>
|
|
<li><a href="#opening-a-library">Opening a library</a></li>
|
|
<li><a href="#the-library">The library</a>
|
|
<ul>
|
|
<li><a href="#rating-and-flagging">Rating and flagging</a></li>
|
|
<li><a href="#getting-about">Getting about</a></li>
|
|
<li><a href="#selecting-several">Selecting several</a></li>
|
|
<li><a href="#collections">Collections</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#developing-a-photograph">Developing a photograph</a>
|
|
<ul>
|
|
<li><a href="#light">Light</a></li>
|
|
<li><a href="#looking-closer">Looking closer</a></li>
|
|
<li><a href="#white-balance-from-the-photograph">White balance from the photograph</a></li>
|
|
<li><a href="#composing">Composing</a></li>
|
|
<li><a href="#local-adjustments">Local adjustments</a></li>
|
|
<li><a href="#repair">Repair</a></li>
|
|
<li><a href="#film">Film</a></li>
|
|
<li><a href="#history-snapshots-presets">History, snapshots, presets</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#merging-a-panorama">Merging a panorama</a></li>
|
|
<li><a href="#export">Export</a></li>
|
|
<li><a href="#settings">Settings</a></li>
|
|
<li><a href="#people">People</a></li>
|
|
<li><a href="#where-things-are-written-down">Where things are written down</a></li>
|
|
<li><a href="#how-this-page-is-made">How this page is made</a></li>
|
|
</ul>
|
|
</nav>
|
|
<main>
|
|
<h1 id="darkroom-shown">DarkRoom, shown</h1>
|
|
<p>A tour of what the application does, one picture per thing. Every image on
|
|
this page was captured from the desktop build driving itself — nothing is a
|
|
mock-up, and nothing has been retouched outside DarkRoom. Where a feature is
|
|
better seen moving, it moves.</p>
|
|
<p>The requirements behind each feature are in <a href="https://gitea.tourolle.paris/dtourolle/DarkRoom/src/branch/master/docs/dev/requirements.md">requirements.md</a>;
|
|
the reasoning is in the design documents linked from each section. This page
|
|
is only about what you see.</p>
|
|
<p>The photographs are the author's. None show a person.</p>
|
|
<h2 id="opening-a-library">Opening a library</h2>
|
|
<p>DarkRoom opens on a library: a folder on this machine, a folder a sync
|
|
client keeps, or a Nextcloud account. A folder needs no password and uploads
|
|
nothing.</p>
|
|
<figure><img loading="lazy" src="media/launch.png" alt="The launch screen: a server field, a folder field, and which formats to scan for"><figcaption>The launch screen: a server field, a folder field, and which formats to scan for</figcaption></figure>
|
|
<p>Once a folder is named, it is the library — you are not asked for it again,
|
|
and <code>Open library</code> opens it whole. <code>Subfolder…</code> narrows the scan to part of
|
|
it. The formats ticked are what the scan looks for; RAW is on and JPEG off
|
|
by default, because a RAW editor's sensible default is the file the camera
|
|
wrote first.</p>
|
|
<figure><img loading="lazy" src="media/launch-folder.png" alt="A folder chosen: the library, whether to scan a subfolder, and the formats"><figcaption>A folder chosen: the library, whether to scan a subfolder, and the formats</figcaption></figure>
|
|
<h2 id="the-library">The library</h2>
|
|
<figure><img loading="lazy" src="media/library.png" alt="The grid: collections on the left, the timeline beside it, the roll of thumbnails, and the filter bar above"><figcaption>The grid: collections on the left, the timeline beside it, the roll of thumbnails, and the filter bar above</figcaption></figure>
|
|
<p>Photographs are ordered by capture time, with a month heading where each
|
|
begins. The strip on the left is the timeline — drag it to jump to a year.
|
|
The bar above the grid filters by rating, flag, colour label and where the
|
|
file is (on this device, or only on the server).</p>
|
|
<h3 id="rating-and-flagging">Rating and flagging</h3>
|
|
<p>Hover a cell and the stars appear; click one. The filter chips above the grid
|
|
count what each rating holds, and clicking <code>3+</code> shows only those.</p>
|
|
<figure><img loading="lazy" src="media/library-rating.gif" alt="Rating two photographs, then filtering the grid to three stars and more"><figcaption>Rating two photographs, then filtering the grid to three stars and more</figcaption></figure>
|
|
<p>Colour labels work as Lightroom's do: <code>6</code> red, <code>7</code> yellow, <code>8</code> green, <code>9</code>
|
|
blue, on the photograph under the pointer or on the selection, and the same
|
|
key again takes the label off. <code>Label</code> on the selection bar offers all five,
|
|
purple included, and <code>None</code>. Each label is drawn with its initial on it, so
|
|
it reads without telling the colours apart, and the filter bar has a chip for
|
|
each. In develop, the top bar names the open photograph's label and sets it,
|
|
and the same keys work there.</p>
|
|
<h3 id="getting-about">Getting about</h3>
|
|
<p>Drag the timeline to scrub through years; Ctrl and the wheel resize the
|
|
thumbnails.</p>
|
|
<figure><img loading="lazy" src="media/library-timeline.gif" alt="Scrubbing the timeline"><figcaption>Scrubbing the timeline</figcaption></figure>
|
|
<figure><img loading="lazy" src="media/library-thumbsize.gif" alt="Resizing the thumbnails with Ctrl and the wheel"><figcaption>Resizing the thumbnails with Ctrl and the wheel</figcaption></figure>
|
|
<h3 id="selecting-several">Selecting several</h3>
|
|
<p><code>Select</code> in the header — or Ctrl-click — starts a selection. Shift-click picks
|
|
a range. The bar at the foot of the grid is everything a selection can be done
|
|
to: collections, keywords, presets, export, and merging to a panorama.</p>
|
|
<figure><img loading="lazy" src="media/library-selection.png" alt="Twelve photographs selected, with the selection bar along the foot of the grid"><figcaption>Twelve photographs selected, with the selection bar along the foot of the grid</figcaption></figure>
|
|
<p><code>Keywords</code> on that bar opens a sheet; type a word and press return, and it
|
|
is on every photograph selected. The list below the field is every keyword
|
|
the library has, ticked where the selection carries it.</p>
|
|
<figure><img loading="lazy" src="media/library-keywords.gif" alt="Keywording twelve frames"><figcaption>Keywording twelve frames</figcaption></figure>
|
|
<h3 id="collections">Collections</h3>
|
|
<p><code>+</code> at the head of the sidebar makes one. Drag a photograph — or the whole
|
|
selection — onto its row to file it there; click the row to see it. A
|
|
photograph can be in several, and the badge on its cell counts them.</p>
|
|
<figure><img loading="lazy" src="media/library-collections.gif" alt="Filing photographs in a collection by dragging them onto it"><figcaption>Filing photographs in a collection by dragging them onto it</figcaption></figure>
|
|
<p>Collections nest. Drag one onto another to put it inside; <code>+</code> with a
|
|
collection selected — or <code>New collection inside</code> from its menu — makes a
|
|
child. A parent shows everything its children hold, and its count says so.
|
|
Right-click a row (hold it, on a tablet) for the menu: rename, nest,
|
|
move back to the top level, keep it offline, delete.</p>
|
|
<figure><img loading="lazy" src="media/library-nesting.gif" alt="Making Trips, nesting Alps and New York inside it, and opening the parent"><figcaption>Making Trips, nesting Alps and New York inside it, and opening the parent</figcaption></figure>
|
|
<figure><img loading="lazy" src="media/library-nesting.png" alt="Trips showing both of its children's photographs"><figcaption>Trips showing both of its children's photographs</figcaption></figure>
|
|
<figure><img loading="lazy" src="media/library-collection-menu.png" alt="The menu on a collection"><figcaption>The menu on a collection</figcaption></figure>
|
|
<h2 id="developing-a-photograph">Developing a photograph</h2>
|
|
<p>Click a thumbnail to open it. The column on the right is every adjustment;
|
|
the strip at its head narrows it to one group.</p>
|
|
<figure><img loading="lazy" src="media/develop.png" alt="The develop view: the photograph, the histogram, and the adjustment column"><figcaption>The develop view: the photograph, the histogram, and the adjustment column</figcaption></figure>
|
|
<figure><img loading="lazy" src="media/develop-groups.gif" alt="Switching between the Optics, Light, Colour, Effects and Detail groups"><figcaption>Switching between the Optics, Light, Colour, Effects and Detail groups</figcaption></figure>
|
|
<h3 id="light">Light</h3>
|
|
<p>Exposure, contrast, highlights, shadows, blacks, whites and a tone curve.
|
|
Hold <code>Before</code> to see the photograph as it was.</p>
|
|
<figure><img loading="lazy" src="media/develop-light.gif" alt="Raising exposure, pulling the highlights, lifting the shadows, then holding Before"><figcaption>Raising exposure, pulling the highlights, lifting the shadows, then holding Before</figcaption></figure>
|
|
<h3 id="looking-closer">Looking closer</h3>
|
|
<p>Double-click for 1:1; drag to move about; double-click again to fit. The
|
|
wheel zooms to any amount in between.</p>
|
|
<figure><img loading="lazy" src="media/develop-zoom.gif" alt="Zooming to 1:1, panning, and back"><figcaption>Zooming to 1:1, panning, and back</figcaption></figure>
|
|
<h3 id="white-balance-from-the-photograph">White balance from the photograph</h3>
|
|
<p>Press <code>pick</code> in the White Balance group, then click something neutral —
|
|
a white wall, a grey card, the air conditioner here. The picker sets the
|
|
sliders from the photograph, not from where they were: below, the frame
|
|
is dragged cold first and one click puts it right. A blown highlight is
|
|
refused, since a clipped pixel has no colour left to balance.</p>
|
|
<figure><img loading="lazy" src="media/develop-wb.gif" alt="Cooling the frame with the slider, then picking a white air conditioner to set the white balance"><figcaption>Cooling the frame with the slider, then picking a white air conditioner to set the white balance</figcaption></figure>
|
|
<h3 id="composing">Composing</h3>
|
|
<p>Crop by dragging the frame's corners, straighten with the slider, lock a
|
|
ratio from the chips. <code>Done composing</code> returns to the photograph.</p>
|
|
<figure><img loading="lazy" src="media/compose.gif" alt="Cropping, straightening and choosing a ratio"><figcaption>Cropping, straightening and choosing a ratio</figcaption></figure>
|
|
<h3 id="local-adjustments">Local adjustments</h3>
|
|
<p><code>Local</code> in the rail turns the column into a mask stack. <code>Find subjects</code> runs a
|
|
segmentation model over the photograph; what it recognises appears as a list
|
|
of categories with how much of the frame each covers. Click one and it is a
|
|
mask — then every slider below edits only that region.</p>
|
|
<figure><img loading="lazy" src="media/local-categories.png" alt="What the model found in an urban scene: ground, architecture, sky, vegetation"><figcaption>What the model found in an urban scene: ground, architecture, sky, vegetation</figcaption></figure>
|
|
<figure><img loading="lazy" src="media/local-segment.png" alt="The sky chosen: tinted on the photograph, and the column now scoped to it"><figcaption>The sky chosen: tinted on the photograph, and the column now scoped to it</figcaption></figure>
|
|
<p>A mask is a stack of parts. Paint into it, subtract a gradient from it, grow
|
|
or shrink its edge, choose how it falls off. <code>Show masks as</code> draws the mask
|
|
tinted, as alpha, or as an outline; the eye on its row switches it off.</p>
|
|
<figure><img loading="lazy" src="media/local-paint.gif" alt="Looking at the mask three ways, painting into it, then growing its edge"><figcaption>Looking at the mask three ways, painting into it, then growing its edge</figcaption></figure>
|
|
<p>Linear and radial gradients, a tone range and a colour range are the other
|
|
ways to make one; each can be combined with any other.</p>
|
|
<h3 id="repair">Repair</h3>
|
|
<p><code>Repair</code> in the rail: click a mark and it is covered from a source DarkRoom
|
|
chooses beside it. Drag either circle to move it; the size, feather and
|
|
opacity are in the panel. Heal blends; clone copies.</p>
|
|
<figure><img loading="lazy" src="media/repair.gif" alt="Covering marks on a road"><figcaption>Covering marks on a road</figcaption></figure>
|
|
<h3 id="film">Film</h3>
|
|
<p>The <code>Film</code> chooser at the head of Adjust applies a spectral simulation of a
|
|
named stock; below it, the print exposure and push controls a film has and a
|
|
sensor does not.</p>
|
|
<figure><img loading="lazy" src="media/film.gif" alt="Choosing Velvia, then holding Before"><figcaption>Choosing Velvia, then holding Before</figcaption></figure>
|
|
<h3 id="history-snapshots-presets">History, snapshots, presets</h3>
|
|
<p>Every change is a step; <code>Undo</code> and the History panel walk them. <code>Snapshot</code>
|
|
keeps the current state under a name. <code>Presets…</code> saves the settings to
|
|
apply elsewhere, and imports <code>.xmp</code> from other applications.</p>
|
|
<figure><img loading="lazy" src="media/presets.png" alt="The presets sheet"><figcaption>The presets sheet</figcaption></figure>
|
|
<h2 id="merging-a-panorama">Merging a panorama</h2>
|
|
<p>Select the frames, then <code>Merge to panorama</code> from the selection bar. The
|
|
frames are read, aligned, and drawn on the suggested projection with each one
|
|
outlined where it landed — twelve hand-held portrait frames across an alpine
|
|
valley, here. Change the projection (a 150° sweep on a flat perspective is
|
|
what the middle of the film shows, and why cylindrical is suggested), ask for
|
|
the border to be filled rather than cropped, then <code>Merge</code>. The composite is
|
|
written beside its sources as a DNG and appears in the grid with the merge
|
|
as the first step in its history.</p>
|
|
<figure><img loading="lazy" src="media/panorama.gif" alt="Twelve frames aligned, the projections tried, and the border filled"><figcaption>Twelve frames aligned, the projections tried, and the border filled</figcaption></figure>
|
|
<figure><img loading="lazy" src="media/panorama-aligned.png" alt="The alignment on a cylinder, each frame outlined where it landed"><figcaption>The alignment on a cylinder, each frame outlined where it landed</figcaption></figure>
|
|
<figure><img loading="lazy" src="media/panorama-filled.png" alt="The same, with the ragged border filled by the model rather than cropped away"><figcaption>The same, with the ragged border filled by the model rather than cropped away</figcaption></figure>
|
|
<h2 id="export">Export</h2>
|
|
<p><code>Export</code> in the develop header, or <code>Export N</code> from a selection. Format,
|
|
size, colour space, sharpening, naming and where the file goes are in
|
|
Settings, and apply to every export until changed. An export with no folder
|
|
set is refused, and the header says so.</p>
|
|
<figure><img loading="lazy" src="media/settings-export.png" alt="Export defaults in Settings"><figcaption>Export defaults in Settings</figcaption></figure>
|
|
<h2 id="settings">Settings</h2>
|
|
<figure><img loading="lazy" src="media/settings.png" alt="The settings page: background activity, indexing, storage, display"><figcaption>The settings page: background activity, indexing, storage, display</figcaption></figure>
|
|
<p>Background activity with progress, thumbnail and face indexing, storage on
|
|
this device, display and colour, export defaults, what is written to XMP
|
|
sidecars, and a diagnostics bundle for a bug report.</p>
|
|
<h2 id="people">People</h2>
|
|
<p>Face detection and identity run over the library and group faces by person;
|
|
the <code>Identity</code> page is where suggestions are confirmed, rejected and split,
|
|
and <code>People</code> on the filter bar narrows the grid to someone. Not pictured
|
|
here, for the obvious reason — <a href="https://gitea.tourolle.paris/dtourolle/DarkRoom/src/branch/master/docs/dev/faces.md">faces.md</a> has the design.</p>
|
|
<h2 id="where-things-are-written-down">Where things are written down</h2>
|
|
<table><thead><tr><th>Feature</th><th>Design</th></tr></thead><tbody>
|
|
<tr><td>Local masks and segmentation</td><td><a href="https://gitea.tourolle.paris/dtourolle/DarkRoom/src/branch/master/docs/dev/segmentation.md">segmentation.md</a>, <a href="https://gitea.tourolle.paris/dtourolle/DarkRoom/src/branch/master/docs/dev/mask-editing.md">mask-editing.md</a></td></tr>
|
|
<tr><td>Repair</td><td><a href="https://gitea.tourolle.paris/dtourolle/DarkRoom/src/branch/master/docs/dev/spot-removal.md">spot-removal.md</a></td></tr>
|
|
<tr><td>Panorama</td><td><a href="https://gitea.tourolle.paris/dtourolle/DarkRoom/src/branch/master/docs/dev/panorama.md">panorama.md</a></td></tr>
|
|
<tr><td>Faces and identity</td><td><a href="https://gitea.tourolle.paris/dtourolle/DarkRoom/src/branch/master/docs/dev/faces.md">faces.md</a></td></tr>
|
|
<tr><td>Gestures, generated from the code</td><td><a href="https://gitea.tourolle.paris/dtourolle/DarkRoom/src/branch/master/docs/gestures.md">gestures.md</a></td></tr>
|
|
<tr><td>Navigation and layout</td><td><a href="https://gitea.tourolle.paris/dtourolle/DarkRoom/src/branch/master/docs/dev/ui-navigation.md">ui-navigation.md</a></td></tr>
|
|
<tr><td>Sync and storage</td><td><a href="https://gitea.tourolle.paris/dtourolle/DarkRoom/src/branch/master/docs/dev/storage.md">storage.md</a></td></tr>
|
|
</tbody></table>
|
|
<h2 id="how-this-page-is-made">How this page is made</h2>
|
|
<p><a href="https://gitea.tourolle.paris/dtourolle/DarkRoom/src/branch/master/tools/manual/README.md"><code>tools/manual/</code></a> drives the desktop build on
|
|
a private X server and records each scene; <code>record.sh <library></code> re-makes
|
|
every picture here. Run it after a change to the interface and commit what
|
|
changed. The pictures are in LFS.</p>
|
|
<p>Making it the first time turned up nine faults, each fixed in its own
|
|
commit before the pictures were taken: the folder picker could not choose
|
|
the top level, month headings overprinted each other, a category mask
|
|
widened the column off the window, the mask tint outlived its mode, the
|
|
first sync uploaded an empty thumbnail shard, a merge that ran out of GPU
|
|
memory left the page on Stop for ever, the export settings promised to ask
|
|
for a folder and did not, the keyword sheet sent its keys to the grid, and
|
|
an empty trash told you to check your library folder.</p>
|
|
</main>
|
|
</div>
|
|
</body>
|
|
</html>
|