Refactor page headers and fragment requirements
This commit is contained in:
parent
bfa7f5ea57
commit
9745a58873
@ -36,13 +36,13 @@ internal class VisionForgePlayGroundForJupyter : JupyterPluginBase(
|
||||
|
||||
render<Gdml> { gdmlModel ->
|
||||
handler.produceHtml {
|
||||
vision(gdmlModel.toVision())
|
||||
vision { gdmlModel.toVision() }
|
||||
}
|
||||
}
|
||||
|
||||
render<Plot> { plot ->
|
||||
handler.produceHtml {
|
||||
vision(plot.asVision())
|
||||
vision { plot.asVision() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package space.kscience.visionforge.examples
|
||||
|
||||
import kotlinx.html.h2
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.values.ValueType
|
||||
import space.kscience.plotly.layout
|
||||
import space.kscience.plotly.models.ScatterMode
|
||||
@ -10,25 +9,15 @@ import space.kscience.plotly.scatter
|
||||
import space.kscience.tables.ColumnHeader
|
||||
import space.kscience.visionforge.html.ResourceLocation
|
||||
import space.kscience.visionforge.markup.markdown
|
||||
import space.kscience.visionforge.plotly.PlotlyPlugin
|
||||
import space.kscience.visionforge.plotly.plotly
|
||||
import space.kscience.visionforge.solid.Solids
|
||||
import space.kscience.visionforge.solid.box
|
||||
import space.kscience.visionforge.solid.solid
|
||||
import space.kscience.visionforge.solid.z
|
||||
import space.kscience.visionforge.tables.TableVisionPlugin
|
||||
import space.kscience.visionforge.tables.columnTable
|
||||
import java.nio.file.Paths
|
||||
|
||||
|
||||
fun main() {
|
||||
val context = Context {
|
||||
plugin(Solids)
|
||||
plugin(PlotlyPlugin)
|
||||
plugin(TableVisionPlugin)
|
||||
}
|
||||
|
||||
context.makeVisionFile(
|
||||
fun main() = makeVisionFile(
|
||||
Paths.get("VisionForgeDemo.html"),
|
||||
resourceLocation = ResourceLocation.EMBED
|
||||
) {
|
||||
@ -183,4 +172,3 @@ fun main() {
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
}
|
@ -3,21 +3,19 @@ package space.kscience.visionforge.examples
|
||||
import kotlinx.html.*
|
||||
import space.kscience.dataforge.context.Global
|
||||
import space.kscience.dataforge.context.fetch
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.visionforge.VisionManager
|
||||
import space.kscience.visionforge.html.Page
|
||||
import space.kscience.visionforge.html.formFragment
|
||||
import space.kscience.visionforge.onPropertyChange
|
||||
import space.kscience.visionforge.three.server.close
|
||||
import space.kscience.visionforge.three.server.openInBrowser
|
||||
import space.kscience.visionforge.three.server.serve
|
||||
import space.kscience.visionforge.three.server.useScript
|
||||
import space.kscience.visionforge.server.close
|
||||
import space.kscience.visionforge.server.openInBrowser
|
||||
import space.kscience.visionforge.server.serve
|
||||
|
||||
fun main() {
|
||||
val visionManager = Global.fetch(VisionManager)
|
||||
|
||||
val server = visionManager.serve {
|
||||
useScript("js/visionforge-playground.js")
|
||||
page {
|
||||
page(header = Page.scriptHeader("js/visionforge-playground.js")) {
|
||||
val form = formFragment("form") {
|
||||
label {
|
||||
htmlFor = "fname"
|
||||
@ -50,7 +48,7 @@ fun main() {
|
||||
}
|
||||
}
|
||||
|
||||
vision("form".asName(), form)
|
||||
vision("form") { form }
|
||||
form.onPropertyChange {
|
||||
println(this)
|
||||
}
|
||||
|
@ -1,19 +1,13 @@
|
||||
package space.kscience.visionforge.examples
|
||||
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.gdml.GdmlShowCase
|
||||
import space.kscience.visionforge.gdml.toVision
|
||||
import space.kscience.visionforge.html.ResourceLocation
|
||||
import space.kscience.visionforge.solid.Solids
|
||||
|
||||
fun main() {
|
||||
val context = Context {
|
||||
plugin(Solids)
|
||||
}
|
||||
|
||||
context.makeVisionFile(resourceLocation = ResourceLocation.SYSTEM){
|
||||
fun main() = makeVisionFile(resourceLocation = ResourceLocation.SYSTEM){
|
||||
vision("canvas") {
|
||||
requirePlugin(Solids)
|
||||
GdmlShowCase.cubes().toVision()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
package space.kscience.visionforge.examples
|
||||
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.gdml.*
|
||||
import space.kscience.visionforge.gdml.toVision
|
||||
import space.kscience.visionforge.html.ResourceLocation
|
||||
@ -10,13 +9,9 @@ import space.kscience.visionforge.solid.invoke
|
||||
import space.kscience.visionforge.visible
|
||||
import java.nio.file.Path
|
||||
|
||||
fun main() {
|
||||
val context = Context {
|
||||
plugin(Solids)
|
||||
}
|
||||
|
||||
context.makeVisionFile(Path.of("curves.html"), resourceLocation = ResourceLocation.EMBED) {
|
||||
fun main() = makeVisionFile(Path.of("curves.html"), resourceLocation = ResourceLocation.EMBED) {
|
||||
vision("canvas") {
|
||||
requirePlugin(Solids)
|
||||
Gdml {
|
||||
// geometry variables
|
||||
val worldSize = 500
|
||||
@ -241,4 +236,3 @@ fun main() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,16 +1,12 @@
|
||||
package space.kscience.visionforge.examples
|
||||
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.gdml.GdmlShowCase
|
||||
import space.kscience.visionforge.gdml.toVision
|
||||
import space.kscience.visionforge.solid.Solids
|
||||
|
||||
fun main() {
|
||||
val context = Context {
|
||||
plugin(Solids)
|
||||
}
|
||||
|
||||
context.makeVisionFile {
|
||||
vision("canvas") { GdmlShowCase.babyIaxo().toVision() }
|
||||
fun main() = makeVisionFile {
|
||||
vision("canvas") {
|
||||
requirePlugin(Solids)
|
||||
GdmlShowCase.babyIaxo().toVision()
|
||||
}
|
||||
}
|
@ -1,16 +1,10 @@
|
||||
package space.kscience.visionforge.examples
|
||||
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.plotly.scatter
|
||||
import space.kscience.visionforge.html.ResourceLocation
|
||||
import space.kscience.visionforge.plotly.PlotlyPlugin
|
||||
import space.kscience.visionforge.plotly.plotly
|
||||
|
||||
fun main() {
|
||||
val context = Context {
|
||||
plugin(PlotlyPlugin)
|
||||
}
|
||||
context.makeVisionFile(resourceLocation = ResourceLocation.SYSTEM){
|
||||
fun main() = makeVisionFile(resourceLocation = ResourceLocation.SYSTEM) {
|
||||
vision {
|
||||
plotly {
|
||||
scatter {
|
||||
@ -20,4 +14,3 @@ fun main() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,20 +2,14 @@ package space.kscience.visionforge.examples
|
||||
|
||||
import kotlinx.html.div
|
||||
import kotlinx.html.h1
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.visionforge.html.ResourceLocation
|
||||
import space.kscience.visionforge.solid.*
|
||||
import java.nio.file.Paths
|
||||
import kotlin.random.Random
|
||||
|
||||
fun main() {
|
||||
val context = Context {
|
||||
plugin(Solids)
|
||||
}
|
||||
private val random = Random(112233)
|
||||
|
||||
val random = Random(112233)
|
||||
|
||||
context.makeVisionFile(
|
||||
fun main() = makeVisionFile(
|
||||
Paths.get("randomSpheres.html"),
|
||||
resourceLocation = ResourceLocation.SYSTEM
|
||||
) {
|
||||
@ -38,4 +32,3 @@ fun main() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@ package space.kscience.visionforge.examples
|
||||
import ru.mipt.npm.root.DGeoManager
|
||||
import ru.mipt.npm.root.serialization.TGeoManager
|
||||
import ru.mipt.npm.root.toSolid
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.get
|
||||
import space.kscience.dataforge.meta.isLeaf
|
||||
@ -22,11 +21,6 @@ private fun Meta.countTypes(): Sequence<String> = sequence {
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val context = Context {
|
||||
plugin(Solids)
|
||||
}
|
||||
|
||||
|
||||
val string = ZipInputStream(TGeoManager::class.java.getResourceAsStream("/root/BM@N_geometry.zip")!!).use {
|
||||
it.nextEntry
|
||||
it.readAllBytes().decodeToString()
|
||||
@ -45,8 +39,9 @@ fun main() {
|
||||
Paths.get("BM@N.vf.json").writeText(Solids.encodeToString(solid))
|
||||
//println(Solids.encodeToString(solid))
|
||||
|
||||
context.makeVisionFile {
|
||||
makeVisionFile {
|
||||
vision("canvas") {
|
||||
requirePlugin(Solids)
|
||||
solid
|
||||
}
|
||||
}
|
||||
|
@ -1,34 +1,25 @@
|
||||
package space.kscience.visionforge.examples
|
||||
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.dataforge.context.Global
|
||||
import space.kscience.visionforge.html.HtmlVisionFragment
|
||||
import space.kscience.visionforge.html.Page
|
||||
import space.kscience.visionforge.html.ResourceLocation
|
||||
import space.kscience.visionforge.html.VisionTagConsumer
|
||||
import space.kscience.visionforge.html.page
|
||||
import space.kscience.visionforge.html.scriptHeader
|
||||
import space.kscience.visionforge.html.importScriptHeader
|
||||
import space.kscience.visionforge.makeFile
|
||||
import space.kscience.visionforge.three.server.VisionServer
|
||||
import space.kscience.visionforge.three.server.useScript
|
||||
import space.kscience.visionforge.visionManager
|
||||
import java.awt.Desktop
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
public fun VisionServer.usePlayground(): Unit {
|
||||
useScript("js/visionforge-playground.js")
|
||||
}
|
||||
|
||||
@OptIn(DFExperimental::class)
|
||||
public fun Context.makeVisionFile(
|
||||
public fun makeVisionFile(
|
||||
path: Path? = null,
|
||||
title: String = "VisionForge page",
|
||||
resourceLocation: ResourceLocation = ResourceLocation.SYSTEM,
|
||||
show: Boolean = true,
|
||||
content: VisionTagConsumer<*>.() -> Unit,
|
||||
content: HtmlVisionFragment,
|
||||
): Unit {
|
||||
val actualPath = visionManager.page(title, content = content).makeFile(path) { actualPath ->
|
||||
val actualPath = Page(Global, content = content).makeFile(path) { actualPath ->
|
||||
mapOf(
|
||||
"playground" to scriptHeader("js/visionforge-playground.js", resourceLocation, actualPath),
|
||||
"title" to Page.title(title),
|
||||
"playground" to Page.importScriptHeader("js/visionforge-playground.js", resourceLocation, actualPath),
|
||||
)
|
||||
}
|
||||
if (show) Desktop.getDesktop().browse(actualPath.toFile().toURI())
|
||||
|
@ -1,15 +1,12 @@
|
||||
package space.kscience.visionforge.examples
|
||||
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.visionforge.html.ResourceLocation
|
||||
import space.kscience.visionforge.solid.*
|
||||
import space.kscience.visionforge.solid.box
|
||||
import space.kscience.visionforge.solid.invoke
|
||||
import space.kscience.visionforge.solid.material
|
||||
import space.kscience.visionforge.solid.solid
|
||||
|
||||
fun main() {
|
||||
val context = Context {
|
||||
plugin(Solids)
|
||||
}
|
||||
|
||||
context.makeVisionFile(resourceLocation = ResourceLocation.SYSTEM){
|
||||
fun main() = makeVisionFile(resourceLocation = ResourceLocation.SYSTEM) {
|
||||
vision("canvas") {
|
||||
solid {
|
||||
box(100, 100, 100)
|
||||
@ -19,4 +16,3 @@ fun main() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,22 +1,17 @@
|
||||
package space.kscience.visionforge.examples
|
||||
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.values.ValueType
|
||||
import space.kscience.tables.ColumnHeader
|
||||
import space.kscience.tables.valueRow
|
||||
import space.kscience.visionforge.html.ResourceLocation
|
||||
import space.kscience.visionforge.tables.TableVisionPlugin
|
||||
import space.kscience.visionforge.tables.table
|
||||
import kotlin.math.pow
|
||||
|
||||
fun main() {
|
||||
val context = Context {
|
||||
plugin(TableVisionPlugin)
|
||||
}
|
||||
val x by ColumnHeader.value(ValueType.NUMBER)
|
||||
val y by ColumnHeader.value(ValueType.NUMBER)
|
||||
|
||||
context.makeVisionFile(resourceLocation = ResourceLocation.SYSTEM) {
|
||||
makeVisionFile(resourceLocation = ResourceLocation.SYSTEM) {
|
||||
vision {
|
||||
table(x, y) {
|
||||
repeat(100) {
|
||||
|
@ -1,19 +1,22 @@
|
||||
package ru.mipt.npm.sat
|
||||
|
||||
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.html.div
|
||||
import kotlinx.html.h1
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.visionforge.html.Page
|
||||
import space.kscience.visionforge.html.plus
|
||||
import space.kscience.visionforge.server.close
|
||||
import space.kscience.visionforge.server.openInBrowser
|
||||
import space.kscience.visionforge.server.serve
|
||||
import space.kscience.visionforge.solid.*
|
||||
import space.kscience.visionforge.three.server.*
|
||||
import space.kscience.visionforge.three.threeJsHeader
|
||||
import space.kscience.visionforge.visionManager
|
||||
import kotlin.random.Random
|
||||
|
||||
|
||||
fun main() {
|
||||
val satContext = Context("sat") {
|
||||
plugin(Solids)
|
||||
@ -23,20 +26,17 @@ fun main() {
|
||||
val sat = visionOfSatellite(ySegments = 3)
|
||||
|
||||
val server = satContext.visionManager.serve {
|
||||
//use client library
|
||||
useThreeJs()
|
||||
//use css
|
||||
useCss("css/styles.css")
|
||||
page {
|
||||
page(header = Page.threeJsHeader + Page.styleSheetHeader("css/styles.css")) {
|
||||
div("flex-column") {
|
||||
h1 { +"Satellite detector demo" }
|
||||
vision(sat)
|
||||
vision { sat }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
server.openInBrowser()
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
GlobalScope.launch {
|
||||
while (isActive) {
|
||||
val randomLayer = Random.nextInt(1, 11)
|
||||
|
@ -1,7 +1,6 @@
|
||||
package space.kscience.visionforge.solid.demo
|
||||
|
||||
import kotlinx.browser.document
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
@ -30,7 +29,8 @@ private class ThreeDemoApp : Application {
|
||||
}
|
||||
}
|
||||
}
|
||||
GlobalScope.launch {
|
||||
|
||||
launch {
|
||||
while (isActive) {
|
||||
delay(500)
|
||||
boxes.forEach { box ->
|
||||
|
@ -44,7 +44,7 @@ public abstract class JupyterPluginBase(final override val context: Context) : J
|
||||
|
||||
render<Vision> { vision ->
|
||||
handler.produceHtml {
|
||||
vision(vision)
|
||||
vision { vision }
|
||||
}
|
||||
|
||||
}
|
||||
@ -62,7 +62,7 @@ public abstract class JupyterPluginBase(final override val context: Context) : J
|
||||
}
|
||||
}
|
||||
fragment(fragment.formBody)
|
||||
vision(fragment.vision)
|
||||
vision { fragment.vision }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,8 +18,8 @@ import space.kscience.dataforge.meta.string
|
||||
import space.kscience.visionforge.html.HtmlFormFragment
|
||||
import space.kscience.visionforge.html.HtmlVisionFragment
|
||||
import space.kscience.visionforge.html.visionFragment
|
||||
import space.kscience.visionforge.three.server.VisionServer
|
||||
import space.kscience.visionforge.three.server.serve
|
||||
import space.kscience.visionforge.server.VisionServer
|
||||
import space.kscience.visionforge.server.serve
|
||||
import space.kscience.visionforge.visionManager
|
||||
|
||||
/**
|
||||
@ -75,7 +75,7 @@ public class VisionForgeForNotebook(override val context: Context) : ContextAwar
|
||||
fragment: HtmlVisionFragment,
|
||||
): String = server?.serveVisionsFromFragment("content[${counter++}]", fragment)
|
||||
?: createHTML().apply {
|
||||
visionFragment(context.visionManager, fragment = fragment)
|
||||
visionFragment(context, fragment = fragment)
|
||||
}.finalize()
|
||||
|
||||
public fun produceHtml(isolated: Boolean? = null, fragment: HtmlVisionFragment): MimeTypedResult =
|
||||
|
@ -29,7 +29,9 @@ internal class GdmlForJupyter : JupyterPluginBase(
|
||||
)
|
||||
|
||||
render<Gdml> { gdmlModel ->
|
||||
handler.produceHtml { vision(gdmlModel.toVision()) }
|
||||
handler.produceHtml {
|
||||
vision { gdmlModel.toVision() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import kotlinx.html.stream.createHTML
|
||||
|
||||
public typealias HtmlFragment = TagConsumer<*>.() -> Unit
|
||||
|
||||
public fun HtmlFragment.render(): String = createHTML().apply(this).finalize()
|
||||
public fun HtmlFragment.renderToString(): String = createHTML().apply(this).finalize()
|
||||
|
||||
public fun TagConsumer<*>.fragment(fragment: HtmlFragment) {
|
||||
fragment()
|
||||
@ -15,3 +15,8 @@ public fun TagConsumer<*>.fragment(fragment: HtmlFragment) {
|
||||
public fun FlowContent.fragment(fragment: HtmlFragment) {
|
||||
fragment(consumer)
|
||||
}
|
||||
|
||||
public operator fun HtmlFragment.plus(other: HtmlFragment): HtmlFragment = {
|
||||
this@plus()
|
||||
other()
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
package space.kscience.visionforge.html
|
||||
|
||||
import kotlinx.html.*
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.context.Global
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.dataforge.names.Name
|
||||
@ -28,7 +30,7 @@ internal const val RENDER_FUNCTION_NAME = "renderAllVisionsById"
|
||||
* @param renderScript if true add rendering script after the fragment
|
||||
*/
|
||||
public fun TagConsumer<*>.visionFragment(
|
||||
manager: VisionManager,
|
||||
context: Context = Global,
|
||||
embedData: Boolean = true,
|
||||
fetchDataUrl: String? = null,
|
||||
fetchUpdatesUrl: String? = null,
|
||||
@ -37,8 +39,8 @@ public fun TagConsumer<*>.visionFragment(
|
||||
fragment: HtmlVisionFragment,
|
||||
): Map<Name, Vision> {
|
||||
val visionMap = HashMap<Name, Vision>()
|
||||
val consumer = object : VisionTagConsumer<Any?>(this@visionFragment, manager, idPrefix) {
|
||||
override fun DIV.renderVision(name: Name, vision: Vision, outputMeta: Meta) {
|
||||
val consumer = object : VisionTagConsumer<Any?>(this@visionFragment, context, idPrefix) {
|
||||
override fun DIV.renderVision(manager: VisionManager, name: Name, vision: Vision, outputMeta: Meta) {
|
||||
visionMap[name] = vision
|
||||
// Toggle update mode
|
||||
|
||||
@ -78,19 +80,19 @@ public fun TagConsumer<*>.visionFragment(
|
||||
}
|
||||
|
||||
public fun FlowContent.visionFragment(
|
||||
manager: VisionManager,
|
||||
context: Context = Global,
|
||||
embedData: Boolean = true,
|
||||
fetchDataUrl: String? = null,
|
||||
fetchUpdatesUrl: String? = null,
|
||||
idPrefix: String? = null,
|
||||
renderSctipt: Boolean = true,
|
||||
renderScript: Boolean = true,
|
||||
fragment: HtmlVisionFragment,
|
||||
): Map<Name, Vision> = consumer.visionFragment(
|
||||
manager,
|
||||
context,
|
||||
embedData,
|
||||
fetchDataUrl,
|
||||
fetchUpdatesUrl,
|
||||
idPrefix,
|
||||
renderSctipt,
|
||||
renderScript,
|
||||
fragment
|
||||
)
|
@ -1,37 +1,52 @@
|
||||
package space.kscience.visionforge.html
|
||||
|
||||
import kotlinx.html.*
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.visionforge.VisionManager
|
||||
|
||||
//data class HeaderContainer
|
||||
import space.kscience.dataforge.context.Context
|
||||
|
||||
public data class Page(
|
||||
public val visionManager: VisionManager,
|
||||
public val title: String,
|
||||
public val headers: Map<String, HtmlFragment>,
|
||||
public val context: Context,
|
||||
public val headers: Map<String, HtmlFragment> = emptyMap(),
|
||||
public val content: HtmlVisionFragment,
|
||||
) {
|
||||
public fun <R> render(root: TagConsumer<R>): R = root.apply {
|
||||
head {
|
||||
meta {
|
||||
charset = "utf-8"
|
||||
}
|
||||
headers.values.forEach {
|
||||
fragment(it)
|
||||
}
|
||||
}
|
||||
title(this@Page.title)
|
||||
}
|
||||
body {
|
||||
visionFragment(visionManager, fragment = content)
|
||||
visionFragment(context, fragment = content)
|
||||
}
|
||||
}.finalize()
|
||||
|
||||
public companion object{
|
||||
/**
|
||||
* Use a script with given [src] as a global header for all pages.
|
||||
*/
|
||||
public fun scriptHeader(src: String, block: SCRIPT.() -> Unit = {}): HtmlFragment = {
|
||||
script {
|
||||
type = "text/javascript"
|
||||
this.src = src
|
||||
block()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use css with given stylesheet link as a global header for all pages.
|
||||
*/
|
||||
public fun styleSheetHeader(href: String, block: LINK.() -> Unit = {}): HtmlFragment = {
|
||||
link {
|
||||
rel = "stylesheet"
|
||||
this.href = href
|
||||
block()
|
||||
}
|
||||
}
|
||||
|
||||
@DFExperimental
|
||||
public fun VisionManager.page(
|
||||
title: String = "VisionForge page",
|
||||
vararg headers: Pair<String, HtmlFragment>,
|
||||
content: HtmlVisionFragment,
|
||||
): Page = Page(this, title, mapOf(*headers), content)
|
||||
public fun title(title:String): HtmlFragment = {
|
||||
title(title)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
package space.kscience.visionforge.html
|
||||
|
||||
import kotlinx.html.*
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.context.PluginFactory
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.MetaSerializer
|
||||
import space.kscience.dataforge.meta.MutableMeta
|
||||
@ -9,9 +11,12 @@ import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.NameToken
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.dataforge.names.parseAsName
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.VisionManager
|
||||
import space.kscience.visionforge.html.VisionTagConsumer.Companion.DEFAULT_VISION_NAME
|
||||
import space.kscience.visionforge.setAsRoot
|
||||
import space.kscience.visionforge.visionManager
|
||||
import kotlin.collections.set
|
||||
|
||||
@DslMarker
|
||||
@ -22,10 +27,25 @@ public annotation class VisionDSL
|
||||
*/
|
||||
@DFExperimental
|
||||
@VisionDSL
|
||||
public class VisionOutput @PublishedApi internal constructor(public val manager: VisionManager) {
|
||||
public class VisionOutput @PublishedApi internal constructor(public val context: Context, public val name: Name?) {
|
||||
public var meta: Meta = Meta.EMPTY
|
||||
|
||||
//TODO expose a way to define required plugins.
|
||||
private val requirements: MutableSet<PluginFactory<*>> = HashSet()
|
||||
|
||||
public fun requirePlugin(factory: PluginFactory<*>) {
|
||||
requirements.add(factory)
|
||||
}
|
||||
|
||||
internal fun buildVisionManager(): VisionManager =
|
||||
if (requirements.all { req -> context.plugins.find(true) { it.tag == req.tag } != null }) {
|
||||
context.visionManager
|
||||
} else {
|
||||
val newContext = context.buildContext(NameToken(DEFAULT_VISION_NAME, name.toString()).asName()) {
|
||||
plugin(VisionManager)
|
||||
requirements.forEach { plugin(it) }
|
||||
}
|
||||
newContext.visionManager
|
||||
}
|
||||
|
||||
public inline fun meta(block: MutableMeta.() -> Unit) {
|
||||
this.meta = Meta(block)
|
||||
@ -36,9 +56,10 @@ public class VisionOutput @PublishedApi internal constructor(public val manager:
|
||||
* Modified [TagConsumer] that allows rendering output fragments and visions in them
|
||||
*/
|
||||
@VisionDSL
|
||||
@OptIn(DFExperimental::class)
|
||||
public abstract class VisionTagConsumer<R>(
|
||||
private val root: TagConsumer<R>,
|
||||
public val manager: VisionManager,
|
||||
public val context: Context,
|
||||
private val idPrefix: String? = null,
|
||||
) : TagConsumer<R> by root {
|
||||
|
||||
@ -46,23 +67,26 @@ public abstract class VisionTagConsumer<R>(
|
||||
|
||||
/**
|
||||
* Render a vision inside the output fragment
|
||||
* @param manager a [VisionManager] to be used in renderer
|
||||
* @param name name of the output container
|
||||
* @param vision an object to be rendered
|
||||
* @param outputMeta optional configuration for the output container
|
||||
*/
|
||||
protected abstract fun DIV.renderVision(name: Name, vision: Vision, outputMeta: Meta)
|
||||
protected abstract fun DIV.renderVision(manager: VisionManager, name: Name, vision: Vision, outputMeta: Meta)
|
||||
|
||||
/**
|
||||
* Create a placeholder for a vision output with optional [Vision] in it
|
||||
* TODO with multi-receivers could be replaced by [VisionTagConsumer, TagConsumer] extension
|
||||
*/
|
||||
public fun <T> TagConsumer<T>.vision(
|
||||
private fun <T> TagConsumer<T>.vision(
|
||||
name: Name,
|
||||
vision: Vision? = null,
|
||||
manager: VisionManager,
|
||||
vision: Vision,
|
||||
outputMeta: Meta = Meta.EMPTY,
|
||||
): T = div {
|
||||
id = resolveId(name)
|
||||
classes = setOf(OUTPUT_CLASS)
|
||||
vision.setAsRoot(manager)
|
||||
attributes[OUTPUT_NAME_ATTRIBUTE] = name.toString()
|
||||
if (!outputMeta.isEmpty()) {
|
||||
//Hard-code output configuration
|
||||
@ -73,9 +97,7 @@ public abstract class VisionTagConsumer<R>(
|
||||
}
|
||||
}
|
||||
}
|
||||
vision?.let {
|
||||
renderVision(name, it, outputMeta)
|
||||
}
|
||||
renderVision(manager, name, vision, outputMeta)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -83,14 +105,14 @@ public abstract class VisionTagConsumer<R>(
|
||||
* TODO replace by multi-receiver
|
||||
*/
|
||||
@OptIn(DFExperimental::class)
|
||||
public inline fun <T> TagConsumer<T>.vision(
|
||||
name: Name,
|
||||
public fun <T> TagConsumer<T>.vision(
|
||||
name: Name? = null,
|
||||
@OptIn(DFExperimental::class) visionProvider: VisionOutput.() -> Vision,
|
||||
): T {
|
||||
val output = VisionOutput(manager)
|
||||
val output = VisionOutput(context, name)
|
||||
val vision = output.visionProvider()
|
||||
vision.setAsRoot(manager)
|
||||
return vision(name, vision, output.meta)
|
||||
val actualName = name ?: NameToken(DEFAULT_VISION_NAME, vision.hashCode().toUInt().toString()).asName()
|
||||
return vision(actualName, output.buildVisionManager(), vision, output.meta)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -98,14 +120,10 @@ public abstract class VisionTagConsumer<R>(
|
||||
*/
|
||||
@OptIn(DFExperimental::class)
|
||||
@VisionDSL
|
||||
public inline fun <T> TagConsumer<T>.vision(
|
||||
name: String = DEFAULT_VISION_NAME,
|
||||
visionProvider: VisionOutput.() -> Vision,
|
||||
): T = vision(Name.parse(name), visionProvider)
|
||||
|
||||
public fun <T> TagConsumer<T>.vision(
|
||||
vision: Vision,
|
||||
): T = vision(NameToken("vision", vision.hashCode().toString()).asName(), vision)
|
||||
name: String?,
|
||||
@OptIn(DFExperimental::class) visionProvider: VisionOutput.() -> Vision,
|
||||
): T = vision(name?.parseAsName(), visionProvider)
|
||||
|
||||
/**
|
||||
* Process the resulting object produced by [TagConsumer]
|
||||
@ -114,9 +132,7 @@ public abstract class VisionTagConsumer<R>(
|
||||
//do nothing by default
|
||||
}
|
||||
|
||||
override fun finalize(): R {
|
||||
return root.finalize().also { processResult(it) }
|
||||
}
|
||||
override fun finalize(): R = root.finalize().also { processResult(it) }
|
||||
|
||||
public companion object {
|
||||
public const val OUTPUT_CLASS: String = "visionforge-output"
|
||||
|
@ -3,7 +3,6 @@ package space.kscience.visionforge.html
|
||||
import kotlinx.html.*
|
||||
import kotlinx.html.stream.createHTML
|
||||
import space.kscience.dataforge.context.Global
|
||||
import space.kscience.dataforge.context.fetch
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.configure
|
||||
import space.kscience.dataforge.meta.set
|
||||
@ -23,8 +22,8 @@ fun FlowContent.renderVisionFragment(
|
||||
fragment: HtmlVisionFragment,
|
||||
): Map<Name, Vision> {
|
||||
val visionMap = HashMap<Name, Vision>()
|
||||
val consumer = object : VisionTagConsumer<Any?>(consumer, Global.fetch(VisionManager), idPrefix) {
|
||||
override fun DIV.renderVision(name: Name, vision: Vision, outputMeta: Meta) {
|
||||
val consumer = object : VisionTagConsumer<Any?>(consumer, Global, idPrefix) {
|
||||
override fun DIV.renderVision(manager: VisionManager, name: Name, vision: Vision, outputMeta: Meta) {
|
||||
visionMap[name] = vision
|
||||
renderer(name, vision, outputMeta)
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import kotlinx.html.link
|
||||
import kotlinx.html.script
|
||||
import kotlinx.html.unsafe
|
||||
import org.slf4j.LoggerFactory
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.visionforge.VisionManager
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
@ -113,10 +112,9 @@ internal fun fileCssHeader(
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a script header, automatically copying file to appropriate location
|
||||
* Make a script header from a resource file, automatically copying file to appropriate location
|
||||
*/
|
||||
@DFExperimental
|
||||
public fun scriptHeader(
|
||||
public fun Page.Companion.importScriptHeader(
|
||||
scriptResource: String,
|
||||
resourceLocation: ResourceLocation,
|
||||
htmlPath: Path? = null,
|
||||
|
@ -390,4 +390,7 @@ public fun SolidGroup.gdml(gdml: Gdml, key: String? = null, transformer: GdmlLoa
|
||||
|
||||
@VisionBuilder
|
||||
@DFExperimental
|
||||
public inline fun VisionOutput.gdml(block: Gdml.() -> Unit): SolidGroup = Gdml(block).toVision()
|
||||
public inline fun VisionOutput.gdml(block: Gdml.() -> Unit): SolidGroup {
|
||||
requirePlugin(Solids)
|
||||
return Gdml(block).toVision()
|
||||
}
|
@ -24,4 +24,7 @@ public fun Plot.asVision(): VisionOfPlotly = VisionOfPlotly(this)
|
||||
@DFExperimental
|
||||
public inline fun VisionOutput.plotly(
|
||||
block: Plot.() -> Unit,
|
||||
): VisionOfPlotly = VisionOfPlotly(Plotly.plot(block))
|
||||
): VisionOfPlotly {
|
||||
requirePlugin(PlotlyPlugin)
|
||||
return VisionOfPlotly(Plotly.plot(block))
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package space.kscience.visionforge.three.server
|
||||
package space.kscience.visionforge.server
|
||||
|
||||
import io.ktor.application.*
|
||||
import io.ktor.features.CORS
|
||||
@ -14,9 +14,9 @@ import io.ktor.routing.*
|
||||
import io.ktor.server.cio.CIO
|
||||
import io.ktor.server.engine.ApplicationEngine
|
||||
import io.ktor.server.engine.embeddedServer
|
||||
import io.ktor.util.getOrFail
|
||||
import io.ktor.websocket.WebSockets
|
||||
import io.ktor.websocket.webSocket
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.channels.consumeEach
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.launch
|
||||
@ -32,9 +32,8 @@ import space.kscience.visionforge.VisionManager
|
||||
import space.kscience.visionforge.flowChanges
|
||||
import space.kscience.visionforge.html.HtmlFragment
|
||||
import space.kscience.visionforge.html.HtmlVisionFragment
|
||||
import space.kscience.visionforge.html.fragment
|
||||
import space.kscience.visionforge.html.visionFragment
|
||||
import space.kscience.visionforge.three.server.VisionServer.Companion.DEFAULT_PAGE
|
||||
import space.kscience.visionforge.server.VisionServer.Companion.DEFAULT_PAGE
|
||||
import java.awt.Desktop
|
||||
import java.net.URI
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
@ -48,7 +47,10 @@ public class VisionServer internal constructor(
|
||||
private val visionManager: VisionManager,
|
||||
private val serverUrl: Url,
|
||||
private val root: Route,
|
||||
) : Configurable, CoroutineScope by root.application {
|
||||
) : Configurable {
|
||||
|
||||
public val application: Application get() = root.application
|
||||
|
||||
override val meta: ObservableMutableMeta = MutableMeta()
|
||||
|
||||
/**
|
||||
@ -76,22 +78,10 @@ public class VisionServer internal constructor(
|
||||
*/
|
||||
public var dataUpdate: Boolean by meta.boolean(true, Name.parse("data.update"))
|
||||
|
||||
/**
|
||||
* a list of headers that should be applied to all pages
|
||||
*/
|
||||
private val globalHeaders: ArrayList<HtmlFragment> = ArrayList()
|
||||
|
||||
/**
|
||||
* Add a header to all pages produced by this server
|
||||
*/
|
||||
public fun header(block: TagConsumer<*>.() -> Unit) {
|
||||
globalHeaders.add(block)
|
||||
}
|
||||
|
||||
private fun HTML.visionPage(
|
||||
title: String,
|
||||
pagePath: String,
|
||||
headers: List<HtmlFragment>,
|
||||
header: HtmlFragment,
|
||||
visionFragment: HtmlVisionFragment,
|
||||
): Map<Name, Vision> {
|
||||
var visionMap: Map<Name, Vision>? = null
|
||||
@ -99,16 +89,14 @@ public class VisionServer internal constructor(
|
||||
head {
|
||||
meta {
|
||||
charset = "utf-8"
|
||||
(globalHeaders + headers).forEach {
|
||||
fragment(it)
|
||||
}
|
||||
header()
|
||||
}
|
||||
title(title)
|
||||
}
|
||||
body {
|
||||
//Load the fragment and remember all loaded visions
|
||||
visionMap = visionFragment(
|
||||
manager = visionManager,
|
||||
context = visionManager.context,
|
||||
embedData = true,
|
||||
fetchUpdatesUrl = "$serverUrl$pagePath/ws",
|
||||
fragment = visionFragment
|
||||
@ -127,9 +115,7 @@ public class VisionServer internal constructor(
|
||||
|
||||
//Update websocket
|
||||
webSocket("ws") {
|
||||
val name: String = call.request.queryParameters["name"]
|
||||
?: error("Vision name is not defined in parameters")
|
||||
|
||||
val name: String = call.request.queryParameters.getOrFail("name")
|
||||
application.log.debug("Opened server socket for $name")
|
||||
val vision: Vision = visions[Name.parse(name)] ?: error("Plot with id='$name' not registered")
|
||||
|
||||
@ -158,8 +144,7 @@ public class VisionServer internal constructor(
|
||||
}
|
||||
//Plots in their json representation
|
||||
get("data") {
|
||||
val name: String = call.request.queryParameters["name"]
|
||||
?: error("Vision name is not defined in parameters")
|
||||
val name: String = call.request.queryParameters.getOrFail("name")
|
||||
|
||||
val vision: Vision? = visions[Name.parse(name)]
|
||||
if (vision == null) {
|
||||
@ -178,7 +163,7 @@ public class VisionServer internal constructor(
|
||||
/**
|
||||
* Serve visions in a given [route] without providing a page template
|
||||
*/
|
||||
public fun serveVisions(route: String, visions: Map<Name, Vision>): Unit {
|
||||
public fun serveVisions(route: String, visions: Map<Name, Vision>) {
|
||||
root.route(route) {
|
||||
serveVisions(this, visions)
|
||||
}
|
||||
@ -192,7 +177,7 @@ public class VisionServer internal constructor(
|
||||
fragment: HtmlVisionFragment,
|
||||
): String = createHTML().apply {
|
||||
val visions = visionFragment(
|
||||
visionManager,
|
||||
visionManager.context,
|
||||
embedData = true,
|
||||
fetchUpdatesUrl = "$serverUrl$route/ws",
|
||||
renderScript = true,
|
||||
@ -203,12 +188,11 @@ public class VisionServer internal constructor(
|
||||
|
||||
/**
|
||||
* Serve a page, potentially containing any number of visions at a given [pagePath] with given [headers].
|
||||
*
|
||||
*/
|
||||
public fun page(
|
||||
pagePath: String = DEFAULT_PAGE,
|
||||
title: String = "VisionForge server page '$pagePath'",
|
||||
headers: List<HtmlFragment> = emptyList(),
|
||||
header: HtmlFragment = {},
|
||||
visionFragment: HtmlVisionFragment,
|
||||
) {
|
||||
val visions = HashMap<Name, Vision>()
|
||||
@ -216,7 +200,7 @@ public class VisionServer internal constructor(
|
||||
val cachedHtml: String? = if (cacheFragments) {
|
||||
//Create and cache page html and map of visions
|
||||
createHTML(true).html {
|
||||
visions.putAll(visionPage(title, pagePath, headers, visionFragment))
|
||||
visions.putAll(visionPage(title, pagePath, header, visionFragment))
|
||||
}
|
||||
} else {
|
||||
null
|
||||
@ -230,7 +214,7 @@ public class VisionServer internal constructor(
|
||||
//re-create html and vision list on each call
|
||||
call.respondHtml {
|
||||
visions.clear()
|
||||
visions.putAll(visionPage(title, pagePath, headers, visionFragment))
|
||||
visions.putAll(visionPage(title, pagePath, header, visionFragment))
|
||||
}
|
||||
} else {
|
||||
//Use cached html
|
||||
@ -249,32 +233,6 @@ public class VisionServer internal constructor(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use a script with given [src] as a global header for all pages.
|
||||
*/
|
||||
public inline fun VisionServer.useScript(src: String, crossinline block: SCRIPT.() -> Unit = {}) {
|
||||
header {
|
||||
script {
|
||||
type = "text/javascript"
|
||||
this.src = src
|
||||
block()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use css with given stylesheet link as a global header for all pages.
|
||||
*/
|
||||
public inline fun VisionServer.useCss(href: String, crossinline block: LINK.() -> Unit = {}) {
|
||||
header {
|
||||
link {
|
||||
rel = "stylesheet"
|
||||
this.href = href
|
||||
block()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach VisionForge server application to given server
|
||||
*/
|
@ -69,5 +69,7 @@ public class Solids(meta: Meta) : VisionPlugin(meta) {
|
||||
|
||||
@VisionBuilder
|
||||
@DFExperimental
|
||||
public inline fun VisionOutput.solid(block: SolidGroup.() -> Unit): SolidGroup =
|
||||
SolidGroup().apply(block)
|
||||
public inline fun VisionOutput.solid(block: SolidGroup.() -> Unit): SolidGroup {
|
||||
requirePlugin(Solids)
|
||||
return SolidGroup().apply(block)
|
||||
}
|
||||
|
@ -87,7 +87,10 @@ public fun Table<Number>.toVision(): VisionOfTable = toVision { (it ?: Double.Na
|
||||
public inline fun VisionOutput.table(
|
||||
vararg headers: ColumnHeader<Value>,
|
||||
block: MutableRowTable<Value>.() -> Unit,
|
||||
): VisionOfTable = RowTable(*headers, block = block).toVision()
|
||||
): VisionOfTable {
|
||||
requirePlugin(TableVisionPlugin)
|
||||
return RowTable(*headers, block = block).toVision()
|
||||
}
|
||||
|
||||
@DFExperimental
|
||||
public inline fun VisionOutput.columnTable(
|
||||
@ -99,6 +102,7 @@ public inline fun VisionOutput.columnTable(
|
||||
public fun VisionOutput.columnTable(
|
||||
vararg dataAndHeaders: Pair<ColumnHeader<Value>, List<Any?>>,
|
||||
): VisionOfTable {
|
||||
requirePlugin(TableVisionPlugin)
|
||||
val columns = dataAndHeaders.map { (header, data) ->
|
||||
ListColumn(header, data.map { Value.of(it) })
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package space.kscience.visionforge.three.server
|
||||
package space.kscience.visionforge.three
|
||||
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.visionforge.runVisionClient
|
@ -1,30 +0,0 @@
|
||||
package space.kscience.visionforge.three.server
|
||||
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.visionforge.VisionManager
|
||||
import space.kscience.visionforge.html.HtmlVisionFragment
|
||||
import space.kscience.visionforge.html.ResourceLocation
|
||||
import space.kscience.visionforge.html.page
|
||||
import space.kscience.visionforge.html.scriptHeader
|
||||
import space.kscience.visionforge.makeFile
|
||||
import java.awt.Desktop
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
public fun VisionServer.useThreeJs(): Unit {
|
||||
useScript("js/visionforge-three.js")
|
||||
}
|
||||
|
||||
@DFExperimental
|
||||
public fun VisionManager.makeThreeJsFile(
|
||||
content: HtmlVisionFragment,
|
||||
path: Path? = null,
|
||||
title: String = "VisionForge page",
|
||||
resourceLocation: ResourceLocation = ResourceLocation.SYSTEM,
|
||||
show: Boolean = true,
|
||||
): Unit {
|
||||
val actualPath = page(title, content = content).makeFile(path) { actualPath ->
|
||||
mapOf("threeJs" to scriptHeader("js/visionforge-three.js", resourceLocation, actualPath))
|
||||
}
|
||||
if (show) Desktop.getDesktop().browse(actualPath.toFile().toURI())
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package space.kscience.visionforge.three
|
||||
|
||||
import space.kscience.dataforge.context.Global
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.visionforge.html.*
|
||||
import space.kscience.visionforge.makeFile
|
||||
import java.awt.Desktop
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
public val Page.Companion.threeJsHeader: HtmlFragment get() = scriptHeader("js/visionforge-three.js")
|
||||
|
||||
|
||||
@DFExperimental
|
||||
public fun makeThreeJsFile(
|
||||
path: Path? = null,
|
||||
title: String = "VisionForge page",
|
||||
resourceLocation: ResourceLocation = ResourceLocation.SYSTEM,
|
||||
show: Boolean = true,
|
||||
content: HtmlVisionFragment,
|
||||
): Unit {
|
||||
val actualPath = Page(Global, content = content).makeFile(path) { actualPath ->
|
||||
mapOf(
|
||||
"title" to Page.title(title),
|
||||
"threeJs" to Page.importScriptHeader("js/visionforge-three.js", resourceLocation, actualPath)
|
||||
)
|
||||
}
|
||||
if (show) Desktop.getDesktop().browse(actualPath.toFile().toURI())
|
||||
}
|
Loading…
Reference in New Issue
Block a user