@@ -8,6 +8,9 @@ import org.jetbrains.compose.desktop.application.tasks.AbstractJPackageTask
88import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
99import org.jetbrains.kotlin.gradle.dsl.JvmTarget
1010import java.net.URI
11+ import java.time.Instant
12+ import java.time.ZoneId
13+ import java.time.format.DateTimeFormatter
1114import java.util.Properties
1215
1316val isFullBuild: Boolean =
@@ -26,6 +29,7 @@ plugins {
2629 alias(libs.plugins.kotlin.serialization)
2730 alias(libs.plugins.build.config)
2831 alias(libs.plugins.osdetector)
32+ alias(libs.plugins.packagedeps)
2933}
3034
3135kotlin {
@@ -66,8 +70,7 @@ kotlin {
6670 val koinBom = project.dependencies.platform(libs.koin.bom)
6771 implementation(composeBom)
6872 implementation(koinBom)
69-
70- implementation(" commons-io:commons-io:2.5" )
73+ implementation(libs.commons.io)
7174 }
7275 androidMain.dependencies {
7376 api(libs.koin.android)
@@ -120,6 +123,8 @@ kotlin {
120123 api(libs.coil.compose)
121124 api(libs.coil.network.okhttp)
122125 api(libs.kmpalette.core)
126+ api(libs.kmpalette.network)
127+ implementation(libs.ktor.client.cio)
123128
124129 // DataStore
125130 implementation(libs.datastore.preferences)
@@ -159,6 +164,7 @@ kotlin {
159164 implementation(compose.desktop.currentOs)
160165 implementation(libs.kotlinx.coroutinesSwing)
161166 implementation(libs.sentry.jvm)
167+ implementation(libs.native.tray)
162168 implementation(projects.mediaJvmUi)
163169 }
164170 }
@@ -170,21 +176,31 @@ compose.desktop {
170176
171177 nativeDistributions {
172178 val listTarget = mutableListOf<TargetFormat >()
173- if (org.gradle.internal.os.OperatingSystem .current().isMacOsX) {
179+ if (org.gradle.internal.os.OperatingSystem
180+ .current()
181+ .isMacOsX
182+ ) {
174183 listTarget.addAll(
175- listOf (TargetFormat .Dmg , TargetFormat .Msi , TargetFormat .Deb , TargetFormat .Rpm )
184+ listOf (TargetFormat .Dmg , TargetFormat .Msi , TargetFormat .Deb , TargetFormat .Rpm ),
176185 )
177186 } else {
178187 listTarget.addAll(
179- listOf (TargetFormat .Dmg , TargetFormat .Msi , TargetFormat .Deb , TargetFormat .Rpm , TargetFormat .AppImage )
188+ listOf (TargetFormat .Dmg , TargetFormat .Msi , TargetFormat .Deb , TargetFormat .Rpm , TargetFormat .AppImage ),
180189 )
181190 }
182191 targetFormats(* listTarget.toTypedArray())
183192 modules(" jdk.unsupported" )
184193 packageName = " SimpMusic"
185194 macOS {
195+ val formatedDate =
196+ Instant .now().let {
197+ DateTimeFormatter
198+ .ofPattern(" yyyy.MM.dd" )
199+ .withZone(ZoneId .of(" UTC" ))
200+ .format(it)
201+ }
186202 includeAllModules = true
187- packageVersion = " 2025.12.24 "
203+ packageVersion = formatedDate
188204 iconFile.set(project.file(" icon/circle_app_icon.icns" ))
189205 val macExtraPlistKeys =
190206 """
@@ -276,6 +292,10 @@ aboutLibraries {
276292 }
277293}
278294
295+ linuxDebConfig {
296+ startupWMClass.set(" java-lang-Thread" )
297+ }
298+
279299afterEvaluate {
280300 tasks.withType<JavaExec > {
281301 jvmArgs(" --add-opens" , " java.desktop/sun.awt=ALL-UNNAMED" )
@@ -307,75 +327,75 @@ afterEvaluate {
307327 return
308328 }
309329
310- val appimagetool =
311- layout.buildDirectory
312- .dir(" tmp" )
313- .get()
314- .asFile
315- .resolve(" appimagetool-x86_64.AppImage" )
330+ val appimagetool =
331+ layout.buildDirectory
332+ .dir(" tmp" )
333+ .get()
334+ .asFile
335+ .resolve(" appimagetool-x86_64.AppImage" )
316336
317- if (! appimagetool.exists()) {
318- downloadFile(
319- " https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage" ,
320- appimagetool,
321- )
322- }
337+ if (! appimagetool.exists()) {
338+ downloadFile(
339+ " https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage" ,
340+ appimagetool,
341+ )
342+ }
323343
324- if (! appimagetool.canExecute()) {
325- appimagetool.setExecutable(true )
326- }
344+ if (! appimagetool.canExecute()) {
345+ appimagetool.setExecutable(true )
346+ }
327347
328- val appDir =
329- if (isRelease) {
330- layout.buildDirectory
331- .dir(" appimage/main-release/$appName .AppDir" )
332- .get()
333- .asFile
334- } else {
335- layout.buildDirectory
336- .dir(" appimage/main/$appName .AppDir" )
337- .get()
338- .asFile
339- }
340- if (appDir.exists()) {
341- appDir.deleteRecursively()
348+ val appDir =
349+ if (isRelease) {
350+ layout.buildDirectory
351+ .dir(" appimage/main-release/$appName .AppDir" )
352+ .get()
353+ .asFile
354+ } else {
355+ layout.buildDirectory
356+ .dir(" appimage/main/$appName .AppDir" )
357+ .get()
358+ .asFile
342359 }
360+ if (appDir.exists()) {
361+ appDir.deleteRecursively()
362+ }
343363
344- FileUtils .copyDirectory(appDirSrc, appDir)
345- FileUtils .copyDirectory(packageOutput, appDir)
364+ FileUtils .copyDirectory(appDirSrc, appDir)
365+ FileUtils .copyDirectory(packageOutput, appDir)
346366
347- val appExecutable = appDir.resolve(" bin/$appName " )
348- if (! appExecutable.canExecute()) {
349- appimagetool.setExecutable(true )
350- }
367+ val appExecutable = appDir.resolve(" bin/$appName " )
368+ if (! appExecutable.canExecute()) {
369+ appimagetool.setExecutable(true )
370+ }
351371
352- val appRun = appDir.resolve(" AppRun" )
353- if (! appRun.canExecute()) {
354- appRun.setReadable(true , false ) // readable by all
355- appRun.setWritable(true , true ) // writable only by owner
356- appRun.setExecutable(true , false )
372+ val appRun = appDir.resolve(" AppRun" )
373+ if (! appRun.canExecute()) {
374+ appRun.setReadable(true , false ) // readable by all
375+ appRun.setWritable(true , true ) // writable only by owner
376+ appRun.setExecutable(true , false )
357377
358- println (
359- " Set AppRun executable permissions, readable: ${appRun.canRead()} , writable: ${appRun.canWrite()} , executable: ${appRun.canExecute()} " ,
360- )
361- }
378+ println (
379+ " Set AppRun executable permissions, readable: ${appRun.canRead()} , writable: ${appRun.canWrite()} , executable: ${appRun.canExecute()} " ,
380+ )
381+ }
362382
363- // Use ProcessBuilder instead of exec {} to avoid capturing project reference
364- val process = ProcessBuilder (
383+ // Use ProcessBuilder instead of exec {} to avoid capturing project reference
384+ val process =
385+ ProcessBuilder (
365386 appimagetool.canonicalPath,
366387 " $appName .AppDir" ,
367- " $appName -x86_64.AppImage"
368- )
369- .directory(appDir.parentFile)
388+ " $appName -x86_64.AppImage" ,
389+ ).directory(appDir.parentFile)
370390 .apply { environment()[" ARCH" ] = " x86_64" } // TODO: 支持arm64
371391 .inheritIO()
372392 .start()
373393
374- val exitCode = process.waitFor()
375- if (exitCode != 0 ) {
376- throw GradleException (" appimagetool failed with exit code $exitCode " )
377- }
394+ val exitCode = process.waitFor()
395+ if (exitCode != 0 ) {
396+ throw GradleException (" appimagetool failed with exit code $exitCode " )
378397 }
398+ }
379399
380400 tasks.findByName(" packageAppImage" )?.doLast {
381401 packAppImage(false )
0 commit comments