Przeglądaj źródła

code improvements

code improvements

code improvements

code improvements

code improvements

code improvements

code improvements

code improvements

code improvements

code improvements

code improvements

code improvements

code improvements

code improvements

code improvements
Milan Jurkulak 1 rok temu
rodzic
commit
718277f36b
34 zmienionych plików z 347 dodań i 668 usunięć
  1. 0 15
      build.gradle.kts
  2. 29 2
      buildSrc/build.gradle.kts
  3. 17 20
      buildSrc/src/main/kotlin/BuildExt.kt
  4. 26 9
      buildSrc/src/main/kotlin/KotlinDependencyHandlerExt.kt
  5. 29 52
      composeApp/composeApp.gradle.kts
  6. 1 1
      composeApp/src/androidMain/kotlin/org/mjdev/desktop/context/DesktopContext.kt
  7. 1 1
      composeApp/src/commonMain/kotlin/org/mjdev/desktop/components/chart/DonutChart.kt
  8. 1 1
      composeApp/src/commonMain/kotlin/org/mjdev/desktop/components/desktoppanel/applets/DesktopPanelIcon.kt
  9. 0 28
      composeApp/src/commonMain/kotlin/org/mjdev/desktop/components/web/ComposeWebView.kt
  10. 0 23
      composeApp/src/commonMain/kotlin/org/mjdev/desktop/components/web/CookieManagerCompat.kt
  11. 0 86
      composeApp/src/commonMain/kotlin/org/mjdev/desktop/components/web/WebEngineKt.kt
  12. 26 236
      composeApp/src/commonMain/kotlin/org/mjdev/desktop/components/web/WebView.kt
  13. 0 23
      composeApp/src/commonMain/kotlin/org/mjdev/desktop/components/web/WebViewImpl.kt
  14. 17 5
      composeApp/src/commonMain/kotlin/org/mjdev/desktop/extensions/CustomExt.kt
  15. 1 0
      composeApp/src/commonMain/kotlin/org/mjdev/desktop/extensions/FocusState.kt
  16. 2 1
      composeApp/src/commonMain/kotlin/org/mjdev/desktop/extensions/LaunchedEffect.kt
  17. 11 2
      composeApp/src/commonMain/kotlin/org/mjdev/desktop/extensions/System.kt
  18. 1 1
      composeApp/src/commonMain/kotlin/org/mjdev/desktop/log/Log.kt
  19. 0 2
      composeApp/src/commonMain/kotlin/org/mjdev/desktop/managers/ai/AiManager.kt
  20. 4 2
      composeApp/src/commonMain/kotlin/org/mjdev/desktop/managers/ai/actions/ActionCurrentTime.kt
  21. 1 1
      composeApp/src/commonMain/kotlin/org/mjdev/desktop/managers/ai/actions/base/ActionsProvider.kt
  22. 6 3
      composeApp/src/commonMain/kotlin/org/mjdev/desktop/managers/ai/plugins/AiPluginEmpty.kt
  23. 35 38
      composeApp/src/commonMain/kotlin/org/mjdev/desktop/managers/ai/plugins/AiPluginGemini.kt
  24. 30 8
      composeApp/src/commonMain/kotlin/org/mjdev/desktop/managers/ai/plugins/AiPluginOllama.kt
  25. 35 60
      composeApp/src/commonMain/kotlin/org/mjdev/desktop/managers/ai/plugins/AiPluginOpenAi.kt
  26. 0 10
      composeApp/src/commonMain/kotlin/org/mjdev/desktop/managers/ai/plugins/AiPluginPerplexity.kt
  27. 6 0
      composeApp/src/desktopMain/kotlin/org/mjdev/desktop/components/main/MainWindow.kt
  28. 1 3
      composeApp/src/desktopMain/kotlin/org/mjdev/desktop/context/DeskptopContext.kt
  29. 4 5
      composeApp/src/desktopMain/kotlin/org/mjdev/desktop/main/Main.kt
  30. 6 2
      composeApp/src/desktopMain/kotlin/org/mjdev/desktop/managers/apps/AppsManager.kt
  31. 6 5
      composeApp/src/desktopMain/kotlin/org/mjdev/desktop/managers/theme/linux/ThemeManagerLinux.kt
  32. 1 1
      gradle.properties
  33. 33 7
      gradle/libs.versions.toml
  34. 17 15
      settings.gradle.kts

+ 0 - 15
build.gradle.kts

@@ -14,7 +14,6 @@ plugins {
     alias(libs.plugins.gradle.ktlint)
     alias(libs.plugins.manes.versions) apply false
     alias(libs.plugins.kotlin.android) apply false
-//    alias(libs.plugins.vlc.setup) apply true
 //    id("com.github.gmazzo.buildconfig") version "5.6.5"
 }
 
