From fa3c30fdacb626f5de1c2aac83b5139eb8239e30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Kr=C3=BChlmann?= Date: Thu, 1 Jan 2026 17:09:43 +0100 Subject: [PATCH] Add download --- pom.xml | 2 +- .../imgfloat/controller/ViewController.java | 8 ++- .../imgfloat/service/VersionService.java | 43 +++++++++++++ src/main/node/package.json | 2 +- src/main/resources/static/css/styles.css | 61 +++++++++++++++++++ src/main/resources/static/js/downloads.js | 40 ++++++++++++ src/main/resources/templates/dashboard.html | 37 +++++++++++ src/main/resources/templates/index.html | 35 +++++++++++ 8 files changed, 225 insertions(+), 3 deletions(-) create mode 100644 src/main/resources/static/js/downloads.js diff --git a/pom.xml b/pom.xml index 956c9fa..8547536 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ dev.kruhlmann imgfloat - 0.0.1-SNAPSHOT + 0.0.1 Imgfloat Livestream overlay with Twitch-authenticated channel admins and broadcasters. jar diff --git a/src/main/java/dev/kruhlmann/imgfloat/controller/ViewController.java b/src/main/java/dev/kruhlmann/imgfloat/controller/ViewController.java index 6515904..ed794ba 100644 --- a/src/main/java/dev/kruhlmann/imgfloat/controller/ViewController.java +++ b/src/main/java/dev/kruhlmann/imgfloat/controller/ViewController.java @@ -56,9 +56,10 @@ public class ViewController { model.addAttribute("username", sessionUsername); model.addAttribute("channel", sessionUsername); model.addAttribute("adminChannels", channelDirectoryService.adminChannelsFor(sessionUsername)); + addVersionAttributes(model); return "dashboard"; } - model.addAttribute("version", versionService.getVersion()); + addVersionAttributes(model); return "index"; } @@ -111,4 +112,9 @@ public class ViewController { model.addAttribute("broadcaster", broadcaster.toLowerCase()); return "broadcast"; } + + private void addVersionAttributes(Model model) { + model.addAttribute("version", versionService.getVersion()); + model.addAttribute("releaseVersion", versionService.getReleaseVersion()); + } } diff --git a/src/main/java/dev/kruhlmann/imgfloat/service/VersionService.java b/src/main/java/dev/kruhlmann/imgfloat/service/VersionService.java index 5576c6a..08b0fb7 100644 --- a/src/main/java/dev/kruhlmann/imgfloat/service/VersionService.java +++ b/src/main/java/dev/kruhlmann/imgfloat/service/VersionService.java @@ -12,21 +12,32 @@ import java.io.InputStreamReader; public class VersionService { private static final Logger LOG = LoggerFactory.getLogger(VersionService.class); private final String version; + private final String releaseVersion; public VersionService() { this.version = resolveVersion(); + this.releaseVersion = normalizeReleaseVersion(this.version); } public String getVersion() { return version; } + public String getReleaseVersion() { + return releaseVersion; + } + private String resolveVersion() { String manifestVersion = getClass().getPackage().getImplementationVersion(); if (manifestVersion != null && !manifestVersion.isBlank()) { return manifestVersion; } + String pomVersion = getPomVersion(); + if (pomVersion != null && !pomVersion.isBlank()) { + return pomVersion; + } + String gitDescribeVersion = getGitVersionString(); if (gitDescribeVersion != null) { return "git-" + gitDescribeVersion; @@ -35,6 +46,38 @@ public class VersionService { return "unknown"; } + private String normalizeReleaseVersion(String baseVersion) { + if (baseVersion == null || baseVersion.isBlank()) { + return "latest"; + } + + String normalized = baseVersion.trim(); + normalized = normalized.replaceFirst("^git-", ""); + normalized = normalized.replaceFirst("(?i)^v", ""); + normalized = normalized.replaceFirst("-SNAPSHOT$", ""); + if (normalized.isBlank()) { + return "latest"; + } + return normalized; + } + + private String getPomVersion() { + try (var inputStream = getClass().getResourceAsStream("/META-INF/maven/dev.kruhlmann/imgfloat/pom.properties")) { + if (inputStream == null) { + return null; + } + var properties = new java.util.Properties(); + properties.load(inputStream); + String pomVersion = properties.getProperty("version"); + if (pomVersion != null && !pomVersion.isBlank()) { + return pomVersion.trim(); + } + } catch (IOException e) { + LOG.warn("Unable to read version from pom.properties", e); + } + return null; + } + private String getGitVersionString() { try { Process check = new ProcessBuilder("git", "--version") diff --git a/src/main/node/package.json b/src/main/node/package.json index 15b31b4..4f840d8 100644 --- a/src/main/node/package.json +++ b/src/main/node/package.json @@ -1,6 +1,6 @@ { "name": "imgfloat-electron", - "version": "1.0.0", + "version": "0.0.1", "description": "Electron wrapper for the Imgfloat overlay", "main": "app.js", "build": { diff --git a/src/main/resources/static/css/styles.css b/src/main/resources/static/css/styles.css index 269ca7b..a5fee32 100644 --- a/src/main/resources/static/css/styles.css +++ b/src/main/resources/static/css/styles.css @@ -385,6 +385,67 @@ body { border-color: rgba(124, 58, 237, 0.18); } +.download-section { + margin-top: 26px; + padding: 20px; + background: rgba(11, 18, 32, 0.92); + border-radius: 14px; + border: 1px solid #1f2937; + box-shadow: 0 20px 50px rgba(0, 0, 0, 0.45); + display: flex; + flex-direction: column; + gap: 14px; +} + +.download-header h2, .download-header h3 { + margin: 6px 0 8px; +} + +.version-inline { + font-weight: 700; + color: #e2e8f0; +} + +.download-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); + gap: 12px; +} + +.download-card { + border: 1px solid #1f2937; + border-radius: 12px; + padding: 14px; + background: rgba(15, 23, 42, 0.82); + display: flex; + flex-direction: column; + gap: 10px; + transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease; +} + +.download-card-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; +} + +.download-card--active { + border-color: rgba(124, 58, 237, 0.55); + box-shadow: 0 16px 40px rgba(124, 58, 237, 0.22); + transform: translateY(-2px); +} + +.download-card .button { + margin-top: 4px; +} + +.download-card-block { + display: flex; + flex-direction: column; + gap: 12px; +} + .eyebrow { text-transform: uppercase; letter-spacing: 1px; diff --git a/src/main/resources/static/js/downloads.js b/src/main/resources/static/js/downloads.js new file mode 100644 index 0000000..f4e324a --- /dev/null +++ b/src/main/resources/static/js/downloads.js @@ -0,0 +1,40 @@ +function detectPlatform() { + const navigatorPlatform = (navigator.userAgentData?.platform || navigator.platform || '').toLowerCase(); + const userAgent = (navigator.userAgent || '').toLowerCase(); + const platformString = `${navigatorPlatform} ${userAgent}`; + + if (platformString.includes('mac') || platformString.includes('darwin')) { + return 'mac'; + } + if (platformString.includes('win')) { + return 'windows'; + } + if (platformString.includes('linux')) { + return 'linux'; + } + return null; +} + +function markRecommendedDownload(section) { + const cards = Array.from(section.querySelectorAll('.download-card')); + if (!cards.length) { + return; + } + + const platform = detectPlatform(); + const preferredCard = cards.find((card) => card.dataset.platform === platform) || cards[0]; + + cards.forEach((card) => { + const isPreferred = card === preferredCard; + card.classList.toggle('download-card--active', isPreferred); + const badge = card.querySelector('.recommended-badge'); + if (badge) { + badge.classList.toggle('hidden', !isPreferred); + } + }); +} + +document.addEventListener('DOMContentLoaded', () => { + const downloadSections = document.querySelectorAll('.download-section, .download-card-block'); + downloadSections.forEach(markRecommendedDownload); +}); diff --git a/src/main/resources/templates/dashboard.html b/src/main/resources/templates/dashboard.html index 0ed17b0..e134258 100644 --- a/src/main/resources/templates/dashboard.html +++ b/src/main/resources/templates/dashboard.html @@ -104,8 +104,45 @@ + +
+
+
+

Desktop app

+

Download Imgfloat

+

Version 0.0.1 ยท build unknown

+
+
+
+
+
+

macOS

+ +
+

Apple Silicon build (ARM64)

+ Download .dmg +
+
+
+

Windows

+ +
+

Installer for Windows 10 and 11

+ Download .exe +
+
+
+

Linux

+ +
+

AppImage for most distributions

+ Download AppImage +
+
+
+ diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index 08e5109..f58f387 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -29,6 +29,40 @@ +
+
+

Desktop app

+

Download Imgfloat

+

Version 0.0.1 for Windows, macOS, and Linux.

+
+
+
+
+

macOS

+ +
+

Apple Silicon build (ARM64)

+ Download .dmg +
+
+
+

Windows

+ +
+

Installer for Windows 10 and 11

+ Download .exe +
+
+
+

Linux

+ +
+

AppImage for most distributions

+ Download AppImage +
+
+
+ +