瀏覽代碼

code improvements

code improvements
Milan Jurkulak 2 年之前
父節點
當前提交
d9698d2646
共有 41 個文件被更改,包括 852 次插入 和 430 次删除
  1. 1 1
      src/main/kotlin/eu/mjdev/desktop/components/desktoppanel/applets/DesktopPanelFavoriteApps.kt
  2. 1 1
      src/main/kotlin/eu/mjdev/desktop/components/desktoppanel/applets/DesktopPanelLanguage.kt
  3. 3 3
      src/main/kotlin/eu/mjdev/desktop/helpers/backgrounds/ProviderLocal.kt
  4. 1 14
      src/main/kotlin/eu/mjdev/desktop/helpers/internal/Palette.kt
  5. 1 1
      src/main/kotlin/eu/mjdev/desktop/helpers/system/Environment.kt
  6. 0 64
      src/main/kotlin/eu/mjdev/desktop/helpers/system/OsRelease.kt
  7. 0 104
      src/main/kotlin/eu/mjdev/desktop/managers/GnomeManager.kt
  8. 0 46
      src/main/kotlin/eu/mjdev/desktop/managers/KCEFHelper.kt
  9. 13 5
      src/main/kotlin/eu/mjdev/desktop/managers/apps/AppsManager.kt
  10. 53 0
      src/main/kotlin/eu/mjdev/desktop/managers/artificialintelligence/AIManager.kt
  11. 13 0
      src/main/kotlin/eu/mjdev/desktop/managers/artificialintelligence/base/AIPlugin.kt
  12. 48 0
      src/main/kotlin/eu/mjdev/desktop/managers/artificialintelligence/plugins/AiPluginGemini.kt
  13. 18 0
      src/main/kotlin/eu/mjdev/desktop/managers/artificialintelligence/plugins/AiPluginNull.kt
  14. 18 0
      src/main/kotlin/eu/mjdev/desktop/managers/artificialintelligence/stt/STTPluginNull.kt
  15. 14 0
      src/main/kotlin/eu/mjdev/desktop/managers/artificialintelligence/stt/base/STTListener.kt
  16. 13 0
      src/main/kotlin/eu/mjdev/desktop/managers/artificialintelligence/stt/base/STTPlugin.kt
  17. 33 0
      src/main/kotlin/eu/mjdev/desktop/managers/artificialintelligence/tts/TTSPluginMain.kt
  18. 30 0
      src/main/kotlin/eu/mjdev/desktop/managers/artificialintelligence/tts/TTSPluginSwift.kt
  19. 13 0
      src/main/kotlin/eu/mjdev/desktop/managers/artificialintelligence/tts/base/TTSPlugin.kt
  20. 13 0
      src/main/kotlin/eu/mjdev/desktop/managers/bluetooth/BluetoothManager.kt
  21. 1 1
      src/main/kotlin/eu/mjdev/desktop/managers/connectivity/ConnectivityManager.kt
  22. 39 0
      src/main/kotlin/eu/mjdev/desktop/managers/dbus/DBusManager.kt
  23. 1 1
      src/main/kotlin/eu/mjdev/desktop/managers/filesystem/FileSystemWatcher.kt
  24. 23 0
      src/main/kotlin/eu/mjdev/desktop/managers/kcef/KCEFManager.kt
  25. 25 0
      src/main/kotlin/eu/mjdev/desktop/managers/kcef/base/KCEFManagerStub.kt
  26. 53 0
      src/main/kotlin/eu/mjdev/desktop/managers/kcef/linux/KCEFManagerLinux.kt
  27. 23 0
      src/main/kotlin/eu/mjdev/desktop/managers/os/OSManager.kt
  28. 44 0
      src/main/kotlin/eu/mjdev/desktop/managers/os/base/OSManagerStub.kt
  29. 48 0
      src/main/kotlin/eu/mjdev/desktop/managers/os/linux/OSManagerLinux.kt
  30. 72 0
      src/main/kotlin/eu/mjdev/desktop/managers/os/linux/OsRelease.kt
  31. 1 1
      src/main/kotlin/eu/mjdev/desktop/managers/processes/ProcessManager.kt
  32. 24 0
      src/main/kotlin/eu/mjdev/desktop/managers/script/ScriptManager.kt
  33. 23 0
      src/main/kotlin/eu/mjdev/desktop/managers/script/base/ScriptManagerStub.kt
  34. 18 0
      src/main/kotlin/eu/mjdev/desktop/managers/script/linux/ScriptManagerLinux.kt
  35. 23 0
      src/main/kotlin/eu/mjdev/desktop/managers/theme/ThemeManager.kt
  36. 20 0
      src/main/kotlin/eu/mjdev/desktop/managers/theme/base/ThemeManagerStub.kt
  37. 94 26
      src/main/kotlin/eu/mjdev/desktop/managers/theme/linux/ThemeManagerLinux.kt
  38. 3 3
      src/main/kotlin/eu/mjdev/desktop/managers/window/WindowsManager.kt
  39. 0 119
      src/main/kotlin/eu/mjdev/desktop/provider/AIProvider.kt
  40. 22 31
      src/main/kotlin/eu/mjdev/desktop/provider/DesktopProvider.kt
  41. 9 9
      src/main/kotlin/eu/mjdev/desktop/provider/DesktopScope.kt

+ 1 - 1
src/main/kotlin/eu/mjdev/desktop/components/desktoppanel/applets/DesktopPanelFavoriteApps.kt

@@ -16,7 +16,7 @@ import androidx.compose.ui.unit.DpSize
 import androidx.compose.ui.unit.dp
 import eu.mjdev.desktop.components.desktoppanel.DesktopPanelIcon
 import eu.mjdev.desktop.data.App
-import eu.mjdev.desktop.provider.AppsProvider.Companion.rememberFavoriteApps
+import eu.mjdev.desktop.managers.apps.AppsManager.Companion.rememberFavoriteApps
 import eu.mjdev.desktop.provider.DesktopScope.Companion.withDesktopScope
 
 @Suppress("FunctionName")

+ 1 - 1
src/main/kotlin/eu/mjdev/desktop/components/desktoppanel/applets/DesktopPanelLanguage.kt