@@ -45,20 +44,6 @@ plugins {
 //    buildConfigField("com.github.gmazzo.buildconfig.demos.kts.SomeData", "DATA", "SomeData(\"a\", 1)")
 //}
 
-// vlc setup
-//vlcSetup {
-//    // todo
-//    //val vlcVersionLibs: String = libs.versions.vlc.get().toString()
-//    vlcVersion.set("3.0.21-2")
-//
-//    shouldCompressVlcFiles = true
-//    shouldIncludeAllVlcFiles = true
-//
-//    pathToCopyVlcLinuxFilesTo = rootDir.resolve("composeApp/src/commonMain/composeResources/vlc/")
-//    pathToCopyVlcMacosFilesTo = rootDir.resolve("composeApp/src/commonMain/composeResources/vlc/")
-//    pathToCopyVlcLinuxFilesTo = rootDir.resolve("composeApp/src/commonMain/composeResources/vlc/")
-//}
-
 //dependencies {
 //    kover(project(":composeApp"))
 //}

+ 29 - 2
buildSrc/build.gradle.kts

@@ -2,12 +2,35 @@
 
 plugins {
     `kotlin-dsl`
+    alias(libs.plugins.kotlin.multiplatform) apply false
 }
 
 repositories {
     mavenLocal()
     gradlePluginPortal()
     mavenCentral()
+    maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
+    maven("https://maven.pkg.jetbrains.space/kotlin/p/wasm/experimental")
+    maven("https://androidx.dev/storage/compose-compiler/repository")
+    maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev")
+    maven("https://gitlab.com/api/v4/projects/38224197/packages/maven")
+    maven("https://oss.sonatype.org/content/repositories/snapshots")
+    maven("https://jogamp.org/deployment/maven")
+    maven("https://jitpack.io")
+    maven("https://maven.mozilla.org/maven2/")
+    maven("https://maven.pkg.github.com/tuProlog/2p-kt")
+    maven("https://dl.bintray.com/animeshz/maven")
+    maven("https://plugins.gradle.org/m2/")
+    maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
+    maven("https://dl.bintray.com/kotlin/ktor")
+    maven("https://central.sonatype.com/repository/maven-snapshots")
+//        google {
+//            mavenContent {
+//                includeGroupAndSubgroups("androidx")
+//                includeGroupAndSubgroups("com.android")
+//                includeGroupAndSubgroups("com.google")
+//            }
+//        }
     google()
 }
 
@@ -41,9 +64,13 @@ val TASK_BUILD_PLUGIN = "installPlugin"
 val PROJECT_IDE_PLUGIN = "studio-plugin"
 val DEPENDENCY_BUILD_SRC = ":$PROJECT_IDE_PLUGIN:$TASK_BUILD_PLUGIN"
 
-fun Task.execute(action: Action<ExecSpec>) = this.project.providers.exec(action)
+fun Task.execute(
+    action: Action<ExecSpec>
+) = project.providers.exec(action)
+
 val Task.pluginProject
-    get() = this.project.rootProject.project(PROJECT_IDE_PLUGIN)
+    get() = project.rootProject.project(PROJECT_IDE_PLUGIN)
+
 val Task.pluginDistributionFile
     get() = "${pluginProject.path}/$FOLDER_BUILD/$FOLDER_DISTRIBUTION/$PLUGIN_FILE_NAME"
 

+ 17 - 20
buildSrc/src/main/kotlin/BuildExt.kt

@@ -1,25 +1,23 @@
-import org.gradle.api.NamedDomainObjectContainer
 import org.gradle.api.Project
 import org.gradle.api.plugins.JavaPluginExtension
 import org.gradle.api.provider.Provider
 import org.gradle.jvm.toolchain.JavaLanguageVersion
 import org.gradle.kotlin.dsl.getByType
-import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
 
-enum class OS { WINDOWS, LINUX, MAC, OTHER }
-
-fun getCurrentOs(): OS {
-    val osName = System
-        .getProperty("os.name")
-        .lowercase()
-        .trim()
-    return when {
-        osName.startsWith("win") -> OS.WINDOWS
-        osName.startsWith("mac") -> OS.MAC
-        osName == "linux" -> OS.LINUX
-        else -> OS.OTHER
-    }
-}
+//enum class OS { WINDOWS, LINUX, MAC, OTHER }
+
+//fun getCurrentOs(): OS {
+//    val osName = System
+//        .getProperty("os.name")
+//        .lowercase()
+//        .trim()
+//    return when {
+//        osName.startsWith("win") -> OS.WINDOWS
+//        osName.startsWith("mac") -> OS.MAC
+//        osName == "linux" -> OS.LINUX
+//        else -> OS.OTHER
+//    }
+//}
 
 val Project.java: JavaPluginExtension
     get() = extensions.getByType()
@@ -32,9 +30,9 @@ fun Project.setJavaLanguageVersion(
     java.toolchain.languageVersion.set(jlv)
 }
 
-fun NamedDomainObjectContainer<KotlinSourceSet>.getOrCreate(
-    name: String
-): KotlinSourceSet = findByName(name) ?: create(name)
+//fun NamedDomainObjectContainer<KotlinSourceSet>.getOrCreate(
+//    name: String
+//): KotlinSourceSet = findByName(name) ?: create(name)
 
 //fun Project.kotlinSourceSets(
 //    config: Action<NamedDomainObjectContainer<KotlinSourceSet>>
@@ -61,4 +59,3 @@ fun NamedDomainObjectContainer<KotlinSourceSet>.getOrCreate(
 //) = with(kotlin) {
 //    sourceSets(config)
 //}
-

+ 26 - 9
buildSrc/src/main/kotlin/KotlinDependencyHandlerExt.kt

@@ -1,9 +1,26 @@
-//    fun KotlinDependencyHandler.kapt(
-//        dependency:String
-//    ) = configurations["kapt"].dependencies.add(dependency)
-
-//    fun DependencyHandlerScope.kapt(
-//        dependency: Provider<MinimalExternalModuleDependency>
-//    ) {
-//        configurations["kapt"].dependencies.add(dependency.get())
-//    }
+import org.gradle.api.Action
+import org.gradle.api.NamedDomainObjectContainer
+import org.gradle.api.Project
+import org.gradle.api.artifacts.Dependency
+import org.gradle.api.artifacts.MinimalExternalModuleDependency
+import org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler
+import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
+
+//fun KotlinDependencyHandler.kapt(
+//    dependency: String
+//) = configurations["kapt"].dependencies.add(dependency)
+
+//fun DependencyHandlerScope.kapt(
+//    dependency: Provider<MinimalExternalModuleDependency>
+//) {
+//    configurations["kapt"].dependencies.add(dependency.get())
+//}
+
+//fun Project.kotlinSourceSets(
+//    config: Action<NamedDomainObjectContainer<KotlinSourceSet>>
+//) = with(kotlin) {
+//    sourceSets(config)
+//}
+
+fun KotlinDependencyHandler.implementationPlatform(dependencyNotation: Any): Dependency? =
+    implementation(project.dependencies.platform(dependencyNotation))

+ 29 - 52
composeApp/composeApp.gradle.kts

@@ -31,6 +31,7 @@ plugins {
     alias(libs.plugins.compose)
     alias(libs.plugins.compose.compiler)
     alias(libs.plugins.vlc.setup)
+    alias(libs.plugins.devtools.ksp)
 }
 
 //</editor-fold>------------------------------------------------------------------------------------
@@ -51,11 +52,6 @@ fun KotlinTargetContainerWithPresetFunctions.desktopTarget(
     configure.execute(this)
 }
 
-@OptIn(ExperimentalWasmDsl::class)
-fun KotlinTargetContainerWithWasmPresetFunctions.wasmJsTarget(
-    configure: Action<KotlinWasmJsTargetDsl>
-) = wasmJs(configure)
-
 val Project.compose: ComposeExtension
     get() = extensions.getByType()
 
@@ -71,20 +67,6 @@ fun targets(
     config: Action<KotlinMultiplatformExtension>
 ) = kotlin(config)
 
-// todo
-@Suppress("UNUSED_PARAMETER", "UnusedReceiverParameter")
-fun Project.wasmjs(config: DesktopExtension.() -> Unit) {
-    // todo
-//    compose.wasmJs(config)
-}
-
-// todo
-fun Project.kotlinSourceSets(
-    config: Action<NamedDomainObjectContainer<KotlinSourceSet>>
-) = with(kotlin) {
-    sourceSets(config)
-}
-
 //</editor-fold>------------------------------------------------------------------------------------
 
 //<editor-fold desc="configurations">---------------------------------------------------------------
@@ -129,7 +111,6 @@ targets {
             jvmTarget.set(JvmTarget.JVM_17)
         }
     }
-
 }
 
 //</editor-fold>------------------------------------------------------------------------------------
