diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 82796f565..52ed1ae71 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -12,6 +12,12 @@ updates:
schedule:
interval: "weekly"
+ - package-ecosystem: "gradle"
+ directory: "/komga-tray"
+ open-pull-requests-limit: 0
+ schedule:
+ interval: "weekly"
+
# Maintain dependencies for GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
diff --git a/.idea/runConfigurations/komga__bootRun__dev.xml b/.idea/runConfigurations/komga__bootRun__dev.xml
index 1af4c7df2..4b8538e53 100644
--- a/.idea/runConfigurations/komga__bootRun__dev.xml
+++ b/.idea/runConfigurations/komga__bootRun__dev.xml
@@ -7,7 +7,7 @@
-
+
- true
+ true
+ true
+ false
+ false
-
\ No newline at end of file
+
diff --git a/.idea/runConfigurations/komga__bootRun__dev_demo_noclaim.xml b/.idea/runConfigurations/komga__bootRun__dev_demo_noclaim.xml
index 562a75e10..0f1628765 100644
--- a/.idea/runConfigurations/komga__bootRun__dev_demo_noclaim.xml
+++ b/.idea/runConfigurations/komga__bootRun__dev_demo_noclaim.xml
@@ -7,7 +7,7 @@
-
+
- true
+ true
+ true
+ false
+ false
-
\ No newline at end of file
+
diff --git a/.idea/runConfigurations/komga__bootRun__dev_localdb_noclaim_oauth2.xml b/.idea/runConfigurations/komga__bootRun__dev_localdb_noclaim_oauth2.xml
index 811f28d1b..4749b7091 100644
--- a/.idea/runConfigurations/komga__bootRun__dev_localdb_noclaim_oauth2.xml
+++ b/.idea/runConfigurations/komga__bootRun__dev_localdb_noclaim_oauth2.xml
@@ -7,7 +7,7 @@
-
+
-
+
- true
+ true
+ true
+ false
+ false
-
\ No newline at end of file
+
diff --git a/komga-tray/build.gradle.kts b/komga-tray/build.gradle.kts
new file mode 100644
index 000000000..6d47c3bf1
--- /dev/null
+++ b/komga-tray/build.gradle.kts
@@ -0,0 +1,40 @@
+plugins {
+ run {
+ kotlin("jvm")
+ kotlin("plugin.spring")
+ }
+ id("com.gorylenko.gradle-git-properties") version "2.4.1"
+ id("org.jetbrains.compose") version "1.4.3"
+ id("dev.hydraulic.conveyor") version "1.5"
+ application
+}
+
+group = "org.gotson"
+
+kotlin {
+ jvmToolchain(19) // for NightMonkeys
+}
+
+dependencies {
+ implementation(project(":komga"))
+ implementation(platform("org.springframework.boot:spring-boot-dependencies:3.1.1"))
+ implementation("org.springframework.boot:spring-boot-starter-web")
+
+ implementation(compose.desktop.common)
+
+ linuxAmd64(compose.desktop.linux_x64)
+ macAmd64(compose.desktop.macos_x64)
+ macAarch64(compose.desktop.macos_arm64)
+ windowsAmd64(compose.desktop.windows_x64)
+}
+
+application {
+ mainClass.set("org.gotson.komga.DesktopApplicationKt")
+}
+
+// Work around temporary Compose bugs
+configurations.all {
+ attributes {
+ attribute(Attribute.of("ui", String::class.java), "awt")
+ }
+}
diff --git a/komga-tray/src/main/kotlin/org/gotson/komga/DesktopApplication.kt b/komga-tray/src/main/kotlin/org/gotson/komga/DesktopApplication.kt
new file mode 100644
index 000000000..88cdef4fa
--- /dev/null
+++ b/komga-tray/src/main/kotlin/org/gotson/komga/DesktopApplication.kt
@@ -0,0 +1,12 @@
+package org.gotson.komga
+
+import org.springframework.boot.builder.SpringApplicationBuilder
+
+fun main(args: Array) {
+ System.setProperty("apple.awt.UIElement", "true")
+ System.setProperty("org.jooq.no-logo", "true")
+ System.setProperty("org.jooq.no-tips", "true")
+ val builder = SpringApplicationBuilder(Application::class.java)
+ builder.headless(false)
+ builder.run(*args)
+}
diff --git a/komga-tray/src/main/kotlin/org/gotson/komga/Utils.kt b/komga-tray/src/main/kotlin/org/gotson/komga/Utils.kt
new file mode 100644
index 000000000..47dd08fba
--- /dev/null
+++ b/komga-tray/src/main/kotlin/org/gotson/komga/Utils.kt
@@ -0,0 +1,11 @@
+package org.gotson.komga
+
+import java.awt.Desktop
+import java.net.URI
+
+fun openUrl(url: String) {
+ if (Desktop.isDesktopSupported())
+ Desktop.getDesktop().let {
+ if (it.isSupported(Desktop.Action.BROWSE)) it.browse(URI.create(url))
+ }
+}
diff --git a/komga-tray/src/main/kotlin/org/gotson/komga/application/gui/TrayIconRunner.kt b/komga-tray/src/main/kotlin/org/gotson/komga/application/gui/TrayIconRunner.kt
new file mode 100644
index 000000000..303c7adc3
--- /dev/null
+++ b/komga-tray/src/main/kotlin/org/gotson/komga/application/gui/TrayIconRunner.kt
@@ -0,0 +1,41 @@
+package org.gotson.komga.application.gui
+
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.res.loadSvgPainter
+import androidx.compose.ui.window.Tray
+import androidx.compose.ui.window.application
+import org.gotson.komga.openUrl
+import org.springframework.beans.factory.annotation.Value
+import org.springframework.boot.ApplicationArguments
+import org.springframework.boot.ApplicationRunner
+import org.springframework.context.annotation.Profile
+import org.springframework.core.env.Environment
+import org.springframework.core.io.ClassPathResource
+import org.springframework.stereotype.Component
+
+@Profile("!test")
+@Component
+class TrayIconRunner(
+ @Value("#{servletContext.contextPath}") servletContextPath: String,
+ @Value("\${server.port}") serverPort: Int,
+ env: Environment,
+) : ApplicationRunner {
+
+ val komgaUrl = "http://localhost:$serverPort$servletContextPath"
+ val iconFileName = if (env.activeProfiles.contains("mac")) "komga-gray-minimal.svg" else "komga-color.svg"
+ override fun run(args: ApplicationArguments) {
+ runTray()
+ }
+
+ private fun runTray() {
+ application {
+ Tray(
+ icon = loadSvgPainter(ClassPathResource("icons/$iconFileName").inputStream, LocalDensity.current),
+ menu = {
+ Item("Open Komga", onClick = { openUrl(komgaUrl) })
+ Item("Quit Komga", onClick = ::exitApplication)
+ },
+ )
+ }
+ }
+}
diff --git a/komga-tray/src/main/resources/application-mac.yml b/komga-tray/src/main/resources/application-mac.yml
new file mode 100644
index 000000000..08a7b49e3
--- /dev/null
+++ b/komga-tray/src/main/resources/application-mac.yml
@@ -0,0 +1,5 @@
+logging:
+ file:
+ name: ${user.home}/Library/Logs/Komga/komga.log
+komga:
+ config-dir: ${user.home}/Library/Application Support/Komga
diff --git a/komga-tray/src/main/resources/application-windows.yml b/komga-tray/src/main/resources/application-windows.yml
new file mode 100644
index 000000000..5a20b4f38
--- /dev/null
+++ b/komga-tray/src/main/resources/application-windows.yml
@@ -0,0 +1,2 @@
+komga:
+ config-dir: ${LOCALAPPDATA}/Komga
diff --git a/komga-tray/src/main/resources/icons/komga-color.svg b/komga-tray/src/main/resources/icons/komga-color.svg
new file mode 100644
index 000000000..ecb000f5e
--- /dev/null
+++ b/komga-tray/src/main/resources/icons/komga-color.svg
@@ -0,0 +1,113 @@
+
+
diff --git a/komga-tray/src/main/resources/icons/komga-gray-minimal.svg b/komga-tray/src/main/resources/icons/komga-gray-minimal.svg
new file mode 100644
index 000000000..8c873833b
--- /dev/null
+++ b/komga-tray/src/main/resources/icons/komga-gray-minimal.svg
@@ -0,0 +1,169 @@
+
+
diff --git a/komga/build.gradle.kts b/komga/build.gradle.kts
index e5c18fd89..e1cb9c19c 100644
--- a/komga/build.gradle.kts
+++ b/komga/build.gradle.kts
@@ -160,7 +160,7 @@ tasks {
}
getByName("jar") {
- enabled = false
+ enabled = true
}
register("npmInstall") {
diff --git a/res/komga-gray-minimal.svg b/res/komga-gray-minimal.svg
new file mode 100644
index 000000000..b8670ab95
--- /dev/null
+++ b/res/komga-gray-minimal.svg
@@ -0,0 +1,169 @@
+
+
diff --git a/res/komga-grayscale.svg b/res/komga-grayscale.svg
new file mode 100644
index 000000000..5b8093b36
--- /dev/null
+++ b/res/komga-grayscale.svg
@@ -0,0 +1,195 @@
+
+
diff --git a/settings.gradle b/settings.gradle
index 38646911e..f7c5da0e5 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1,2 @@
include 'komga'
+include 'komga-tray'