@@ -16,7 +16,7 @@ fun DesktopPanelLanguage(
 ) = withDesktopScope {
     DesktopPanelText(
         modifier = modifier,
-        text = api.appsProvider.currentLocale.country,
+        text = currentLocale.country,
         onTooltip = onTooltip,
         onClick = onClick,
         textColor = iconsTintColor

+ 3 - 3
src/main/kotlin/eu/mjdev/desktop/helpers/backgrounds/ProviderLocal.kt

@@ -12,9 +12,9 @@ import eu.mjdev.desktop.data.User
 import eu.mjdev.desktop.extensions.Custom.get
 import eu.mjdev.desktop.extensions.Custom.listFilesOnly
 import eu.mjdev.desktop.helpers.internal.ImagesProvider
-import eu.mjdev.desktop.provider.AppsProvider.Companion.DIR_NAME_BACKGROUNDS
-import eu.mjdev.desktop.provider.AppsProvider.Companion.DIR_NAME_DOT_LOCAL
-import eu.mjdev.desktop.provider.AppsProvider.Companion.DIR_NAME_SHARE
+import eu.mjdev.desktop.managers.apps.AppsManager.Companion.DIR_NAME_BACKGROUNDS
+import eu.mjdev.desktop.managers.apps.AppsManager.Companion.DIR_NAME_DOT_LOCAL
+import eu.mjdev.desktop.managers.apps.AppsManager.Companion.DIR_NAME_SHARE
 
 // todo more folders
 @Suppress("unused")

+ 1 - 14
src/main/kotlin/eu/mjdev/desktop/helpers/internal/Palette.kt

@@ -11,8 +11,6 @@ import eu.mjdev.desktop.extensions.ColorUtils.lighter
 import eu.mjdev.desktop.extensions.ColorUtils.nonAlphaValue
 import eu.mjdev.desktop.extensions.Compose.SuperDarkGray
 import eu.mjdev.desktop.extensions.Image.loadPicture
-import eu.mjdev.desktop.managers.GnomeManager
-import eu.mjdev.desktop.managers.GnomeManager.Companion.THEME_MJDEV
 import eu.mjdev.desktop.provider.DesktopProvider
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
@@ -21,7 +19,6 @@ import kotlinx.coroutines.launch
 @Suppress("CanBeParameter", "MemberVisibilityCanBePrivate", "unused")
 class Palette(
     val api: DesktopProvider,
-    val gnome: GnomeManager = api.gnome,
     val scope: CoroutineScope = api.scope,
     val initialColor: Color = Color.SuperDarkGray,
     val borderFactor: Float = 0.1f,
@@ -89,18 +86,8 @@ class Palette(
             textColorState.value = text
         }
         createFromPalette()
-//        setGtkTheme(THEME_MJDEV)
-//        setSoundTheme(THEME_MJDEV)
     }
 
     fun createFromPalette() =
-        api.gtkTheme.createFromPalette()
-
-    fun setGtkTheme(
-        themeName: String = THEME_MJDEV
-    ) = gnome.setGTKTheme(themeName)
-
-    fun setSoundTheme(
-        themeName: String = THEME_MJDEV
-    ) = gnome.setSoundTheme(themeName)
+        api.themeManager.createFromPalette()
 }

+ 1 - 1
src/main/kotlin/eu/mjdev/desktop/helpers/system/Environment.kt

@@ -1,6 +1,6 @@
 package eu.mjdev.desktop.helpers.system
 
-import eu.mjdev.desktop.managers.GnomeManager.Companion.THEME_MJDEV
+import eu.mjdev.desktop.managers.theme.linux.ThemeManagerLinux.Companion.THEME_MJDEV
 
 class Environment(
     val data: List<String> = Shell.executeAndReadLines("env")

+ 0 - 64
src/main/kotlin/eu/mjdev/desktop/helpers/system/OsRelease.kt

@@ -1,64 +0,0 @@
-package eu.mjdev.desktop.helpers.system
-
-import eu.mjdev.desktop.extensions.Custom.lines
-import eu.mjdev.desktop.extensions.Custom.notStartsWith
-import java.io.File
-
-@Suppress("unused")
-class OsRelease(
-    configFile: File = File("/etc/os-release"),
-    configFileContent: List<String> = configFile.lines.filter { line ->
-        line.trim().let { l -> l.isNotEmpty() && l.notStartsWith("#") }
-    }
-) : HashMap<String, File>() {
-    init {
-        configFileContent.map {
-            it.split("=").let { pair -> Pair(pair[0], pair.getOrNull(1)) }
-        }.forEach {
-            put(it.first, File(it.second ?: ""))
-        }
-    }
-
-    val prettyName
-        get() = this[PRETTY_NAME]
-    val name
-        get() = this[NAME]
-    val versionId
-        get() = this[VERSION_ID]
-    val version
-        get() = this[VERSION]
-    val versionCodeName
-        get() = this[VERSION_CODENAME]
-    val id
-        get() = this[ID]
-    val idLike
-        get() = this[ID_LIKE]
-    val homeUrl
-        get() = this[HOME_URL]
-    val supportUrl
-        get() = this[SUPPORT_URL]
-    val bugReportUrl
-        get() = this[BUG_REPORT_URL]
-    val privacyPolicyUrl
-        get() = this[PRIVACY_POLICY_URL]
-    val ubuntuCodename
-        get() = this[UBUNTU_CODENAME]
-    val logo
-        get() = this[LOGO]
-
-    companion object {
-        const val PRETTY_NAME = "PRETTY_NAME"
-        const val NAME = "NAME"
-        const val VERSION_ID = "VERSION_ID"
-        const val VERSION = "VERSION"
-        const val VERSION_CODENAME = "VERSION_CODENAME"
-        const val ID = "ID"
-        const val ID_LIKE = "ID_LIKE"
-        const val HOME_URL = "HOME_URL"
-        const val SUPPORT_URL = "SUPPORT_URL"
-        const val BUG_REPORT_URL = "BUG_REPORT_URL"
-        const val PRIVACY_POLICY_URL = "PRIVACY_POLICY_URL"
-        const val UBUNTU_CODENAME = "UBUNTU_CODENAME"
-        const val LOGO = "LOGO"
-    }
-}

+ 0 - 104
src/main/kotlin/eu/mjdev/desktop/managers/GnomeManager.kt

@@ -1,104 +0,0 @@
-package eu.mjdev.desktop.managers
-
-import eu.mjdev.desktop.helpers.system.Shell
-
-//import org.freedesktop.dbus.annotations.DBusInterfaceName
-//import org.freedesktop.dbus.connections.impl.DBusConnection
-//import org.freedesktop.dbus.interfaces.DBusInterface
-//import org.freedesktop.dbus.types.Variant
-
-@Suppress("unused", "MemberVisibilityCanBePrivate")
-class GnomeManager {
-
-//    val connection by lazy { DBusConnection.getConnection(DBusConnection.DBusBusType.SESSION) }
-//    val settingsInterface by lazy {  connection.getRemoteObject(
-//        "org.freedesktop.portal.Desktop",
-//        "/org/freedesktop/portal/desktop",
-//        SettingsInterface::class.java
-//    ) }
-//
-//    val currentColorScheme get() = settingsInterface.Read("org.freedesktop.appearance", "color-scheme")
-
-//    init {
-//        println(recursiveVariantValue(currentColorScheme))
-//    }
-
-    fun setColorScheme(
-        schemeName: String
-    ) {
-        Shell.executeAndRead(
-            "gsettings",
-            "set",
-            "org.gnome.desktop.interface",
-            "color-scheme",
-            schemeName
-        )
-    }
-
-    fun setGTKTheme(
-        themeName: String = THEME_YARU
-    ) = Shell.executeAndRead(
-        "gsettings",
-        "set",
-        "org.gnome.desktop.interface",
-        "gtk-theme",
-        themeName
-    )
-
-    fun getGTKTheme(): String = Shell.executeAndRead(
-        "gsettings",
-        "get",
-        "org.gnome.desktop.interface",
-        "gtk-theme"
-    ).replace("'", "").trim()
-
-    fun setIconTheme(
-        themeName: String = THEME_YARU
-    ) = Shell.executeAndRead(
-        "gsettings",
-        "set",
-        "org.gnome.desktop.interface",
-        "icon-theme",
-        themeName
-    )
-
-    fun setSoundTheme(
-        themeName: String = THEME_YARU
-    ) = Shell.executeAndRead(
-        "gsettings",
-        "set",
-        "org.gnome.desktop.sound",
-        "gtk-theme",
-        themeName
-    )
-
-    fun setDarkColorScheme() =
-        setColorScheme(COLOR_SCHEME_PREFER_DARK)
-
-//    fun recursiveVariantValue(variant: Variant<*>): Any {
-//        val value = variant.value
-//        if(value !is Variant<*>) return value
-//        else return recursiveVariantValue(value)
-//    }
-
-//    @DBusInterfaceName("org.freedesktop.portal.Settings")
-//    interface SettingsInterface : DBusInterface {
-//        fun Read(namespace: String, key: String): Variant<*>
-//    }
-
-    companion object {
-        const val COLOR_SCHEME_PREFER_DARK = "prefer-dark"
-
-        const val COLOR_SCHEME_MJDEV = "mjdev"
-
-        const val THEME_YARU = "Yaru"
-
-        const val THEME_ADWAITA = "Adwaita"
-        const val THEME_ADWAITA_DARK = "Adwaita-dark"
-
-        const val THEME_MJDEV = "Mjdev"
-
-        const val THEME_CURSOR_BLOOM = "bloom"
-    }
-
-}

+ 0 - 46
src/main/kotlin/eu/mjdev/desktop/managers/KCEFHelper.kt

@@ -1,46 +0,0 @@
-package eu.mjdev.desktop.managers
-
-import androidx.compose.runtime.MutableState
-import androidx.compose.runtime.mutableStateOf
-import dev.datlag.kcef.KCEF
-import eu.mjdev.desktop.provider.DesktopProvider
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
-import java.io.File
-import kotlin.math.max
-
-class KCEFHelper(
-    private val api: DesktopProvider,
-    private val scope: CoroutineScope = api.scope,
-    val initialized: MutableState<Boolean> = mutableStateOf(false),
-    val restartRequired: MutableState<Boolean> = mutableStateOf(false),
-    val downloading: MutableState<Int> = mutableStateOf(0)
-) {
-    fun init() = scope.launch(Dispatchers.IO) {
-        if (!initialized.value && !restartRequired.value) {
-            KCEF.init(builder = {
-                installDir(File("/tmp/kcef-bundle"))
-                progress {
-                    onDownloading {
-                        downloading.value = max(it, 0F).toInt()
-                    }
-                    onInitialized {
-                        initialized.value = true
-                    }
-                }
-                settings {
-                    cachePath = File("cache").absolutePath
-                }
-            }, onError = { err ->
-                err?.printStackTrace()
-            }, onRestartRequired = {
-                restartRequired.value = true
-            })
-        }
-    }
-
-    fun dispose() {
-        KCEF.disposeBlocking()
-    }
-}

+ 13 - 5
src/main/kotlin/eu/mjdev/desktop/provider/AppsProvider.kt → src/main/kotlin/eu/mjdev/desktop/managers/apps/AppsManager.kt

@@ -1,4 +1,12 @@
-package eu.mjdev.desktop.provider
+/*
+ * Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+package eu.mjdev.desktop.managers.apps
 
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.State
@@ -14,6 +22,7 @@ import eu.mjdev.desktop.extensions.Custom.lines
 import eu.mjdev.desktop.extensions.Custom.text
 import eu.mjdev.desktop.extensions.Custom.textAsLocale
 import eu.mjdev.desktop.helpers.system.Shell
+import eu.mjdev.desktop.provider.DesktopProvider
 import eu.mjdev.desktop.provider.DesktopProvider.Companion.LocalDesktop
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
@@ -23,7 +32,7 @@ import java.io.File
 import java.util.*
 
 @Suppress("unused", "MemberVisibilityCanBePrivate")
-class AppsProvider(
+class AppsManager(
     val api: DesktopProvider,
     val scope: CoroutineScope = api.scope
 ) {
@@ -56,13 +65,12 @@ class AppsProvider(
         @Composable
         fun rememberFavoriteApps(
             api: DesktopProvider = LocalDesktop.current,
-            appsProvider: AppsProvider = api.appsProvider,
+            appsManager: AppsManager = api.appsManager,
         ): State<List<App>> {
 //            var processes by mutableStateOf(0)
 //            processManagerListener { processes = size }
-            return appsProvider.favoriteApps.collectAsState(emptyList())
+            return appsManager.favoriteApps.collectAsState(emptyList())
             // todo
-
         }
     }
 

+ 53 - 0
src/main/kotlin/eu/mjdev/desktop/managers/artificialintelligence/AIManager.kt

@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+package eu.mjdev.desktop.managers.artificialintelligence
+
+import eu.mjdev.desktop.managers.artificialintelligence.base.AIPlugin
+import eu.mjdev.desktop.managers.artificialintelligence.plugins.AiPluginNull
+import eu.mjdev.desktop.managers.artificialintelligence.stt.STTPluginNull
+import eu.mjdev.desktop.managers.artificialintelligence.stt.base.STTPlugin
+import eu.mjdev.desktop.managers.artificialintelligence.tts.TTSPluginMain
+import eu.mjdev.desktop.managers.artificialintelligence.tts.base.TTSPlugin
+import eu.mjdev.desktop.provider.DesktopProvider
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+
+@Suppress("MemberVisibilityCanBePrivate", "unused")
+class AIManager(
+    val api: DesktopProvider,
+    var pluginAI: AIPlugin,
+    var pluginSTT: STTPlugin,
+    var pluginTTS: TTSPlugin
+) {
+    val scope = api.scope
+    val isAvailable: () -> Boolean = { pluginAI !is AiPluginNull }
+
+    fun ask(
+        question: String,
+        block: AIManager.(question: String, result: String) -> Unit
+    ) = scope.launch(Dispatchers.IO) {
+        pluginAI.ask(question).also { result ->
+            block.invoke(this@AIManager, question, result)
+        }
+    }
+
+    fun talk(text: String, clearQueue: Boolean = false) =
+        pluginTTS.talk(text, clearQueue)
+
+    companion object {
+        fun aiManager(
+            api: DesktopProvider,
+            scope: CoroutineScope = api.scope,
+            ai: AIPlugin = AiPluginNull(scope),
+            stt: STTPlugin = STTPluginNull(scope),
+            tts: TTSPlugin = TTSPluginMain(scope)
+        ) = AIManager(api, ai, stt, tts)
+    }
+}

+ 13 - 0
src/main/kotlin/eu/mjdev/desktop/managers/artificialintelligence/base/AIPlugin.kt

@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+package eu.mjdev.desktop.managers.artificialintelligence.base
+
+interface AIPlugin {
+    suspend fun ask(question: String): String
+}

+ 48 - 0
src/main/kotlin/eu/mjdev/desktop/managers/artificialintelligence/plugins/AiPluginGemini.kt

@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+package eu.mjdev.desktop.managers.artificialintelligence.plugins
+
+import dev.shreyaspatil.ai.client.generativeai.GenerativeModel
+import dev.shreyaspatil.ai.client.generativeai.type.content
+import eu.mjdev.desktop.extensions.Custom.loadKey
+import eu.mjdev.desktop.managers.artificialintelligence.base.AIPlugin
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.async
+
+/*
+* Gemini key : https://makersuite.google.com/app/apikey
+* */
+class AiPluginGemini(
+    val scope: CoroutineScope
+) : AIPlugin {
+    private val key = loadKey("gemini")
+    private val generativeModel: GenerativeModel? by lazy {
+        if (key.isNotEmpty()) {
+            GenerativeModel(
+                modelName = "gemini-1.5-pro-latest",
+                apiKey = key
+            )
+        } else null
+    }
+
+    override suspend fun ask(question: String): String = scope.async {
+        var error: Throwable? = null
+        runCatching {
+            if (key.isEmpty()) {
+                throw (Exception("Error: No gemini api key provided, pleas read manual and provide Your api key."))
+            } else {
+                generativeModel?.generateContent(content {
+                    text(question)
+                })?.text
+            }
+        }.onFailure { e ->
+            error = e
+        }.getOrNull() ?: error?.message ?: ""
+    }.await()
+}

+ 18 - 0
src/main/kotlin/eu/mjdev/desktop/managers/artificialintelligence/plugins/AiPluginNull.kt

@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+package eu.mjdev.desktop.managers.artificialintelligence.plugins
+
+import eu.mjdev.desktop.managers.artificialintelligence.base.AIPlugin
+import kotlinx.coroutines.CoroutineScope
+
+@Suppress("UNUSED_PARAMETER")
+class AiPluginNull(scope: CoroutineScope) : AIPlugin {
+    override suspend fun ask(question: String): String =
+        "No a.i. provider selected please read manual."
+}

+ 18 - 0
src/main/kotlin/eu/mjdev/desktop/managers/artificialintelligence/stt/STTPluginNull.kt

@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+package eu.mjdev.desktop.managers.artificialintelligence.stt
+
+import eu.mjdev.desktop.managers.artificialintelligence.stt.base.STTPlugin
+import eu.mjdev.desktop.managers.artificialintelligence.stt.base.STTListener
+import kotlinx.coroutines.CoroutineScope
+
+@Suppress("UNUSED_PARAMETER")
+class STTPluginNull(scope: CoroutineScope) : STTPlugin {
+    override val listeners: List<STTListener> = mutableListOf()
+}

+ 14 - 0
src/main/kotlin/eu/mjdev/desktop/managers/artificialintelligence/stt/base/STTListener.kt

@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+package eu.mjdev.desktop.managers.artificialintelligence.stt.base
+
+interface STTListener {
+    @Suppress("unused")
+    fun onTextRecognized(text: String)
+}

+ 13 - 0
src/main/kotlin/eu/mjdev/desktop/managers/artificialintelligence/stt/base/STTPlugin.kt

@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+package eu.mjdev.desktop.managers.artificialintelligence.stt.base
+
+interface STTPlugin {
+    val listeners: List<STTListener>
+}

+ 33 - 0
src/main/kotlin/eu/mjdev/desktop/managers/artificialintelligence/tts/TTSPluginMain.kt

@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+package eu.mjdev.desktop.managers.artificialintelligence.tts
+
+import eu.mjdev.desktop.managers.artificialintelligence.tts.base.TTSPlugin
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.async
+import kotlinx.coroutines.launch
+import nl.marc_apps.tts.TextToSpeechFactory
+import nl.marc_apps.tts.experimental.ExperimentalDesktopTarget
+
+@OptIn(ExperimentalDesktopTarget::class)
+class TTSPluginMain(
+    val scope: CoroutineScope
+) : TTSPlugin {
+    private val textToSpeech = scope.async {
+        TextToSpeechFactory().createOrNull()
+    }
+
+    override fun talk(text: String, clearQueue: Boolean) {
+        println("talking: $text")
+        scope.launch(Dispatchers.IO) {
+            textToSpeech.await()?.say(text, clearQueue)
+        }
+    }
+}

+ 30 - 0
src/main/kotlin/eu/mjdev/desktop/managers/artificialintelligence/tts/TTSPluginSwift.kt

@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+package eu.mjdev.desktop.managers.artificialintelligence.tts
+
+import eu.mjdev.desktop.helpers.system.Shell
+import eu.mjdev.desktop.managers.artificialintelligence.tts.base.TTSPlugin
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+
+@Suppress("unused")
+class TTSPluginSwift(
+    val scope: CoroutineScope
+) : TTSPlugin {
+    override fun talk(text: String, clearQueue: Boolean) {
+        println("talking: $text")
+        scope.launch(Dispatchers.IO) {
+            Shell.executeAndRead(
+                "/opt/swift/bin/swift",
+                "\"${text.replace("\"", " ")}\""
+            )
+        }
+    }
+}

