Tóth István Zoltán
07/16/2025, 8:52 AMStefan Oltmann
07/16/2025, 12:10 PMactual library support is basically non-existentI have to strongly disagree. The most important Multiplatform libraries are available for wasmJS by now. I did https://stefan-oltmann.de/oni-seed-browser , which is a full-featured production app used by thousands of users every day and it’s super stable - despite the tech being officially not production ready. In https://stefan-oltmann.de/pixelsafe/ I even have cryptography included. I did some more projects with Compose for Web and I didn’t miss anything so far.
Tóth István Zoltán
07/16/2025, 12:13 PMTóth István Zoltán
07/16/2025, 12:14 PMStefan Oltmann
07/16/2025, 12:16 PMTóth István Zoltán
07/16/2025, 12:16 PMTóth István Zoltán
07/16/2025, 12:17 PMStefan Oltmann
07/16/2025, 12:17 PMTóth István Zoltán
07/16/2025, 12:18 PMStefan Oltmann
07/16/2025, 12:18 PMTóth István Zoltán
07/16/2025, 12:19 PMTóth István Zoltán
07/16/2025, 12:19 PMStefan Oltmann
07/16/2025, 12:19 PMTóth István Zoltán
07/16/2025, 12:20 PMTóth István Zoltán
07/16/2025, 12:21 PMStefan Oltmann
07/16/2025, 12:21 PMTóth István Zoltán
07/16/2025, 12:22 PMTóth István Zoltán
07/16/2025, 12:23 PMStefan Oltmann
07/16/2025, 12:23 PMTóth István Zoltán
07/16/2025, 12:23 PMTóth István Zoltán
07/16/2025, 12:24 PMTóth István Zoltán
07/16/2025, 12:24 PMStefan Oltmann
07/16/2025, 12:25 PMTóth István Zoltán
07/16/2025, 12:26 PMTóth István Zoltán
07/16/2025, 12:26 PMStefan Oltmann
07/16/2025, 12:26 PMWhy the bloody hell do you attack each of my posts which this nonsense?Excuse me? I didn’t mean any offense and I’m not aware that we had this discussion before. I was just wondering why you claim that wasmJS has no library support while I see most libraries supporting it.
Stefan Oltmann
07/16/2025, 12:27 PMTóth István Zoltán
07/16/2025, 12:29 PMStefan Oltmann
07/16/2025, 12:34 PMTóth István Zoltán
07/16/2025, 12:37 PMTóth István Zoltán
07/16/2025, 12:38 PMStefan Oltmann
07/16/2025, 12:38 PMTóth István Zoltán
07/16/2025, 12:39 PMStefan Oltmann
07/16/2025, 12:40 PMStefan Oltmann
07/16/2025, 12:46 PMTóth István Zoltán
07/16/2025, 12:48 PMStefan Oltmann
07/16/2025, 12:52 PMTóth István Zoltán
07/16/2025, 12:53 PMStefan Oltmann
07/16/2025, 12:57 PMTóth István Zoltán
07/16/2025, 12:59 PMTóth István Zoltán
07/16/2025, 1:00 PMStefan Oltmann
07/16/2025, 1:00 PMTóth István Zoltán
07/16/2025, 1:01 PMTóth István Zoltán
07/16/2025, 1:01 PMStefan Oltmann
07/16/2025, 1:02 PMMichael Paus
07/16/2025, 1:44 PMStefan Oltmann
07/16/2025, 2:08 PMStefan Oltmann
07/16/2025, 2:12 PMStefan Oltmann
07/16/2025, 2:14 PMSargun Vohra
07/16/2025, 4:57 PMStefan Oltmann
07/16/2025, 5:45 PMTóth István Zoltán
07/16/2025, 5:47 PMStefan Oltmann
07/16/2025, 5:47 PMTóth István Zoltán
07/16/2025, 5:47 PMStefan Oltmann
07/16/2025, 5:48 PMTóth István Zoltán
07/16/2025, 5:48 PMTóth István Zoltán
07/16/2025, 5:48 PMStefan Oltmann
07/16/2025, 5:49 PMTóth István Zoltán
07/16/2025, 5:49 PMTóth István Zoltán
07/16/2025, 5:50 PMStefan Oltmann
07/16/2025, 5:52 PMTóth István Zoltán
07/16/2025, 5:52 PMTóth István Zoltán
07/16/2025, 5:52 PMTóth István Zoltán
07/16/2025, 5:54 PMStefan Oltmann
07/16/2025, 5:54 PMStefan Oltmann
07/16/2025, 5:54 PMTóth István Zoltán
07/16/2025, 5:55 PMTóth István Zoltán
07/16/2025, 5:55 PMTóth István Zoltán
07/16/2025, 5:55 PMStefan Oltmann
07/16/2025, 5:55 PMTóth István Zoltán
07/16/2025, 5:56 PMStefan Oltmann
07/16/2025, 5:57 PMTóth István Zoltán
07/16/2025, 5:57 PMStefan Oltmann
07/16/2025, 6:00 PMTóth István Zoltán
07/16/2025, 6:01 PMStefan Oltmann
07/16/2025, 6:02 PMTóth István Zoltán
07/16/2025, 6:03 PMStefan Oltmann
07/16/2025, 6:03 PMTóth István Zoltán
07/16/2025, 6:04 PMimport kotlinx.coroutines.await
import org.khronos.webgl.ArrayBuffer
import org.w3c.fetch.Response
internal suspend fun deflate(input: dynamic): ArrayBuffer {
val readableStream = ReadableStream(js("{ start : function(controller) { controller.enqueue(input); controller.close(); } }"))
val compressionStream = CompressionStream("deflate-raw")
val compressedStream = readableStream.pipeThrough(compressionStream)
return Response(compressedStream).arrayBuffer().await()
}
external class ReadableStream(options: dynamic) {
fun pipeThrough(transform: dynamic, options: dynamic = definedExternally): dynamic
}
external class CompressionStream(algorithm: String) {
val readable: ReadableStream
}
Tóth István Zoltán
07/16/2025, 6:04 PMsuspend fun add(filename: String, data: ByteArray) {
val deflated = deflate(data.toPlatformArray())
val compressed = Uint8Array(deflated).toKotlinArray()
Tóth István Zoltán
07/16/2025, 6:05 PMfun Uint8Array.toKotlinArray(): ByteArray =
Int8Array(buffer, byteOffset, byteLength).unsafeCast<ByteArray>()
@Suppress("NOTHING_TO_INLINE")
inline fun ByteArray.toPlatformArray(): Uint8Array {
val i8a = unsafeCast<Int8Array>()
return Uint8Array(i8a.buffer, i8a.byteOffset, i8a.byteLength)
}
Tóth István Zoltán
07/16/2025, 6:05 PMSargun Vohra
07/16/2025, 6:06 PMWhy do you plan to add platform specific implementations in addition? For the performance?
So that your implementation is just a fallback?
I did it mainly for the ease of writing tests and benchmarks against the known working version. So figured I'd include it in the library if someone wants it (also as a library architecture test case for plugging in "different" compression algos)
Tóth István Zoltán
07/16/2025, 6:07 PMStefan Oltmann
07/16/2025, 6:08 PMTóth István Zoltán
07/16/2025, 6:09 PMStefan Oltmann
07/16/2025, 6:10 PMSargun Vohra
07/16/2025, 6:13 PMStefan Oltmann
07/16/2025, 6:13 PMcrypto, compression I try to keep distance from because they are time sinksRegarding crypto I’m thankful that we already have a great lib here. 😅
Tóth István Zoltán
07/16/2025, 6:15 PMStefan Oltmann
07/16/2025, 6:15 PMSargun Vohra
07/16/2025, 6:16 PMStefan Oltmann
07/16/2025, 6:17 PMTóth István Zoltán
07/16/2025, 6:18 PMStefan Oltmann
07/16/2025, 6:18 PMSargun Vohra
07/16/2025, 6:19 PMStefan Oltmann
07/16/2025, 6:19 PMSargun Vohra
07/16/2025, 6:19 PMStefan Oltmann
07/16/2025, 6:21 PMI think in the particular case of compress/decompress (and crypto) native solutions are better, the Kotlin overhead could hit on those areas hard.Maybe for some use cases, but I’m sure for my lib it won’t be an issue.
Stefan Oltmann
07/16/2025, 6:21 PMSargun Vohra
07/16/2025, 6:22 PMStefan Oltmann
07/16/2025, 6:23 PMTóth István Zoltán
07/16/2025, 6:24 PMStefan Oltmann
07/16/2025, 6:24 PMSargun Vohra
07/16/2025, 6:25 PMSargun Vohra
07/16/2025, 6:26 PMTóth István Zoltán
07/16/2025, 6:26 PMTóth István Zoltán
07/16/2025, 6:27 PMStefan Oltmann
07/16/2025, 6:27 PMStefan Oltmann
07/16/2025, 6:28 PMI don't use JS libraries in KMP as a matter of principle. :)100% I do in one single case and I want to get rid of that.
Sargun Vohra
07/16/2025, 6:30 PMSargun Vohra
07/16/2025, 6:31 PMStefan Oltmann
07/16/2025, 6:31 PMStefan Oltmann
07/16/2025, 6:32 PMStefan Oltmann
07/16/2025, 6:33 PMStefan Oltmann
07/16/2025, 6:34 PMStefan Oltmann
07/16/2025, 6:35 PMTóth István Zoltán
07/16/2025, 6:36 PMTóth István Zoltán
07/16/2025, 6:37 PMStefan Oltmann
07/16/2025, 6:38 PMStefan Oltmann
07/16/2025, 6:39 PMStefan Oltmann
07/16/2025, 6:43 PMTóth István Zoltán
07/16/2025, 6:43 PMTóth István Zoltán
07/16/2025, 6:52 PMTóth István Zoltán
07/16/2025, 6:53 PMTóth István Zoltán
07/16/2025, 6:54 PMSargun Vohra
07/16/2025, 6:54 PMTóth István Zoltán
07/16/2025, 6:55 PMStefan Oltmann
07/16/2025, 6:56 PMTóth István Zoltán
07/16/2025, 6:58 PMStefan Oltmann
07/16/2025, 6:58 PMTóth István Zoltán
07/16/2025, 6:58 PMTóth István Zoltán
07/16/2025, 6:59 PMSargun Vohra
07/16/2025, 6:59 PMStefan Oltmann
07/16/2025, 7:00 PMSargun Vohra
07/16/2025, 7:00 PMWell, tables with sort, filter, resize columns, reorder columns, multi-level, etc and performance is not that simple imho.this sort of data table UI library would be a solid library addition to the CMP ecosystem (and Jetpack Compose) ecosystem
Tóth István Zoltán
07/16/2025, 7:01 PMSargun Vohra
07/16/2025, 7:02 PMTóth István Zoltán
07/16/2025, 7:02 PMTóth István Zoltán
07/16/2025, 7:03 PMTóth István Zoltán
07/16/2025, 7:03 PMSargun Vohra
07/16/2025, 7:03 PMStefan Oltmann
07/16/2025, 7:03 PMTóth István Zoltán
07/16/2025, 7:04 PMTóth István Zoltán
07/16/2025, 7:04 PMStefan Oltmann
07/16/2025, 7:05 PMStefan Oltmann
07/16/2025, 7:06 PMTóth István Zoltán
07/16/2025, 7:07 PMTóth István Zoltán
07/16/2025, 7:07 PMSargun Vohra
07/16/2025, 7:07 PMDOM-based Compose for Web was rebranded to Compose HTML.
Compose for Web now means running WebAssembly Canvas with real Composethe naming in this ecosystem is ... rough "Compose" -> compiler plugin for reactivity "Compose" -> Skia-based implementation of Jetpack Compose on Multiplatform "Compose" -> alternative compositions, like Compose HTML, Redwood, my MapLibre Compose library
Stefan Oltmann
07/16/2025, 7:08 PMStefan Oltmann
07/16/2025, 7:09 PMTóth István Zoltán
07/16/2025, 7:09 PMTóth István Zoltán
07/16/2025, 7:09 PMStefan Oltmann
07/16/2025, 7:11 PMTóth István Zoltán
07/16/2025, 7:12 PMSargun Vohra
07/16/2025, 7:13 PMCompose does have some structural problems that make proper validation hard to implement well.I'd be curious to learn about these problems. What about Compose makes it hard to build a library like those in the JS world (for example, https://github.com/TanStack/form)?
Tóth István Zoltán
07/16/2025, 7:13 PM@Adaptive
fun formBasicExample(): AdaptiveFragment {
val template = FormData()
val form = observe { adatFormBackend(template) }
localContext(form) {
flowBox {
maxWidth .. gap { 20.dp }
column {
gap { 16.dp } .. width { 250.dp }
textEditor { template.string }
booleanEditor { template.boolean }
intEditor { <http://template.int|template.int> }
longEditor { template.long }
doubleEditor { template.double }
dateEditor { template.date }
timeEditor { template.time }
dateTimeEditor { template.dateTime }
timeRangeEditor { template.timeRange }
colorEditor { template.color }
enumEditorDropdown(ExampleEnum.entries) { template.enum }
enumEditorList(ExampleEnum.entries) { template.enum }
badgeEditor { template.badges }
button("Save") .. onClick {
if (form.isInvalid()) {
warningNotification("Form is not valid!")
return@onClick
}
infoNotification("Saved!")
}
}
column {
width { 250.dp } .. gap { 16.dp }
text("Valid: ${form.isValid()}")
codeFence(form.inputValue.encodeToPrettyJson()) .. maxWidth
}
}
}
return fragment()
}
@Adat
class FormData(
val boolean: Boolean = true,
val int: Int = 34,
val long: Long = 45,
val double: Double = 89.10,
val string: String = "ab",
val date : LocalDate = localDate(),
val time : LocalTime = localTime(),
val dateTime : LocalDateTime = localDateTime(),
val timeRange : TimeRange = TimeRange(),
val color : Color = color(0xff0000),
val enum: ExampleEnum = ExampleEnum.V1,
val enumOrNull: ExampleEnum? = null,
val badges : Set<String> = setOf("badge1", "badge2")
) {
override fun descriptor() {
properties {
int maximum 100 minimum 10
string pattern "[a-z]+"
}
}
}
Stefan Oltmann
07/16/2025, 7:15 PMTóth István Zoltán
07/16/2025, 7:16 PMTóth István Zoltán
07/16/2025, 7:19 PMIt’s just a big HTML5 canvas and that makes a difference.It has benefits and also drawbacks. There is a lot of experience behind browser mechanisms and APIs. I think it depends on the type of application. Yours is clearly graphics oriented, there it makes perfect sense. In a data input intensive application it might be a bad choice.
Stefan Oltmann
07/16/2025, 7:20 PMTóth István Zoltán
07/16/2025, 7:21 PMTóth István Zoltán
07/16/2025, 7:22 PMStefan Oltmann
07/16/2025, 7:22 PMTóth István Zoltán
07/16/2025, 7:23 PMSargun Vohra
07/16/2025, 7:23 PMTóth István Zoltán
07/16/2025, 7:25 PMStefan Oltmann
07/16/2025, 7:25 PMTóth István Zoltán
07/16/2025, 7:26 PMTóth István Zoltán
07/16/2025, 7:27 PMStefan Oltmann
07/16/2025, 7:27 PMTóth István Zoltán
07/16/2025, 7:28 PMStefan Oltmann
07/16/2025, 7:28 PMTóth István Zoltán
07/16/2025, 7:28 PMTóth István Zoltán
07/16/2025, 7:29 PMTóth István Zoltán
07/16/2025, 7:29 PMTóth István Zoltán
07/16/2025, 7:30 PMTóth István Zoltán
07/16/2025, 7:31 PMSargun Vohra
07/16/2025, 7:31 PMSargun Vohra
07/16/2025, 7:32 PMStefan Oltmann
07/16/2025, 7:32 PMTóth István Zoltán
07/16/2025, 7:32 PMTóth István Zoltán
07/16/2025, 7:34 PMTóth István Zoltán
07/16/2025, 7:34 PMTóth István Zoltán
07/16/2025, 7:36 PM