Skip to content

Commit b37a76f

Browse files
committed
Document Desktop Open image multi-select
1 parent 4c895aa commit b37a76f

5 files changed

Lines changed: 14 additions & 6 deletions

File tree

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ EasyWatermark (`me.rosuh.easywatermark`) — a privacy-focused Android app that
3939
- **Desktop app-entry `UserConfigRepository` wiring (S4d-80, accepted 2026-06-27, commit `3daa7c4`):** `desktopApp` `Main.kt` now constructs `UserConfigRepository(createUserConfigDataStore(dir = File("build/s4d80-desktop-userconfig")))` and proves read → `updateFormat(PNG)` + `updateCompressLevel(60)` → read via `runBlocking { userPreferences.first() }`, verified by `:desktopApp:run` (printed initial `(JPEG, 80)` then `(PNG, 60)`; a second run showed initial `(PNG, 60)` → on-disk persistence across runs). This is the **first app-entry (non-test) use of the common prefs repo** — an **app-level smoke/witness**, NOT the real Compose Desktop editor consuming prefs (C4) and NOT 1:1 UI/UX parity. `desktopApp/build.gradle.kts` declares the existing catalog aliases `libs.kotlin.coroutine.core` + `libs.datastore.preference` (NO new dependency version) because `:desktopApp` directly consumes `:shared`'s public-API types (`Flow`, `DataStore<Preferences>`), which `:shared` exposes as `implementation` (so they don't transit — `:app` declares them likewise). Desktop-only: Android/`:shared`/iOS/renderer/Room/Koin/goldens untouched.
4040
- **Desktop minimal product flow (S4d-119 readiness -> S4d-130, accepted 2026-06-27):** S4d-119 proved `:desktopApp` was still a CLI witness and selected the smallest non-speculative Desktop product-flow path. S4d-120 added desktop `createWaterMarkDataStore(...)`, a Desktop `WaterMarkRepository`/`WatermarkConfigEditor` roundtrip test, and a headless **open -> edit -> render -> save** flow writing `desktopApp/build/s4d120-desktop-headless/watermarked.png` (or CLI output path). S4d-121 added the minimal Compose Desktop `Window`: no-arg `:desktopApp:run` opens `EasyWatermark — Desktop (S4d-121)` and the sample button runs the same save spine; `--headless` remains the bounded automation path. **S4d-122 made Desktop text rendering and `composeOverRealImage` consume persisted `WaterMark.textColor` + `TextTypeface`** (color now defaults to shared amber `WaterMark.default.textColor`, and typeface maps to Compose `FontWeight`/`FontStyle`) and passed `TextPaintStyle` through. **S4d-123 completed shared drawStyle threading**, so `TextPaintStyle.Stroke` is raster-honored on Desktop/iOS Skiko; Android text production remains native. **S4d-124 selected the next Desktop slice, and S4d-125 added the first interactive file input:** the Desktop window now has an **"Open image..."** button using native JDK/AWT `FileDialog` to pick a real image, read bytes off the UI thread, and call the existing `DesktopWatermarkFlow.runSaveFlow(inputBytes, inputLabel)` spine. Cancel is a no-op; `--headless` stays unchanged. **S4d-126/S4d-130 split output-format safely:** S4d-127 added the Desktop encoder primitive (`ImageFormat.PNG` byte-identical `encodePng`; `ImageFormat.JPEG` ARGB -> opaque RGB flatten + JDK `ImageIO` quality), with `composeOverRealImage` still defaulting PNG for composer goldens. S4d-128 then wired the Desktop save spine to `UserConfigRepository.userPreferences.first()`: empty Desktop stores now follow shared/Android defaults (`JPEG/80`) and default output paths become format-aware (`watermarked.jpg` or `watermarked.png`), while PNG remains available when prefs select PNG. S4d-130 added the minimal window selector: two preset buttons (`JPEG / 80`, `PNG / 100`) write through the shared `OutputPrefsEditor` over the same store the save flow reads. **Still not a full Desktop editor and not 1:1 parity:** no drag/drop, interactive image preview, templates, share substitute, icon watermark, arbitrary quality selector, or final Android v2.10.0 UI parity.
4141
- **Desktop product-flow continuation (S4d-132 -> S4d-143b, accepted 2026-06-27/28):** S4d-132..S4d-137 completed the Desktop icon/source mini-epic at functional-code level: icon composition primitive, persisted Image-mode save branch, "Open icon...", "Use text watermark", and remembered source-image reuse. S4d-138/S4d-140 then added the release-code map, `DesktopSaveDecision` harness, and destination-only "Save as..." control. **S4d-141..S4d-143b closed the first templates persistence lane:** Desktop has an empty-store Room builder (`buildTemplateDatabase(dir)` with `BundledSQLiteDriver`), a `:desktopApp --headless` TemplateRepository/TemplateEditor witness, and a read-only proof that both Android seed DBs match generated Room identity hash `72366f557b971b39675d0f26cbc46e0a`. **S4d-224 (accepted 2026-06-29, commit `c7959d0`) implemented the Desktop English template seed** via a `:shared` desktopMain resource + `TemplateDatabaseSeeds.unpackDefaultTemplateSeed` + `buildTemplateDatabase(dir, seedFile)` (direct seed-file copy because Room KMP off-Android does not expose `createFromFile`/`createFromAsset`). **S4d-225 (accepted 2026-06-29, commit `875b105`) made that seeding locale-aware** (`ewm-db-ch.db` for `zh` JVM locales, `ewm-db-eng.db` otherwise). **S4d-226 (accepted 2026-06-29, commit `1fa945f`) added the minimal row-level template update action** (Update button → `TemplateEditor.update`, preserving id/creationDate, enabled only for nonblank current text). **Still not a full Desktop editor and not 1:1 parity:** no full Android-equivalent editor/polish, batch, arbitrary quality selector, or final Android v2.10.0 UI parity.
42-
- **Desktop window controls + share substitute + drag/drop + templates UI (S4d-145 -> S4d-160, accepted 2026-06-28):** Desktop window enrichment stayed one-file and functional: manual watermark text control, preview output, degree/color/opacity/gap/text-size/tile/typeface/style controls, scrolling, a no-new-dependency share substitute, dropped-image input, and a minimal Templates section. The share substitute tracks only the last successful real save from Render & Save / Save as / Open image / image drop; Preview temp output is deliberately not share state. Drag/drop uses Compose Desktop `dragAndDropTarget` + AWT file-list flavor and reuses the Open-image save spine. **S4d-159** chose templates UI over reactive preview because `Template.content` is just watermark text; reactive preview would reverse the deliberate manual-preview contract. **S4d-160** added Save current text / list / Use / Delete over `buildTemplateDatabase(dir)` + `TemplateRepository` + `TemplateEditor`; Use applies via `WatermarkConfigEditor.updateText`. **S4d-224 then added Desktop template default seeding** from the English Android seed DB via a `:shared` desktopMain resource + helper, **S4d-225 made it locale-aware** (`ewm-db-ch.db` for `zh` JVM locales, `ewm-db-eng.db` otherwise), and **S4d-226 added the row-level template update action** (Update button → `TemplateEditor.update`, preserving id/creationDate). **S4d-228 (accepted 2026-06-29, commit `c833aea`) then made image drag/drop multi-file:** the drop handler now watermarks and saves every supported dropped image **sequentially** (`DesktopSaveDecision.supportedImageFiles` → per-file `resolveUniqueOutputFile` → `runSaveFlow`; per-file failures continue the batch; a setup failure resets `busy`), while Open image stays single-file and Save as single-output. **Still not 1:1 parity:** full Desktop batch UX (Open-image multi-select, folder batch, progress/cancel) and final Android v2.10.0 screenshot/recording parity remain separate gates.
42+
- **Desktop window controls + share substitute + drag/drop + templates UI (S4d-145 -> S4d-160, accepted 2026-06-28):** Desktop window enrichment stayed one-file and functional: manual watermark text control, preview output, degree/color/opacity/gap/text-size/tile/typeface/style controls, scrolling, a no-new-dependency share substitute, dropped-image input, and a minimal Templates section. The share substitute tracks only the last successful real save from Render & Save / Save as / Open image / image drop; Preview temp output is deliberately not share state. Drag/drop uses Compose Desktop `dragAndDropTarget` + AWT file-list flavor and reuses the Open-image save spine. **S4d-159** chose templates UI over reactive preview because `Template.content` is just watermark text; reactive preview would reverse the deliberate manual-preview contract. **S4d-160** added Save current text / list / Use / Delete over `buildTemplateDatabase(dir)` + `TemplateRepository` + `TemplateEditor`; Use applies via `WatermarkConfigEditor.updateText`. **S4d-224 then added Desktop template default seeding** from the English Android seed DB via a `:shared` desktopMain resource + helper, **S4d-225 made it locale-aware** (`ewm-db-ch.db` for `zh` JVM locales, `ewm-db-eng.db` otherwise), and **S4d-226 added the row-level template update action** (Update button → `TemplateEditor.update`, preserving id/creationDate). **S4d-228 (accepted 2026-06-29, commit `c833aea`) then made image drag/drop multi-file:** the drop handler now watermarks and saves every supported dropped image **sequentially** (`DesktopSaveDecision.supportedImageFiles` → per-file `resolveUniqueOutputFile` → `runSaveFlow`; per-file failures continue the batch; a setup failure resets `busy`), while Open image stayed single-file and Save as single-output. **S4d-229 (accepted 2026-06-29, commit `4c895aa`) then made Open image multi-select too:** the picker uses native AWT `FileDialog.isMultipleMode = true` + `dialog.files`, filters through the same `DesktopSaveDecision.supportedImageFiles`, and saves the selection through the same sequential resolve-after-write spine (single selection = one-item batch; real modal multi-select is a manual GUI residual); Save as stays single-output. **Still not 1:1 parity:** remaining Desktop batch UX (folder batch, progress/cancel) and final Android v2.10.0 screenshot/recording parity remain separate gates.
4343
- **Desktop reactive preview (S4d-198, accepted 2026-06-28, commit `1f0019b5`, `DesktopWindow.kt` only):** the blind-edit "click Preview to see it" loop is **gone** — a successful explicit editor/mode action now **auto-refreshes** the on-screen preview. One extracted local helper `refreshPreview()` drives the 6 Apply fields (text/degree/color/opacity/gaps/text-size), tile REPEAT/CLAMP, typeface, text-style, the "Use text watermark" (→Text) and "Open icon…" (→Image) mode flips, **template Use**, and the **Open image… / image drop** source changes — all through the SAME `DesktopWatermarkFlow.runSaveFlow` → `DesktopImageDecoder` temp-file spine the manual `Preview` button uses (which now just calls the helper; the duplicated inline render was removed). **Bounded** to explicit clicks / source-load events (NOT per keystroke — `onValueChange` untouched). Preview writes ONLY `build/s4d147-desktop-preview/preview.img` and **never** sets `lastSavedFile` (share-substitute stays real-save-bound); a refresh failure keeps the last good preview. "Render & Save sample" / "Save as…" remain real-save-only; Open image / drop still set `lastImage`+`lastSavedFile` from their real save, with the added refresh gated on real-save success. Functional Desktop product-flow, **not** 1:1 parity. (S4d-198b is the docs closeout.)
4444
- **Desktop persistence/output/icon now use user storage, with the icon copy in a tested `shared/desktopMain` helper (`:shared:desktopTest`) (S4d-215 -> S4d-221, accepted 2026-06-28/29):** the interactive Desktop window persists its state under the stable per-user dir `~/.easywatermark` (watermark config + output prefs + templates Room DB — S4d-215), writes real saves (drop / Render & Save / Open image) to a user output dir (`~/Pictures` else `~/.easywatermark/output` — S4d-217), and on "Open icon…" copies the picked icon into app-private `~/.easywatermark/watermark_icons/icon.<ext>` persisting THAT copy path (S4d-219) — instead of repo-local `build/` dev paths or the user's original icon path. **S4d-221 (commit `1fe8ab9e`) extracted the S4d-219 copy-then-prune logic out of `DesktopWindow.kt` into a tested `shared/desktopMain` helper `DesktopIconPersistence.persistIcon(source, iconsDir)`** (verbatim semantics — same-copy no-op via `canonicalFile`, copy -> `incoming.tmp` -> atomic `Files.move(ATOMIC_MOVE, REPLACE_EXISTING)`, prune others only after the target exists -> bounded one file, throws so the prior copy survives a failed source read), now **CI-covered** by `DesktopIconPersistenceTest` (`:shared:desktopTest`, 5/0) — parity with the tested iOS `IosIconPersistence` (S4d-116). `DesktopWindow.kt` now just wires the UI to the helper (`IMAGE_EXTENSIONS` filter, mode→Image flip, S4d-198 preview refresh, and `try/catch → "Failed: …"` status unchanged). **Intentionally unchanged (do NOT "fix"):** `DesktopWatermarkFlow` build-local defaults + `Main.kt` headless/demo witness + the preview temp stay build-local by design. **S4d-222 (accepted 2026-06-29, commit `0eaafdd5`)** added collision-avoiding default save filenames: the three default-name real-save sites (image drop, Render & Save sample, Open image) now call `DesktopSaveDecision.resolveUniqueOutputFile(outputDir, fmt)` (shared `desktopMain`), producing `watermarked.<ext>` if free else the smallest `watermarked_n.<ext>` suffix; `Save as…` (user-chosen path), preview temp, headless/demo, and `DesktopWatermarkFlow` defaults stay build-local by design. Tested in `:shared:desktopTest` (`DesktopSaveDecisionTest`, 6 new cases, 11/0 total). Release-grade Desktop save safety, **not** Android v2.10.0 1:1 parity; the GUI icon-picker click stays a thin manual/UI-wiring acceptance path (the copy/prune correctness is now unit-tested).
4545
- **Desktop release hygiene + packaging proof (S4d-161 -> S4d-166, accepted 2026-06-28):** S4d-161 (read-only decision) chose release-grade hygiene + a packaging proof over speculative UI widening. **S4d-162** (commit `bb5e770f`, `DesktopWindow.kt` only) made the window title release-facing (`EasyWatermark — Desktop`, dropping the `(S4d-121)` dev marker) and disabled the Templates "Save current text" button when the watermark text is blank. **S4d-163** (commit `ccc9f3f7`, `desktopApp/build.gradle.kts` only) replaced the plain Gradle `application` plugin with the Compose Desktop `compose.desktop { application { mainClass; nativeDistributions { packageName = "EasyWatermark"; packageVersion = "1.0.0" } } }` DSL, so the packaging tasks now exist (`createDistributable`/`runDistributable`/`packageDistributionForCurrentOS`) and `createDistributable` builds an **unsigned `.app` image** (native launcher + `Info.plist` + bundled JRE + app jars, under `desktopApp/build/compose/binaries/main/app/`). The `run` task moved into the Compose DSL and `:desktopApp:run --args='--headless'` still works (witnesses unchanged). **JDK caveat:** `createDistributable` **fails** under the jenv/Homebrew OpenJDK because Compose Desktop's `checkRuntime` rejects Homebrew vendors (CMP issue #3107); it **succeeds** with the already-installed Amazon Corretto 17 (`/Library/Java/JavaVirtualMachines/amazon-corretto-17.jdk/Contents/Home`). The accepted path is to use a **supported packaging JDK** — do **NOT** add `compose.desktop.packaging.checkJdkVendor=false`. **S4d-166** adds `.github/workflows/desktop_packaging.yml`, a narrow `workflow_dispatch` + path-filtered PR job using Ubuntu + Zulu 17 that runs `:desktopApp:run --args='--headless'` before `:desktopApp:createDistributable`; the green Actions run is the proof that Zulu satisfies the vendor guard. **Overclaim guard:** unsigned app-image proof/workflow ONLY — no installer formats (DMG/PKG/MSI/DEB), signing/notarization, app icon, real product-version source yet (`packageVersion = "1.0.0"` is a placeholder), macOS packaging, or Android v2.10.0 1:1 UI/UX parity.

0 commit comments

Comments
 (0)