+ 13 - 0
src/main/kotlin/eu/mjdev/desktop/managers/artificialintelligence/tts/base/TTSPlugin.kt

@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+package eu.mjdev.desktop.managers.artificialintelligence.tts.base
+
+interface TTSPlugin {
+    fun talk(text: String, clearQueue: Boolean = false)
+}

+ 13 - 0
src/main/kotlin/eu/mjdev/desktop/managers/bluetooth/BluetoothManager.kt

@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+package eu.mjdev.desktop.managers.bluetooth
+
+// todo
+@Suppress("unused")
+class BluetoothManager

+ 1 - 1
src/main/kotlin/eu/mjdev/desktop/managers/ConnectivityManager.kt → src/main/kotlin/eu/mjdev/desktop/managers/connectivity/ConnectivityManager.kt

@@ -1,4 +1,4 @@
-package eu.mjdev.desktop.managers
+package eu.mjdev.desktop.managers.connectivity
 
 import dev.tmapps.konnection.Konnection
 import dev.tmapps.konnection.NetworkConnection

+ 39 - 0
src/main/kotlin/eu/mjdev/desktop/managers/dbus/DBusManager.kt

@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+package eu.mjdev.desktop.managers.dbus
+
+// todo
+@Suppress("unused")
+class DBusManager {
+//    val connection by lazy { DBusConnection.getConnection(DBusConnection.DBusBusType.SESSION) }
+//    val settingsInterface by lazy {
+//        connection.getRemoteObject(
+//            "org.freedesktop.portal.Desktop",
+//            "/org/freedesktop/portal/desktop",
+//            SettingsInterface::class.java
+//        )
+//    }
+//
+//    val currentColorScheme get() = settingsInterface.Read("org.freedesktop.appearance", "color-scheme")
+//
+//    init {
+//        println(recursiveVariantValue(currentColorScheme))
+//    }
+//
+//    fun recursiveVariantValue(variant: Variant<*>): Any {
+//        val value = variant.value
+//        if (value !is Variant<*>) return value
+//        else return recursiveVariantValue(value)
+//    }
+//
+//    @DBusInterfaceName("org.freedesktop.portal.Settings")
+//    interface SettingsInterface : DBusInterface {
+//        fun Read(namespace: String, key: String): Variant<*>
+//    }
+}

