Prechádzať zdrojové kódy

code improvements

code improvements

code improvements
Milan Jurkulak 1 rok pred
rodič
commit
8f003ddab3
30 zmenil súbory, kde vykonal 311 pridanie a 252 odobranie
  1. 2 2
      build.gradle.kts
  2. 1 1
      buildSrc/build.gradle.kts
  3. 0 2
      buildSrc/src/main/kotlin/AndroidConfig.kt
  4. 0 2
      buildSrc/src/main/kotlin/BasePlugin.kt
  5. 24 0
      buildSrc/src/main/kotlin/BuildExt.kt
  6. 0 2
      buildSrc/src/main/kotlin/DeployTask.kt
  7. 0 3
      buildSrc/src/main/kotlin/GenerateChangelogTask.kt
  8. 0 2
      buildSrc/src/main/kotlin/KotlinDependencyHandlerExt.kt
  9. 0 5
      buildSrc/src/main/kotlin/MultiPlatformPlugin.kt
  10. 11 0
      buildSrc/src/main/kotlin/ProjectExt.kt
  11. 0 4
      buildSrc/src/main/kotlin/SourceSets.kt
  12. 0 15
      buildSrc/src/main/kotlin/org/mjdev/gradle/extensions/ProjectExt.kt
  13. 8 4
      composeApp/build.gradle.kts
  14. 4 2
      composeApp/src/androidMain/AndroidManifest.xml
  15. 13 46
      composeApp/src/androidMain/kotlin/org/mjdev/desktop/activity/MainActivity.kt
  16. 6 3
      composeApp/src/androidMain/kotlin/org/mjdev/desktop/context/DesktopContext.kt
  17. 77 0
      composeApp/src/androidMain/kotlin/org/mjdev/desktop/extensions/ComponentActivity.kt
  18. 4 3
      composeApp/src/androidMain/kotlin/org/mjdev/desktop/helpers/crash/CrashHandler.kt
  19. 1 0
      composeApp/src/androidMain/res/values/splash.xml
  20. 0 1
      composeApp/src/commonMain/kotlin/org/mjdev/desktop/components/immersivelist/ImmersiveList.kt
  21. 11 1
      composeApp/src/commonMain/kotlin/org/mjdev/desktop/components/text/TextArea.kt
  22. 2 1
      composeApp/src/commonMain/kotlin/org/mjdev/desktop/extensions/FocusState.kt
  23. 4 1
      composeApp/src/wasmJsMain/kotlin/org/mjdev/desktop/data/User.kt
  24. 4 6
      composeApp/src/wasmJsMain/kotlin/org/mjdev/desktop/extensions/ImageBitmapExt.kt
  25. 3 0
      composeApp/src/wasmJsMain/kotlin/org/mjdev/desktop/helpers/streams/ResourceStream.wasmJs.kt
  26. 1 0
      composeApp/src/wasmJsMain/kotlin/org/mjdev/desktop/helpers/system/meminfo/MemInfo.wasmJs.kt
  27. 110 124
      composeApp/src/wasmJsMain/kotlin/org/mjdev/desktop/main/MainView.kt
  28. 13 11
      composeApp/src/wasmJsMain/kotlin/org/mjdev/desktop/system/Filesystem.wasmJs.kt
  29. 1 1
      gradle.properties
  30. 11 10
      gradle/libs.versions.toml

+ 2 - 2
build.gradle.kts

@@ -9,9 +9,9 @@ plugins {
     // auto applied
 //    alias(libs.plugins.qodana)
 //    alias(libs.plugins.kover)
-    alias(libs.plugins.changelog)
+    alias(libs.plugins.changelog) apply false
 //    alias(libs.plugins.gradle.ktlint)
-    alias(libs.plugins.manes.versions)
+    alias(libs.plugins.manes.versions) apply false
 }
 
 //dependencies {

+ 1 - 1
buildSrc/build.gradle.kts

@@ -26,7 +26,7 @@ gradlePlugin {
             id = "MultiPlatformPlugin"
             displayName = "MultiPlatformPlugin"
             description = "Common library plugin to handle all stuffs needed."
-            implementationClass = "org.mjdev.gradle.plugins.MultiPlatformPlugin"
+            implementationClass = "MultiPlatformPlugin"
         }
     }
 }

+ 0 - 2
buildSrc/src/main/kotlin/org/mjdev/gradle/extensions/AndroidConfig.kt → buildSrc/src/main/kotlin/AndroidConfig.kt

@@ -1,5 +1,3 @@
-package org.mjdev.gradle.extensions
-
 //import org.gradle.api.JavaVersion
 
 //fun BaseExtension.androidConfig() {

+ 0 - 2
buildSrc/src/main/kotlin/org/mjdev/gradle/plugins/BasePlugin.kt → buildSrc/src/main/kotlin/BasePlugin.kt

@@ -1,5 +1,3 @@
-package org.mjdev.gradle.plugins
-
 import org.gradle.api.Plugin
 import org.gradle.api.Project
 

+ 24 - 0
buildSrc/src/main/kotlin/BuildExt.kt

