forked from kscience/visionforge
Working server prototype
This commit is contained in:
parent
a7136d3eff
commit
faddb8a393
@ -3,17 +3,19 @@ package ru.mipt.npm.sat
|
|||||||
|
|
||||||
import hep.dataforge.context.Global
|
import hep.dataforge.context.Global
|
||||||
import hep.dataforge.names.asName
|
import hep.dataforge.names.asName
|
||||||
|
import hep.dataforge.vision.VisionManager
|
||||||
import hep.dataforge.vision.get
|
import hep.dataforge.vision.get
|
||||||
import hep.dataforge.vision.server.visionModule
|
import hep.dataforge.vision.server.close
|
||||||
|
import hep.dataforge.vision.server.serve
|
||||||
|
import hep.dataforge.vision.server.show
|
||||||
import hep.dataforge.vision.solid.Solid
|
import hep.dataforge.vision.solid.Solid
|
||||||
import hep.dataforge.vision.solid.SolidManager
|
import hep.dataforge.vision.solid.SolidManager
|
||||||
import hep.dataforge.vision.solid.color
|
import hep.dataforge.vision.solid.color
|
||||||
import io.ktor.server.cio.CIO
|
|
||||||
import io.ktor.server.engine.embeddedServer
|
|
||||||
import io.ktor.util.KtorExperimentalAPI
|
import io.ktor.util.KtorExperimentalAPI
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.html.h1
|
||||||
import kotlinx.html.script
|
import kotlinx.html.script
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
@ -23,28 +25,36 @@ fun main() {
|
|||||||
ySegments = 3,
|
ySegments = 3,
|
||||||
)
|
)
|
||||||
|
|
||||||
val context = Global.context("SAT"){
|
val context = Global.context("SAT") {
|
||||||
plugin(SolidManager)
|
plugin(SolidManager)
|
||||||
}
|
}
|
||||||
|
|
||||||
embeddedServer(CIO, 8080, host = "localhost"){
|
val server = context.plugins.fetch(VisionManager).serve {
|
||||||
visionModule(context).apply {
|
|
||||||
header {
|
header {
|
||||||
script {
|
script {
|
||||||
src = "sat-demo.js"
|
src = "sat-demo.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
page {
|
page {
|
||||||
|
h1 { +"Satellite detector demo" }
|
||||||
vision("main".asName(), sat)
|
vision("main".asName(), sat)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
launch {
|
launch {
|
||||||
while (isActive){
|
while (isActive) {
|
||||||
val currentLayer = Random.nextInt(10)
|
val currentLayer = Random.nextInt(10)
|
||||||
(sat["layer[$currentLayer]"] as? Solid)?.color(123)
|
(sat["layer[$currentLayer]"] as? Solid)?.color(123)
|
||||||
delay(300)
|
delay(300)
|
||||||
(sat["layer[$currentLayer]"] as? Solid)?.color = null
|
(sat["layer[$currentLayer]"] as? Solid)?.color = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.start(wait = true)
|
}
|
||||||
|
server.show()
|
||||||
|
|
||||||
|
println("Press Enter to close server")
|
||||||
|
while (readLine()!="exit"){
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
server.close()
|
||||||
|
|
||||||
}
|
}
|
@ -20,7 +20,7 @@ import org.w3c.dom.Element
|
|||||||
import org.w3c.dom.HTMLDivElement
|
import org.w3c.dom.HTMLDivElement
|
||||||
import org.w3c.dom.HTMLElement
|
import org.w3c.dom.HTMLElement
|
||||||
|
|
||||||
class ThreeDemoGrid(element: Element, idPrefix: String = "") : Page<Solid> {
|
class ThreeDemoGrid(element: Element) : Page<Solid> {
|
||||||
private lateinit var navigationElement: HTMLElement
|
private lateinit var navigationElement: HTMLElement
|
||||||
private lateinit var contentElement: HTMLDivElement
|
private lateinit var contentElement: HTMLDivElement
|
||||||
|
|
||||||
|
@ -5,23 +5,23 @@ import hep.dataforge.vision.Vision
|
|||||||
import kotlinx.html.FlowContent
|
import kotlinx.html.FlowContent
|
||||||
import kotlinx.html.TagConsumer
|
import kotlinx.html.TagConsumer
|
||||||
|
|
||||||
public class BindingHtmlOutputScope<T, V : Vision>(
|
public class BindingOutputTagConsumer<T, V : Vision>(
|
||||||
root: TagConsumer<T>,
|
root: TagConsumer<T>,
|
||||||
prefix: String? = null,
|
prefix: String? = null,
|
||||||
) : HtmlOutputScope<T, V>(root, prefix) {
|
) : OutputTagConsumer<T, V>(root, prefix) {
|
||||||
|
|
||||||
private val _bindings = HashMap<Name, V>()
|
private val _bindings = HashMap<Name, V>()
|
||||||
public val bindings: Map<Name, V> get() = _bindings
|
public val bindings: Map<Name, V> get() = _bindings
|
||||||
|
|
||||||
override fun renderVision(htmlOutput: HtmlOutput<V>, vision: V) {
|
override fun FlowContent.renderVision(name: Name, vision: V) {
|
||||||
_bindings[htmlOutput.name] = vision
|
_bindings[name] = vision
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun <T : Any> TagConsumer<T>.visionFragment(fragment: HtmlVisionFragment<Vision>): Map<Name, Vision> {
|
public fun <T : Any> TagConsumer<T>.visionFragment(fragment: HtmlVisionFragment<Vision>): Map<Name, Vision> {
|
||||||
return BindingHtmlOutputScope<T, Vision>(this).apply(fragment.content).bindings
|
return BindingOutputTagConsumer<T, Vision>(this).apply(fragment.content).bindings
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun FlowContent.visionFragment(fragment: HtmlVisionFragment<Vision>): Map<Name, Vision> {
|
public fun FlowContent.visionFragment(fragment: HtmlVisionFragment<Vision>): Map<Name, Vision> {
|
||||||
return BindingHtmlOutputScope<Any?, Vision>(consumer).apply(fragment.content).bindings
|
return BindingOutputTagConsumer<Any?, Vision>(consumer).apply(fragment.content).bindings
|
||||||
}
|
}
|
@ -14,7 +14,7 @@ public fun FlowContent.fragment(fragment: HtmlFragment) {
|
|||||||
fragment.content(consumer)
|
fragment.content(consumer)
|
||||||
}
|
}
|
||||||
|
|
||||||
public class HtmlVisionFragment<V : Vision>(public val content: HtmlOutputScope<*, V>.() -> Unit)
|
public class HtmlVisionFragment<V : Vision>(public val content: OutputTagConsumer<*, V>.() -> Unit)
|
||||||
|
|
||||||
public fun buildVisionFragment(block: HtmlOutputScope<*, Vision>.() -> Unit): HtmlVisionFragment<Vision> =
|
public fun buildVisionFragment(block: OutputTagConsumer<*, Vision>.() -> Unit): HtmlVisionFragment<Vision> =
|
||||||
HtmlVisionFragment(block)
|
HtmlVisionFragment(block)
|
||||||
|
@ -5,47 +5,52 @@ import hep.dataforge.names.toName
|
|||||||
import hep.dataforge.vision.Vision
|
import hep.dataforge.vision.Vision
|
||||||
import kotlinx.html.*
|
import kotlinx.html.*
|
||||||
|
|
||||||
public class HtmlOutput<V : Vision>(
|
/**
|
||||||
public val outputScope: HtmlOutputScope<*, V>,
|
* An HTML div wrapper that includes the output [name] and inherited [render] function
|
||||||
|
*/
|
||||||
|
public class OutputDiv<in V : Vision>(
|
||||||
|
private val div: DIV,
|
||||||
public val name: Name,
|
public val name: Name,
|
||||||
public val div: DIV,
|
public val render: (V) -> Unit,
|
||||||
)
|
) : HtmlBlockTag by div
|
||||||
|
|
||||||
public abstract class HtmlOutputScope<R, V : Vision>(
|
/**
|
||||||
|
* Modified [TagConsumer] that allows rendering output fragments and visions in them
|
||||||
|
*/
|
||||||
|
public abstract class OutputTagConsumer<R, V : Vision>(
|
||||||
private val root: TagConsumer<R>,
|
private val root: TagConsumer<R>,
|
||||||
public val idPrefix: String? = null,
|
private val idPrefix: String? = null,
|
||||||
) : TagConsumer<R> by root {
|
) : TagConsumer<R> by root {
|
||||||
|
|
||||||
public open fun resolveId(name: Name): String = (idPrefix ?: "output:") + name.toString()
|
public open fun resolveId(name: Name): String = (idPrefix ?: "output:") + name.toString()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a placeholder but do not attach any [Vision] to it
|
* Render a vision inside the output fragment
|
||||||
*/
|
*/
|
||||||
public inline fun <T> TagConsumer<T>.visionOutput(
|
protected abstract fun FlowContent.renderVision(name: Name, vision: V)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a placeholder for an output window
|
||||||
|
*/
|
||||||
|
public fun <T> TagConsumer<T>.visionOutput(
|
||||||
name: Name,
|
name: Name,
|
||||||
crossinline block: HtmlOutput<V>.() -> Unit = {},
|
block: OutputDiv<V>.() -> Unit = {},
|
||||||
): T = div {
|
): T = div {
|
||||||
id = resolveId(name)
|
id = resolveId(name)
|
||||||
classes = setOf(OUTPUT_CLASS)
|
classes = setOf(OUTPUT_CLASS)
|
||||||
attributes[OUTPUT_NAME_ATTRIBUTE] = name.toString()
|
attributes[OUTPUT_NAME_ATTRIBUTE] = name.toString()
|
||||||
@Suppress("UNCHECKED_CAST")
|
OutputDiv<V>(this, name) { renderVision(name, it) }.block()
|
||||||
HtmlOutput(this@HtmlOutputScope, name, this).block()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fun <T> TagConsumer<T>.visionOutput(
|
||||||
public inline fun <T> TagConsumer<T>.visionOutput(
|
|
||||||
name: String,
|
name: String,
|
||||||
crossinline block: HtmlOutput<V>.() -> Unit = {},
|
block: OutputDiv<V>.() -> Unit = {},
|
||||||
): T = visionOutput(name.toName(), block)
|
): T = visionOutput(name.toName(), block)
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a placeholder and put a [Vision] in it
|
|
||||||
*/
|
|
||||||
public abstract fun renderVision(htmlOutput: HtmlOutput<V>, vision: V)
|
|
||||||
|
|
||||||
public fun <T> TagConsumer<T>.vision(name: Name, vision: V): Unit {
|
public fun <T> TagConsumer<T>.vision(name: Name, vision: V): Unit {
|
||||||
visionOutput(name) {
|
visionOutput(name) {
|
||||||
renderVision(this, vision)
|
render(vision)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
|||||||
package hep.dataforge.vision.html
|
package hep.dataforge.vision.html
|
||||||
|
|
||||||
|
import hep.dataforge.names.Name
|
||||||
import hep.dataforge.vision.Vision
|
import hep.dataforge.vision.Vision
|
||||||
import kotlinx.html.FlowContent
|
import kotlinx.html.FlowContent
|
||||||
import kotlinx.html.TagConsumer
|
import kotlinx.html.TagConsumer
|
||||||
@ -7,22 +8,23 @@ import kotlinx.html.stream.createHTML
|
|||||||
|
|
||||||
public typealias HtmlVisionRenderer<V> = FlowContent.(V) -> Unit
|
public typealias HtmlVisionRenderer<V> = FlowContent.(V) -> Unit
|
||||||
|
|
||||||
public class StaticHtmlOutputScope<R, V : Vision>(
|
/**
|
||||||
|
* An [OutputTagConsumer] that directly renders given [Vision] using provided [renderer]
|
||||||
|
*/
|
||||||
|
public class StaticOutputTagConsumer<R, V : Vision>(
|
||||||
root: TagConsumer<R>,
|
root: TagConsumer<R>,
|
||||||
prefix: String? = null,
|
prefix: String? = null,
|
||||||
private val render: HtmlVisionRenderer<V>,
|
private val renderer: HtmlVisionRenderer<V>,
|
||||||
) : HtmlOutputScope<R, V>(root, prefix) {
|
) : OutputTagConsumer<R, V>(root, prefix) {
|
||||||
|
|
||||||
override fun renderVision(htmlOutput: HtmlOutput<V>, vision: V) {
|
override fun FlowContent.renderVision(name: Name, vision: V): Unit = renderer(vision)
|
||||||
htmlOutput.div.render(vision)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun <T : Any> HtmlVisionFragment<Vision>.renderToObject(
|
public fun <T : Any> HtmlVisionFragment<Vision>.renderToObject(
|
||||||
root: TagConsumer<T>,
|
root: TagConsumer<T>,
|
||||||
prefix: String? = null,
|
prefix: String? = null,
|
||||||
renderer: HtmlVisionRenderer<Vision>,
|
renderer: HtmlVisionRenderer<Vision>,
|
||||||
): T = StaticHtmlOutputScope(root, prefix, renderer).apply(content).finalize()
|
): T = StaticOutputTagConsumer(root, prefix, renderer).apply(content).finalize()
|
||||||
|
|
||||||
public fun HtmlVisionFragment<Vision>.renderToString(renderer: HtmlVisionRenderer<Vision>): String =
|
public fun HtmlVisionFragment<Vision>.renderToString(renderer: HtmlVisionRenderer<Vision>): String =
|
||||||
renderToObject(createHTML(), null, renderer)
|
renderToObject(createHTML(), null, renderer)
|
@ -10,16 +10,17 @@ import kotlin.test.Test
|
|||||||
|
|
||||||
class HtmlTagTest {
|
class HtmlTagTest {
|
||||||
|
|
||||||
fun HtmlOutput<Vision>.vision(block: Vision.() -> Unit) =
|
fun OutputDiv<Vision>.visionBase(block: VisionBase.() -> Unit) =
|
||||||
outputScope.renderVision(this, VisionBase().apply(block))
|
render(VisionBase().apply(block))
|
||||||
|
|
||||||
val fragment = buildVisionFragment {
|
val fragment = buildVisionFragment {
|
||||||
div {
|
div {
|
||||||
h1 { +"Head" }
|
h1 { +"Head" }
|
||||||
visionOutput("ddd") {
|
visionOutput("ddd") {
|
||||||
vision {
|
visionBase {
|
||||||
configure {
|
configure {
|
||||||
set("myProp", 82)
|
set("myProp", 82)
|
||||||
|
set("otherProp", false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,14 @@ package hep.dataforge.vision.client
|
|||||||
|
|
||||||
import hep.dataforge.context.*
|
import hep.dataforge.context.*
|
||||||
import hep.dataforge.meta.Meta
|
import hep.dataforge.meta.Meta
|
||||||
|
import hep.dataforge.meta.get
|
||||||
|
import hep.dataforge.meta.node
|
||||||
import hep.dataforge.vision.Vision
|
import hep.dataforge.vision.Vision
|
||||||
import hep.dataforge.vision.VisionChange
|
import hep.dataforge.vision.VisionChange
|
||||||
import hep.dataforge.vision.VisionManager
|
import hep.dataforge.vision.VisionManager
|
||||||
import hep.dataforge.vision.html.HtmlOutputScope
|
import hep.dataforge.vision.html.OutputTagConsumer
|
||||||
import hep.dataforge.vision.html.HtmlOutputScope.Companion.OUTPUT_ENDPOINT_ATTRIBUTE
|
import hep.dataforge.vision.html.OutputTagConsumer.Companion.OUTPUT_ENDPOINT_ATTRIBUTE
|
||||||
import hep.dataforge.vision.html.HtmlOutputScope.Companion.OUTPUT_NAME_ATTRIBUTE
|
import hep.dataforge.vision.html.OutputTagConsumer.Companion.OUTPUT_NAME_ATTRIBUTE
|
||||||
import kotlinx.browser.document
|
import kotlinx.browser.document
|
||||||
import kotlinx.browser.window
|
import kotlinx.browser.window
|
||||||
import org.w3c.dom.Element
|
import org.w3c.dom.Element
|
||||||
@ -41,12 +43,12 @@ public class VisionClient : AbstractPlugin() {
|
|||||||
getRenderers().maxByOrNull { it.rateVision(vision) }
|
getRenderers().maxByOrNull { it.rateVision(vision) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch from server and render a vision, described in a given with [HtmlOutputScope.OUTPUT_CLASS] class.
|
* Fetch from server and render a vision, described in a given with [OutputTagConsumer.OUTPUT_CLASS] class.
|
||||||
*/
|
*/
|
||||||
public fun fetchAndRenderVision(element: Element, requestUpdates: Boolean = true) {
|
public fun fetchAndRenderVision(element: Element, requestUpdates: Boolean = true) {
|
||||||
val name = resolveName(element) ?: error("The element is not a vision output")
|
val name = resolveName(element) ?: error("The element is not a vision output")
|
||||||
console.info("Found DF output with name $name")
|
console.info("Found DF output with name $name")
|
||||||
if (!element.classList.contains(HtmlOutputScope.OUTPUT_CLASS)) error("The element $element is not an output element")
|
if (!element.classList.contains(OutputTagConsumer.OUTPUT_CLASS)) error("The element $element is not an output element")
|
||||||
val endpoint = resolveEndpoint(element)
|
val endpoint = resolveEndpoint(element)
|
||||||
console.info("Vision server is resolved to $endpoint")
|
console.info("Vision server is resolved to $endpoint")
|
||||||
|
|
||||||
@ -60,9 +62,9 @@ public class VisionClient : AbstractPlugin() {
|
|||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
response.text().then { text ->
|
response.text().then { text ->
|
||||||
val vision = visionManager.decodeFromString(text)
|
val vision = visionManager.decodeFromString(text)
|
||||||
|
|
||||||
val renderer = findRendererFor(vision) ?: error("Could nof find renderer for $vision")
|
val renderer = findRendererFor(vision) ?: error("Could nof find renderer for $vision")
|
||||||
renderer.render(element, vision)
|
val rendererConfiguration = vision.properties[RENDERER_CONFIGURATION_META_KEY].node ?: Meta.EMPTY
|
||||||
|
renderer.render(element, vision, rendererConfiguration)
|
||||||
if (requestUpdates) {
|
if (requestUpdates) {
|
||||||
val wsUrl = URL(endpoint).apply {
|
val wsUrl = URL(endpoint).apply {
|
||||||
pathname += "/ws"
|
pathname += "/ws"
|
||||||
@ -73,9 +75,10 @@ public class VisionClient : AbstractPlugin() {
|
|||||||
onmessage = { messageEvent ->
|
onmessage = { messageEvent ->
|
||||||
val stringData: String? = messageEvent.data as? String
|
val stringData: String? = messageEvent.data as? String
|
||||||
if (stringData != null) {
|
if (stringData != null) {
|
||||||
// console.info("Received WS update: $stringData")
|
val dif = visionManager.jsonFormat.decodeFromString(
|
||||||
val dif = visionManager.jsonFormat
|
VisionChange.serializer(),
|
||||||
.decodeFromString(VisionChange.serializer(), stringData)
|
stringData
|
||||||
|
)
|
||||||
vision.update(dif)
|
vision.update(dif)
|
||||||
} else {
|
} else {
|
||||||
console.error("WebSocket message data is not a string")
|
console.error("WebSocket message data is not a string")
|
||||||
@ -103,6 +106,8 @@ public class VisionClient : AbstractPlugin() {
|
|||||||
|
|
||||||
public companion object : PluginFactory<VisionClient> {
|
public companion object : PluginFactory<VisionClient> {
|
||||||
|
|
||||||
|
public const val RENDERER_CONFIGURATION_META_KEY: String = "@renderer"
|
||||||
|
|
||||||
override fun invoke(meta: Meta, context: Context): VisionClient = VisionClient()
|
override fun invoke(meta: Meta, context: Context): VisionClient = VisionClient()
|
||||||
|
|
||||||
override val tag: PluginTag = PluginTag(name = "vision.client", group = PluginTag.DATAFORGE_GROUP)
|
override val tag: PluginTag = PluginTag(name = "vision.client", group = PluginTag.DATAFORGE_GROUP)
|
||||||
@ -112,10 +117,10 @@ public class VisionClient : AbstractPlugin() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch and render visions for all elements with [HtmlOutputScope.OUTPUT_CLASS] class inside given [element].
|
* Fetch and render visions for all elements with [OutputTagConsumer.OUTPUT_CLASS] class inside given [element].
|
||||||
*/
|
*/
|
||||||
public fun VisionClient.fetchVisionsInChildren(element: Element, requestUpdates: Boolean = true) {
|
public fun VisionClient.fetchVisionsInChildren(element: Element, requestUpdates: Boolean = true) {
|
||||||
val elements = element.getElementsByClassName(HtmlOutputScope.OUTPUT_CLASS)
|
val elements = element.getElementsByClassName(OutputTagConsumer.OUTPUT_CLASS)
|
||||||
console.info("Finished search for outputs. Found ${elements.length} items")
|
console.info("Finished search for outputs. Found ${elements.length} items")
|
||||||
elements.asList().forEach { child ->
|
elements.asList().forEach { child ->
|
||||||
fetchAndRenderVision(child, requestUpdates)
|
fetchAndRenderVision(child, requestUpdates)
|
||||||
@ -123,7 +128,7 @@ public fun VisionClient.fetchVisionsInChildren(element: Element, requestUpdates:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch visions from the server for all elements with [HtmlOutputScope.OUTPUT_CLASS] class in the document body
|
* Fetch visions from the server for all elements with [OutputTagConsumer.OUTPUT_CLASS] class in the document body
|
||||||
*/
|
*/
|
||||||
public fun VisionClient.fetchAndRenderAllVisions(requestUpdates: Boolean = true) {
|
public fun VisionClient.fetchAndRenderAllVisions(requestUpdates: Boolean = true) {
|
||||||
val element = document.body ?: error("Document does not have a body")
|
val element = document.body ?: error("Document does not have a body")
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
package hep.dataforge.vision.client
|
package hep.dataforge.vision.client
|
||||||
|
|
||||||
import hep.dataforge.meta.DFExperimental
|
import hep.dataforge.meta.DFExperimental
|
||||||
|
import hep.dataforge.meta.Meta
|
||||||
import hep.dataforge.names.Name
|
import hep.dataforge.names.Name
|
||||||
import hep.dataforge.names.toName
|
import hep.dataforge.names.toName
|
||||||
import hep.dataforge.provider.Type
|
import hep.dataforge.provider.Type
|
||||||
import hep.dataforge.vision.Vision
|
import hep.dataforge.vision.Vision
|
||||||
import hep.dataforge.vision.html.BindingHtmlOutputScope
|
import hep.dataforge.vision.html.BindingOutputTagConsumer
|
||||||
import hep.dataforge.vision.html.HtmlOutputScope
|
|
||||||
import hep.dataforge.vision.html.HtmlVisionFragment
|
import hep.dataforge.vision.html.HtmlVisionFragment
|
||||||
|
import hep.dataforge.vision.html.OutputTagConsumer
|
||||||
import kotlinx.browser.document
|
import kotlinx.browser.document
|
||||||
import kotlinx.html.TagConsumer
|
import kotlinx.html.TagConsumer
|
||||||
import org.w3c.dom.*
|
import org.w3c.dom.*
|
||||||
@ -25,7 +26,7 @@ public interface ElementVisionRenderer {
|
|||||||
/**
|
/**
|
||||||
* Display the [vision] inside a given [element] replacing its current content
|
* Display the [vision] inside a given [element] replacing its current content
|
||||||
*/
|
*/
|
||||||
public fun render(element: Element, vision: Vision): Unit
|
public fun render(element: Element, vision: Vision, meta: Meta = Meta.EMPTY): Unit
|
||||||
|
|
||||||
public companion object {
|
public companion object {
|
||||||
public const val TYPE: String = "elementVisionRenderer"
|
public const val TYPE: String = "elementVisionRenderer"
|
||||||
@ -44,11 +45,11 @@ public fun Map<String, Vision>.bind(rendererFactory: (Vision) -> ElementVisionRe
|
|||||||
|
|
||||||
@DFExperimental
|
@DFExperimental
|
||||||
public fun Element.renderAllVisions(visionProvider: (Name) -> Vision, rendererFactory: (Vision) -> ElementVisionRenderer) {
|
public fun Element.renderAllVisions(visionProvider: (Name) -> Vision, rendererFactory: (Vision) -> ElementVisionRenderer) {
|
||||||
val elements = getElementsByClassName(HtmlOutputScope.OUTPUT_CLASS)
|
val elements = getElementsByClassName(OutputTagConsumer.OUTPUT_CLASS)
|
||||||
elements.asList().forEach { element ->
|
elements.asList().forEach { element ->
|
||||||
val name = element.attributes[HtmlOutputScope.OUTPUT_NAME_ATTRIBUTE]?.value
|
val name = element.attributes[OutputTagConsumer.OUTPUT_NAME_ATTRIBUTE]?.value
|
||||||
if (name == null) {
|
if (name == null) {
|
||||||
console.error("Attribute ${HtmlOutputScope.OUTPUT_NAME_ATTRIBUTE} not defined in the output element")
|
console.error("Attribute ${OutputTagConsumer.OUTPUT_NAME_ATTRIBUTE} not defined in the output element")
|
||||||
return@forEach
|
return@forEach
|
||||||
}
|
}
|
||||||
val vision = visionProvider(name.toName())
|
val vision = visionProvider(name.toName())
|
||||||
@ -65,7 +66,7 @@ public fun Document.renderAllVisions(visionProvider: (Name) -> Vision, rendererF
|
|||||||
public fun HtmlVisionFragment<Vision>.renderInDocument(
|
public fun HtmlVisionFragment<Vision>.renderInDocument(
|
||||||
root: TagConsumer<HTMLElement>,
|
root: TagConsumer<HTMLElement>,
|
||||||
renderer: ElementVisionRenderer,
|
renderer: ElementVisionRenderer,
|
||||||
): HTMLElement = BindingHtmlOutputScope<HTMLElement, Vision>(root).apply(content).let { scope ->
|
): HTMLElement = BindingOutputTagConsumer<HTMLElement, Vision>(root).apply(content).let { scope ->
|
||||||
scope.finalize().apply {
|
scope.finalize().apply {
|
||||||
scope.bindings.forEach { (name, vision) ->
|
scope.bindings.forEach { (name, vision) ->
|
||||||
val id = scope.resolveId(name)
|
val id = scope.resolveId(name)
|
||||||
|
@ -346,7 +346,7 @@ private class GDMLTransformer(val settings: GDMLTransformerSettings) {
|
|||||||
final.prototypes {
|
final.prototypes {
|
||||||
proto.children.forEach { (token, item) ->
|
proto.children.forEach { (token, item) ->
|
||||||
item.parent = null
|
item.parent = null
|
||||||
set(token.asName(), item)
|
set(token.asName(), item as? Solid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
styleCache.forEach {
|
styleCache.forEach {
|
||||||
|
@ -29,10 +29,14 @@ import io.ktor.routing.application
|
|||||||
import io.ktor.routing.get
|
import io.ktor.routing.get
|
||||||
import io.ktor.routing.route
|
import io.ktor.routing.route
|
||||||
import io.ktor.routing.routing
|
import io.ktor.routing.routing
|
||||||
|
import io.ktor.server.cio.CIO
|
||||||
import io.ktor.server.engine.ApplicationEngine
|
import io.ktor.server.engine.ApplicationEngine
|
||||||
|
import io.ktor.server.engine.embeddedServer
|
||||||
|
import io.ktor.util.KtorExperimentalAPI
|
||||||
import io.ktor.util.error
|
import io.ktor.util.error
|
||||||
import io.ktor.websocket.WebSockets
|
import io.ktor.websocket.WebSockets
|
||||||
import io.ktor.websocket.webSocket
|
import io.ktor.websocket.webSocket
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
import kotlinx.html.*
|
import kotlinx.html.*
|
||||||
import kotlinx.html.stream.createHTML
|
import kotlinx.html.stream.createHTML
|
||||||
@ -47,7 +51,7 @@ public class VisionServer internal constructor(
|
|||||||
private val visionManager: VisionManager,
|
private val visionManager: VisionManager,
|
||||||
private val application: Application,
|
private val application: Application,
|
||||||
private val rootRoute: String,
|
private val rootRoute: String,
|
||||||
) : Configurable {
|
) : Configurable, CoroutineScope by application {
|
||||||
override val config: Config = Config()
|
override val config: Config = Config()
|
||||||
public var updateInterval: Long by config.long(300, key = UPDATE_INTERVAL_KEY)
|
public var updateInterval: Long by config.long(300, key = UPDATE_INTERVAL_KEY)
|
||||||
public var cacheFragments: Boolean by config.boolean(true)
|
public var cacheFragments: Boolean by config.boolean(true)
|
||||||
@ -78,23 +82,8 @@ public class VisionServer internal constructor(
|
|||||||
title(title)
|
title(title)
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
// attributes[OUTPUT_ENDPOINT_ATTRIBUTE] = if (rootRoute.endsWith("/")) {
|
|
||||||
// rootRoute
|
|
||||||
// } else {
|
|
||||||
// "$rootRoute/"
|
|
||||||
// }
|
|
||||||
//Load the fragment and remember all loaded visions
|
//Load the fragment and remember all loaded visions
|
||||||
visionMap = visionFragment(visionFragment)
|
visionMap = visionFragment(visionFragment)
|
||||||
// //The script runs when all headers already with required libraries are already loaded
|
|
||||||
// script {
|
|
||||||
// type = "text/javascript"
|
|
||||||
//
|
|
||||||
// val normalizedRoute =
|
|
||||||
// unsafe {
|
|
||||||
// //language=JavaScript
|
|
||||||
// +"fetchAndRenderAllVisions()"
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return visionMap
|
return visionMap
|
||||||
@ -171,7 +160,7 @@ public class VisionServer internal constructor(
|
|||||||
route: String = DEFAULT_PAGE,
|
route: String = DEFAULT_PAGE,
|
||||||
title: String = "VisionForge server page '$route'",
|
title: String = "VisionForge server page '$route'",
|
||||||
headers: List<HtmlFragment> = emptyList(),
|
headers: List<HtmlFragment> = emptyList(),
|
||||||
content: HtmlOutputScope<*, Vision>.() -> Unit,
|
content: OutputTagConsumer<*, Vision>.() -> Unit,
|
||||||
) {
|
) {
|
||||||
page(buildVisionFragment(content), route, title, headers)
|
page(buildVisionFragment(content), route, title, headers)
|
||||||
}
|
}
|
||||||
@ -215,6 +204,15 @@ public fun Application.visionModule(context: Context, route: String = DEFAULT_PA
|
|||||||
return VisionServer(visionManager, this, route)
|
return VisionServer(visionManager, this, route)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(KtorExperimentalAPI::class)
|
||||||
|
public fun VisionManager.serve(
|
||||||
|
host: String = "localhost",
|
||||||
|
port: Int = 7777,
|
||||||
|
block: VisionServer.()->Unit
|
||||||
|
): ApplicationEngine = context.embeddedServer(CIO, port, host) {
|
||||||
|
visionModule(context).apply(block)
|
||||||
|
}.start()
|
||||||
|
|
||||||
public fun ApplicationEngine.show() {
|
public fun ApplicationEngine.show() {
|
||||||
val connector = environment.connectors.first()
|
val connector = environment.connectors.first()
|
||||||
val uri = URI("http", null, connector.host, connector.port, null, null, null)
|
val uri = URI("http", null, connector.host, connector.port, null, null, null)
|
||||||
|
@ -65,9 +65,9 @@ public class SolidManager(meta: Meta) : AbstractPlugin(meta) {
|
|||||||
serializersModule = serializersModuleForSolids
|
serializersModule = serializersModuleForSolids
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun encodeToString(solid: Solid): String = jsonForSolids.encodeToString(PolymorphicSerializer(Vision::class), solid)
|
public fun encodeToString(solid: Solid): String = jsonForSolids.encodeToString(PolymorphicSerializer(Vision::class), solid)
|
||||||
|
|
||||||
internal fun decodeFromString(str: String): Vision = jsonForSolids.decodeFromString(PolymorphicSerializer(Vision::class), str).also {
|
fun decodeFromString(str: String): Solid = jsonForSolids.decodeFromString(PolymorphicSerializer(Solid::class), str).also {
|
||||||
if(it is VisionGroup){
|
if(it is VisionGroup){
|
||||||
it.attachChildren()
|
it.attachChildren()
|
||||||
}
|
}
|
||||||
|
@ -133,8 +133,8 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
|
|||||||
return if (vision is Solid) ElementVisionRenderer.DEFAULT_RATING else ElementVisionRenderer.ZERO_RATING
|
return if (vision is Solid) ElementVisionRenderer.DEFAULT_RATING else ElementVisionRenderer.ZERO_RATING
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun render(element: Element, vision: Vision) {
|
override fun render(element: Element, vision: Vision, meta: Meta) {
|
||||||
createCanvas(element).render(vision as? Solid ?: error("Only solids are rendered"))
|
createCanvas(element, Canvas3DOptions.read(meta)).render(vision as? Solid ?: error("Only solids are rendered"))
|
||||||
}
|
}
|
||||||
|
|
||||||
public companion object : PluginFactory<ThreePlugin> {
|
public companion object : PluginFactory<ThreePlugin> {
|
||||||
|
Loading…
Reference in New Issue
Block a user