+ 1 - 1
src/main/kotlin/eu/mjdev/desktop/managers/FileSystemWatcher.kt → src/main/kotlin/eu/mjdev/desktop/managers/filesystem/FileSystemWatcher.kt

@@ -1,4 +1,4 @@
-package eu.mjdev.desktop.managers
+package eu.mjdev.desktop.managers.filesystem
 
 //import io.github.irgaly.kfswatch.KfsDirectoryWatcher
 //import io.github.irgaly.kfswatch.KfsDirectoryWatcherEvent

+ 23 - 0
src/main/kotlin/eu/mjdev/desktop/managers/kcef/KCEFManager.kt

@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+package eu.mjdev.desktop.managers.kcef
+
+import eu.mjdev.desktop.managers.kcef.base.KCEFManagerStub
+import eu.mjdev.desktop.managers.kcef.linux.KCEFManagerLinux
+import eu.mjdev.desktop.provider.DesktopProvider
+import org.jetbrains.skiko.OS
+import org.jetbrains.skiko.hostOs
+
+fun kcefManager(
+    api: DesktopProvider
+) = when (hostOs) {
+    // todo other platforms
+    OS.Linux -> KCEFManagerLinux(api)
+    else -> KCEFManagerStub(api)
+}

+ 25 - 0
src/main/kotlin/eu/mjdev/desktop/managers/kcef/base/KCEFManagerStub.kt

