├── example ├── .gitignore ├── proguard-rules-jvm.pro ├── proguard-rules-android.pro └── src │ ├── androidMain │ ├── res │ │ ├── values │ │ │ ├── strings.xml │ │ │ └── themes.xml │ │ ├── mipmap │ │ │ └── ic_launcher.png │ │ ├── mipmap-v26 │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_launcher.xml │ │ ├── values-night │ │ │ └── themes.xml │ │ ├── values-night-v29 │ │ │ └── themes.xml │ │ ├── mipmap-night-v26 │ │ │ └── ic_launcher.xml │ │ ├── values-v31 │ │ │ └── themes.xml │ │ └── values-night-v31 │ │ │ └── themes.xml │ ├── AndroidManifest.xml │ └── kotlin │ │ └── top │ │ └── yukonga │ │ └── miuix │ │ └── uitest │ │ └── MainActivity.kt │ ├── macosMain │ ├── resources │ │ └── Miuix.icns │ └── kotlin │ │ └── Main.macos.kt │ ├── webMain │ ├── resources │ │ ├── favicon.ico │ │ ├── index.html │ │ └── styles.css │ └── kotlin │ │ ├── WebPlatformApi.web.kt │ │ └── Main.web.kt │ ├── desktopMain │ ├── resources │ │ ├── linux │ │ │ └── icon.png │ │ ├── macos │ │ │ └── icon.icns │ │ └── windows │ │ │ └── icon.ico │ ├── composeResources │ │ └── drawable │ │ │ └── icon.png │ └── kotlin │ │ └── Main.desktop.kt │ ├── commonMain │ ├── composeResources │ │ └── drawable │ │ │ └── ic_launcher.png │ └── kotlin │ │ ├── App.kt │ │ ├── ui │ │ └── Theme.kt │ │ ├── utils │ │ └── FPSMonitor.kt │ │ └── preview │ │ └── Preview.kt │ ├── iosMain │ └── kotlin │ │ └── Main.ios.kt │ ├── jsMain │ └── kotlin │ │ └── WebPlatformApi.js.kt │ └── wasmJsMain │ └── kotlin │ └── WebPlatformApi.wasmJs.kt ├── .gitattributes ├── .idea ├── icon.png ├── vcs.xml ├── xcode.xml ├── migrations.xml ├── copyright │ ├── profiles_settings.xml │ └── miuix.xml ├── scopes │ └── miuix.xml ├── runConfigurations │ ├── iosApp.xml │ ├── docs__js_.xml │ ├── example__js_.xml │ ├── example__macos_.xml │ ├── example__wasmJs_.xml │ └── example__desktop_.xml └── runConfigurations.xml ├── assets ├── 001.webp ├── 002.webp ├── 003.webp ├── 004.webp ├── 005.webp └── 006.webp ├── docs ├── public │ └── Icon.webp ├── .vitepress │ ├── theme │ │ └── style │ │ │ ├── index.css │ │ │ ├── img.css │ │ │ ├── hidden.css │ │ │ ├── blur.css │ │ │ └── var.css │ ├── locales │ │ └── index.ts │ └── config.ts ├── package.json ├── demo │ ├── src │ │ ├── jsMain │ │ │ ├── kotlin │ │ │ │ └── Main.js.kt │ │ │ └── resources │ │ │ │ └── index.html │ │ └── commonMain │ │ │ └── kotlin │ │ │ ├── ColorPaletteDemo.kt │ │ │ ├── ColorPickerDemo.kt │ │ │ ├── SliderDemo.kt │ │ │ ├── SmallTitleDemo.kt │ │ │ ├── TabRowDemo.kt │ │ │ ├── SuperArrowDemo.kt │ │ │ ├── SwitchDemo.kt │ │ │ ├── SurfaceDemo.kt │ │ │ ├── CheckboxDemo.kt │ │ │ ├── WindowDialogDemo.kt │ │ │ ├── TextFieldDemo.kt │ │ │ ├── SuperBottomSheetDemo.kt │ │ │ ├── SuperSwitchDemo.kt │ │ │ ├── SuperDialogDemo.kt │ │ │ ├── BasicComponentDemo.kt │ │ │ ├── WindowDropdownDemo.kt │ │ │ └── WindowListPopupDemo.kt │ └── build.gradle.kts ├── zh_CN │ ├── index.md │ ├── guide │ │ ├── best-practices.md │ │ ├── textstyles.md │ │ ├── multiplatform.md │ │ ├── theme.md │ │ └── getting-started.md │ └── components │ │ └── smalltitle.md ├── index.md ├── iconGen │ └── build.gradle.kts ├── guide │ ├── textstyles.md │ ├── best-practices.md │ ├── multiplatform.md │ └── theme.md └── components │ └── smalltitle.md ├── iosApp ├── Configuration │ └── Config.xcconfig ├── iosApp │ ├── Assets.xcassets │ │ ├── Contents.json │ │ └── AppIcon.appiconset │ │ │ ├── app-icon-1024.png │ │ │ └── Contents.json │ ├── iosApp.swift │ ├── ContentView.swift │ └── Info.plist └── iosApp.xcodeproj │ └── xcshareddata │ └── xcschemes │ └── iosApp.xcscheme ├── spotless └── copyright.txt ├── gradle ├── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties └── libs.versions.toml ├── convention-plugins ├── build.gradle.kts ├── settings.gradle.kts └── src │ └── main │ └── kotlin │ └── root.publication.gradle.kts ├── miuix ├── src │ ├── commonMain │ │ └── kotlin │ │ │ └── top │ │ │ └── yukonga │ │ │ └── miuix │ │ │ └── kmp │ │ │ ├── icon │ │ │ ├── MiuixIcon.kt │ │ │ └── icons │ │ │ │ ├── basic │ │ │ │ ├── ArrowRight.kt │ │ │ │ ├── Check.kt │ │ │ │ └── Search.kt │ │ │ │ ├── useful │ │ │ │ ├── Like.kt │ │ │ │ ├── Confirm.kt │ │ │ │ ├── ImmersionMore.kt │ │ │ │ ├── Search.kt │ │ │ │ ├── Play.kt │ │ │ │ ├── Remove.kt │ │ │ │ └── Unlike.kt │ │ │ │ └── other │ │ │ │ └── GitHub.kt │ │ │ ├── theme │ │ │ ├── DynamicColors.kt │ │ │ ├── ContentColor.kt │ │ │ └── ThemeController.kt │ │ │ ├── anim │ │ │ ├── SinOutEasing.kt │ │ │ ├── DecelerateEasing.kt │ │ │ └── AccelerateEasing.kt │ │ │ ├── color │ │ │ ├── space │ │ │ │ ├── OkHsv.kt │ │ │ │ ├── Hsv.kt │ │ │ │ ├── OkLch.kt │ │ │ │ └── OkLab.kt │ │ │ └── api │ │ │ │ └── Extensions.kt │ │ │ ├── utils │ │ │ └── Utils.kt │ │ │ ├── basic │ │ │ ├── SmallTitle.kt │ │ │ ├── FloatingActionButton.kt │ │ │ └── Divider.kt │ │ │ └── interfaces │ │ │ └── HoldDownInteraction.kt │ ├── jsMain │ │ └── kotlin │ │ │ └── top │ │ │ └── yukonga │ │ │ └── miuix │ │ │ └── kmp │ │ │ ├── theme │ │ │ └── DynamicColors.js.kt │ │ │ └── utils │ │ │ └── Utils.js.kt │ ├── iosMain │ │ └── kotlin │ │ │ └── top │ │ │ └── yukonga │ │ │ └── miuix │ │ │ └── kmp │ │ │ ├── theme │ │ │ └── DynamicColors.ios.kt │ │ │ └── utils │ │ │ └── Utils.ios.kt │ ├── macosMain │ │ └── kotlin │ │ │ └── top │ │ │ └── yukonga │ │ │ └── miuix │ │ │ └── kmp │ │ │ ├── theme │ │ │ └── DynamicColors.macos.kt │ │ │ └── utils │ │ │ └── Utils.macos.kt │ ├── desktopMain │ │ └── kotlin │ │ │ └── top │ │ │ └── yukonga │ │ │ └── miuix │ │ │ └── kmp │ │ │ ├── theme │ │ │ └── DynamicColors.desktop.kt │ │ │ └── utils │ │ │ └── Utils.desktop.kt │ ├── wasmJsMain │ │ └── kotlin │ │ │ └── top │ │ │ └── yukonga │ │ │ └── miuix │ │ │ └── kmp │ │ │ ├── theme │ │ │ └── DynamicColors.wasmJs.kt │ │ │ └── utils │ │ │ └── Utils.wasmJs.kt │ └── androidMain │ │ └── kotlin │ │ └── top │ │ └── yukonga │ │ └── miuix │ │ └── kmp │ │ └── theme │ │ └── DynamicColors.android.kt └── build.gradle.kts ├── .github ├── dependabot.yml └── workflows │ ├── test.yml │ ├── docs.yml │ └── publish.yml ├── .gitignore ├── gradle.properties ├── settings.gradle.kts └── gradlew.bat /example/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /example/proguard-rules-jvm.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/proguard-rules-android.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.idea/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compose-miuix-ui/miuix/HEAD/.idea/icon.png -------------------------------------------------------------------------------- /assets/001.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compose-miuix-ui/miuix/HEAD/assets/001.webp -------------------------------------------------------------------------------- /assets/002.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compose-miuix-ui/miuix/HEAD/assets/002.webp -------------------------------------------------------------------------------- /assets/003.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compose-miuix-ui/miuix/HEAD/assets/003.webp -------------------------------------------------------------------------------- /assets/004.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compose-miuix-ui/miuix/HEAD/assets/004.webp -------------------------------------------------------------------------------- /assets/005.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compose-miuix-ui/miuix/HEAD/assets/005.webp -------------------------------------------------------------------------------- /assets/006.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compose-miuix-ui/miuix/HEAD/assets/006.webp -------------------------------------------------------------------------------- /docs/public/Icon.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compose-miuix-ui/miuix/HEAD/docs/public/Icon.webp -------------------------------------------------------------------------------- /iosApp/Configuration/Config.xcconfig: -------------------------------------------------------------------------------- 1 | TEAM_ID= 2 | BUNDLE_ID=top.yukonga.miuix.kmp 3 | APP_NAME=Miuix 4 | -------------------------------------------------------------------------------- /example/src/androidMain/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Miuix 3 | -------------------------------------------------------------------------------- /spotless/copyright.txt: -------------------------------------------------------------------------------- 1 | // Copyright $YEAR, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compose-miuix-ui/miuix/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /iosApp/iosApp/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } -------------------------------------------------------------------------------- /docs/.vitepress/theme/style/index.css: -------------------------------------------------------------------------------- 1 | @import './var.css'; 2 | @import './blur.css'; 3 | @import './hidden.css'; 4 | @import './img.css'; -------------------------------------------------------------------------------- /docs/.vitepress/theme/style/img.css: -------------------------------------------------------------------------------- 1 | td img { 2 | display: block; 3 | margin-left: auto; 4 | margin-right: auto; 5 | width: 24px; 6 | } -------------------------------------------------------------------------------- /example/src/macosMain/resources/Miuix.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compose-miuix-ui/miuix/HEAD/example/src/macosMain/resources/Miuix.icns -------------------------------------------------------------------------------- /example/src/webMain/resources/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compose-miuix-ui/miuix/HEAD/example/src/webMain/resources/favicon.ico -------------------------------------------------------------------------------- /example/src/androidMain/res/mipmap/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compose-miuix-ui/miuix/HEAD/example/src/androidMain/res/mipmap/ic_launcher.png -------------------------------------------------------------------------------- /example/src/desktopMain/resources/linux/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compose-miuix-ui/miuix/HEAD/example/src/desktopMain/resources/linux/icon.png -------------------------------------------------------------------------------- /example/src/desktopMain/resources/macos/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compose-miuix-ui/miuix/HEAD/example/src/desktopMain/resources/macos/icon.icns -------------------------------------------------------------------------------- /example/src/desktopMain/resources/windows/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compose-miuix-ui/miuix/HEAD/example/src/desktopMain/resources/windows/icon.ico -------------------------------------------------------------------------------- /convention-plugins/build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | plugins { 5 | `kotlin-dsl` 6 | } -------------------------------------------------------------------------------- /example/src/desktopMain/composeResources/drawable/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compose-miuix-ui/miuix/HEAD/example/src/desktopMain/composeResources/drawable/icon.png -------------------------------------------------------------------------------- /example/src/androidMain/res/mipmap-v26/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compose-miuix-ui/miuix/HEAD/example/src/androidMain/res/mipmap-v26/ic_launcher_foreground.png -------------------------------------------------------------------------------- /example/src/commonMain/composeResources/drawable/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compose-miuix-ui/miuix/HEAD/example/src/commonMain/composeResources/drawable/ic_launcher.png -------------------------------------------------------------------------------- /iosApp/iosApp/iosApp.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | @main 4 | struct iosApp: App { 5 | var body: some Scene { 6 | WindowGroup { 7 | ContentView() 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compose-miuix-ui/miuix/HEAD/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/xcode.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /example/src/iosMain/kotlin/Main.ios.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import androidx.compose.ui.window.ComposeUIViewController 5 | 6 | fun main() = ComposeUIViewController { App() } 7 | -------------------------------------------------------------------------------- /example/src/androidMain/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/copyright/miuix.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/anim/SinOutEasing.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.anim 5 | 6 | import androidx.compose.animation.core.Easing 7 | import kotlin.math.PI 8 | import kotlin.math.sin 9 | 10 | val SinOutEasing: Easing = Easing { fraction -> 11 | sin((fraction * PI / 2).toFloat()) 12 | } -------------------------------------------------------------------------------- /example/src/androidMain/res/mipmap-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/src/androidMain/res/mipmap-night-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/src/androidMain/res/values-v31/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "license": "Apache-2.0", 3 | "devDependencies": { 4 | "@nolebase/vitepress-plugin-enhanced-readabilities": "^2.18.2", 5 | "@nolebase/vitepress-plugin-git-changelog": "^2.18.2", 6 | "medium-zoom": "^1.1.0", 7 | "vitepress": "^2.0.0-alpha.15", 8 | "vitepress-plugin-comment-with-giscus": "^1.1.15", 9 | "vue": "^3.5.25" 10 | }, 11 | "scripts": { 12 | "docs:dev": "vitepress dev --host", 13 | "docs:build": "vitepress build", 14 | "docs:preview": "vitepress preview" 15 | }, 16 | "type": "module" 17 | } 18 | -------------------------------------------------------------------------------- /example/src/androidMain/res/values-night-v31/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "gradle" 9 | directory: "/" 10 | schedule: 11 | interval: "daily" 12 | 13 | - package-ecosystem: "github-actions" 14 | directory: "/" 15 | schedule: 16 | interval: "daily" 17 | -------------------------------------------------------------------------------- /docs/.vitepress/locales/index.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitepress"; 2 | import en_US from "./en_US"; 3 | import zh_CN from "./zh_CN"; 4 | export default defineConfig({ 5 | locales: { 6 | root: { 7 | label: "English", 8 | lang: en_US.lang, 9 | themeConfig: en_US.themeConfig, 10 | description: en_US.description, 11 | }, 12 | zh_CN: { 13 | label: "简体中文", 14 | lang: zh_CN.lang, 15 | themeConfig: zh_CN.themeConfig, 16 | description: zh_CN.description, 17 | }, 18 | }, 19 | }); 20 | -------------------------------------------------------------------------------- /iosApp/iosApp/ContentView.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import SwiftUI 3 | import shared 4 | 5 | struct ComposeView: UIViewControllerRepresentable { 6 | func makeUIViewController(context: Context) -> UIViewController { 7 | Main_iosKt.main() 8 | } 9 | 10 | func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} 11 | } 12 | 13 | struct ContentView: View { 14 | var body: some View { 15 | ComposeView() 16 | .ignoresSafeArea(.keyboard) // Compose has own keyboard handler 17 | .edgesIgnoringSafeArea(.all) // edge to edge 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /convention-plugins/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | @file:Suppress("UnstableApiUsage") 5 | 6 | pluginManagement { 7 | repositories { 8 | google() 9 | gradlePluginPortal() 10 | mavenCentral() 11 | } 12 | } 13 | 14 | dependencyResolutionManagement { 15 | repositories { 16 | google() 17 | gradlePluginPortal() 18 | mavenCentral() 19 | } 20 | 21 | versionCatalogs { 22 | create("libs") { 23 | from(files("../gradle/libs.versions.toml")) 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /docs/demo/src/jsMain/kotlin/Main.js.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import androidx.compose.ui.ExperimentalComposeUiApi 5 | import androidx.compose.ui.window.ComposeViewport 6 | import kotlinx.browser.document 7 | import org.w3c.dom.url.URLSearchParams 8 | 9 | @OptIn(ExperimentalComposeUiApi::class) 10 | fun main() { 11 | val iFrameParams = URLSearchParams(document.location?.search) 12 | val id = iFrameParams.get("id") 13 | ComposeViewport( 14 | viewportContainerId = "ComposeTarget" 15 | ) { 16 | Demo(demoId = id) 17 | } 18 | } -------------------------------------------------------------------------------- /miuix/src/jsMain/kotlin/top/yukonga/miuix/kmp/theme/DynamicColors.js.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.theme 5 | 6 | import androidx.compose.material3.ColorScheme 7 | import androidx.compose.runtime.Composable 8 | 9 | @Composable 10 | actual fun platformDynamicColors(dark: Boolean): Colors { 11 | val cs: ColorScheme = if (dark) { 12 | androidx.compose.material3.darkColorScheme() 13 | } else { 14 | androidx.compose.material3.lightColorScheme() 15 | } 16 | return mapMd3ToMiuixColorsCommon(cs, dark) 17 | } 18 | 19 | -------------------------------------------------------------------------------- /miuix/src/iosMain/kotlin/top/yukonga/miuix/kmp/theme/DynamicColors.ios.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.theme 5 | 6 | import androidx.compose.material3.ColorScheme 7 | import androidx.compose.runtime.Composable 8 | 9 | @Composable 10 | actual fun platformDynamicColors(dark: Boolean): Colors { 11 | val cs: ColorScheme = if (dark) { 12 | androidx.compose.material3.darkColorScheme() 13 | } else { 14 | androidx.compose.material3.lightColorScheme() 15 | } 16 | return mapMd3ToMiuixColorsCommon(cs, dark) 17 | } 18 | 19 | -------------------------------------------------------------------------------- /miuix/src/macosMain/kotlin/top/yukonga/miuix/kmp/theme/DynamicColors.macos.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.theme 5 | 6 | import androidx.compose.material3.ColorScheme 7 | import androidx.compose.runtime.Composable 8 | 9 | @Composable 10 | actual fun platformDynamicColors(dark: Boolean): Colors { 11 | val cs: ColorScheme = if (dark) { 12 | androidx.compose.material3.darkColorScheme() 13 | } else { 14 | androidx.compose.material3.lightColorScheme() 15 | } 16 | return mapMd3ToMiuixColorsCommon(cs, dark) 17 | } 18 | 19 | -------------------------------------------------------------------------------- /miuix/src/desktopMain/kotlin/top/yukonga/miuix/kmp/theme/DynamicColors.desktop.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.theme 5 | 6 | import androidx.compose.material3.ColorScheme 7 | import androidx.compose.runtime.Composable 8 | 9 | @Composable 10 | actual fun platformDynamicColors(dark: Boolean): Colors { 11 | val cs: ColorScheme = if (dark) { 12 | androidx.compose.material3.darkColorScheme() 13 | } else { 14 | androidx.compose.material3.lightColorScheme() 15 | } 16 | return mapMd3ToMiuixColorsCommon(cs, dark) 17 | } 18 | 19 | -------------------------------------------------------------------------------- /miuix/src/wasmJsMain/kotlin/top/yukonga/miuix/kmp/theme/DynamicColors.wasmJs.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.theme 5 | 6 | import androidx.compose.material3.ColorScheme 7 | import androidx.compose.runtime.Composable 8 | 9 | @Composable 10 | actual fun platformDynamicColors(dark: Boolean): Colors { 11 | val cs: ColorScheme = if (dark) { 12 | androidx.compose.material3.darkColorScheme() 13 | } else { 14 | androidx.compose.material3.lightColorScheme() 15 | } 16 | return mapMd3ToMiuixColorsCommon(cs, dark) 17 | } 18 | 19 | -------------------------------------------------------------------------------- /example/src/webMain/resources/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Miuix 10 | 11 | 12 | 13 | 14 |
Loading
15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /convention-plugins/src/main/kotlin/root.publication.gradle.kts: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | val isRelease = project.hasProperty("release") 5 | val baseVersion = "0.7.3" 6 | 7 | fun gitShort(): String = try { 8 | val p = ProcessBuilder("git", "rev-parse", "--short", "HEAD").start() 9 | p.inputStream.bufferedReader().readText().trim().ifEmpty { "local" } 10 | } catch (_: Exception) { 11 | "local" 12 | } 13 | 14 | val computedVersion = if (isRelease) baseVersion else "$baseVersion-${gitShort()}-SNAPSHOT" 15 | allprojects { 16 | group = "top.yukonga.miuix.kmp" 17 | version = computedVersion 18 | } 19 | -------------------------------------------------------------------------------- /miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/color/space/OkHsv.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.color.space 5 | 6 | import androidx.compose.ui.graphics.Color 7 | import top.yukonga.miuix.kmp.color.core.Transforms 8 | 9 | /** 10 | * OkHSV representation with normalized, user-friendly ranges. 11 | * - `h`: hue in degrees [0, 360) 12 | * - `s`: saturation in percent [0.0, 100.0] 13 | * - `v`: value/brightness in percent [0.0, 100.0 14 | */ 15 | data class OkHsv(val h: Float, val s: Float, val v: Float) { 16 | fun toColor(alpha: Float = 1f): Color = Transforms.okhsvToColor(h, s, v, alpha) 17 | } -------------------------------------------------------------------------------- /.idea/scopes/miuix.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /docs/demo/src/jsMain/resources/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Demo 11 | 24 | 25 | 26 | 27 |
28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/color/space/Hsv.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.color.space 5 | 6 | import androidx.compose.ui.graphics.Color 7 | import androidx.compose.ui.graphics.Color.Companion.hsv 8 | 9 | /** 10 | * HSV representation with normalized, user-friendly ranges. 11 | * - `h`: hue in degrees [0, 360) 12 | * - `s`: saturation in percent [0.0, 100.0] 13 | * - `v`: value/brightness in percent [0.0, 100.0] 14 | */ 15 | data class Hsv(val h: Double, val s: Double, val v: Double) { 16 | fun toColor(alpha: Float = 1f): Color { 17 | val hue = (((h % 360.0) + 360.0) % 360.0).toFloat() 18 | val sN = (s / 100.0).coerceIn(0.0, 1.0).toFloat() 19 | val vN = (v / 100.0).coerceIn(0.0, 1.0).toFloat() 20 | return hsv(hue, sN, vN, alpha) 21 | } 22 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | .idea/workspace.xml 4 | .idea/misc.xml 5 | .idea/markdown.xml 6 | .idea/modules.xml 7 | .idea/libraries 8 | .idea/caches 9 | .idea/navEditor.xml 10 | .idea/tasks.xml 11 | .idea/compiler.xml 12 | .idea/other.xml 13 | .idea/kotlinc.xml 14 | .idea/jarRepositories.xml 15 | .idea/deploymentTargetDropDown.xml 16 | .idea/deploymentTargetSelector.xml 17 | .idea/androidTestResultsUserPreferences.xml 18 | .idea/appInsightsSettings.xml 19 | .idea/artifacts 20 | .idea/AndroidProjectSystem.xml 21 | .idea/deviceManager.xml 22 | .idea/gradle.xml 23 | .idea/copilot* 24 | .fleet 25 | .vscode 26 | .DS_Store 27 | build 28 | captures 29 | .externalNativeBuild 30 | .cxx 31 | local.properties 32 | xcuserdata 33 | .kotlin 34 | *.lock 35 | convention-plugins/bin 36 | docs/node_modules 37 | docs/.vitepress/dist 38 | docs/.vitepress/cache 39 | docs/public/dokka 40 | docs/public/compose 41 | docs/public/icons 42 | -------------------------------------------------------------------------------- /miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/color/space/OkLch.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.color.space 5 | 6 | import androidx.compose.ui.graphics.Color 7 | import top.yukonga.miuix.kmp.color.core.Transforms 8 | 9 | /** 10 | * OkLCH representation with normalized, user-friendly ranges. 11 | * - `l`: 0.0..100.0 (lightness percent) 12 | * - `c`: 0.0..100.0 (chroma percent, mapped to internal [0.0, 0.4]) 13 | * - `h`: hue in degrees [0, 360) 14 | */ 15 | data class OkLch(val l: Double, val c: Double, val h: Double) { 16 | fun toColor(alpha: Float = 1f): Color { 17 | val lN = (l / 100.0).coerceIn(0.0, 1.0).toFloat() 18 | val cN = ((c / 100.0) * 0.4).toFloat().coerceIn(0f, 0.4f) 19 | val hDeg = (((h % 360.0) + 360.0) % 360.0).toFloat() 20 | return Transforms.oklchToColor(lN, cN, hDeg, alpha) 21 | } 22 | } -------------------------------------------------------------------------------- /.idea/runConfigurations/iosApp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 17 | -------------------------------------------------------------------------------- /miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/color/space/OkLab.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.color.space 5 | 6 | import androidx.compose.ui.graphics.Color 7 | import top.yukonga.miuix.kmp.color.core.Transforms 8 | 9 | /** 10 | * OkLAB representation with normalized, user-friendly ranges. 11 | * - `l`: 0.0..100.0 (lightness percent) 12 | * - `a`: -100.0..100.0 (green-red axis, mapped to internal [-0.4, 0.4]) 13 | * - `b`: -100.0..100.0 (blue-yellow axis, mapped to internal [-0.4, 0.4]) 14 | */ 15 | data class OkLab(val l: Double, val a: Double, val b: Double) { 16 | fun toColor(alpha: Float = 1f): Color { 17 | val lN = (l / 100.0).coerceIn(0.0, 1.0).toFloat() 18 | val aN = ((a / 100.0) * 0.4).toFloat().coerceIn(-0.4f, 0.4f) 19 | val bN = ((b / 100.0) * 0.4).toFloat().coerceIn(-0.4f, 0.4f) 20 | val ok = floatArrayOf(lN, aN, bN) 21 | return Transforms.okLabToColor(ok, alpha) 22 | } 23 | } -------------------------------------------------------------------------------- /miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/theme/ContentColor.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.theme 5 | 6 | import androidx.compose.runtime.compositionLocalOf 7 | import androidx.compose.ui.graphics.Color 8 | 9 | /** 10 | * CompositionLocal containing the preferred content color for a given position in the hierarchy. 11 | * This typically represents the `on` color for a color in [Colors]. For example, if the 12 | * background color is [Colors.surface], this color is typically set to 13 | * [Colors.onSurface]. 14 | * 15 | * This color should be used for any typography / iconography, to ensure that the color of these 16 | * adjusts when the background color changes. For example, on a dark background, text should be 17 | * light, and on a light background, text should be dark. 18 | * 19 | * Defaults to [Color.Black] if no color has been explicitly set. 20 | */ 21 | val LocalContentColor = compositionLocalOf { Color.Black } 22 | -------------------------------------------------------------------------------- /docs/zh_CN/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | # https://vitepress.dev/reference/default-theme-home-page 3 | layout: home 4 | 5 | hero: 6 | name: Miuix 7 | text: A UI library for Compose MultiPlatform 8 | tagline: 提供 Xiaomi HyperOS 设计风格的组件库 9 | image: 10 | src: /Icon.webp 11 | alt: Logo 12 | actions: 13 | - theme: brand 14 | text: 快速开始 15 | link: /zh_CN/guide/getting-started 16 | - theme: alt 17 | text: 组件库 18 | link: /zh_CN/components/index 19 | - theme: alt 20 | text: GitHub 21 | link: https://github.com/compose-miuix-ui/miuix 22 | 23 | features: 24 | - icon: 🚀 25 | title: 简单易用 26 | details: 类似 Compose Material 的使用方式,低学习成本,快速上手。 27 | - icon: 🎨 28 | title: 小米美学 29 | details: 精确还原小米 HyperOS 原生组件的设计与交互体验。 30 | - icon: 📦 31 | title: 组件丰富 32 | details: 提供全面的基础组件和部分复杂的交互组件,满足大多数应用开发需求。 33 | - icon: 🌐 34 | title: 全平台支持 35 | details: 基于 Compose Multiplatform,一套代码支持 Android、iOS、Desktop 等平台。 36 | 37 | nolebase: 38 | gitChangelog: false 39 | pageProperties: false 40 | --- 41 | -------------------------------------------------------------------------------- /.idea/runConfigurations/docs__js_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 18 | true 19 | true 20 | false 21 | false 22 | 23 | 24 | -------------------------------------------------------------------------------- /.idea/runConfigurations/example__js_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 18 | true 19 | true 20 | false 21 | false 22 | 23 | 24 | -------------------------------------------------------------------------------- /.idea/runConfigurations/example__macos_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 18 | true 19 | true 20 | false 21 | false 22 | 23 | 24 | -------------------------------------------------------------------------------- /.idea/runConfigurations/example__wasmJs_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 18 | true 19 | true 20 | false 21 | false 22 | 23 | 24 | -------------------------------------------------------------------------------- /.idea/runConfigurations/example__desktop_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 18 | false 19 | true 20 | false 21 | false 22 | 23 | 24 | -------------------------------------------------------------------------------- /docs/zh_CN/guide/best-practices.md: -------------------------------------------------------------------------------- 1 | # 最佳实践 2 | 3 | Miuix UI 库已被多个开源项目采用。以下是一些基于 Miuix 创建的示例应用,它们展示了如何在实际项目中应用 Miuix 组件和设计理念。 4 | 5 | ### 示例应用 6 | 本项目自身的示例应用展示了如何使用 Miuix 已包含的所有组件构建界面。您可以在示例应用中找到各种组件的用法和示例,帮助您更好地理解和使用 Miuix UI 库。 7 | 8 | ### Updater-KMP 9 | 10 | [Updater-KMP](https://github.com/YuKongA/Updater-KMP) 是一个使用 Kotlin 多平台技术开发的获取小米系统更新的工具。该项目展示了如何利用 Miuix UI 组件构建跨平台一致的用户界面。 11 | 12 | ### StatusBarLyric 13 | 14 | [StatusBarLyric](https://github.com/Block-Network/StatusBarLyric) 是一个在状态栏显示正在播放音乐歌词的 Xposed 应用。该项目展示了如何将 Miuix 组件与 Xposed 结合下的使用。 15 | 16 | ### XiaomiHelper 17 | 18 | [XiaomiHelper](https://github.com/HowieHChen/XiaomiHelper) 是一个适用于 Xiaomi 解锁设备的 Xposed 大杂烩模块,提供多种系统优化和功能增强选项。该项目展示了如何使用 Miuix 构建复杂的多功能应用。 19 | 20 | ### OShin 21 | 22 | [OShin](https://github.com/suqi8/OShin) 是一个适用于 OPPO 系解锁设备的 Xposed 大杂烩模块。该项目充分展示了如何以 Miuix 为基础创建更多样的组件。 23 | 24 | ### HyperStar 25 | 26 | [HyperStar](https://github.com/YunZiA/HyperStar) 是一个适用于 Xiaomi 解锁设备的 Xposed 系统界面自定义模块。该项目展示了 Miuix 在复杂界面布局中的应用。 27 | 28 | ### Payload-Dumper-Compose 29 | 30 | [Payload-Dumper-Compose](https://github.com/rcmiku/Payload-Dumper-Compose) 是一个基于 Jetpack Compose 的安卓系统镜像提取工具。 31 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Gradle 2 | org.gradle.jvmargs=-Xmx8g -Dfile.encoding=UTF-8 3 | org.gradle.parallel=true 4 | org.gradle.caching=true 5 | org.gradle.configureondemand=true 6 | # Android 7 | android.useAndroidX=true 8 | android.nonTransitiveRClass=true 9 | android.r8.maxWorkers=4 10 | android.r8.optimizedResourceShrinking=true 11 | # Dokka 12 | org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled 13 | org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn=true 14 | # Kotlin 15 | kotlin.code.style=official 16 | # MPP 17 | kotlin.mpp.androidSourceSetLayoutVersion=2 18 | kotlin.mpp.enableCInteropCommonization=true 19 | # Native 20 | kotlin.native.binary.smallBinary=true 21 | kotlin.native.ignoreDisabledTargets=true 22 | # Incremental compilation 23 | kotlin.incremental=true 24 | kotlin.incremental.multiplatform=true 25 | kotlin.incremental.jvm.fir=true 26 | kotlin.incremental.js=true 27 | kotlin.incremental.js.klib=true 28 | kotlin.incremental.native=true 29 | kotlin.incremental.wasm=true 30 | # Experimental target 31 | org.jetbrains.compose.experimental.macos.enabled=true 32 | # Xcode 33 | kotlin.apple.xcodeCompatibility.nowarn=true 34 | xcodeproj=./iosApp 35 | -------------------------------------------------------------------------------- /miuix/src/androidMain/kotlin/top/yukonga/miuix/kmp/theme/DynamicColors.android.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.theme 5 | 6 | import android.os.Build 7 | import androidx.compose.material3.ColorScheme 8 | import androidx.compose.material3.dynamicDarkColorScheme 9 | import androidx.compose.material3.dynamicLightColorScheme 10 | import androidx.compose.runtime.Composable 11 | import androidx.compose.ui.platform.LocalContext 12 | 13 | @Composable 14 | actual fun platformDynamicColors(dark: Boolean): Colors { 15 | 16 | val context = LocalContext.current 17 | val cs: ColorScheme = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { 18 | if (dark) { 19 | androidx.compose.material3.darkColorScheme() 20 | } else { 21 | androidx.compose.material3.lightColorScheme() 22 | } 23 | } else { 24 | if (dark) { 25 | dynamicDarkColorScheme(context) 26 | } else { 27 | dynamicLightColorScheme(context) 28 | } 29 | } 30 | return mapMd3ToMiuixColorsCommon(cs, dark) 31 | } 32 | -------------------------------------------------------------------------------- /miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/anim/DecelerateEasing.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.anim 5 | 6 | import androidx.compose.animation.core.Easing 7 | import androidx.compose.runtime.Immutable 8 | import kotlin.math.pow 9 | 10 | /** 11 | * This is equivalent to the Android [DecelerateInterpolator](https://cs.android.com/search?q=file:androidx/core/animation/DecelerateInterpolator.java+class:androidx.core.animation.DecelerateInterpolator) 12 | * 13 | * @param factor Degree to which the animation should be eased. Setting factor to 1.0f produces 14 | * an upside-down y=x^2 parabola. Increasing factor above 1.0f makes exaggerates the 15 | * ease-out effect (i.e., it starts even faster and ends evens slower) 16 | */ 17 | @Immutable 18 | class DecelerateEasing( 19 | private val factor: Float = 1.0f, 20 | ) : Easing { 21 | override fun transform(fraction: Float): Float = 22 | if (factor == 1.0f) { 23 | 1.0f - (1.0f - fraction) * (1.0f - fraction) 24 | } else { 25 | 1.0f - (1.0f - fraction).pow(2 * fraction) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/anim/AccelerateEasing.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.anim 5 | 6 | import androidx.compose.animation.core.Easing 7 | import androidx.compose.runtime.Immutable 8 | import kotlin.math.pow 9 | 10 | /** 11 | * This is equivalent to the Android [AccelerateInterpolator](https://cs.android.com/search?q=file:androidx/core/animation/AccelerateInterpolator.java+class:androidx.core.animation.AccelerateInterpolator) 12 | * 13 | * @param factor Degree to which the animation should be eased. Setting 14 | * factor to 1.0f produces a y=x^2 parabola. Increasing factor above 15 | * 1.0f exaggerates the ease-in effect (i.e., it starts even 16 | * slower and ends evens faster) 17 | */ 18 | @Immutable 19 | class AccelerateEasing( 20 | private val factor: Float = 1.0f, 21 | ) : Easing { 22 | private val doubleFactor: Float = 2 * factor 23 | 24 | override fun transform(fraction: Float): Float = 25 | if (factor == 1.0f) { 26 | fraction * fraction 27 | } else { 28 | fraction.pow(doubleFactor) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /example/src/androidMain/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 13 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /example/src/macosMain/kotlin/Main.macos.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import androidx.compose.ui.unit.DpSize 5 | import androidx.compose.ui.unit.dp 6 | import androidx.compose.ui.window.Window 7 | import kotlinx.cinterop.ExperimentalForeignApi 8 | import platform.AppKit.NSApplication 9 | import platform.AppKit.NSApplicationActivationPolicy 10 | import platform.AppKit.NSApplicationDelegateProtocol 11 | import platform.CoreGraphics.CGSizeMake 12 | import platform.darwin.NSObject 13 | 14 | @OptIn(ExperimentalForeignApi::class) 15 | fun main() { 16 | val nsApplication = NSApplication.sharedApplication() 17 | nsApplication.setActivationPolicy(NSApplicationActivationPolicy.NSApplicationActivationPolicyRegular) 18 | nsApplication.delegate = 19 | object : NSObject(), NSApplicationDelegateProtocol { 20 | override fun applicationShouldTerminateAfterLastWindowClosed(sender: NSApplication): Boolean = true 21 | } 22 | Window( 23 | title = "Miuix", 24 | size = DpSize(420.dp, 840.dp), 25 | ) { 26 | window.minSize = CGSizeMake(300.0, 600.0) 27 | App() 28 | } 29 | nsApplication.run() 30 | } 31 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | # https://vitepress.dev/reference/default-theme-home-page 3 | layout: home 4 | 5 | hero: 6 | name: Miuix 7 | text: A UI library for Compose MultiPlatform 8 | tagline: Provides components with Xiaomi HyperOS design style 9 | image: 10 | src: /Icon.webp 11 | alt: Logo 12 | actions: 13 | - theme: brand 14 | text: Getting Started 15 | link: /guide/getting-started 16 | - theme: alt 17 | text: Components 18 | link: /components/index 19 | - theme: alt 20 | text: GitHub 21 | link: https://github.com/compose-miuix-ui/miuix 22 | 23 | features: 24 | - icon: 🚀 25 | title: Easy to Use 26 | details: Similar to Compose Material with low learning curve for quick integration. 27 | - icon: 🎨 28 | title: Xiaomi Aesthetics 29 | details: Authentic Xiaomi HyperOS design language and interactive effects for your apps. 30 | - icon: 📦 31 | title: Rich Components 32 | details: Comprehensive basic and complex components for most application scenarios. 33 | - icon: 🌐 34 | title: Cross-Platform 35 | details: One codebase for Android, iOS, Desktop and more via Compose Multiplatform. 36 | 37 | nolebase: 38 | gitChangelog: false 39 | pageProperties: false 40 | --- 41 | -------------------------------------------------------------------------------- /example/src/commonMain/kotlin/App.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import androidx.compose.foundation.layout.PaddingValues 5 | import androidx.compose.runtime.Composable 6 | import androidx.compose.runtime.MutableState 7 | import androidx.compose.runtime.mutableStateOf 8 | import androidx.compose.runtime.remember 9 | import androidx.compose.ui.unit.dp 10 | import top.yukonga.miuix.kmp.utils.Platform 11 | import top.yukonga.miuix.kmp.utils.platform 12 | import ui.AppTheme 13 | import ui.keyColorFor 14 | 15 | @Composable 16 | fun App( 17 | colorMode: MutableState = remember { mutableStateOf(0) }, 18 | seedIndex: MutableState = remember { mutableStateOf(0) }, 19 | padding: PaddingValues = PaddingValues(0.dp), 20 | enableOverScroll: Boolean = platform() == Platform.Android || platform() == Platform.IOS 21 | ) { 22 | val keyColor = keyColorFor(seedIndex.value) 23 | AppTheme(colorMode = colorMode.value, keyColor = keyColor) { 24 | UITest( 25 | colorMode = colorMode, 26 | seedIndex = seedIndex, 27 | padding = padding, 28 | enableOverScroll = enableOverScroll 29 | ) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /example/src/desktopMain/kotlin/Main.desktop.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import androidx.compose.ui.Alignment 5 | import androidx.compose.ui.unit.DpSize 6 | import androidx.compose.ui.unit.dp 7 | import androidx.compose.ui.window.Window 8 | import androidx.compose.ui.window.WindowPosition 9 | import androidx.compose.ui.window.application 10 | import androidx.compose.ui.window.rememberWindowState 11 | import org.jetbrains.compose.resources.painterResource 12 | import top.yukonga.miuix.kmp.example.generated.resources.Res 13 | import top.yukonga.miuix.kmp.example.generated.resources.icon 14 | import java.awt.Dimension 15 | 16 | fun main() = application { 17 | val state = rememberWindowState( 18 | size = DpSize(420.dp, 840.dp), 19 | position = WindowPosition.Aligned(Alignment.Center), 20 | ) 21 | val isHotReloadMode = System.getProperty("app.mode") == "hot" 22 | Window( 23 | state = state, 24 | onCloseRequest = ::exitApplication, 25 | alwaysOnTop = isHotReloadMode, 26 | title = "Miuix", 27 | icon = painterResource(Res.drawable.icon), 28 | ) { 29 | window.minimumSize = Dimension(300, 600) 30 | App() 31 | } 32 | } -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | @file:Suppress("UnstableApiUsage") 5 | 6 | pluginManagement { 7 | includeBuild("convention-plugins") 8 | repositories { 9 | google { 10 | mavenContent { 11 | includeGroupAndSubgroups("androidx") 12 | includeGroupAndSubgroups("com.android") 13 | includeGroupAndSubgroups("com.google") 14 | } 15 | } 16 | mavenCentral() 17 | gradlePluginPortal() 18 | } 19 | } 20 | 21 | dependencyResolutionManagement { 22 | repositories { 23 | google { 24 | mavenContent { 25 | includeGroupAndSubgroups("androidx") 26 | includeGroupAndSubgroups("com.android") 27 | includeGroupAndSubgroups("com.google") 28 | } 29 | } 30 | mavenCentral() 31 | } 32 | } 33 | 34 | plugins { 35 | id("com.android.settings") version ("8.13.1") 36 | id("org.gradle.toolchains.foojay-resolver-convention") version ("1.0.0") 37 | } 38 | 39 | android { 40 | compileSdk = 36 41 | targetSdk = 36 42 | minSdk = 24 43 | buildToolsVersion = "36.1.0" 44 | } 45 | 46 | rootProject.name = "miuix" 47 | 48 | include(":miuix") 49 | include(":example") 50 | include(":docs:demo") 51 | include(":docs:iconGen") 52 | -------------------------------------------------------------------------------- /docs/iconGen/build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | plugins { kotlin("jvm") } 5 | 6 | java { 7 | toolchain.languageVersion = JavaLanguageVersion.of(21) 8 | } 9 | 10 | dependencies { 11 | implementation(project(":miuix")) 12 | } 13 | 14 | val iconsSourceDir = rootProject.layout.projectDirectory.dir("miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/icon").asFile 15 | val outputDir = layout.buildDirectory.dir("generated-svg") 16 | 17 | tasks.register("generateSvg") { 18 | group = "iconGen" 19 | description = "Generate SVGs from Compose ImageVector definitions" 20 | dependsOn(tasks.named("classes")) 21 | classpath = sourceSets.main.get().runtimeClasspath 22 | mainClass.set("top.yukonga.miuix.docs.icongen.MainKt") 23 | val lightColor = project.findProperty("iconLightColor")?.toString() ?: "#000000" 24 | val darkColor = project.findProperty("iconDarkColor")?.toString() ?: "#FFFFFF" 25 | val preserve = project.findProperty("iconPreserveColors")?.toString()?.equals("true", true) == true 26 | outputs.dir(outputDir) 27 | doFirst { outputDir.get().asFile.mkdirs() } 28 | args = listOf( 29 | "--src", iconsSourceDir.absolutePath, 30 | "--out", outputDir.get().asFile.absolutePath, 31 | "--light", lightColor, 32 | "--dark", darkColor, 33 | "--preserve-colors", preserve.toString() 34 | ) 35 | } 36 | -------------------------------------------------------------------------------- /example/src/jsMain/kotlin/WebPlatformApi.js.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import kotlinx.browser.document 5 | 6 | internal actual fun platformHideLoading() { 7 | val loading = document.getElementById("loading") 8 | val composeApp = document.getElementById("composeApp") 9 | loading?.asDynamic().style?.display = "none" 10 | composeApp?.asDynamic().style?.display = "block" 11 | } 12 | 13 | internal actual fun platformGetCssVar(name: String): Double { 14 | val docEl = document.documentElement ?: return 0.0 15 | val style = docEl.ownerDocument?.defaultView?.getComputedStyle(docEl) ?: return 0.0 16 | val raw = style.getPropertyValue(name) 17 | val trimmed = raw.trim() 18 | val direct = trimmed.toDoubleOrNull() 19 | if (direct != null) return direct 20 | val px = trimmed.removeSuffix("px").toDoubleOrNull() 21 | return px ?: 0.0 22 | } 23 | 24 | internal actual fun platformIsTouchEnabled(): Boolean { 25 | val hasCoarse = js("window.matchMedia && window.matchMedia('(pointer: coarse)').matches") as? Boolean ?: false 26 | if (hasCoarse) return true 27 | 28 | val hasOnTouchStart = js("'ontouchstart' in window") as? Boolean ?: false 29 | if (hasOnTouchStart) return true 30 | 31 | val maxTouchPoints = (js("navigator.maxTouchPoints") as? Int ?: 0) + 32 | (js("navigator.msMaxTouchPoints") as? Int ?: 0) 33 | return maxTouchPoints > 0 34 | } 35 | -------------------------------------------------------------------------------- /miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/color/api/Extensions.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.color.api 5 | 6 | import androidx.compose.ui.graphics.Color 7 | import top.yukonga.miuix.kmp.color.core.Transforms 8 | import top.yukonga.miuix.kmp.color.space.Hsv 9 | import top.yukonga.miuix.kmp.color.space.OkLab 10 | import top.yukonga.miuix.kmp.color.space.OkLch 11 | 12 | /** Convert Compose Color to user-friendly OkLab. */ 13 | fun Color.toOkLab(): OkLab { 14 | val lab = Transforms.colorToOkLab(this) 15 | val l = (lab[0] * 100.0).coerceIn(0.0, 100.0) 16 | val a = (lab[1] / 0.4 * 100.0).coerceIn(-100.0, 100.0) 17 | val b = (lab[2] / 0.4 * 100.0).coerceIn(-100.0, 100.0) 18 | return OkLab(l, a, b) 19 | } 20 | 21 | /** Convert Compose Color to Hvs. */ 22 | fun Color.toHsv(): Hsv { 23 | val hsvArr = Transforms.colorToHsv(this) 24 | val h = hsvArr[0].toDouble() 25 | val s = (hsvArr[1] * 100.0).coerceIn(0.0, 100.0) 26 | val v = (hsvArr[2] * 100.0).coerceIn(0.0, 100.0) 27 | return Hsv(h, s, v) 28 | } 29 | 30 | /** Convert Compose Color to user-friendly OkLch. */ 31 | fun Color.toOkLch(): OkLch { 32 | val lch = Transforms.colorToOklch(this) 33 | val l = (lch[0] * 100.0).coerceIn(0.0, 100.0) 34 | val c = (lch[1] / 0.4 * 100.0).coerceIn(0.0, 100.0) 35 | val h = lch[2].toDouble() // degrees already normalized 36 | return OkLch(l, c, h) 37 | } -------------------------------------------------------------------------------- /miuix/src/jsMain/kotlin/top/yukonga/miuix/kmp/utils/Utils.js.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.utils 5 | 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.runtime.derivedStateOf 8 | import androidx.compose.runtime.getValue 9 | import androidx.compose.runtime.remember 10 | import androidx.compose.ui.ExperimentalComposeUiApi 11 | import androidx.compose.ui.graphics.Color 12 | import androidx.compose.ui.platform.LocalWindowInfo 13 | import androidx.compose.ui.unit.Dp 14 | import androidx.compose.ui.unit.dp 15 | import androidx.compose.ui.window.DialogProperties 16 | 17 | @Composable 18 | @OptIn(ExperimentalComposeUiApi::class) 19 | actual fun getWindowSize(): WindowSize { 20 | val window = LocalWindowInfo.current 21 | val windowSize by remember(window) { 22 | derivedStateOf { 23 | WindowSize( 24 | width = window.containerSize.width, 25 | height = window.containerSize.height 26 | ) 27 | } 28 | } 29 | return windowSize 30 | } 31 | 32 | actual fun platform(): Platform = Platform.Js 33 | 34 | @Composable 35 | actual fun getRoundedCorner(): Dp = 0.dp 36 | 37 | @OptIn(ExperimentalComposeUiApi::class) 38 | @Composable 39 | actual fun platformDialogProperties(): DialogProperties = DialogProperties( 40 | usePlatformDefaultWidth = false, 41 | scrimColor = Color.Transparent 42 | ) 43 | 44 | @Composable 45 | actual fun removePlatformDialogDefaultEffects() {} 46 | -------------------------------------------------------------------------------- /miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/utils/Utils.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.utils 5 | 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.runtime.Immutable 8 | import androidx.compose.ui.unit.Dp 9 | import androidx.compose.ui.window.DialogProperties 10 | 11 | /** 12 | * Window size data class. 13 | */ 14 | @Immutable 15 | data class WindowSize(val width: Int, val height: Int) 16 | 17 | /** 18 | * Returns the current window size. 19 | */ 20 | @Composable 21 | expect fun getWindowSize(): WindowSize 22 | 23 | /** 24 | * Platform enum class. 25 | */ 26 | enum class Platform { 27 | Android, 28 | IOS, 29 | Desktop, 30 | WasmJs, 31 | MacOS, 32 | Js 33 | } 34 | 35 | /** 36 | * Returns the current platform name. 37 | */ 38 | expect fun platform(): Platform 39 | 40 | /** 41 | * Returns the rounded corner of the current device. 42 | */ 43 | @Composable 44 | expect fun getRoundedCorner(): Dp 45 | 46 | /** 47 | * Returns platform-specific DialogProperties. 48 | */ 49 | @Composable 50 | expect fun platformDialogProperties(): DialogProperties 51 | 52 | /** 53 | * Removes platform default dialog effects such as window animations and dimming. 54 | * 55 | * Platform implementations should clear default window animations and dim amount to ensure 56 | * Miuix custom dialog animations and dim layer behave consistently across devices. 57 | */ 58 | @Composable 59 | expect fun removePlatformDialogDefaultEffects() 60 | -------------------------------------------------------------------------------- /miuix/src/macosMain/kotlin/top/yukonga/miuix/kmp/utils/Utils.macos.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.utils 5 | 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.runtime.derivedStateOf 8 | import androidx.compose.runtime.getValue 9 | import androidx.compose.runtime.remember 10 | import androidx.compose.ui.ExperimentalComposeUiApi 11 | import androidx.compose.ui.graphics.Color 12 | import androidx.compose.ui.platform.LocalWindowInfo 13 | import androidx.compose.ui.unit.Dp 14 | import androidx.compose.ui.unit.dp 15 | import androidx.compose.ui.window.DialogProperties 16 | 17 | @Composable 18 | @OptIn(ExperimentalComposeUiApi::class) 19 | actual fun getWindowSize(): WindowSize { 20 | val window = LocalWindowInfo.current 21 | val windowSize by remember(window) { 22 | derivedStateOf { 23 | WindowSize( 24 | width = window.containerSize.width, 25 | height = window.containerSize.height 26 | ) 27 | } 28 | } 29 | return windowSize 30 | } 31 | 32 | actual fun platform(): Platform = Platform.MacOS 33 | 34 | @Composable 35 | actual fun getRoundedCorner(): Dp = 0.dp 36 | 37 | @OptIn(ExperimentalComposeUiApi::class) 38 | @Composable 39 | actual fun platformDialogProperties(): DialogProperties = DialogProperties( 40 | usePlatformDefaultWidth = false, 41 | scrimColor = Color.Transparent 42 | ) 43 | 44 | @Composable 45 | actual fun removePlatformDialogDefaultEffects() {} 46 | -------------------------------------------------------------------------------- /miuix/src/wasmJsMain/kotlin/top/yukonga/miuix/kmp/utils/Utils.wasmJs.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.utils 5 | 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.runtime.derivedStateOf 8 | import androidx.compose.runtime.getValue 9 | import androidx.compose.runtime.remember 10 | import androidx.compose.ui.ExperimentalComposeUiApi 11 | import androidx.compose.ui.graphics.Color 12 | import androidx.compose.ui.platform.LocalWindowInfo 13 | import androidx.compose.ui.unit.Dp 14 | import androidx.compose.ui.unit.dp 15 | import androidx.compose.ui.window.DialogProperties 16 | 17 | @Composable 18 | @OptIn(ExperimentalComposeUiApi::class) 19 | actual fun getWindowSize(): WindowSize { 20 | val window = LocalWindowInfo.current 21 | val windowSize by remember(window) { 22 | derivedStateOf { 23 | WindowSize( 24 | width = window.containerSize.width, 25 | height = window.containerSize.height 26 | ) 27 | } 28 | } 29 | return windowSize 30 | } 31 | 32 | actual fun platform(): Platform = Platform.WasmJs 33 | 34 | @Composable 35 | actual fun getRoundedCorner(): Dp = 0.dp 36 | 37 | @OptIn(ExperimentalComposeUiApi::class) 38 | @Composable 39 | actual fun platformDialogProperties(): DialogProperties = DialogProperties( 40 | usePlatformDefaultWidth = false, 41 | scrimColor = Color.Transparent 42 | ) 43 | 44 | @Composable 45 | actual fun removePlatformDialogDefaultEffects() {} 46 | -------------------------------------------------------------------------------- /miuix/src/desktopMain/kotlin/top/yukonga/miuix/kmp/utils/Utils.desktop.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.utils 5 | 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.runtime.derivedStateOf 8 | import androidx.compose.runtime.getValue 9 | import androidx.compose.runtime.remember 10 | import androidx.compose.ui.ExperimentalComposeUiApi 11 | import androidx.compose.ui.graphics.Color 12 | import androidx.compose.ui.platform.LocalWindowInfo 13 | import androidx.compose.ui.unit.Dp 14 | import androidx.compose.ui.unit.dp 15 | import androidx.compose.ui.window.DialogProperties 16 | 17 | @Composable 18 | @OptIn(ExperimentalComposeUiApi::class) 19 | actual fun getWindowSize(): WindowSize { 20 | val window = LocalWindowInfo.current 21 | val windowSize by remember(window) { 22 | derivedStateOf { 23 | WindowSize( 24 | width = window.containerSize.width, 25 | height = window.containerSize.height 26 | ) 27 | } 28 | } 29 | return windowSize 30 | } 31 | 32 | actual fun platform(): Platform = Platform.Desktop 33 | 34 | @Composable 35 | actual fun getRoundedCorner(): Dp = 0.dp 36 | 37 | @OptIn(ExperimentalComposeUiApi::class) 38 | @Composable 39 | actual fun platformDialogProperties(): DialogProperties = DialogProperties( 40 | usePlatformDefaultWidth = false, 41 | scrimColor = Color.Transparent 42 | ) 43 | 44 | @Composable 45 | actual fun removePlatformDialogDefaultEffects() {} 46 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Build All Tests 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | paths-ignore: 7 | - 'README.md' 8 | - 'docs/**' 9 | - 'iosApp/iosApp/Info.plist' 10 | pull_request: 11 | branches: [ "main" ] 12 | paths-ignore: 13 | - 'README.md' 14 | - 'docs/**' 15 | - 'iosApp/iosApp/Info.plist' 16 | 17 | permissions: 18 | contents: read 19 | 20 | jobs: 21 | build: 22 | strategy: 23 | matrix: 24 | include: 25 | - target: ':miuix:iosSimulatorArm64Test' 26 | os: macos-latest 27 | - target: ':miuix:desktopTest' 28 | os: ubuntu-latest 29 | - target: ':miuix:assembleAndroidTest' 30 | os: ubuntu-latest 31 | - target: ':miuix:wasmJsBrowserTest' 32 | os: ubuntu-latest 33 | runs-on: ${{ matrix.os }} 34 | steps: 35 | - name: Checkout Sources 36 | uses: actions/checkout@v6 37 | - name: Set up JDK 38 | uses: actions/setup-java@v5 39 | with: 40 | distribution: 'zulu' 41 | java-version: '21' 42 | - name: Build with Gradle 43 | uses: gradle/actions/setup-gradle@v5 44 | - name: Run check 45 | if: matrix.os == 'macos-latest' 46 | run: ./gradlew check 47 | - name: Run test 48 | run: ./gradlew ${{ matrix.target }} 49 | - name: Upload reports 50 | if: failure() 51 | uses: actions/upload-artifact@v6 52 | with: 53 | name: reports 54 | path: | 55 | **/build/reports/** 56 | -------------------------------------------------------------------------------- /miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/SmallTitle.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.basic 5 | 6 | import androidx.compose.foundation.layout.PaddingValues 7 | import androidx.compose.foundation.layout.padding 8 | import androidx.compose.runtime.Composable 9 | import androidx.compose.runtime.NonRestartableComposable 10 | import androidx.compose.runtime.derivedStateOf 11 | import androidx.compose.runtime.getValue 12 | import androidx.compose.runtime.remember 13 | import androidx.compose.ui.Modifier 14 | import androidx.compose.ui.graphics.Color 15 | import androidx.compose.ui.unit.dp 16 | import top.yukonga.miuix.kmp.theme.MiuixTheme 17 | 18 | /** 19 | * A [SmallTitle] with Miuix style. 20 | * 21 | * @param text The text to be displayed in the [SmallTitle]. 22 | * @param modifier The modifier to be applied to the [SmallTitle]. 23 | * @param textColor The color of the [SmallTitle]. 24 | * @param insideMargin The margin inside the [SmallTitle]. 25 | */ 26 | @Composable 27 | @NonRestartableComposable 28 | fun SmallTitle( 29 | text: String, 30 | modifier: Modifier = Modifier, 31 | textColor: Color = MiuixTheme.colorScheme.onBackgroundVariant, 32 | insideMargin: PaddingValues = PaddingValues(28.dp, 8.dp) 33 | ) { 34 | val resolvedColor by remember(textColor) { derivedStateOf { textColor } } 35 | Text( 36 | modifier = modifier.padding(insideMargin), 37 | text = text, 38 | style = MiuixTheme.textStyles.subtitle, 39 | color = resolvedColor 40 | ) 41 | } 42 | -------------------------------------------------------------------------------- /example/src/webMain/resources/styles.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /* For KernelSU WebUI */ 18 | @import url('https://mui.kernelsu.org/internal/insets.css'); 19 | 20 | html, body { 21 | width: 100%; 22 | height: 100%; 23 | margin: 0; 24 | padding: 0; 25 | overflow: hidden; 26 | } 27 | 28 | #loading { 29 | position: absolute; 30 | top: 50%; 31 | left: 50%; 32 | transform: translate(-50%, -50%); 33 | font-size: 24px; 34 | font-weight: bold; 35 | } 36 | 37 | #loading::after { 38 | content: '.'; 39 | animation: ellipsis 1.5s infinite; 40 | } 41 | 42 | @keyframes ellipsis { 43 | 0% { 44 | content: '.'; 45 | } 46 | 47 | 33% { 48 | content: '..'; 49 | } 50 | 51 | 66% { 52 | content: '...'; 53 | } 54 | 55 | 100% { 56 | content: '.'; 57 | } 58 | } 59 | 60 | #composeApp { 61 | width: 100%; 62 | height: 100%; 63 | margin: 0; 64 | padding: 0; 65 | overflow: hidden; 66 | } -------------------------------------------------------------------------------- /docs/zh_CN/guide/textstyles.md: -------------------------------------------------------------------------------- 1 | # 文本样式 2 | 3 | 本页基于实际实现,完整列出 Miuix 提供的文本样式。 4 | 5 | ## 使用方式 6 | 7 | - 在组合函数中通过 `MiuixTheme.textStyles.<名称>` 访问。 8 | - 所有样式的颜色会在运行时由 `MiuixTheme.colorScheme.onBackground` 统一设置。 9 | 10 | ## 样式列表 11 | 12 | | 样式名 | 字号 | 字重 | 行高 | 13 | |--------------|-------|--------|--------| 14 | | `main` | 17sp | Normal | - | 15 | | `paragraph` | 17sp | Normal | 1.2em | 16 | | `body1` | 16sp | Normal | - | 17 | | `body2` | 14sp | Normal | - | 18 | | `button` | 17sp | Normal | - | 19 | | `footnote1` | 13sp | Normal | - | 20 | | `footnote2` | 11sp | Normal | - | 21 | | `headline1` | 17sp | Normal | - | 22 | | `headline2` | 16sp | Normal | - | 23 | | `subtitle` | 14sp | Bold | - | 24 | | `title1` | 32sp | Normal | - | 25 | | `title2` | 24sp | Normal | - | 26 | | `title3` | 20sp | Normal | - | 27 | | `title4` | 18sp | Normal | - | 28 | 29 | ## 使用示例 30 | 31 | ```kotlin 32 | Text( 33 | text = "标题", 34 | style = MiuixTheme.textStyles.title2 35 | ) 36 | 37 | Text( 38 | text = "正文", 39 | style = MiuixTheme.textStyles.body1 40 | ) 41 | ``` 42 | 43 | ## 自定义 44 | 45 | - 通过 `defaultTextStyles(...)` 覆盖样式,并传入 `MiuixTheme(textStyles = ...)`。 46 | 47 | ```kotlin 48 | val customTextStyles = defaultTextStyles( 49 | title1 = TextStyle( 50 | fontSize = 36.sp, 51 | fontWeight = FontWeight.Bold 52 | ) 53 | ) 54 | 55 | val controller = remember { ThemeController(ColorSchemeMode.System) } 56 | MiuixTheme( 57 | controller = controller, 58 | textStyles = customTextStyles 59 | ) { /* 内容 */ } 60 | ``` 61 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/style/blur.css: -------------------------------------------------------------------------------- 1 | :root { 2 | 3 | /* 亮色模式主要模糊颜色 */ 4 | --miuix-color-blur: rgba(255, 255, 255, 0.7); 5 | 6 | /* 亮色模式文档页左侧顶部颜色 */ 7 | --miuix-color-blur-top: rgba(246, 246, 247, 0.7); 8 | 9 | /* 亮色模式搜索框颜色 */ 10 | --miuix-color-search-button: rgba(255, 255, 255, 0.5); 11 | 12 | 13 | /* 搜索框模糊 */ 14 | .DocSearch-Button { 15 | background-color: var(--miuix-color-search-button); 16 | } 17 | 18 | /* 文档页侧边栏顶部模糊 */ 19 | .curtain { 20 | background-color: var(--miuix-color-blur-top); 21 | backdrop-filter: blur(12px); 22 | } 23 | 24 | /* 移动端大纲栏模糊 */ 25 | .VPLocalNav { 26 | background-color: var(--miuix-color-blur); 27 | backdrop-filter: blur(12px); 28 | -webkit-backdrop-filter: blur(12px); 29 | } 30 | 31 | @media (min-width: 960px) { 32 | 33 | /* 首页下滑后导航栏模糊 */ 34 | .VPNavBar:not(.has-sidebar):not(.home .top) { 35 | background-color: var(--miuix-color-blur); 36 | backdrop-filter: blur(12px); 37 | -webkit-backdrop-filter: blur(12px); 38 | } 39 | 40 | /* 文档页导航中间模糊 */ 41 | .VPNavBar:not(.home.top):not(.home) .content-body { 42 | background-color: var(--miuix-color-blur); 43 | backdrop-filter: blur(12px); 44 | -webkit-backdrop-filter: blur(12px); 45 | } 46 | } 47 | 48 | } 49 | 50 | 51 | .dark { 52 | /* 深色模式主要模糊颜色 */ 53 | --miuix-color-blur: rgba(30, 30, 30, 0.7); 54 | 55 | /* 亮色模式文档页左侧顶部颜色 */ 56 | --miuix-color-blur-top: rgba(22, 22, 24, 0.7); 57 | 58 | /* 深色模式搜索框颜色 */ 59 | --miuix-color-search-button: rgba(30, 30, 30, 0.5); 60 | } -------------------------------------------------------------------------------- /example/src/commonMain/kotlin/ui/Theme.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package ui 5 | 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.runtime.remember 8 | import androidx.compose.ui.graphics.Color 9 | import top.yukonga.miuix.kmp.theme.ColorSchemeMode 10 | import top.yukonga.miuix.kmp.theme.MiuixTheme 11 | import top.yukonga.miuix.kmp.theme.ThemeController 12 | 13 | @Composable 14 | fun AppTheme( 15 | colorMode: Int = 0, 16 | keyColor: Color? = null, 17 | content: @Composable () -> Unit 18 | ) { 19 | val controller = remember(colorMode, keyColor) { 20 | when (colorMode) { 21 | 1 -> ThemeController(ColorSchemeMode.Light) 22 | 2 -> ThemeController(ColorSchemeMode.Dark) 23 | 3 -> ThemeController(ColorSchemeMode.MonetSystem, keyColor = keyColor) 24 | 4 -> ThemeController(ColorSchemeMode.MonetLight, keyColor = keyColor) 25 | 5 -> ThemeController(ColorSchemeMode.MonetDark, keyColor = keyColor) 26 | else -> ThemeController(ColorSchemeMode.System) 27 | } 28 | } 29 | return MiuixTheme( 30 | controller = controller, 31 | content = content 32 | ) 33 | } 34 | 35 | val KeyColors: List> = listOf( 36 | "Blue" to Color(0xFF3482FF), 37 | "Green" to Color(0xFF36D167), 38 | "Purple" to Color(0xFF7C4DFF), 39 | "Yellow" to Color(0xFFFFB21D), 40 | "Orange" to Color(0xFFFF5722), 41 | "Pink" to Color(0xFFE91E63), 42 | "Teal" to Color(0xFF00BCD4) 43 | ) 44 | 45 | fun keyColorFor(index: Int): Color? = if (index <= 0) null else KeyColors.getOrNull(index - 1)?.second -------------------------------------------------------------------------------- /miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/icon/icons/basic/ArrowRight.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.icon.icons.basic 5 | 6 | import androidx.compose.ui.graphics.Color 7 | import androidx.compose.ui.graphics.PathFillType 8 | import androidx.compose.ui.graphics.SolidColor 9 | import androidx.compose.ui.graphics.vector.ImageVector 10 | import androidx.compose.ui.graphics.vector.path 11 | import androidx.compose.ui.unit.dp 12 | import top.yukonga.miuix.kmp.icon.MiuixIcons 13 | 14 | val MiuixIcons.Basic.ArrowRight: ImageVector 15 | get() { 16 | if (_arrowRight != null) return _arrowRight!! 17 | _arrowRight = ImageVector.Builder("ArrowRight", 10.dp, 16.dp, 10f, 16f).apply { 18 | path( 19 | fill = SolidColor(Color.Black), 20 | fillAlpha = 1f, 21 | pathFillType = PathFillType.EvenOdd 22 | ) { 23 | moveTo(1.65f, 1.469f) 24 | curveTo(1.929f, 1.19f, 2.381f, 1.19f, 2.66f, 1.469f) 25 | lineTo(8.721f, 7.53f) 26 | curveTo(9.0f, 7.809f, 9.0f, 8.261f, 8.721f, 8.54f) 27 | lineTo(2.66f, 14.601f) 28 | curveTo(2.381f, 14.88f, 1.929f, 14.88f, 1.65f, 14.601f) 29 | curveTo(1.371f, 14.322f, 1.371f, 13.87f, 1.65f, 13.591f) 30 | lineTo(7.205f, 8.035f) 31 | lineTo(1.65f, 2.479f) 32 | curveTo(1.371f, 2.2f, 1.371f, 1.748f, 1.65f, 1.469f) 33 | close() 34 | } 35 | }.build() 36 | return _arrowRight!! 37 | } 38 | 39 | private var _arrowRight: ImageVector? = null -------------------------------------------------------------------------------- /iosApp/iosApp/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0.7 19 | CFBundleVersion 20 | 692 21 | LSRequiresIPhoneOS 22 | 23 | CADisableMinimumFrameDurationOnPhone 24 | 25 | UIApplicationSceneManifest 26 | 27 | UIApplicationSupportsMultipleScenes 28 | 29 | 30 | UILaunchScreen 31 | 32 | UIRequiredDeviceCapabilities 33 | 34 | arm64 35 | 36 | UISupportedInterfaceOrientations 37 | 38 | UIInterfaceOrientationPortrait 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UISupportedInterfaceOrientations~ipad 43 | 44 | UIInterfaceOrientationPortrait 45 | UIInterfaceOrientationPortraitUpsideDown 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | 50 | -------------------------------------------------------------------------------- /docs/zh_CN/guide/multiplatform.md: -------------------------------------------------------------------------------- 1 | # 平台支持 2 | 3 | Miuix 是一个支持多种平台的 Compose Multiplatform UI 框架,允许您使用相同的代码库在不同平台上构建应用程序。 4 | 5 | ## 支持的平台 6 | 7 | 目前,Miuix 支持以下平台: 8 | 9 | - **Android**:适用于 Android 移动设备 10 | - **iOS**:适用于 iPhone 和 iPad 设备 11 | - **Desktop(JVM)**:适用于基于 JVM 的桌面应用 12 | - **WasmJs**:适用于 WebAssembly(Web) 环境 13 | - **MacOS**:适用于 macOS 原生应用 14 | - **Js**:适用于 JavaScript(Web) 环境 15 | 16 | ## 平台检测与适配 17 | 18 | 您可以使用 `platform()` 函数检测当前运行的平台,并据此调整 UI 或功能: 19 | 20 | ```kotlin 21 | when (platform()) { 22 | Platform.Android -> { 23 | // Android 特定代码 24 | } 25 | Platform.IOS -> { 26 | // iOS 特定代码 27 | } 28 | Platform.Desktop -> { 29 | // JVM 特定代码 30 | } 31 | Platform.WasmJs -> { 32 | // WebAssembly 特定代码 33 | } 34 | Platform.MacOS -> { 35 | // macOS 特定代码 36 | } 37 | Platform.Js -> { 38 | // JavaScript 特定代码 39 | } 40 | } 41 | ``` 42 | 43 | ## 窗口尺寸管理 44 | 45 | Miuix 提供了跨平台的窗口尺寸获取功能: 46 | 47 | ```kotlin 48 | @Composable 49 | fun MyResponsiveContent() { 50 | val windowSize = getWindowSize() 51 | val width = windowSize.width 52 | val height = windowSize.height 53 | // 根据窗口尺寸调整 UI 布局 54 | Surface( 55 | modifier = Modifier.fillMaxSize(), 56 | ) { 57 | if (width > 600) { 58 | Text("宽屏布局") 59 | } else { 60 | Text("窄屏布局") 61 | } 62 | Text("\n高度:$height") 63 | } 64 | } 65 | ``` 66 | 67 | ## 设备圆角 68 | 69 | 安卓设备的屏幕圆角不同且其他平台不存在屏幕圆角获取,您可以使用 `getRoundedCorner()` 函数获取设备的圆角大小(不存在时使用预设值): 70 | 71 | ```kotlin 72 | @Composable 73 | fun AdaptiveRoundedComponent() { 74 | val cornerRadius = getRoundedCorner() 75 | Surface( 76 | shape = RoundedCornerShape(cornerRadius), 77 | // 其他属性 78 | ) { 79 | // 内容 80 | } 81 | } 82 | ``` 83 | -------------------------------------------------------------------------------- /example/src/androidMain/kotlin/top/yukonga/miuix/uitest/MainActivity.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.uitest 5 | 6 | import App 7 | import android.graphics.Color 8 | import android.os.Build 9 | import android.os.Bundle 10 | import androidx.activity.ComponentActivity 11 | import androidx.activity.SystemBarStyle 12 | import androidx.activity.compose.setContent 13 | import androidx.activity.enableEdgeToEdge 14 | import androidx.compose.foundation.isSystemInDarkTheme 15 | import androidx.compose.runtime.DisposableEffect 16 | import androidx.compose.runtime.mutableIntStateOf 17 | import androidx.compose.runtime.remember 18 | 19 | class MainActivity : ComponentActivity() { 20 | override fun onCreate(savedInstanceState: Bundle?) { 21 | super.onCreate(savedInstanceState) 22 | 23 | setContent { 24 | val colorMode = remember { mutableIntStateOf(0) } 25 | val darkMode = when (colorMode.intValue) { 26 | 2, 5 -> true 27 | 0, 3 -> isSystemInDarkTheme() 28 | else -> false 29 | } 30 | 31 | DisposableEffect(darkMode) { 32 | enableEdgeToEdge( 33 | statusBarStyle = SystemBarStyle.auto(Color.TRANSPARENT, Color.TRANSPARENT) { darkMode }, 34 | navigationBarStyle = SystemBarStyle.auto(Color.TRANSPARENT, Color.TRANSPARENT) { darkMode }, 35 | ) 36 | 37 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { 38 | window.isNavigationBarContrastEnforced = false // Xiaomi moment, this code must be here 39 | } 40 | 41 | onDispose {} 42 | } 43 | 44 | App(colorMode) 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/icon/icons/basic/Check.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.icon.icons.basic 5 | 6 | import androidx.compose.ui.graphics.Color 7 | import androidx.compose.ui.graphics.PathFillType 8 | import androidx.compose.ui.graphics.SolidColor 9 | import androidx.compose.ui.graphics.vector.ImageVector 10 | import androidx.compose.ui.graphics.vector.path 11 | import androidx.compose.ui.unit.dp 12 | import top.yukonga.miuix.kmp.icon.MiuixIcons 13 | 14 | val MiuixIcons.Basic.Check: ImageVector 15 | get() { 16 | if (_check != null) return _check!! 17 | _check = ImageVector.Builder("Check", 26.dp, 26.dp, 56f, 56f).apply { 18 | path( 19 | fill = SolidColor(Color.Black), 20 | fillAlpha = 1f, 21 | pathFillType = PathFillType.EvenOdd 22 | ) { 23 | moveTo(46.8171f, 18.1514f) 24 | curveTo(48.0496f, 16.6624f, 47.8417f, 14.4561f, 46.3527f, 13.2235f) 25 | curveTo(44.8636f, 11.991f, 42.6573f, 12.1989f, 41.4247f, 13.6879f) 26 | lineTo(22.9535f, 36.0031f) 27 | lineTo(13.4007f, 26.4502f) 28 | curveTo(12.0338f, 25.0833f, 9.8177f, 25.0833f, 8.4509f, 26.4502f) 29 | curveTo(7.0841f, 27.817f, 7.0841f, 30.0331f, 8.4509f, 31.3999f) 30 | lineTo(20.7077f, 43.6567f) 31 | curveTo(21.7243f, 44.6733f, 23.2108f, 44.9338f, 24.4682f, 44.4381f) 32 | curveTo(25.0159f, 44.2302f, 25.5189f, 43.8818f, 25.9192f, 43.3982f) 33 | lineTo(46.8171f, 18.1514f) 34 | close() 35 | } 36 | }.build() 37 | return _check!! 38 | } 39 | 40 | private var _check: ImageVector? = null -------------------------------------------------------------------------------- /docs/guide/textstyles.md: -------------------------------------------------------------------------------- 1 | # Text Styles 2 | 3 | This page documents all text styles provided by Miuix, based on the actual implementation. 4 | 5 | ## Using Text Styles 6 | 7 | - Use `MiuixTheme.textStyles.` in composables. 8 | - The color of all styles is set from `MiuixTheme.colorScheme.onBackground` at runtime. 9 | 10 | ## Styles 11 | 12 | | Style Name | Size | Weight | Line Height | 13 | |------------|------|--------|-------------| 14 | | `main` | 17sp | Normal | - | 15 | | `paragraph`| 17sp | Normal | 1.2em | 16 | | `body1` | 16sp | Normal | - | 17 | | `body2` | 14sp | Normal | - | 18 | | `button` | 17sp | Normal | - | 19 | | `footnote1`| 13sp | Normal | - | 20 | | `footnote2`| 11sp | Normal | - | 21 | | `headline1`| 17sp | Normal | - | 22 | | `headline2`| 16sp | Normal | - | 23 | | `subtitle` | 14sp | Bold | - | 24 | | `title1` | 32sp | Normal | - | 25 | | `title2` | 24sp | Normal | - | 26 | | `title3` | 20sp | Normal | - | 27 | | `title4` | 18sp | Normal | - | 28 | 29 | ## Usage 30 | 31 | ```kotlin 32 | Text( 33 | text = "Title", 34 | style = MiuixTheme.textStyles.title2 35 | ) 36 | 37 | Text( 38 | text = "Body", 39 | style = MiuixTheme.textStyles.body1 40 | ) 41 | ``` 42 | 43 | ## Customization 44 | 45 | - Override styles via `defaultTextStyles(...)` and pass to `MiuixTheme(textStyles = ...)`. 46 | 47 | ```kotlin 48 | val customTextStyles = defaultTextStyles( 49 | title1 = TextStyle( 50 | fontSize = 36.sp, 51 | fontWeight = FontWeight.Bold 52 | ) 53 | ) 54 | 55 | val controller = remember { ThemeController(ColorSchemeMode.System) } 56 | MiuixTheme( 57 | controller = controller, 58 | textStyles = customTextStyles 59 | ) { /* Content */ } 60 | ``` 61 | -------------------------------------------------------------------------------- /miuix/src/iosMain/kotlin/top/yukonga/miuix/kmp/utils/Utils.ios.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.utils 5 | 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.runtime.derivedStateOf 8 | import androidx.compose.runtime.getValue 9 | import androidx.compose.runtime.remember 10 | import androidx.compose.ui.ExperimentalComposeUiApi 11 | import androidx.compose.ui.graphics.Color 12 | import androidx.compose.ui.platform.LocalDensity 13 | import androidx.compose.ui.unit.Dp 14 | import androidx.compose.ui.unit.dp 15 | import androidx.compose.ui.window.DialogProperties 16 | import kotlinx.cinterop.ExperimentalForeignApi 17 | import kotlinx.cinterop.useContents 18 | import platform.UIKit.UIScreen 19 | import kotlin.math.roundToInt 20 | 21 | @OptIn(ExperimentalForeignApi::class) 22 | @Composable 23 | actual fun getWindowSize(): WindowSize { 24 | val screenBounds = remember { UIScreen.mainScreen.bounds } 25 | val density = LocalDensity.current.density 26 | val windowSize by remember(screenBounds, density) { 27 | derivedStateOf { 28 | val width = screenBounds.useContents { size.width } * density 29 | val height = screenBounds.useContents { size.height } * density 30 | WindowSize(width.roundToInt(), height.roundToInt()) 31 | } 32 | } 33 | return windowSize 34 | } 35 | 36 | actual fun platform(): Platform = Platform.IOS 37 | 38 | @Composable 39 | actual fun getRoundedCorner(): Dp = 0.dp 40 | 41 | @OptIn(ExperimentalComposeUiApi::class) 42 | @Composable 43 | actual fun platformDialogProperties(): DialogProperties = DialogProperties( 44 | usePlatformDefaultWidth = false, 45 | scrimColor = Color.Transparent 46 | ) 47 | 48 | @Composable 49 | actual fun removePlatformDialogDefaultEffects() {} 50 | -------------------------------------------------------------------------------- /docs/demo/build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin 5 | import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension 6 | 7 | plugins { 8 | alias(libs.plugins.compose.compiler) 9 | alias(libs.plugins.jetbrains.compose) 10 | alias(libs.plugins.kotlin.multiplatform) 11 | alias(libs.plugins.spotless) 12 | } 13 | 14 | java { 15 | toolchain.languageVersion = JavaLanguageVersion.of(21) 16 | } 17 | 18 | kotlin { 19 | jvmToolchain(21) 20 | 21 | js(IR) { 22 | outputModuleName = "demo" 23 | browser { 24 | commonWebpackConfig { 25 | outputFileName = "demo.js" 26 | } 27 | } 28 | compilerOptions { 29 | freeCompilerArgs.add("-Xes-long-as-bigint") 30 | freeCompilerArgs.add("-XXLanguage:+JsAllowLongInExportedDeclarations") 31 | } 32 | binaries.executable() 33 | } 34 | 35 | sourceSets { 36 | commonMain.dependencies { 37 | implementation(compose.runtime) 38 | implementation(compose.foundation) 39 | implementation(compose.ui) 40 | 41 | implementation(libs.jetbrains.androidx.navigation) 42 | implementation(project(":miuix")) 43 | } 44 | } 45 | } 46 | 47 | rootProject.plugins.withType { 48 | rootProject.the().lockFileDirectory = rootProject.file("docs/demo").resolve("kotlin-js-store") 49 | } 50 | 51 | spotless { 52 | kotlin { 53 | target("src/**/*.kt") 54 | licenseHeaderFile(rootProject.file("./spotless/copyright.txt"), "(^(?![\\/ ]\\**).*$)") 55 | } 56 | 57 | kotlinGradle { 58 | target("*.kts") 59 | licenseHeaderFile(rootProject.file("./spotless/copyright.txt"), "(^(?![\\/ ]\\**).*$)") 60 | } 61 | } -------------------------------------------------------------------------------- /example/src/webMain/kotlin/Main.web.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import androidx.compose.foundation.layout.PaddingValues 5 | import androidx.compose.runtime.LaunchedEffect 6 | import androidx.compose.runtime.getValue 7 | import androidx.compose.runtime.mutableStateOf 8 | import androidx.compose.runtime.remember 9 | import androidx.compose.runtime.setValue 10 | import androidx.compose.ui.ExperimentalComposeUiApi 11 | import androidx.compose.ui.platform.LocalLayoutDirection 12 | import androidx.compose.ui.unit.Dp 13 | import androidx.compose.ui.window.ComposeViewport 14 | 15 | @OptIn(ExperimentalComposeUiApi::class) 16 | fun main() { 17 | hideLoading() 18 | ComposeViewport(viewportContainerId = "composeApp") { 19 | 20 | LocalLayoutDirection.current 21 | var insetTopPx by remember { mutableStateOf(0.0) } 22 | var insetBottomPx by remember { mutableStateOf(0.0) } 23 | var insetStartPx by remember { mutableStateOf(0.0) } 24 | var insetEndPx by remember { mutableStateOf(0.0) } 25 | 26 | LaunchedEffect(Unit) { 27 | insetTopPx = getCssVar("--safe-area-inset-top") 28 | insetStartPx = getCssVar("--safe-area-inset-left") 29 | insetEndPx = getCssVar("--safe-area-inset-right") 30 | insetBottomPx = getCssVar("--safe-area-inset-bottom") 31 | } 32 | 33 | App( 34 | padding = PaddingValues( 35 | top = Dp(insetTopPx.toFloat()), 36 | bottom = Dp(insetBottomPx.toFloat()), 37 | start = Dp(insetStartPx.toFloat()), 38 | end = Dp(insetEndPx.toFloat()) 39 | ), 40 | enableOverScroll = isTouchEnabled() 41 | ) 42 | } 43 | } 44 | 45 | private fun hideLoading() = platformHideLoading() 46 | 47 | private fun getCssVar(name: String): Double = platformGetCssVar(name) 48 | 49 | private fun isTouchEnabled(): Boolean = platformIsTouchEnabled() 50 | -------------------------------------------------------------------------------- /docs/guide/best-practices.md: -------------------------------------------------------------------------------- 1 | # Best Practices 2 | 3 | The Miuix UI library has been adopted by many open-source projects. Below are some example applications built with Miuix, showcasing how to apply Miuix components and design principles in real-world projects. 4 | 5 | ### Example Applications 6 | The example application of this project itself demonstrates how to build interfaces using all the components included in Miuix. You can find usage and examples of various components in the example application to better understand and use the Miuix UI library. 7 | 8 | ### Updater-KMP 9 | 10 | [Updater-KMP](https://github.com/YuKongA/Updater-KMP) is a tool developed using Kotlin Multiplatform technology to fetch Xiaomi system updates. This project demonstrates how to use Miuix UI components to build a consistent cross-platform user interface. 11 | 12 | ### StatusBarLyric 13 | 14 | [StatusBarLyric](https://github.com/Block-Network/StatusBarLyric) is an Xposed application that displays the currently playing music lyrics in the status bar. This project demonstrates how to use Miuix components in conjunction with Xposed. 15 | 16 | ### XiaomiHelper 17 | 18 | [XiaomiHelper](https://github.com/HowieHChen/XiaomiHelper) is an Xposed module for Xiaomi unlocked devices, offering various system optimization and feature enhancement options. This project demonstrates how to use Miuix to build complex multifunctional applications. 19 | 20 | ### OShin 21 | 22 | [OShin](https://github.com/suqi8/OShin) is an Xposed module for OPPO unlocked devices. This project fully demonstrates how to create more diverse components based on Miuix. 23 | 24 | ### HyperStar 25 | 26 | [HyperStar](https://github.com/YunZiA/HyperStar) is an Xposed system interface customization module for Xiaomi unlocked devices. This project showcases the application of Miuix in complex interface layouts. 27 | 28 | ### Payload-Dumper-Compose 29 | 30 | [Payload-Dumper-Compose](https://github.com/rcmiku/Payload-Dumper-Compose) is an Android system image extraction tool based on Jetpack Compose. 31 | -------------------------------------------------------------------------------- /docs/demo/src/commonMain/kotlin/ColorPaletteDemo.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import androidx.compose.foundation.background 5 | import androidx.compose.foundation.layout.Arrangement 6 | import androidx.compose.foundation.layout.Box 7 | import androidx.compose.foundation.layout.Column 8 | import androidx.compose.foundation.layout.fillMaxSize 9 | import androidx.compose.foundation.layout.fillMaxWidth 10 | import androidx.compose.foundation.layout.padding 11 | import androidx.compose.foundation.layout.widthIn 12 | import androidx.compose.runtime.Composable 13 | import androidx.compose.runtime.getValue 14 | import androidx.compose.runtime.mutableStateOf 15 | import androidx.compose.runtime.remember 16 | import androidx.compose.runtime.setValue 17 | import androidx.compose.ui.Alignment 18 | import androidx.compose.ui.Modifier 19 | import androidx.compose.ui.graphics.Brush 20 | import androidx.compose.ui.graphics.Color 21 | import androidx.compose.ui.unit.dp 22 | import top.yukonga.miuix.kmp.basic.ColorPalette 23 | import top.yukonga.miuix.kmp.theme.MiuixTheme 24 | 25 | @Composable 26 | fun ColorPaletteDemo() { 27 | Box( 28 | modifier = Modifier 29 | .fillMaxSize() 30 | .background(Brush.linearGradient(listOf(Color(0xfff77062), Color(0xfffe5196)))), 31 | contentAlignment = Alignment.Center 32 | ) { 33 | Column( 34 | Modifier 35 | .padding(16.dp) 36 | .widthIn(max = 400.dp) 37 | .fillMaxWidth(), 38 | verticalArrangement = Arrangement.spacedBy(16.dp), 39 | horizontalAlignment = Alignment.CenterHorizontally 40 | ) { 41 | val miuixColor = MiuixTheme.colorScheme.primary 42 | var selectedColor by remember { mutableStateOf(miuixColor) } 43 | ColorPalette( 44 | initialColor = selectedColor, 45 | onColorChanged = { selectedColor = it } 46 | ) 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/icon/icons/useful/Like.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.icon.icons.useful 5 | 6 | import androidx.compose.ui.graphics.Color 7 | import androidx.compose.ui.graphics.PathFillType 8 | import androidx.compose.ui.graphics.SolidColor 9 | import androidx.compose.ui.graphics.vector.ImageVector 10 | import androidx.compose.ui.graphics.vector.path 11 | import androidx.compose.ui.unit.dp 12 | import top.yukonga.miuix.kmp.icon.MiuixIcons 13 | 14 | val MiuixIcons.Useful.Like: ImageVector 15 | get() { 16 | if (_like != null) return _like!! 17 | _like = ImageVector.Builder("Like", 26.0.dp, 26.0.dp, 26.0f, 26.0f).apply { 18 | path( 19 | fill = SolidColor(Color(0xFFFA311B)), 20 | pathFillType = PathFillType.EvenOdd 21 | ) { 22 | moveTo(21.9482f, 13.9362f) 23 | lineTo(13.4026f, 22.7729f) 24 | curveTo(13.1825f, 23.0005f, 12.8176f, 23.0005f, 12.5975f, 22.7729f) 25 | lineTo(4.0533f, 13.9376f) 26 | curveTo(4.0096f, 13.8937f, 3.9666f, 13.8492f, 3.9243f, 13.8041f) 27 | lineTo(3.9047f, 13.7839f) 28 | curveTo(2.8702f, 12.6721f, 2.2382f, 11.1816f, 2.2382f, 9.5433f) 29 | curveTo(2.2382f, 6.1045f, 5.0259f, 3.3167f, 8.4647f, 3.3167f) 30 | curveTo(10.2526f, 3.3167f, 11.8644f, 4.0703f, 13.0001f, 5.277f) 31 | curveTo(14.1357f, 4.0703f, 15.7475f, 3.3167f, 17.5354f, 3.3167f) 32 | curveTo(20.9742f, 3.3167f, 23.762f, 6.1045f, 23.762f, 9.5433f) 33 | curveTo(23.762f, 11.1816f, 23.1292f, 12.6721f, 22.0948f, 13.7839f) 34 | lineTo(22.0745f, 13.8056f) 35 | curveTo(22.033f, 13.8497f, 21.9909f, 13.8932f, 21.9482f, 13.9362f) 36 | close() 37 | } 38 | }.build() 39 | return _like!! 40 | } 41 | private var _like: ImageVector? = null -------------------------------------------------------------------------------- /example/src/wasmJsMain/kotlin/WebPlatformApi.wasmJs.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | @OptIn(ExperimentalWasmJsInterop::class) 5 | @JsFun( 6 | """ 7 | function hideLoading() { 8 | document.getElementById('loading').style.display = 'none'; 9 | document.getElementById('composeApp').style.display = 'block'; 10 | } 11 | """ 12 | ) 13 | private external fun hideLoading() 14 | 15 | @OptIn(ExperimentalWasmJsInterop::class) 16 | @JsFun( 17 | """ 18 | function getCssVar(name) { 19 | const docEl = document.documentElement; 20 | if (!docEl) return 0.0; 21 | const style = window.getComputedStyle(docEl); 22 | if (!style) return 0.0; 23 | const raw = style.getPropertyValue(name); 24 | if (!raw) return 0.0; 25 | const trimmed = raw.trim(); 26 | if (!trimmed) return 0.0; 27 | const parsed = parseFloat(trimmed); 28 | return Number.isNaN(parsed) ? 0.0 : parsed; 29 | } 30 | """ 31 | ) 32 | private external fun getCssVar(name: String): Double 33 | 34 | @OptIn(ExperimentalWasmJsInterop::class) 35 | @JsFun( 36 | """ 37 | function isTouchEnabled() { 38 | if (window.matchMedia && window.matchMedia('(pointer: coarse)').matches) { 39 | return true; 40 | } 41 | if ('ontouchstart' in window) { 42 | return true; 43 | } 44 | const nav = navigator || {}; 45 | const points = (nav.maxTouchPoints || 0) + (nav.msMaxTouchPoints || 0); 46 | return points > 0; 47 | } 48 | """ 49 | ) 50 | private external fun isTouchEnabled(): Boolean 51 | 52 | internal actual fun platformHideLoading() { 53 | return hideLoading() 54 | } 55 | 56 | internal actual fun platformGetCssVar(name: String): Double { 57 | return getCssVar(name) 58 | } 59 | 60 | internal actual fun platformIsTouchEnabled(): Boolean { 61 | return isTouchEnabled() 62 | } -------------------------------------------------------------------------------- /docs/.vitepress/theme/style/var.css: -------------------------------------------------------------------------------- 1 | :root { 2 | 3 | /* 亮色模式主题色系统 */ 4 | --miuix-color-primary: #3482FF; 5 | --miuix-color-primary-container: #5D9BFF; 6 | --miuix-color-on-tertiary-container: #3482FF; 7 | --miuix-color-outline: #D9D9D9; 8 | --miuix-color-divider-line: #E0E0E0; 9 | 10 | /* 主要字体 */ 11 | --vp-font-family-base: 'MiSans VF', 'Inter', ui-sans-serif, system-ui, sans-serif, 12 | 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; 13 | 14 | /* 主要主题色 */ 15 | --vp-c-brand-1: var(--miuix-color-primary); 16 | --vp-c-brand-2: var(--miuix-color-primary-container); 17 | --vp-c-brand-3: var(--miuix-color-on-tertiary-container); 18 | 19 | /* 边框和分隔线颜色 */ 20 | --vp-c-divider: var(--miuix-color-divider-line); 21 | --vp-c-gutter: var(--miuix-color-outline); 22 | 23 | /* hero logo 背景颜色 */ 24 | --vp-home-hero-image-background-image: linear-gradient(to right bottom, var(--miuix-color-primary), var(--miuix-color-primary-container)); 25 | --vp-home-hero-image-filter: blur(40px); 26 | 27 | /* 搜索框边框描边颜色 */ 28 | @media (min-width: 960px) { 29 | .DocSearch-Button { 30 | border: 1px solid var(--miuix-color-outline); 31 | } 32 | } 33 | 34 | } 35 | 36 | /* 深色模式主题色 */ 37 | .dark { 38 | --miuix-color-primary: #277AF7; 39 | --miuix-color-primary-container: #338FE4; 40 | --miuix-color-on-tertiary-container: #277AF7; 41 | --miuix-color-outline: #404040; 42 | --miuix-color-divider-line: #393939; 43 | } 44 | 45 | /* 标题颜色 */ 46 | .VPDoc h1 { 47 | background: linear-gradient(120deg, var(--miuix-color-primary), var(--miuix-color-primary-container)); 48 | -webkit-background-clip: text; 49 | background-clip: text; 50 | -webkit-text-fill-color: transparent; 51 | } 52 | 53 | /* Medium Zoom */ 54 | .medium-zoom-overlay { 55 | z-index: 30; 56 | } 57 | 58 | .medium-zoom-image { 59 | z-index: 9999 !important; 60 | } 61 | 62 | /* Nólëbase Integrations */ 63 | .VPSocialLinks.VPNavBarSocialLinks.social-links { 64 | margin-right: 0; 65 | } -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | android-gradle-plugin = "8.13.2" 3 | androidx-activity = "1.12.1" 4 | androidx-window = "1.5.1" 5 | capsule = "2.1.1-patch2" 6 | jetbrains-androidx-navigation = "2.9.1" 7 | jetbrains-compose = "1.9.3" 8 | jetbrains-compose-hotReload = "1.0.0" 9 | jetbrains-compose-window-size = "1.9.0" 10 | jetbrains-dokka = "2.1.0" 11 | kotlin = "2.2.21" 12 | material-kolor = "4.0.5" 13 | spotless = "8.1.0" 14 | 15 | [libraries] 16 | androidx-activity = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity" } 17 | androidx-window = { group = "androidx.window", name = "window", version.ref = "androidx-window" } 18 | jetbrains-androidx-navigation = { module = "org.jetbrains.androidx.navigation:navigation-compose", version.ref = "jetbrains-androidx-navigation" } 19 | jetbrains-compose-ui-backhandler = { module = "org.jetbrains.compose.ui:ui-backhandler", version.ref = "jetbrains-compose" } 20 | jetbrains-compose-window-size = { module = "org.jetbrains.compose.material3:material3-window-size-class", version.ref = "jetbrains-compose-window-size" } 21 | gaze-capsule = { module = "com.mocharealm.gaze:capsule", version.ref = "capsule" } 22 | materialkolor-material-kolor = { module = "com.materialkolor:material-kolor", version.ref = "material-kolor" } 23 | 24 | [plugins] 25 | android-application = { id = "com.android.application", version.ref = "android-gradle-plugin" } 26 | android-kotlin-multiplatform-library = { id = "com.android.kotlin.multiplatform.library", version.ref = "android-gradle-plugin" } 27 | compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } 28 | jetbrains-compose = { id = "org.jetbrains.compose", version.ref = "jetbrains-compose" } 29 | jetbrains-compose-hotReload = { id = "org.jetbrains.compose.hot-reload", version.ref = "jetbrains-compose-hotReload" } 30 | jetbrains-dokka = { id = "org.jetbrains.dokka", version.ref = "jetbrains-dokka" } 31 | kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } 32 | spotless = { id = "com.diffplug.spotless", version.ref = "spotless" } -------------------------------------------------------------------------------- /docs/zh_CN/guide/theme.md: -------------------------------------------------------------------------------- 1 | # 主题系统 2 | 3 | Miuix 提供了一套完整的主题系统,使您能够轻松地在整个应用中保持一致的设计风格。整个主题系统由颜色方案和文本样式组成。 4 | 5 | ## 使用 MiuixTheme 6 | 7 | 使用 `ThemeController` 控制配色模式,然后用 `MiuixTheme` 包裹内容: 8 | 9 | ```kotlin 10 | @Composable 11 | fun App() { 12 | // 可用模式:System、Light、Dark、MonetSystem、MonetLight、MonetDark 13 | val controller = remember { ThemeController(ColorSchemeMode.System) } 14 | MiuixTheme(controller = controller) { /* 内容 */ } 15 | } 16 | ``` 17 | 18 | 使用 `ColorSchemeMode.System` / `ColorSchemeMode.MonetSystem` 时会自动跟随系统深色模式。 19 | 20 | ### 具体方式 21 | 22 | - 通过 `ThemeController` 控制配色模式,并可启用莫奈颜色;在莫奈模式下支持自定义种子色(`keyColor`): 23 | 24 | ```kotlin 25 | @Composable 26 | fun AppWithMonet() { 27 | val controller = remember { 28 | ThemeController( 29 | ColorSchemeMode.MonetSystem, // 或 MonetLight、MonetDark 30 | keyColor = Color(0xFF3482FF) // 自定义种子颜色 31 | ) 32 | } 33 | MiuixTheme(controller = controller) { /* 内容 */ } 34 | } 35 | ``` 36 | 37 | - 直接传入颜色方案到 `MiuixTheme(colors = ...)`,用于完全自定义或使用内置浅/深色方案: 38 | 39 | ```kotlin 40 | @Composable 41 | fun AppWithColors() { 42 | val colors = lightColorScheme() // 或 darkColorScheme() 43 | MiuixTheme(colors = colors) { /* 内容 */ } 44 | } 45 | ``` 46 | 47 | ## 自定义主题 48 | 49 | 可以通过以下方式进行主题自定义: 50 | 51 | - 通过 `ThemeController(ColorSchemeMode.*)` 选择配色模式。 52 | - 选择动态配色:`MonetSystem` / `MonetLight` / `MonetDark`。 53 | - 传入 `textStyles` 覆盖文本样式: 54 | 55 | ```kotlin 56 | val customTextStyles = defaultTextStyles( 57 | title1 = TextStyle( 58 | fontSize = 36.sp, 59 | fontWeight = FontWeight.Bold 60 | ), 61 | // 其他文本样式... 62 | ) 63 | 64 | val controller = remember { ThemeController(ColorSchemeMode.Light) } 65 | MiuixTheme( 66 | controller = controller, 67 | textStyles = customTextStyles 68 | ) { 69 | // 您的应用内容 70 | } 71 | ``` 72 | 73 | ## 跟随系统深色模式 74 | 75 | 跟随系统深色模式已内置,使用 `ColorSchemeMode.System` 即可: 76 | 77 | ```kotlin 78 | @Composable 79 | fun MyApp() { 80 | val controller = remember { ThemeController(ColorSchemeMode.System) } 81 | MiuixTheme(controller = controller) { 82 | // 应用内容 83 | } 84 | } 85 | ``` 86 | -------------------------------------------------------------------------------- /docs/demo/src/commonMain/kotlin/ColorPickerDemo.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import androidx.compose.foundation.background 5 | import androidx.compose.foundation.layout.Arrangement 6 | import androidx.compose.foundation.layout.Box 7 | import androidx.compose.foundation.layout.Column 8 | import androidx.compose.foundation.layout.fillMaxSize 9 | import androidx.compose.foundation.layout.fillMaxWidth 10 | import androidx.compose.foundation.layout.padding 11 | import androidx.compose.foundation.layout.widthIn 12 | import androidx.compose.runtime.Composable 13 | import androidx.compose.runtime.getValue 14 | import androidx.compose.runtime.mutableStateOf 15 | import androidx.compose.runtime.remember 16 | import androidx.compose.runtime.setValue 17 | import androidx.compose.ui.Alignment 18 | import androidx.compose.ui.Modifier 19 | import androidx.compose.ui.graphics.Brush 20 | import androidx.compose.ui.graphics.Color 21 | import androidx.compose.ui.unit.dp 22 | import top.yukonga.miuix.kmp.basic.ColorPicker 23 | import top.yukonga.miuix.kmp.basic.SliderDefaults 24 | import top.yukonga.miuix.kmp.theme.MiuixTheme 25 | 26 | @Composable 27 | fun ColorPickerDemo() { 28 | Box( 29 | modifier = Modifier 30 | .fillMaxSize() 31 | .background(Brush.linearGradient(listOf(Color(0xfff77062), Color(0xfffe5196)))), 32 | contentAlignment = Alignment.Center 33 | ) { 34 | Column( 35 | Modifier 36 | .padding(16.dp) 37 | .widthIn(max = 600.dp) 38 | .fillMaxWidth(), 39 | verticalArrangement = Arrangement.spacedBy(16.dp), 40 | horizontalAlignment = Alignment.CenterHorizontally 41 | ) { 42 | val miuixColor = MiuixTheme.colorScheme.primary 43 | var selectedColor by remember { mutableStateOf(miuixColor) } 44 | ColorPicker( 45 | initialColor = selectedColor, 46 | onColorChanged = { selectedColor = it }, 47 | hapticEffect = SliderDefaults.SliderHapticEffect.Step 48 | ) 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | # Simple workflow for deploying static content to GitHub Pages 2 | name: Build Docs Page 3 | 4 | on: 5 | # Only run this workflow manually from the Actions tab 6 | workflow_dispatch: 7 | 8 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 9 | permissions: 10 | contents: read 11 | pages: write 12 | id-token: write 13 | 14 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 15 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 16 | concurrency: 17 | group: "pages" 18 | cancel-in-progress: false 19 | 20 | jobs: 21 | build: 22 | runs-on: ubuntu-latest 23 | steps: 24 | - name: Checkout Sources 25 | uses: actions/checkout@v6 26 | with: 27 | fetch-depth: 0 28 | - name: Set up JDK 29 | uses: actions/setup-java@v5 30 | with: 31 | distribution: 'zulu' 32 | java-version: '21' 33 | - name: Setup Gradle 34 | uses: gradle/actions/setup-gradle@v5 35 | - name: Build Docs 36 | run: | 37 | ./gradlew :miuix:dokkaGenerate 38 | mkdir -p ./docs/public/dokka 39 | mv ./miuix/build/dokka/html/* ./docs/public/dokka 40 | ./gradlew :docs:demo:jsBrowserDistribution 41 | mkdir -p ./docs/public/compose 42 | mv ./docs/demo/build/dist/js/productionExecutable/* ./docs/public/compose 43 | ./gradlew :docs:iconGen:generateSvg 44 | mv ./docs/iconGen/build/generated-svg/icons ./docs/public/icons 45 | cd ./docs 46 | yarn install 47 | yarn docs:build 48 | - name: Setup Pages 49 | uses: actions/configure-pages@v5 50 | - name: Upload artifact 51 | uses: actions/upload-pages-artifact@v4 52 | with: 53 | path: ./docs/.vitepress/dist 54 | 55 | deploy: 56 | environment: 57 | name: github-pages 58 | url: ${{ steps.deployment.outputs.page_url }} 59 | runs-on: ubuntu-latest 60 | needs: build 61 | steps: 62 | - name: Deploy to GitHub Pages 63 | id: deployment 64 | uses: actions/deploy-pages@v4 -------------------------------------------------------------------------------- /docs/.vitepress/config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitepress"; 2 | import locales from "./locales"; 3 | import { 4 | GitChangelog, 5 | GitChangelogMarkdownSection, 6 | } from "@nolebase/vitepress-plugin-git-changelog/vite"; 7 | 8 | export default defineConfig({ 9 | base: "/miuix/", 10 | title: "Miuix", 11 | locales: locales.locales, 12 | head: [ 13 | ['link', { rel: 'icon', href: '/miuix/Icon.webp' }], 14 | ['link', { rel: 'preconnect', href: 'https://cdn-font.hyperos.mi.com/font/css?family=MiSans_VF:VF:Chinese_Simplify,Latin&display=swap' }], 15 | ], 16 | markdown: { 17 | image: { 18 | lazyLoading: true, 19 | }, 20 | }, 21 | cleanUrls: true, 22 | themeConfig: { 23 | logo: "/Icon.webp", 24 | socialLinks: [ 25 | { icon: 'github', link: 'https://github.com/compose-miuix-ui/miuix' } 26 | ], 27 | search: { 28 | provider: "local", 29 | options: { 30 | locales: { 31 | zh_CN: { 32 | translations: { 33 | button: { 34 | buttonText: "搜索", 35 | buttonAriaLabel: "搜索", 36 | }, 37 | modal: { 38 | noResultsText: "无法找到相关结果", 39 | resetButtonTitle: "清除查询条件", 40 | footer: { 41 | selectText: "选择", 42 | navigateText: "切换", 43 | closeText: "关闭", 44 | }, 45 | }, 46 | }, 47 | }, 48 | }, 49 | }, 50 | }, 51 | docFooter: { 52 | prev: false, 53 | next: false, 54 | }, 55 | }, 56 | vite: { 57 | optimizeDeps: { 58 | exclude: [ 59 | "@nolebase/vitepress-plugin-enhanced-readabilities/client", 60 | "vitepress", 61 | "@nolebase/ui", 62 | ], 63 | }, 64 | ssr: { 65 | noExternal: [ 66 | "@nolebase/vitepress-plugin-enhanced-readabilities", 67 | "@nolebase/ui", 68 | ], 69 | }, 70 | plugins: [ 71 | GitChangelog({ 72 | repoURL: () => "https://github.com/compose-miuix-ui/miuix", 73 | }), 74 | GitChangelogMarkdownSection({ 75 | sections: { 76 | disableContributors: true, 77 | }, 78 | }), 79 | ], 80 | }, 81 | }); 82 | -------------------------------------------------------------------------------- /example/src/commonMain/kotlin/utils/FPSMonitor.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package utils 5 | 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.runtime.LaunchedEffect 8 | import androidx.compose.runtime.getValue 9 | import androidx.compose.runtime.mutableStateOf 10 | import androidx.compose.runtime.remember 11 | import androidx.compose.runtime.setValue 12 | import androidx.compose.runtime.withFrameMillis 13 | import androidx.compose.ui.Modifier 14 | import androidx.compose.ui.graphics.Color 15 | import kotlinx.coroutines.coroutineScope 16 | import top.yukonga.miuix.kmp.basic.Text 17 | 18 | /** 19 | * A simple FPS monitor that displays the current frames per second. 20 | */ 21 | @Composable 22 | fun FPSMonitor(modifier: Modifier = Modifier) { 23 | var fps by remember { mutableStateOf(0) } 24 | var maxFps by remember { mutableStateOf(0) } 25 | var lastFrameTime by remember { mutableStateOf(0L) } 26 | var frameCount by remember { mutableStateOf(0) } 27 | var totalFrameTime by remember { mutableStateOf(0L) } 28 | 29 | if (fps > maxFps) { 30 | maxFps = fps 31 | } 32 | 33 | val color = when { 34 | fps >= maxFps - 2 -> Color.Green 35 | fps >= maxFps - 6 -> Color.Blue 36 | fps >= maxFps - 15 -> Color(0xFFFFD700) 37 | else -> Color.Red 38 | } 39 | 40 | Text( 41 | modifier = modifier, 42 | text = "FPS: $fps", 43 | color = color 44 | ) 45 | 46 | LaunchedEffect(Unit) { 47 | coroutineScope { 48 | while (true) { 49 | withFrameMillis { frameTimeMillis -> 50 | if (lastFrameTime != 0L) { 51 | val frameDuration = frameTimeMillis - lastFrameTime 52 | totalFrameTime += frameDuration 53 | frameCount++ 54 | if (totalFrameTime >= 1000L) { 55 | fps = frameCount 56 | frameCount = 0 57 | totalFrameTime = 0L 58 | } 59 | } 60 | lastFrameTime = frameTimeMillis 61 | } 62 | } 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /docs/demo/src/commonMain/kotlin/SliderDemo.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import androidx.compose.foundation.background 5 | import androidx.compose.foundation.layout.Arrangement 6 | import androidx.compose.foundation.layout.Box 7 | import androidx.compose.foundation.layout.Column 8 | import androidx.compose.foundation.layout.fillMaxSize 9 | import androidx.compose.foundation.layout.fillMaxWidth 10 | import androidx.compose.foundation.layout.padding 11 | import androidx.compose.foundation.layout.widthIn 12 | import androidx.compose.runtime.Composable 13 | import androidx.compose.runtime.getValue 14 | import androidx.compose.runtime.mutableStateOf 15 | import androidx.compose.runtime.remember 16 | import androidx.compose.runtime.setValue 17 | import androidx.compose.ui.Alignment 18 | import androidx.compose.ui.Modifier 19 | import androidx.compose.ui.graphics.Brush 20 | import androidx.compose.ui.graphics.Color 21 | import androidx.compose.ui.unit.dp 22 | import top.yukonga.miuix.kmp.basic.RangeSlider 23 | import top.yukonga.miuix.kmp.basic.Slider 24 | 25 | @Composable 26 | fun SliderDemo() { 27 | Box( 28 | modifier = Modifier 29 | .fillMaxSize() 30 | .background(Brush.linearGradient(listOf(Color(0xfff77062), Color(0xfffe5196)))), 31 | contentAlignment = Alignment.Center 32 | ) { 33 | Column( 34 | Modifier 35 | .padding(16.dp) 36 | .widthIn(max = 600.dp) 37 | .fillMaxWidth(), 38 | verticalArrangement = Arrangement.spacedBy(16.dp), 39 | horizontalAlignment = Alignment.CenterHorizontally 40 | ) { 41 | var slider1 by remember { mutableStateOf(0.5f) } 42 | var slider2 by remember { mutableStateOf(0.7f) } 43 | var slider3 by remember { mutableStateOf(0.2f..0.8f) } 44 | Slider( 45 | value = slider1, 46 | onValueChange = { slider1 = it } 47 | ) 48 | Slider( 49 | value = slider2, 50 | onValueChange = { slider2 = it }, 51 | enabled = false 52 | ) 53 | RangeSlider( 54 | value = slider3, 55 | onValueChange = { slider3 = it } 56 | ) 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/interfaces/HoldDownInteraction.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.interfaces 5 | 6 | import androidx.compose.foundation.interaction.Interaction 7 | import androidx.compose.foundation.interaction.InteractionSource 8 | import androidx.compose.foundation.interaction.MutableInteractionSource 9 | import androidx.compose.runtime.Composable 10 | import androidx.compose.runtime.LaunchedEffect 11 | import androidx.compose.runtime.State 12 | import androidx.compose.runtime.mutableStateOf 13 | import androidx.compose.runtime.remember 14 | import top.yukonga.miuix.kmp.interfaces.HoldDownInteraction.HoldDown 15 | import top.yukonga.miuix.kmp.interfaces.HoldDownInteraction.Release 16 | 17 | /** 18 | * An interaction related to hold down events. 19 | * 20 | * @see HoldDown 21 | * @see Release 22 | */ 23 | interface HoldDownInteraction : Interaction { 24 | /** 25 | * An interaction representing a hold down event on a component. 26 | * 27 | * @see Release 28 | */ 29 | class HoldDown : HoldDownInteraction 30 | 31 | /** 32 | * An interaction representing a [HoldDown] event being released on a component. 33 | * 34 | * @property holdDown the source [HoldDown] interaction that is being released 35 | * 36 | * @see HoldDown 37 | */ 38 | class Release( 39 | val holdDown: HoldDown, 40 | ) : HoldDownInteraction 41 | } 42 | 43 | /** 44 | * Subscribes to this [MutableInteractionSource] and returns a [State] representing whether this 45 | * component is selected or not. 46 | * 47 | * @return [State] representing whether this component is being focused or not 48 | */ 49 | @Composable 50 | fun InteractionSource.collectIsHeldDownAsState(): State { 51 | val isHeldDown = remember { mutableStateOf(false) } 52 | LaunchedEffect(this) { 53 | val holdInteraction = mutableListOf() 54 | interactions.collect { interaction -> 55 | when (interaction) { 56 | is HoldDown -> holdInteraction.add(interaction) 57 | is Release -> holdInteraction.remove(interaction.holdDown) 58 | } 59 | isHeldDown.value = holdInteraction.isNotEmpty() 60 | } 61 | } 62 | return isHeldDown 63 | } 64 | -------------------------------------------------------------------------------- /miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/icon/icons/useful/Confirm.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.icon.icons.useful 5 | 6 | import androidx.compose.ui.graphics.Color 7 | import androidx.compose.ui.graphics.SolidColor 8 | import androidx.compose.ui.graphics.vector.ImageVector 9 | import androidx.compose.ui.graphics.vector.path 10 | import androidx.compose.ui.unit.dp 11 | import top.yukonga.miuix.kmp.icon.MiuixIcons 12 | 13 | val MiuixIcons.Useful.Confirm: ImageVector 14 | get() { 15 | if (_confirm != null) return _confirm!! 16 | _confirm = ImageVector.Builder("Confirm", 26.0.dp, 26.0.dp, 26.0f, 26.0f).apply { 17 | path( 18 | fill = SolidColor(Color.Black) 19 | ) { 20 | moveTo(23.571f, 4.818f) 21 | curveTo(23.518f, 4.739f, 23.428f, 4.673f, 23.249f, 4.54f) 22 | curveTo(23.069f, 4.406f, 22.979f, 4.34f, 22.889f, 4.312f) 23 | curveTo(22.726f, 4.261f, 22.549f, 4.287f, 22.408f, 4.383f) 24 | curveTo(22.329f, 4.436f, 22.263f, 4.525f, 22.129f, 4.705f) 25 | lineTo(10.625f, 20.196f) 26 | lineTo(3.889f, 13.13f) 27 | curveTo(3.735f, 12.968f, 3.658f, 12.887f, 3.573f, 12.844f) 28 | curveTo(3.422f, 12.767f, 3.243f, 12.762f, 3.088f, 12.833f) 29 | curveTo(3.001f, 12.872f, 2.92f, 12.949f, 2.758f, 13.103f) 30 | curveTo(2.597f, 13.257f, 2.516f, 13.334f, 2.472f, 13.419f) 31 | curveTo(2.395f, 13.571f, 2.391f, 13.75f, 2.461f, 13.905f) 32 | curveTo(2.5f, 13.991f, 2.577f, 14.072f, 2.731f, 14.234f) 33 | lineTo(10.044f, 21.904f) 34 | curveTo(10.296f, 22.169f, 10.422f, 22.301f, 10.566f, 22.344f) 35 | curveTo(10.692f, 22.381f, 10.827f, 22.373f, 10.947f, 22.32f) 36 | curveTo(11.085f, 22.261f, 11.194f, 22.114f, 11.412f, 21.82f) 37 | lineTo(23.414f, 5.659f) 38 | curveTo(23.547f, 5.479f, 23.614f, 5.39f, 23.642f, 5.299f) 39 | curveTo(23.693f, 5.136f, 23.667f, 4.959f, 23.571f, 4.818f) 40 | close() 41 | } 42 | }.build() 43 | return _confirm!! 44 | } 45 | 46 | private var _confirm: ImageVector? = null -------------------------------------------------------------------------------- /miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/icon/icons/useful/ImmersionMore.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.icon.icons.useful 5 | 6 | import androidx.compose.ui.graphics.Color 7 | import androidx.compose.ui.graphics.SolidColor 8 | import androidx.compose.ui.graphics.vector.ImageVector 9 | import androidx.compose.ui.graphics.vector.path 10 | import androidx.compose.ui.unit.dp 11 | import top.yukonga.miuix.kmp.icon.MiuixIcons 12 | 13 | val MiuixIcons.Useful.ImmersionMore: ImageVector 14 | get() { 15 | if (_immersionMore != null) return _immersionMore!! 16 | _immersionMore = ImageVector.Builder("ImmersionMore", 26.0.dp, 26.0.dp, 26.0f, 26.0f).apply { 17 | path( 18 | fill = SolidColor(Color.Black) 19 | ) { 20 | moveTo(13.0f, 14.405f) 21 | curveTo(12.224f, 14.405f, 11.594f, 13.776f, 11.594f, 13.0f) 22 | curveTo(11.594f, 12.224f, 12.224f, 11.595f, 13.0f, 11.595f) 23 | curveTo(13.776f, 11.595f, 14.405f, 12.224f, 14.405f, 13.0f) 24 | curveTo(14.405f, 13.776f, 13.776f, 14.405f, 13.0f, 14.405f) 25 | close() 26 | } 27 | path( 28 | fill = SolidColor(Color.Black) 29 | ) { 30 | moveTo(13.0f, 21.432f) 31 | curveTo(12.224f, 21.432f, 11.594f, 20.803f, 11.594f, 20.027f) 32 | curveTo(11.594f, 19.251f, 12.224f, 18.622f, 13.0f, 18.622f) 33 | curveTo(13.776f, 18.622f, 14.405f, 19.251f, 14.405f, 20.027f) 34 | curveTo(14.405f, 20.803f, 13.776f, 21.432f, 13.0f, 21.432f) 35 | close() 36 | } 37 | path( 38 | fill = SolidColor(Color.Black) 39 | ) { 40 | moveTo(13.0f, 7.378f) 41 | curveTo(12.224f, 7.378f, 11.594f, 6.749f, 11.594f, 5.973f) 42 | curveTo(11.594f, 5.197f, 12.224f, 4.568f, 13.0f, 4.568f) 43 | curveTo(13.776f, 4.568f, 14.405f, 5.197f, 14.405f, 5.973f) 44 | curveTo(14.405f, 6.749f, 13.776f, 7.378f, 13.0f, 7.378f) 45 | close() 46 | } 47 | }.build() 48 | return _immersionMore!! 49 | } 50 | 51 | private var _immersionMore: ImageVector? = null -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish to GitHub Packages 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | tags: ["*"] 7 | paths: 8 | - "miuix/**" 9 | - "convention-plugins/**" 10 | - "gradle/**" 11 | - "settings.gradle.kts" 12 | - "build.gradle.kts" 13 | - "gradle.properties" 14 | 15 | concurrency: 16 | group: publish-${{ github.ref }} 17 | cancel-in-progress: true 18 | 19 | jobs: 20 | publish_snapshot: 21 | runs-on: ubuntu-latest 22 | if: github.ref_type == 'branch' 23 | permissions: 24 | contents: read 25 | packages: write 26 | 27 | steps: 28 | - name: Checkout Sources 29 | uses: actions/checkout@v6 30 | with: 31 | fetch-depth: 0 32 | 33 | - name: Setup JDK 34 | uses: actions/setup-java@v5 35 | with: 36 | distribution: 'zulu' 37 | java-version: '21' 38 | 39 | - name: Setup Gradle 40 | uses: gradle/actions/setup-gradle@v5 41 | 42 | - name: Publish SNAPSHOT to GitHub Packages 43 | run: ./gradlew --no-daemon --stacktrace publishAllPublicationsToGithubRepository --no-configuration-cache 44 | env: 45 | GITHUB_ACTOR: ${{ github.actor }} 46 | GITHUB_TOKEN: ${{ github.token }} 47 | GPG_SIGNING_KEY: ${{ secrets.GPG_SIGNING_KEY }} 48 | GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} 49 | 50 | publish_release: 51 | runs-on: ubuntu-latest 52 | if: startsWith(github.ref, 'refs/tags/') 53 | permissions: 54 | contents: read 55 | packages: write 56 | 57 | steps: 58 | - name: Checkout Sources 59 | uses: actions/checkout@v6 60 | with: 61 | fetch-depth: 0 62 | 63 | - name: Setup JDK 64 | uses: actions/setup-java@v5 65 | with: 66 | distribution: 'zulu' 67 | java-version: '21' 68 | 69 | - name: Setup Gradle 70 | uses: gradle/actions/setup-gradle@v5 71 | 72 | - name: Publish release to GitHub Packages 73 | run: ./gradlew --no-daemon --stacktrace -Prelease=true publishAllPublicationsToGithubRepository --no-configuration-cache 74 | env: 75 | GITHUB_ACTOR: ${{ github.actor }} 76 | GITHUB_TOKEN: ${{ github.token }} 77 | GPG_SIGNING_KEY: ${{ secrets.GPG_SIGNING_KEY }} 78 | GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} -------------------------------------------------------------------------------- /docs/demo/src/commonMain/kotlin/SmallTitleDemo.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import androidx.compose.foundation.background 5 | import androidx.compose.foundation.layout.Arrangement 6 | import androidx.compose.foundation.layout.Box 7 | import androidx.compose.foundation.layout.Column 8 | import androidx.compose.foundation.layout.PaddingValues 9 | import androidx.compose.foundation.layout.fillMaxSize 10 | import androidx.compose.foundation.layout.fillMaxWidth 11 | import androidx.compose.foundation.layout.padding 12 | import androidx.compose.foundation.layout.widthIn 13 | import androidx.compose.runtime.Composable 14 | import androidx.compose.ui.Alignment 15 | import androidx.compose.ui.Modifier 16 | import androidx.compose.ui.graphics.Brush 17 | import androidx.compose.ui.graphics.Color 18 | import androidx.compose.ui.unit.dp 19 | import top.yukonga.miuix.kmp.basic.Card 20 | import top.yukonga.miuix.kmp.basic.SmallTitle 21 | import top.yukonga.miuix.kmp.basic.Surface 22 | import top.yukonga.miuix.kmp.basic.Text 23 | 24 | @Composable 25 | fun SmallTitleDemo() { 26 | Box( 27 | modifier = Modifier 28 | .fillMaxSize() 29 | .background(Brush.linearGradient(listOf(Color(0xfff77062), Color(0xfffe5196)))), 30 | contentAlignment = Alignment.Center 31 | ) { 32 | 33 | Column( 34 | Modifier 35 | .padding(16.dp) 36 | .widthIn(max = 600.dp) 37 | .fillMaxWidth(), 38 | verticalArrangement = Arrangement.spacedBy(16.dp), 39 | horizontalAlignment = Alignment.CenterHorizontally 40 | ) { 41 | Card { 42 | Surface { 43 | Column { 44 | SmallTitle( 45 | text = "Small Title" 46 | ) 47 | Card( 48 | modifier = Modifier 49 | .padding(horizontal = 12.dp) 50 | .padding(bottom = 12.dp), 51 | insideMargin = PaddingValues(16.dp) 52 | ) { 53 | Text("This is a card with a Text inside it.") 54 | } 55 | } 56 | } 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /docs/demo/src/commonMain/kotlin/TabRowDemo.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import androidx.compose.foundation.background 5 | import androidx.compose.foundation.layout.Arrangement 6 | import androidx.compose.foundation.layout.Box 7 | import androidx.compose.foundation.layout.Column 8 | import androidx.compose.foundation.layout.fillMaxSize 9 | import androidx.compose.foundation.layout.fillMaxWidth 10 | import androidx.compose.foundation.layout.padding 11 | import androidx.compose.foundation.layout.widthIn 12 | import androidx.compose.runtime.Composable 13 | import androidx.compose.runtime.getValue 14 | import androidx.compose.runtime.mutableStateOf 15 | import androidx.compose.runtime.remember 16 | import androidx.compose.runtime.setValue 17 | import androidx.compose.ui.Alignment 18 | import androidx.compose.ui.Modifier 19 | import androidx.compose.ui.graphics.Brush 20 | import androidx.compose.ui.graphics.Color 21 | import androidx.compose.ui.unit.dp 22 | import top.yukonga.miuix.kmp.basic.TabRow 23 | import top.yukonga.miuix.kmp.basic.TabRowWithContour 24 | 25 | @Composable 26 | fun TabRowDemo() { 27 | Box( 28 | modifier = Modifier 29 | .fillMaxSize() 30 | .background(Brush.linearGradient(listOf(Color(0xfff77062), Color(0xfffe5196)))), 31 | contentAlignment = Alignment.Center 32 | ) { 33 | Column( 34 | Modifier 35 | .padding(16.dp) 36 | .widthIn(max = 600.dp) 37 | .fillMaxWidth(), 38 | verticalArrangement = Arrangement.spacedBy(16.dp), 39 | horizontalAlignment = Alignment.CenterHorizontally 40 | ) { 41 | val tabs1 = listOf("Recommended", "Following", "Popular", "Featured") 42 | var selectedTabIndex1 by remember { mutableStateOf(0) } 43 | 44 | TabRow( 45 | tabs = tabs1, 46 | selectedTabIndex = selectedTabIndex1, 47 | onTabSelected = { selectedTabIndex1 = it } 48 | ) 49 | val tabs2 = listOf("All", "Photos", "Videos", "Documents") 50 | var selectedTabIndex2 by remember { mutableStateOf(0) } 51 | 52 | TabRowWithContour( 53 | tabs = tabs2, 54 | selectedTabIndex = selectedTabIndex2, 55 | onTabSelected = { selectedTabIndex2 = it } 56 | ) 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/icon/icons/basic/Search.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.icon.icons.basic 5 | 6 | import androidx.compose.ui.graphics.Color 7 | import androidx.compose.ui.graphics.PathFillType 8 | import androidx.compose.ui.graphics.SolidColor 9 | import androidx.compose.ui.graphics.vector.ImageVector 10 | import androidx.compose.ui.graphics.vector.path 11 | import androidx.compose.ui.unit.dp 12 | import top.yukonga.miuix.kmp.icon.MiuixIcons 13 | 14 | val MiuixIcons.Basic.Search: ImageVector 15 | get() { 16 | if (_search != null) return _search!! 17 | _search = ImageVector.Builder("Search", 20.0f.dp, 20.0f.dp, 20.0f, 20.0f).apply { 18 | path( 19 | fill = SolidColor(Color.White), 20 | pathFillType = PathFillType.EvenOdd 21 | ) { 22 | moveTo(12.572f, 13.379f) 23 | curveTo(11.541f, 14.183f, 10.244f, 14.662f, 8.835f, 14.662f) 24 | curveTo(5.477f, 14.662f, 2.754f, 11.94f, 2.754f, 8.581f) 25 | curveTo(2.754f, 5.223f, 5.477f, 2.5f, 8.835f, 2.5f) 26 | curveTo(12.194f, 2.5f, 14.916f, 5.223f, 14.916f, 8.581f) 27 | curveTo(14.916f, 9.99f, 14.437f, 11.287f, 13.633f, 12.318f) 28 | lineTo(17.464f, 16.149f) 29 | curveTo(17.563f, 16.248f, 17.612f, 16.297f, 17.645f, 16.346f) 30 | curveTo(17.78f, 16.548f, 17.78f, 16.811f, 17.645f, 17.013f) 31 | curveTo(17.612f, 17.062f, 17.563f, 17.111f, 17.464f, 17.21f) 32 | curveTo(17.366f, 17.308f, 17.316f, 17.358f, 17.267f, 17.39f) 33 | curveTo(17.065f, 17.525f, 16.802f, 17.525f, 16.601f, 17.39f) 34 | curveTo(16.551f, 17.358f, 16.502f, 17.308f, 16.403f, 17.21f) 35 | lineTo(12.572f, 13.379f) 36 | close() 37 | moveTo(13.416f, 8.581f) 38 | curveTo(13.416f, 11.111f, 11.365f, 13.162f, 8.835f, 13.162f) 39 | curveTo(6.305f, 13.162f, 4.254f, 11.111f, 4.254f, 8.581f) 40 | curveTo(4.254f, 6.051f, 6.305f, 4f, 8.835f, 4f) 41 | curveTo(11.365f, 4f, 13.416f, 6.051f, 13.416f, 8.581f) 42 | close() 43 | } 44 | }.build() 45 | return _search!! 46 | } 47 | 48 | private var _search: ImageVector? = null -------------------------------------------------------------------------------- /miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/icon/icons/useful/Search.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.icon.icons.useful 5 | 6 | import androidx.compose.ui.graphics.Color 7 | import androidx.compose.ui.graphics.PathFillType 8 | import androidx.compose.ui.graphics.SolidColor 9 | import androidx.compose.ui.graphics.vector.ImageVector 10 | import androidx.compose.ui.graphics.vector.path 11 | import androidx.compose.ui.unit.dp 12 | import top.yukonga.miuix.kmp.icon.MiuixIcons 13 | 14 | val MiuixIcons.Useful.Search: ImageVector 15 | get() { 16 | if (_search != null) return _search!! 17 | _search = ImageVector.Builder("Search", 26.0.dp, 26.0.dp, 26.0f, 26.0f).apply { 18 | path( 19 | fill = SolidColor(Color.Black), 20 | pathFillType = PathFillType.EvenOdd 21 | ) { 22 | moveTo(15.712f, 16.843f) 23 | curveTo(14.35f, 17.955f, 12.611f, 18.622f, 10.716f, 18.622f) 24 | curveTo(6.35f, 18.622f, 2.811f, 15.082f, 2.811f, 10.716f) 25 | curveTo(2.811f, 6.35f, 6.35f, 2.811f, 10.716f, 2.811f) 26 | curveTo(15.082f, 2.811f, 18.622f, 6.35f, 18.622f, 10.716f) 27 | curveTo(18.622f, 12.611f, 17.955f, 14.35f, 16.843f, 15.712f) 28 | lineTo(22.32f, 21.189f) 29 | curveTo(22.477f, 21.345f, 22.555f, 21.424f, 22.596f, 21.508f) 30 | curveTo(22.672f, 21.664f, 22.672f, 21.845f, 22.596f, 22.001f) 31 | curveTo(22.555f, 22.085f, 22.477f, 22.164f, 22.32f, 22.32f) 32 | curveTo(22.164f, 22.477f, 22.085f, 22.555f, 22.001f, 22.596f) 33 | curveTo(21.845f, 22.672f, 21.664f, 22.672f, 21.508f, 22.596f) 34 | curveTo(21.424f, 22.555f, 21.345f, 22.477f, 21.189f, 22.32f) 35 | lineTo(15.712f, 16.843f) 36 | close() 37 | moveTo(17.022f, 10.716f) 38 | curveTo(17.022f, 14.199f, 14.199f, 17.022f, 10.716f, 17.022f) 39 | curveTo(7.234f, 17.022f, 4.411f, 14.199f, 4.411f, 10.716f) 40 | curveTo(4.411f, 7.234f, 7.234f, 4.411f, 10.716f, 4.411f) 41 | curveTo(14.199f, 4.411f, 17.022f, 7.234f, 17.022f, 10.716f) 42 | close() 43 | } 44 | }.build() 45 | return _search!! 46 | } 47 | 48 | private var _search: ImageVector? = null 49 | -------------------------------------------------------------------------------- /docs/guide/multiplatform.md: -------------------------------------------------------------------------------- 1 | # Platform Support 2 | 3 | Miuix is a Compose Multiplatform UI framework that supports multiple platforms, allowing you to build applications for different platforms using the same codebase. 4 | 5 | ## Supported Platforms 6 | 7 | Currently, Miuix supports the following platforms: 8 | 9 | - **Android**: For Android mobile devices 10 | - **iOS**: For iPhone and iPad devices 11 | - **Desktop (JVM)**: For JVM-based desktop applications 12 | - **WasmJs**: For WebAssembly (Web) environments 13 | - **MacOS**: For native macOS applications 14 | - **Js**: For JavaScript (Web) environments 15 | 16 | ## Platform Detection and Adaptation 17 | 18 | You can use the `platform()` function to detect the current running platform and adjust the UI or functionality accordingly: 19 | 20 | ```kotlin 21 | when (platform()) { 22 | Platform.Android -> { 23 | // Android-specific code 24 | } 25 | Platform.IOS -> { 26 | // iOS-specific code 27 | } 28 | Platform.Desktop -> { 29 | // JVM-specific code 30 | } 31 | Platform.WasmJs -> { 32 | // WebAssembly-specific code 33 | } 34 | Platform.MacOS -> { 35 | // macOS-specific code 36 | } 37 | Platform.Js -> { 38 | // JavaScript-specific code 39 | } 40 | } 41 | ``` 42 | 43 | ## Window Size Management 44 | 45 | Miuix provides cross-platform functionality to retrieve window sizes: 46 | 47 | ```kotlin 48 | @Composable 49 | fun MyResponsiveContent() { 50 | val windowSize = getWindowSize() 51 | val width = windowSize.width 52 | val height = windowSize.height 53 | // Adjust UI layout based on window size 54 | Surface( 55 | modifier = Modifier.fillMaxSize(), 56 | ) { 57 | if (width > 600) { 58 | Text("Wide Layout") 59 | } else { 60 | Text("Narrow Layout") 61 | } 62 | Text("\nHeight: $height") 63 | } 64 | } 65 | ``` 66 | 67 | ## Device Rounded Corners 68 | 69 | Android devices have varying screen corner radii, while other platforms may not have rounded corners. You can use the `getRoundedCorner()` function to retrieve the corner radius of the device (using a preset value if unavailable): 70 | 71 | ```kotlin 72 | @Composable 73 | fun AdaptiveRoundedComponent() { 74 | val cornerRadius = getRoundedCorner() 75 | Surface( 76 | shape = RoundedCornerShape(cornerRadius), 77 | // Other properties 78 | ) { 79 | // Content 80 | } 81 | } 82 | ``` 83 | -------------------------------------------------------------------------------- /miuix/build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl 5 | 6 | plugins { 7 | alias(libs.plugins.android.kotlin.multiplatform.library) 8 | alias(libs.plugins.compose.compiler) 9 | alias(libs.plugins.jetbrains.compose) 10 | alias(libs.plugins.jetbrains.dokka) 11 | alias(libs.plugins.kotlin.multiplatform) 12 | alias(libs.plugins.spotless) 13 | id("module.publication") 14 | } 15 | 16 | java { 17 | toolchain.languageVersion = JavaLanguageVersion.of(21) 18 | } 19 | 20 | kotlin { 21 | jvmToolchain(21) 22 | withSourcesJar(true) 23 | 24 | android { 25 | namespace = "top.yukonga.miuix.kmp" 26 | } 27 | 28 | jvm("desktop") 29 | 30 | listOf( 31 | iosArm64(), 32 | iosSimulatorArm64(), 33 | macosArm64(), 34 | ).forEach { 35 | it.compilerOptions { 36 | freeCompilerArgs.add("-Xbinary=preCodegenInlineThreshold=40") 37 | } 38 | } 39 | 40 | @OptIn(ExperimentalWasmDsl::class) 41 | wasmJs { 42 | browser() 43 | } 44 | 45 | js(IR) { 46 | browser() 47 | compilerOptions { 48 | freeCompilerArgs.add("-Xes-long-as-bigint") 49 | freeCompilerArgs.add("-XXLanguage:+JsAllowLongInExportedDeclarations") 50 | } 51 | } 52 | 53 | sourceSets { 54 | androidMain.dependencies { 55 | implementation(libs.androidx.window) // Android WindowMetrics 56 | } 57 | commonMain.dependencies { 58 | implementation(compose.runtime) 59 | implementation(compose.foundation) 60 | implementation(compose.ui) 61 | implementation(compose.material3) 62 | 63 | implementation(libs.jetbrains.compose.ui.backhandler) 64 | implementation(libs.jetbrains.compose.window.size) 65 | 66 | implementation(libs.gaze.capsule) // Capsule for Multiplatform 67 | implementation(libs.materialkolor.material.kolor) // Material Color for Multiplatform 68 | } 69 | } 70 | } 71 | 72 | spotless { 73 | kotlin { 74 | target("src/**/*.kt") 75 | licenseHeaderFile(rootProject.file("./spotless/copyright.txt"), "(^(?![\\/ ]\\**).*$)") 76 | } 77 | 78 | kotlinGradle { 79 | target("*.kts") 80 | licenseHeaderFile(rootProject.file("./spotless/copyright.txt"), "(^(?![\\/ ]\\**).*$)") 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /docs/demo/src/commonMain/kotlin/SuperArrowDemo.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import androidx.compose.foundation.background 5 | import androidx.compose.foundation.layout.Arrangement 6 | import androidx.compose.foundation.layout.Box 7 | import androidx.compose.foundation.layout.Column 8 | import androidx.compose.foundation.layout.Row 9 | import androidx.compose.foundation.layout.fillMaxSize 10 | import androidx.compose.foundation.layout.fillMaxWidth 11 | import androidx.compose.foundation.layout.padding 12 | import androidx.compose.foundation.layout.widthIn 13 | import androidx.compose.runtime.Composable 14 | import androidx.compose.ui.Alignment 15 | import androidx.compose.ui.Modifier 16 | import androidx.compose.ui.graphics.Brush 17 | import androidx.compose.ui.graphics.Color 18 | import androidx.compose.ui.unit.dp 19 | import top.yukonga.miuix.kmp.basic.Card 20 | import top.yukonga.miuix.kmp.extra.SuperArrow 21 | 22 | @Composable 23 | fun SuperArrowDemo() { 24 | Box( 25 | modifier = Modifier 26 | .fillMaxSize() 27 | .background(Brush.linearGradient(listOf(Color(0xfff77062), Color(0xfffe5196)))), 28 | contentAlignment = Alignment.Center 29 | ) { 30 | Column( 31 | Modifier 32 | .padding(16.dp) 33 | .widthIn(max = 600.dp) 34 | .fillMaxWidth(), 35 | verticalArrangement = Arrangement.spacedBy(16.dp), 36 | horizontalAlignment = Alignment.CenterHorizontally 37 | ) { 38 | Row( 39 | horizontalArrangement = Arrangement.spacedBy(16.dp), 40 | verticalAlignment = Alignment.CenterVertically 41 | ) { 42 | Card { 43 | SuperArrow( 44 | title = "Setting Item", 45 | onClick = { /* Handle click event */ } 46 | ) 47 | SuperArrow( 48 | title = "Wireless Network", 49 | summary = "Connected to WIFI-HOME", 50 | onClick = { /* Handle click event */ } 51 | ) 52 | SuperArrow( 53 | title = "Disabled Item", 54 | summary = "This item is currently unavailable", 55 | enabled = false, 56 | onClick = { /* Won't be triggered */ } 57 | ) 58 | } 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /docs/demo/src/commonMain/kotlin/SwitchDemo.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import androidx.compose.foundation.background 5 | import androidx.compose.foundation.layout.Arrangement 6 | import androidx.compose.foundation.layout.Box 7 | import androidx.compose.foundation.layout.Column 8 | import androidx.compose.foundation.layout.Row 9 | import androidx.compose.foundation.layout.fillMaxSize 10 | import androidx.compose.foundation.layout.fillMaxWidth 11 | import androidx.compose.foundation.layout.padding 12 | import androidx.compose.foundation.layout.widthIn 13 | import androidx.compose.runtime.Composable 14 | import androidx.compose.runtime.getValue 15 | import androidx.compose.runtime.mutableStateOf 16 | import androidx.compose.runtime.remember 17 | import androidx.compose.runtime.setValue 18 | import androidx.compose.ui.Alignment 19 | import androidx.compose.ui.Modifier 20 | import androidx.compose.ui.graphics.Brush 21 | import androidx.compose.ui.graphics.Color 22 | import androidx.compose.ui.unit.dp 23 | import top.yukonga.miuix.kmp.basic.Switch 24 | 25 | @Composable 26 | fun SwitchDemo() { 27 | Box( 28 | modifier = Modifier 29 | .fillMaxSize() 30 | .background(Brush.linearGradient(listOf(Color(0xfff77062), Color(0xfffe5196)))), 31 | contentAlignment = Alignment.Center 32 | ) { 33 | Column( 34 | Modifier 35 | .padding(16.dp) 36 | .widthIn(max = 600.dp) 37 | .fillMaxWidth(), 38 | verticalArrangement = Arrangement.spacedBy(16.dp), 39 | horizontalAlignment = Alignment.CenterHorizontally 40 | ) { 41 | var switch1 by remember { mutableStateOf(false) } 42 | var switch2 by remember { mutableStateOf(true) } 43 | Row( 44 | horizontalArrangement = Arrangement.spacedBy(32.dp), 45 | ) { 46 | Switch( 47 | checked = switch1, 48 | onCheckedChange = { switch1 = it } 49 | ) 50 | Switch( 51 | checked = switch2, 52 | onCheckedChange = { switch2 = it } 53 | ) 54 | Switch( 55 | checked = false, 56 | onCheckedChange = { }, 57 | enabled = false 58 | ) 59 | Switch( 60 | checked = true, 61 | onCheckedChange = { }, 62 | enabled = false 63 | ) 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /docs/demo/src/commonMain/kotlin/SurfaceDemo.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import androidx.compose.foundation.background 5 | import androidx.compose.foundation.isSystemInDarkTheme 6 | import androidx.compose.foundation.layout.Arrangement 7 | import androidx.compose.foundation.layout.Box 8 | import androidx.compose.foundation.layout.Column 9 | import androidx.compose.foundation.layout.fillMaxSize 10 | import androidx.compose.foundation.layout.fillMaxWidth 11 | import androidx.compose.foundation.layout.padding 12 | import androidx.compose.foundation.layout.size 13 | import androidx.compose.foundation.layout.widthIn 14 | import androidx.compose.foundation.shape.RoundedCornerShape 15 | import androidx.compose.runtime.Composable 16 | import androidx.compose.ui.Alignment 17 | import androidx.compose.ui.Modifier 18 | import androidx.compose.ui.graphics.Brush 19 | import androidx.compose.ui.graphics.Color 20 | import androidx.compose.ui.unit.dp 21 | import top.yukonga.miuix.kmp.basic.Surface 22 | import top.yukonga.miuix.kmp.basic.Text 23 | import top.yukonga.miuix.kmp.theme.MiuixTheme 24 | 25 | @Composable 26 | fun SurfaceDemo() { 27 | Box( 28 | modifier = Modifier 29 | .fillMaxSize() 30 | .background(Brush.linearGradient(listOf(Color(0xfff77062), Color(0xfffe5196)))), 31 | contentAlignment = Alignment.Center 32 | ) { 33 | Column( 34 | Modifier 35 | .padding(16.dp) 36 | .widthIn(max = 600.dp) 37 | .fillMaxWidth(), 38 | verticalArrangement = Arrangement.spacedBy(16.dp), 39 | horizontalAlignment = Alignment.CenterHorizontally 40 | ) { 41 | Surface( 42 | modifier = Modifier 43 | .size(height = 200.dp, width = 400.dp) 44 | .padding(16.dp), 45 | color = MiuixTheme.colorScheme.background, 46 | shape = RoundedCornerShape(16.dp), 47 | shadowElevation = 4.dp 48 | ) { 49 | Text( 50 | text = "Surface Example\n\n" + 51 | "Size: height = 200.dp, width = 400.dp\n" + 52 | "color: MiuixTheme.colorScheme.background\n" + 53 | "shape: RoundedCornerShape(16.dp)\n" + 54 | "shadowElevation: 4.dp\n" + 55 | "isSystemInDarkTheme: ${isSystemInDarkTheme()}", 56 | modifier = Modifier.padding(16.dp).fillMaxSize() 57 | ) 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /docs/demo/src/commonMain/kotlin/CheckboxDemo.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import androidx.compose.foundation.background 5 | import androidx.compose.foundation.layout.Arrangement 6 | import androidx.compose.foundation.layout.Box 7 | import androidx.compose.foundation.layout.Column 8 | import androidx.compose.foundation.layout.Row 9 | import androidx.compose.foundation.layout.fillMaxSize 10 | import androidx.compose.foundation.layout.fillMaxWidth 11 | import androidx.compose.foundation.layout.padding 12 | import androidx.compose.foundation.layout.widthIn 13 | import androidx.compose.runtime.Composable 14 | import androidx.compose.runtime.getValue 15 | import androidx.compose.runtime.mutableStateOf 16 | import androidx.compose.runtime.remember 17 | import androidx.compose.runtime.setValue 18 | import androidx.compose.ui.Alignment 19 | import androidx.compose.ui.Modifier 20 | import androidx.compose.ui.graphics.Brush 21 | import androidx.compose.ui.graphics.Color 22 | import androidx.compose.ui.unit.dp 23 | import top.yukonga.miuix.kmp.basic.Checkbox 24 | 25 | @Composable 26 | fun CheckboxDemo() { 27 | Box( 28 | modifier = Modifier 29 | .fillMaxSize() 30 | .background(Brush.linearGradient(listOf(Color(0xfff77062), Color(0xfffe5196)))), 31 | contentAlignment = Alignment.Center 32 | ) { 33 | Column( 34 | Modifier 35 | .padding(16.dp) 36 | .widthIn(max = 600.dp) 37 | .fillMaxWidth(), 38 | verticalArrangement = Arrangement.spacedBy(16.dp), 39 | horizontalAlignment = Alignment.CenterHorizontally 40 | ) { 41 | var checkbox1 by remember { mutableStateOf(false) } 42 | var checkbox2 by remember { mutableStateOf(true) } 43 | Row( 44 | horizontalArrangement = Arrangement.spacedBy(32.dp), 45 | ) { 46 | Checkbox( 47 | checked = checkbox1, 48 | onCheckedChange = { checkbox1 = it } 49 | ) 50 | Checkbox( 51 | checked = checkbox2, 52 | onCheckedChange = { checkbox2 = it } 53 | ) 54 | Checkbox( 55 | checked = false, 56 | onCheckedChange = { }, 57 | enabled = false 58 | ) 59 | Checkbox( 60 | checked = true, 61 | onCheckedChange = { }, 62 | enabled = false 63 | ) 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /docs/demo/src/commonMain/kotlin/WindowDialogDemo.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import androidx.compose.foundation.background 5 | import androidx.compose.foundation.layout.Arrangement 6 | import androidx.compose.foundation.layout.Box 7 | import androidx.compose.foundation.layout.Column 8 | import androidx.compose.foundation.layout.fillMaxSize 9 | import androidx.compose.foundation.layout.fillMaxWidth 10 | import androidx.compose.foundation.layout.padding 11 | import androidx.compose.foundation.layout.widthIn 12 | import androidx.compose.runtime.Composable 13 | import androidx.compose.runtime.mutableStateOf 14 | import androidx.compose.runtime.remember 15 | import androidx.compose.ui.Alignment 16 | import androidx.compose.ui.Modifier 17 | import androidx.compose.ui.graphics.Brush 18 | import androidx.compose.ui.graphics.Color 19 | import androidx.compose.ui.unit.dp 20 | import top.yukonga.miuix.kmp.basic.Card 21 | import top.yukonga.miuix.kmp.basic.TextButton 22 | import top.yukonga.miuix.kmp.extra.LocalWindowDialogState 23 | import top.yukonga.miuix.kmp.extra.WindowDialog 24 | 25 | @Composable 26 | fun WindowDialogDemo() { 27 | Box( 28 | modifier = Modifier 29 | .fillMaxSize() 30 | .background(Brush.linearGradient(listOf(Color(0xfff77062), Color(0xfffe5196)))), 31 | contentAlignment = Alignment.Center 32 | ) { 33 | Column( 34 | Modifier 35 | .padding(16.dp) 36 | .widthIn(max = 600.dp) 37 | .fillMaxWidth(), 38 | verticalArrangement = Arrangement.spacedBy(16.dp), 39 | horizontalAlignment = Alignment.CenterHorizontally 40 | ) { 41 | val showDialog = remember { mutableStateOf(false) } 42 | Card { 43 | TextButton( 44 | text = "Show a WindowDialog", 45 | onClick = { showDialog.value = true } 46 | ) 47 | WindowDialog( 48 | title = "WindowDialog Title", 49 | summary = "This is a window-level dialog that does not require MiuixPopupHost.", 50 | show = showDialog, 51 | onDismissRequest = { showDialog.value = false } 52 | ) { 53 | val dismiss = LocalWindowDialogState.current 54 | TextButton( 55 | text = "Confirm", 56 | onClick = { dismiss?.invoke() }, 57 | modifier = Modifier.fillMaxWidth() 58 | ) 59 | } 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /docs/zh_CN/components/smalltitle.md: -------------------------------------------------------------------------------- 1 | # SmallTitle 2 | 3 | `SmallTitle` 是 Miuix 中的基础小标题组件,用于快速创建小型标题文本。采用 Miuix 的设计风格,具有预设的字体大小、字重和内边距。 4 | 5 |
6 | 7 |
8 | 9 | ## 引入 10 | 11 | ```kotlin 12 | import top.yukonga.miuix.kmp.basic.SmallTitle 13 | ``` 14 | 15 | ## 基本用法 16 | 17 | SmallTitle 组件可以用于展示小型标题文本: 18 | 19 | ```kotlin 20 | SmallTitle( 21 | text = "小标题" 22 | ) 23 | ``` 24 | 25 | ## 自定义外观 26 | 27 | ### 自定义颜色 28 | 29 | ```kotlin 30 | SmallTitle( 31 | text = "自定义颜色的小标题", 32 | textColor = Color.Red 33 | ) 34 | ``` 35 | 36 | ### 自定义内边距 37 | 38 | ```kotlin 39 | SmallTitle( 40 | text = "自定义内边距的小标题", 41 | insideMargin = PaddingValues(16.dp, 4.dp) 42 | ) 43 | ``` 44 | 45 | ## 属性 46 | 47 | ### SmallTitle 属性 48 | 49 | | 属性名 | 类型 | 说明 | 默认值 | 是否必须 | 50 | | ------------ | ------------- | ------------------ | ------------------------------------------ | -------- | 51 | | text | String | 要显示的文本内容 | - | 是 | 52 | | modifier | Modifier | 应用于组件的修饰符 | Modifier | 否 | 53 | | textColor | Color | 标题文本颜色 | MiuixTheme.colorScheme.onBackgroundVariant | 否 | 54 | | insideMargin | PaddingValues | 组件内部边距 | PaddingValues(28.dp, 8.dp) | 否 | 55 | 56 | ## 进阶用法 57 | 58 | ### 与其他组件组合使用 59 | 60 | ```kotlin 61 | var checked by remember { mutableStateOf(false) } 62 | 63 | Column { 64 | SmallTitle(text = "设置") 65 | Card( 66 | modifier = Modifier.padding(horizontal = 12.dp).padding(bottom = 12.dp) 67 | ) { 68 | SuperSwitch( 69 | title = "蓝牙", 70 | checked = checked, 71 | onCheckedChange = { checked = it } 72 | ) 73 | } 74 | HorizontalDivider( 75 | modifier = Modifier.padding(horizontal = 12.dp) 76 | ) 77 | SmallTitle(text = "高级设置") 78 | // 其他设置项 79 | } 80 | ``` 81 | 82 | ### 自定义样式 83 | 84 | ```kotlin 85 | SmallTitle( 86 | text = "完全自定义样式", 87 | modifier = Modifier 88 | .fillMaxWidth() 89 | .background(Color.LightGray), 90 | textColor = Color.Blue, 91 | insideMargin = PaddingValues(32.dp, 12.dp) 92 | ) 93 | ``` -------------------------------------------------------------------------------- /miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/FloatingActionButton.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.basic 5 | 6 | import androidx.compose.foundation.layout.Box 7 | import androidx.compose.foundation.layout.defaultMinSize 8 | import androidx.compose.runtime.Composable 9 | import androidx.compose.runtime.NonRestartableComposable 10 | import androidx.compose.runtime.getValue 11 | import androidx.compose.runtime.rememberUpdatedState 12 | import androidx.compose.ui.Alignment 13 | import androidx.compose.ui.Modifier 14 | import androidx.compose.ui.graphics.Color 15 | import androidx.compose.ui.graphics.Shape 16 | import androidx.compose.ui.semantics.Role 17 | import androidx.compose.ui.semantics.role 18 | import androidx.compose.ui.semantics.semantics 19 | import androidx.compose.ui.unit.Dp 20 | import androidx.compose.ui.unit.dp 21 | import com.mocharealm.gaze.capsule.ContinuousCapsule 22 | import top.yukonga.miuix.kmp.theme.MiuixTheme 23 | 24 | /** 25 | * A [FloatingActionButton] component with Miuix style. 26 | * 27 | * @param onClick The callback when the [FloatingActionButton] is clicked. 28 | * @param modifier The modifier to be applied to the [FloatingActionButton]. 29 | * @param shape The shape of the [FloatingActionButton]. 30 | * @param containerColor The color of the [FloatingActionButton]. 31 | * @param shadowElevation The shadow elevation of the [FloatingActionButton]. 32 | * @param minWidth The minimum width of the [FloatingActionButton]. 33 | * @param minHeight The minimum height of the [FloatingActionButton]. 34 | * @param content The [Composable] content of the [FloatingActionButton]. 35 | */ 36 | @Composable 37 | @NonRestartableComposable 38 | fun FloatingActionButton( 39 | onClick: () -> Unit, 40 | modifier: Modifier = Modifier, 41 | shape: Shape = ContinuousCapsule, 42 | containerColor: Color = MiuixTheme.colorScheme.primary, 43 | shadowElevation: Dp = 4.dp, 44 | minWidth: Dp = 60.dp, 45 | minHeight: Dp = 60.dp, 46 | content: @Composable () -> Unit, 47 | ) { 48 | val currentOnClick by rememberUpdatedState(onClick) 49 | 50 | Surface( 51 | onClick = currentOnClick, 52 | modifier = modifier 53 | .semantics { role = Role.Button }, 54 | shape = shape, 55 | color = containerColor, 56 | shadowElevation = shadowElevation 57 | ) { 58 | Box( 59 | modifier = Modifier.defaultMinSize( 60 | minWidth = minWidth, 61 | minHeight = minHeight, 62 | ), 63 | contentAlignment = Alignment.Center, 64 | ) { 65 | content() 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /docs/demo/src/commonMain/kotlin/TextFieldDemo.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import androidx.compose.foundation.background 5 | import androidx.compose.foundation.layout.Arrangement 6 | import androidx.compose.foundation.layout.Box 7 | import androidx.compose.foundation.layout.Column 8 | import androidx.compose.foundation.layout.fillMaxSize 9 | import androidx.compose.foundation.layout.fillMaxWidth 10 | import androidx.compose.foundation.layout.padding 11 | import androidx.compose.foundation.layout.widthIn 12 | import androidx.compose.runtime.Composable 13 | import androidx.compose.runtime.getValue 14 | import androidx.compose.runtime.mutableStateOf 15 | import androidx.compose.runtime.remember 16 | import androidx.compose.runtime.setValue 17 | import androidx.compose.ui.Alignment 18 | import androidx.compose.ui.Modifier 19 | import androidx.compose.ui.graphics.Brush 20 | import androidx.compose.ui.graphics.Color 21 | import androidx.compose.ui.unit.dp 22 | import top.yukonga.miuix.kmp.basic.TextField 23 | 24 | @Composable 25 | fun TextFieldDemo() { 26 | Box( 27 | modifier = Modifier 28 | .fillMaxSize() 29 | .background(Brush.linearGradient(listOf(Color(0xfff77062), Color(0xfffe5196)))), 30 | contentAlignment = Alignment.Center 31 | ) { 32 | Column( 33 | Modifier 34 | .padding(16.dp) 35 | .widthIn(max = 600.dp) 36 | .fillMaxWidth(), 37 | verticalArrangement = Arrangement.spacedBy(16.dp), 38 | horizontalAlignment = Alignment.CenterHorizontally 39 | ) { 40 | var text1 by remember { mutableStateOf("") } 41 | var text2 by remember { mutableStateOf("") } 42 | var text3 by remember { mutableStateOf("This is read-only content") } 43 | 44 | TextField( 45 | value = text1, 46 | onValueChange = { text1 = it }, 47 | label = "Username" 48 | ) 49 | 50 | TextField( 51 | value = text2, 52 | onValueChange = { text2 = it }, 53 | label = "Please enter content", 54 | useLabelAsPlaceholder = true 55 | ) 56 | TextField( 57 | value = "", 58 | onValueChange = { }, 59 | label = "Disabled Input Field", 60 | enabled = false 61 | ) 62 | TextField( 63 | value = text3, 64 | onValueChange = { text3 = it }, 65 | label = "Read-Only Input Field", 66 | readOnly = true 67 | ) 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /docs/guide/theme.md: -------------------------------------------------------------------------------- 1 | # Theme System 2 | 3 | Miuix provides a complete theme system that allows you to easily maintain a consistent design style 4 | throughout your application. The theme system consists of color schemes and text styles. 5 | 6 | ## Using MiuixTheme 7 | 8 | Use `ThemeController` to control the color scheme mode, then wrap your content with `MiuixTheme`: 9 | 10 | ```kotlin 11 | @Composable 12 | fun App() { 13 | // Available modes: System, Light, Dark, MonetSystem, MonetLight, MonetDark 14 | val controller = remember { ThemeController(ColorSchemeMode.System) } 15 | MiuixTheme(controller = controller) { /* Content */ } 16 | } 17 | ``` 18 | 19 | `ColorSchemeMode.System` / `ColorSchemeMode.MonetSystem` automatically follows the system’s dark mode. 20 | 21 | ### Specific Modes 22 | 23 | - Use `ThemeController` to control modes and enable Monet dynamic colors; Monet modes support a custom seed color via `keyColor`: 24 | 25 | ```kotlin 26 | @Composable 27 | fun AppWithMonet() { 28 | val controller = remember { 29 | ThemeController( 30 | ColorSchemeMode.MonetSystem, // or MonetLight, MonetDark 31 | keyColor = Color(0xFF3482FF) // Custom seed color 32 | ) 33 | } 34 | MiuixTheme(controller = controller) { /* Content */ } 35 | } 36 | ``` 37 | 38 | - Provide a color scheme directly to `MiuixTheme(colors = ...)` for full customization or to use built-in light/dark schemes: 39 | 40 | ```kotlin 41 | @Composable 42 | fun AppWithColors() { 43 | val colors = lightColorScheme() // or darkColorScheme() 44 | MiuixTheme(colors = colors) { /* Content */ } 45 | } 46 | ``` 47 | 48 | ## Customizing the Theme 49 | 50 | You can customize the theme in the following ways: 51 | 52 | - Select a color scheme mode via `ThemeController(ColorSchemeMode.*)`. 53 | - Opt into dynamic colors via `MonetSystem` / `MonetLight` / `MonetDark`. 54 | - Override text styles by passing `textStyles`: 55 | 56 | ```kotlin 57 | val customTextStyles = defaultTextStyles( 58 | title1 = TextStyle( 59 | fontSize = 36.sp, 60 | fontWeight = FontWeight.Bold 61 | ), 62 | // Other text styles... 63 | ) 64 | 65 | val controller = remember { ThemeController(ColorSchemeMode.Light) } 66 | MiuixTheme( 67 | controller = controller, 68 | textStyles = customTextStyles 69 | ) { 70 | // Your application content 71 | } 72 | ``` 73 | 74 | ## Follow System Dark Mode 75 | 76 | Following the system’s dark mode is built-in. Use `ColorSchemeMode.System`: 77 | 78 | ```kotlin 79 | @Composable 80 | fun MyApp() { 81 | val controller = remember { ThemeController(ColorSchemeMode.System) } 82 | MiuixTheme(controller = controller) { 83 | // Application content 84 | } 85 | } 86 | ``` 87 | -------------------------------------------------------------------------------- /docs/demo/src/commonMain/kotlin/SuperBottomSheetDemo.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import androidx.compose.foundation.background 5 | import androidx.compose.foundation.layout.Arrangement 6 | import androidx.compose.foundation.layout.Box 7 | import androidx.compose.foundation.layout.Column 8 | import androidx.compose.foundation.layout.fillMaxSize 9 | import androidx.compose.foundation.layout.fillMaxWidth 10 | import androidx.compose.foundation.layout.padding 11 | import androidx.compose.foundation.layout.widthIn 12 | import androidx.compose.runtime.Composable 13 | import androidx.compose.runtime.mutableStateOf 14 | import androidx.compose.runtime.remember 15 | import androidx.compose.ui.Alignment 16 | import androidx.compose.ui.Modifier 17 | import androidx.compose.ui.graphics.Brush 18 | import androidx.compose.ui.graphics.Color 19 | import androidx.compose.ui.unit.dp 20 | import top.yukonga.miuix.kmp.basic.Card 21 | import top.yukonga.miuix.kmp.basic.Scaffold 22 | import top.yukonga.miuix.kmp.basic.TextButton 23 | import top.yukonga.miuix.kmp.extra.SuperBottomSheet 24 | 25 | @Composable 26 | fun SuperBottomSheetDemo() { 27 | Scaffold { 28 | Box( 29 | modifier = Modifier 30 | .fillMaxSize() 31 | .background(Brush.linearGradient(listOf(Color(0xfff77062), Color(0xfffe5196)))), 32 | contentAlignment = Alignment.Center 33 | ) { 34 | Column( 35 | Modifier 36 | .padding(16.dp) 37 | .widthIn(max = 600.dp) 38 | .fillMaxWidth(), 39 | verticalArrangement = Arrangement.spacedBy(16.dp), 40 | horizontalAlignment = Alignment.CenterHorizontally 41 | ) { 42 | val showBottomSheet = remember { mutableStateOf(false) } 43 | Card { 44 | TextButton( 45 | text = "Show a BottomSheet", 46 | onClick = { showBottomSheet.value = true } 47 | ) 48 | SuperBottomSheet( 49 | title = "BottomSheet Title", 50 | show = showBottomSheet, 51 | onDismissRequest = { showBottomSheet.value = false } // Close bottom sheet 52 | ) { 53 | TextButton( 54 | text = "Confirm", 55 | onClick = { showBottomSheet.value = false }, // Close bottom sheet 56 | modifier = Modifier.fillMaxWidth().padding(bottom = 12.dp) 57 | ) 58 | } 59 | } 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /docs/demo/src/commonMain/kotlin/SuperSwitchDemo.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import androidx.compose.foundation.background 5 | import androidx.compose.foundation.layout.Arrangement 6 | import androidx.compose.foundation.layout.Box 7 | import androidx.compose.foundation.layout.Column 8 | import androidx.compose.foundation.layout.fillMaxSize 9 | import androidx.compose.foundation.layout.fillMaxWidth 10 | import androidx.compose.foundation.layout.padding 11 | import androidx.compose.foundation.layout.widthIn 12 | import androidx.compose.runtime.Composable 13 | import androidx.compose.runtime.getValue 14 | import androidx.compose.runtime.mutableStateOf 15 | import androidx.compose.runtime.remember 16 | import androidx.compose.runtime.setValue 17 | import androidx.compose.ui.Alignment 18 | import androidx.compose.ui.Modifier 19 | import androidx.compose.ui.graphics.Brush 20 | import androidx.compose.ui.graphics.Color 21 | import androidx.compose.ui.unit.dp 22 | import top.yukonga.miuix.kmp.basic.Card 23 | import top.yukonga.miuix.kmp.extra.SuperSwitch 24 | 25 | @Composable 26 | fun SuperSwitchDemo() { 27 | Box( 28 | modifier = Modifier 29 | .fillMaxSize() 30 | .background(Brush.linearGradient(listOf(Color(0xfff77062), Color(0xfffe5196)))), 31 | contentAlignment = Alignment.Center 32 | ) { 33 | Column( 34 | Modifier 35 | .padding(16.dp) 36 | .widthIn(max = 600.dp) 37 | .fillMaxWidth(), 38 | verticalArrangement = Arrangement.spacedBy(16.dp), 39 | horizontalAlignment = Alignment.CenterHorizontally 40 | ) { 41 | var isChecked by remember { mutableStateOf(false) } 42 | var wifiEnabled by remember { mutableStateOf(false) } 43 | 44 | Card( 45 | modifier = Modifier.weight(0.5f) 46 | ) { 47 | SuperSwitch( 48 | title = "Switch Option", 49 | checked = isChecked, 50 | onCheckedChange = { isChecked = it } 51 | ) 52 | SuperSwitch( 53 | title = "WiFi", 54 | summary = "Turn on to connect to wireless networks", 55 | checked = wifiEnabled, 56 | onCheckedChange = { wifiEnabled = it } 57 | ) 58 | SuperSwitch( 59 | title = "Disabled Switch", 60 | summary = "This switch is currently unavailable", 61 | checked = true, 62 | onCheckedChange = {}, 63 | enabled = false 64 | ) 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /docs/demo/src/commonMain/kotlin/SuperDialogDemo.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import androidx.compose.foundation.background 5 | import androidx.compose.foundation.layout.Arrangement 6 | import androidx.compose.foundation.layout.Box 7 | import androidx.compose.foundation.layout.Column 8 | import androidx.compose.foundation.layout.fillMaxSize 9 | import androidx.compose.foundation.layout.fillMaxWidth 10 | import androidx.compose.foundation.layout.padding 11 | import androidx.compose.foundation.layout.widthIn 12 | import androidx.compose.runtime.Composable 13 | import androidx.compose.runtime.mutableStateOf 14 | import androidx.compose.runtime.remember 15 | import androidx.compose.ui.Alignment 16 | import androidx.compose.ui.Modifier 17 | import androidx.compose.ui.graphics.Brush 18 | import androidx.compose.ui.graphics.Color 19 | import androidx.compose.ui.unit.dp 20 | import top.yukonga.miuix.kmp.basic.Card 21 | import top.yukonga.miuix.kmp.basic.Scaffold 22 | import top.yukonga.miuix.kmp.basic.TextButton 23 | import top.yukonga.miuix.kmp.extra.SuperDialog 24 | 25 | @Composable 26 | fun SuperDialogDemo() { 27 | Scaffold { 28 | Box( 29 | modifier = Modifier 30 | .fillMaxSize() 31 | .background(Brush.linearGradient(listOf(Color(0xfff77062), Color(0xfffe5196)))), 32 | contentAlignment = Alignment.Center 33 | ) { 34 | Column( 35 | Modifier 36 | .padding(16.dp) 37 | .widthIn(max = 600.dp) 38 | .fillMaxWidth(), 39 | verticalArrangement = Arrangement.spacedBy(16.dp), 40 | horizontalAlignment = Alignment.CenterHorizontally 41 | ) { 42 | val showDialog = remember { mutableStateOf(false) } 43 | Card { 44 | TextButton( 45 | text = "Show a SuperDialog", 46 | onClick = { showDialog.value = true } 47 | ) 48 | SuperDialog( 49 | title = "SuperDialog Title", 50 | summary = "This is a basic dialog example that can contain various content.", 51 | show = showDialog, 52 | onDismissRequest = { showDialog.value = false } // Close dialog 53 | ) { 54 | TextButton( 55 | text = "Confirm", 56 | onClick = { showDialog.value = false }, // Close dialog 57 | modifier = Modifier.fillMaxWidth() 58 | ) 59 | } 60 | } 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/icon/icons/useful/Play.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.icon.icons.useful 5 | 6 | import androidx.compose.ui.graphics.Color 7 | import androidx.compose.ui.graphics.PathFillType 8 | import androidx.compose.ui.graphics.SolidColor 9 | import androidx.compose.ui.graphics.vector.ImageVector 10 | import androidx.compose.ui.graphics.vector.path 11 | import androidx.compose.ui.unit.dp 12 | import top.yukonga.miuix.kmp.icon.MiuixIcons 13 | 14 | val MiuixIcons.Useful.Play: ImageVector 15 | get() { 16 | if (_play != null) return _play!! 17 | _play = ImageVector.Builder("Play", 26.0.dp, 26.0.dp, 26.0f, 26.0f).apply { 18 | path( 19 | fill = SolidColor(Color.Black), 20 | pathFillType = PathFillType.EvenOdd 21 | ) { 22 | moveTo(21.914f, 12.431f) 23 | curveTo(21.729f, 12.015f, 21.178f, 11.697f, 20.075f, 11.06f) 24 | lineTo(8.228f, 4.22f) 25 | curveTo(7.125f, 3.583f, 6.574f, 3.265f, 6.122f, 3.313f) 26 | curveTo(5.727f, 3.354f, 5.369f, 3.561f, 5.135f, 3.882f) 27 | curveTo(4.868f, 4.25f, 4.868f, 4.887f, 4.868f, 6.16f) 28 | verticalLineTo(19.84f) 29 | curveTo(4.868f, 21.113f, 4.868f, 21.75f, 5.135f, 22.118f) 30 | curveTo(5.369f, 22.439f, 5.727f, 22.646f, 6.122f, 22.687f) 31 | curveTo(6.574f, 22.735f, 7.125f, 22.416f, 8.228f, 21.78f) 32 | lineTo(20.075f, 14.94f) 33 | curveTo(21.178f, 14.303f, 21.729f, 13.985f, 21.914f, 13.569f) 34 | curveTo(22.076f, 13.207f, 22.076f, 12.793f, 21.914f, 12.431f) 35 | close() 36 | moveTo(20.018f, 12.919f) 37 | curveTo(19.991f, 12.859f, 19.913f, 12.814f, 19.755f, 12.723f) 38 | lineTo(6.946f, 5.327f) 39 | curveTo(6.788f, 5.236f, 6.71f, 5.191f, 6.645f, 5.198f) 40 | curveTo(6.589f, 5.204f, 6.537f, 5.233f, 6.504f, 5.279f) 41 | curveTo(6.466f, 5.332f, 6.466f, 5.423f, 6.466f, 5.605f) 42 | lineTo(6.466f, 20.395f) 43 | curveTo(6.466f, 20.577f, 6.466f, 20.668f, 6.504f, 20.721f) 44 | curveTo(6.537f, 20.767f, 6.589f, 20.796f, 6.645f, 20.802f) 45 | curveTo(6.71f, 20.809f, 6.788f, 20.764f, 6.946f, 20.673f) 46 | lineTo(19.755f, 13.277f) 47 | curveTo(19.913f, 13.186f, 19.991f, 13.141f, 20.018f, 13.081f) 48 | curveTo(20.041f, 13.03f, 20.041f, 12.97f, 20.018f, 12.919f) 49 | close() 50 | } 51 | }.build() 52 | return _play!! 53 | } 54 | 55 | private var _play: ImageVector? = null -------------------------------------------------------------------------------- /iosApp/iosApp.xcodeproj/xcshareddata/xcschemes/iosApp.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | 14 | 20 | 21 | 22 | 23 | 24 | 30 | 31 | 41 | 43 | 49 | 50 | 51 | 54 | 55 | 56 | 62 | 63 | 65 | 66 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/theme/ThemeController.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.theme 5 | 6 | import androidx.compose.foundation.isSystemInDarkTheme 7 | import androidx.compose.runtime.Composable 8 | import androidx.compose.runtime.ProvidableCompositionLocal 9 | import androidx.compose.runtime.Stable 10 | import androidx.compose.runtime.getValue 11 | import androidx.compose.runtime.mutableStateOf 12 | import androidx.compose.runtime.remember 13 | import androidx.compose.runtime.setValue 14 | import androidx.compose.runtime.staticCompositionLocalOf 15 | import androidx.compose.ui.graphics.Color 16 | import com.materialkolor.dynamicColorScheme 17 | 18 | @Stable 19 | enum class ColorSchemeMode { 20 | System, 21 | Light, 22 | Dark, 23 | MonetSystem, 24 | MonetLight, 25 | MonetDark, 26 | } 27 | 28 | internal fun colorsFromSeed(seed: Color, dark: Boolean): Colors { 29 | val cs = dynamicColorScheme(seedColor = seed, isDark = dark) 30 | return mapMd3ToMiuixColorsCommon(cs, dark) 31 | } 32 | 33 | @Stable 34 | class ThemeController( 35 | colorSchemeMode: ColorSchemeMode = ColorSchemeMode.System, 36 | keyColor: Color? = null, 37 | isDark: Boolean? = null, 38 | ) { 39 | var colorSchemeMode: ColorSchemeMode by mutableStateOf(colorSchemeMode) 40 | var keyColor: Color? by mutableStateOf(keyColor) 41 | var isDark: Boolean? by mutableStateOf(isDark) 42 | 43 | @Composable 44 | fun currentColors(): Colors { 45 | return when (colorSchemeMode) { 46 | ColorSchemeMode.System -> { 47 | val dark = isDark ?: isSystemInDarkTheme() 48 | remember(dark) { if (dark) darkColorScheme() else lightColorScheme() } 49 | } 50 | 51 | ColorSchemeMode.Light -> remember { lightColorScheme() } 52 | ColorSchemeMode.Dark -> remember { darkColorScheme() } 53 | ColorSchemeMode.MonetSystem -> { 54 | val dark = isDark ?: isSystemInDarkTheme() 55 | keyColor?.let { 56 | remember(keyColor, dark) { colorsFromSeed(seed = it, dark = dark) } 57 | } ?: platformDynamicColors(dark = dark) 58 | } 59 | 60 | ColorSchemeMode.MonetLight -> { 61 | keyColor?.let { 62 | remember(keyColor) { colorsFromSeed(seed = it, dark = false) } 63 | } ?: platformDynamicColors(dark = false) 64 | } 65 | 66 | ColorSchemeMode.MonetDark -> { 67 | keyColor?.let { 68 | remember(keyColor) { colorsFromSeed(seed = it, dark = true) } 69 | } ?: platformDynamicColors(dark = true) 70 | } 71 | } 72 | } 73 | } 74 | 75 | internal val LocalColorSchemeMode: ProvidableCompositionLocal = staticCompositionLocalOf { null } 76 | -------------------------------------------------------------------------------- /miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/Divider.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.basic 5 | 6 | import androidx.compose.foundation.Canvas 7 | import androidx.compose.foundation.layout.fillMaxHeight 8 | import androidx.compose.foundation.layout.fillMaxWidth 9 | import androidx.compose.foundation.layout.height 10 | import androidx.compose.foundation.layout.width 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.runtime.NonRestartableComposable 13 | import androidx.compose.ui.Modifier 14 | import androidx.compose.ui.geometry.Offset 15 | import androidx.compose.ui.graphics.Color 16 | import androidx.compose.ui.unit.Dp 17 | import androidx.compose.ui.unit.dp 18 | import top.yukonga.miuix.kmp.theme.MiuixTheme 19 | 20 | /** 21 | * A divider is a thin line that groups content in lists and layouts. 22 | * 23 | * @param modifier the [Modifier] to be applied to this divider line. 24 | * @param thickness thickness of this divider line. Using [Dp.Hairline] will produce a single pixel 25 | * divider regardless of screen density. 26 | * @param color color of this divider line. 27 | */ 28 | @Composable 29 | @NonRestartableComposable 30 | fun HorizontalDivider( 31 | modifier: Modifier = Modifier, 32 | thickness: Dp = DividerDefaults.Thickness, 33 | color: Color = DividerDefaults.DividerColor, 34 | ) = 35 | Canvas(modifier.fillMaxWidth().height(thickness)) { 36 | drawLine( 37 | color = color, 38 | strokeWidth = thickness.toPx(), 39 | start = Offset(0f, thickness.toPx() / 2), 40 | end = Offset(size.width, thickness.toPx() / 2), 41 | ) 42 | } 43 | 44 | /** 45 | * A divider is a thin line that groups content in lists and layouts. 46 | * 47 | * @param modifier the [Modifier] to be applied to this divider line. 48 | * @param thickness thickness of this divider line. Using [Dp.Hairline] will produce a single pixel 49 | * divider regardless of screen density. 50 | * @param color color of this divider line. 51 | */ 52 | @Composable 53 | @NonRestartableComposable 54 | fun VerticalDivider( 55 | modifier: Modifier = Modifier, 56 | thickness: Dp = DividerDefaults.Thickness, 57 | color: Color = DividerDefaults.DividerColor 58 | ) = 59 | Canvas(modifier.fillMaxHeight().width(thickness)) { 60 | drawLine( 61 | color = color, 62 | strokeWidth = thickness.toPx(), 63 | start = Offset(thickness.toPx() / 2, 0f), 64 | end = Offset(thickness.toPx() / 2, size.height), 65 | ) 66 | } 67 | 68 | object DividerDefaults { 69 | 70 | /** 71 | * Default thickness of the divider line. 72 | */ 73 | val Thickness = 0.75.dp 74 | 75 | /** 76 | * Default color of the divider line. 77 | */ 78 | val DividerColor @Composable get() = MiuixTheme.colorScheme.dividerLine 79 | 80 | } 81 | -------------------------------------------------------------------------------- /docs/zh_CN/guide/getting-started.md: -------------------------------------------------------------------------------- 1 | # 快速开始 2 | 3 | 当前支持的平台: **Android** / **Desktop(JVM)** / **iOS** / **WasmJs** / **Js** / **macOS(Native)** 4 | 5 | ::: warning 注意 6 | 此库处于实验阶段,API 可能会在未来版本中变更而不另行通知 7 | ::: 8 | 9 | ## 添加依赖 10 | 11 | 要在您的项目中使用 Miuix,请按照以下步骤添加依赖: 12 | 13 | ### Gradle (Kotlin DSL) 14 | 15 | 1. 在根目录的 settings.gradle.kts 添加(正常情况应已包含): 16 | 17 | ```kotlin 18 | repositories { 19 | mavenCentral() 20 | } 21 | ``` 22 | 23 | 2. 检查 Maven Central 当前最新版本: 24 | [![Maven Central](https://img.shields.io/maven-central/v/top.yukonga.miuix.kmp/miuix)](https://search.maven.org/search?q=g:top.yukonga.miuix.kmp) 25 | 26 | 3. 在项目的 build.gradle.kts 中添加依赖: 27 | 28 | - 在 Compose Multiplatform 项目目录的 build.gradle.kts 中: 29 | 30 | ```kotlin 31 | kotlin { 32 | sourceSets { 33 | commonMain.dependencies { 34 | implementation("top.yukonga.miuix.kmp:miuix:") 35 | } 36 | } 37 | } 38 | 39 | ``` 40 | 41 | - 在 Android Compose 项目目录的 build.gradle.kts 中: 42 | 43 | ```kotlin 44 | dependencies { 45 | implementation("top.yukonga.miuix.kmp:miuix-android:") 46 | } 47 | ``` 48 | 49 | - 在其他常规项目中使用,则只需要根据需要添加对应平台后缀的依赖即可: 50 | 51 | ```kotlin 52 | implementation("top.yukonga.miuix.kmp:miuix-android:") 53 | implementation("top.yukonga.miuix.kmp:miuix-iosarm64:") 54 | implementation("top.yukonga.miuix.kmp:miuix-iosx64:") 55 | implementation("top.yukonga.miuix.kmp:miuix-iossimulatorarm64:") 56 | implementation("top.yukonga.miuix.kmp:miuix-macosx64:") 57 | implementation("top.yukonga.miuix.kmp:miuix-macosarm64:") 58 | implementation("top.yukonga.miuix.kmp:miuix-desktop:") 59 | implementation("top.yukonga.miuix.kmp:miuix-wasmjs:") 60 | implementation("top.yukonga.miuix.kmp:miuix-js:") 61 | ``` 62 | 63 | ## 基本用法 64 | 65 | ### 应用 Miuix 主题 66 | 67 | ```kotlin 68 | @Composable 69 | fun AppTheme( 70 | content: @Composable () -> Unit 71 | ) { 72 | // 可用模式: System, Light, Dark, MonetSystem, MonetLight, MonetDark 73 | val controller = remember { ThemeController(ColorSchemeMode.System) } 74 | return MiuixTheme( 75 | controller = controller, 76 | content = content 77 | ) 78 | } 79 | ``` 80 | 81 | ### 使用 Miuix 脚手架 82 | 83 | ```kotlin 84 | Scaffold( 85 | topBar = { 86 | // TopBar 87 | }, 88 | bottomBar = { 89 | // BottomBar 90 | }, 91 | floatingActionButton = { 92 | // FloatingActionButton 93 | }, 94 | floatingToolbar = { 95 | // FloatingToolbar 96 | } 97 | ) { 98 | // Content... 99 | } 100 | ``` 101 | 102 | ::: warning 注意 103 | Scaffold 组件为跨平台提供了一个合适的弹出窗口的容器。`SuperDialog`、`SuperDropdown`、`SuperSpinner`、 104 | `ListPopup` 等组件都基于此实现弹出窗口,因此都需要被该组件包裹。 105 | ::: 106 | 107 | ## API 文档 108 | 109 | - 查看 [API 文档](/miuix/dokka/index.html){target="_blank"},此文档使用 Dokka 生成,包含了所有 API 110 | 的详细信息。 111 | -------------------------------------------------------------------------------- /docs/demo/src/commonMain/kotlin/BasicComponentDemo.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import androidx.compose.foundation.background 5 | import androidx.compose.foundation.layout.Arrangement 6 | import androidx.compose.foundation.layout.Box 7 | import androidx.compose.foundation.layout.Column 8 | import androidx.compose.foundation.layout.fillMaxSize 9 | import androidx.compose.foundation.layout.fillMaxWidth 10 | import androidx.compose.foundation.layout.padding 11 | import androidx.compose.foundation.layout.widthIn 12 | import androidx.compose.runtime.Composable 13 | import androidx.compose.ui.Alignment 14 | import androidx.compose.ui.Modifier 15 | import androidx.compose.ui.graphics.Brush 16 | import androidx.compose.ui.graphics.Color 17 | import androidx.compose.ui.unit.dp 18 | import top.yukonga.miuix.kmp.basic.BasicComponent 19 | import top.yukonga.miuix.kmp.basic.Card 20 | import top.yukonga.miuix.kmp.basic.Icon 21 | import top.yukonga.miuix.kmp.icon.MiuixIcons 22 | import top.yukonga.miuix.kmp.icon.icons.useful.Personal 23 | import top.yukonga.miuix.kmp.theme.MiuixTheme 24 | 25 | @Composable 26 | fun BasicComponentDemo() { 27 | Box( 28 | modifier = Modifier 29 | .fillMaxSize() 30 | .background(Brush.linearGradient(listOf(Color(0xfff77062), Color(0xfffe5196)))), 31 | contentAlignment = Alignment.Center 32 | ) { 33 | Column( 34 | Modifier 35 | .padding(16.dp) 36 | .widthIn(max = 600.dp) 37 | .fillMaxWidth(), 38 | verticalArrangement = Arrangement.spacedBy(16.dp), 39 | horizontalAlignment = Alignment.CenterHorizontally 40 | ) { 41 | Card { 42 | BasicComponent( 43 | title = "BasicComponent", 44 | summary = "Without onClick" 45 | ) 46 | BasicComponent( 47 | title = "Wi-Fi", 48 | summary = "Connected to MIUI-WiFi", 49 | onClick = { /* Handle click event */ } 50 | ) 51 | BasicComponent( 52 | title = "Nickname", 53 | summary = "A brief introduction", 54 | leftAction = { 55 | Icon( 56 | modifier = Modifier.padding(end = 16.dp), 57 | imageVector = MiuixIcons.Useful.Personal, 58 | contentDescription = "Avatar Icon", 59 | tint = MiuixTheme.colorScheme.onBackground 60 | ) 61 | }, 62 | onClick = { /* Handle click event */ } 63 | ) 64 | BasicComponent( 65 | title = "Mobile Network", 66 | summary = "SIM card not inserted", 67 | enabled = false 68 | ) 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /docs/demo/src/commonMain/kotlin/WindowDropdownDemo.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import androidx.compose.foundation.background 5 | import androidx.compose.foundation.layout.Arrangement 6 | import androidx.compose.foundation.layout.Box 7 | import androidx.compose.foundation.layout.Column 8 | import androidx.compose.foundation.layout.fillMaxSize 9 | import androidx.compose.foundation.layout.fillMaxWidth 10 | import androidx.compose.foundation.layout.padding 11 | import androidx.compose.foundation.layout.widthIn 12 | import androidx.compose.runtime.Composable 13 | import androidx.compose.runtime.getValue 14 | import androidx.compose.runtime.mutableStateOf 15 | import androidx.compose.runtime.remember 16 | import androidx.compose.runtime.setValue 17 | import androidx.compose.ui.Alignment 18 | import androidx.compose.ui.Modifier 19 | import androidx.compose.ui.graphics.Brush 20 | import androidx.compose.ui.graphics.Color 21 | import androidx.compose.ui.unit.dp 22 | import top.yukonga.miuix.kmp.basic.Card 23 | import top.yukonga.miuix.kmp.extra.WindowDropdown 24 | 25 | @Composable 26 | fun WindowDropdownDemo() { 27 | Box( 28 | modifier = Modifier 29 | .fillMaxSize() 30 | .background(Brush.linearGradient(listOf(Color(0xfff77062), Color(0xfffe5196)))), 31 | contentAlignment = Alignment.Center 32 | ) { 33 | Column( 34 | Modifier 35 | .padding(16.dp) 36 | .widthIn(max = 600.dp) 37 | .fillMaxWidth(), 38 | verticalArrangement = Arrangement.spacedBy(16.dp), 39 | horizontalAlignment = Alignment.CenterHorizontally 40 | ) { 41 | var selectedIndex1 by remember { mutableStateOf(0) } 42 | val options1 = listOf("Option 1", "Option 2", "Option 3") 43 | var selectedIndex2 by remember { mutableStateOf(0) } 44 | val options2 = listOf("Chinese", "English", "Japanese") 45 | 46 | Card { 47 | WindowDropdown( 48 | title = "Dropdown Menu", 49 | items = options1, 50 | selectedIndex = selectedIndex1, 51 | onSelectedIndexChange = { selectedIndex1 = it } 52 | ) 53 | WindowDropdown( 54 | title = "Language Settings", 55 | summary = "Choose your preferred language", 56 | items = options2, 57 | selectedIndex = selectedIndex2, 58 | onSelectedIndexChange = { selectedIndex2 = it } 59 | ) 60 | WindowDropdown( 61 | title = "Disabled Dropdown", 62 | summary = "This dropdown menu is currently unavailable", 63 | items = listOf("Option 1"), 64 | selectedIndex = 0, 65 | onSelectedIndexChange = {}, 66 | enabled = false 67 | ) 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/icon/icons/other/GitHub.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.icon.icons.other 5 | 6 | import androidx.compose.ui.graphics.Color 7 | import androidx.compose.ui.graphics.PathFillType 8 | import androidx.compose.ui.graphics.SolidColor 9 | import androidx.compose.ui.graphics.vector.ImageVector 10 | import androidx.compose.ui.graphics.vector.path 11 | import androidx.compose.ui.unit.dp 12 | import top.yukonga.miuix.kmp.icon.MiuixIcons 13 | 14 | val MiuixIcons.Other.GitHub: ImageVector 15 | get() { 16 | if (_github != null) return _github!! 17 | _github = ImageVector.Builder("GitHub", 24.dp, 24.dp, 1024f, 1024f).apply { 18 | path( 19 | fill = SolidColor(Color.Black), 20 | fillAlpha = 1f, 21 | pathFillType = PathFillType.EvenOdd 22 | ) { 23 | moveTo(512f, 0f) 24 | curveTo(229.12f, 0f, 0f, 229.12f, 0f, 512f) 25 | curveTo(0f, 738.56f, 146.56f, 929.92f, 350.08f, 997.76f) 26 | curveTo(375.68f, 1002.24f, 385.28f, 986.88f, 385.28f, 973.44f) 27 | curveTo(385.28f, 961.28f, 384.64f, 920.96f, 384.64f, 878.08f) 28 | curveTo(256f, 901.76f, 222.72f, 846.72f, 212.48f, 817.92f) 29 | curveTo(206.72f, 803.2f, 181.76f, 757.76f, 160f, 745.6f) 30 | curveTo(142.08f, 736f, 116.48f, 712.32f, 159.36f, 711.68f) 31 | curveTo(199.68f, 711.04f, 228.48f, 748.8f, 238.08f, 764.16f) 32 | curveTo(284.16f, 841.6f, 357.76f, 819.84f, 387.2f, 806.4f) 33 | curveTo(391.68f, 773.12f, 405.12f, 750.72f, 419.84f, 737.92f) 34 | curveTo(305.92f, 725.12f, 186.88f, 680.96f, 186.88f, 485.12f) 35 | curveTo(186.88f, 429.44f, 206.72f, 383.36f, 239.36f, 347.52f) 36 | curveTo(234.24f, 334.72f, 216.32f, 282.24f, 244.48f, 211.84f) 37 | curveTo(244.48f, 211.84f, 287.36f, 198.4f, 385.28f, 264.32f) 38 | curveTo(426.24f, 252.8f, 469.76f, 247.04f, 513.28f, 247.04f) 39 | curveTo(556.8f, 247.04f, 600.32f, 252.8f, 641.28f, 264.32f) 40 | curveTo(739.2f, 197.76f, 782.08f, 211.84f, 782.08f, 211.84f) 41 | curveTo(810.24f, 282.24f, 792.32f, 334.72f, 787.2f, 347.52f) 42 | curveTo(819.84f, 383.36f, 839.68f, 428.8f, 839.68f, 485.12f) 43 | curveTo(839.68f, 681.6f, 720f, 725.12f, 606.08f, 737.92f) 44 | curveTo(624.64f, 753.92f, 640.64f, 784.64f, 640.64f, 832.64f) 45 | curveTo(640.64f, 901.12f, 640f, 956.16f, 640f, 973.44f) 46 | curveTo(640f, 986.88f, 649.6f, 1002.88f, 675.2f, 997.76f) 47 | curveTo(877.44f, 929.92f, 1024f, 737.92f, 1024f, 512f) 48 | curveTo(1024f, 229.12f, 794.88f, 0f, 512f, 0f) 49 | close() 50 | } 51 | }.build() 52 | return _github!! 53 | } 54 | 55 | private var _github: ImageVector? = null -------------------------------------------------------------------------------- /example/src/commonMain/kotlin/preview/Preview.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package preview 5 | 6 | import App 7 | import FourthPage 8 | import MainPage 9 | import SecondPage 10 | import ThirdPage 11 | import androidx.compose.foundation.layout.PaddingValues 12 | import androidx.compose.runtime.Composable 13 | import androidx.compose.runtime.mutableIntStateOf 14 | import androidx.compose.runtime.remember 15 | import org.jetbrains.compose.ui.tooling.preview.Preview 16 | import top.yukonga.miuix.kmp.basic.Scaffold 17 | import ui.AppTheme 18 | 19 | @Composable 20 | @Preview 21 | fun UITestPreview() { 22 | AppTheme { 23 | Scaffold { 24 | App() 25 | } 26 | } 27 | } 28 | 29 | @Composable 30 | @Preview 31 | fun MainPagePreview() { 32 | AppTheme { 33 | Scaffold { 34 | MainPage( 35 | PaddingValues(), enableScrollEndHaptic = true, 36 | enableOverScroll = false, 37 | isWideScreen = false, 38 | showTopAppBar = false 39 | ) 40 | } 41 | } 42 | } 43 | 44 | @Composable 45 | @Preview 46 | fun SecondPagePreview() { 47 | AppTheme { 48 | Scaffold { 49 | SecondPage( 50 | PaddingValues(), 51 | enableScrollEndHaptic = true, 52 | enableOverScroll = false, 53 | isWideScreen = false, 54 | showTopAppBar = false 55 | ) 56 | } 57 | } 58 | } 59 | 60 | @Composable 61 | @Preview 62 | fun ThirdPagePreview() { 63 | AppTheme { 64 | Scaffold { 65 | ThirdPage( 66 | PaddingValues(), 67 | enableScrollEndHaptic = true, 68 | enableOverScroll = false, 69 | isWideScreen = false, 70 | showTopAppBar = false, 71 | ) 72 | } 73 | } 74 | } 75 | 76 | @Composable 77 | @Preview 78 | fun FourthPagePreview() { 79 | AppTheme { 80 | Scaffold { 81 | FourthPage( 82 | PaddingValues(), 83 | false, 84 | {}, 85 | false, 86 | {}, 87 | false, 88 | {}, 89 | false, 90 | {}, 91 | 0, 92 | {}, 93 | 0, 94 | {}, 95 | false, 96 | {}, 97 | 1, 98 | {}, 99 | 1, 100 | {}, 101 | false, 102 | {}, 103 | 2, 104 | {}, 105 | false, 106 | {}, 107 | true, 108 | {}, 109 | false, 110 | false, 111 | remember { mutableIntStateOf(0) }, 112 | remember { mutableIntStateOf(0) } 113 | ) 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/icon/icons/useful/Remove.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.icon.icons.useful 5 | 6 | import androidx.compose.ui.graphics.Color 7 | import androidx.compose.ui.graphics.PathFillType 8 | import androidx.compose.ui.graphics.SolidColor 9 | import androidx.compose.ui.graphics.vector.ImageVector 10 | import androidx.compose.ui.graphics.vector.path 11 | import androidx.compose.ui.unit.dp 12 | import top.yukonga.miuix.kmp.icon.MiuixIcons 13 | 14 | val MiuixIcons.Useful.Remove: ImageVector 15 | get() { 16 | if (_remove != null) return _remove!! 17 | _remove = ImageVector.Builder("Remove", 26.0.dp, 26.0.dp, 26.0f, 26.0f).apply { 18 | path( 19 | fill = SolidColor(Color.Black), 20 | pathFillType = PathFillType.EvenOdd 21 | ) { 22 | moveTo(6.927f, 19.073f) 23 | curveTo(10.281f, 22.428f, 15.719f, 22.428f, 19.073f, 19.073f) 24 | curveTo(22.428f, 15.719f, 22.428f, 10.281f, 19.073f, 6.927f) 25 | curveTo(15.719f, 3.572f, 10.281f, 3.572f, 6.927f, 6.927f) 26 | curveTo(3.572f, 10.281f, 3.572f, 15.719f, 6.927f, 19.073f) 27 | close() 28 | moveTo(5.795f, 20.205f) 29 | curveTo(9.774f, 24.184f, 16.226f, 24.184f, 20.205f, 20.205f) 30 | curveTo(24.184f, 16.226f, 24.184f, 9.774f, 20.205f, 5.795f) 31 | curveTo(16.226f, 1.816f, 9.774f, 1.816f, 5.795f, 5.795f) 32 | curveTo(1.816f, 9.774f, 1.816f, 16.226f, 5.795f, 20.205f) 33 | close() 34 | } 35 | path( 36 | fill = SolidColor(Color.White), 37 | pathFillType = PathFillType.EvenOdd 38 | ) { 39 | moveTo(8.178f, 12.196f) 40 | curveTo(7.957f, 12.196f, 7.846f, 12.196f, 7.757f, 12.226f) 41 | curveTo(7.594f, 12.283f, 7.466f, 12.411f, 7.409f, 12.575f) 42 | curveTo(7.378f, 12.664f, 7.378f, 12.774f, 7.378f, 12.996f) 43 | curveTo(7.378f, 13.217f, 7.378f, 13.328f, 7.409f, 13.417f) 44 | curveTo(7.466f, 13.58f, 7.594f, 13.709f, 7.757f, 13.765f) 45 | curveTo(7.846f, 13.796f, 7.957f, 13.796f, 8.178f, 13.796f) 46 | horizontalLineTo(17.822f) 47 | curveTo(18.043f, 13.796f, 18.154f, 13.796f, 18.243f, 13.765f) 48 | curveTo(18.406f, 13.709f, 18.535f, 13.58f, 18.591f, 13.417f) 49 | curveTo(18.622f, 13.328f, 18.622f, 13.217f, 18.622f, 12.996f) 50 | curveTo(18.622f, 12.774f, 18.622f, 12.664f, 18.591f, 12.575f) 51 | curveTo(18.535f, 12.411f, 18.406f, 12.283f, 18.243f, 12.226f) 52 | curveTo(18.154f, 12.196f, 18.043f, 12.196f, 17.822f, 12.196f) 53 | horizontalLineTo(8.178f) 54 | close() 55 | } 56 | }.build() 57 | return _remove!! 58 | } 59 | private var _remove: ImageVector? = null -------------------------------------------------------------------------------- /miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/icon/icons/useful/Unlike.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package top.yukonga.miuix.kmp.icon.icons.useful 5 | 6 | import androidx.compose.ui.graphics.Color 7 | import androidx.compose.ui.graphics.PathFillType 8 | import androidx.compose.ui.graphics.SolidColor 9 | import androidx.compose.ui.graphics.vector.ImageVector 10 | import androidx.compose.ui.graphics.vector.path 11 | import androidx.compose.ui.unit.dp 12 | import top.yukonga.miuix.kmp.icon.MiuixIcons 13 | 14 | val MiuixIcons.Useful.Unlike: ImageVector 15 | get() { 16 | if (_unlike != null) return _unlike!! 17 | _unlike = ImageVector.Builder("Unlike", 26.0.dp, 26.0.dp, 26.0f, 26.0f).apply { 18 | path( 19 | fill = SolidColor(Color.Black), 20 | pathFillType = PathFillType.EvenOdd 21 | ) { 22 | moveTo(13.4026f, 22.7729f) 23 | lineTo(21.9482f, 13.9362f) 24 | curveTo(21.9909f, 13.8932f, 22.033f, 13.8497f, 22.0745f, 13.8056f) 25 | lineTo(22.0955f, 13.7839f) 26 | curveTo(23.1299f, 12.6721f, 23.762f, 11.1816f, 23.762f, 9.5433f) 27 | curveTo(23.762f, 6.1045f, 20.9742f, 3.3167f, 17.5354f, 3.3167f) 28 | curveTo(15.7475f, 3.3167f, 14.1357f, 4.0703f, 13.0001f, 5.277f) 29 | curveTo(11.8644f, 4.0703f, 10.2526f, 3.3167f, 8.4647f, 3.3167f) 30 | curveTo(5.0259f, 3.3167f, 2.2382f, 6.1045f, 2.2382f, 9.5433f) 31 | curveTo(2.2382f, 11.1816f, 2.8709f, 12.6721f, 3.9053f, 13.7839f) 32 | lineTo(3.9243f, 13.8041f) 33 | curveTo(3.9666f, 13.8492f, 4.0096f, 13.8937f, 4.0533f, 13.9376f) 34 | lineTo(12.5975f, 22.7729f) 35 | curveTo(12.8176f, 23.0005f, 13.1825f, 23.0005f, 13.4026f, 22.7729f) 36 | close() 37 | moveTo(5.2318f, 12.8529f) 38 | curveTo(5.1653f, 12.788f, 5.1008f, 12.7212f, 5.0383f, 12.6525f) 39 | lineTo(5.0307f, 12.6445f) 40 | curveTo(4.2905f, 11.8264f, 3.84f, 10.7414f, 3.84f, 9.5512f) 41 | curveTo(3.84f, 7.004f, 5.9049f, 4.9391f, 8.4521f, 4.9391f) 42 | curveTo(10.1622f, 4.9391f, 11.6549f, 5.8698f, 12.4513f, 7.2524f) 43 | curveTo(12.566f, 7.4515f, 12.7723f, 7.5859f, 13.0021f, 7.5859f) 44 | curveTo(13.2319f, 7.5859f, 13.4381f, 7.4515f, 13.5528f, 7.2524f) 45 | curveTo(14.3493f, 5.8698f, 15.842f, 4.9391f, 17.5521f, 4.9391f) 46 | curveTo(20.0993f, 4.9391f, 22.1642f, 7.004f, 22.1642f, 9.5512f) 47 | curveTo(22.1642f, 10.8727f, 21.6084f, 12.0644f, 20.7178f, 12.9053f) 48 | lineTo(13.144f, 20.7507f) 49 | curveTo(13.0653f, 20.8321f, 12.9348f, 20.8321f, 12.8562f, 20.7507f) 50 | lineTo(5.2318f, 12.8529f) 51 | close() 52 | } 53 | }.build() 54 | return _unlike!! 55 | } 56 | private var _unlike: ImageVector? = null -------------------------------------------------------------------------------- /docs/demo/src/commonMain/kotlin/WindowListPopupDemo.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025, compose-miuix-ui contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import androidx.compose.foundation.background 5 | import androidx.compose.foundation.layout.Box 6 | import androidx.compose.foundation.layout.fillMaxSize 7 | import androidx.compose.foundation.layout.padding 8 | import androidx.compose.runtime.Composable 9 | import androidx.compose.runtime.getValue 10 | import androidx.compose.runtime.mutableStateOf 11 | import androidx.compose.runtime.remember 12 | import androidx.compose.runtime.setValue 13 | import androidx.compose.ui.Alignment 14 | import androidx.compose.ui.Modifier 15 | import androidx.compose.ui.graphics.Brush 16 | import androidx.compose.ui.graphics.Color 17 | import androidx.compose.ui.unit.dp 18 | import top.yukonga.miuix.kmp.basic.DropdownImpl 19 | import top.yukonga.miuix.kmp.basic.ListPopupColumn 20 | import top.yukonga.miuix.kmp.basic.PopupPositionProvider 21 | import top.yukonga.miuix.kmp.basic.TextButton 22 | import top.yukonga.miuix.kmp.extra.LocalWindowListPopupState 23 | import top.yukonga.miuix.kmp.extra.WindowListPopup 24 | 25 | @Composable 26 | fun WindowListPopupDemo() { 27 | Box( 28 | modifier = Modifier 29 | .fillMaxSize() 30 | .background(Brush.linearGradient(listOf(Color(0xfff77062), Color(0xfffe5196)))), 31 | contentAlignment = Alignment.Center 32 | ) { 33 | val showPopup = remember { mutableStateOf(false) } 34 | var selectedIndex by remember { mutableStateOf(0) } 35 | val items = listOf("Option 1", "Option 2", "Option 3") 36 | Box( 37 | modifier = Modifier.fillMaxSize(), 38 | contentAlignment = Alignment.TopCenter 39 | ) { 40 | Box { 41 | TextButton( 42 | text = "Show WindowListPopup", 43 | onClick = { showPopup.value = true }, 44 | modifier = Modifier.padding(top = 16.dp) 45 | ) 46 | WindowListPopup( 47 | show = showPopup, 48 | alignment = PopupPositionProvider.Align.TopLeft, 49 | onDismissRequest = { showPopup.value = false } 50 | ) { 51 | val dismiss = LocalWindowListPopupState.current 52 | ListPopupColumn { 53 | items.forEachIndexed { index, string -> 54 | DropdownImpl( 55 | text = string, 56 | optionSize = items.size, 57 | isSelected = selectedIndex == index, 58 | onSelectedIndexChange = { selectedIdx -> 59 | selectedIndex = selectedIdx 60 | dismiss() 61 | }, 62 | index = index 63 | ) 64 | } 65 | } 66 | } 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /docs/components/smalltitle.md: -------------------------------------------------------------------------------- 1 | # SmallTitle 2 | 3 | `SmallTitle` is a basic title component in Miuix used to create small-sized title text. It follows Miuix's design style with preset font size, weight, and padding. 4 | 5 |
6 | 7 |
8 | 9 | ## Import 10 | 11 | ```kotlin 12 | import top.yukonga.miuix.kmp.basic.SmallTitle 13 | ``` 14 | 15 | ## Basic Usage 16 | 17 | SmallTitle component can be used to display small title text: 18 | 19 | ```kotlin 20 | SmallTitle( 21 | text = "Small Title" 22 | ) 23 | ``` 24 | 25 | ## Customization 26 | 27 | ### Custom Color 28 | 29 | ```kotlin 30 | SmallTitle( 31 | text = "Small Title with Custom Color", 32 | textColor = Color.Red 33 | ) 34 | ``` 35 | 36 | ### Custom Padding 37 | 38 | ```kotlin 39 | SmallTitle( 40 | text = "Small Title with Custom Padding", 41 | insideMargin = PaddingValues(16.dp, 4.dp) 42 | ) 43 | ``` 44 | 45 | ## Properties 46 | 47 | ### SmallTitle Properties 48 | 49 | | Property Name | Type | Description | Default Value | Required | 50 | | ------------- | ------------- | ----------------------------- | ------------------------------------------ | -------- | 51 | | text | String | Text content to display | - | Yes | 52 | | modifier | Modifier | Modifier applied to component | Modifier | No | 53 | | textColor | Color | Title text color | MiuixTheme.colorScheme.onBackgroundVariant | No | 54 | | insideMargin | PaddingValues | Component internal padding | PaddingValues(28.dp, 8.dp) | No | 55 | 56 | ## Advanced Usage 57 | 58 | ### Using with Other Components 59 | 60 | ```kotlin 61 | var checked by remember { mutableStateOf(false) } 62 | 63 | Column { 64 | SmallTitle(text = "Settings") 65 | Card( 66 | modifier = Modifier.padding(horizontal = 12.dp).padding(bottom = 12.dp) 67 | ) { 68 | SuperSwitch( 69 | title = "Bluetooth", 70 | checked = checked, 71 | onCheckedChange = { checked = it } 72 | ) 73 | } 74 | HorizontalDivider( 75 | modifier = Modifier.padding(horizontal = 12.dp) 76 | ) 77 | SmallTitle(text = "Advanced Settings") 78 | // Other settings items 79 | } 80 | ``` 81 | 82 | ### Custom Styling 83 | 84 | ```kotlin 85 | SmallTitle( 86 | text = "Fully Customized Style", 87 | modifier = Modifier 88 | .fillMaxWidth() 89 | .background(Color.LightGray), 90 | textColor = Color.Blue, 91 | insideMargin = PaddingValues(32.dp, 12.dp) 92 | ) 93 | ``` -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | 74 | 75 | @rem Execute Gradle 76 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* 77 | 78 | :end 79 | @rem End local scope for the variables with windows NT shell 80 | if %ERRORLEVEL% equ 0 goto mainEnd 81 | 82 | :fail 83 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 84 | rem the _cmd.exe /c_ return code! 85 | set EXIT_CODE=%ERRORLEVEL% 86 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 87 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 88 | exit /b %EXIT_CODE% 89 | 90 | :mainEnd 91 | if "%OS%"=="Windows_NT" endlocal 92 | 93 | :omega 94 | --------------------------------------------------------------------------------