summaryrefslogtreecommitdiff
path: root/docs/PLAN.md
blob: 636f009394c16849eba97bec3b86bb3f30ed6772 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# ggaze — Project Plan (living document)

**GNOME Gaze** — a small, fast, native image viewer for Fedora
Linux, written in C with GTK4. Its job: quickly preview a folder of pictures
downloaded from a camera, cull the rejects, move on.

This file is the running tracker. The detailed design lives in the sibling
docs; this page keeps the overview, the decisions log, and the status of each
milestone in one place. Update it as we go.

---

## Elevator pitch

`ggaze ~/Downloads/Camera/IMG_0001.jpg` opens the folder as a thumbnail grid,
`Enter` drops into the large view, `h`/`l` (or `←`/`→`) scrubs through the shoot, `i` shows
EXIF, `d` bins a reject into `./Trash` (undoable), `D` deletes it outright, `v` marks
keepers, `m` then `1` ships them to "irregular ninja", `e` opens a keeper in GIMP,
`!` runs `usbimport` to pull new shots, `a` previews a quick GEGL auto-fix, `Esc` returns to the grid, `q` quits.
No library, no database, no sidecars.

## Two views

- **Grid** — thumbnail overview of the whole folder, keyboard-navigable;
  thumbnails resize with `+`/`-` and the grid reflows to fit.
- **Large** — single picture with zoom/pan, fullscreen, slideshow.

Switch with `Enter` (grid→large) and `Esc`/`Backspace` (large→grid), or `t` to
toggle. The cursor stays in sync across the switch.

## Opening files & folders

- `ggaze file.jpg` opens the file (large view, parent folder as navigator);
  `ggaze dir/` opens the folder in the grid.
- Drag-and-drop a file or folder onto the window to open it.
- `o` opens a file/folder dialog.

## Folder monitoring

- `GFileMonitor` watches the current dir; external adds/deletes/moves
  refresh the grid live (debounced). A removed current file falls back to the
  nearest; `r` still does a manual reload.

## Deletion model

- `d` → move to `<folder>/.Trash/` (local, recoverable, undoable via `u`).
- `D` → permanent delete (unlink, no undo).
- `./Trash` lives with the shoot; empty it via the menu or from a shell.
- Trashed/deleted items stay listed but dimmed (hide toggle in settings).

## Moving & marks

- `v` / `V` / `Ctrl+a` mark pictures; marks persist across views.
- `m` → popup of **configured** destinations, each with an auto-assigned
  hotkey (`1`, `2`, …). Press the hotkey to move the marked set there.
- Destinations = ordered `a(ss)` list of name → path pairs in GSettings.
- `u` undoes the last `d` or `m`.

## External programs

- `e` → popup of configured programs (auto-assigned hotkeys), launches the
  current image in the chosen one (detached `GSubprocess`, `%f` = path).
- `editors` = ordered `a(ss)` list of name → command pairs in GSettings.

## Shell scripts

- `!` → popup of configured scripts (auto-assigned hotkeys), runs the chosen
  one **asynchronously** via `/bin/sh -c` (`%f` = image, `%d` = folder).
- On exit, ggaze rescans the directory (e.g. `usbimport` adds files).
- `scripts` = ordered `a(ss)` list of name → command pairs in GSettings.

## Quick enhance (GEGL, optional)

- `a` → popup of enhance presets (auto-assigned hotkeys); applies a GEGL graph
  as a **non-destructive live preview**; press again / `Esc` to turn off.
- `s` / menu *Save enhanced copy…* writes `<name>-enhanced.<ext>`; original
  untouched. **No auto-save** — navigating away from an un-exported preview
  prompts Save/Discard/Cancel.
- `enhance-presets` = ordered `a(ss)` list of `(name, gegl-graph)` pairs.
- GEGL is an optional meson feature; without it, ggaze is a plain fast viewer.
  GEGL also brings ICC color management. See [gegl.md](gegl.md).

## Crop, straighten & rotate (GEGL, optional)

- `c` crop (adjustable rect, aspect presets), `R` straighten (horizon drag /
  angle nudge), and `[`/`]` rotate 90° CCW/CW — non-destructive, same preview
  graph + `s` to export a copy.
- Hold `Space` to compare original vs modified (before/after); release returns
  to the modified preview.
- Large view only; GEGL required.

## Copy to clipboard

- `Ctrl+c` / menu *Copy*: no marks → current image as PNG pixels; marks →
  marked files as `text/uri-list`. Paste into Katogram/GIMP/file managers
  (like gthumb).

## Reachability (keyboard + GUI)

Every GUI element has a hotkey, and it's shown on the element (menu items,
tooltips, popup entries). Conversely, every action is also reachable by
mouse — header-bar buttons for the common ones, the `F10` menu for the rest.
`?` lists all shortcuts. Neither keyboard nor mouse is a fallback.