@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+package eu.mjdev.desktop.managers.kcef.base
+
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.mutableStateOf
+import eu.mjdev.desktop.provider.DesktopProvider
+
+open class KCEFManagerStub(
+    val api: DesktopProvider
+) {
+    val initialized: MutableState<Boolean> = mutableStateOf(false)
+    val restartRequired: MutableState<Boolean> = mutableStateOf(false)
+    val downloading: MutableState<Int> = mutableStateOf(0)
+
+    open fun init() {}
+
+    open fun dispose() {}
+}

+ 53 - 0
src/main/kotlin/eu/mjdev/desktop/managers/kcef/linux/KCEFManagerLinux.kt

@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+package eu.mjdev.desktop.managers.kcef.linux
+
+import dev.datlag.kcef.KCEF
+import eu.mjdev.desktop.managers.kcef.base.KCEFManagerStub
+import eu.mjdev.desktop.provider.DesktopProvider
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import java.io.File
+import kotlin.math.max
+
+class KCEFManagerLinux(
+    api: DesktopProvider
+) : KCEFManagerStub(api) {
+    private val scope: CoroutineScope get() = api.scope
+
+    override fun init() {
+        scope.launch(Dispatchers.IO) {
+            if (!initialized.value && !restartRequired.value) {
+                KCEF.init(builder = {
+                    installDir(File("/tmp/kcef-bundle"))
+                    progress {
+                        onDownloading {
+                            downloading.value = max(it, 0F).toInt()
+                        }
+                        onInitialized {
+                            initialized.value = true
+                        }
+                    }
+                    settings {
+                        cachePath = File("cache").absolutePath
+                    }
+                }, onError = { err ->
+                    err?.printStackTrace()
+                }, onRestartRequired = {
+                    restartRequired.value = true
+                })
+            }
+        }
+    }
+
+    override fun dispose() {
+        KCEF.disposeBlocking()
+    }
+}

+ 23 - 0
src/main/kotlin/eu/mjdev/desktop/managers/os/OSManager.kt

@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+package eu.mjdev.desktop.managers.os
+
+import eu.mjdev.desktop.managers.os.base.OSManagerStub
+import eu.mjdev.desktop.managers.os.linux.OSManagerLinux
+import eu.mjdev.desktop.provider.DesktopProvider
+import org.jetbrains.skiko.OS
+import org.jetbrains.skiko.hostOs
+
+fun osManager(
+    api: DesktopProvider
+) = when (hostOs) {
+    // todo other platforms
+    OS.Linux -> OSManagerLinux(api)
+    else -> OSManagerStub(api)
+}

+ 44 - 0
src/main/kotlin/eu/mjdev/desktop/managers/os/base/OSManagerStub.kt

@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+package eu.mjdev.desktop.managers.os.base
+
+import eu.mjdev.desktop.provider.DesktopProvider
+
+open class OSManagerStub(
+    val api: DesktopProvider
+) {
+    open val prettyName: String
+        get() = ""
+    open val name: String
+        get() = ""
+    open val versionId: String
+        get() = ""
+    open val version: String
+        get() = ""
+    open val versionCodeName: String
+        get() = ""
+    open val id: String
+        get() = ""
+    open val idLike: String
+        get() = ""
+    open val homeUrl: String
+        get() = ""
+    open val supportUrl: String
+        get() = ""
+    open val bugReportUrl: String
+        get() = ""
+    open val privacyPolicyUrl: String
+        get() = ""
+    open val codename: String
+        get() = ""
+    open val logo: String
+        get() = ""
+    open val machineName: String
+        get() = ""
+}

+ 48 - 0
src/main/kotlin/eu/mjdev/desktop/managers/os/linux/OSManagerLinux.kt

@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+package eu.mjdev.desktop.managers.os.linux
+
+import eu.mjdev.desktop.helpers.system.Shell
+import eu.mjdev.desktop.managers.os.base.OSManagerStub
+import eu.mjdev.desktop.provider.DesktopProvider
+
+class OSManagerLinux(
+    api: DesktopProvider
+) : OSManagerStub(api) {
+    private val osRelease = OsRelease()
+
+    override val prettyName
+        get() = osRelease.prettyName
+    override val name
+        get() = osRelease.name
+    override val versionId
+        get() = osRelease.versionId
+    override val version
+        get() = osRelease.version
+    override val versionCodeName
+        get() = osRelease.versionCodeName
+    override val id
+        get() = osRelease.id
+    override val idLike
+        get() = osRelease.idLike
+    override val homeUrl
+        get() = osRelease.homeUrl
+    override val supportUrl
+        get() = osRelease.supportUrl
+    override val bugReportUrl
+        get() = osRelease.bugReportUrl
+    override val privacyPolicyUrl
+        get() = osRelease.privacyPolicyUrl
+    override val codename
+        get() = osRelease.ubuntuCodename
+    override val logo
+        get() = osRelease.logo
+    override val machineName
+        get() = Shell.executeAndRead("hostname").trim()
+}

+ 72 - 0
src/main/kotlin/eu/mjdev/desktop/managers/os/linux/OsRelease.kt