@@ -1,10 +1,34 @@
 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
 
+val Project.java: JavaPluginExtension
+    get() = extensions.getByType()
+
+fun Project.setJavaLanguageVersion(
+    version: Provider<String>
+): JavaLanguageVersion = version.get().let { vs ->
+    JavaLanguageVersion.of(vs)
+}.also { jlv ->
+    java.toolchain.languageVersion.set(jlv)
+}
+
 fun NamedDomainObjectContainer<KotlinSourceSet>.getOrCreate(
     name: String
 ): KotlinSourceSet = findByName(name) ?: create(name)
 
+//fun Project.kotlinSourceSets(
+//    config: Action<NamedDomainObjectContainer<KotlinSourceSet>>
+//) = kotlin.sourceSets.configure(config)
+
+//fun NamedDomainObjectContainer<KotlinSourceSet>.getOrCreate(
+//    name: String
+//): KotlinSourceSet = findByName(name) ?: create(name)
+
 //fun NamedDomainObjectContainer<KotlinSourceSet>.desktopMain(
 //    block: KotlinSourceSet.() -> Unit = {}
 //): KotlinSourceSet = getOrCreate("desktopMain").apply(block)

+ 0 - 2
buildSrc/src/main/kotlin/org/mjdev/gradle/tasks/DeployTask.kt → buildSrc/src/main/kotlin/DeployTask.kt

@@ -1,5 +1,3 @@
-package org.mjdev.gradle.tasks
-
 import org.gradle.api.DefaultTask
 import org.gradle.api.tasks.Input
 import org.gradle.api.tasks.TaskAction

+ 0 - 3
buildSrc/src/main/kotlin/org/mjdev/gradle/tasks/GenerateChangelogTask.kt → buildSrc/src/main/kotlin/GenerateChangelogTask.kt

@@ -1,11 +1,8 @@
-package org.mjdev.gradle.tasks
-
 import org.gradle.api.DefaultTask
 import org.gradle.api.tasks.Input
 import org.gradle.api.tasks.TaskAction
 import java.io.ByteArrayOutputStream
 
