From 4b9ddb7b290957eee35efc750d26963b77375768 Mon Sep 17 00:00:00 2001 From: James Yox Date: Sun, 22 Mar 2026 16:51:22 -0600 Subject: [PATCH 1/4] First draft of DOM interop --- src/commonMain/kotlin/api.kt | 7 +++++++ src/jsMain/kotlin/dom-js.kt | 19 ++++++++++++++++++- src/jsTest/kotlin/interoperable.kt | 22 ++++++++++++++++++++++ src/wasmJsMain/kotlin/dom-js.kt | 21 ++++++++++++++++++++- src/wasmJsTest/kotlin/interoperable.kt | 22 ++++++++++++++++++++++ 5 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 src/jsTest/kotlin/interoperable.kt create mode 100644 src/wasmJsTest/kotlin/interoperable.kt diff --git a/src/commonMain/kotlin/api.kt b/src/commonMain/kotlin/api.kt index 54a48b3e..dfa47c89 100644 --- a/src/commonMain/kotlin/api.kt +++ b/src/commonMain/kotlin/api.kt @@ -1,6 +1,9 @@ package kotlinx.html import kotlinx.html.org.w3c.dom.events.Event +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract interface TagConsumer { fun onTagStart(tag: Tag) @@ -14,6 +17,10 @@ interface TagConsumer { fun finalize(): R } +interface InteroperableTagConsumer : TagConsumer { + fun onRenderedContent(content: I) +} + @HtmlTagMarker interface Tag { val tagName: String diff --git a/src/jsMain/kotlin/dom-js.kt b/src/jsMain/kotlin/dom-js.kt index b1622317..e9dda123 100644 --- a/src/jsMain/kotlin/dom-js.kt +++ b/src/jsMain/kotlin/dom-js.kt @@ -2,9 +2,12 @@ package kotlinx.html.dom import kotlinx.html.DefaultUnsafe import kotlinx.html.Entities +import kotlinx.html.FlowContent +import kotlinx.html.InteroperableTagConsumer import kotlinx.html.Tag import kotlinx.html.TagConsumer import kotlinx.html.Unsafe +import kotlinx.html.consumers.FinalizeConsumer import kotlinx.html.consumers.onFinalize import kotlinx.html.org.w3c.dom.events.Event import org.w3c.dom.Document @@ -21,7 +24,7 @@ private inline fun HTMLElement.setEvent(name: String, noinline callback : (Event asDynamic()[name] = callback } -class JSDOMBuilder(val document : Document) : TagConsumer { +class JSDOMBuilder(val document : Document) : InteroperableTagConsumer { private val path = arrayListOf() private var lastLeaved : HTMLElement? = null @@ -116,8 +119,22 @@ class JSDOMBuilder(val document : Document) : TagConsumer Node) { + contract { callsInPlace(block, InvocationKind.AT_MOST_ONCE) } + val consumer = when (val topConsumer = consumer) { + is FinalizeConsumer<*,*> -> topConsumer.downstream + else -> null + } + if (consumer is JSDOMBuilder) { + consumer.onRenderedContent(block()) + } +} fun Document.createTree() : TagConsumer = JSDOMBuilder(this) val Document.create : TagConsumer diff --git a/src/jsTest/kotlin/interoperable.kt b/src/jsTest/kotlin/interoperable.kt new file mode 100644 index 00000000..ee591b4a --- /dev/null +++ b/src/jsTest/kotlin/interoperable.kt @@ -0,0 +1,22 @@ +import kotlinx.browser.document +import kotlinx.html.js.div +import kotlinx.html.dom.append +import kotlinx.html.dom.dom +import kotlin.test.Test +import kotlin.test.assertEquals + +class InteroperableImplTest { + @Test fun testInteroperableDOM() { + val wrapper = wrapper() + + wrapper.append.div { + dom { + document.createElement("svg") + } + } + + assertEquals(expected = "
", actual = wrapper.innerHTML) + } + + private fun wrapper() = document.body!!.append.div {} +} \ No newline at end of file diff --git a/src/wasmJsMain/kotlin/dom-js.kt b/src/wasmJsMain/kotlin/dom-js.kt index f5ae9d13..a9a1daa6 100644 --- a/src/wasmJsMain/kotlin/dom-js.kt +++ b/src/wasmJsMain/kotlin/dom-js.kt @@ -2,9 +2,12 @@ package kotlinx.html.dom import kotlinx.html.DefaultUnsafe import kotlinx.html.Entities +import kotlinx.html.FlowContent +import kotlinx.html.InteroperableTagConsumer import kotlinx.html.Tag import kotlinx.html.TagConsumer import kotlinx.html.Unsafe +import kotlinx.html.consumers.FinalizeConsumer import kotlinx.html.consumers.onFinalize import kotlinx.html.org.w3c.dom.events.Event import org.w3c.dom.Document @@ -21,7 +24,7 @@ private inline fun Element.setEvent(name: String, noinline callback: (Event) -> addEventListener(eventName, callback) } -class JSDOMBuilder(val document: Document) : TagConsumer { +class JSDOMBuilder(val document: Document) : InteroperableTagConsumer { private val path = arrayListOf() private var lastLeaved: Element? = null @@ -117,12 +120,28 @@ class JSDOMBuilder(val document: Document) : TagConsumer override fun finalize(): R = lastLeaved?.asR() ?: throw IllegalStateException("We can't finalize as there was no tags") + override fun onRenderedContent(content: Node) { + path.last().appendChild(content) + } + private inline fun Element.asR(): R { return jsCast(this) } } +@OptIn(ExperimentalContracts::class) +inline fun FlowContent.dom(crossinline block: () -> Node) { + contract { callsInPlace(block, InvocationKind.AT_MOST_ONCE) } + val consumer = when (val topConsumer = consumer) { + is FinalizeConsumer<*,*> -> topConsumer.downstream + else -> null + } + if (consumer is JSDOMBuilder) { + consumer.onRenderedContent(block()) + } +} + fun jsCast(any: JsAny): T = js("(any)") fun Document.createTree(): TagConsumer = JSDOMBuilder(this) diff --git a/src/wasmJsTest/kotlin/interoperable.kt b/src/wasmJsTest/kotlin/interoperable.kt new file mode 100644 index 00000000..ee591b4a --- /dev/null +++ b/src/wasmJsTest/kotlin/interoperable.kt @@ -0,0 +1,22 @@ +import kotlinx.browser.document +import kotlinx.html.js.div +import kotlinx.html.dom.append +import kotlinx.html.dom.dom +import kotlin.test.Test +import kotlin.test.assertEquals + +class InteroperableImplTest { + @Test fun testInteroperableDOM() { + val wrapper = wrapper() + + wrapper.append.div { + dom { + document.createElement("svg") + } + } + + assertEquals(expected = "
", actual = wrapper.innerHTML) + } + + private fun wrapper() = document.body!!.append.div {} +} \ No newline at end of file From 09baf3b05a321c0cbf1397a09a304026ff330059 Mon Sep 17 00:00:00 2001 From: James Yox Date: Thu, 26 Mar 2026 21:42:40 -0600 Subject: [PATCH 2/4] Resdesign with more recent suggestions --- build.gradle.kts | 10 ++++++-- src/commonMain/kotlin/api.kt | 10 +++----- src/commonMain/kotlin/delayed-consumer.kt | 3 +++ src/commonMain/kotlin/filter-consumer.kt | 3 +++ src/commonMain/kotlin/finalize-consumer.kt | 3 +++ src/commonMain/kotlin/measure-consumer.kt | 3 +++ src/commonMain/kotlin/stream.kt | 4 +++ src/jsMain/kotlin/dom-js.kt | 25 ++++-------------- src/jsTest/kotlin/interoperable.kt | 14 ++++++++-- src/jvmMain/kotlin/dom-jvm.kt | 4 +++ src/jvmTest/kotlin/interoperable.kt | 30 ++++++++++++++++++++++ src/wasmJsMain/kotlin/dom-js.kt | 24 +++-------------- src/wasmJsTest/kotlin/interoperable.kt | 14 ++++++++-- 13 files changed, 95 insertions(+), 52 deletions(-) create mode 100644 src/jvmTest/kotlin/interoperable.kt diff --git a/build.gradle.kts b/build.gradle.kts index aec7ff6e..f9aa0f97 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -90,7 +90,7 @@ kotlin { browser { testTask { useKarma { - useChromeHeadless() + useFirefox() } } } @@ -102,7 +102,13 @@ kotlin { } wasmJs { moduleName = project.name - browser() + browser { + testTask { + useKarma { + useFirefox() + } + } + } mavenPublication { groupId = group as String diff --git a/src/commonMain/kotlin/api.kt b/src/commonMain/kotlin/api.kt index dfa47c89..5cdc7722 100644 --- a/src/commonMain/kotlin/api.kt +++ b/src/commonMain/kotlin/api.kt @@ -1,9 +1,6 @@ package kotlinx.html import kotlinx.html.org.w3c.dom.events.Event -import kotlin.contracts.ExperimentalContracts -import kotlin.contracts.InvocationKind -import kotlin.contracts.contract interface TagConsumer { fun onTagStart(tag: Tag) @@ -15,11 +12,12 @@ interface TagConsumer { fun onTagContentUnsafe(block: Unsafe.() -> Unit) fun onTagComment(content: CharSequence) fun finalize(): R + @ExperimentalKotlinxHtmlApi + val last: Any? } -interface InteroperableTagConsumer : TagConsumer { - fun onRenderedContent(content: I) -} +@RequiresOptIn +annotation class ExperimentalKotlinxHtmlApi @HtmlTagMarker interface Tag { diff --git a/src/commonMain/kotlin/delayed-consumer.kt b/src/commonMain/kotlin/delayed-consumer.kt index baf810b3..f23cf0a7 100644 --- a/src/commonMain/kotlin/delayed-consumer.kt +++ b/src/commonMain/kotlin/delayed-consumer.kt @@ -48,6 +48,9 @@ class DelayedConsumer(val downstream: TagConsumer) : TagConsumer { return downstream.finalize() } + @ExperimentalKotlinxHtmlApi + override val last: Any? get() = downstream.last + override fun onTagContentUnsafe(block: Unsafe.() -> Unit) { processDelayedTag() return downstream.onTagContentUnsafe(block) diff --git a/src/commonMain/kotlin/filter-consumer.kt b/src/commonMain/kotlin/filter-consumer.kt index 8fc60af1..c7f1fc98 100644 --- a/src/commonMain/kotlin/filter-consumer.kt +++ b/src/commonMain/kotlin/filter-consumer.kt @@ -81,6 +81,9 @@ private class FilterTagConsumer(val downstream: TagConsumer, val predicate } override fun finalize(): T = downstream.finalize() + + @ExperimentalKotlinxHtmlApi + override val last: Any? get() = downstream.last } fun TagConsumer.filter(predicate: PredicateResults.(Tag) -> PredicateResult): TagConsumer = diff --git a/src/commonMain/kotlin/finalize-consumer.kt b/src/commonMain/kotlin/finalize-consumer.kt index 22b9484c..bda9c23a 100644 --- a/src/commonMain/kotlin/finalize-consumer.kt +++ b/src/commonMain/kotlin/finalize-consumer.kt @@ -26,6 +26,9 @@ class FinalizeConsumer(val downstream: TagConsumer, val block: (F, Bool override fun onTagComment(content: CharSequence) = downstream.onTagComment(content) override fun finalize() = block(downstream.finalize(), level > 0) + + @ExperimentalKotlinxHtmlApi + override val last: Any? get() = downstream.last } public fun TagConsumer.onFinalize(block: (from: T, partial: Boolean) -> Unit): TagConsumer = diff --git a/src/commonMain/kotlin/measure-consumer.kt b/src/commonMain/kotlin/measure-consumer.kt index ffec48ca..b8d3c9a2 100644 --- a/src/commonMain/kotlin/measure-consumer.kt +++ b/src/commonMain/kotlin/measure-consumer.kt @@ -41,6 +41,9 @@ private class TimeMeasureConsumer(val downstream: TagConsumer, val start: } override fun finalize(): TimedValue = TimedValue(downstream.finalize(), start.elapsedNow()) + + @ExperimentalKotlinxHtmlApi + override val last: Any? get() = downstream.last } fun TagConsumer.measureTime(timeSource: TimeSource = TimeSource.Monotonic): TagConsumer> = TimeMeasureConsumer(this, timeSource.markNow()) diff --git a/src/commonMain/kotlin/stream.kt b/src/commonMain/kotlin/stream.kt index 63fd9cd0..804ad4b6 100644 --- a/src/commonMain/kotlin/stream.kt +++ b/src/commonMain/kotlin/stream.kt @@ -86,6 +86,10 @@ class HTMLStreamBuilder( override fun finalize(): O = out + @ExperimentalKotlinxHtmlApi + override val last: Any? get() = out + + override fun onTagContentUnsafe(block: Unsafe.() -> Unit) { UnsafeImpl.block() } diff --git a/src/jsMain/kotlin/dom-js.kt b/src/jsMain/kotlin/dom-js.kt index e9dda123..69e6edda 100644 --- a/src/jsMain/kotlin/dom-js.kt +++ b/src/jsMain/kotlin/dom-js.kt @@ -2,12 +2,10 @@ package kotlinx.html.dom import kotlinx.html.DefaultUnsafe import kotlinx.html.Entities -import kotlinx.html.FlowContent -import kotlinx.html.InteroperableTagConsumer +import kotlinx.html.ExperimentalKotlinxHtmlApi import kotlinx.html.Tag import kotlinx.html.TagConsumer import kotlinx.html.Unsafe -import kotlinx.html.consumers.FinalizeConsumer import kotlinx.html.consumers.onFinalize import kotlinx.html.org.w3c.dom.events.Event import org.w3c.dom.Document @@ -24,7 +22,7 @@ private inline fun HTMLElement.setEvent(name: String, noinline callback : (Event asDynamic()[name] = callback } -class JSDOMBuilder(val document : Document) : InteroperableTagConsumer { +class JSDOMBuilder(val document : Document) : TagConsumer { private val path = arrayListOf() private var lastLeaved : HTMLElement? = null @@ -116,24 +114,11 @@ class JSDOMBuilder(val document : Document) : Interoperable override fun finalize(): R = lastLeaved?.asR() ?: throw IllegalStateException("We can't finalize as there was no tags") + @ExperimentalKotlinxHtmlApi + override val last: Any? get() = path.lastOrNull() + @Suppress("UnsafeCastFromDynamic") private fun HTMLElement.asR(): R = this.asDynamic() - - override fun onRenderedContent(content: Node) { - path.last().appendChild(content) - } -} - -@OptIn(ExperimentalContracts::class) -inline fun FlowContent.dom(crossinline block: () -> Node) { - contract { callsInPlace(block, InvocationKind.AT_MOST_ONCE) } - val consumer = when (val topConsumer = consumer) { - is FinalizeConsumer<*,*> -> topConsumer.downstream - else -> null - } - if (consumer is JSDOMBuilder) { - consumer.onRenderedContent(block()) - } } fun Document.createTree() : TagConsumer = JSDOMBuilder(this) diff --git a/src/jsTest/kotlin/interoperable.kt b/src/jsTest/kotlin/interoperable.kt index ee591b4a..c164bff9 100644 --- a/src/jsTest/kotlin/interoperable.kt +++ b/src/jsTest/kotlin/interoperable.kt @@ -1,16 +1,20 @@ import kotlinx.browser.document +import kotlinx.html.ExperimentalKotlinxHtmlApi +import kotlinx.html.Tag import kotlinx.html.js.div import kotlinx.html.dom.append -import kotlinx.html.dom.dom +import org.w3c.dom.HTMLElement +import org.w3c.dom.Node import kotlin.test.Test import kotlin.test.assertEquals +@OptIn(ExperimentalKotlinxHtmlApi::class) class InteroperableImplTest { @Test fun testInteroperableDOM() { val wrapper = wrapper() wrapper.append.div { - dom { + interop { document.createElement("svg") } } @@ -19,4 +23,10 @@ class InteroperableImplTest { } private fun wrapper() = document.body!!.append.div {} + + // Stand in for how a library might want to interop with kotlinx-html + private fun Tag.interop(block: () -> Node) { + val last = consumer.last as HTMLElement + last.appendChild(block()) + } } \ No newline at end of file diff --git a/src/jvmMain/kotlin/dom-jvm.kt b/src/jvmMain/kotlin/dom-jvm.kt index b13b6e26..4e1f7005 100644 --- a/src/jvmMain/kotlin/dom-jvm.kt +++ b/src/jvmMain/kotlin/dom-jvm.kt @@ -1,6 +1,7 @@ package kotlinx.html.dom import kotlinx.html.Entities +import kotlinx.html.ExperimentalKotlinxHtmlApi import kotlinx.html.Tag import kotlinx.html.TagConsumer import kotlinx.html.Unsafe @@ -100,6 +101,9 @@ class HTMLDOMBuilder(val document : Document) : TagConsumer { override fun finalize() = lastLeaved ?: throw IllegalStateException("No tags were emitted") + @ExperimentalKotlinxHtmlApi + override val last: Any? get() = path.lastOrNull() + override fun onTagContentUnsafe(block: Unsafe.() -> Unit) { UnsafeImpl.block() } diff --git a/src/jvmTest/kotlin/interoperable.kt b/src/jvmTest/kotlin/interoperable.kt new file mode 100644 index 00000000..7e7c59d1 --- /dev/null +++ b/src/jvmTest/kotlin/interoperable.kt @@ -0,0 +1,30 @@ +import kotlinx.html.ExperimentalKotlinxHtmlApi +import kotlinx.html.Tag +import kotlinx.html.div +import kotlinx.html.dom.createHTMLDocument +import kotlinx.html.dom.serialize +import org.w3c.dom.Document +import org.w3c.dom.Element +import org.w3c.dom.Node +import javax.xml.parsers.DocumentBuilderFactory +import kotlin.test.Test +import kotlin.test.assertEquals + +@OptIn(ExperimentalKotlinxHtmlApi::class) +class InteroperableImplTest { + @Test fun testInteroperableDOM() { + DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument() + val tree = createHTMLDocument().div { + interop { + it.createElement("svg") + } + } + assertEquals(expected = "\n
", actual = tree.serialize(prettyPrint = false)) + } + + // Stand in for how a library might want to interop with kotlinx-html + private fun Tag.interop(block: (Document) -> Node) { + val last = consumer.last as Element + last.appendChild(block(last.ownerDocument)) + } +} \ No newline at end of file diff --git a/src/wasmJsMain/kotlin/dom-js.kt b/src/wasmJsMain/kotlin/dom-js.kt index a9a1daa6..ab306050 100644 --- a/src/wasmJsMain/kotlin/dom-js.kt +++ b/src/wasmJsMain/kotlin/dom-js.kt @@ -2,12 +2,10 @@ package kotlinx.html.dom import kotlinx.html.DefaultUnsafe import kotlinx.html.Entities -import kotlinx.html.FlowContent -import kotlinx.html.InteroperableTagConsumer +import kotlinx.html.ExperimentalKotlinxHtmlApi import kotlinx.html.Tag import kotlinx.html.TagConsumer import kotlinx.html.Unsafe -import kotlinx.html.consumers.FinalizeConsumer import kotlinx.html.consumers.onFinalize import kotlinx.html.org.w3c.dom.events.Event import org.w3c.dom.Document @@ -24,7 +22,7 @@ private inline fun Element.setEvent(name: String, noinline callback: (Event) -> addEventListener(eventName, callback) } -class JSDOMBuilder(val document: Document) : InteroperableTagConsumer { +class JSDOMBuilder(val document: Document) : TagConsumer { private val path = arrayListOf() private var lastLeaved: Element? = null @@ -120,26 +118,12 @@ class JSDOMBuilder(val document: Document) : InteroperableT override fun finalize(): R = lastLeaved?.asR() ?: throw IllegalStateException("We can't finalize as there was no tags") - override fun onRenderedContent(content: Node) { - path.last().appendChild(content) - } + @ExperimentalKotlinxHtmlApi + override val last: Any? get() = path.lastOrNull() private inline fun Element.asR(): R { return jsCast(this) } - -} - -@OptIn(ExperimentalContracts::class) -inline fun FlowContent.dom(crossinline block: () -> Node) { - contract { callsInPlace(block, InvocationKind.AT_MOST_ONCE) } - val consumer = when (val topConsumer = consumer) { - is FinalizeConsumer<*,*> -> topConsumer.downstream - else -> null - } - if (consumer is JSDOMBuilder) { - consumer.onRenderedContent(block()) - } } fun jsCast(any: JsAny): T = js("(any)") diff --git a/src/wasmJsTest/kotlin/interoperable.kt b/src/wasmJsTest/kotlin/interoperable.kt index ee591b4a..c164bff9 100644 --- a/src/wasmJsTest/kotlin/interoperable.kt +++ b/src/wasmJsTest/kotlin/interoperable.kt @@ -1,16 +1,20 @@ import kotlinx.browser.document +import kotlinx.html.ExperimentalKotlinxHtmlApi +import kotlinx.html.Tag import kotlinx.html.js.div import kotlinx.html.dom.append -import kotlinx.html.dom.dom +import org.w3c.dom.HTMLElement +import org.w3c.dom.Node import kotlin.test.Test import kotlin.test.assertEquals +@OptIn(ExperimentalKotlinxHtmlApi::class) class InteroperableImplTest { @Test fun testInteroperableDOM() { val wrapper = wrapper() wrapper.append.div { - dom { + interop { document.createElement("svg") } } @@ -19,4 +23,10 @@ class InteroperableImplTest { } private fun wrapper() = document.body!!.append.div {} + + // Stand in for how a library might want to interop with kotlinx-html + private fun Tag.interop(block: () -> Node) { + val last = consumer.last as HTMLElement + last.appendChild(block()) + } } \ No newline at end of file From f1886def5070ea6497df372a6cd5ccbcee57bde2 Mon Sep 17 00:00:00 2001 From: James Yox Date: Thu, 26 Mar 2026 21:44:47 -0600 Subject: [PATCH 3/4] Couple things I missed before pushing --- build.gradle.kts | 10 ++-------- src/commonMain/kotlin/stream.kt | 1 - 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index f9aa0f97..aec7ff6e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -90,7 +90,7 @@ kotlin { browser { testTask { useKarma { - useFirefox() + useChromeHeadless() } } } @@ -102,13 +102,7 @@ kotlin { } wasmJs { moduleName = project.name - browser { - testTask { - useKarma { - useFirefox() - } - } - } + browser() mavenPublication { groupId = group as String diff --git a/src/commonMain/kotlin/stream.kt b/src/commonMain/kotlin/stream.kt index 804ad4b6..fee27edc 100644 --- a/src/commonMain/kotlin/stream.kt +++ b/src/commonMain/kotlin/stream.kt @@ -89,7 +89,6 @@ class HTMLStreamBuilder( @ExperimentalKotlinxHtmlApi override val last: Any? get() = out - override fun onTagContentUnsafe(block: Unsafe.() -> Unit) { UnsafeImpl.block() } From 6cd181c8978a2cda0a7beee7c92bb559dac477f1 Mon Sep 17 00:00:00 2001 From: James Yox Date: Sat, 28 Mar 2026 11:16:20 -0600 Subject: [PATCH 4/4] More PR Comments --- src/commonMain/kotlin/api.kt | 2 +- src/commonMain/kotlin/delayed-consumer.kt | 2 +- src/commonMain/kotlin/filter-consumer.kt | 2 +- src/commonMain/kotlin/finalize-consumer.kt | 2 +- src/commonMain/kotlin/measure-consumer.kt | 2 +- src/commonMain/kotlin/stream.kt | 2 +- src/jsMain/kotlin/dom-js.kt | 2 +- src/jsTest/kotlin/interoperable.kt | 2 +- src/jvmMain/kotlin/dom-jvm.kt | 2 +- src/jvmTest/kotlin/interoperable.kt | 2 +- src/wasmJsMain/kotlin/dom-js.kt | 2 +- src/wasmJsTest/kotlin/interoperable.kt | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/commonMain/kotlin/api.kt b/src/commonMain/kotlin/api.kt index 5cdc7722..064f31c5 100644 --- a/src/commonMain/kotlin/api.kt +++ b/src/commonMain/kotlin/api.kt @@ -13,7 +13,7 @@ interface TagConsumer { fun onTagComment(content: CharSequence) fun finalize(): R @ExperimentalKotlinxHtmlApi - val last: Any? + val head: Any? } @RequiresOptIn diff --git a/src/commonMain/kotlin/delayed-consumer.kt b/src/commonMain/kotlin/delayed-consumer.kt index f23cf0a7..ac4fa8ca 100644 --- a/src/commonMain/kotlin/delayed-consumer.kt +++ b/src/commonMain/kotlin/delayed-consumer.kt @@ -49,7 +49,7 @@ class DelayedConsumer(val downstream: TagConsumer) : TagConsumer { } @ExperimentalKotlinxHtmlApi - override val last: Any? get() = downstream.last + override val head: Any? get() = downstream.head override fun onTagContentUnsafe(block: Unsafe.() -> Unit) { processDelayedTag() diff --git a/src/commonMain/kotlin/filter-consumer.kt b/src/commonMain/kotlin/filter-consumer.kt index c7f1fc98..de99f583 100644 --- a/src/commonMain/kotlin/filter-consumer.kt +++ b/src/commonMain/kotlin/filter-consumer.kt @@ -83,7 +83,7 @@ private class FilterTagConsumer(val downstream: TagConsumer, val predicate override fun finalize(): T = downstream.finalize() @ExperimentalKotlinxHtmlApi - override val last: Any? get() = downstream.last + override val head: Any? get() = downstream.head } fun TagConsumer.filter(predicate: PredicateResults.(Tag) -> PredicateResult): TagConsumer = diff --git a/src/commonMain/kotlin/finalize-consumer.kt b/src/commonMain/kotlin/finalize-consumer.kt index bda9c23a..425b72e1 100644 --- a/src/commonMain/kotlin/finalize-consumer.kt +++ b/src/commonMain/kotlin/finalize-consumer.kt @@ -28,7 +28,7 @@ class FinalizeConsumer(val downstream: TagConsumer, val block: (F, Bool override fun finalize() = block(downstream.finalize(), level > 0) @ExperimentalKotlinxHtmlApi - override val last: Any? get() = downstream.last + override val head: Any? get() = downstream.head } public fun TagConsumer.onFinalize(block: (from: T, partial: Boolean) -> Unit): TagConsumer = diff --git a/src/commonMain/kotlin/measure-consumer.kt b/src/commonMain/kotlin/measure-consumer.kt index b8d3c9a2..c2346f2c 100644 --- a/src/commonMain/kotlin/measure-consumer.kt +++ b/src/commonMain/kotlin/measure-consumer.kt @@ -43,7 +43,7 @@ private class TimeMeasureConsumer(val downstream: TagConsumer, val start: override fun finalize(): TimedValue = TimedValue(downstream.finalize(), start.elapsedNow()) @ExperimentalKotlinxHtmlApi - override val last: Any? get() = downstream.last + override val head: Any? get() = downstream.head } fun TagConsumer.measureTime(timeSource: TimeSource = TimeSource.Monotonic): TagConsumer> = TimeMeasureConsumer(this, timeSource.markNow()) diff --git a/src/commonMain/kotlin/stream.kt b/src/commonMain/kotlin/stream.kt index fee27edc..f3ccbe12 100644 --- a/src/commonMain/kotlin/stream.kt +++ b/src/commonMain/kotlin/stream.kt @@ -87,7 +87,7 @@ class HTMLStreamBuilder( override fun finalize(): O = out @ExperimentalKotlinxHtmlApi - override val last: Any? get() = out + override val head: O get() = out override fun onTagContentUnsafe(block: Unsafe.() -> Unit) { UnsafeImpl.block() diff --git a/src/jsMain/kotlin/dom-js.kt b/src/jsMain/kotlin/dom-js.kt index 69e6edda..516f4f39 100644 --- a/src/jsMain/kotlin/dom-js.kt +++ b/src/jsMain/kotlin/dom-js.kt @@ -115,7 +115,7 @@ class JSDOMBuilder(val document : Document) : TagConsumer Node) { - val last = consumer.last as HTMLElement + val last = consumer.head as HTMLElement last.appendChild(block()) } } \ No newline at end of file diff --git a/src/jvmMain/kotlin/dom-jvm.kt b/src/jvmMain/kotlin/dom-jvm.kt index 4e1f7005..caf04cb6 100644 --- a/src/jvmMain/kotlin/dom-jvm.kt +++ b/src/jvmMain/kotlin/dom-jvm.kt @@ -102,7 +102,7 @@ class HTMLDOMBuilder(val document : Document) : TagConsumer { override fun finalize() = lastLeaved ?: throw IllegalStateException("No tags were emitted") @ExperimentalKotlinxHtmlApi - override val last: Any? get() = path.lastOrNull() + override val head: Element get() = path.last() override fun onTagContentUnsafe(block: Unsafe.() -> Unit) { UnsafeImpl.block() diff --git a/src/jvmTest/kotlin/interoperable.kt b/src/jvmTest/kotlin/interoperable.kt index 7e7c59d1..f17811a9 100644 --- a/src/jvmTest/kotlin/interoperable.kt +++ b/src/jvmTest/kotlin/interoperable.kt @@ -24,7 +24,7 @@ class InteroperableImplTest { // Stand in for how a library might want to interop with kotlinx-html private fun Tag.interop(block: (Document) -> Node) { - val last = consumer.last as Element + val last = consumer.head as Element last.appendChild(block(last.ownerDocument)) } } \ No newline at end of file diff --git a/src/wasmJsMain/kotlin/dom-js.kt b/src/wasmJsMain/kotlin/dom-js.kt index ab306050..b1e71d22 100644 --- a/src/wasmJsMain/kotlin/dom-js.kt +++ b/src/wasmJsMain/kotlin/dom-js.kt @@ -119,7 +119,7 @@ class JSDOMBuilder(val document: Document) : TagConsumer lastLeaved?.asR() ?: throw IllegalStateException("We can't finalize as there was no tags") @ExperimentalKotlinxHtmlApi - override val last: Any? get() = path.lastOrNull() + override val head: Element get() = path.last() private inline fun Element.asR(): R { return jsCast(this) diff --git a/src/wasmJsTest/kotlin/interoperable.kt b/src/wasmJsTest/kotlin/interoperable.kt index c164bff9..5ae47281 100644 --- a/src/wasmJsTest/kotlin/interoperable.kt +++ b/src/wasmJsTest/kotlin/interoperable.kt @@ -26,7 +26,7 @@ class InteroperableImplTest { // Stand in for how a library might want to interop with kotlinx-html private fun Tag.interop(block: () -> Node) { - val last = consumer.last as HTMLElement + val last = consumer.head as HTMLElement last.appendChild(block()) } } \ No newline at end of file