@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+package eu.mjdev.desktop.managers.os.linux
+
+import eu.mjdev.desktop.extensions.Custom.lines
+import eu.mjdev.desktop.extensions.Custom.notStartsWith
+import java.io.File
+
+@Suppress("unused")
+class OsRelease(
+    configFile: File = File("/etc/os-release"),
+    configFileContent: List<String> = configFile.lines.filter { line ->
+        line.trim().let { l -> l.isNotEmpty() && l.notStartsWith("#") }
+    }
+) : HashMap<String, String>() {
+    init {
+        configFileContent.map {
+            it.split("=").let { pair -> Pair(pair[0], pair.getOrNull(1)) }
+        }.forEach {
+            put(it.first, it.second ?: "")
+        }
+    }
+
+    val prettyName: String
+        get() = this[PRETTY_NAME].orEmpty()
+    val name: String
+        get() = this[NAME].orEmpty()
+    val versionId: String
+        get() = this[VERSION_ID].orEmpty()
+    val version: String
+        get() = this[VERSION].orEmpty()
+    val versionCodeName: String
+        get() = this[VERSION_CODENAME].orEmpty()
+    val id: String
+        get() = this[ID].orEmpty()
+    val idLike: String
+        get() = this[ID_LIKE].orEmpty()
+    val homeUrl: String
+        get() = this[HOME_URL].orEmpty()
+    val supportUrl: String
+        get() = this[SUPPORT_URL].orEmpty()
+    val bugReportUrl: String
+        get() = this[BUG_REPORT_URL].orEmpty()
+    val privacyPolicyUrl: String
+        get() = this[PRIVACY_POLICY_URL].orEmpty()
+    val ubuntuCodename: String
+        get() = this[UBUNTU_CODENAME].orEmpty()
+    val logo: String
+        get() = this[LOGO].orEmpty()
+
+    companion object {
+        const val PRETTY_NAME = "PRETTY_NAME"
+        const val NAME = "NAME"
+        const val VERSION_ID = "VERSION_ID"
+        const val VERSION = "VERSION"
+        const val VERSION_CODENAME = "VERSION_CODENAME"
+        const val ID = "ID"
+        const val ID_LIKE = "ID_LIKE"
+        const val HOME_URL = "HOME_URL"
+        const val SUPPORT_URL = "SUPPORT_URL"
+        const val BUG_REPORT_URL = "BUG_REPORT_URL"
+        const val PRIVACY_POLICY_URL = "PRIVACY_POLICY_URL"
+        const val UBUNTU_CODENAME = "UBUNTU_CODENAME"
+        const val LOGO = "LOGO"
+    }
+}

+ 1 - 1
src/main/kotlin/eu/mjdev/desktop/managers/ProcessManager.kt → src/main/kotlin/eu/mjdev/desktop/managers/processes/ProcessManager.kt

@@ -6,7 +6,7 @@
  *  w: https://mjdev.org
  */
 
-package eu.mjdev.desktop.managers
+package eu.mjdev.desktop.managers.processes
 
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect

+ 24 - 0
src/main/kotlin/eu/mjdev/desktop/managers/script/ScriptManager.kt

@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+package eu.mjdev.desktop.managers.script
+
+import eu.mjdev.desktop.managers.script.base.ScriptManagerStub
+import eu.mjdev.desktop.managers.script.linux.ScriptManagerLinux
+import eu.mjdev.desktop.provider.DesktopProvider
+import org.jetbrains.skiko.OS
+import org.jetbrains.skiko.hostOs
+
+// todo other platforms
+@Suppress("unused")
+fun scriptManager(
+    api: DesktopProvider
+) = when (hostOs) {
+    OS.Linux -> ScriptManagerLinux(api)
+    else -> ScriptManagerStub(api)
+}

+ 23 - 0
src/main/kotlin/eu/mjdev/desktop/managers/script/base/ScriptManagerStub.kt

@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+package eu.mjdev.desktop.managers.script.base
+
+import eu.mjdev.desktop.provider.DesktopProvider
+
+// todo
+open class ScriptManagerStub(
+    val api: DesktopProvider
+) {
+//    val scriptManager: ScriptEngineManager by lazy { ScriptEngineManager() }
+//    val scriptEngine: ScriptEngine by lazy {
+//        scriptManager.getEngineByName("JavaScript").apply {
+//            // todo
+//        }
+//    }
+}

+ 18 - 0
src/main/kotlin/eu/mjdev/desktop/managers/script/linux/ScriptManagerLinux.kt

@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+package eu.mjdev.desktop.managers.script.linux
+
+import eu.mjdev.desktop.managers.script.base.ScriptManagerStub
+import eu.mjdev.desktop.provider.DesktopProvider
+
+// todo
+class ScriptManagerLinux(
+    api: DesktopProvider
+) : ScriptManagerStub(api) {
+}

+ 23 - 0
src/main/kotlin/eu/mjdev/desktop/managers/theme/ThemeManager.kt

@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+package eu.mjdev.desktop.managers.theme
+
+import eu.mjdev.desktop.managers.theme.base.ThemeManagerStub
+import eu.mjdev.desktop.managers.theme.linux.ThemeManagerLinux
+import eu.mjdev.desktop.provider.DesktopProvider
+import org.jetbrains.skiko.OS
+import org.jetbrains.skiko.hostOs
+
+// todo other platforms
+fun themeManager(
+    api: DesktopProvider
+) = when (hostOs) {
+    OS.Linux -> ThemeManagerLinux(api)
+    else -> ThemeManagerStub(api)
+}

+ 20 - 0
src/main/kotlin/eu/mjdev/desktop/managers/theme/base/ThemeManagerStub.kt

@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+package eu.mjdev.desktop.managers.theme.base
+
+import eu.mjdev.desktop.provider.DesktopProvider
+
+@Suppress("unused")
+open class ThemeManagerStub(
+    private val api: DesktopProvider,
+) {
+    open fun createFromPalette() {
+        // no op
+    }
+}

+ 94 - 26
src/main/kotlin/eu/mjdev/desktop/theme/gtk/GtkThemeHelper.kt → src/main/kotlin/eu/mjdev/desktop/managers/theme/linux/ThemeManagerLinux.kt

