Skip to content

Commit e6b7a8c

Browse files
committed
example: commonize code between wasm and js targets
1 parent 4d1f674 commit e6b7a8c

File tree

26 files changed

+161
-307
lines changed

26 files changed

+161
-307
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
.idea/AndroidProjectSystem.xml
2020
.idea/deviceManager.xml
2121
.idea/gradle.xml
22+
.idea/copilot.data.migration.agent.xml
23+
.idea/copilot.data.migration.edit.xml
2224
.fleet
2325
.vscode
2426
.DS_Store

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ plugins {
66
alias(libs.plugins.android.application) apply false
77
alias(libs.plugins.android.library) apply false
88
alias(libs.plugins.compose.compiler) apply false
9-
alias(libs.plugins.compose.hotReload) apply false
109
alias(libs.plugins.jetbrains.compose) apply false
10+
alias(libs.plugins.jetbrains.compose.hotReload) apply false
1111
alias(libs.plugins.jetbrains.dokka) apply false
1212
alias(libs.plugins.kotlin.multiplatform) apply false
1313
alias(libs.plugins.spotless) apply false

docs/demo/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ kotlin {
3434
implementation(compose.foundation)
3535
implementation(compose.ui)
3636

37-
implementation(libs.androidx.navigation)
37+
implementation(libs.jetbrains.androidx.navigation)
3838
implementation(project(":miuix"))
3939
}
4040
}

docs/demo/src/jsMain/kotlin/Main.js.kt

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,15 @@
44
import androidx.compose.ui.ExperimentalComposeUiApi
55
import androidx.compose.ui.window.ComposeViewport
66
import kotlinx.browser.document
7-
import org.jetbrains.skiko.wasm.onWasmReady
87
import org.w3c.dom.url.URLSearchParams
98

