Update static/dynamic rendering logic
This commit is contained in:
parent
3c51060e2e
commit
eae1316de5
@ -12,7 +12,7 @@ val fxVersion by extra("11")
|
||||
|
||||
allprojects {
|
||||
group = "space.kscience"
|
||||
version = "0.3.0-dev-3"
|
||||
version = "0.3.0-dev-4"
|
||||
}
|
||||
|
||||
subprojects {
|
||||
|
@ -1,6 +1,6 @@
|
||||
package ru.mipt.npm.muon.monitor
|
||||
|
||||
import kotlinx.browser.document
|
||||
import org.w3c.dom.Document
|
||||
import react.dom.client.createRoot
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.context.fetch
|
||||
@ -13,7 +13,7 @@ import space.kscience.visionforge.startApplication
|
||||
|
||||
private class MMDemoApp : Application {
|
||||
|
||||
override fun start(state: Map<String, Any>) {
|
||||
override fun start(document: Document, state: Map<String, Any>) {
|
||||
|
||||
val context = Context("MM-demo") {
|
||||
plugin(ThreePlugin)
|
||||
|
339
demo/playground/notebooks/demo3D.ipynb
Normal file
339
demo/playground/notebooks/demo3D.ipynb
Normal file
File diff suppressed because one or more lines are too long
@ -1,4 +1,5 @@
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.visionforge.jupyter.VFNotebookPlugin
|
||||
import space.kscience.visionforge.markup.MarkupPlugin
|
||||
import space.kscience.visionforge.plotly.PlotlyPlugin
|
||||
import space.kscience.visionforge.ring.ThreeWithControlsPlugin
|
||||
@ -11,4 +12,5 @@ fun main() = runVisionClient {
|
||||
plugin(PlotlyPlugin)
|
||||
plugin(MarkupPlugin)
|
||||
plugin(TableVisionJsPlugin)
|
||||
plugin(VFNotebookPlugin)
|
||||
}
|
@ -6,13 +6,13 @@ import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.gdml.Gdml
|
||||
import space.kscience.plotly.Plot
|
||||
import space.kscience.visionforge.gdml.toVision
|
||||
import space.kscience.visionforge.jupyter.JupyterPluginBase
|
||||
import space.kscience.visionforge.jupyter.VFIntegrationBase
|
||||
import space.kscience.visionforge.plotly.PlotlyPlugin
|
||||
import space.kscience.visionforge.plotly.asVision
|
||||
import space.kscience.visionforge.solid.Solids
|
||||
|
||||
@DFExperimental
|
||||
internal class VisionForgePlayGroundForJupyter : JupyterPluginBase(
|
||||
internal class VisionForgePlayGroundForJupyter : VFIntegrationBase(
|
||||
Context("VisionForge") {
|
||||
plugin(Solids)
|
||||
plugin(PlotlyPlugin)
|
||||
|
@ -11,7 +11,6 @@ import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.visionforge.Colors
|
||||
import space.kscience.visionforge.html.VisionPage
|
||||
import space.kscience.visionforge.server.DataServeMode
|
||||
import space.kscience.visionforge.server.close
|
||||
import space.kscience.visionforge.server.openInBrowser
|
||||
import space.kscience.visionforge.server.serve
|
||||
@ -37,7 +36,6 @@ fun main() {
|
||||
}
|
||||
|
||||
val server = satContext.visionManager.serve {
|
||||
dataMode = DataServeMode.UPDATE
|
||||
page(VisionPage.threeJsHeader, VisionPage.styleSheetHeader("css/styles.css")) {
|
||||
div("flex-column") {
|
||||
h1 { +"Satellite detector demo" }
|
||||
|
@ -1,9 +1,9 @@
|
||||
package space.kscience.visionforge.solid.demo
|
||||
|
||||
import kotlinx.browser.document
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import org.w3c.dom.Document
|
||||
import space.kscience.visionforge.Application
|
||||
import space.kscience.visionforge.solid.x
|
||||
import space.kscience.visionforge.solid.y
|
||||
@ -12,7 +12,7 @@ import kotlin.random.Random
|
||||
|
||||
private class ThreeDemoApp : Application {
|
||||
|
||||
override fun start(state: Map<String, Any>) {
|
||||
override fun start(document: Document, state: Map<String, Any>) {
|
||||
|
||||
val element = document.getElementById("demo") ?: error("Element with id 'demo' not found on page")
|
||||
|
||||
|
51
jupyter/src/jsMain/kotlin/VFNotebookPlugin.kt
Normal file
51
jupyter/src/jsMain/kotlin/VFNotebookPlugin.kt
Normal file
@ -0,0 +1,51 @@
|
||||
package space.kscience.visionforge.jupyter
|
||||
|
||||
import kotlinx.browser.window
|
||||
import org.w3c.dom.Element
|
||||
import space.kscience.dataforge.context.AbstractPlugin
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.context.PluginFactory
|
||||
import space.kscience.dataforge.context.PluginTag
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.visionforge.VisionClient
|
||||
import space.kscience.visionforge.renderAllVisions
|
||||
import space.kscience.visionforge.renderAllVisionsById
|
||||
import space.kscience.visionforge.renderAllVisionsIn
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@JsExport
|
||||
public class VFNotebookPlugin : AbstractPlugin() {
|
||||
private val client by require(VisionClient)
|
||||
|
||||
public fun renderAllVisionsIn(element: Element) {
|
||||
client.renderAllVisionsIn(element)
|
||||
}
|
||||
|
||||
public fun renderAllVisionsById(id: String) {
|
||||
client.renderAllVisionsById(id)
|
||||
}
|
||||
|
||||
public fun renderAllVisions() {
|
||||
client.renderAllVisions()
|
||||
}
|
||||
|
||||
|
||||
init {
|
||||
//register VisionForge in the browser window
|
||||
window.asDynamic().vf = this
|
||||
window.asDynamic().VisionForge = this
|
||||
}
|
||||
|
||||
@Suppress("NON_EXPORTABLE_TYPE")
|
||||
override val tag: PluginTag get() = Companion.tag
|
||||
|
||||
@Suppress("NON_EXPORTABLE_TYPE")
|
||||
public companion object : PluginFactory<VFNotebookPlugin> {
|
||||
override fun build(context: Context, meta: Meta): VFNotebookPlugin = VFNotebookPlugin()
|
||||
|
||||
override val tag: PluginTag = PluginTag(name = "vision.notebook", group = PluginTag.DATAFORGE_GROUP)
|
||||
|
||||
override val type: KClass<out VFNotebookPlugin> = VFNotebookPlugin::class
|
||||
}
|
||||
|
||||
}
|
@ -1,11 +1,9 @@
|
||||
package space.kscience.visionforge.jupyter
|
||||
|
||||
import io.ktor.server.engine.ApplicationEngine
|
||||
import kotlinx.html.FORM
|
||||
import kotlinx.html.TagConsumer
|
||||
import kotlinx.html.p
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.html.*
|
||||
import kotlinx.html.stream.createHTML
|
||||
import kotlinx.html.style
|
||||
import org.jetbrains.kotlinx.jupyter.api.HTML
|
||||
import org.jetbrains.kotlinx.jupyter.api.MimeTypedResult
|
||||
import space.kscience.dataforge.context.Context
|
||||
@ -21,11 +19,21 @@ import space.kscience.visionforge.html.visionFragment
|
||||
import space.kscience.visionforge.server.VisionServer
|
||||
import space.kscience.visionforge.server.serve
|
||||
import space.kscience.visionforge.visionManager
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.random.Random
|
||||
import kotlin.random.nextUInt
|
||||
|
||||
internal fun TagConsumer<*>.renderScriptForId(id: String) {
|
||||
script {
|
||||
type = "text/javascript"
|
||||
unsafe { +"VisionForge.renderAllVisionsById(\"$id\");" }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A handler class that includes a server and common utilities
|
||||
*/
|
||||
public class VisionForgeForNotebook(override val context: Context) : ContextAware {
|
||||
public class VFForNotebook(override val context: Context) : ContextAware, CoroutineScope {
|
||||
private var counter = 0
|
||||
|
||||
private var engine: ApplicationEngine? = null
|
||||
@ -33,6 +41,8 @@ public class VisionForgeForNotebook(override val context: Context) : ContextAwar
|
||||
|
||||
public var isolateFragments: Boolean = false
|
||||
|
||||
override val coroutineContext: CoroutineContext get() = context.coroutineContext
|
||||
|
||||
public fun legacyMode() {
|
||||
isolateFragments = true
|
||||
}
|
||||
@ -68,15 +78,29 @@ public class VisionForgeForNotebook(override val context: Context) : ContextAwar
|
||||
public fun stopServer() {
|
||||
engine?.apply {
|
||||
logger.info { "Stopping VisionForge server" }
|
||||
}?.stop(1000, 2000)
|
||||
stop(1000, 2000)
|
||||
engine = null
|
||||
server = null
|
||||
}
|
||||
}
|
||||
|
||||
private fun produceHtmlString(
|
||||
fragment: HtmlVisionFragment,
|
||||
): String = server?.serveVisionsFromFragment("content[${counter++}]", fragment)
|
||||
?: createHTML().apply {
|
||||
visionFragment(context, fragment = fragment)
|
||||
}.finalize()
|
||||
): String = createHTML().apply {
|
||||
val server = server
|
||||
val id = "fragment[${fragment.hashCode()}/${Random.nextUInt()}]"
|
||||
div {
|
||||
this.id = id
|
||||
if (server != null) {
|
||||
//if server exist, serve dynamically
|
||||
server.serveVisionsFromFragment(consumer, "content-${counter++}", fragment)
|
||||
} else {
|
||||
//if not, use static rendering
|
||||
visionFragment(context, fragment = fragment)
|
||||
}
|
||||
}
|
||||
renderScriptForId(id)
|
||||
}.finalize()
|
||||
|
||||
public fun produceHtml(isolated: Boolean? = null, fragment: HtmlVisionFragment): MimeTypedResult =
|
||||
HTML(produceHtmlString(fragment), isolated ?: isolateFragments)
|
@ -1,8 +1,7 @@
|
||||
package space.kscience.visionforge.jupyter
|
||||
|
||||
import kotlinx.html.p
|
||||
import kotlinx.html.*
|
||||
import kotlinx.html.stream.createHTML
|
||||
import kotlinx.html.style
|
||||
import org.jetbrains.kotlinx.jupyter.api.HTML
|
||||
import org.jetbrains.kotlinx.jupyter.api.declare
|
||||
import org.jetbrains.kotlinx.jupyter.api.libraries.JupyterIntegration
|
||||
@ -11,11 +10,16 @@ import space.kscience.dataforge.context.ContextAware
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.html.*
|
||||
import kotlin.random.Random
|
||||
import kotlin.random.nextUInt
|
||||
|
||||
/**
|
||||
* A base class for different Jupyter VF integrations
|
||||
*/
|
||||
@DFExperimental
|
||||
public abstract class JupyterPluginBase(final override val context: Context) : JupyterIntegration(), ContextAware {
|
||||
public abstract class VFIntegrationBase(final override val context: Context) : JupyterIntegration(), ContextAware {
|
||||
|
||||
protected val handler: VisionForgeForNotebook = VisionForgeForNotebook(context)
|
||||
protected val handler: VFForNotebook = VFForNotebook(context)
|
||||
|
||||
protected abstract fun Builder.afterLoaded()
|
||||
|
||||
@ -50,7 +54,24 @@ public abstract class JupyterPluginBase(final override val context: Context) : J
|
||||
}
|
||||
|
||||
render<VisionPage> { page ->
|
||||
HTML(page.render(createHTML()), true)
|
||||
HTML(createHTML().apply {
|
||||
head {
|
||||
meta {
|
||||
charset = "utf-8"
|
||||
}
|
||||
page.pageHeaders.values.forEach {
|
||||
fragment(it)
|
||||
}
|
||||
}
|
||||
body {
|
||||
val id = "fragment[${page.hashCode()}/${Random.nextUInt()}]"
|
||||
div {
|
||||
this.id = id
|
||||
visionFragment(context, fragment = page.content)
|
||||
}
|
||||
renderScriptForId(id)
|
||||
}
|
||||
}.finalize(), true)
|
||||
}
|
||||
|
||||
render<HtmlFormFragment> { fragment ->
|
@ -5,11 +5,11 @@ import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.gdml.Gdml
|
||||
import space.kscience.visionforge.gdml.toVision
|
||||
import space.kscience.visionforge.jupyter.JupyterPluginBase
|
||||
import space.kscience.visionforge.jupyter.VFIntegrationBase
|
||||
import space.kscience.visionforge.solid.Solids
|
||||
|
||||
@DFExperimental
|
||||
internal class GdmlForJupyter : JupyterPluginBase(
|
||||
internal class GdmlForJupyter : VFIntegrationBase(
|
||||
Context("GDML") {
|
||||
plugin(Solids)
|
||||
}
|
||||
|
@ -8,8 +8,6 @@ import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.VisionManager
|
||||
import kotlin.random.Random
|
||||
import kotlin.random.nextUInt
|
||||
|
||||
public typealias HtmlVisionFragment = VisionTagConsumer<*>.() -> Unit
|
||||
|
||||
@ -17,9 +15,6 @@ public typealias HtmlVisionFragment = VisionTagConsumer<*>.() -> Unit
|
||||
public fun HtmlVisionFragment(content: VisionTagConsumer<*>.() -> Unit): HtmlVisionFragment = content
|
||||
|
||||
|
||||
internal const val RENDER_FUNCTION_NAME = "renderAllVisionsById"
|
||||
|
||||
|
||||
/**
|
||||
* Render a fragment in the given consumer and return a map of extracted visions
|
||||
* @param context a context used to create a vision fragment
|
||||
@ -35,7 +30,6 @@ public fun TagConsumer<*>.visionFragment(
|
||||
fetchDataUrl: String? = null,
|
||||
fetchUpdatesUrl: String? = null,
|
||||
idPrefix: String? = null,
|
||||
renderScript: Boolean = true,
|
||||
fragment: HtmlVisionFragment,
|
||||
): Map<Name, Vision> {
|
||||
val visionMap = HashMap<Name, Vision>()
|
||||
@ -63,19 +57,9 @@ public fun TagConsumer<*>.visionFragment(
|
||||
}
|
||||
}
|
||||
}
|
||||
if (renderScript) {
|
||||
val id = "fragment[${fragment.hashCode()}/${Random.nextUInt()}]"
|
||||
div {
|
||||
this.id = id
|
||||
fragment(consumer)
|
||||
}
|
||||
script {
|
||||
type = "text/javascript"
|
||||
unsafe { +"window.${RENDER_FUNCTION_NAME}(\"$id\");" }
|
||||
}
|
||||
} else {
|
||||
fragment(consumer)
|
||||
}
|
||||
|
||||
fragment(consumer)
|
||||
|
||||
return visionMap
|
||||
}
|
||||
|
||||
@ -83,16 +67,14 @@ public fun FlowContent.visionFragment(
|
||||
context: Context = Global,
|
||||
embedData: Boolean = true,
|
||||
fetchDataUrl: String? = null,
|
||||
fetchUpdatesUrl: String? = null,
|
||||
flowDataUrl: String? = null,
|
||||
idPrefix: String? = null,
|
||||
renderScript: Boolean = true,
|
||||
fragment: HtmlVisionFragment,
|
||||
): Map<Name, Vision> = consumer.visionFragment(
|
||||
context,
|
||||
embedData,
|
||||
fetchDataUrl,
|
||||
fetchUpdatesUrl,
|
||||
flowDataUrl,
|
||||
idPrefix,
|
||||
renderScript,
|
||||
fragment
|
||||
)
|
@ -13,20 +13,6 @@ public data class VisionPage(
|
||||
public val pageHeaders: Map<String, HtmlFragment> = emptyMap(),
|
||||
public val content: HtmlVisionFragment,
|
||||
) {
|
||||
public fun <R> render(root: TagConsumer<R>): R = root.apply {
|
||||
head {
|
||||
meta {
|
||||
charset = "utf-8"
|
||||
}
|
||||
pageHeaders.values.forEach {
|
||||
fragment(it)
|
||||
}
|
||||
}
|
||||
body {
|
||||
visionFragment(context, fragment = content)
|
||||
}
|
||||
}.finalize()
|
||||
|
||||
public companion object{
|
||||
/**
|
||||
* Use a script with given [src] as a global header for all pages.
|
||||
|
@ -2,7 +2,7 @@ package space.kscience.visionforge
|
||||
|
||||
import kotlinx.browser.document
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.dom.hasClass
|
||||
import org.w3c.dom.Document
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
|
||||
@ -36,7 +36,7 @@ public interface Application: CoroutineScope {
|
||||
* Starting point for an application.
|
||||
* @param state Initial state between Hot Module Replacement (HMR).
|
||||
*/
|
||||
public fun start(state: Map<String, Any>)
|
||||
public fun start(document: Document, state: Map<String, Any>)
|
||||
|
||||
/**
|
||||
* Ending point for an application.
|
||||
@ -46,17 +46,13 @@ public interface Application: CoroutineScope {
|
||||
}
|
||||
|
||||
public fun startApplication(builder: () -> Application) {
|
||||
fun start(state: dynamic): Application? {
|
||||
return if (document.body?.hasClass("application") == true) {
|
||||
val application = builder()
|
||||
fun start(document: Document, state: dynamic): Application{
|
||||
val application = builder()
|
||||
|
||||
@Suppress("UnsafeCastFromDynamic")
|
||||
application.start(state?.appState ?: emptyMap())
|
||||
@Suppress("UnsafeCastFromDynamic")
|
||||
application.start(document, state?.appState ?: emptyMap())
|
||||
|
||||
application
|
||||
} else {
|
||||
null
|
||||
}
|
||||
return application
|
||||
}
|
||||
|
||||
var application: Application? = null
|
||||
@ -73,9 +69,9 @@ public fun startApplication(builder: () -> Application) {
|
||||
}
|
||||
|
||||
if (document.body != null) {
|
||||
application = start(state)
|
||||
application = start(document, state)
|
||||
} else {
|
||||
application = null
|
||||
document.addEventListener("DOMContentLoaded", { application = start(state) })
|
||||
document.addEventListener("DOMContentLoaded", { application = start(document, state) })
|
||||
}
|
||||
}
|
@ -13,7 +13,6 @@ import space.kscience.dataforge.meta.MetaSerializer
|
||||
import space.kscience.dataforge.meta.get
|
||||
import space.kscience.dataforge.meta.int
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.visionforge.html.RENDER_FUNCTION_NAME
|
||||
import space.kscience.visionforge.html.VisionTagConsumer
|
||||
import space.kscience.visionforge.html.VisionTagConsumer.Companion.OUTPUT_CONNECT_ATTRIBUTE
|
||||
import space.kscience.visionforge.html.VisionTagConsumer.Companion.OUTPUT_ENDPOINT_ATTRIBUTE
|
||||
@ -46,18 +45,16 @@ public class VisionClient : AbstractPlugin() {
|
||||
return attribute?.value
|
||||
}
|
||||
|
||||
private fun getRenderers() = context.gather<ElementVisionRenderer>(ElementVisionRenderer.TYPE).values
|
||||
private val renderers by lazy { context.gather<ElementVisionRenderer>(ElementVisionRenderer.TYPE).values }
|
||||
|
||||
private fun findRendererFor(vision: Vision): ElementVisionRenderer? {
|
||||
return getRenderers().mapNotNull {
|
||||
val rating = it.rateVision(vision)
|
||||
if (rating > 0) {
|
||||
rating to it
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}.maxByOrNull { it.first }?.second
|
||||
}
|
||||
private fun findRendererFor(vision: Vision): ElementVisionRenderer? = renderers.mapNotNull {
|
||||
val rating = it.rateVision(vision)
|
||||
if (rating > 0) {
|
||||
rating to it
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}.maxByOrNull { it.first }?.second
|
||||
|
||||
private fun Element.getEmbeddedData(className: String): String? = getElementsByClassName(className)[0]?.innerHTML
|
||||
|
||||
@ -78,7 +75,7 @@ public class VisionClient : AbstractPlugin() {
|
||||
if (vision != null) {
|
||||
vision.setAsRoot(visionManager)
|
||||
val renderer = findRendererFor(vision)
|
||||
?: error("Could not find renderer for ${visionManager.encodeToString(vision)}")
|
||||
?: error("Could not find renderer for ${vision::class}")
|
||||
renderer.render(element, vision, outputMeta)
|
||||
|
||||
element.attributes[OUTPUT_CONNECT_ATTRIBUTE]?.let { attr ->
|
||||
@ -228,7 +225,7 @@ public class VisionClient : AbstractPlugin() {
|
||||
|
||||
|
||||
private fun whenDocumentLoaded(block: Document.() -> Unit): Unit {
|
||||
if (document.readyState == DocumentReadyState.COMPLETE) {
|
||||
if (document.body != null) {
|
||||
block(document)
|
||||
} else {
|
||||
document.addEventListener("DOMContentLoaded", { block(document) })
|
||||
@ -267,14 +264,29 @@ public fun VisionClient.renderAllVisions(): Unit = whenDocumentLoaded {
|
||||
renderAllVisionsIn(element)
|
||||
}
|
||||
|
||||
public class VisionClientApplication(public val context: Context) : Application {
|
||||
private val client = context.fetch(VisionClient)
|
||||
|
||||
override fun start(document: Document, state: Map<String, Any>) {
|
||||
console.info("Starting Vision Client")
|
||||
val element = document.body ?: error("Document does not have a body")
|
||||
client.renderAllVisionsIn(element)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a vision client context and render all visions on the page.
|
||||
*/
|
||||
public fun runVisionClient(contextBuilder: ContextBuilder.() -> Unit) {
|
||||
console.info("Starting VisionForge context")
|
||||
val context = Context("VisionForge", contextBuilder)
|
||||
val visionClient = context.fetch(VisionClient)
|
||||
window.asDynamic()[RENDER_FUNCTION_NAME] = visionClient::renderAllVisionsById
|
||||
|
||||
//visionClient.renderAllVisions()
|
||||
val context = Context("VisionForge") {
|
||||
plugin(VisionClient)
|
||||
contextBuilder()
|
||||
}
|
||||
|
||||
startApplication {
|
||||
VisionClientApplication(context)
|
||||
}
|
||||
}
|
@ -1,9 +1,14 @@
|
||||
package space.kscience.visionforge
|
||||
|
||||
import kotlinx.html.body
|
||||
import kotlinx.html.head
|
||||
import kotlinx.html.meta
|
||||
import kotlinx.html.stream.createHTML
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.visionforge.html.HtmlFragment
|
||||
import space.kscience.visionforge.html.VisionPage
|
||||
import space.kscience.visionforge.html.fragment
|
||||
import space.kscience.visionforge.html.visionFragment
|
||||
import java.awt.Desktop
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
@ -56,20 +61,34 @@ import java.nio.file.Path
|
||||
|
||||
/**
|
||||
* Export a [VisionPage] to a file
|
||||
*
|
||||
* @param fileHeaders additional file-system specific headers.
|
||||
*/
|
||||
@DFExperimental
|
||||
public fun VisionPage.makeFile(
|
||||
path: Path?,
|
||||
defaultHeaders: ((Path) -> Map<String, HtmlFragment>)? = null,
|
||||
fileHeaders: ((Path) -> Map<String, HtmlFragment>)? = null,
|
||||
): Path {
|
||||
val actualFile = path?.let {
|
||||
Path.of(System.getProperty("user.home")).resolve(path)
|
||||
} ?: Files.createTempFile("tempPlot", ".html")
|
||||
|
||||
val actualDefaultHeaders = defaultHeaders?.invoke(actualFile)
|
||||
val actualPage = if (actualDefaultHeaders == null) this else copy(pageHeaders = actualDefaultHeaders + pageHeaders)
|
||||
val actualDefaultHeaders = fileHeaders?.invoke(actualFile)
|
||||
val actualHeaders = if (actualDefaultHeaders == null) pageHeaders else actualDefaultHeaders + pageHeaders
|
||||
|
||||
val htmlString = actualPage.render(createHTML())
|
||||
val htmlString = createHTML().apply {
|
||||
head {
|
||||
meta {
|
||||
charset = "utf-8"
|
||||
}
|
||||
actualHeaders.values.forEach {
|
||||
fragment(it)
|
||||
}
|
||||
}
|
||||
body {
|
||||
visionFragment(context, fragment = content)
|
||||
}
|
||||
}.finalize()
|
||||
|
||||
Files.writeString(actualFile, htmlString)
|
||||
return actualFile
|
||||
|
@ -115,7 +115,7 @@ public class VisionServer internal constructor(
|
||||
context = visionManager.context,
|
||||
embedData = dataMode == DataServeMode.EMBED,
|
||||
fetchDataUrl = if (dataMode != DataServeMode.EMBED) "$serverUrl$pagePath/data" else null,
|
||||
fetchUpdatesUrl = if (dataMode == DataServeMode.UPDATE) "$serverUrl$pagePath/ws" else null,
|
||||
flowDataUrl = if (dataMode == DataServeMode.UPDATE) "$serverUrl$pagePath/ws" else null,
|
||||
fragment = visionFragment
|
||||
)
|
||||
}
|
||||
@ -192,18 +192,19 @@ public class VisionServer internal constructor(
|
||||
* Compile a fragment to string and serve visions from it
|
||||
*/
|
||||
public fun serveVisionsFromFragment(
|
||||
consumer: TagConsumer<*>,
|
||||
route: String,
|
||||
fragment: HtmlVisionFragment,
|
||||
): String = createHTML().apply {
|
||||
val visions = visionFragment(
|
||||
): Unit {
|
||||
val visions = consumer.visionFragment(
|
||||
visionManager.context,
|
||||
embedData = true,
|
||||
fetchUpdatesUrl = "$serverUrl$route/ws",
|
||||
renderScript = true,
|
||||
fragment = fragment
|
||||
)
|
||||
|
||||
serveVisions(route, visions)
|
||||
}.finalize()
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve a page, potentially containing any number of visions at a given [route] with given [header].
|
||||
@ -248,7 +249,11 @@ public class VisionServer internal constructor(
|
||||
title: String = "VisionForge server page '$route'",
|
||||
visionFragment: HtmlVisionFragment,
|
||||
) {
|
||||
page(route, mapOf("title" to VisionPage.title(title)) + headers.associateBy { it.hashCode().toString() }, visionFragment)
|
||||
page(
|
||||
route,
|
||||
mapOf("title" to VisionPage.title(title)) + headers.associateBy { it.hashCode().toString() },
|
||||
visionFragment
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user