@@ -1,4 +1,12 @@
-package eu.mjdev.desktop.theme.gtk
+/*
+ * Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+package eu.mjdev.desktop.managers.theme.linux
 
 import androidx.compose.ui.graphics.Color
 import eu.mjdev.desktop.data.DesktopFile
@@ -8,29 +16,27 @@ import eu.mjdev.desktop.extensions.ColorUtils.isLightColor
 import eu.mjdev.desktop.extensions.Compose.SuperDarkGray
 import eu.mjdev.desktop.extensions.Compose.hexRgb
 import eu.mjdev.desktop.helpers.internal.Palette
-import eu.mjdev.desktop.managers.GnomeManager.Companion.THEME_ADWAITA_DARK
-import eu.mjdev.desktop.managers.GnomeManager.Companion.THEME_CURSOR_BLOOM
-import eu.mjdev.desktop.managers.GnomeManager.Companion.THEME_MJDEV
+import eu.mjdev.desktop.helpers.system.Shell
+import eu.mjdev.desktop.managers.theme.base.ThemeManagerStub
 import eu.mjdev.desktop.provider.DesktopProvider
 import java.io.File
 
-
-// global theme : ~/.config/gtk-2.0 and ~/.config/gtk-3.0 and ~/.config/gtk-4.0
 @Suppress("unused", "MemberVisibilityCanBePrivate")
-class GtkThemeHelper(
-    private val api: DesktopProvider,
-    private val palette: Palette = api.palette,
-    private val themeName: String = THEME_MJDEV,
-    private val themesDir: File = File(api.homeDir, ".themes"),
-    private val themeDir: File = File(themesDir, themeName),
-    private val themeDesktopFile: File = File(themeDir, "index.theme"),
-    private val gtk2ThemeDir: File = File(themeDir, "gtk-2.0"),
-    private val gtk3ThemeDir: File = File(themeDir, "gtk-3.0"),
-    private val gtk4ThemeDir: File = File(themeDir, "gtk-4.0"),
-    private val gtk3CssFile: File = File(gtk3ThemeDir, "gtk.css"),
-    private val gtk4CssFile: File = File(gtk4ThemeDir, "gtk.css"),
-) {
-    fun createFromPalette() {
+class ThemeManagerLinux(
+    api: DesktopProvider,
+) : ThemeManagerStub(api) {
+    private val palette: Palette = api.palette
+    private val themeName: String = THEME_MJDEV
+    private val themesDir: File = File(api.homeDir, ".themes")
+    private val themeDir: File = File(themesDir, themeName)
+    private val themeDesktopFile: File = File(themeDir, "index.theme")
+    private val gtk2ThemeDir: File = File(themeDir, "gtk-2.0")
+    private val gtk3ThemeDir: File = File(themeDir, "gtk-3.0")
+    private val gtk4ThemeDir: File = File(themeDir, "gtk-4.0")
+    private val gtk3CssFile: File = File(gtk3ThemeDir, "gtk.css")
+    private val gtk4CssFile: File = File(gtk4ThemeDir, "gtk.css")
+
+    override fun createFromPalette() {
         createDesktopFile()
         createCssFile(gtk3CssFile)
         createCssFile(gtk4CssFile)
@@ -98,7 +104,8 @@ class GtkThemeHelper(
  */
     // ~/.config/gtk-3.0/gtk.css
     // ~/.config/gtk-4.0/gtk.css
