diff --git a/.github/workflows/jdeploy.yml b/.github/workflows/jdeploy.yml
new file mode 100644
index 0000000..876fb68
--- /dev/null
+++ b/.github/workflows/jdeploy.yml
@@ -0,0 +1,64 @@
+# Builds the native installers for Windows, macOS and Linux and publishes them as GitHub release assets.
+#
+# Two channels, distinguished by what was pushed:
+# * a `v*` tag -> a versioned release; the tag name minus the leading `v` becomes the app version.
+# * a `*-snapshot` branch -> a rolling prerelease named after the branch, replaced on every push.
+#
+# Auto-update needs nothing else from us. The jDeploy action writes a package-info.json to a `jdeploy` tag in
+# this repository, and the native launcher installed on a user's machine reads it on startup to notice a newer
+# release. That only works while the repository is public — a private repository needs a separate public
+# release-only repository passed as `target_repository`.
+name: jdeploy
+
+on:
+ push:
+ tags:
+ - 'v*'
+ branches:
+ - '*-snapshot'
+ # Lets the pipeline be exercised without cutting a tag. Running it on a branch produces the snapshot
+ # prerelease for that branch, exactly as a push would.
+ workflow_dispatch:
+
+# The action rewrites a shared `jdeploy` tag, so two runs must never overlap. Queued rather than cancelled:
+# cancelling mid-upload is what leaves that tag half written.
+concurrency:
+ group: jdeploy-release-${{ github.repository }}
+ cancel-in-progress: false
+
+jobs:
+ bundle:
+ runs-on: ubuntu-latest
+ permissions:
+ # Needed to create the release and to push the `jdeploy` tag that carries package-info.json.
+ contents: write
+
+ steps:
+ - uses: actions/checkout@v4
+
+ # Zulu rather than Temurin: the JavaFX dependencies are pinned to a Zulu FX build locally, and Azul
+ # publishes non-LTS feature releases, which Java 26 is.
+ - name: Set up JDK 26
+ uses: actions/setup-java@v4
+ with:
+ java-version: '26'
+ distribution: 'zulu'
+ cache: maven
+
+ # The -Pjdeploy profile synchronises package.json by shelling out to `npm pkg set`, so Node has to be on
+ # PATH before Maven runs, not just before the jDeploy action.
+ - name: Set up Node
+ uses: actions/setup-node@v4
+ with:
+ node-version: '22'
+
+ - name: Build
+ run: ./mvnw -B --no-transfer-progress -Pjdeploy package
+
+ - name: Build app installer bundles
+ uses: shannah/jdeploy@v6.1.5
+ with:
+ github_token: ${{ github.token }}
+ # Kept in step with jdeploy.cli.version in pom.xml. The action's own default lags its release tag.
+ jdeploy_version: '6.1.5'
+
diff --git a/.gitignore b/.gitignore
index b683da0..65a6dfd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -42,3 +42,9 @@ logs/
*.mv.db
*.trace.db
+### jDeploy
+# Generated by the jDeploy CLI: the npm-shaped launcher bundle and the installer/release staging directory.
+jdeploy-bundle/
+jdeploy/
+node_modules/
+
diff --git a/CLAUDE.md b/CLAUDE.md
index eda6a17..6800bf9 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -11,6 +11,35 @@ This project is a desktop application for images / pictures management. Like col
* For packaging, use spring boot generated jar, and jdeploy to create OS package (jdk/jre included)
* Use lombok when applicable (Constructor and getter / setter)
+## Packaging & distribution (jDeploy)
+
+Native installers with a bundled JRE are produced by jDeploy, driven from two files: `package.json` at the
+repository root (the descriptor) and `.github/workflows/jdeploy.yml` (the release pipeline).
+
+* **Entry point.** jDeploy has no main-class setting — it runs the jar. So the entry point is declared once, as
+ `spring-boot-maven-plugin`'s `${main.class}`, which lands in the repackaged jar's
+ manifest as `Start-Class`. `jdeploy.jar` in `package.json` points at `target/pholio.jar`; `` is
+ version-free precisely so that path never needs updating.
+* **Runtime.** `jdeploy.javaVersion: "26"` with `jdeploy.javafx: true` provisions an Azul Zulu FX **JRE** per
+ platform. `windows-arm64` is deliberately absent from `downloadPage.platforms`: Azul publishes no FX build for
+ it at 26, so listing it would produce an installer that cannot start.
+* **Syncing the descriptor.** `mvn -Pjdeploy package` runs `jdeploy-maven-plugin:sync-package-json`, which
+ copies version, name, description, title, jar path, Java version and the JavaFX flag from the POM into
+ `package.json`. It works by shelling out to `npm pkg set`, which is why it sits behind a profile — a plain
+ `mvn package` must not require Node. Every other key in `package.json` is hand-maintained.
+* **Releasing.** Push a `v` tag (the leading `v` is stripped to form the app version); the workflow
+ builds the bundles and attaches them to that release. Pushing a `*-snapshot` branch publishes a rolling
+ prerelease instead. Branch and tag names must be at most 16 characters of `[A-Za-z0-9._-]` — the jDeploy
+ action silently skips anything else.
+* **Auto-update.** Handled entirely by the native launcher: it reads a `package-info.json` that the action
+ writes to a `jdeploy` tag in this repository, and picks up a newer release on next start. This requires the
+ repository to stay public; a private one needs a separate public release repository passed to the action as
+ `target_repository`.
+* **Do not add `documentTypes`, `urlSchemes` or `singleton` without changing the launcher first.** jDeploy
+ passes opened files and URIs to `main`, and `LaunchOptions` reads any unrecognised argument as a subcommand
+ and switches to `HEADLESS`. File associations would therefore make a double-clicked photo start Pholio with
+ no window.
+
## Testing
* for unit test use AssertJ with SoftAssertion
diff --git a/icon.png b/icon.png
new file mode 100644
index 0000000..3669a2e
Binary files /dev/null and b/icon.png differ
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..e07f9c3
--- /dev/null
+++ b/package.json
@@ -0,0 +1,35 @@
+{
+ "name": "pholio",
+ "version": "0.1.0-SNAPSHOT",
+ "description": "High-volume desktop photo and media library manager",
+ "author": "Christophe Lallement",
+ "repository": "https://github.com/Imag-In/pholio",
+ "bin": {
+ "pholio": "jdeploy-bundle/jdeploy.js"
+ },
+ "files": [
+ "jdeploy-bundle"
+ ],
+ "preferGlobal": true,
+ "jdeploy": {
+ "jar": "target/pholio.jar",
+ "javaVersion": "26",
+ "javafx": true,
+ "jdk": false,
+ "title": "Pholio",
+ "args": [
+ "-Djdeploy.file.limit=8192"
+ ],
+ "downloadPage": {
+ "platforms": [
+ "mac-arm64",
+ "mac-x64",
+ "windows-x64",
+ "linux-x64",
+ "linux-arm64",
+ "debian-x64",
+ "debian-arm64"
+ ]
+ }
+ }
+}
diff --git a/pom.xml b/pom.xml
index 7ecc6a1..9776e67 100644
--- a/pom.xml
+++ b/pom.xml
@@ -43,6 +43,16 @@
io.pholio.PholioApplication
+
+
+ 1.0.8
+
+ 6.1.5
@@ -162,10 +172,22 @@
+
+ pholio
org.springframework.boot
spring-boot-maven-plugin
+
${main.class}
@@ -188,4 +210,48 @@
+
+
+
+
+ jdeploy
+
+
+
+ ca.weblite
+ jdeploy-maven-plugin
+ ${jdeploy-maven-plugin.version}
+
+ ${jdeploy.cli.version}
+
+
+
+ sync-package-json
+
+ package
+
+ sync-package-json
+
+
+
+
+
+
+
+