## Quality & testing

- ≥80% unit-test coverage on plain-C modules (gcov/lcov), gated in CI.
- Run the `auditing-code-quality` skill at each milestone boundary and before
  release — for C: `c-best-practices` + `find-code-bugs` + `solid-principles`
  + `beyond-solid-principles`, findings tracked as tasks via
  `agent-task-management`. Fix all HIGH/MEDIUM before release.

---

## Tech at a glance

| Concern      | Choice                          |
|--------------|---------------------------------|
| Language     | C11                             |
| UI           | GTK4 + libadwaita (decided)    |
| Async/objects| GLib / GObject / GTask          |
| Config       | GSettings (`org.buetow.ggaze`) |
| Build        | Meson + Ninja                   |
| Decode       | GdkPixbuf fallback + libjxl/libavif/libheif (feature options) |
| Image proc.  | GEGL + babl (optional, feature-gated) — enhance, ICC, export copy |
| Thumbnails   | freedesktop TMS, `~/.cache/thumbnails/` |
| Packaging    | Fedora RPM + AppStream (Flatpak later) |
| Testing      | `meson test` + gcov/lcov; ≥80% on plain-C modules |
| Quality audit | `auditing-code-quality` skill at milestones (C: c-best-practices + find-code-bugs + SOLID + beyond-SOLID) |

See [tech-stack.md](tech-stack.md).

---

## Milestones

| M   | Title                      | Status   | Notes |
|-----|----------------------------|----------|-------|
| M0  | Skeleton (app + empty window) | not started | Meson, GtkApplication, file/folder arg, --version/--help |
| M1  | Show one image (zoom/pan)    | not started | custom viewer widget, GdkPixbuf backend, EXIF orientation on load |
| M2  | Walk the directory          | not started | navigator, `h`/`l` prev/next, folder arg + drag-drop open, `GFileMonitor` auto-refresh, wrap, header counter |
| M3  | Responsive + prefetch       | not started | GTask decode, 2–3 slot LRU, cancel-in-flight |
| M4  | Fullscreen + slideshow + info | not started | `f`, `s`, `i` EXIF overlay |
| M5  | Modern formats             | not started | JXL/AVIF/HEIF backends, animated GIF/WebP |
| M6  | Progressive low-res preview | not started | libjpeg-turbo early low-res scan |
| M7  | Thumbnail cache + grid view | not started | TMS cache + `gridview`, dim trashed items |
| M8  | Selection, move, open-external & scripts | not started | marks, `m`/`e`/`!` popups, `mover`/`opener`/`runner`, `Ctrl+c` clipboard, destinations+editors+scripts `a(ss)`, prefs |
| M9  | GEGL quick-enhance, crop/straighten/rotate (optional) | not started | `enhancer`, `a`/`c`/`R`/`[`/`]` tools, hold-`Space` compare, non-destructive preview, `s` save copy (no auto-save, prompt on navigate), ICC via GEGL |
| M10 | Polish & packaging         | not started | AppStream, RPM, man page, settings, keyboard-completeness audit, ≥80% coverage gate |

Later / maybe: configurable keybindings, recursive walking, RAW embedded
preview, burst grouping, RAW+JPEG pair hiding, GEGL transforms/artistic.

See [roadmap.md](roadmap.md).

---

## Decisions log

Decisions made during planning. Newest first.

