Compare commits

...

3 Commits

14 changed files with 155 additions and 91 deletions

View File

@ -1,12 +1,31 @@
# Changelog # Changelog
## [Unreleased] ## Unreleased
### Added ### Added
### Changed
- **Breaking API** Move vision cache to upper level for renderers to avoid re-creating visions for page reload.
- **Breaking API** Forms refactor
### Deprecated
### Removed
### Fixed
### Security
## 0.3.0 - 2023-12-23
### Added
- Context receivers flag - Context receivers flag
- MeshLine for thick lines - MeshLine for thick lines
- Custom client-side events and thier processing in VisionServer - Custom client-side events and thier processing in VisionServer
### Changed ### Changed
- Color accessor property is now `colorProperty`. Color uses non-nullable `invoke` instead of `set`. - Color accessor property is now `colorProperty`. Color uses non-nullable `invoke` instead of `set`.
- API update for server and pages - API update for server and pages
- Edges moved to solids module for easier construction - Edges moved to solids module for easier construction
@ -17,17 +36,14 @@
- Naming of Canvas3D options. - Naming of Canvas3D options.
- Lights are added to the scene instead of 3D options. - Lights are added to the scene instead of 3D options.
### Deprecated
### Removed
### Fixed ### Fixed
- Jupyter integration for IDEA and Jupyter lab. - Jupyter integration for IDEA and Jupyter lab.
### Security ## 0.2.0
## [0.2.0]
### Added ### Added
- Server module - Server module
- Change collector - Change collector
- Customizable accessors for colors - Customizable accessors for colors
@ -38,8 +54,8 @@
- Markdown module - Markdown module
- Tables module - Tables module
### Changed ### Changed
- Vision does not implement ItemProvider anymore. Property changes are done via `getProperty`/`setProperty` and `property` delegate. - Vision does not implement ItemProvider anymore. Property changes are done via `getProperty`/`setProperty` and `property` delegate.
- Point3D and Point2D are made separate classes instead of expect/actual (to split up different engines. - Point3D and Point2D are made separate classes instead of expect/actual (to split up different engines.
- JavaFX support moved to a separate module - JavaFX support moved to a separate module
@ -54,16 +70,10 @@
- Property listeners are not triggered if there are no changes. - Property listeners are not triggered if there are no changes.
- Feedback websocket connection in the client. - Feedback websocket connection in the client.
### Deprecated
### Removed ### Removed
- Primary modules dependencies on UI - Primary modules dependencies on UI
### Fixed ### Fixed
- Version conflicts - Version conflicts
### Security

View File

@ -11,7 +11,7 @@ val dataforgeVersion by extra("0.7.1")
allprojects { allprojects {
group = "space.kscience" group = "space.kscience"
version = "0.3.0" version = "0.3.1"
} }
subprojects { subprojects {

View File

@ -4,6 +4,7 @@ import ringui.SmartTabs
import ringui.Tab import ringui.Tab
import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.request import space.kscience.dataforge.context.request
import space.kscience.plotly.Plotly.plot
import space.kscience.plotly.models.Trace import space.kscience.plotly.models.Trace
import space.kscience.plotly.scatter import space.kscience.plotly.scatter
import space.kscience.visionforge.Application import space.kscience.visionforge.Application
@ -97,7 +98,7 @@ private class JsPlaygroundApp : Application {
Tab("plotly") { Tab("plotly") {
Plotly { Plotly {
attrs { attrs {
plot = space.kscience.plotly.Plotly.plot { plot = plot {
scatter { scatter {
x(1, 2, 3) x(1, 2, 3)
y(5, 8, 7) y(5, 8, 7)

View File

@ -11,6 +11,7 @@ import space.kscience.visionforge.markup.VisionOfMarkup
import space.kscience.visionforge.react.flexRow import space.kscience.visionforge.react.flexRow
import space.kscience.visionforge.ring.ThreeCanvasWithControls import space.kscience.visionforge.ring.ThreeCanvasWithControls
import space.kscience.visionforge.ring.solid import space.kscience.visionforge.ring.solid
import space.kscience.visionforge.setAsRoot
import space.kscience.visionforge.solid.* import space.kscience.visionforge.solid.*
import styled.css import styled.css
import styled.styledDiv import styled.styledDiv
@ -27,7 +28,9 @@ val GravityDemo = fc<DemoProps> { props ->
val energyTrace = Trace { val energyTrace = Trace {
name = "energy" name = "energy"
} }
val markup = VisionOfMarkup() val markup = VisionOfMarkup().apply {
setAsRoot(props.solids.visionManager)
}
styledDiv { styledDiv {
css { css {

View File

@ -10,7 +10,7 @@ import space.kscience.dataforge.context.request
import space.kscience.visionforge.VisionManager import space.kscience.visionforge.VisionManager
import space.kscience.visionforge.html.VisionOfHtmlForm import space.kscience.visionforge.html.VisionOfHtmlForm
import space.kscience.visionforge.html.VisionPage import space.kscience.visionforge.html.VisionPage
import space.kscience.visionforge.html.bindForm import space.kscience.visionforge.html.visionOfForm
import space.kscience.visionforge.onPropertyChange import space.kscience.visionforge.onPropertyChange
import space.kscience.visionforge.server.close import space.kscience.visionforge.server.close
import space.kscience.visionforge.server.openInBrowser import space.kscience.visionforge.server.openInBrowser
@ -36,7 +36,7 @@ fun main() {
visionManager, visionManager,
VisionPage.scriptHeader("js/visionforge-playground.js"), VisionPage.scriptHeader("js/visionforge-playground.js"),
) { ) {
bindForm(form) { visionOfForm(form) {
label { label {
htmlFor = "fname" htmlFor = "fname"
+"First name:" +"First name:"
@ -67,8 +67,8 @@ fun main() {
value = "Submit" value = "Submit"
} }
} }
println(form.values)
vision(form) vision(form)
println(form.values)
} }
}.start(false) }.start(false)

View File

@ -9,7 +9,9 @@ kscience {
// useSerialization { // useSerialization {
// json() // json()
// } // }
jvm() jvm{
withJava()
}
jvmMain{ jvmMain{
implementation("io.ktor:ktor-server-cio") implementation("io.ktor:ktor-server-cio")
implementation(projects.visionforgeThreejs.visionforgeThreejsServer) implementation(projects.visionforgeThreejs.visionforgeThreejsServer)

View File

@ -750,10 +750,10 @@ public abstract interface class space/kscience/visionforge/html/HtmlVisionFragme
public final class space/kscience/visionforge/html/HtmlVisionRendererKt { public final class space/kscience/visionforge/html/HtmlVisionRendererKt {
public static final fun appendTo (Lspace/kscience/visionforge/html/HtmlVisionFragment;Lspace/kscience/visionforge/html/VisionTagConsumer;)V public static final fun appendTo (Lspace/kscience/visionforge/html/HtmlVisionFragment;Lspace/kscience/visionforge/html/VisionTagConsumer;)V
public static final fun visionFragment (Lkotlinx/html/FlowContent;Lspace/kscience/visionforge/VisionManager;ZLjava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Ljava/lang/String;Lspace/kscience/visionforge/html/HtmlVisionFragment;)V public static final fun visionFragment (Lkotlinx/html/FlowContent;Lspace/kscience/visionforge/VisionManager;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Lspace/kscience/visionforge/html/HtmlVisionFragment;)V
public static final fun visionFragment (Lkotlinx/html/TagConsumer;Lspace/kscience/visionforge/VisionManager;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lspace/kscience/visionforge/html/HtmlVisionFragment;)V public static final fun visionFragment (Lkotlinx/html/TagConsumer;Lspace/kscience/visionforge/VisionManager;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Lspace/kscience/visionforge/html/HtmlVisionFragment;)V
public static synthetic fun visionFragment$default (Lkotlinx/html/FlowContent;Lspace/kscience/visionforge/VisionManager;ZLjava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Ljava/lang/String;Lspace/kscience/visionforge/html/HtmlVisionFragment;ILjava/lang/Object;)V public static synthetic fun visionFragment$default (Lkotlinx/html/FlowContent;Lspace/kscience/visionforge/VisionManager;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Lspace/kscience/visionforge/html/HtmlVisionFragment;ILjava/lang/Object;)V
public static synthetic fun visionFragment$default (Lkotlinx/html/TagConsumer;Lspace/kscience/visionforge/VisionManager;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lspace/kscience/visionforge/html/HtmlVisionFragment;ILjava/lang/Object;)V public static synthetic fun visionFragment$default (Lkotlinx/html/TagConsumer;Lspace/kscience/visionforge/VisionManager;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Lspace/kscience/visionforge/html/HtmlVisionFragment;ILjava/lang/Object;)V
} }
public final class space/kscience/visionforge/html/InputFeedbackMode : java/lang/Enum { public final class space/kscience/visionforge/html/InputFeedbackMode : java/lang/Enum {
@ -782,6 +782,21 @@ public final class space/kscience/visionforge/html/ResourceLocation : java/lang/
public abstract interface annotation class space/kscience/visionforge/html/VisionDSL : java/lang/annotation/Annotation { public abstract interface annotation class space/kscience/visionforge/html/VisionDSL : java/lang/annotation/Annotation {
} }
public final class space/kscience/visionforge/html/VisionDisplay {
public fun <init> (Lspace/kscience/visionforge/VisionManager;Lspace/kscience/visionforge/Vision;Lspace/kscience/dataforge/meta/Meta;)V
public final fun component1 ()Lspace/kscience/visionforge/VisionManager;
public final fun component2 ()Lspace/kscience/visionforge/Vision;
public final fun component3 ()Lspace/kscience/dataforge/meta/Meta;
public final fun copy (Lspace/kscience/visionforge/VisionManager;Lspace/kscience/visionforge/Vision;Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/visionforge/html/VisionDisplay;
public static synthetic fun copy$default (Lspace/kscience/visionforge/html/VisionDisplay;Lspace/kscience/visionforge/VisionManager;Lspace/kscience/visionforge/Vision;Lspace/kscience/dataforge/meta/Meta;ILjava/lang/Object;)Lspace/kscience/visionforge/html/VisionDisplay;
public fun equals (Ljava/lang/Object;)Z
public final fun getMeta ()Lspace/kscience/dataforge/meta/Meta;
public final fun getVision ()Lspace/kscience/visionforge/Vision;
public final fun getVisionManager ()Lspace/kscience/visionforge/VisionManager;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final class space/kscience/visionforge/html/VisionOfCheckbox : space/kscience/visionforge/html/VisionOfHtmlInput { public final class space/kscience/visionforge/html/VisionOfCheckbox : space/kscience/visionforge/html/VisionOfHtmlInput {
public static final field Companion Lspace/kscience/visionforge/html/VisionOfCheckbox$Companion; public static final field Companion Lspace/kscience/visionforge/html/VisionOfCheckbox$Companion;
public fun <init> ()V public fun <init> ()V
@ -852,7 +867,7 @@ public final class space/kscience/visionforge/html/VisionOfHtmlControl$Companion
public final fun serializer ()Lkotlinx/serialization/KSerializer; public final fun serializer ()Lkotlinx/serialization/KSerializer;
} }
public final class space/kscience/visionforge/html/VisionOfHtmlForm : space/kscience/visionforge/html/VisionOfHtmlControl { public final class space/kscience/visionforge/html/VisionOfHtmlForm : space/kscience/visionforge/html/VisionOfHtmlControl, space/kscience/visionforge/ClickControl {
public static final field Companion Lspace/kscience/visionforge/html/VisionOfHtmlForm$Companion; public static final field Companion Lspace/kscience/visionforge/html/VisionOfHtmlForm$Companion;
public fun <init> (Ljava/lang/String;)V public fun <init> (Ljava/lang/String;)V
public final fun getFormId ()Ljava/lang/String; public final fun getFormId ()Ljava/lang/String;
@ -876,9 +891,11 @@ public final class space/kscience/visionforge/html/VisionOfHtmlForm$Companion {
} }
public final class space/kscience/visionforge/html/VisionOfHtmlFormKt { public final class space/kscience/visionforge/html/VisionOfHtmlFormKt {
public static final fun bindForm (Lkotlinx/html/TagConsumer;Lspace/kscience/visionforge/html/VisionOfHtmlForm;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun button (Lspace/kscience/visionforge/html/VisionOutput;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)Lspace/kscience/visionforge/html/VisionOfHtmlButton; public static final fun button (Lspace/kscience/visionforge/html/VisionOutput;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)Lspace/kscience/visionforge/html/VisionOfHtmlButton;
public static synthetic fun button$default (Lspace/kscience/visionforge/html/VisionOutput;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/visionforge/html/VisionOfHtmlButton; public static synthetic fun button$default (Lspace/kscience/visionforge/html/VisionOutput;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/visionforge/html/VisionOfHtmlButton;
public static final fun onSubmit (Lspace/kscience/visionforge/html/VisionOfHtmlForm;Lkotlinx/coroutines/CoroutineScope;Lkotlin/jvm/functions/Function1;)Lkotlinx/coroutines/Job;
public static final fun visionOfForm (Lkotlinx/html/TagConsumer;Lspace/kscience/visionforge/html/VisionOfHtmlForm;Ljava/lang/String;Lkotlinx/html/FormEncType;Lkotlinx/html/FormMethod;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static synthetic fun visionOfForm$default (Lkotlinx/html/TagConsumer;Lspace/kscience/visionforge/html/VisionOfHtmlForm;Ljava/lang/String;Lkotlinx/html/FormEncType;Lkotlinx/html/FormMethod;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ljava/lang/Object;
} }
public class space/kscience/visionforge/html/VisionOfHtmlInput : space/kscience/visionforge/html/VisionOfHtmlControl { public class space/kscience/visionforge/html/VisionOfHtmlInput : space/kscience/visionforge/html/VisionOfHtmlControl {

View File

@ -39,7 +39,8 @@ public interface ControlVision : Vision {
@Serializable @Serializable
@SerialName("control.click") @SerialName("control.click")
public class VisionClickEvent(override val meta: Meta) : VisionControlEvent() { public class VisionClickEvent(override val meta: Meta) : VisionControlEvent() {
public val payload: Meta? by meta.node() public val payload: Meta get() = meta[::payload.name] ?: Meta.EMPTY
public val name: Name? get() = meta["name"].string?.parseAsName() public val name: Name? get() = meta["name"].string?.parseAsName()
override fun toString(): String = meta.toString() override fun toString(): String = meta.toString()

View File

@ -14,13 +14,17 @@ public fun interface HtmlVisionFragment{
public fun HtmlVisionFragment.appendTo(consumer: VisionTagConsumer<*>): Unit = consumer.append() public fun HtmlVisionFragment.appendTo(consumer: VisionTagConsumer<*>): Unit = consumer.append()
public data class VisionDisplay(val visionManager: VisionManager, val vision: Vision, val meta: Meta)
/** /**
* 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
* @param context a context used to create a vision fragment * @param visionManager a context plugin used to create a vision fragment
* @param embedData embed Vision initial state in the HTML * @param embedData embed Vision initial state in the HTML
* @param fetchDataUrl fetch data after first render from given url * @param fetchDataUrl fetch data after first render from given url
* @param updatesUrl receive push updates from the server at given url * @param updatesUrl receive push updates from the server at given url
* @param idPrefix a prefix to be used before vision ids * @param idPrefix a prefix to be used before vision ids
* @param displayCache external cache for Vision displays. It is required to avoid re-creating visions on page update
* @param fragment the fragment to render
*/ */
public fun TagConsumer<*>.visionFragment( public fun TagConsumer<*>.visionFragment(
visionManager: VisionManager, visionManager: VisionManager,
@ -28,39 +32,31 @@ public fun TagConsumer<*>.visionFragment(
fetchDataUrl: String? = null, fetchDataUrl: String? = null,
updatesUrl: String? = null, updatesUrl: String? = null,
idPrefix: String? = null, idPrefix: String? = null,
onVisionRendered: (Name, Vision) -> Unit = { _, _ -> }, displayCache: MutableMap<Name, VisionDisplay> = mutableMapOf(),
fragment: HtmlVisionFragment, fragment: HtmlVisionFragment,
) { ) {
val collector: MutableMap<Name, Pair<VisionOutput, Vision>> = mutableMapOf()
val consumer = object : VisionTagConsumer<Any?>(this@visionFragment, visionManager, idPrefix) { 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
val actualName = name ?: NameToken( val actualName = name ?: NameToken(
DEFAULT_VISION_NAME, DEFAULT_VISION_NAME,
buildOutput.hashCode().toUInt().toString() buildOutput.hashCode().toString(16)
).asName() ).asName()
val (output, vision) = collector.getOrPut(actualName) { val display = displayCache.getOrPut(actualName) {
val output = VisionOutput(context, actualName) val output = VisionOutput(context, actualName)
val vision = output.buildOutput() val vision = output.buildOutput()
onVisionRendered(actualName, vision) VisionDisplay(output.visionManager, vision, output.meta)
output to vision
} }
return addVision(actualName, output.visionManager, vision, output.meta) return addVision(actualName, display.visionManager, display.vision, display.meta)
} }
override fun DIV.renderVision(manager: VisionManager, name: Name, vision: Vision, outputMeta: Meta) { override fun DIV.renderVision(manager: VisionManager, name: Name, vision: Vision, outputMeta: Meta) {
val (_, actualVision) = collector.getOrPut(name) { displayCache[name] = VisionDisplay(manager, vision, outputMeta)
val output = VisionOutput(context, name)
onVisionRendered(name, vision)
output to vision
}
// Toggle update mode // Toggle update mode
updatesUrl?.let { updatesUrl?.let {
@ -76,7 +72,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(actualVision)}\n" +"\n${manager.encodeToString(vision)}\n"
} }
} }
} }
@ -91,8 +87,8 @@ public fun FlowContent.visionFragment(
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,
displayCache: MutableMap<Name, VisionDisplay> = mutableMapOf(),
fragment: HtmlVisionFragment, fragment: HtmlVisionFragment,
): Unit = consumer.visionFragment( ): Unit = consumer.visionFragment(
visionManager = visionManager, visionManager = visionManager,
@ -100,6 +96,6 @@ public fun FlowContent.visionFragment(
fetchDataUrl = fetchDataUrl, fetchDataUrl = fetchDataUrl,
updatesUrl = updatesUrl, updatesUrl = updatesUrl,
idPrefix = idPrefix, idPrefix = idPrefix,
onVisionRendered = onVisionRendered, displayCache = displayCache,
fragment = fragment fragment = fragment
) )

View File

@ -1,15 +1,15 @@
package space.kscience.visionforge.html package space.kscience.visionforge.html
import kotlinx.html.FORM import kotlinx.coroutines.CoroutineScope
import kotlinx.html.TagConsumer import kotlinx.coroutines.Job
import kotlinx.html.form import kotlinx.html.*
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.node import space.kscience.dataforge.meta.node
import space.kscience.dataforge.meta.string import space.kscience.dataforge.meta.string
import space.kscience.visionforge.ClickControl import space.kscience.visionforge.ClickControl
import space.kscience.visionforge.onClick
/** /**
* @param formId an id of the element in rendered DOM, this form is bound to * @param formId an id of the element in rendered DOM, this form is bound to
@ -18,19 +18,31 @@ import space.kscience.visionforge.ClickControl
@SerialName("html.form") @SerialName("html.form")
public class VisionOfHtmlForm( public class VisionOfHtmlForm(
public val formId: String, public val formId: String,
) : VisionOfHtmlControl() { ) : VisionOfHtmlControl(), ClickControl {
public var values: Meta? by properties.node() public var values: Meta? by properties.node()
} }
public fun <R> TagConsumer<R>.bindForm(
visionOfForm: VisionOfHtmlForm, /**
builder: FORM.() -> Unit, * Create a [VisionOfHtmlForm] and bind this form to the id
): R = form { */
this.id = visionOfForm.formId @HtmlTagMarker
builder() public inline fun <T, C : TagConsumer<T>> C.visionOfForm(
vision: VisionOfHtmlForm,
action: String? = null,
encType: FormEncType? = null,
method: FormMethod? = null,
classes: String? = null,
crossinline block: FORM.() -> Unit = {},
) : T = form(action, encType, method, classes){
this.id = vision.formId
block()
} }
public fun VisionOfHtmlForm.onSubmit(scope: CoroutineScope, block: (Meta?) -> Unit): Job = onClick(scope) { block(payload) }
@Serializable @Serializable
@SerialName("html.button") @SerialName("html.button")
public class VisionOfHtmlButton : VisionOfHtmlControl(), ClickControl { public class VisionOfHtmlButton : VisionOfHtmlControl(), ClickControl {

View File

@ -15,8 +15,10 @@ import space.kscience.dataforge.names.Name
import space.kscience.visionforge.html.VisionOfHtmlButton import space.kscience.visionforge.html.VisionOfHtmlButton
import space.kscience.visionforge.html.VisionOfHtmlForm import space.kscience.visionforge.html.VisionOfHtmlForm
/**
internal fun FormData.toMeta(): Meta { * Convert form data to Meta
*/
public fun FormData.toMeta(): Meta {
@Suppress("UNUSED_VARIABLE") val formData = this @Suppress("UNUSED_VARIABLE") val formData = this
//val res = js("Object.fromEntries(formData);") //val res = js("Object.fromEntries(formData);")
val `object` = js("{}") val `object` = js("{}")
@ -67,8 +69,10 @@ internal val formVisionRenderer: ElementVisionRenderer =
form.onsubmit = { event -> form.onsubmit = { event ->
event.preventDefault() event.preventDefault()
val formData = FormData(form).toMeta() val formData = FormData(form).toMeta()
client.sendMetaEvent(name, formData) client.context.launch {
console.info("Sent: ${formData.toMap()}") client.sendEvent(name, VisionClickEvent(name = name, payload = formData))
}
console.info("Sent form data: ${formData.toMap()}")
false false
} }
} }

View File

@ -17,9 +17,9 @@ import space.kscience.dataforge.context.info
import space.kscience.dataforge.context.logger import space.kscience.dataforge.context.logger
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.*
import space.kscience.dataforge.names.Name 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.HtmlVisionFragment import space.kscience.visionforge.html.HtmlVisionFragment
import space.kscience.visionforge.html.VisionDisplay
import space.kscience.visionforge.html.visionFragment import space.kscience.visionforge.html.visionFragment
import space.kscience.visionforge.server.VisionRoute import space.kscience.visionforge.server.VisionRoute
import space.kscience.visionforge.server.serveVisionData import space.kscience.visionforge.server.serveVisionData
@ -142,7 +142,7 @@ public class VisionForge(
//server.serveVisionsFromFragment(consumer, "content-${counter++}", fragment) //server.serveVisionsFromFragment(consumer, "content-${counter++}", fragment)
val cellRoute = "content-${counter++}" val cellRoute = "content-${counter++}"
val collector: MutableMap<Name, Vision> = mutableMapOf() val cache: MutableMap<Name, VisionDisplay> = mutableMapOf()
val url = engine.environment.connectors.first().let { val url = engine.environment.connectors.first().let {
url { url {
@ -153,13 +153,13 @@ public class VisionForge(
} }
} }
engine.application.serveVisionData(VisionRoute(cellRoute, visionManager), collector) engine.application.serveVisionData(VisionRoute(cellRoute, visionManager), cache)
visionFragment( visionFragment(
visionManager, visionManager,
embedData = true, embedData = true,
updatesUrl = url, updatesUrl = url,
onVisionRendered = { name, vision -> collector[name] = vision }, displayCache = cache,
fragment = fragment fragment = fragment
) )
} else { } else {

View File

@ -1,31 +1,50 @@
package space.kscience.visionforge.server package space.kscience.visionforge.server
import io.ktor.http.* import io.ktor.http.ContentType
import io.ktor.server.application.* import io.ktor.http.HttpStatusCode
import io.ktor.server.engine.* import io.ktor.http.URLProtocol
import io.ktor.server.html.* import io.ktor.http.path
import io.ktor.server.http.content.* import io.ktor.server.application.Application
import io.ktor.server.plugins.* import io.ktor.server.application.call
import io.ktor.server.plugins.cors.routing.* import io.ktor.server.application.install
import io.ktor.server.request.* import io.ktor.server.application.log
import io.ktor.server.response.* import io.ktor.server.engine.EngineConnectorConfig
import io.ktor.server.html.respondHtml
import io.ktor.server.plugins.cors.routing.CORS
import io.ktor.server.request.header
import io.ktor.server.request.host
import io.ktor.server.request.port
import io.ktor.server.response.header
import io.ktor.server.response.respond
import io.ktor.server.response.respondText
import io.ktor.server.routing.* import io.ktor.server.routing.*
import io.ktor.server.util.* import io.ktor.server.util.getOrFail
import io.ktor.server.websocket.* import io.ktor.server.util.url
import io.ktor.util.pipeline.* import io.ktor.server.websocket.WebSockets
import io.ktor.websocket.* import io.ktor.server.websocket.application
import io.ktor.server.websocket.webSocket
import io.ktor.websocket.Frame
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
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
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.html.* import kotlinx.html.body
import kotlinx.html.head
import kotlinx.html.header
import kotlinx.html.meta
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.ContextAware import space.kscience.dataforge.context.ContextAware
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.Configurable
import space.kscience.dataforge.meta.ObservableMutableMeta
import space.kscience.dataforge.meta.enum
import space.kscience.dataforge.meta.long
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.visionforge.* import space.kscience.visionforge.Vision
import space.kscience.visionforge.VisionEvent
import space.kscience.visionforge.VisionManager
import space.kscience.visionforge.flowChanges
import space.kscience.visionforge.html.* import space.kscience.visionforge.html.*
import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.milliseconds
@ -72,7 +91,6 @@ public class VisionRoute(
/** /**
* Serve visions in a given [route] without providing a page template. * Serve visions in a given [route] without providing a page template.
* [visions] could be changed during the service.
* *
* @return a [Flow] of backward events, including vision change events * @return a [Flow] of backward events, including vision change events
*/ */
@ -137,8 +155,8 @@ public fun Application.serveVisionData(
public fun Application.serveVisionData( public fun Application.serveVisionData(
configuration: VisionRoute, configuration: VisionRoute,
data: Map<Name, Vision>, data: Map<Name, VisionDisplay>,
): Unit = serveVisionData(configuration) { data[it] } ): Unit = serveVisionData(configuration) { data[it]?.vision }
/** /**
* Serve a page, potentially containing any number of visions at a given [route] with given [header]. * Serve a page, potentially containing any number of visions at a given [route] with given [header].
@ -154,10 +172,10 @@ public fun Application.visionPage(
) { ) {
require(WebSockets) require(WebSockets)
val collector: MutableMap<Name, Vision> = mutableMapOf() val cache: MutableMap<Name, VisionDisplay> = mutableMapOf()
//serve data //serve data
serveVisionData(configuration, collector) serveVisionData(configuration, cache)
//filled pages //filled pages
routing { routing {
@ -193,7 +211,7 @@ public fun Application.visionPage(
path(route, "ws") path(route, "ws")
} }
} else null, } else null,
onVisionRendered = { name, vision -> collector[name] = vision }, displayCache = cache,
fragment = visionFragment fragment = visionFragment
) )
} }