From 78fa24a665002c01f0ea7ded8791c7e6af470305 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Sun, 12 Jul 2026 17:00:43 +0300 Subject: Add ggaze implementation plan (docs/) --- docs/PLAN.md | 230 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 docs/PLAN.md (limited to 'docs/PLAN.md') 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 `/.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 `-enhanced.`; 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 `-enhanced.` 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 -- cgit v1.2.3