| **Desktop window controls/share substitute/drag-drop/templates UI** | **S4d-145..S4d-160 (accepted 2026-06-28)** widened the Desktop window through small one-file slices: watermark text editing, preview output, degree, color, opacity, h/v gaps, text size, tile mode, typeface/style, scrolling, share substitute, dropped-image input, and minimal templates UI. **S4d-157** added the selected no-new-dependency share substitute: only successful real saves update `lastSavedFile`, so Render & Save / Save as / Open image outputs can be shown in their folder or copied as a path; Preview temp output is deliberately excluded. **S4d-158** added Compose Desktop `dragAndDropTarget` over the existing Open-image save spine: dropped local image files are filtered by the same extension set, read on IO, saved through `DesktopWatermarkFlow.runSaveFlow`, and update `lastImage` plus successful-save `lastSavedFile`. **S4d-159/S4d-160** chose and added the minimal Templates section: Save current text / list / Use / Delete over `buildTemplateDatabase(dir)` + `TemplateRepository` + `TemplateEditor`; Use applies text through `WatermarkConfigEditor.updateText`. **S4d-198 (accepted 2026-06-28, commit `1f0019b5`)** then added **reactive preview**: a successful explicit editor/mode action (the 6 Apply fields, tile/typeface/style, the Use-text/Open-icon mode flips, template Use) **and** Open image / drop source changes now auto-refresh the on-screen preview via one `refreshPreview()` helper over the SAME `runSaveFlow` → `DesktopImageDecoder` temp-file spine the manual `Preview` button uses — bounded to explicit clicks/source events (not per-keystroke); preview writes only the temp file and never sets `lastSavedFile` (share-substitute stays real-save-bound); Render & Save / Save as stay real-save-only. This is functional Desktop product-flow coverage, not Android v2.10.0 UI/UX parity. **S4d-224 (accepted 2026-06-29, commit `c7959d0`)** then added Desktop template default seeding from the English Android seed DB via a `:shared` desktopMain resource + helper. **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`)** then added the minimal row-level template update action (Update → `TemplateEditor.update`, preserving id/creationDate). Remaining Desktop gates include batch and final screenshot/recording parity after release-grade migration. **S4d-215 (accepted 2026-06-28, commit `873dbcbd`, `DesktopWindow.kt` only)** made the interactive window persist its user state — watermark config, output prefs, and the templates Room DB — under the stable per-user dir `~/.easywatermark` (new same-file helper `resolveDesktopAppDataDir()`, matching the `CreateDataStore.desktop.kt` convention; `user.home`-null falls back to a repo-local `build/` dir with a warning, no crash) instead of the old repo-local `build/` dev paths. **Intentionally unchanged (do NOT "fix"):** `DesktopWatermarkFlow` build-local *defaults* and `Main.kt`'s headless/demo witness paths stay build-local by design, and the transient preview temp stays `build/s4d147-desktop-preview/preview.img`. Runtime-proven: a window launch creates `~/.easywatermark/ewm-db` (templates DB) and no `build/s4d160-desktop-templates`; the two DataStore preference files (`sp_water_mark_config`, `sp_water_mark_user_config`) are written **lazily on first GUI edit**, so they are diff-proven + left as a manual GUI acceptance check (type text → save template → restart-persistence), not an automated proof. Release-grade Desktop persistence correctness, **not** 1:1 parity; template edit-in-place has already landed (S4d-226); locale-aware seeding has already landed (S4d-225). **S4d-217 (accepted 2026-06-28, commit `ce677bb2`, `DesktopWindow.kt` only)** then routed the window's REAL save *output* off the repo-local `build/` default: image **drop**, **Render & Save sample**, and **Open image…** now write to a user output dir — `~/Pictures` if that directory exists, else `~/.easywatermark/output` (`File(resolveDesktopAppDataDir(), "output")`) (new same-file helper `resolveDesktopOutputDir()`, created if missing) — passing `outputFile = File(outputDir, DesktopSaveDecision.defaultOutputFileName(fmt))` with `fmt = userConfigRepo.userPreferences.first().outputFormat`. **Unchanged by design:** `Save as…` still writes the user-chosen dialog path; the preview temp stays `build/s4d147-desktop-preview/preview.img`; and `DesktopWatermarkFlow` defaults + `Main.kt` headless/demo witness stay build-local (the `--headless` run still wrote `build/s4d120-desktop-headless/watermarked.jpg`). The output filename is fixed by format (`watermarked.jpg`/`.png`), so repeated real saves **overwrite** — collision-avoiding names remain deferred. Proof = compile + headless witness + diff/branch routing (on this host `~/Pictures` exists → that branch); the GUI save **click was not automated**, so the file actually landing in the user dir is a coordinator manual acceptance check, not a runtime-automated proof. Release-grade Desktop save-location correctness, **not** 1:1 parity. **S4d-219 (accepted 2026-06-29, commit `97baa9d2`, `DesktopWindow.kt` only)** made the watermark **icon** persistence durable: "Open icon…" no longer persists the user's original absolute path — it copies the picked icon into app-private storage `~/.easywatermark/watermark_icons/icon.<ext>` (preserving the picked extension) and persists THAT copy path, so Image-mode no longer hard-fails (`DesktopWatermarkFlow.require(iconFile.isFile)`) when the source moves/renames/deletes or the config opens on another machine — parity with iOS `IosIconPersistence` (S4d-116) at the release-grade persistence level. **Copy-then-prune** (never clear-before-copy): re-picking the existing copy is a no-op (`selected.canonicalFile == copied.canonicalFile`); else copy source → `incoming.tmp` then `java.nio.file.Files.move(tmp → copied, ATOMIC_MOVE, REPLACE_EXISTING)`, and only AFTER `copied` exists prune the other files → bounded to one helper-owned icon; an unsupported atomic move or a failed read throws → the existing `Failed: …` path with the prior copy untouched (preview refresh + mode→Image preserved). GUI icon-pick **end-to-end proof was not automated**; evidence is compile + headless + diff + an offline algorithm sim, with a manual GUI acceptance path. Release-grade Desktop icon-persistence durability, **not** 1:1 parity. **S4d-221 (accepted 2026-06-29, commit `1fe8ab9e`)** extracted that S4d-219 copy-then-prune logic out of `DesktopWindow.kt` into a tested `shared/desktopMain` helper `DesktopIconPersistence.persistIcon(source, iconsDir)` (verbatim semantics: `~/.easywatermark/watermark_icons/icon.<ext>`, extension preserved blank→png, same-copy no-op, copy to `incoming.tmp` then atomic `Files.move(ATOMIC_MOVE, REPLACE_EXISTING)`, prune others only after the target exists, prior copy survives a failed source read); `DesktopWindow.kt` now just calls it before `editor.updateIcon(MediaRef(copied.absolutePath))`, with the UI filter/mode-flip/preview-refresh/status **unchanged**. The logic is now CI-covered by `DesktopIconPersistenceTest` (`:shared:desktopTest`, 5 tests / 0 failures) — parity with the tested iOS `IosIconPersistence`; the GUI icon-picker click remains a thin manual/UI-wiring acceptance path, not the proof of copy/prune correctness. **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)`, producing `watermarked.<ext>` if free else the smallest `watermarked_n.<ext>` suffix; tested in `:shared:desktopTest` (`DesktopSaveDecisionTest`, 6 new cases, 11/0 total). `Save as…`, preview temp, headless/demo, and `DesktopWatermarkFlow` defaults stay build-local by design. Release-grade Desktop save safety, **not** 1:1 parity. **S4d-228 (accepted 2026-06-29, commit `c833aea`)** then made image **drag/drop** multi-file: every supported dropped image is watermarked and saved **sequentially** (`DesktopSaveDecision.supportedImageFiles` → per-file `resolveUniqueOutputFile` → `runSaveFlow`; per-file failures continue the batch, a setup failure resets `busy` and shows a status), while **Open image** was still single-file and **Save as** stayed single-output. **S4d-229 (accepted 2026-06-29, commit `4c895aa`)** then made **Open image** multi-select too (native AWT `FileDialog.isMultipleMode = true` + `dialog.files`, filtered via the same `DesktopSaveDecision.supportedImageFiles`, saved through the same sequential resolve-after-write spine; single selection = one-item batch; real modal multi-select is a manual GUI residual), while **Save as** stays single-output. Remaining Desktop batch UX (folder batch, progress/cancel) and final screenshot/recording 1:1 parity remain deferred. |
0 commit comments