summaryrefslogtreecommitdiff
path: root/docs/PLAN.md
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-07-12 17:00:43 +0300
committerPaul Buetow <paul@buetow.org>2026-07-12 17:00:43 +0300
commit78fa24a665002c01f0ea7ded8791c7e6af470305 (patch)
treeaaa5625337ff4cc97f40f855405c9091b3b3496a /docs/PLAN.md
Add ggaze implementation plan (docs/)
Diffstat (limited to 'docs/PLAN.md')
-rw-r--r--docs/PLAN.md230
1 files changed, 230 insertions, 0 deletions
diff --git a/docs/PLAN.md b/docs/PLAN.md
new file mode 100644
index 0000000..636f009
--- /dev/null
+++ b/docs/PLAN.md
@@ -0,0 +1,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. \ No newline at end of file