Refactor server API

This commit is contained in:
Alexander Nozik 2022-12-06 15:54:34 +03:00
parent fd8f693151
commit fef1df3ab4
No known key found for this signature in database
GPG Key ID: F7FCF2DD25C71357
20 changed files with 142 additions and 108 deletions

View File

@ -10,13 +10,14 @@ import space.kscience.visionforge.jupyter.VFIntegrationBase
import space.kscience.visionforge.plotly.PlotlyPlugin import space.kscience.visionforge.plotly.PlotlyPlugin
import space.kscience.visionforge.plotly.asVision import space.kscience.visionforge.plotly.asVision
import space.kscience.visionforge.solid.Solids import space.kscience.visionforge.solid.Solids
import space.kscience.visionforge.visionManager
@DFExperimental @DFExperimental
internal class VisionForgePlayGroundForJupyter : VFIntegrationBase( internal class VisionForgePlayGroundForJupyter : VFIntegrationBase(
Context("VisionForge") { Context("VisionForge") {
plugin(Solids) plugin(Solids)
plugin(PlotlyPlugin) plugin(PlotlyPlugin)
} }.visionManager
) { ) {
override fun Builder.afterLoaded() { override fun Builder.afterLoaded() {

View File

@ -8,8 +8,9 @@ import kotlinx.html.*
import space.kscience.dataforge.context.Global import space.kscience.dataforge.context.Global
import space.kscience.dataforge.context.fetch import space.kscience.dataforge.context.fetch
import space.kscience.visionforge.VisionManager import space.kscience.visionforge.VisionManager
import space.kscience.visionforge.html.VisionOfHtmlForm
import space.kscience.visionforge.html.VisionPage import space.kscience.visionforge.html.VisionPage
import space.kscience.visionforge.html.formFragment import space.kscience.visionforge.html.bindForm
import space.kscience.visionforge.onPropertyChange import space.kscience.visionforge.onPropertyChange
import space.kscience.visionforge.server.EngineConnectorConfig import space.kscience.visionforge.server.EngineConnectorConfig
import space.kscience.visionforge.server.close import space.kscience.visionforge.server.close
@ -28,8 +29,18 @@ fun main() {
resources() resources()
} }
visionPage(connector, visionManager, VisionPage.scriptHeader("js/visionforge-playground.js")) { val form = VisionOfHtmlForm("form").apply {
val form = formFragment("form") { onPropertyChange(visionManager.context) {
println(this)
}
}
visionPage(
connector,
visionManager,
VisionPage.scriptHeader("js/visionforge-playground.js"),
) {
bindForm(form) {
label { label {
htmlFor = "fname" htmlFor = "fname"
+"First name:" +"First name:"
@ -61,10 +72,7 @@ fun main() {
} }
} }
vision("form") { form } vision(form)
form.onPropertyChange {
println(this)
}
} }
}.start(false) }.start(false)

View File

@ -1,11 +1,7 @@
package space.kscience.visionforge.examples package space.kscience.visionforge.examples
import space.kscience.dataforge.context.Global import space.kscience.dataforge.context.Global
import space.kscience.visionforge.html.HtmlVisionFragment import space.kscience.visionforge.html.*
import space.kscience.visionforge.html.ResourceLocation
import space.kscience.visionforge.html.VisionPage
import space.kscience.visionforge.html.importScriptHeader
import space.kscience.visionforge.makeFile
import space.kscience.visionforge.visionManager import space.kscience.visionforge.visionManager
import java.awt.Desktop import java.awt.Desktop
import java.nio.file.Path import java.nio.file.Path

View File

@ -20,10 +20,10 @@ import space.kscience.dataforge.context.logger
import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.int import space.kscience.dataforge.meta.int
import space.kscience.dataforge.meta.string import space.kscience.dataforge.meta.string
import space.kscience.dataforge.names.Name
import space.kscience.visionforge.Vision
import space.kscience.visionforge.VisionManager import space.kscience.visionforge.VisionManager
import space.kscience.visionforge.html.HtmlFormFragment
import space.kscience.visionforge.html.HtmlVisionFragment import space.kscience.visionforge.html.HtmlVisionFragment
import space.kscience.visionforge.html.VisionCollector
import space.kscience.visionforge.html.visionFragment import space.kscience.visionforge.html.visionFragment
import space.kscience.visionforge.server.EngineConnectorConfig import space.kscience.visionforge.server.EngineConnectorConfig
import space.kscience.visionforge.server.VisionRoute import space.kscience.visionforge.server.VisionRoute
@ -107,7 +107,7 @@ public class VFForNotebook(override val context: Context) : ContextAware, Corout
//server.serveVisionsFromFragment(consumer, "content-${counter++}", fragment) //server.serveVisionsFromFragment(consumer, "content-${counter++}", fragment)
val cellRoute = "content-${counter++}" val cellRoute = "content-${counter++}"
val collector: VisionCollector = mutableMapOf() val collector: MutableMap<Name, Vision> = mutableMapOf()
val url = engine.environment.connectors.first().let { val url = engine.environment.connectors.first().let {
url { url {
@ -121,15 +121,15 @@ public class VFForNotebook(override val context: Context) : ContextAware, Corout
engine.application.serveVisionData(VisionRoute(cellRoute, visionManager), collector) engine.application.serveVisionData(VisionRoute(cellRoute, visionManager), collector)
visionFragment( visionFragment(
context, visionManager,
embedData = true, embedData = true,
updatesUrl = url, updatesUrl = url,
collector = collector, onVisionRendered = { name, vision -> collector[name] = vision },
fragment = fragment fragment = fragment
) )
} else { } else {
//if not, use static rendering //if not, use static rendering
visionFragment(context, fragment = fragment) visionFragment(visionManager, fragment = fragment)
} }
} }
renderScriptForId(id) renderScriptForId(id)

View File

@ -9,6 +9,7 @@ import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.ContextAware import space.kscience.dataforge.context.ContextAware
import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.DFExperimental
import space.kscience.visionforge.Vision import space.kscience.visionforge.Vision
import space.kscience.visionforge.VisionManager
import space.kscience.visionforge.html.* import space.kscience.visionforge.html.*
import kotlin.random.Random import kotlin.random.Random
import kotlin.random.nextUInt import kotlin.random.nextUInt
@ -17,9 +18,12 @@ import kotlin.random.nextUInt
* A base class for different Jupyter VF integrations * A base class for different Jupyter VF integrations
*/ */
@DFExperimental @DFExperimental
public abstract class VFIntegrationBase(final override val context: Context) : JupyterIntegration(), ContextAware { public abstract class VFIntegrationBase(
public val visionManager: VisionManager,
) : JupyterIntegration(), ContextAware {
protected val handler: VFForNotebook = VFForNotebook(context) override val context: Context get() = visionManager.context
protected val handler: VFForNotebook = VFForNotebook(visionManager.context)
protected abstract fun Builder.afterLoaded() protected abstract fun Builder.afterLoaded()
@ -67,7 +71,7 @@ public abstract class VFIntegrationBase(final override val context: Context) : J
val id = "fragment[${page.hashCode()}/${Random.nextUInt()}]" val id = "fragment[${page.hashCode()}/${Random.nextUInt()}]"
div { div {
this.id = id this.id = id
visionFragment(context, fragment = page.content) visionFragment(visionManager, fragment = page.content)
} }
renderScriptForId(id) renderScriptForId(id)
} }

View File

@ -0,0 +1,27 @@
package space.kscience.visionforge.jupyter
import kotlinx.html.FORM
import kotlinx.html.form
import kotlinx.html.id
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.get
import space.kscience.visionforge.html.HtmlFragment
import space.kscience.visionforge.html.VisionOfHtmlForm
public class HtmlFormFragment internal constructor(
public val vision: VisionOfHtmlForm,
public val formBody: HtmlFragment,
){
public val values: Meta? get() = vision.values
public operator fun get(valueName: String): Meta? = values?.get(valueName)
}
public fun HtmlFormFragment(id: String? = null, builder: FORM.() -> Unit): HtmlFormFragment {
val realId = id ?: "form[${builder.hashCode().toUInt()}]"
return HtmlFormFragment(VisionOfHtmlForm(realId)) {
form {
this.id = realId
builder()
}
}
}

View File

@ -7,12 +7,13 @@ import space.kscience.gdml.Gdml
import space.kscience.visionforge.gdml.toVision import space.kscience.visionforge.gdml.toVision
import space.kscience.visionforge.jupyter.VFIntegrationBase import space.kscience.visionforge.jupyter.VFIntegrationBase
import space.kscience.visionforge.solid.Solids import space.kscience.visionforge.solid.Solids
import space.kscience.visionforge.visionManager
@DFExperimental @DFExperimental
internal class GdmlForJupyter : VFIntegrationBase( internal class GdmlForJupyter : VFIntegrationBase(
Context("GDML") { Context("GDML") {
plugin(Solids) plugin(Solids)
} }.visionManager
) { ) {
override fun Builder.afterLoaded() { override fun Builder.afterLoaded() {

View File

@ -25,7 +25,7 @@ public class ThreeWithControlsPlugin : AbstractPlugin(), ElementVisionRenderer {
override fun rateVision(vision: Vision): Int = override fun rateVision(vision: Vision): Int =
if (vision is Solid) ElementVisionRenderer.DEFAULT_RATING * 2 else ElementVisionRenderer.ZERO_RATING if (vision is Solid) ElementVisionRenderer.DEFAULT_RATING * 2 else ElementVisionRenderer.ZERO_RATING
override fun render(element: Element, vision: Vision, meta: Meta) { override fun render(element: Element, name: Name, vision: Vision, meta: Meta) {
createRoot(element).render { createRoot(element).render {
child(ThreeCanvasWithControls) { child(ThreeCanvasWithControls) {
attrs { attrs {

View File

@ -1,7 +1,6 @@
package space.kscience.visionforge.html package space.kscience.visionforge.html
import kotlinx.html.* import kotlinx.html.*
import space.kscience.dataforge.context.Context
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
@ -15,8 +14,6 @@ public typealias HtmlVisionFragment = VisionTagConsumer<*>.() -> Unit
@DFExperimental @DFExperimental
public fun HtmlVisionFragment(content: VisionTagConsumer<*>.() -> Unit): HtmlVisionFragment = content public fun HtmlVisionFragment(content: VisionTagConsumer<*>.() -> Unit): HtmlVisionFragment = content
public typealias VisionCollector = MutableMap<Name, Pair<VisionOutput, Vision>>
/** /**
* Render a fragment in the given consumer and return a map of extracted visions * Render a fragment in the given consumer and return a map of extracted visions
@ -27,15 +24,18 @@ public typealias VisionCollector = MutableMap<Name, Pair<VisionOutput, Vision>>
* @param idPrefix a prefix to be used before vision ids * @param idPrefix a prefix to be used before vision ids
*/ */
public fun TagConsumer<*>.visionFragment( public fun TagConsumer<*>.visionFragment(
context: Context, visionManager: VisionManager,
embedData: Boolean = true, embedData: Boolean = true,
fetchDataUrl: String? = null, fetchDataUrl: String? = null,
updatesUrl: String? = null, updatesUrl: String? = null,
idPrefix: String? = null, idPrefix: String? = null,
collector: VisionCollector = mutableMapOf(), onVisionRendered: (Name, Vision) -> Unit = { _, _ -> },
fragment: HtmlVisionFragment, fragment: HtmlVisionFragment,
) { ) {
val consumer = object : VisionTagConsumer<Any?>(this@visionFragment, context, idPrefix) {
val collector: MutableMap<Name, Pair<VisionOutput, Vision>> = mutableMapOf()
val consumer = object : VisionTagConsumer<Any?>(this@visionFragment, visionManager, idPrefix) {
override fun <T> TagConsumer<T>.vision(name: Name?, buildOutput: VisionOutput.() -> Vision): T { override fun <T> TagConsumer<T>.vision(name: Name?, buildOutput: VisionOutput.() -> Vision): T {
//Avoid re-creating cached visions //Avoid re-creating cached visions
@ -47,6 +47,7 @@ public fun TagConsumer<*>.visionFragment(
val (output, vision) = collector.getOrPut(actualName) { val (output, vision) = collector.getOrPut(actualName) {
val output = VisionOutput(context, actualName) val output = VisionOutput(context, actualName)
val vision = output.buildOutput() val vision = output.buildOutput()
onVisionRendered(actualName, vision)
output to vision output to vision
} }
@ -54,8 +55,15 @@ public fun TagConsumer<*>.visionFragment(
} }
override fun DIV.renderVision(manager: VisionManager, name: Name, vision: Vision, outputMeta: Meta) { override fun DIV.renderVision(manager: VisionManager, name: Name, vision: Vision, outputMeta: Meta) {
// Toggle update mode
val (_, actualVision) = collector.getOrPut(name) {
val output = VisionOutput(context, name)
onVisionRendered(name, vision)
output to vision
}
// Toggle update mode
updatesUrl?.let { updatesUrl?.let {
attributes[OUTPUT_CONNECT_ATTRIBUTE] = it attributes[OUTPUT_CONNECT_ATTRIBUTE] = it
} }
@ -69,7 +77,7 @@ public fun TagConsumer<*>.visionFragment(
type = "text/json" type = "text/json"
attributes["class"] = OUTPUT_DATA_CLASS attributes["class"] = OUTPUT_DATA_CLASS
unsafe { unsafe {
+"\n${manager.encodeToString(vision)}\n" +"\n${manager.encodeToString(actualVision)}\n"
} }
} }
} }
@ -80,19 +88,20 @@ public fun TagConsumer<*>.visionFragment(
} }
public fun FlowContent.visionFragment( public fun FlowContent.visionFragment(
context: Context, visionManager: VisionManager,
embedData: Boolean = true, embedData: Boolean = true,
fetchDataUrl: String? = null, fetchDataUrl: String? = null,
updatesUrl: String? = null, updatesUrl: String? = null,
onVisionRendered: (Name, Vision) -> Unit = { _, _ -> },
idPrefix: String? = null, idPrefix: String? = null,
visionCache: VisionCollector = mutableMapOf(),
fragment: HtmlVisionFragment, fragment: HtmlVisionFragment,
): Unit = consumer.visionFragment( ): Unit = consumer.visionFragment(
context, visionManager = visionManager,
embedData, embedData = embedData,
fetchDataUrl, fetchDataUrl = fetchDataUrl,
updatesUrl, updatesUrl = updatesUrl,
idPrefix, idPrefix = idPrefix,
visionCache, onVisionRendered = onVisionRendered,
fragment = fragment fragment = fragment
) )

View File

@ -7,7 +7,6 @@ import kotlinx.html.id
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.node import space.kscience.dataforge.meta.node
@Serializable @Serializable
@ -18,29 +17,10 @@ public class VisionOfHtmlForm(
public var values: Meta? by mutableProperties.node() public var values: Meta? by mutableProperties.node()
} }
public class HtmlFormFragment internal constructor( public fun <R> TagConsumer<R>.bindForm(
public val vision: VisionOfHtmlForm, visionOfForm: VisionOfHtmlForm,
public val formBody: HtmlFragment,
){
public val values: Meta? get() = vision.values
public operator fun get(valueName: String): Meta? = values?.get(valueName)
}
public fun HtmlFormFragment(id: String? = null, builder: FORM.() -> Unit): HtmlFormFragment {
val realId = id ?: "form[${builder.hashCode().toUInt()}]"
return HtmlFormFragment(VisionOfHtmlForm(realId)) {
form {
this.id = realId
builder()
}
}
}
public fun <R> TagConsumer<R>.formFragment(
id: String? = null,
builder: FORM.() -> Unit, builder: FORM.() -> Unit,
): VisionOfHtmlForm { ): R = form {
val formFragment = HtmlFormFragment(id, builder) this.id = visionOfForm.formId
fragment(formFragment.formBody) builder()
return formFragment.vision
} }

View File

@ -2,6 +2,7 @@ package space.kscience.visionforge.html
import kotlinx.html.* import kotlinx.html.*
import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.ContextAware
import space.kscience.dataforge.context.PluginFactory import space.kscience.dataforge.context.PluginFactory
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.MetaSerializer import space.kscience.dataforge.meta.MetaSerializer
@ -56,9 +57,11 @@ public class VisionOutput @PublishedApi internal constructor(public val context:
@VisionDSL @VisionDSL
public abstract class VisionTagConsumer<R>( public abstract class VisionTagConsumer<R>(
private val root: TagConsumer<R>, private val root: TagConsumer<R>,
public val context: Context, public val visionManager: VisionManager,
private val idPrefix: String? = null, private val idPrefix: String? = null,
) : TagConsumer<R> by root { ) : TagConsumer<R> by root, ContextAware {
override val context: Context get() = visionManager.context
public open fun resolveId(name: Name): String = (idPrefix ?: "output") + "[$name]" public open fun resolveId(name: Name): String = (idPrefix ?: "output") + "[$name]"

View File

@ -18,7 +18,7 @@ fun FlowContent.renderVisionFragment(
fragment: HtmlVisionFragment, fragment: HtmlVisionFragment,
): Map<Name, Vision> { ): Map<Name, Vision> {
val visionMap = HashMap<Name, Vision>() val visionMap = HashMap<Name, Vision>()
val consumer = object : VisionTagConsumer<Any?>(consumer, Global, idPrefix) { val consumer = object : VisionTagConsumer<Any?>(consumer, Global.visionManager, idPrefix) {
override fun DIV.renderVision(manager: VisionManager, name: Name, vision: Vision, outputMeta: Meta) { override fun DIV.renderVision(manager: VisionManager, name: Name, vision: Vision, outputMeta: Meta) {
visionMap[name] = vision visionMap[name] = vision
renderer(name, vision, outputMeta) renderer(name, vision, outputMeta)

View File

@ -4,6 +4,7 @@ import kotlinx.browser.document
import kotlinx.browser.window import kotlinx.browser.window
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.w3c.dom.* import org.w3c.dom.*
import org.w3c.dom.url.URL import org.w3c.dom.url.URL
@ -68,18 +69,6 @@ public class VisionClient : AbstractPlugin() {
changeCollector.propertyChanged(visionName, propertyName, item) changeCollector.propertyChanged(visionName, propertyName, item)
} }
public fun visionPropertyChanged(visionName: Name, propertyName: Name, item: Boolean) {
visionPropertyChanged(visionName, propertyName, Meta(item))
}
public fun visionPropertyChanged(visionName: Name, propertyName: Name, item: String) {
visionPropertyChanged(visionName, propertyName, Meta(item))
}
public fun visionPropertyChanged(visionName: Name, propertyName: Name, item: Number) {
visionPropertyChanged(visionName, propertyName, Meta(item))
}
public fun visionChanged(name: Name?, child: Vision?) { public fun visionChanged(name: Name?, child: Vision?) {
changeCollector.setChild(name, child) changeCollector.setChild(name, child)
} }
@ -139,10 +128,12 @@ public class VisionClient : AbstractPlugin() {
onopen = { onopen = {
feedbackJob = visionManager.context.launch { feedbackJob = visionManager.context.launch {
delay(feedbackAggregationTime.milliseconds) while (isActive) {
if (!changeCollector.isEmpty()) { delay(feedbackAggregationTime.milliseconds)
send(visionManager.encodeToString(changeCollector.deepCopy(visionManager))) if (!changeCollector.isEmpty()) {
changeCollector.reset() send(visionManager.encodeToString(changeCollector.deepCopy(visionManager)))
changeCollector.reset()
}
} }
} }
logger.info { "WebSocket update channel established for output '$name'" } logger.info { "WebSocket update channel established for output '$name'" }
@ -247,6 +238,21 @@ public class VisionClient : AbstractPlugin() {
} }
} }
public fun VisionClient.visionPropertyChanged(visionName: Name, propertyName: String, item: Meta?) {
visionPropertyChanged(visionName, propertyName.parseAsName(true), item)
}
public fun VisionClient.visionPropertyChanged(visionName: Name, propertyName: String, item: Number) {
visionPropertyChanged(visionName, propertyName.parseAsName(true), Meta(item))
}
public fun VisionClient.visionPropertyChanged(visionName: Name, propertyName: String, item: String) {
visionPropertyChanged(visionName, propertyName.parseAsName(true), Meta(item))
}
public fun VisionClient.visionPropertyChanged(visionName: Name, propertyName: String, item: Boolean) {
visionPropertyChanged(visionName, propertyName.parseAsName(true), Meta(item))
}
private fun whenDocumentLoaded(block: Document.() -> Unit): Unit { private fun whenDocumentLoaded(block: Document.() -> Unit): Unit {
if (document.body != null) { if (document.body != null) {

View File

@ -9,8 +9,11 @@ import org.w3c.dom.HTMLFormElement
import org.w3c.dom.HTMLInputElement import org.w3c.dom.HTMLInputElement
import org.w3c.dom.get import org.w3c.dom.get
import org.w3c.xhr.FormData import org.w3c.xhr.FormData
import space.kscience.dataforge.context.debug
import space.kscience.dataforge.context.logger
import space.kscience.dataforge.meta.DynamicMeta import space.kscience.dataforge.meta.DynamicMeta
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.toMap
import space.kscience.dataforge.meta.valueSequence import space.kscience.dataforge.meta.valueSequence
import space.kscience.visionforge.html.VisionOfHtmlForm import space.kscience.visionforge.html.VisionOfHtmlForm
import space.kscience.visionforge.html.VisionOfNumberField import space.kscience.visionforge.html.VisionOfNumberField
@ -33,7 +36,7 @@ internal fun textVisionRenderer(
value = it ?: "" value = it ?: ""
} }
onChangeFunction = { onChangeFunction = {
// client.visionPropertyChanged(name, VisionOfTextField::text.name.pa, value) client.visionPropertyChanged(name, VisionOfTextField::text.name, value)
} }
} }
} }
@ -55,7 +58,7 @@ internal fun numberVisionRenderer(
value = it?.toDouble() ?: 0.0 value = it?.toDouble() ?: 0.0
} }
onChangeFunction = { onChangeFunction = {
// vision.value = value.toDoubleOrNull() client.visionPropertyChanged(name, VisionOfNumberField::value.name, value)
} }
} }
} }
@ -90,9 +93,10 @@ internal fun formVisionRenderer(
val form = document.getElementById(vision.formId) as? HTMLFormElement val form = document.getElementById(vision.formId) as? HTMLFormElement
?: error("An element with id = '${vision.formId} is not a form") ?: error("An element with id = '${vision.formId} is not a form")
console.info("Adding hooks to form '$form'") client.logger.debug{"Adding hooks to form with id = '$vision.formId'"}
vision.useProperty(VisionOfHtmlForm::values) { values -> vision.useProperty(VisionOfHtmlForm::values) { values ->
client.logger.debug{"Updating form '${vision.formId}' with values $values"}
val inputs = form.getElementsByTagName("input") val inputs = form.getElementsByTagName("input")
values?.valueSequence()?.forEach { (token, value) -> values?.valueSequence()?.forEach { (token, value) ->
(inputs[token.toString()] as? HTMLInputElement)?.value = value.toString() (inputs[token.toString()] as? HTMLInputElement)?.value = value.toString()
@ -102,8 +106,8 @@ internal fun formVisionRenderer(
form.onsubmit = { event -> form.onsubmit = { event ->
event.preventDefault() event.preventDefault()
val formData = FormData(form).toMeta() val formData = FormData(form).toMeta()
//console.log(formData.toString()) client.visionPropertyChanged(name, VisionOfHtmlForm::values.name, formData)
//vision.values = formData console.info("Sent: ${formData.toMap()}")
false false
} }
} }

View File

@ -1,4 +1,4 @@
package space.kscience.visionforge package space.kscience.visionforge.html
import kotlinx.html.body import kotlinx.html.body
import kotlinx.html.head import kotlinx.html.head
@ -6,10 +6,7 @@ import kotlinx.html.meta
import kotlinx.html.stream.createHTML import kotlinx.html.stream.createHTML
import space.kscience.dataforge.context.Global import space.kscience.dataforge.context.Global
import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.DFExperimental
import space.kscience.visionforge.html.HtmlFragment import space.kscience.visionforge.visionManager
import space.kscience.visionforge.html.VisionPage
import space.kscience.visionforge.html.fragment
import space.kscience.visionforge.html.visionFragment
import java.awt.Desktop import java.awt.Desktop
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
@ -87,7 +84,7 @@ public fun VisionPage.makeFile(
} }
} }
body { body {
visionFragment(Global, fragment = content) visionFragment(Global.visionManager, fragment = content)
} }
}.finalize() }.finalize()

View File

@ -28,7 +28,7 @@ public actual class PlotlyPlugin : VisionPlugin(), ElementVisionRenderer {
else -> ElementVisionRenderer.ZERO_RATING else -> ElementVisionRenderer.ZERO_RATING
} }
override fun render(element: Element, vision: Vision, meta: Meta) { override fun render(element: Element, name: Name, vision: Vision, meta: Meta) {
val plot = (vision as? VisionOfPlotly)?.plot ?: error("VisionOfPlotly expected but ${vision::class} found") val plot = (vision as? VisionOfPlotly)?.plot ?: error("VisionOfPlotly expected but ${vision::class} found")
val config = PlotlyConfig.read(meta) val config = PlotlyConfig.read(meta)
element.plot(plot, config) element.plot(plot, config)

View File

@ -15,7 +15,6 @@ import io.ktor.server.util.*
import io.ktor.server.websocket.* import io.ktor.server.websocket.*
import io.ktor.util.pipeline.* import io.ktor.util.pipeline.*
import io.ktor.websocket.* import io.ktor.websocket.*
import kotlinx.coroutines.channels.consumeEach
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -97,8 +96,8 @@ public fun Application.serveVisionData(
val vision: Vision = resolveVision(Name.parse(name)) ?: error("Plot with id='$name' not registered") val vision: Vision = resolveVision(Name.parse(name)) ?: error("Plot with id='$name' not registered")
launch { launch {
incoming.consumeEach { for(frame in incoming) {
val data = it.data.decodeToString() val data = frame.data.decodeToString()
application.log.debug("Received update: \n$data") application.log.debug("Received update: \n$data")
val change = configuration.visionManager.jsonFormat.decodeFromString( val change = configuration.visionManager.jsonFormat.decodeFromString(
VisionChange.serializer(), data VisionChange.serializer(), data
@ -143,8 +142,8 @@ public fun Application.serveVisionData(
public fun Application.serveVisionData( public fun Application.serveVisionData(
configuration: VisionRoute, configuration: VisionRoute,
cache: VisionCollector, data: Map<Name, Vision>,
): Unit = serveVisionData(configuration) { cache[it]?.second } ): Unit = serveVisionData(configuration) { data[it] }
// //
///** ///**
@ -179,7 +178,7 @@ public fun Application.visionPage(
) { ) {
require(WebSockets) require(WebSockets)
val collector: VisionCollector = mutableMapOf() val collector: MutableMap<Name, Vision> = mutableMapOf()
val html = createHTML().apply { val html = createHTML().apply {
head { head {
@ -193,7 +192,7 @@ public fun Application.visionPage(
body { body {
//Load the fragment and remember all loaded visions //Load the fragment and remember all loaded visions
visionFragment( visionFragment(
context = configuration.context, visionManager = configuration.visionManager,
embedData = configuration.dataMode == VisionRoute.Mode.EMBED, embedData = configuration.dataMode == VisionRoute.Mode.EMBED,
fetchDataUrl = if (configuration.dataMode != VisionRoute.Mode.EMBED) { fetchDataUrl = if (configuration.dataMode != VisionRoute.Mode.EMBED) {
url { url {
@ -210,7 +209,7 @@ public fun Application.visionPage(
path(route, "ws") path(route, "ws")
} }
} else null, } else null,
visionCache = collector, onVisionRendered = { name, vision -> collector[name] = vision },
fragment = visionFragment fragment = visionFragment
) )
} }

View File

@ -35,7 +35,7 @@ public class TableVisionJsPlugin : AbstractPlugin(), ElementVisionRenderer {
else -> ElementVisionRenderer.ZERO_RATING else -> ElementVisionRenderer.ZERO_RATING
} }
override fun render(element: Element, vision: Vision, meta: Meta) { override fun render(element: Element, name: Name, vision: Vision, meta: Meta) {
val table: VisionOfTable = (vision as? VisionOfTable) val table: VisionOfTable = (vision as? VisionOfTable)
?: error("VisionOfTable expected but ${vision::class} found") ?: error("VisionOfTable expected but ${vision::class} found")

View File

@ -86,7 +86,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
}.launchIn(context) }.launchIn(context)
vision.children.changes.onEach { childName -> vision.children.changes.onEach { childName ->
if(childName.isEmpty()) return@onEach if (childName.isEmpty()) return@onEach
val child = vision.children.getChild(childName) val child = vision.children.getChild(childName)
@ -147,7 +147,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
render(vision) render(vision)
} }
override fun render(element: Element, vision: Vision, meta: Meta) { override fun render(element: Element, name: Name, vision: Vision, meta: Meta) {
renderSolid( renderSolid(
element, element,
vision as? Solid ?: error("Solid expected but ${vision::class} found"), vision as? Solid ?: error("Solid expected but ${vision::class} found"),

View File

@ -3,7 +3,6 @@ package space.kscience.visionforge.three
import space.kscience.dataforge.context.Global import space.kscience.dataforge.context.Global
import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.DFExperimental
import space.kscience.visionforge.html.* import space.kscience.visionforge.html.*
import space.kscience.visionforge.makeFile
import space.kscience.visionforge.visionManager import space.kscience.visionforge.visionManager
import java.awt.Desktop import java.awt.Desktop
import java.nio.file.Path import java.nio.file.Path