├── 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 |
7 |
--------------------------------------------------------------------------------
/example/src/androidMain/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/icon/MiuixIcon.kt:
--------------------------------------------------------------------------------
1 | // Copyright 2025, compose-miuix-ui contributors
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | package top.yukonga.miuix.kmp.icon
5 |
6 | object MiuixIcons {
7 | object Basic
8 | object Useful
9 | object Other
10 | }
11 |
--------------------------------------------------------------------------------
/.idea/migrations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/example/src/webMain/kotlin/WebPlatformApi.web.kt:
--------------------------------------------------------------------------------
1 | // Copyright 2025, compose-miuix-ui contributors
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | internal expect fun platformHideLoading()
5 |
6 | internal expect fun platformGetCssVar(name: String): Double
7 |
8 | internal expect fun platformIsTouchEnabled(): Boolean
9 |
10 |
--------------------------------------------------------------------------------
/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/theme/DynamicColors.kt:
--------------------------------------------------------------------------------
1 | // Copyright 2025, compose-miuix-ui contributors
2 | // SPDX-License-Identifier: Apache-2.0
3 | package top.yukonga.miuix.kmp.theme
4 |
5 | import androidx.compose.runtime.Composable
6 |
7 | @Composable
8 | expect fun platformDynamicColors(dark: Boolean): Colors
9 |
--------------------------------------------------------------------------------
/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "app-icon-1024.png",
5 | "idiom" : "universal",
6 | "platform" : "ios",
7 | "size" : "1024x1024"
8 | }
9 | ],
10 | "info" : {
11 | "author" : "xcode",
12 | "version" : 1
13 | }
14 | }
--------------------------------------------------------------------------------
/docs/.vitepress/theme/style/hidden.css:
--------------------------------------------------------------------------------
1 | :root {
2 |
3 | /* 手机端菜单栏顶部横条隐藏 */
4 | .VPNavBar.screen-open {
5 | border-bottom: none;
6 | }
7 |
8 | /* 手机端菜单栏菜单分割线隐藏 */
9 | .VPNavScreenMenuLink {
10 | border-bottom: none;
11 | }
12 |
13 | /* 手机端菜单组隐藏 */
14 | .VPNavScreenMenuGroup {
15 | border-bottom: none;
16 | }
17 | }
--------------------------------------------------------------------------------
/example/src/androidMain/res/values-night-v29/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
9 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/copyright/miuix.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
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 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
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 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | true
19 | true
20 | false
21 | false
22 |
23 |
24 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/example__js_.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | true
19 | true
20 | false
21 | false
22 |
23 |
24 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/example__macos_.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | true
19 | true
20 | false
21 | false
22 |
23 |
24 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/example__wasmJs_.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | true
19 | true
20 | false
21 | false
22 |
23 |
24 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/example__desktop_.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
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 | [](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 |
--------------------------------------------------------------------------------