-@Suppress("DEPRECATION")
 open class GenerateChangelogTask : DefaultTask() {
     @Input
     var appName: String = ""

+ 0 - 2
buildSrc/src/main/kotlin/org/mjdev/gradle/extensions/KotlinDependencyHandlerExt.kt → buildSrc/src/main/kotlin/KotlinDependencyHandlerExt.kt

@@ -1,5 +1,3 @@
-package org.mjdev.gradle.extensions
-
 //    fun KotlinDependencyHandler.kapt(
 //        dependency:String
 //    ) = configurations["kapt"].dependencies.add(dependency)

+ 0 - 5
buildSrc/src/main/kotlin/org/mjdev/gradle/plugins/MultiPlatformPlugin.kt → buildSrc/src/main/kotlin/MultiPlatformPlugin.kt

@@ -1,9 +1,4 @@
-package org.mjdev.gradle.plugins
-
 import org.gradle.api.Project
-import org.mjdev.gradle.tasks.DeployTask
-import org.mjdev.gradle.tasks.GenerateChangelogTask
-import  org.mjdev.gradle.extensions.ProjectExt.registerTask
 
 class MultiPlatformPlugin : BasePlugin() {
     override fun Project.onConfigure() {

+ 11 - 0
buildSrc/src/main/kotlin/ProjectExt.kt

@@ -0,0 +1,11 @@
+import org.gradle.api.Project
+import org.gradle.api.Task
+
+const val GROUP = "mjdev"
+
+inline fun <reified T : Task> Project.registerTask(
+    name: String,
+    group: String = GROUP
+) = tasks.register(name, T::class.java) {
+    this.group = group
+}

+ 0 - 4
buildSrc/src/main/kotlin/org/mjdev/gradle/extensions/SourceSets.kt → buildSrc/src/main/kotlin/SourceSets.kt

@@ -1,7 +1,3 @@
-import org.gradle.api.NamedDomainObjectContainer
-import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
-import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
-
 //import com.android.build.gradle.BaseExtension
 //import org.gradle.api.Project
 //import org.gradle.kotlin.dsl.NamedDomainObjectCollectionDelegateProvider

+ 0 - 15
buildSrc/src/main/kotlin/org/mjdev/gradle/extensions/ProjectExt.kt

@@ -1,15 +0,0 @@
-package org.mjdev.gradle.extensions
-
-import org.gradle.api.Project
-import org.gradle.api.Task
-
-object ProjectExt {
-    const val GROUP = "mjdev"
-
-    inline fun <reified T : Task> Project.registerTask(
-        name: String,
-        group: String = GROUP
-    ) = tasks.register(name, T::class.java) {
-        this.group = group
-    }
-}

+ 8 - 4
composeApp/build.gradle.kts

@@ -1,3 +1,4 @@
+import org.jetbrains.compose.ComposeExtension
 import org.jetbrains.compose.desktop.DesktopExtension
 import org.jetbrains.compose.desktop.application.dsl.TargetFormat
 import org.jetbrains.compose.resources.ResourcesExtension
@@ -19,8 +20,6 @@ plugins {
 //    MultiPlatformPlugin
 }
 
-java.toolchain.languageVersion.set(JavaLanguageVersion.of(17))
-
 fun NamedDomainObjectContainer<KotlinSourceSet>.getOrCreate(
     name: String
 ): KotlinSourceSet = findByName(name) ?: create(name)
@@ -40,6 +39,9 @@ fun KotlinTargetContainerWithWasmPresetFunctions.wasmJsTarget(
     configure: Action<KotlinWasmJsTargetDsl>
 ) = wasmJs(configure)
 
+val Project.compose: ComposeExtension
+    get() = extensions.getByType()
+
 fun Project.desktop(
     config: DesktopExtension.() -> Unit
 ) = compose.desktop(config)
@@ -54,7 +56,7 @@ fun targets(
 
 // todo
 @Suppress("UNUSED_PARAMETER", "UnusedReceiverParameter")
-fun Project.wasm(config: DesktopExtension.() -> Unit) {
+fun Project.wasmjs(config: DesktopExtension.() -> Unit) {
     // todo
 //    compose.wasmJs(config)
 }
@@ -66,6 +68,8 @@ fun Project.kotlinSourceSets(
     sourceSets(config)
 }
 
+setJavaLanguageVersion(libs.versions.java.language.version)
+
 targets {
     desktopTarget {
     }
@@ -327,5 +331,5 @@ desktop {
 }
 
 // wasm config
-wasm {
+wasmjs {
 }

+ 4 - 2
composeApp/src/androidMain/AndroidManifest.xml

@@ -25,8 +25,9 @@
         tools:ignore="SelectedPhotoAccess" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
-    <uses-permission android:name="android.permission.SET_ALARM" />
-    <uses-permission android:name="android.permission.USE_EXACT_ALARM" />
+    <uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
+    <uses-permission android:name="android.permission.USE_EXACT_ALARM"
+        tools:ignore="ExactAlarm" />
 
     <queries>
         <package android:name="com.example.store" />
@@ -54,6 +55,7 @@
             android:screenOrientation="fullSensor"
             android:configChanges="orientation|screenSize|screenLayout|keyboardHidden|mnc|colorMode|density|fontScale|fontWeightAdjustment|keyboard|layoutDirection|locale|mcc|navigation|smallestScreenSize|touchscreen|uiMode"
             android:exported="true"
+            android:theme="@style/Theme.SplashScreen.Splash"
             tools:ignore="DiscouragedApi">
 
             <intent-filter>

+ 13 - 46
composeApp/src/androidMain/kotlin/org/mjdev/desktop/activity/MainActivity.kt

@@ -1,14 +1,11 @@
 package org.mjdev.desktop.activity
 
-import android.graphics.Color
 import android.os.Bundle
-import android.util.Log
-import android.view.WindowManager
 import androidx.activity.ComponentActivity
 import androidx.activity.SystemBarStyle
-import androidx.activity.compose.BackHandler
 import androidx.activity.compose.setContent
 import androidx.activity.enableEdgeToEdge
+import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.WindowInsets
 import androidx.compose.foundation.layout.consumeWindowInsets
@@ -20,84 +17,53 @@ import androidx.compose.material.Scaffold
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
-import androidx.core.view.WindowCompat
-import androidx.core.view.WindowInsetsCompat
-import androidx.core.view.WindowInsetsControllerCompat
 import com.google.accompanist.permissions.ExperimentalPermissionsApi
-import org.mjdev.desktop.activity.MainActivity.Companion.TAG
 import org.mjdev.desktop.main.MainView
 import org.mjdev.desktop.context.DesktopContext.Companion.rememberDesktopContext
 import org.mjdev.desktop.context.LocalDesktopContext
 import org.mjdev.desktop.extensions.Compose.preview
 import org.mjdev.desktop.helpers.permission.rememberPermissionManager
 import org.mjdev.desktop.helpers.theme.DesktopTheme
-import org.mjdev.desktop.helpers.WakeLockHelper
-import org.mjdev.desktop.BuildConfig
+import org.mjdev.desktop.extensions.ComponentActivityExt.OnBackPress
+import org.mjdev.desktop.extensions.ComponentActivityExt.TRANSPARENT
+import org.mjdev.desktop.extensions.ComponentActivityExt.setBackgroundColor
 
+@Suppress("unused")
 @OptIn(ExperimentalPermissionsApi::class)
 class MainActivity : ComponentActivity() {
-    private val wakeLockHelper by lazy { WakeLockHelper(this) }
-
     override fun onCreate(savedInstanceState: Bundle?) {
-        wakeLockHelper.acquireWakeLock()
         installSplashScreen()
-        setKeepScreenOn()
         super.onCreate(savedInstanceState)
         enableEdgeToEdge(
-            statusBarStyle = SystemBarStyle.dark(Color.TRANSPARENT),
-            navigationBarStyle = SystemBarStyle.dark(Color.TRANSPARENT)
+            statusBarStyle = SystemBarStyle.dark(TRANSPARENT),
+            navigationBarStyle = SystemBarStyle.dark(TRANSPARENT)
         )
-        WindowCompat.getInsetsController(window, window.decorView).apply {
-            hide(WindowInsetsCompat.Type.statusBars())
-            hide(WindowInsetsCompat.Type.navigationBars())
-            systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
-        }
+        setBackgroundColor(Color.Black)
         setContent {
-            onBackPress(!BuildConfig.DEBUG)
+            OnBackPress()
             rememberPermissionManager()
             CompositionLocalProvider(
-                LocalDesktopContext provides rememberDesktopContext(baseContext)
+                LocalDesktopContext provides rememberDesktopContext(this)
             ) {
                 ActivityMain()
             }
         }
     }
 
-    override fun onDestroy() {
-        super.onDestroy()
-        resetKeepScreenOn()
-        wakeLockHelper.releaseWakeLock()
-    }
-
-    private fun ComponentActivity.setKeepScreenOn() {
-        window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
-    }
-
-    private fun ComponentActivity.resetKeepScreenOn() {
-        window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
-    }
-
     companion object {
         val TAG = MainActivity::class.simpleName
     }
 }
 
-@Composable
-fun onBackPress(
-    enabled:Boolean = true,
-    action: () -> Unit = {
-        Log.d(TAG, "OnBackPressed")
-    }
-) = BackHandler(true, action)
-
-@OptIn(ExperimentalPermissionsApi::class)
 @Composable
 private fun ActivityMain() {
     DesktopTheme {
         Scaffold(
             modifier = Modifier.fillMaxSize()
+                .background(Color.Black)
         ) { paddingValues ->
             Box(
                 modifier = Modifier
@@ -105,6 +71,7 @@ private fun ActivityMain() {
                     .padding(paddingValues)
                     .consumeWindowInsets(paddingValues)
                     .windowInsetsPadding(WindowInsets.safeDrawing)
+                    .background(Color.Black)
             ) {
                 MainView()
             }

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

@@ -1,11 +1,12 @@
 package org.mjdev.desktop.context
 
-import android.content.Context
+import android.annotation.SuppressLint
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.unit.DpSize
 import androidx.compose.ui.unit.dp
+import androidx.core.app.ComponentActivity
 import coil3.ImageLoader
 import coil3.PlatformContext
 import coil3.compose.LocalPlatformContext
@@ -48,7 +49,8 @@ import kotlin.reflect.full.companionObject
 
 @Suppress("unused", "MemberVisibilityCanBePrivate")
 class DesktopContext(
-    val context: Context? = null,
+    @SuppressLint("RestrictedApi")
+    val context: ComponentActivity? = null,
     override val scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
     override val imageLoader: ImageLoader? = null,
     val isInDesign: Boolean = false,
@@ -132,7 +134,8 @@ class DesktopContext(
     companion object {
         @Composable
         fun rememberDesktopContext(
-            context: Context,
+            @SuppressLint("RestrictedApi")
+            context: ComponentActivity,
             imageLoader: ImageLoader = asyncImageLoader(),
             platformContext: PlatformContext = LocalPlatformContext.current,
             scope: CoroutineScope = rememberCoroutineScope(),

+ 77 - 0
composeApp/src/androidMain/kotlin/org/mjdev/desktop/extensions/ComponentActivity.kt

@@ -0,0 +1,77 @@
+package org.mjdev.desktop.extensions
+
+import android.util.Log
+import android.view.WindowManager
+import androidx.activity.compose.BackHandler
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.toArgb
+import androidx.core.app.ComponentActivity
+import androidx.core.view.WindowCompat
+import androidx.core.view.WindowInsetsCompat
+import androidx.core.view.WindowInsetsControllerCompat
+import org.mjdev.desktop.BuildConfig
+import org.mjdev.desktop.activity.MainActivity.Companion.TAG
+import org.mjdev.desktop.helpers.WakeLockHelper
+import androidx.core.graphics.drawable.toDrawable
+
+@Suppress("unused", "MemberVisibilityCanBePrivate")
+object ComponentActivityExt {
+
+    const val WAKE_LOCK_TAG = 1
+
+    val TRANSPARENT : Int = Color.Transparent.toArgb()
+
+    val ComponentActivity.decorView
+        get() = window.decorView
+
+    val ComponentActivity.wakeLockHelper: WakeLockHelper
+        get() = decorView.getTag(WAKE_LOCK_TAG) as? WakeLockHelper ?: WakeLockHelper(this)
+
+    fun ComponentActivity.acquireWakeLock() {
+        wakeLockHelper.acquireWakeLock()
+    }
+
+    fun ComponentActivity.releaseWakeLock() {
+        wakeLockHelper.releaseWakeLock()
+    }
+
+    fun ComponentActivity.hideBars() {
+        WindowCompat.getInsetsController(window, window.decorView).apply {
+            hide(WindowInsetsCompat.Type.statusBars())
+            hide(WindowInsetsCompat.Type.navigationBars())
+            systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
+        }
+    }
+
+    fun ComponentActivity.showBars() {
+        WindowCompat.getInsetsController(window, window.decorView).apply {
+            show(WindowInsetsCompat.Type.statusBars())
+            show(WindowInsetsCompat.Type.navigationBars())
+            systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_DEFAULT
+        }
+    }
+
+    fun ComponentActivity.setKeepScreenOn() = with(window) {
+        addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
+    }
+
+    fun ComponentActivity.resetKeepScreenOn() = with(window) {
+        clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
+    }
+
+    // todo : black is white ?
+    fun ComponentActivity.setBackgroundColor(
+        color: Color = Color.Black
+    ) {
+        decorView.background = color.toArgb().toDrawable()
+    }
+
+    @Composable
+    fun OnBackPress(
+        enabled: Boolean = !BuildConfig.DEBUG,
+        action: () -> Unit = {
+            Log.d(TAG, "OnBackPressed")
+        }
+    ) = BackHandler(enabled, action)
+}

+ 4 - 3
composeApp/src/androidMain/kotlin/org/mjdev/desktop/helpers/crash/CrashHandler.kt

@@ -7,12 +7,13 @@ import android.content.Intent
 import android.util.Log
 import kotlin.system.exitProcess
 
+@Suppress("UNREACHABLE_CODE")
 class CrashHandler(
-    val context: Context,
-    val restartMillis:Long = 1000
+    private val context: Context,
+    private val restartMillis:Long = 1000
 ) : Thread.UncaughtExceptionHandler {
     private val defaultExceptionHandler = Thread.getDefaultUncaughtExceptionHandler()
-    val alarmMgr
+    private val alarmMgr
         get() = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
 
     @Suppress("KotlinUnreachableCode")

+ 1 - 0
composeApp/src/androidMain/res/values/splash.xml

@@ -6,6 +6,7 @@
         <item name="windowSplashScreenBackground">#00000000</item>
         <item name="windowSplashScreenAnimatedIcon">@mipmap/ic_launcher</item>
         <item name="android:windowIsTranslucent">true</item>
+        <item name="android:windowFullscreen">false</item>
         <item name="postSplashScreenTheme">@style/Theme.SplashScreen.Splash</item>
     </style>
 </resources>

+ 0 - 1
composeApp/src/commonMain/kotlin/org/mjdev/desktop/components/immersivelist/ImmersiveList.kt

@@ -20,7 +20,6 @@ import org.mjdev.desktop.extensions.FocusState.bringIntoViewIfChildrenAreFocused
 import org.jetbrains.compose.ui.tooling.preview.Preview
 
 @Suppress("IllegalExperimentalApiUsage")
-@OptIn(ExperimentalComposeUiApi::class)
 @Composable
 fun ImmersiveList(
     background: @Composable ImmersiveListBackgroundScope.(

+ 11 - 1
composeApp/src/commonMain/kotlin/org/mjdev/desktop/components/text/TextArea.kt

@@ -8,10 +8,13 @@ import androidx.compose.material.Icon
 import androidx.compose.material.TextFieldDefaults
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.platform.ClipboardManager
+import androidx.compose.ui.platform.LocalClipboardManager
 import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.unit.dp
 import org.mjdev.desktop.components.input.SelectableOutlineEditText
@@ -84,4 +87,11 @@ fun TextArea(
     )
 }
 
-// todo preview
+@Preview
+@Composable
+fun TextAreaPreview() {
+    TextArea(
+        textState = remember { mutableStateOf("Hello") },
+        clipboardManager = LocalClipboardManager.current
+    )
+}

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

@@ -1,3 +1,5 @@
+@file:Suppress("unused")
+
 package org.mjdev.desktop.extensions
 
 import androidx.compose.foundation.ExperimentalFoundationApi
@@ -64,7 +66,6 @@ object FocusState {
         key: Any? = Unit
     ) = remember(key) { FocusRequester() }
 
-    @OptIn(ExperimentalFoundationApi::class)
     fun Modifier.bringIntoViewIfChildrenAreFocused(): Modifier = composed(
         inspectorInfo = debugInspectorInfo { name = "bringIntoViewIfChildrenAreFocused" },
         factory = {

+ 4 - 1
composeApp/src/wasmJsMain/kotlin/org/mjdev/desktop/data/User.kt

@@ -1,3 +1,5 @@
+@file:Suppress("UNUSED_PARAMETER")
+
 package org.mjdev.desktop.data
 
 import okio.Path
@@ -8,6 +10,7 @@ import org.mjdev.desktop.interfaces.ITheme
 import org.mjdev.desktop.interfaces.IUser
 import org.mjdev.desktop.interfaces.IUserDirs
 
+@Suppress("MemberVisibilityCanBePrivate")
 class User() : IUser {
     override val userName: String
         get() = "-" // todo
@@ -57,6 +60,6 @@ class User() : IUser {
 
         fun allUsers(
             context: IDesktopContext
-        ): List<User> = emptyList()
+        ): List<User> = listOf(NOBODY)
     }
 }

+ 4 - 6
composeApp/src/wasmJsMain/kotlin/org/mjdev/desktop/extensions/ImageBitmapExt.kt

@@ -6,19 +6,17 @@ import org.mjdev.desktop.interfaces.IDesktopContext
 @Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")
 actual object ImageBitmapExt {
 
+    // todo
     actual fun ImageBitmap.cut(
         x: Int,
         y: Int,
         width: Int,
         height: Int
-    ): ImageBitmap {
-        TODO("Not yet implemented")
-    }
+    ): ImageBitmap = this
 
+    // todo
     actual suspend fun IDesktopContext.loadPicture(
         src: Any?
-    ): ImageBitmap? {
-        TODO("Not yet implemented")
-    }
+    ): ImageBitmap? = null
 
 }

+ 3 - 0
composeApp/src/wasmJsMain/kotlin/org/mjdev/desktop/helpers/streams/ResourceStream.wasmJs.kt

@@ -5,8 +5,11 @@ package org.mjdev.desktop.helpers.streams
 actual class ResourceStream actual constructor(
     val resourcePath: String
 ) {
+    // todo
     actual val bytes: ByteArray
         get() = byteArrayOf()
+
+    // todo
     actual val text: String
         get() = ""
 }

+ 1 - 0
composeApp/src/wasmJsMain/kotlin/org/mjdev/desktop/helpers/system/meminfo/MemInfo.wasmJs.kt

@@ -2,6 +2,7 @@ package org.mjdev.desktop.helpers.system.meminfo
 
 import org.mjdev.desktop.interfaces.IDesktopContext
 
+@Suppress("FunctionName")
 actual fun MemInfo(
     context: IDesktopContext
 ): MemInfoStub = MemInfoStub(context)

+ 110 - 124
composeApp/src/wasmJsMain/kotlin/org/mjdev/desktop/main/MainView.kt

@@ -1,150 +1,136 @@
 package org.mjdev.desktop.main
 
 import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.max
-import org.mjdev.desktop.components.appsmenu.AppsMenu
-import org.mjdev.desktop.components.appsmenu.AppsMenuState.Companion.rememberAppsMenuState
-import org.mjdev.desktop.components.controlcenter.ControlCenter
-import org.mjdev.desktop.components.desktop.Desktop
-import org.mjdev.desktop.components.desktop.widgets.MemoryChart
-import org.mjdev.desktop.components.desktoppanel.DesktopPanel
-import org.mjdev.desktop.components.sliding.base.VisibilityState.Companion.rememberVisibilityState
-import org.mjdev.desktop.components.tooltip.TooltipState
-import org.mjdev.desktop.components.tooltip.rememberTooltipState
 import org.mjdev.desktop.context.DesktopContextScope.Companion.withDesktopContext
-import org.mjdev.desktop.extensions.Compose.isDesign
 import org.mjdev.desktop.extensions.Compose.preview
-import org.mjdev.desktop.extensions.LaunchedEffect.runAsync
-import org.mjdev.desktop.extensions.MutableStateExt.rememberCalculated
 import org.jetbrains.compose.ui.tooling.preview.Preview
+import org.mjdev.desktop.components.background.BackgroundImage
+import org.mjdev.desktop.components.qrcode.QrCodeView
 
 @Composable
 fun MainView() = withDesktopContext {
-//    Box(
-//        modifier = Modifier.fillMaxSize(),
-//        contentAlignment = Alignment.Center
-//    ) {
-//        BackgroundImage(
-//            modifier = Modifier.fillMaxSize(),
-//            switchDelay = theme.backgroundRotationDelay,
-//            images = backgrounds,
-//            onChange = { src ->
-//                palette.apply {
-//                    update(src)
-//                }.also { p ->
-//                    context.currentUser.theme.backgroundColor = p.backgroundColor
-//                }
-//            }
-//        )
-//        Box(
-//            modifier = Modifier.size(256.dp, 256.dp),
-//            contentAlignment = Alignment.Center
-//        ) {
-//            Column {
-//                QrCodeView(
-//                    modifier = Modifier.fillMaxSize()
-//                )
-//            }
-//        }
-//    }
-    val tooltipState: TooltipState = rememberTooltipState()
-    val appsMenuState = rememberAppsMenuState(
-        visible = isDesign
-    )
-    val controlCenterState = rememberVisibilityState(
-        visible = isDesign
-    )
-    val panelState = rememberVisibilityState(
-        visible = isDesign || !panelAutoHideEnabled,
-        enabled = panelAutoHideEnabled,
-        autoHideDelay = panelHideDelay
-    )
-    val bottomPadding by rememberCalculated(
-        panelState.enabled, panelState.height
-    ) {
-        if (panelState.enabled) 0.dp
-        else max(0.dp, panelState.height)
-    }
-//    val installWindowState = rememberVisibilityState()
-//    val infoWindowState = rememberVisibilityState(false) //(api.isFirstStart || api.isDebug) // todo
-    val onTooltip: (item: Any?) -> Unit = { item ->
-        println("Showing tooltip : $item")
-        tooltipState.show(item)
-    }
-    // todo not showing nothing
     Box(
         modifier = Modifier.fillMaxSize(),
         contentAlignment = Alignment.Center
     ) {
-        // todo, something prevent preview
-        if (!isDesign) {
-            Desktop(
-                tooltipState = tooltipState,
-                onTooltip = onTooltip,
-                padding = PaddingValues(
-                    bottom = bottomPadding
-                ),
-                widgets = {
-                    MemoryChart(
-                        modifier = Modifier
-                            .size(350.dp, 300.dp)
-                            .align(Alignment.BottomEnd)
-                    )
-                }, onLeftMouseClick = {
-                    runAsync {
-                        panelState.hide()
-                        appsMenuState.hide()
-                        controlCenterState.hide()
-                    }
-                }, onRightMouseClick = {
-//                contextMenuState.show()
-                })
-        }
-        DesktopPanel(
-            panelState = panelState,
-            onTooltip = onTooltip,
-            onMenuIconClicked = {
-                runAsync {
-                    appsMenuState.show()
+        BackgroundImage(
+            modifier = Modifier.fillMaxSize(),
+            switchDelay = theme.backgroundRotationDelay,
+            images = backgrounds,
+            onChange = { src ->
+                palette.apply {
+                    update(src)
+                }.also { p ->
+                    context.currentUser.theme.backgroundColor = p.backgroundColor
                 }
             }
         )
-        ControlCenter(
-            modifier = Modifier
-                .fillMaxHeight()
-                .align(Alignment.TopEnd),
-            controlCenterState = controlCenterState,
-            onTooltip = onTooltip,
-        )
-        AppsMenu(
-            modifier = Modifier
-                .wrapContentSize()
-                .align(Alignment.BottomStart),
-            appsMenuState = appsMenuState,
-            panelState = panelState,
-            onAppClick = { app ->
-                runAsync {
-                    app.start()
-                    appsMenuState.hide()
-                }
-            },
-            onAppContextMenuClick = {},
-            onCategoryContextMenuClick = {},
-            onUserAvatarClick = {},
-            onActionClick = {},
-            onTooltip = onTooltip,
-        )
+        Box(
+            modifier = Modifier.size(256.dp, 256.dp),
+            contentAlignment = Alignment.Center
+        ) {
+            Column {
+                QrCodeView(
+                    modifier = Modifier.fillMaxSize()
+                )
+            }
+        }
     }
+//    val tooltipState: TooltipState = rememberTooltipState()
+//    val appsMenuState = rememberAppsMenuState(
+//        visible = isDesign
+//    )
+//    val controlCenterState = rememberVisibilityState(
+//        visible = isDesign
+//    )
+//    val panelState = rememberVisibilityState(
+//        visible = isDesign || !panelAutoHideEnabled,
+//        enabled = panelAutoHideEnabled,
+//        autoHideDelay = panelHideDelay
+//    )
+//    val bottomPadding by rememberCalculated(
+//        panelState.enabled, panelState.height
+//    ) {
+//        if (panelState.enabled) 0.dp
+//        else max(0.dp, panelState.height)
+//    }
+////    val installWindowState = rememberVisibilityState()
+////    val infoWindowState = rememberVisibilityState(false) //(api.isFirstStart || api.isDebug) // todo
+//    val onTooltip: (item: Any?) -> Unit = { item ->
+//        println("Showing tooltip : $item")
+//        tooltipState.show(item)
+//    }
+//    // todo not showing nothing
+//    Box(
+//        modifier = Modifier.fillMaxSize(),
+//        contentAlignment = Alignment.Center
+//    ) {
+//        // todo, something prevent preview
+//        if (!isDesign) {
+//            Desktop(
+//                tooltipState = tooltipState,
+//                onTooltip = onTooltip,
+//                padding = PaddingValues(
+//                    bottom = bottomPadding
+//                ),
+//                widgets = {
+//                    MemoryChart(
+//                        modifier = Modifier
+//                            .size(350.dp, 300.dp)
+//                            .align(Alignment.BottomEnd)
+//                    )
+//                }, onLeftMouseClick = {
+//                    runAsync {
+//                        panelState.hide()
+//                        appsMenuState.hide()
+//                        controlCenterState.hide()
+//                    }
+//                }, onRightMouseClick = {
+////                contextMenuState.show()
+//                })
+//        }
+//        DesktopPanel(
+//            panelState = panelState,
+//            onTooltip = onTooltip,
+//            onMenuIconClicked = {
+//                runAsync {
+//                    appsMenuState.show()
+//                }
+//            }
+//        )
+//        ControlCenter(
+//            modifier = Modifier
+//                .fillMaxHeight()
+//                .align(Alignment.TopEnd),
+//            controlCenterState = controlCenterState,
+//            onTooltip = onTooltip,
+//        )
+//        AppsMenu(
+//            modifier = Modifier
+//                .wrapContentSize()
+//                .align(Alignment.BottomStart),
+//            appsMenuState = appsMenuState,
+//            panelState = panelState,
+//            onAppClick = { app ->
+//                runAsync {
+//                    app.start()
+//                    appsMenuState.hide()
+//                }
+//            },
+//            onAppContextMenuClick = {},
+//            onCategoryContextMenuClick = {},
+//            onUserAvatarClick = {},
+//            onActionClick = {},
+//            onTooltip = onTooltip,
+//        )
+//    }
 }
 
 @Preview

+ 13 - 11
composeApp/src/wasmJsMain/kotlin/org/mjdev/desktop/system/Filesystem.wasmJs.kt

@@ -13,21 +13,23 @@ actual object Filesystem : FileSystem() {
         TODO("Not yet implemented")
     }
 
-    actual fun extension(path: Path): String {
-        TODO("Not yet implemented")
+    actual fun extension(
+        path: Path
+    ): String = path.name.split(".").let {
+        if (it.size>0) it.last() else ""
     }
 
-    actual fun isDirectory(path: Path): Boolean {
-        TODO("Not yet implemented")
-    }
+    actual fun isDirectory(
+        path: Path
+    ): Boolean = metadataOrNull(path)?.isDirectory == true
 
-    actual fun isFile(path: Path): Boolean {
-        TODO("Not yet implemented")
-    }
+    actual fun isFile(
+        path: Path
+    ): Boolean = metadataOrNull(path)?.isRegularFile == true
 
-    actual fun isSymbolicLink(path: Path): Boolean {
-        TODO("Not yet implemented")
-    }
+    actual fun isSymbolicLink(
+        path: Path
+    ): Boolean = metadata(path)?.symlinkTarget != null
 
     actual fun cwd(): Path {
         TODO("Not yet implemented")

+ 1 - 1
gradle.properties

@@ -21,7 +21,7 @@ android.defaults.buildfeatures.aidl=false
 android.defaults.buildfeatures.renderscript=false
 android.defaults.buildfeatures.resvalues=true
 android.defaults.buildfeatures.shaders=false
-android.enableJetifier=true
+android.enableJetifier=false
 android.useAndroidX=true
 # kotlin Multiplatform
 kotlin.mpp.enableCInteropCommonization=true

+ 11 - 10
gradle/libs.versions.toml

@@ -9,19 +9,20 @@ app-name = "mjdev-desktop"
 app-pkg-name = "org.mjdev.desktop"
 app-pkg-version = "1.0.3"
 app-vendor = "mjdev"
+java-language-version="17"
 # app specific android
 android-compile-sdk = "36"
 android-min-sdk = "24"
 #android-target-sdk = "36"
 # lib versions
-gradle = "8.10.0"
-gradle-kotlinter = "4.3.0"
+gradle = "8.10.1"
+#gradle-kotlinter = "4.3.0"
 androidx-activity-compose = "1.10.1"
-androidx-lifecycle = "2.8.4"
-androidx-ui-tooling = "1.8.1"
+androidx-lifecycle = "2.9.0"
+androidx-ui-tooling = "1.8.2"
 app-icon = "1.2.6"
 coil = "3.2.0"
-compose-plugin = "1.8.0"
+compose-plugin = "1.8.1"
 core-splash-screen = "1.0.1"
 generative-ai = "0.9.0-1.1.0"
 gettext-lib = "1.11.0"
@@ -36,7 +37,7 @@ kotlinx-coroutines = "1.10.2"
 kotlinx-date-time = "0.6.2"
 #logback = "1.5.7"
 okhttp3 = "4.12.0"
-okio = "3.11.0"
+#okio = "3.12.0"
 qrose = "1.0.1"
 tts = "3.0.0"
 ychat = "1.4.1"
@@ -47,8 +48,8 @@ kover = "0.9.1"
 qodana = "2025.1.1"
 intelliJPlatform = "2.6.0"
 changelog = "2.2.1"
-gradle-ktlint="12.2.0"
-foojay-resolver-convention="0.9.0"
+gradle-ktlint="12.3.0"
+#foojay-resolver-convention="0.9.0"
 manes-versions="0.52.0"
 
 [libraries]
@@ -56,7 +57,7 @@ manes-versions="0.52.0"
 gradle = { module = "com.android.tools.build:gradle", version.ref = "gradle" }
 gradle-api = { module = "com.android.tools.build:gradle-api", version.ref = "gradle" }
 gradle-kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
-gradle-kotlinter = { module = "org.jmailen.gradle:kotlinter-gradle", version.ref = "gradle-kotlinter" }
+#gradle-kotlinter = { module = "org.jmailen.gradle:kotlinter-gradle", version.ref = "gradle-kotlinter" }
 # androidx
 androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity-compose" }
 androidx-lifecycle-viewmodel = { group = "org.jetbrains.androidx.lifecycle", name = "lifecycle-viewmodel", version.ref = "androidx-lifecycle" }
@@ -68,7 +69,7 @@ jetlime = { module = "io.github.pushpalroy:jetlime", version.ref = "jetlime" }
 # translations
 gettext-lib = { module = "org.mini2Dx:gettext-lib", version.ref = "gettext-lib" }
 # io
-okio = { module = "com.squareup.okio:okio", version.ref = "okio" }
+#okio = { module = "com.squareup.okio:okio", version.ref = "okio" }
 # chat
 ychat = { module = "co.yml:ychat", version.ref = "ychat" }
 # json