From 2e28fe5e4bdd30ddbd612bad476bea878c80cadd Mon Sep 17 00:00:00 2001 From: agent-polyblank Date: Thu, 8 Jan 2026 10:18:19 +0000 Subject: [PATCH 1/2] add wasm target --- kodeview/build.gradle.kts | 8 + webExample/build.gradle.kts | 18 ++ webExample/src/wasmJsMain/kotlin/Dropdown.kt | 56 +++++++ .../src/wasmJsMain/kotlin/ThemeSwitcher.kt | 38 +++++ webExample/src/wasmJsMain/kotlin/main.js.kt | 154 ++++++++++++++++++ .../src/wasmJsMain/resources/index.html | 15 ++ .../src/wasmJsMain/resources/styles.css | 8 + 7 files changed, 297 insertions(+) create mode 100644 webExample/src/wasmJsMain/kotlin/Dropdown.kt create mode 100644 webExample/src/wasmJsMain/kotlin/ThemeSwitcher.kt create mode 100644 webExample/src/wasmJsMain/kotlin/main.js.kt create mode 100644 webExample/src/wasmJsMain/resources/index.html create mode 100644 webExample/src/wasmJsMain/resources/styles.css diff --git a/kodeview/build.gradle.kts b/kodeview/build.gradle.kts index 2e20552..ed03a71 100644 --- a/kodeview/build.gradle.kts +++ b/kodeview/build.gradle.kts @@ -1,3 +1,5 @@ +import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl + @Suppress("DSL_SCOPE_VIOLATION") plugins { alias(libs.plugins.multiplatform) @@ -26,6 +28,12 @@ dependencies { kotlin { jvm() + @OptIn(ExperimentalWasmDsl::class) + wasmJs { + browser() + binaries.executable() + } + js(IR) { browser() } diff --git a/webExample/build.gradle.kts b/webExample/build.gradle.kts index 7606433..2532b22 100644 --- a/webExample/build.gradle.kts +++ b/webExample/build.gradle.kts @@ -1,3 +1,5 @@ +import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl + @Suppress("DSL_SCOPE_VIOLATION") plugins { alias(libs.plugins.multiplatform) @@ -11,6 +13,12 @@ kotlin { binaries.executable() } + @OptIn(ExperimentalWasmDsl::class) + wasmJs { + browser() + binaries.executable() + } + sourceSets { val jsMain by getting { dependencies { @@ -22,5 +30,15 @@ kotlin { implementation(project(":kodeview")) } } + val wasmJsMain by getting { + dependencies { + implementation(compose.ui) + implementation(compose.foundation) + implementation(compose.material) + implementation(compose.material3) + implementation(compose.materialIconsExtended) + implementation(project(":kodeview")) + } + } } } \ No newline at end of file diff --git a/webExample/src/wasmJsMain/kotlin/Dropdown.kt b/webExample/src/wasmJsMain/kotlin/Dropdown.kt new file mode 100644 index 0000000..1bdf446 --- /dev/null +++ b/webExample/src/wasmJsMain/kotlin/Dropdown.kt @@ -0,0 +1,56 @@ +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp + +@Composable +fun Dropdown( + options: List, + selected: Int, + modifier: Modifier = Modifier, + onSelect: (String) -> Unit, +) { + var selectedOption by remember { + mutableStateOf(options[selected]) + } + + var isExpanded by remember { mutableStateOf(false) } + + DropdownMenu( + expanded = isExpanded, + onDismissRequest = { isExpanded = false }, + ) { + options.forEach { option -> + DropdownMenuItem( + text = { Text(text = option) }, + onClick = { + selectedOption = option + onSelect(option) + isExpanded = !isExpanded + }, + ) + } + } + + Text( + text = selectedOption, + textAlign = TextAlign.Center, + modifier = modifier + .clip(RoundedCornerShape(16.dp)) + .fillMaxWidth() + .clickable { isExpanded = !isExpanded } + .padding(8.dp), + ) +} \ No newline at end of file diff --git a/webExample/src/wasmJsMain/kotlin/ThemeSwitcher.kt b/webExample/src/wasmJsMain/kotlin/ThemeSwitcher.kt new file mode 100644 index 0000000..520c26d --- /dev/null +++ b/webExample/src/wasmJsMain/kotlin/ThemeSwitcher.kt @@ -0,0 +1,38 @@ +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Icon +import androidx.compose.material3.Surface +import androidx.compose.material3.Switch +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.DarkMode +import androidx.compose.material.icons.outlined.LightMode +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +@Composable +fun ThemeSwitcher( + isDarkMode: Boolean, + modifier: Modifier = Modifier, + onChange: (Boolean) -> Unit, +) { + Surface { + Row( + modifier = modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically, + ) { + Spacer(Modifier.width(8.dp)) + Icon(Icons.Outlined.LightMode, contentDescription = "Light mode") + Spacer(Modifier.width(16.dp)) + Switch(checked = isDarkMode, onCheckedChange = onChange) + Spacer(Modifier.width(16.dp)) + Icon(Icons.Default.DarkMode, contentDescription = "Dark mode") + Spacer(Modifier.width(8.dp)) + } + } +} \ No newline at end of file diff --git a/webExample/src/wasmJsMain/kotlin/main.js.kt b/webExample/src/wasmJsMain/kotlin/main.js.kt new file mode 100644 index 0000000..c1d121f --- /dev/null +++ b/webExample/src/wasmJsMain/kotlin/main.js.kt @@ -0,0 +1,154 @@ +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Divider +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TextFieldDefaults +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.window.CanvasBasedWindow +import dev.snipme.highlights.Highlights +import dev.snipme.highlights.model.SyntaxLanguage +import dev.snipme.highlights.model.SyntaxTheme +import dev.snipme.highlights.model.SyntaxThemes +import dev.snipme.highlights.model.SyntaxThemes.useDark +import dev.snipme.kodeview.view.material3.CodeEditText +import dev.snipme.kodeview.view.CodeTextView + +private val sampleCode = + """ + class Main { + public static void main(String[] args) { + int abcd = 100; + } + } + """.trimIndent() + +@ExperimentalComposeUiApi +fun main() { + CanvasBasedWindow( + title = "KodeView example", + canvasElementId = "ComposeTarget", + applyDefaultStyles = true, + ) { + val isDarkModeState = remember { mutableStateOf(false) } + val isDarkMode = isDarkModeState.value + + val highlightsState = remember { + mutableStateOf( + Highlights.Builder(code = sampleCode).build() + ) + } + val highlights = highlightsState.value + + fun updateSyntaxTheme(theme: SyntaxTheme) { + highlightsState.value = highlights.getBuilder() + .theme(theme) + .build() + } + + fun updateSyntaxLanguage(language: SyntaxLanguage) { + highlightsState.value = highlights.getBuilder() + .language(language) + .build() + } + + MaterialTheme(colorScheme = if (isDarkMode) darkColorScheme() else lightColorScheme()) { + Surface { + Column( + modifier = Modifier + .fillMaxSize() + .padding(16.dp), + horizontalAlignment = Alignment.Start, + verticalArrangement = Arrangement.SpaceBetween + ) { + Spacer(Modifier.height(8.dp)) + + ThemeSwitcher( + isDarkMode, + modifier = Modifier.fillMaxWidth(), + ) { setToDarkMode -> + isDarkModeState.value = setToDarkMode + updateSyntaxTheme(highlights.getTheme().useDark(setToDarkMode)!!) + } + + Spacer(Modifier.height(16.dp)) + + Text( + modifier = Modifier.fillMaxWidth(), + text = "KodeView", + fontSize = 18.sp, + textAlign = TextAlign.Center, + ) + + Spacer(modifier = Modifier.size(16.dp)) + + CodeTextView(highlights = highlights) + + Spacer(modifier = Modifier.size(16.dp)) + + Divider() + + Spacer(modifier = Modifier.size(16.dp)) + + Text("Edit this...") + CodeEditText( + highlights = highlights, + onValueChange = { textValue -> + highlightsState.value = highlights.getBuilder() + .code(textValue) + .build() + }, + colors = TextFieldDefaults.colors( + unfocusedContainerColor = Color.Transparent, + focusedContainerColor = Color.Transparent, + focusedIndicatorColor = Color.Transparent, + unfocusedIndicatorColor = Color.Transparent, + disabledIndicatorColor = Color.Transparent, + errorIndicatorColor = Color.Transparent, + ), + ) + + Spacer(modifier = Modifier.size(16.dp)) + + Spacer(modifier = Modifier.weight(1f)) + + Dropdown( + options = SyntaxThemes.getNames(), + selected = SyntaxThemes.themes().keys.indexOf(highlights.getTheme().key), + ) { selectedThemeName -> + updateSyntaxTheme( + SyntaxThemes.themes(isDarkMode)[selectedThemeName.lowercase()]!! + ) + } + + Spacer(modifier = Modifier.size(16.dp)) + + Dropdown( + options = SyntaxLanguage.getNames(), + selected = SyntaxLanguage.getNames().indexOfFirst { + it.equals(highlights.getLanguage().name, ignoreCase = true) + }) { selectedLanguage -> + updateSyntaxLanguage(SyntaxLanguage.getByName(selectedLanguage)!!) + } + } + } + } + } + } \ No newline at end of file diff --git a/webExample/src/wasmJsMain/resources/index.html b/webExample/src/wasmJsMain/resources/index.html new file mode 100644 index 0000000..446a5fc --- /dev/null +++ b/webExample/src/wasmJsMain/resources/index.html @@ -0,0 +1,15 @@ + + + + + KodeView Example + + + + +
+ +
+ + + \ No newline at end of file diff --git a/webExample/src/wasmJsMain/resources/styles.css b/webExample/src/wasmJsMain/resources/styles.css new file mode 100644 index 0000000..e5b3293 --- /dev/null +++ b/webExample/src/wasmJsMain/resources/styles.css @@ -0,0 +1,8 @@ +#root { + width: 100%; + height: 100vh; +} + +#root > .compose-web-column > div { + position: relative; +} \ No newline at end of file From 8034189b83a31256f70dd4c86bf47b22beb3bb62 Mon Sep 17 00:00:00 2001 From: agent-polyblank Date: Thu, 8 Jan 2026 13:14:56 +0000 Subject: [PATCH 2/2] add common for web versions --- webExample/build.gradle.kts | 13 +---- .../{jsMain => commonMain}/kotlin/Dropdown.kt | 0 .../kotlin/ThemeSwitcher.kt | 0 webExample/src/wasmJsMain/kotlin/Dropdown.kt | 56 ------------------- .../src/wasmJsMain/kotlin/ThemeSwitcher.kt | 38 ------------- 5 files changed, 2 insertions(+), 105 deletions(-) rename webExample/src/{jsMain => commonMain}/kotlin/Dropdown.kt (100%) rename webExample/src/{jsMain => commonMain}/kotlin/ThemeSwitcher.kt (100%) delete mode 100644 webExample/src/wasmJsMain/kotlin/Dropdown.kt delete mode 100644 webExample/src/wasmJsMain/kotlin/ThemeSwitcher.kt diff --git a/webExample/build.gradle.kts b/webExample/build.gradle.kts index 2532b22..92193d7 100644 --- a/webExample/build.gradle.kts +++ b/webExample/build.gradle.kts @@ -20,17 +20,8 @@ kotlin { } sourceSets { - val jsMain by getting { - dependencies { - implementation(compose.ui) - implementation(compose.foundation) - implementation(compose.material) - implementation(compose.material3) - implementation(compose.materialIconsExtended) - implementation(project(":kodeview")) - } - } - val wasmJsMain by getting { + + val commonMain by getting { dependencies { implementation(compose.ui) implementation(compose.foundation) diff --git a/webExample/src/jsMain/kotlin/Dropdown.kt b/webExample/src/commonMain/kotlin/Dropdown.kt similarity index 100% rename from webExample/src/jsMain/kotlin/Dropdown.kt rename to webExample/src/commonMain/kotlin/Dropdown.kt diff --git a/webExample/src/jsMain/kotlin/ThemeSwitcher.kt b/webExample/src/commonMain/kotlin/ThemeSwitcher.kt similarity index 100% rename from webExample/src/jsMain/kotlin/ThemeSwitcher.kt rename to webExample/src/commonMain/kotlin/ThemeSwitcher.kt diff --git a/webExample/src/wasmJsMain/kotlin/Dropdown.kt b/webExample/src/wasmJsMain/kotlin/Dropdown.kt deleted file mode 100644 index 1bdf446..0000000 --- a/webExample/src/wasmJsMain/kotlin/Dropdown.kt +++ /dev/null @@ -1,56 +0,0 @@ -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp - -@Composable -fun Dropdown( - options: List, - selected: Int, - modifier: Modifier = Modifier, - onSelect: (String) -> Unit, -) { - var selectedOption by remember { - mutableStateOf(options[selected]) - } - - var isExpanded by remember { mutableStateOf(false) } - - DropdownMenu( - expanded = isExpanded, - onDismissRequest = { isExpanded = false }, - ) { - options.forEach { option -> - DropdownMenuItem( - text = { Text(text = option) }, - onClick = { - selectedOption = option - onSelect(option) - isExpanded = !isExpanded - }, - ) - } - } - - Text( - text = selectedOption, - textAlign = TextAlign.Center, - modifier = modifier - .clip(RoundedCornerShape(16.dp)) - .fillMaxWidth() - .clickable { isExpanded = !isExpanded } - .padding(8.dp), - ) -} \ No newline at end of file diff --git a/webExample/src/wasmJsMain/kotlin/ThemeSwitcher.kt b/webExample/src/wasmJsMain/kotlin/ThemeSwitcher.kt deleted file mode 100644 index 520c26d..0000000 --- a/webExample/src/wasmJsMain/kotlin/ThemeSwitcher.kt +++ /dev/null @@ -1,38 +0,0 @@ -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.width -import androidx.compose.material3.Icon -import androidx.compose.material3.Surface -import androidx.compose.material3.Switch -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.DarkMode -import androidx.compose.material.icons.outlined.LightMode -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp - -@Composable -fun ThemeSwitcher( - isDarkMode: Boolean, - modifier: Modifier = Modifier, - onChange: (Boolean) -> Unit, -) { - Surface { - Row( - modifier = modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.Center, - verticalAlignment = Alignment.CenterVertically, - ) { - Spacer(Modifier.width(8.dp)) - Icon(Icons.Outlined.LightMode, contentDescription = "Light mode") - Spacer(Modifier.width(16.dp)) - Switch(checked = isDarkMode, onCheckedChange = onChange) - Spacer(Modifier.width(16.dp)) - Icon(Icons.Default.DarkMode, contentDescription = "Dark mode") - Spacer(Modifier.width(8.dp)) - } - } -} \ No newline at end of file