-    class Theme(val file: File) {
+    @Suppress("MemberVisibilityCanBePrivate")
+    class GtkTheme(val file: File) {
         var bgColor: Color = Color.SuperDarkGray
         var fgColor: Color = Color.SuperDarkGray
         var baseColor: Color = Color.SuperDarkGray
@@ -307,13 +314,74 @@ class GtkThemeHelper(
         """.trimIndent()
     }
 
+    fun setColorScheme(
+        schemeName: String
+    ) {
+        Shell.executeAndRead(
+            "gsettings",
+            "set",
+            "org.gnome.desktop.interface",
+            "color-scheme",
+            schemeName
+        )
+    }
+
+    fun setGTKTheme(
+        themeName: String = THEME_YARU
+    ) = Shell.executeAndRead(
+        "gsettings",
+        "set",
+        "org.gnome.desktop.interface",
+        "gtk-theme",
+        themeName
+    )
+
+
+    fun getGTKTheme(): String = Shell.executeAndRead(
+        "gsettings",
+        "get",
+        "org.gnome.desktop.interface",
+        "gtk-theme"
+    ).replace("'", "").trim()
+
+    fun setIconTheme(
+        themeName: String = THEME_YARU
+    ) = Shell.executeAndRead(
+        "gsettings",
+        "set",
+        "org.gnome.desktop.interface",
+        "icon-theme",
+        themeName
+    )
+
+    fun setSoundTheme(
+        themeName: String = THEME_YARU
+    ) = Shell.executeAndRead(
+        "gsettings",
+        "set",
+        "org.gnome.desktop.sound",
+        "gtk-theme",
+        themeName
+    )
+
+    fun setDarkColorScheme() =
+        setColorScheme(COLOR_SCHEME_PREFER_DARK)
+
     companion object {
-        fun theme(
+        const val COLOR_SCHEME_PREFER_DARK = "prefer-dark"
+        const val COLOR_SCHEME_MJDEV = "mjdev"
+        const val THEME_YARU = "Yaru"
+        const val THEME_ADWAITA = "Adwaita"
+        const val THEME_ADWAITA_DARK = "Adwaita-dark"
+        const val THEME_MJDEV = "Mjdev"
+        const val THEME_CURSOR_BLOOM = "bloom"
+
+        private fun theme(
             file: File,
-            block: Theme.() -> Unit
-        ) = Theme(file).apply(block)
+            block: GtkTheme.() -> Unit
+        ) = GtkTheme(file).apply(block)
 
-        fun Theme.write() {
+        private fun GtkTheme.write() {
             toString().also { data ->
                 file.apply {
                     parentFile.mkdirs()
@@ -323,4 +391,4 @@ class GtkThemeHelper(
             }
         }
     }
-}
+}

+ 3 - 3
src/main/kotlin/eu/mjdev/desktop/managers/WindowsManager.kt → src/main/kotlin/eu/mjdev/desktop/managers/window/WindowsManager.kt

@@ -8,12 +8,12 @@
 
 @file:Suppress("LocalVariableName")
 
-package eu.mjdev.desktop.managers
+package eu.mjdev.desktop.managers.window
 
-import com.sun.jna.platform.unix.X11.*
+import com.sun.jna.platform.unix.X11.Window
 import eu.mjdev.desktop.data.App
 import eu.mjdev.desktop.provider.DesktopProvider
-import kotlinx.coroutines.*
+import kotlinx.coroutines.CoroutineScope
 
 @Suppress("unused")
 class WindowsManager(

+ 0 - 119
src/main/kotlin/eu/mjdev/desktop/provider/AIProvider.kt

@@ -1,119 +0,0 @@
-package eu.mjdev.desktop.provider
-
-import dev.shreyaspatil.ai.client.generativeai.GenerativeModel
-import dev.shreyaspatil.ai.client.generativeai.type.content
-import eu.mjdev.desktop.extensions.Custom.loadKey
-import eu.mjdev.desktop.helpers.system.Shell
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.async
-import kotlinx.coroutines.launch
-import nl.marc_apps.tts.TextToSpeechFactory
-import nl.marc_apps.tts.experimental.ExperimentalDesktopTarget
-
-/*
-* Gemini key : https://makersuite.google.com/app/apikey
-* */
-@Suppress("MemberVisibilityCanBePrivate", "unused", "UNUSED_PARAMETER")
-class AIProvider(
-    val scope: CoroutineScope,
-    var pluginAI: IAIPlugin = AiPluginNull(scope),
-    val pluginSTT: ISTTPlugin = STTPluginNull(scope),
-    val pluginTTS: TTSPlugin = TTSPluginMain(scope),
-    val isAvailable: () -> Boolean = { pluginAI !is AiPluginNull }
-) {
-    fun ask(
-        question: String,
-        block: AIProvider.(question: String, result: String) -> Unit
-    ) = scope.launch(Dispatchers.IO) {
-        pluginAI.ask(question).also { result ->
-            block.invoke(this@AIProvider, question, result)
-        }
-    }
-
-    fun talk(text: String, clearQueue: Boolean = false) =
-        pluginTTS.talk(text, clearQueue)
-
-    interface IAIPlugin {
-        suspend fun ask(question: String): String
-    }
-
-    interface STTListener {
-        fun onTextRecognized(text: String)
-    }
-
-    interface ISTTPlugin {
-        val listeners: List<STTListener>
-    }
-
-    interface TTSPlugin {
-        fun talk(text: String, clearQueue: Boolean = false)
-    }
-
-    class STTPluginNull(scope: CoroutineScope) : ISTTPlugin {
-        override val listeners: List<STTListener> = mutableListOf()
-    }
-
-    @OptIn(ExperimentalDesktopTarget::class)
-    class TTSPluginMain(
-        val scope: CoroutineScope
-    ) : TTSPlugin {
-        private val textToSpeech = scope.async {
-            TextToSpeechFactory().createOrNull()
-        }
-
-        override fun talk(text: String, clearQueue: Boolean) {
-            println("talking: $text")
-            scope.launch(Dispatchers.IO) {
-                textToSpeech.await()?.say(text, clearQueue)
-            }
-        }
-    }
-
-    class TTSPluginSwift(
-        val scope: CoroutineScope
-    ) : TTSPlugin {
-        override fun talk(text: String, clearQueue: Boolean) {
-            println("talking: $text")
-            scope.launch(Dispatchers.IO) {
-                Shell.executeAndRead(
-                    "/opt/swift/bin/swift",
-                    "\"${text.replace("\"", " ")}\""
-                )
-            }
-        }
-    }
-
-    class AiPluginNull(scope: CoroutineScope) : IAIPlugin {
-        override suspend fun ask(question: String): String = ""
-    }
-
-    class AiPluginGemini(
-        val scope: CoroutineScope
-    ) : IAIPlugin {
-        private val key = loadKey("gemini")
-        private val generativeModel: GenerativeModel? by lazy {
-            if (key.isNotEmpty()) {
-                GenerativeModel(
-                    modelName = "gemini-1.5-pro-latest",
-                    apiKey = key
-                )
-            } else null
-        }
-
-        override suspend fun ask(question: String): String = scope.async {
-            var error: Throwable? = null
-            runCatching {
-                if (key.isEmpty()) {
-                    throw (Exception("Error: No gemini api key provided, pleas read manual and provide Your api key."))
-                } else {
-                    generativeModel?.generateContent(content {
-                        text(question)
-                    })?.text
-                }
-            }.onFailure { e ->
-                error = e
-            }.getOrNull() ?: error?.message ?: ""
-        }.await()
-    }
-}

+ 22 - 31
src/main/kotlin/eu/mjdev/desktop/provider/DesktopProvider.kt

@@ -11,14 +11,16 @@ import eu.mjdev.desktop.data.DesktopFile
 import eu.mjdev.desktop.data.User
 import eu.mjdev.desktop.extensions.Compose.asyncImageLoader
 import eu.mjdev.desktop.helpers.internal.Palette
-import eu.mjdev.desktop.helpers.system.OsRelease
 import eu.mjdev.desktop.helpers.system.Shell
-import eu.mjdev.desktop.managers.ConnectivityManager
-import eu.mjdev.desktop.managers.GnomeManager
-import eu.mjdev.desktop.managers.KCEFHelper
-import eu.mjdev.desktop.managers.ProcessManager
-import eu.mjdev.desktop.provider.AIProvider.AiPluginGemini
-import eu.mjdev.desktop.theme.gtk.GtkThemeHelper
+import eu.mjdev.desktop.managers.apps.AppsManager
+import eu.mjdev.desktop.managers.artificialintelligence.AIManager
+import eu.mjdev.desktop.managers.artificialintelligence.AIManager.Companion.aiManager
+import eu.mjdev.desktop.managers.artificialintelligence.plugins.AiPluginGemini
+import eu.mjdev.desktop.managers.connectivity.ConnectivityManager
+import eu.mjdev.desktop.managers.kcef.kcefManager
+import eu.mjdev.desktop.managers.os.osManager
+import eu.mjdev.desktop.managers.processes.ProcessManager
+import eu.mjdev.desktop.managers.theme.themeManager
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import java.awt.Desktop
@@ -38,42 +40,31 @@ class DesktopProvider(
     val isFirstStart: Boolean = true, // todo
     val isInstalled: Boolean = false, // todo
 ) : AutoCloseable {
-    //    val scriptManager: ScriptEngineManager by lazy { ScriptEngineManager() }
-//    val scriptEngine: ScriptEngine by lazy {
-//        scriptManager.getEngineByName("JavaScript").apply {
-//            // todo
-//        }
-//    }
-    val osDetails by lazy { OsRelease() }
+    val osManager by lazy { osManager(this) }
     val toolkit: Toolkit by lazy { Toolkit.getDefaultToolkit() }
-    val kcefHelper by lazy { KCEFHelper(this) }
-    val gnome by lazy { GnomeManager() }
+    val kcefHelper by lazy { kcefManager(this) }
     val connectionManager by lazy { ConnectivityManager() }
-
-    //    val windowsManager by lazy { WindowsManager(this) }
-//    val dbus: DBus by lazy { DBus() }
-    val ai: AIProvider by lazy {
-        // todo user can configure
-        AIProvider(scope, AiPluginGemini(scope))
+    val ai: AIManager by lazy {
+        aiManager(this).apply {
+            // todo user can configure
+            pluginAI = AiPluginGemini(scope)
+        }
     }
-    val appsProvider by lazy { AppsProvider(this) }
-
-    //    val mounts by lazy {
-//        FileSystemWatcher(scope) {
-//            println("Mounted: ${this.targetDirectory}")
-//        }
-//    }
-    val gtkTheme by lazy { GtkThemeHelper(this) }
+    val appsManager by lazy { AppsManager(this) }
+    val themeManager by lazy { themeManager(this) }
     val palette by lazy { Palette(this) }
     val desktopUtils: Desktop by lazy { Desktop.getDesktop() }
     val processManager by lazy { ProcessManager() }
+    //    val windowsManager by lazy { WindowsManager(this) }
+    //    val dbus: DBus by lazy { DBus() }
 
     val homeDir
         get() = currentUser.homeDir
     val allUsers
         get() = User.allUsers
+    val currentLocale get() = appsManager.currentLocale
     val machineName
-        get() = Shell.executeAndRead("hostname").trim()
+        get() = osManager.machineName
 
     // todo state
     val currentUser: User

+ 9 - 9
src/main/kotlin/eu/mjdev/desktop/provider/DesktopScope.kt

@@ -26,8 +26,8 @@ open class DesktopScope(
     val isTransparencySupported
         get() = api.isTransparencySupported
 
-    val osDetails
-        get() = api.osDetails
+    val osManager
+        get() = api.osManager
 
     val machineName
         get() = api.machineName
@@ -68,20 +68,20 @@ open class DesktopScope(
 //    val dbus
 //        get() = api.dbus
 
-    val gnome
-        get() = api.gnome
+    val currentLocale
+        get() = api.currentLocale
 
-    val appsProvider
-        get() = api.appsProvider
+    val appsManager
+        get() = api.appsManager
 
     val appCategories
-        get() = appsProvider.appCategories
+        get() = appsManager.appCategories
 
     val allApps
-        get() = appsProvider.allApps
+        get() = appsManager.allApps
 
     val favoriteApps
-        get() = appsProvider.favoriteApps
+        get() = appsManager.favoriteApps
 
     val palette
         get() = api.palette