| # | Date       | Decision                                                                 | Rationale                         |
|---|------------|--------------------------------------------------------------------------|-----------------------------------|
| 28 | 2026-07-12 | Folder monitoring via `GFileMonitor` (GIO): external adds/deletes/moves refresh the grid live (debounced); removed current file falls back to nearest. | New shots from usbimport/etc. appear without manual reload. |
| 29 | 2026-07-12 | **UI toolkit: libadwaita** (was A). GNOME-native header bar/dark viewer/system theme; no theming overrides. | Native Fedora look per the gthumb-but-KISS direction. |
| 30 | 2026-07-12 | **App ID `org.buetow.ggaze`** (was B). | Matches buetow.org domain. |
| 31 | 2026-07-12 | **Custom viewer widget** (was L), not `GtkPicture`. | Cursor-centered zoom, pan clamp, hold-`Space` compare, tool overlays. |
| 32 | 2026-07-12 | **Single instance** (was E); new `open`/drop replaces current folder+image. | Standard GNOME behavior; no window sprawl. |
| 33 | 2026-07-12 | **Camera specifics** (was K): burst grouping deferred to "later"; hide RAW sidecars by default (toggle to reveal); default sort = filename (EXIF capture-time as a menu option); import folder = just a path. | KISS first; culling-friendly grid; filename ≈ shot order. |
| 34 | 2026-07-12 | **GEGL integration** (was U): optional meson feature; built-in presets programmatic, user presets as `gegl-graph` text; apply only when settled (not during scrub); "enhanced" badge; export `<stem>-enhanced.<ext>` same dir, collision suffix `-1`; dirty-prompt fires on `d`/`D`/`m`; no gegl-gtk. | Keeps core fast; KISS preset UI; explicit save. |
| 35 | 2026-07-12 | **GEGL compose order** (was W): load → enhance(color) → rotate → straighten → crop → export; straighten auto-crop default on. | Crop the final framed image; remove rotated corners. |
| 36 | 2026-07-12 | **Enhance presets** (was X+#4): one active color preset (replace); crop/straighten/rotate stack on top; "reset preview" clears all; combine color presets via one `gegl-graph` entry; curves via `gegl:contrast-curve`; no fine-adjust nudging in v1. | Predictable + KISS; combinable via graph text. |
| 37 | 2026-07-12 | **Milestone Leans locked**: C GdkPixbuf-first; G color via GEGL/sRGB-else; H scroll=zoom + `pan-when-zoomed` mode; M GSettings `a(ss)` destinations; N move+suffix; O 1-9,0,a-z (cap 36); P one-level undo; Q marks path-based/survive re-sort/clear on trash; R raw cmd+%f+GSubprocess; S /bin/sh -c single-quote+rescan; T 64-512px ±32px custom bucket; V PNG+uri-list union provider; Y EXIF normalize-to-identity, export tag=1; Z folder→grid/file→parent/many→first; AA 250ms debounce nearest; F flat default; J RPM+AppStream first. | Working defaults confirmed at each milestone. |
| 40 | 2026-07-12 | Run the `auditing-code-quality` skill at each milestone + before release (C-adapted: `c-best-practices` + `find-code-bugs` + `solid-principles` + `beyond-solid-principles`, tracked via `agent-task-management`); fix all HIGH/MEDIUM findings. | Structured, well-factored project; catch defects + design smells early. |
| 39 | 2026-07-12 | **≥80% unit-test coverage** on plain-C modules (navigator/detect/thumbnail/mover/opener/runner/enhancer/trash/settings) via gcov/lcov with a CI coverage gate; GTK widgets get smoke tests. | Quality floor; refactor safely. |
| 38 | 2026-07-12 | **Gaps folded in**: mark count in header; window-geometry persistence; CLI `--version`/`--help` (+`--sort`/`--view` later); bulk `D` confirm >1; `scroll-behavior` = zoom/pan-when-zoomed/navigate; `Ctrl+c` copies the *displayed* image; export same ext + JPEG q95 (lossless later); `e` opens the *original* file; go-to-# skipped for v1. | Plan now complete before implementation. |
| 27 | 2026-07-12 | Open a folder arg (`ggaze dir/` → grid) and accept drag-and-drop of a file/folder onto the window; `o` dialog allows folders too. | Match gthumb flexibility; open anything from CLI, file manager, or drag. |
| 26 | 2026-07-12 | Honor EXIF Orientation on load (upright display); manual rotate/straighten compose on top; export resets the tag to normal. | Portrait/tilted camera shots display correctly without manual fix. |
| 25 | 2026-07-12 | Expand GEGL enhance presets: brightness, contrast, saturation, warm/cool, white balance, shadows/highlights, levels, clarity + artistic (B&W/sepia/vignette/softglow); strength tunable via `enhance-presets` gegl-graph text (no slider UI). | Cover the common quick fixes as one-shot presets; keep KISS. |
| 24 | 2026-07-12 | Compare moved to `Space` (hold); zoom-fit folded into `0` (toggle fit/100%); `\` freed. | `Space` is the comfortable hold-to-compare key; one zoom toggle key. |
| 23 | 2026-07-12 | Hold `Space` to flash the original image; release to return to the modified preview (before/after compare) to decide whether to `s` save. | Judge edits before saving; no accidental keeps. |
| 22 | 2026-07-12 | Add 90° rotation (`[`/`]`, `gegl:rotate-on-center`) as a one-shot non-destructive GEGL transform; `s` exports a copy. | Quick orientation fix; original untouched. |
| 21 | 2026-07-12 | Add crop (`c`, `gegl:crop`) and straighten (`R`, `gegl:rotate`) as non-destructive interactive GEGL tools; `s` exports a copy. | Level horizons and frame shots without leaving ggaze; original untouched. |
| 20 | 2026-07-12 | `Ctrl+c` copies to clipboard: current image as PNG pixels, or marked files as `text/uri-list` (paste into Katogram/GIMP/etc. like gthumb). | Quick hand-off of an image/selection to other apps. |
| 19 | 2026-07-12 | Layout & design reminiscent of gthumb (header bar, thumbnail grid, full-window viewer) but KISS: no folder sidebar, no catalogs/tags, no status-bar clutter, no batch/edit toolbars. | Familiar GNOME image-app feel without the weight. |
| 18 | 2026-07-12 | No auto-save of image changes. `s` saves an enhanced copy manually; navigating away (or quitting) from an un-exported enhance preview prompts Save/Discard/Cancel. Slideshow moved to `S` to free `s` for save. | Originals never silently modified; explicit consent. |
| 17 | 2026-07-12 | Plan GEGL (optional, feature-gated) for quick non-destructive enhance (`a` popup of presets) + export copy (`s`); also brings ICC color mgmt and format save. | Judge/fix keepers in-app without a full editor; original never modified. |
| 16 | 2026-07-12 | Grid thumbnails are resizable (`+`/`-`); grid auto-reflows to fit; size persisted in `thumbnail-size` GSettings. | Overview at a glance vs. detail, ad hoc. |
| 15 | 2026-07-12 | `!` runs configurable shell scripts asynchronously via `/bin/sh -c` (`%f`/`%d`), rescan dir on exit; `scripts` `a(ss)` settings. | Run usbimport etc. from within ggaze; pick up new files. |
| 14 | 2026-07-12 | `e` opens the current image in a configurable external program via a popup (auto-assigned hotkeys); `editors` `a(ss)` settings; detached GSubprocess launch. | Hand off to GIMP/identify/etc. without leaving ggaze. |
| 13 | 2026-07-12 | UI is self-documenting: hotkeys shown on elements, tooltips, `?` overlay, badges/counters/toasts narrate state. | Discoverable without a manual. |
| 12 | 2026-07-12 | Every action is also reachable through the GUI (button/menu), not only by hotkey. | Keyboard and mouse are equally first-class. |
| 11 | 2026-07-12 | Hotkeys are shown on the elements themselves (menu items, tooltips, popup entries). | Discoverability; no hidden keys. |
| 10 | 2026-07-12 | Every GUI element has a hotkey / is keyboard-reachable; full mnemonics. | Fully keyboard-driven, no mouse needed. |
| 9 | 2026-07-12 | `m` moves marked pictures to a configured destination via a popup with auto-assigned hotkeys; destinations user-configurable; multi-select via marks. | Fast triage of camera dumps into named folders. |
| 8 | 2026-07-12 | Follow the c-best-practices skill; conventions pinned in coding-conventions.md. | Consistent C style across the project. |
| 7 | 2026-07-12 | vi-style nav (`h`/`l` prev/next) plus cursor keys (`←`/`→`); `j`/`k` pan when zoomed. | vi users + cursor fallback. |
| 6 | 2026-07-12 | `D` permanently deletes; no undo.                                       | Fast path for obvious garbage.    |
| 5 | 2026-07-12 | `d` moves to a local `./Trash` folder, not the system trash; undoable.   | Trash travels with the shoot; easy to inspect/empty. |
| 4 | 2026-07-12 | Two views: thumbnail grid + large single-picture, in one window.         | Overview + detail, both keyboard-driven. |
| 3 | 2026-07-12 | Aim = quickly preview camera downloads and cull them.                    | Narrows scope to a culling viewer. |
| 2 | 2026-07-12 | Stack: C + GTK4, native Fedora/GNOME look.                               | User requirement.                 |
| 1 | 2026-07-12 | Planning docs only first; no implementation yet.                         | Get the design straight first.    |

Most open questions are now **decided** (see decisions #29–#38); remaining
working Leans and per-milestone details live in
[open-questions.md](open-questions.md).

---

## Documents index

- [README.md](README.md) — overview + working assumptions
- [goals-and-scope.md](goals-and-scope.md) — goals, non-goals, target workflow
- [architecture.md](architecture.md) — modules, data flow, concurrency
- [ui-and-interactions.md](ui-and-interactions.md) — views, keybindings, gestures, grid
- [tech-stack.md](tech-stack.md) — libraries, decode backends, deps
- [coding-conventions.md](coding-conventions.md) — C style (c-best-practices skill)
- [gegl.md](gegl.md) — GEGL quick-enhance & image-processing plan
- [roadmap.md](roadmap.md) — milestone detail
- [open-questions.md](open-questions.md) — undecided items
- `PLAN.md` — this file (tracker)

## How to use this file

- Flip milestone **Status** as work starts/finishes.
- Add a row to the **Decisions log** whenever something is settled (and move
  the matching item out of `open-questions.md`).
- Keep the elevator pitch and tables in sync with the detail docs; if they
  disagree, the detail docs are authoritative and this file gets updated.