109
@OptIn(ExperimentalComposeUiApi::class)
1110
fun main() {
1211
val iFrameParams = URLSearchParams(document.location?.search)
1312
val id = iFrameParams.get("id")
14-
onWasmReady {
15-
ComposeViewport(
16-
viewportContainerId = "ComposeTarget"
17-
) {
18-
Demo(demoId = id)
19-
}
13+
ComposeViewport(
14+
viewportContainerId = "ComposeTarget"
15+
) {
16+
Demo(demoId = id)
2017
}
2118
}

docs/iconGen/src/main/kotlin/top/yukonga/miuix/docs/icongen/Main.kt

Lines changed: 75 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// Copyright 2025, miuix-kotlin-multiplatform contributors
2+
// SPDX-License-Identifier: Apache-2.0
3+
14
package top.yukonga.miuix.docs.icongen
25

36
import java.io.File
@@ -34,12 +37,14 @@ fun main(args: Array<String>) {
3437
var count = 0
3538
ktFiles.forEach { file ->
3639
val content = file.readText()
37-
val builderRegex = Regex("ImageVector\\.Builder\\(\\s*\\\"([^\\\"]+)\\\"\\s*,\\s*([0-9.]+)f?\\.dp\\s*,\\s*([0-9.]+)f?\\.dp\\s*,\\s*([0-9.]+)f\\s*,\\s*([0-9.]+)f")
40+
val builderRegex =
41+
Regex("ImageVector\\.Builder\\(\\s*\"([^\"]+)\"\\s*,\\s*([0-9.]+)f?\\.dp\\s*,\\s*([0-9.]+)f?\\.dp\\s*,\\s*([0-9.]+)f\\s*,\\s*([0-9.]+)f")
3842
val builderMatch = builderRegex.find(content)
3943
val iconName = builderMatch?.groupValues?.getOrNull(1) ?: file.nameWithoutExtension
4044
val viewportWidth = builderMatch?.groupValues?.getOrNull(4)?.toFloatOrNull() ?: 24f
4145
val viewportHeight = builderMatch?.groupValues?.getOrNull(5)?.toFloatOrNull() ?: 24f
4246
val pathBlocks = Regex("path\\((.*?)\\) *\\{(.*?)\\}", setOf(RegexOption.DOT_MATCHES_ALL)).findAll(content)
47+
4348
data class SvgPath(
4449
val d: String,
4550
val evenOdd: Boolean,
@@ -52,6 +57,7 @@ fun main(args: Array<String>) {
5257
val strokeLineJoin: String?,
5358
val strokeMiterLimit: String?
5459
)
60+
5561
val paths = mutableListOf<SvgPath>()
5662
pathBlocks.forEach { m ->
5763
val header = m.groupValues[1]
@@ -61,16 +67,22 @@ fun main(args: Array<String>) {
6167
fun parseColor(kind: String): String? {
6268
val regex = Regex("$kind *= *SolidColor\\(Color\\.([A-Za-z]+)\\)")
6369
val named = regex.find(header)?.groupValues?.getOrNull(1)
64-
if (named != null) return when (named) { "White" -> "#FFFFFF"; "Black" -> "#000000"; "Transparent" -> "none"; else -> null }
70+
if (named != null) return when (named) {
71+
"White" -> "#FFFFFF"; "Black" -> "#000000"; "Transparent" -> "none"; else -> null
72+
}
6573
val hexRegex = Regex("$kind *= *SolidColor\\(Color\\((0x[0-9A-Fa-f]{8})\\)\\)")
6674
val hexMatch = hexRegex.find(header)?.groupValues?.getOrNull(1)
6775
if (hexMatch != null) {
6876
val value = hexMatch.removePrefix("0x")
69-
val a = value.substring(0,2); val r = value.substring(2,4); val g = value.substring(4,6); val b = value.substring(6,8)
77+
val a = value.substring(0, 2)
78+
val r = value.substring(2, 4)
79+
val g = value.substring(4, 6)
80+
val b = value.substring(6, 8)
7081
return if (a.equals("FF", true)) "#$r$g$b" else "#$r$g$b$a"
7182
}
7283
return null
7384
}
85+
7486
val fillColor = parseColor("fill")
7587
val strokeColor = parseColor("stroke")
7688
val strokeAlpha = Regex("strokeAlpha *= *(\\d*\\.?\\d+)f").find(header)?.groupValues?.getOrNull(1)?.toFloatOrNull()
@@ -79,50 +91,91 @@ fun main(args: Array<String>) {
7991
val strokeLineJoin = Regex("strokeLineJoin *= *([A-Za-z.]+)").find(header)?.groupValues?.getOrNull(1)?.substringAfterLast('.')
8092
val strokeMiterLimit = Regex("strokeLineMiter *= *(\\d*\\.?\\d+)f").find(header)?.groupValues?.getOrNull(1)
8193
val sb = StringBuilder()
82-
val moveRegex = Regex("moveTo\\((.*?)f, (.*?)f\\)"); val lineToRegex = Regex("lineTo\\((.*?)f, (.*?)f\\)"); val curveToRegex = Regex("curveTo\\((.*?)f, (.*?)f, (.*?)f, (.*?)f, (.*?)f, (.*?)f\\)")
83-
val moveRelRegex = Regex("moveToRelative\\((.*?)f, (.*?)f\\)"); val lineRelRegex = Regex("lineToRelative\\((.*?)f, (.*?)f\\)"); val curveRelRegex = Regex("curveToRelative\\((.*?)f, (.*?)f, (.*?)f, (.*?)f, (.*?)f, (.*?)f\\)")
84-
val hLineRegex = Regex("horizontalLineTo\\((.*?)f\\)"); val vLineRegex = Regex("verticalLineTo\\((.*?)f\\)"); val hLineRelRegex = Regex("horizontalLineToRelative\\((.*?)f\\)"); val vLineRelRegex = Regex("verticalLineToRelative\\((.*?)f\\)")
85-
val quadRegex = Regex("quadTo\\((.*?)f, (.*?)f, (.*?)f, (.*?)f\\)"); val quadRelRegex = Regex("quadToRelative\\((.*?)f, (.*?)f, (.*?)f, (.*?)f\\)")
86-
val rCurveRegex = Regex("reflectiveCurveTo\\((.*?)f, (.*?)f, (.*?)f, (.*?)f\\)"); val rCurveRelRegex = Regex("reflectiveCurveToRelative\\((.*?)f, (.*?)f, (.*?)f, (.*?)f\\)")
87-
val rQuadRegex = Regex("reflectiveQuadTo\\((.*?)f, (.*?)f\\)"); val rQuadRelRegex = Regex("reflectiveQuadToRelative\\((.*?)f, (.*?)f\\)"); val closeRegex = Regex("close\\(\\)")
94+
val moveRegex = Regex("moveTo\\((.*?)f, (.*?)f\\)")
95+
val lineToRegex = Regex("lineTo\\((.*?)f, (.*?)f\\)")
96+
val curveToRegex = Regex("curveTo\\((.*?)f, (.*?)f, (.*?)f, (.*?)f, (.*?)f, (.*?)f\\)")
97+
val moveRelRegex = Regex("moveToRelative\\((.*?)f, (.*?)f\\)")
98+
val lineRelRegex = Regex("lineToRelative\\((.*?)f, (.*?)f\\)")
99+
val curveRelRegex = Regex("curveToRelative\\((.*?)f, (.*?)f, (.*?)f, (.*?)f, (.*?)f, (.*?)f\\)")
100+
val hLineRegex = Regex("horizontalLineTo\\((.*?)f\\)")
101+
val vLineRegex = Regex("verticalLineTo\\((.*?)f\\)")
102+
val hLineRelRegex = Regex("horizontalLineToRelative\\((.*?)f\\)")
103+
val vLineRelRegex = Regex("verticalLineToRelative\\((.*?)f\\)")
104+
val quadRegex = Regex("quadTo\\((.*?)f, (.*?)f, (.*?)f, (.*?)f\\)")
105+
val quadRelRegex = Regex("quadToRelative\\((.*?)f, (.*?)f, (.*?)f, (.*?)f\\)")
106+
val rCurveRegex = Regex("reflectiveCurveTo\\((.*?)f, (.*?)f, (.*?)f, (.*?)f\\)")
107+
val rCurveRelRegex = Regex("reflectiveCurveToRelative\\((.*?)f, (.*?)f, (.*?)f, (.*?)f\\)")
108+
val rQuadRegex = Regex("reflectiveQuadTo\\((.*?)f, (.*?)f\\)")
109+
val rQuadRelRegex = Regex("reflectiveQuadToRelative\\((.*?)f, (.*?)f\\)")
110+
val closeRegex = Regex("close\\(\\)")
88111
body.lineSequence().forEach { line ->
89112
moveRegex.findAll(line).forEach { r -> sb.append("M${r.groupValues[1]} ${r.groupValues[2]} ") }
90113
lineToRegex.findAll(line).forEach { r -> sb.append("L${r.groupValues[1]} ${r.groupValues[2]} ") }
91-
curveToRegex.findAll(line).forEach { r -> sb.append("C${r.groupValues[1]} ${r.groupValues[2]} ${r.groupValues[3]} ${r.groupValues[4]} ${r.groupValues[5]} ${r.groupValues[6]} ") }
114+
curveToRegex.findAll(line)
115+
.forEach { r -> sb.append("C${r.groupValues[1]} ${r.groupValues[2]} ${r.groupValues[3]} ${r.groupValues[4]} ${r.groupValues[5]} ${r.groupValues[6]} ") }
92116
moveRelRegex.findAll(line).forEach { r -> sb.append("m${r.groupValues[1]} ${r.groupValues[2]} ") }
93117
lineRelRegex.findAll(line).forEach { r -> sb.append("l${r.groupValues[1]} ${r.groupValues[2]} ") }
94-
curveRelRegex.findAll(line).forEach { r -> sb.append("c${r.groupValues[1]} ${r.groupValues[2]} ${r.groupValues[3]} ${r.groupValues[4]} ${r.groupValues[5]} ${r.groupValues[6]} ") }
118+
curveRelRegex.findAll(line)
119+
.forEach { r -> sb.append("c${r.groupValues[1]} ${r.groupValues[2]} ${r.groupValues[3]} ${r.groupValues[4]} ${r.groupValues[5]} ${r.groupValues[6]} ") }
95120
hLineRegex.findAll(line).forEach { r -> sb.append("H${r.groupValues[1]} ") }
96121
vLineRegex.findAll(line).forEach { r -> sb.append("V${r.groupValues[1]} ") }
97122
hLineRelRegex.findAll(line).forEach { r -> sb.append("h${r.groupValues[1]} ") }
98123
vLineRelRegex.findAll(line).forEach { r -> sb.append("v${r.groupValues[1]} ") }
99-
quadRegex.findAll(line).forEach { r -> sb.append("Q${r.groupValues[1]} ${r.groupValues[2]} ${r.groupValues[3]} ${r.groupValues[4]} ") }
100-
quadRelRegex.findAll(line).forEach { r -> sb.append("q${r.groupValues[1]} ${r.groupValues[2]} ${r.groupValues[3]} ${r.groupValues[4]} ") }
101-
rCurveRegex.findAll(line).forEach { r -> sb.append("S${r.groupValues[1]} ${r.groupValues[2]} ${r.groupValues[3]} ${r.groupValues[4]} ") }
102-
rCurveRelRegex.findAll(line).forEach { r -> sb.append("s${r.groupValues[1]} ${r.groupValues[2]} ${r.groupValues[3]} ${r.groupValues[4]} ") }
124+
quadRegex.findAll(line)
125+
.forEach { r -> sb.append("Q${r.groupValues[1]} ${r.groupValues[2]} ${r.groupValues[3]} ${r.groupValues[4]} ") }
126+
quadRelRegex.findAll(line)
127+
.forEach { r -> sb.append("q${r.groupValues[1]} ${r.groupValues[2]} ${r.groupValues[3]} ${r.groupValues[4]} ") }
128+
rCurveRegex.findAll(line)
129+
.forEach { r -> sb.append("S${r.groupValues[1]} ${r.groupValues[2]} ${r.groupValues[3]} ${r.groupValues[4]} ") }
130+
rCurveRelRegex.findAll(line)
131+
.forEach { r -> sb.append("s${r.groupValues[1]} ${r.groupValues[2]} ${r.groupValues[3]} ${r.groupValues[4]} ") }
103132
rQuadRegex.findAll(line).forEach { r -> sb.append("T${r.groupValues[1]} ${r.groupValues[2]} ") }
104133
rQuadRelRegex.findAll(line).forEach { r -> sb.append("t${r.groupValues[1]} ${r.groupValues[2]} ") }
105134
if (closeRegex.containsMatchIn(line)) sb.append("Z ")
106135
}
107136
val d = sb.toString().trim()
108-
if (d.isNotBlank()) paths += SvgPath(d, evenOdd, fillAlpha, fillColor, strokeColor, strokeWidth, strokeAlpha, strokeLineCap, strokeLineJoin, strokeMiterLimit)
137+
if (d.isNotBlank()) paths += SvgPath(
138+
d,
139+
evenOdd,
140+
fillAlpha,
141+
fillColor,
142+
strokeColor,
143+
strokeWidth,
144+
strokeAlpha,
145+
strokeLineCap,
146+
strokeLineJoin,
147+
strokeMiterLimit
148+
)
109149
}
110150
if (paths.isEmpty()) return@forEach
111151
fun num(v: Float): String = if (v % 1f == 0f) v.toInt().toString() else v.toString().trimEnd('0').trimEnd('.')
112152
val svg = buildString {
113153
appendLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
114-
appendLine("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${num(viewportWidth)}\" height=\"${num(viewportHeight)}\" viewBox=\"0 0 ${num(viewportWidth)} ${num(viewportHeight)}\" fill=\"none\" stroke=\"none\">")
154+
appendLine(
155+
"<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${num(viewportWidth)}\" height=\"${num(viewportHeight)}\" viewBox=\"0 0 ${
156+
num(
157+
viewportWidth
158+
)
159+
} ${num(viewportHeight)}\" fill=\"none\" stroke=\"none\">"
160+
)
115161
appendLine(" <style>\n :root{color-scheme:light dark;}\n svg{color:$light;}\n @media (prefers-color-scheme: dark){svg{color:$dark;}}\n </style>")
116162
paths.forEach { sp ->
117163
val rule = if (sp.evenOdd) " fill-rule=\"evenodd\" clip-rule=\"evenodd\"" else ""
118-
val alphaAttr = if (sp.fillAlpha != null && sp.fillAlpha < 0.999f) " fill-opacity=\"${"%.3f".format(sp.fillAlpha)}\"" else ""
164+
val alphaAttr =
165+
if (sp.fillAlpha != null && sp.fillAlpha < 0.999f) " fill-opacity=\"${"%.3f".format(sp.fillAlpha)}\"" else ""
119166
val fillValue = if (preserveColors) {
120-
when (sp.fill) { null -> "currentColor"; else -> sp.fill }
167+
when (sp.fill) {
168+
null -> "currentColor"; else -> sp.fill
169+
}
121170
} else {
122-
when (sp.fill) { null, "#FFFFFF", "#000000" -> "currentColor"; "none" -> "none"; else -> sp.fill }
171+
when (sp.fill) {
172+
null, "#FFFFFF", "#000000" -> "currentColor"; "none" -> "none"; else -> sp.fill
173+
}
123174
}
124175
val fillAttr = " fill=\"$fillValue\""
125-
val strokeAttr = sp.stroke?.let { col -> val value = if (!preserveColors && (col == "#FFFFFF" || col == "#000000")) "currentColor" else col; " stroke=\"$value\"" } ?: ""
176+
val strokeAttr = sp.stroke?.let { col ->
177+
val value = if (!preserveColors && (col == "#FFFFFF" || col == "#000000")) "currentColor" else col; " stroke=\"$value\""
178+
} ?: ""
126179
val strokeWidthAttr = sp.strokeWidth?.let { " stroke-width=\"$it\"" } ?: ""
127180
val strokeAlphaAttr = sp.strokeAlpha?.let { if (it < 0.999f) " stroke-opacity=\"${"%.3f".format(it)}\"" else "" } ?: ""
128181
val lineCapAttr = sp.strokeLineCap?.let { " stroke-linecap=\"${it.lowercase()}\"" } ?: ""
@@ -135,7 +188,7 @@ fun main(args: Array<String>) {
135188
val relativeDir = file.relativeTo(src).parentFile
136189
val outDir = File(dest, relativeDir.path)
137190
outDir.mkdirs()
138-
File(outDir, iconName + ".svg").writeText(svg)
191+
File(outDir, "$iconName.svg").writeText(svg)
139192
count++
140193
}
141194
println("[iconGen] Generated $count SVG(s) into $dest")

example/build.gradle.kts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,16 @@
66
import com.android.build.gradle.internal.api.BaseVariantOutputImpl
77
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
88
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
9+
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig
910
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin
1011
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension
1112
import java.util.Properties
1213

1314
plugins {
1415
alias(libs.plugins.android.application)
1516
alias(libs.plugins.compose.compiler)
16-
alias(libs.plugins.compose.hotReload)
1717
alias(libs.plugins.jetbrains.compose)
18+
alias(libs.plugins.jetbrains.compose.hotReload)
1819
alias(libs.plugins.kotlin.multiplatform)
1920
alias(libs.plugins.spotless)
2021
}
@@ -62,22 +63,31 @@ kotlin {
6263

6364
jvm("desktop")
6465

65-
@OptIn(ExperimentalWasmDsl::class)
66-
wasmJs {
66+
js(IR) {
67+
outputModuleName.set("composeApp")
6768
browser {
68-
outputModuleName = "uitest"
6969
commonWebpackConfig {
70-
outputFileName = "uitest.js"
70+
outputFileName = "composeApp.js"
7171
}
7272
}
7373
binaries.executable()
7474
}
7575

76-
js(IR) {
76+
@OptIn(ExperimentalWasmDsl::class)
77+
wasmJs {
78+
outputModuleName.set("composeApp")
7779
browser {
78-
outputModuleName = "uitest"
80+
val rootDirPath = project.rootDir.path
81+
val projectDirPath = project.projectDir.path
7982
commonWebpackConfig {
80-
outputFileName = "uitest.js"
83+
outputFileName = "composeApp.js"
84+
devServer = (devServer ?: KotlinWebpackConfig.DevServer()).apply {
85+
static = (static ?: mutableListOf()).apply {
86+
// Serve sources to debug inside browser
87+
add(rootDirPath)
88+
add(projectDirPath)
89+
}
90+
}
8191
}
8292
}
8393
binaries.executable()

example/src/jsMain/kotlin/Main.js.kt

Lines changed: 0 additions & 56 deletions
This file was deleted.
-495 KB
Binary file not shown.

example/src/jsMain/resources/app.js

Lines changed: 0 additions & 13 deletions
This file was deleted.

example/src/jsMain/resources/index.html

Lines changed: 0 additions & 20 deletions
This file was deleted.

0 commit comments

Comments
 (0)