@@ -173,6 +154,8 @@ kotlin {
                 implementation(libs.kotlinx.serialization.json)
                 // desktop file linux
                 implementation(libs.ini4j)
+                // files
+                implementation(libs.okio)
                 // tts
                 implementation(libs.tts)
                 implementation(libs.tts.compose)
@@ -185,35 +168,39 @@ kotlin {
                 // timeline
                 implementation(libs.jetlime)
                 // html to text
-                implementation("com.github.BartoszJarocki:android-boilerpipe:-SNAPSHOT")
+                implementation(libs.html2text)
                 // markdown
-                implementation("io.noties.markwon:core:4.6.2")
-                // open ai
-                implementation (platform("com.aallam.openai:openai-client-bom:3.2.0"))
-                implementation ("com.aallam.openai:openai-client")
+                implementation(libs.markwon)
+                // strings
+                implementation(libs.human.readable)
+                // alerts
+                implementation(libs.alert.kmp)
+                // ai plugins
+                implementation(libs.flock.aigentic.core)
+                implementation(libs.flock.aigentic.openai)
+                implementation(libs.flock.aigentic.gemini)
+                implementation(libs.flock.aigentic.ollama)
+                implementation(libs.flock.aigentic.vertexai)
+                implementation(libs.flock.aigentic.http)
+                implementation(libs.flock.aigentic.openapi)
+//                ksp("community.flock.aigentic:ksp-processor:0.1.0")
                 // ktor
-                implementation("io.ktor:ktor-client-core:2.3.9")
-                implementation("io.ktor:ktor-client-cio:2.3.9")
-                implementation("io.ktor:ktor-client-content-negotiation:2.3.9")
-                implementation("io.ktor:ktor-client-logging:2.3.9")
-                implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.9")
+                implementation(libs.ktor.client.core)
+                implementation(libs.ktor.client.cio)
+                implementation(libs.ktor.client.content.negotiation)
+                implementation(libs.ktor.client.logging)
+                implementation(libs.ktor.serialization.kotlinx.json)
                 // okhttp
-                implementation("com.squareup.okhttp3:okhttp:4.12.0")
-                implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")
-                implementation("com.squareup.okhttp3:okhttp-urlconnection:4.12.0")
-                implementation("com.squareup.okhttp3:okhttp-dnsoverhttps:4.12.0")
-                implementation("com.squareup.okhttp3:okhttp-tls:4.12.0")
+                implementation(libs.okhttp3.client)
+                implementation(libs.okhttp3.logging.interceptor)
+                implementation(libs.okhttp3.urlconnection)
+                implementation(libs.okhttp3.dnsoverhttps)
+                implementation(libs.okhttp3.tls)
 //                implementation("com.squareup.okhttp3:okhttp-ws:4.12.0")
                 // notification
                 // implementation("io.github.shadmanadman:knotif:0.56.0")
                 // sensors
                 //implementation("io.github.shadmanadman:KSensor:0.59.0")
-                // flowmvi
-//                implementation(libs.flowmvi.core)
-//                implementation(libs.flowmvi.compose)
-//                implementation(libs.flowmvi.android)
-//                implementation(libs.flowmvi.savedstate)
-//                implementation(libs.flowmvi.debugger)
                 // locale
 //            implementation(libs.i18n4k)
                 // anims kottie
@@ -231,15 +218,8 @@ kotlin {
 //            implementation("com.mohamedrejeb.richeditor:richeditor-compose:1.0.0-rc10")
 //                implementation (libs.popkorn)
 //                kapt(libs.popkorn.compiler)
-//                implementation("io.github.khubaibkhan4:mediaplayer-kmp:2.0.6")
-                // strings
-                implementation(libs.human.readable)
-                // alerts
-                implementation(libs.alert.kmp)
                 // tor
                 // implementation("io.matthewnelson.kmp-tor:runtime:2.1.0")
-                // files
-                implementation(libs.okio)
             }
         }
 
@@ -260,8 +240,6 @@ kotlin {
                 implementation(libs.androidx.core.splashscreen)
                 // permissions
                 implementation(libs.accompanist.permissions)
-                // ai gemini
-                implementation(libs.sh.google.generative.ai)
                 // sensors
                 //implementation("io.github.shadmanadman:KSensor:0.59.0")
             }
@@ -282,8 +260,6 @@ kotlin {
                 implementation(libs.okhttp3.client)
                 // images okhttp
                 implementation(libs.coil.okhttp)
-                // ai gemini
-                implementation(libs.sh.google.generative.ai)
                 // chatgpt
                 implementation(libs.ychat)
                 // desktop files
@@ -341,6 +317,7 @@ desktop {
     group = libs.versions.app.pkg.name.get()
     version = libs.versions.app.pkg.version.get()
     application {
+        jvmArgs("-Dprism.order=sw")
         mainClass = "org.mjdev.desktop.main.MainKt"
         nativeDistributions {
             packageName = libs.versions.app.name.get()

+ 1 - 1
composeApp/src/androidMain/kotlin/org/mjdev/desktop/context/DesktopContext.kt

@@ -133,7 +133,7 @@ class DesktopContext(
         IAiManager::class -> AiManager(
             context = this,
             // todo user can configure
-            pluginAI = AiPluginOpenAi(this@DesktopContext),
+            pluginAI = AiPluginGemini(this@DesktopContext),
             pluginTTS = TTSPluginAndroid(this@DesktopContext),
             pluginSTT = STTPluginEmpty(this@DesktopContext)
         )

+ 1 - 1
composeApp/src/commonMain/kotlin/org/mjdev/desktop/components/chart/DonutChart.kt

@@ -61,7 +61,7 @@ fun DonutChart(
     LaunchedEffect(Unit) {
         while (isActive) {
             delay(refreshTimeout)
-            needRefresh = currentTimeMillis()
+            needRefresh = currentTimeMillis
         }
     }
 }

+ 1 - 1
composeApp/src/commonMain/kotlin/org/mjdev/desktop/components/desktoppanel/applets/DesktopPanelIcon.kt

@@ -64,7 +64,7 @@ fun DesktopPanelIcon(
     }
     val isStarting by rememberCalculated(app) { app?.isStarting ?: false }
     val isRunning by rememberCalculated(app) {
-        app?.isRunning == true || processManager.hasAppProcess(app)
+        app?.isRunning == true // || processManager.hasAppProcess(app) // todo
     }
     Box(
         modifier = Modifier

+ 0 - 28
composeApp/src/commonMain/kotlin/org/mjdev/desktop/components/web/ComposeWebView.kt

@@ -1,28 +0,0 @@
-package org.mjdev.desktop.components.web
-
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import org.jetbrains.compose.ui.tooling.preview.Preview
-import org.mjdev.desktop.extensions.Compose.preview
-import org.mjdev.desktop.context.DesktopContextScope.Companion.withDesktopContext
-
-@Suppress( "FunctionName", "unused")
-@Composable
-fun ComposeWebView(
-    modifier: Modifier = Modifier,
-    url: String = "https://m.youtube.com/watch?v=BwY6LD7KAQk",
-) = withDesktopContext {
-//    val state = rememberWebViewState(url)
-//    val navigator: WebViewNavigator = rememberWebViewNavigator()
-//    WebView(
-//        modifier = modifier,
-//        state = state,
-//        navigator = navigator
-//    )
-}
-
-@Preview
-@Composable
-fun PreviewComposeWebView() = preview {
-    ComposeWebView()
-}

+ 0 - 23
composeApp/src/commonMain/kotlin/org/mjdev/desktop/components/web/CookieManagerCompat.kt

@@ -1,23 +0,0 @@
-package org.mjdev.desktop.components.web
-
-//import java.net.CookieHandler
-//import java.net.URI
-
-object CookieManagerCompat {
-//    fun setCookie(
-//        url: String,
-//        headers: HashMap<String, String>
-//    ) {
-//        LinkedHashMap<String, List<String>>().apply {
-//            this["Set-Cookie"] = headers.map { "${it.key}=${it.value}" }
-//        }.also { nHeaders ->
-//            CookieHandler.getDefault().put(URI.create(url), nHeaders)
-//        }
-//    }
-//
-//    fun getCookie(uri: URI): MutableMap<String, String>? {
-//        return CookieHandler.getDefault().get(uri, HashMap()).map {
-//            it.key to it.value[0]
-//        }.toMap().toMutableMap()
-//    }
-}

+ 0 - 86
composeApp/src/commonMain/kotlin/org/mjdev/desktop/components/web/WebEngineKt.kt

@@ -1,86 +0,0 @@
-package org.mjdev.desktop.components.web
-
-//import javafx.scene.web.WebEngine
-//import javafx.scene.web.WebView
-//import java.net.CookieHandler
-//import java.net.URI
-//
-//fun WebView.load(url: String) {
-//    engine.load(url)
-//}
-//
-//fun WebView.loadContent(content: String) {
-//    engine.loadContent(content)
-//}
-//
-//fun WebView.stopLoading() {
-//    engine.stopLoading()
-//}
-//
-//fun WebView.goForward() {
-//    engine.goForward()
-//}
-//
-//fun WebView.goBack() {
-//    engine.goBack()
-//}
-//
-//fun WebView.canGoBack(): Boolean {
-//    return engine.canGoBack()
-//}
-//
-//fun WebView.canGoForward(): Boolean {
-//    return engine.canGoForward()
-//}
-//
-//fun WebView.getCurrentUrl(): String? {
-//    return engine.getCurrentUrl()
-//}
-//
-//fun WebView.goRoot() {
-//    engine.history.go(-engine.history.currentIndex)
-//}
-//
-//fun WebView.executeScript(script: String) {
-//    engine.executeScript(script)
-//}
-//
-//fun WebView.setCookie(url: String, headers: HashMap<String, List<String>>) {
-//    val uri: URI = URI.create(url)
-//    CookieHandler.getDefault().put(uri, headers)
-//}
-//
-//fun WebView.getCookies(): MutableMap<String, MutableList<String>>? {
-//    val cookieHandler = CookieHandler.getDefault()
-//    val uri = URI.create(engine.location)
-//    return cookieHandler.get(uri, HashMap())
-//}
-//
-//fun WebEngine.getCurrentUrl(): String? {
-//    if (history.entries.size <= 0) return null
-//    return history.entries[history.currentIndex].url
-//}
-//
-//fun WebEngine.stopLoading() {
-//    loadWorker.cancel()
-//}
-//
-//fun WebEngine.goForward() {
-//    if (canGoForward()) {
-//        history.go(1)
-//    }
-//}
-//
-//fun WebEngine.goBack() {
-//    if (canGoBack()) {
-//        history.go(-1)
-//    }
-//}
-//
-//fun WebEngine.canGoBack(): Boolean {
-//    return history.maxSize > 0 && history.currentIndex != 0
-//}
-//
-//fun WebEngine.canGoForward(): Boolean {
-//    return history.maxSize > 0 && history.currentIndex != history.maxSize - 1
-//}

+ 26 - 236
composeApp/src/commonMain/kotlin/org/mjdev/desktop/components/web/WebView.kt

@@ -7,239 +7,29 @@
  */
 
 package org.mjdev.desktop.components.web
-//
-//import androidx.compose.runtime.*
-//import androidx.compose.runtime.snapshots.SnapshotStateList
-//import androidx.compose.ui.Modifier
-//import androidx.compose.ui.awt.SwingPanel
-//import javafx.application.Platform
-//import javafx.concurrent.Worker.State.*
-//import javafx.embed.swing.JFXPanel
-//import javafx.scene.Scene
-//import javafx.scene.layout.StackPane
-//import javafx.scene.web.WebView
-//import kotlinx.coroutines.CoroutineScope
-//import kotlinx.coroutines.flow.MutableSharedFlow
-//import kotlinx.coroutines.launch
-//
-//// todo key handler
-//@Composable
-//internal fun WebView(
-//    state: WebViewState,
-//    modifier: Modifier = Modifier,
-//    navigator: WebViewNavigator = rememberWebViewNavigator(),
-//    onCreated: (WebView) -> Unit = {},
-//    onDispose: (WebView) -> Unit = {},
-//) {
-//    var webView by remember { mutableStateOf<WebView?>(null) }
-//    LaunchedEffect(webView, navigator) {
-//        with(navigator) {
-//            webView?.handleNavigationEvents()
-//        }
-//    }
-//    val currentOnDispose by rememberUpdatedState(onDispose)
-//    webView?.let { wv ->
-//        DisposableEffect(wv) {
-//            onDispose { currentOnDispose(wv) }
-//        }
-//    }
-//    SwingPanel(factory = {
-//        JFXPanel().also { jfxP ->
-//            Platform.runLater {
-//                val rootVewView = WebView()
-//                webView = rootVewView
-//                rootVewView.isVisible = true
-//                val root = StackPane()
-//                root.children.add(webView)
-//                val scene = Scene(root)
-//                onCreated.invoke(rootVewView)
-//                addEngineListener(rootVewView, state, navigator)
-//                when (val content = state.content) {
-//                    is WebContent.Url -> {
-//                        val url = content.url
-//                        if (url.isNotEmpty() && url != rootVewView.getCurrentUrl()) {
-//                            rootVewView.load(url)
-//                        }
-//                    }
-//
-//                    is WebContent.Data -> {
-//                        rootVewView.loadContent(content.data)
-//                    }
-//                }
-//                jfxP.scene = scene
-//            }
-//        }
-//    }, modifier = modifier) { _ ->
-//    }
-//}
-//
-//fun addEngineListener(
-//    webView: WebView,
-//    state: WebViewState,
-//    navigator: WebViewNavigator
-//) {
-//    val engine = webView.engine
-//    engine.loadWorker.exceptionProperty().addListener { _, _, newError ->
-//        println("page load error : $newError")
-//        state.errorsForCurrentRequest.add(
-//            WebViewError(
-//                engine.getCurrentUrl(),
-//                newError.message.toString()
-//            )
-//        )
-//    }
-//    engine.setOnError { error -> println("onError : $error") }
-//    engine.titleProperty().addListener { observable, oldValue, newValue ->
-//        println("page load titleProperty : $newValue")
-//        state.pageTitle = newValue
-//    }
-//    engine.loadWorker.progressProperty().addListener { observable, oldValue, newValue ->
-//        println("page load progressProperty : $newValue")
-//        if (newValue.toFloat() >= 0f) {
-//            state.loadingState = LoadingState.Loading(newValue.toFloat())
-//        }
-//    }
-//    engine.history.currentIndexProperty().addListener { observable, oldValue, newValue ->
-//        val url = engine.getCurrentUrl()
-//        if (url != null &&
-//            !url.startsWith("data:text/html") &&
-//            state.content.getCurrentUrl() != url
-//        ) {
-//            state.content = state.content.withUrl(url)
-//        }
-//    }
-//    engine.loadWorker.stateProperty().addListener { _, _, newState ->
-//        when (newState) {
-//            SUCCEEDED -> {
-//                state.loadingState = LoadingState.Finished
-//                navigator.canGoBack = engine.canGoBack()
-//                navigator.canGoForward = engine.canGoForward()
-//            }
-//
-//            FAILED -> {}
-//            RUNNING -> {
-//                state.loadingState = LoadingState.Loading(0f)
-//            }
-//
-//            CANCELLED -> {}
-//            READY, SCHEDULED -> {
-//                state.loadingState = LoadingState.Initializing
-//                state.errorsForCurrentRequest.clear()
-//            }
-//        }
-//    }
-//}
-//
-//@Composable
-//fun rememberWebViewNavigator(
-//    coroutineScope: CoroutineScope = rememberCoroutineScope()
-//): WebViewNavigator = remember(coroutineScope) { WebViewNavigator(coroutineScope) }
-//
-//sealed class WebContent {
-//    data class Url(
-//        val url: String,
-//        val additionalHttpHeaders: Map<String, String> = emptyMap(),
-//    ) : WebContent()
-//
-//    data class Data(val data: String) : WebContent()
-//
-//    fun getCurrentUrl(): String? {
-//        return when (this) {
-//            is Url -> url
-//            is Data -> null
-//        }
-//    }
-//}
-//
-//fun WebContent.withUrl(url: String) = when (this) {
-//    is WebContent.Url -> copy(url = url)
-//    else -> WebContent.Url(url)
-//}
-//
-//@Stable
-//class WebViewState(webContent: WebContent) {
-//    var content: WebContent by mutableStateOf(webContent)
-//    var loadingState: LoadingState by mutableStateOf(LoadingState.Finished)
-//        internal set
-//    val isLoading: Boolean
-//        get() = loadingState !is LoadingState.Finished
-//    var pageTitle: String? by mutableStateOf(null)
-//        internal set
-//    val errorsForCurrentRequest: SnapshotStateList<WebViewError> = mutableStateListOf()
-//}
-//
-//@Stable
-//class WebViewNavigator(private val coroutineScope: CoroutineScope) {
-//    private enum class NavigationEvent { BACK, FORWARD, RELOAD, STOP_LOADING }
-//
-//    private val navigationEvents: MutableSharedFlow<NavigationEvent> = MutableSharedFlow()
-//    suspend fun WebView.handleNavigationEvents() {
-//        navigationEvents.collect { event ->
-//            when (event) {
-//                NavigationEvent.BACK -> {
-//                    if (canGoBack) {
-//                        engine.goBack()
-//                    }
-//                }
-//
-//                NavigationEvent.FORWARD -> {
-//                    if (canGoForward) {
-//                        engine.goForward()
-//                    }
-//                }
-//
-//                NavigationEvent.RELOAD -> engine.reload()
-//                NavigationEvent.STOP_LOADING -> engine.stopLoading()
-//            }
-//        }
-//    }
-//
-//    var canGoBack: Boolean by mutableStateOf(false)
-//        internal set
-//
-//    var canGoForward: Boolean by mutableStateOf(false)
-//        internal set
-//
-//    fun navigateBack() {
-//        coroutineScope.launch { navigationEvents.emit(NavigationEvent.BACK) }
-//    }
-//
-//    fun navigateForward() {
-//        coroutineScope.launch { navigationEvents.emit(NavigationEvent.FORWARD) }
-//    }
-//
-//    fun reload() {
-//        coroutineScope.launch { navigationEvents.emit(NavigationEvent.RELOAD) }
-//    }
-//
-//    fun stopLoading() {
-//        coroutineScope.launch { navigationEvents.emit(NavigationEvent.STOP_LOADING) }
-//    }
-//}
-//
-//@Immutable
-//data class WebViewError(
-//    val request: String?,
-//    val error: String
-//)
-//
-//@Composable
-//fun rememberWebViewState(
-//    url: String,
-//    additionalHttpHeaders: Map<String, String> = mapOf()
-//): WebViewState = remember(url, additionalHttpHeaders) {
-//    WebViewState(
-//        WebContent.Url(
-//            url = url,
-//            additionalHttpHeaders = additionalHttpHeaders
-//        )
-//    )
-//}
-//
-//@Composable
-//fun rememberWebViewStateWithHTMLData(
-//    data: String,
-//    baseUrl: String?
-//): WebViewState = remember(data) {
-//    WebViewState(WebContent.Data(data))
-//}
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.ui.Modifier
+import org.mjdev.desktop.context.DesktopContextScope.Companion.withDesktopContext
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.text.style.TextAlign
+import org.mjdev.desktop.components.text.TextAny
+
+@Composable
+fun WebView(
+    url: String = "https://google.com",
+    modifier: Modifier = Modifier,
+) = withDesktopContext {
+    Box(
+        modifier = modifier.background(backgroundColor)
+    ) {
+        TextAny(
+            text = "WebView is not supported on this platform.\n$url",
+            modifier = Modifier.align(Alignment.Center),
+            textAlign = TextAlign.Left,
+            color = textColor,
+        )
+    }
+}

+ 0 - 23
composeApp/src/commonMain/kotlin/org/mjdev/desktop/components/web/WebViewImpl.kt

@@ -1,23 +0,0 @@
-/*
- * Copyright (c) Milan Jurkulák 2024.
- *  Contact:
- *  e: mimoccc@gmail.com
- *  e: mj@mjdev.org
- *  w: https://mjdev.org
- */
-
-package org.mjdev.desktop.components.web
-//
-//import androidx.compose.runtime.Composable
-//import androidx.compose.ui.Modifier
-//
-//@Composable
-//internal fun WebViewImpl(
-//    state: WebViewState,
-//    modifier: Modifier,
-//    navigator: WebViewNavigator
-//) {
-//    WebView(state, modifier, navigator, onCreated = {
-//    }, onDispose = {
-//    })
-//}

+ 17 - 5
composeApp/src/commonMain/kotlin/org/mjdev/desktop/extensions/CustomExt.kt

@@ -5,21 +5,33 @@ import androidx.compose.runtime.collectAsState
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.channelFlow
 import kotlinx.coroutines.launch
-import kotlinx.datetime.Clock
 import kotlinx.datetime.Instant
 import kotlinx.datetime.toLocalDateTime
 import kotlinx.datetime.TimeZone.Companion.currentSystemDefault
 import kotlinx.serialization.ExperimentalSerializationApi
 import kotlinx.serialization.json.Json
+import org.mjdev.desktop.extensions.System.currentTime
 import org.mjdev.desktop.log.Log
 import kotlin.coroutines.suspendCoroutine
+import kotlin.time.ExperimentalTime
 
+@OptIn(ExperimentalTime::class)
 @Suppress("unused")
 object CustomExt {
 
+//    fun runSafe(
+//        shouldLog: Boolean = true,
+//        block: () -> Unit
+//    ) = runCatching {
+//        block()
+//    }.onFailure { err ->
+//        if (shouldLog) Log.e(err)
+//    }
+
     // todo
+
     private fun Instant.formatDate(): String {
-        val localDateTime = this.toLocalDateTime(currentSystemDefault())
+        val localDateTime = toLocalDateTime(currentSystemDefault())
         val day = localDateTime.date.dayOfMonth.toString().padStart(2, '0')
         val month = localDateTime.date.monthNumber.toString().padStart(2, '0')
         val year = localDateTime.date.year.toString()
@@ -28,7 +40,7 @@ object CustomExt {
 
     // todo
     private fun Instant.formatTime(): String {
-        val localDateTime = this.toLocalDateTime(currentSystemDefault())
+        val localDateTime = toLocalDateTime(currentSystemDefault())
         val hours = localDateTime.hour.toString().padStart(2, '0')
         val minutes = localDateTime.minute.toString().padStart(2, '0')
         val seconds = localDateTime.second.toString().padStart(2, '0')
@@ -42,7 +54,7 @@ object CustomExt {
                 var date = "1.1.1970"
                 do {
                     // todo
-                    Clock.System.now().formatDate().also { t ->
+                    currentTime.formatDate().also { t ->
                         if (date != t) {
                             date = t
                             send(date)
@@ -59,7 +71,7 @@ object CustomExt {
             launch {
                 var time = "00:00:00"
                 do {
-                    Clock.System.now().formatTime().also { t ->
+                    currentTime.formatTime().also { t ->
                         if (time != t) {
                             time = t
                             send(time)

+ 1 - 0
composeApp/src/commonMain/kotlin/org/mjdev/desktop/extensions/FocusState.kt

@@ -87,6 +87,7 @@ object FocusState {
         }
     )
 
+    @Suppress("SuspiciousModifierThen")
     @Composable
     fun Modifier.requestFocusOnTouch(
         focusRequester: FocusRequester,

+ 2 - 1
composeApp/src/commonMain/kotlin/org/mjdev/desktop/extensions/LaunchedEffect.kt

@@ -16,6 +16,7 @@ import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.launch
 import org.mjdev.desktop.extensions.MutableStateExt.rememberState
+import org.mjdev.desktop.extensions.System.currentTimeMillis
 import kotlin.coroutines.CoroutineContext
 
 @Suppress("ComposableNaming")
@@ -59,7 +60,7 @@ object LaunchedEffect {
             }
             if (delay > 0L) {
                 delay(delay)
-                state = System.currentTimeMillis()
+                state = currentTimeMillis
             }
         }
         return result

+ 11 - 2
composeApp/src/commonMain/kotlin/org/mjdev/desktop/extensions/System.kt

@@ -1,7 +1,16 @@
 package org.mjdev.desktop.extensions
 
-import kotlinx.datetime.Clock
+import kotlin.time.Clock
+import kotlin.time.ExperimentalTime
+import kotlinx.datetime.Instant
 
+@OptIn(ExperimentalTime::class)
 object System {
-    fun currentTimeMillis() = Clock.System.now().toEpochMilliseconds()
+
+    val currentTimeMillis
+        get() = Clock.System.now().toEpochMilliseconds()
+
+    val currentTime
+        get() = Instant.fromEpochMilliseconds(currentTimeMillis)
+
 }

+ 1 - 1
composeApp/src/commonMain/kotlin/org/mjdev/desktop/log/Log.kt

@@ -13,7 +13,7 @@ import org.mjdev.desktop.extensions.System.currentTimeMillis
 
 // todo colors
 object Log {
-    private val creationDate = currentTimeMillis().toString()
+    private val creationDate = currentTimeMillis.toString()
     private val logFile = "/var/tmp/mjdev-desktop/log/$creationDate.log".toPath()
 //    private val logWriter = PrintWriter(logFile.apply {
 //        runCatching {

+ 0 - 2
composeApp/src/commonMain/kotlin/org/mjdev/desktop/managers/ai/AiManager.kt

@@ -15,12 +15,10 @@ import org.mjdev.desktop.managers.ai.tts.base.TTSPlugin
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
 import org.mjdev.desktop.context.IDesktopContext
-import org.mjdev.desktop.managers.ai.plugins.AiPluginGemini
 import org.mjdev.desktop.managers.ai.actions.ActionCalculator
 import org.mjdev.desktop.managers.ai.actions.base.ActionsProvider
 import org.mjdev.desktop.managers.ai.actions.ActionCurrentTime
 import org.mjdev.desktop.managers.ai.actions.base.ActionException.ActionNone
-import org.mjdev.desktop.managers.ai.plugins.AiPluginOpenAi
 import org.mjdev.desktop.managers.ai.plugins.base.AIPlugin
 import org.mjdev.desktop.managers.ai.tts.TTSPluginEmpty
 

+ 4 - 2
composeApp/src/commonMain/kotlin/org/mjdev/desktop/managers/ai/actions/ActionCurrentTime.kt

@@ -2,15 +2,17 @@ package org.mjdev.desktop.managers.ai.actions
 
 import kotlinx.datetime.TimeZone
 import kotlinx.datetime.toLocalDateTime
+import org.mjdev.desktop.extensions.System.currentTime
 import org.mjdev.desktop.managers.ai.actions.base.ActionsProvider
+import kotlin.time.ExperimentalTime
 
+@OptIn(ExperimentalTime::class)
 val ActionsProvider.ActionCurrentTime
     get() = action(
         "current time"
     ) {
         val timeZone = TimeZone.currentSystemDefault()
-        val localDateTime = kotlinx.datetime.Clock.System.now()
-            .toLocalDateTime(timeZone)
+        val localDateTime = currentTime.toLocalDateTime(timeZone)
         val hours = localDateTime.hour.toString().padStart(2, '0')
         val minutes = localDateTime.minute.toString().padStart(2, '0')
         val seconds = localDateTime.second.toString().padStart(2, '0')

+ 1 - 1
composeApp/src/commonMain/kotlin/org/mjdev/desktop/managers/ai/actions/base/ActionsProvider.kt

@@ -69,7 +69,7 @@ class ActionsProvider(
                 }
             }
             if (action != null) {
-                action.lastSeen = currentTimeMillis()
+                action.lastSeen = currentTimeMillis
                 action.history.addIfNotExists(t) { t1, t2 -> t1.contentEquals(t2, true) }
                 action.action.invoke(ActionProviderScope(context)).let { r ->
                     when (r) {

+ 6 - 3
composeApp/src/commonMain/kotlin/org/mjdev/desktop/managers/ai/plugins/AiPluginEmpty.kt

@@ -15,6 +15,9 @@ import org.mjdev.desktop.managers.ai.plugins.base.AIPlugin
 class AiPluginEmpty(
     context: IDesktopContext
 ) : AIPlugin {
-    override suspend fun ask(question: String): String =
-        "No a.i. provider selected please read manual."
-}
+
+    override suspend fun ask(
+        question: String
+    ): String = "No a.i. provider selected please read manual."
+
+}

+ 35 - 38
composeApp/src/commonMain/kotlin/org/mjdev/desktop/managers/ai/plugins/AiPluginGemini.kt

@@ -8,50 +8,47 @@
 
 package org.mjdev.desktop.managers.ai.plugins
 
-import dev.shreyaspatil.ai.client.generativeai.GenerativeModel
-import dev.shreyaspatil.ai.client.generativeai.type.content
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.async
+import community.flock.aigentic.core.agent.start
+import community.flock.aigentic.core.agent.tool.Result.Fatal
+import community.flock.aigentic.core.agent.tool.Result.Finished
+import community.flock.aigentic.core.agent.tool.Result.Stuck
+import community.flock.aigentic.core.dsl.agent
+import community.flock.aigentic.gemini.dsl.geminiModel
+import community.flock.aigentic.gemini.model.GeminiModelIdentifier
+import community.flock.aigentic.gemini.model.GeminiModelIdentifier.Gemini1_5Pro
 import org.mjdev.desktop.context.IDesktopContext
+import org.mjdev.desktop.log.Log
 import org.mjdev.desktop.managers.ai.plugins.base.AIPlugin
 
-/*
-* Gemini key : https://makersuite.google.com/app/apikey
-* */
+// todo tools = actions
+// Gemini key : https://makersuite.google.com/app/apikey
 class AiPluginGemini(
     private val context: IDesktopContext,
-    private val aiModel: GeminiModel = GeminiModel.Gemini1_5Pro,
-    private val scope: CoroutineScope = context.scope
+    private val apiKey: String = context.keysManager.loadKey("gemini"),
+    private val model: GeminiModelIdentifier = Gemini1_5Pro,
 ) : AIPlugin {
-    private val apiKey = context.keysManager.loadKey("gemini")
-    private val generativeModel: GenerativeModel? by lazy {
-        if (apiKey.isNotEmpty()) {
-            GenerativeModel(
-                modelName = aiModel.model,
-                apiKey = apiKey
-            )
-        } else null
-    }
 
-    override suspend fun ask(question: String): String = scope.async {
-        var error: Throwable? = null
-        runCatching {
-            if (apiKey.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()
-
-    @Suppress("EnumEntryName")
-    enum class GeminiModel(val model: String) {
-        Gemini1_5Pro("gemini-1.5-pro-latest")
+    override suspend fun ask(
+        question: String
+    ): String = agent {
+//        Log.d("Using api key : $apiKey")
+        geminiModel {
+            apiKey(apiKey)
+            modelIdentifier(model)
+        }
+        task("Provide information") {
+            addInstruction("Respond to user queries with relevant information.")
+        }
+        context {
+            addText(question)
+        }
+        finishResponse()
+    }.start().result.let { result ->
+        when (result) {
+            is Finished<*> -> result.response.toString()
+            is Stuck -> result.reason
+            is Fatal -> result.message
+        }
     }
+
 }

+ 30 - 8
composeApp/src/commonMain/kotlin/org/mjdev/desktop/managers/ai/plugins/AiPluginOllama.kt

@@ -1,18 +1,40 @@
 package org.mjdev.desktop.managers.ai.plugins
 
-import kotlinx.coroutines.CoroutineScope
+import community.flock.aigentic.core.agent.start
+import community.flock.aigentic.core.agent.tool.Result.Fatal
+import community.flock.aigentic.core.agent.tool.Result.Finished
+import community.flock.aigentic.core.agent.tool.Result.Stuck
+import community.flock.aigentic.core.dsl.agent
+import community.flock.aigentic.ollama.dsl.ollamaModel
 import org.mjdev.desktop.context.IDesktopContext
 import org.mjdev.desktop.managers.ai.plugins.base.AIPlugin
 
-// todo
+// todo tools = actions
+// todo test with ollama model
 class AiPluginOllama(
     private val context: IDesktopContext,
-    private val scope: CoroutineScope = context.scope
+    private val apiKey: String = context.keysManager.loadKey("ollama"),
 ) : AIPlugin {
-//    private val ollama by lazy { null }
 
-    override suspend fun ask(question: String): String {
-//        ollama.setModel("")
-        return "" // todo
+    override suspend fun ask(
+        question: String
+    ): String = agent {
+        ollamaModel {
+            apiUrl(apiKey)
+        }
+        task("Provide information") {
+            addInstruction("Respond to user queries with relevant information.")
+        }
+        context {
+            addText(question)
+        }
+        finishResponse()
+    }.start().result.let { result ->
+        when (result) {
+            is Finished<*> -> result.response.toString()
+            is Stuck -> result.reason
+            is Fatal -> result.message
+        }
     }
-}
+
+}

+ 35 - 60
composeApp/src/commonMain/kotlin/org/mjdev/desktop/managers/ai/plugins/AiPluginOpenAi.kt

@@ -1,71 +1,46 @@
 package org.mjdev.desktop.managers.ai.plugins
 
-import kotlinx.coroutines.CoroutineScope
 import org.mjdev.desktop.context.IDesktopContext
 import org.mjdev.desktop.managers.ai.plugins.base.AIPlugin
-import kotlin.time.Duration.Companion.seconds
-import com.aallam.openai.api.BetaOpenAI
-import com.aallam.openai.api.chat.ChatCompletionChunk
-import com.aallam.openai.api.chat.ChatCompletionRequest
-import com.aallam.openai.api.chat.ChatMessage
-import com.aallam.openai.api.chat.ChatRole
-import com.aallam.openai.api.http.Timeout
-import com.aallam.openai.api.logging.LogLevel
-import com.aallam.openai.api.logging.Logger
-import com.aallam.openai.api.model.ModelId
-import com.aallam.openai.client.OpenAI
-import com.aallam.openai.client.OpenAIConfig
-import kotlinx.coroutines.async
-import kotlinx.coroutines.flow.first
+import community.flock.aigentic.core.agent.tool.Result.Stuck
+import community.flock.aigentic.core.agent.tool.Result.Fatal
+import community.flock.aigentic.core.agent.tool.Result.Finished
+import community.flock.aigentic.core.agent.start
+import community.flock.aigentic.core.dsl.agent
+import community.flock.aigentic.openai.dsl.openAIModel
+import community.flock.aigentic.openai.model.OpenAIModelIdentifier
+import community.flock.aigentic.openai.model.OpenAIModelIdentifier.GPT4OMini
 import org.mjdev.desktop.log.Log
 
-// todo test
+// todo tools = actions
+// Open AI keys: https://platform.openai.com/api-keys
 class AiPluginOpenAi(
     private val context: IDesktopContext,
-    private val scope: CoroutineScope = context.scope
+    private val apiKey: String = context.keysManager.loadKey("open-ai"),
+    private val model: OpenAIModelIdentifier = GPT4OMini,
 ) : AIPlugin {
-    private val apiKey = context.keysManager.loadKey("open-ai")
-    private val modelId = ModelId("gpt-3.5-turbo")
-    private val openAI: OpenAI? = runCatching {
-        if (apiKey.isNotEmpty()) OpenAI(
-            OpenAIConfig(
-                token = apiKey,
-                logLevel = LogLevel.All,
-                logger = Logger.Simple,
-                timeout = Timeout(socket = 60.seconds),
-            )
-        ) else {
-            Log.w("OpenAI API key is not set, using empty OpenAI client.")
-            null
+
+    override suspend fun ask(
+        question: String
+    ): String = agent {
+//        Log.d("Using api key : $apiKey")
+        openAIModel {
+            apiKey(apiKey)
+            modelIdentifier(model)
+        }
+        task("Provide information") {
+            addInstruction("Respond to user queries with relevant information.")
+        }
+        context {
+            addText(question)
+        }
+        finishResponse()
+    }.start().result.let { result ->
+        when (result) {
+            is Finished<*> -> result.response.toString()
+            is Stuck -> result.reason
+            is Fatal -> result.message
         }
-    }.onFailure { e ->
-        Log.e(e)
-    }.getOrNull()
+    }
 
-    @OptIn(BetaOpenAI::class)
-    override suspend fun ask(question: String): String = scope.async {
-        runCatching {
-            openAI?.chatCompletions(
-                ChatCompletionRequest(
-                    model = modelId,
-                    messages = listOf(
-                        ChatMessage(
-                            role = ChatRole.User,
-                            content = question
-                        )
-                    ),
-                    maxTokens = 1000,
-                    temperature = 0.0
-                )
-            )?.first().let { chunk ->
-                if (chunk is ChatCompletionChunk) {
-                    chunk.choices.firstOrNull()?.delta?.content ?: ""
-                } else {
-                    ""
-                }
-            }
-        }.onFailure { e ->
-            Log.e(e)
-        }.getOrDefault("")
-    }.await()
-}
+}

+ 0 - 10
composeApp/src/commonMain/kotlin/org/mjdev/desktop/managers/ai/plugins/AiPluginPerplexity.kt

@@ -1,10 +0,0 @@
-package org.mjdev.desktop.managers.ai.plugins
-
-import org.mjdev.desktop.managers.ai.plugins.base.AIPlugin
-
-// todo
-class AiPluginPerplexity : AIPlugin  {
-    override suspend fun ask(question: String): String {
-        TODO("Not yet implemented")
-    }
-}

+ 6 - 0
composeApp/src/desktopMain/kotlin/org/mjdev/desktop/components/main/MainWindow.kt

@@ -79,6 +79,12 @@ fun MainWindow() = withDesktopContext {
                     modifier = Modifier.size(350.dp, 300.dp)
                         .align(Alignment.BottomEnd)
                 )
+//                WebView(
+//                    modifier = Modifier
+//                        .size(800.dp, 600.dp)
+//                        .align(Alignment.Center),
+//                    url = "https://www.google.com"
+//                )
             },
             onLeftMouseClick = {
                 runAsync {

+ 1 - 3
composeApp/src/desktopMain/kotlin/org/mjdev/desktop/context/DeskptopContext.kt

@@ -19,7 +19,6 @@ import org.mjdev.desktop.data.User
 import org.mjdev.desktop.extensions.Compose.isDesign
 import org.mjdev.desktop.helpers.application.ApplicationScope
 import org.mjdev.desktop.helpers.image.ImageLoader.asyncImageLoader
-import org.mjdev.desktop.helpers.persistence.StorageProvider
 import org.mjdev.desktop.managers.palette.Palette
 import org.mjdev.desktop.helpers.system.shell.Shell
 import org.mjdev.desktop.interfaces.IApp
@@ -32,7 +31,6 @@ import org.mjdev.desktop.log.Log
 import org.mjdev.desktop.managers.ai.AiManager
 import org.mjdev.desktop.managers.ai.IAiManager
 import org.mjdev.desktop.managers.ai.plugins.AiPluginGemini
-import org.mjdev.desktop.managers.ai.plugins.AiPluginOpenAi
 import org.mjdev.desktop.managers.ai.stt.STTPluginEmpty
 import org.mjdev.desktop.managers.ai.tts.TTSPluginSwift
 import org.mjdev.desktop.managers.apps.IAppsManager
@@ -248,7 +246,7 @@ class DesktopContext(
         IAiManager::class -> AiManager(
             context = this,
             // todo user can configure
-            pluginAI = AiPluginOpenAi(this@DesktopContext),
+            pluginAI = AiPluginGemini(this@DesktopContext),
             pluginTTS = TTSPluginSwift(this@DesktopContext),
             pluginSTT = STTPluginEmpty(this@DesktopContext)
         )

+ 4 - 5
composeApp/src/desktopMain/kotlin/org/mjdev/desktop/main/Main.kt

@@ -1,6 +1,5 @@
 package org.mjdev.desktop.main
 
-import Notify
 import org.mjdev.desktop.components.main.MainWindow
 import org.mjdev.desktop.helpers.application.application
 
@@ -11,8 +10,8 @@ fun main(
     exitProcessOnExit = true
 ) {
     MainWindow()
-    Notify(
-        message= "Hello World!",
-        duration=NotificationDuration.LONG
-    )
+//    Notify(
+//        message = "Hello World!",
+//        duration = NotificationDuration.LONG
+//    )
 }

+ 6 - 2
composeApp/src/desktopMain/kotlin/org/mjdev/desktop/managers/apps/AppsManager.kt

@@ -165,8 +165,12 @@ class AppsManager(
         }.toList()
 
     override val favoriteApps: List<App>
-        get() = Shell.executeAndRead("gsettings", "get", "org.gnome.shell", "favorite-apps")
-            .replace("'", "\"") // todo ?
+        get() = Shell.executeAndRead(
+            "gsettings",
+            "get",
+            "org.gnome.shell",
+            "favorite-apps"
+        ).replace("'", "\"") // todo ?
             .jsonToList<String>()
             .flatMap { deskFileName ->
                 findDesktopFileByName(deskFileName).map { deskFile ->

+ 6 - 5
composeApp/src/desktopMain/kotlin/org/mjdev/desktop/managers/theme/linux/ThemeManagerLinux.kt

@@ -28,7 +28,6 @@ import org.mjdev.desktop.managers.theme.base.ThemeManagerStub
 import org.mjdev.desktop.extensions.PathExt.exists
 import org.mjdev.desktop.extensions.StringExt.hexRgb
 import org.mjdev.desktop.context.IDesktopContext
-import java.util.concurrent.CompletableFuture
 import java.util.concurrent.CompletableFuture.runAsync
 
 @Suppress("unused", "MemberVisibilityCanBePrivate")
@@ -54,14 +53,16 @@ class ThemeManagerLinux(
 
     override fun clearSystemTheme() {
         runCatching {
-            Log.i("Deleting : file:///${systemGtk3CssFile.absolutePath}")
+//            Log.i("Deleting : file:///${systemGtk3CssFile.absolutePath}")
             systemGtk3CssFile.delete()
-            Log.i("Deleting : file:///${systemGtk4CssFile.absolutePath}")
+//            Log.i("Deleting : file:///${systemGtk4CssFile.absolutePath}")
             systemGtk4CssFile.delete()
-            Log.i("Deleting theme : file:///${themeDesktopFile.parentFile.absolutePath}")
+//            Log.i("Deleting theme : file:///${themeDesktopFile.parentFile.absolutePath}")
             themeDesktopFile.parentFile.delete()
         }.onFailure { e ->
             Log.e(e)
+        }.onSuccess {
+            Log.d("Custom system theme cleared.")
         }
     }
 
@@ -77,7 +78,7 @@ class ThemeManagerLinux(
         }
     }
 
-    private fun createDesktopFile() = runAsync{
+    private fun createDesktopFile() = runAsync {
         runCatching {
             desktopFile(themeDesktopFile) {
                 mkDirs()

+ 1 - 1
gradle.properties

@@ -4,7 +4,7 @@ kotlin.code.style=official
 # do not check or build disabled
 kotlin.native.ignoreDisabledTargets=true
 # gradle
-org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx2048M"
+org.gradle.jvmargs=--enable-preview -Xmx2048M -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx2048M"
 org.gradle.parallel=true
 # gradle build cache
 org.gradle.configuration-cache=true

+ 33 - 7
gradle/libs.versions.toml

@@ -19,14 +19,14 @@ android-min-sdk = "24"
 vlc-version = "3.0.21-2"
 vlc-dir = "composeApp/src/commonMain/resources/vlc/"
 ### lib versions ###################################################################################
-gradle = "8.10.1"
+gradle = "8.11.0"
 #gradle-kotlinter = "4.3.0"
 androidx-activity-compose = "1.10.1"
-androidx-lifecycle = "2.9.0"
-androidx-ui-tooling = "1.8.2"
+androidx-lifecycle = "2.9.1"
+androidx-ui-tooling = "1.8.3"
 app-icon = "1.2.6"
 coil = "3.2.0"
-compose-plugin = "1.8.1"
+compose-plugin = "1.8.2"
 core-splash-screen = "1.0.1"
 generative-ai = "0.9.0-1.1.0"
 gettext-lib = "1.11.0"
@@ -66,7 +66,11 @@ vlcj = "4.11.0"
 jna = "5.17.0"
 image-comparison = "4.4.0"
 haze = "1.6.4"
-kotlinx-serialization-json= "1.8.1"
+kotlinx-serialization-json = "1.8.1"
+ktor = "3.2.0"
+flock = "0.1.0"
+markwon = "4.6.2"
+html2text = "-SNAPSHOT"
 ### libs ###########################################################################################
 [libraries]
 ####################################################################################################
@@ -96,7 +100,7 @@ ychat = { module = "co.yml:ychat", version.ref = "ychat" }
 # date time
 kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinx-date-time" }
 # json
-kotlinx-serialization-json = { module="org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref="kotlinx-serialization-json"}
+kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization-json" }
 # okhttp
 okhttp3-client = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp3" }
 # coil
@@ -139,7 +143,29 @@ jna-platform-jpms = { module = "net.java.dev.jna:jna-platform-jpms", version.ref
 # image comparer
 imageComparison = { module = "com.github.romankh3:image-comparison", version.ref = "image-comparison" }
 # blur
-haze = { module= "dev.chrisbanes.haze:haze", version.ref="haze" }
+haze = { module = "dev.chrisbanes.haze:haze", version.ref = "haze" }
+# network
+ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
+ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktor" }
+ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" }
+ktor-client-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" }
+ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" }
+okhttp3-logging-interceptor = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp3" }
+okhttp3-urlconnection = { module = "com.squareup.okhttp3:okhttp-urlconnection", version.ref = "okhttp3" }
+okhttp3-dnsoverhttps = { module = "com.squareup.okhttp3:okhttp-dnsoverhttps", version.ref = "okhttp3" }
+okhttp3-tls = { module = "com.squareup.okhttp3:okhttp-tls", version.ref = "okhttp3" }
+# ai agents
+flock-aigentic-core = { module = "community.flock.aigentic:core", version.ref = "flock" }
+flock-aigentic-openai = { module = "community.flock.aigentic:openai", version.ref = "flock" }
+flock-aigentic-gemini = { module = "community.flock.aigentic:gemini", version.ref = "flock" }
+flock-aigentic-ollama = { module = "community.flock.aigentic:ollama", version.ref = "flock" }
+flock-aigentic-vertexai = { module = "community.flock.aigentic:vertexai", version.ref = "flock" }
+flock-aigentic-http = { module = "community.flock.aigentic:http", version.ref = "flock" }
+flock-aigentic-openapi = { module = "community.flock.aigentic:openapi", version.ref = "flock" }
+# markdown
+markwon = { module = "io.noties.markwon:core", version.ref = "markwon" }
+# html 2 text
+html2text = { module = "com.github.BartoszJarocki:android-boilerpipe", version.ref = "html2text" }
 ### plugins ########################################################################################
 [plugins]
 ####################################################################################################

+ 17 - 15
settings.gradle.kts

@@ -28,13 +28,14 @@ pluginManagement {
         maven("https://plugins.gradle.org/m2/")
         maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
         maven("https://dl.bintray.com/kotlin/ktor")
-        google {
-            mavenContent {
-                includeGroupAndSubgroups("androidx")
-                includeGroupAndSubgroups("com.android")
-                includeGroupAndSubgroups("com.google")
-            }
-        }
+        maven("https://central.sonatype.com/repository/maven-snapshots")
+//        google {
+//            mavenContent {
+//                includeGroupAndSubgroups("androidx")
+//                includeGroupAndSubgroups("com.android")
+//                includeGroupAndSubgroups("com.google")
+//            }
+//        }
         google()
     }
 }
@@ -60,13 +61,14 @@ dependencyResolutionManagement {
         maven("https://plugins.gradle.org/m2/")
         maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
         maven("https://dl.bintray.com/kotlin/ktor")
-        google {
-            mavenContent {
-                includeGroupAndSubgroups("androidx")
-                includeGroupAndSubgroups("com.android")
-                includeGroupAndSubgroups("com.google")
-            }
-        }
+        maven("https://central.sonatype.com/repository/maven-snapshots")
+//        google {
+//            mavenContent {
+//                includeGroupAndSubgroups("androidx")
+//                includeGroupAndSubgroups("com.android")
+//                includeGroupAndSubgroups("com.google")
+//            }
+//        }
         google()
     }
 }
@@ -84,7 +86,7 @@ include(":composeApp")
 rootProject.children.forEach { project ->
     fun configureProject(project: ProjectDescriptor) {
         project.buildFileName = "${project.name}.gradle.kts"
-        project.children.forEach { child->
+        project.children.forEach { child ->
             configureProject(child)
         }
     }