From 9745a5887355e03bbbe0c12e1e48a4091f02a350 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 20 Jan 2022 11:13:17 +0300 Subject: [PATCH] Refactor page headers and fragment requirements --- .../kotlin/VisionForgePlayGroundForJupyter.kt | 4 +- .../src/jvmMain/kotlin/allThingsDemo.kt | 276 +++++++++--------- .../src/jvmMain/kotlin/formServer.kt | 14 +- .../src/jvmMain/kotlin/gdmlCubes.kt | 14 +- .../src/jvmMain/kotlin/gdmlCurve.kt | 12 +- .../playground/src/jvmMain/kotlin/gdmlIaxo.kt | 12 +- .../src/jvmMain/kotlin/plotlyVision.kt | 19 +- .../src/jvmMain/kotlin/randomSpheres.kt | 41 ++- .../src/jvmMain/kotlin/rootParser.kt | 9 +- .../src/jvmMain/kotlin/serverExtensions.kt | 27 +- .../src/jvmMain/kotlin/simpleCube.kt | 24 +- demo/playground/src/jvmMain/kotlin/tables.kt | 7 +- .../main/kotlin/ru/mipt/npm/sat/satServer.kt | 22 +- .../visionforge/solid/demo/ThreeDemoApp.kt | 4 +- .../src/jvmMain/kotlin/JupyterPluginBase.kt | 4 +- .../jvmMain/kotlin/VisionForgeForNotebook.kt | 6 +- .../src/jvmMain/kotlin/GdmlForJupyter.kt | 4 +- .../kscience/visionforge/html/HtmlFragment.kt | 7 +- .../visionforge/html/HtmlVisionRenderer.kt | 16 +- .../space/kscience/visionforge/html/Page.kt | 53 ++-- .../visionforge/html/VisionTagConsumer.kt | 64 ++-- .../kscience/visionforge/html/HtmlTagTest.kt | 5 +- .../kscience/visionforge/html/headers.kt | 6 +- .../kscience/visionforge/gdml/gdmlLoader.kt | 5 +- .../visionforge/plotly/VisionOfPlotly.kt | 5 +- .../{three => }/server/VisionServer.kt | 76 ++--- .../kscience/visionforge/solid/Solids.kt | 6 +- .../visionforge/tables/VisionOfTable.kt | 6 +- .../visionforge/three/{server => }/jsMain.kt | 2 +- .../three/server/serverExtensions.kt | 30 -- .../visionforge/three/serverExtensions.kt | 29 ++ 31 files changed, 374 insertions(+), 435 deletions(-) rename visionforge-server/src/main/kotlin/space/kscience/visionforge/{three => }/server/VisionServer.kt (82%) rename visionforge-threejs/visionforge-threejs-server/src/jsMain/kotlin/space/kscience/visionforge/three/{server => }/jsMain.kt (83%) delete mode 100644 visionforge-threejs/visionforge-threejs-server/src/jvmMain/kotlin/space/kscience/visionforge/three/server/serverExtensions.kt create mode 100644 visionforge-threejs/visionforge-threejs-server/src/jvmMain/kotlin/space/kscience/visionforge/three/serverExtensions.kt diff --git a/demo/playground/src/jvmMain/kotlin/VisionForgePlayGroundForJupyter.kt b/demo/playground/src/jvmMain/kotlin/VisionForgePlayGroundForJupyter.kt index 6cbe311d..651b580d 100644 --- a/demo/playground/src/jvmMain/kotlin/VisionForgePlayGroundForJupyter.kt +++ b/demo/playground/src/jvmMain/kotlin/VisionForgePlayGroundForJupyter.kt @@ -36,13 +36,13 @@ internal class VisionForgePlayGroundForJupyter : JupyterPluginBase( render { gdmlModel -> handler.produceHtml { - vision(gdmlModel.toVision()) + vision { gdmlModel.toVision() } } } render { plot -> handler.produceHtml { - vision(plot.asVision()) + vision { plot.asVision() } } } } diff --git a/demo/playground/src/jvmMain/kotlin/allThingsDemo.kt b/demo/playground/src/jvmMain/kotlin/allThingsDemo.kt index 9c68af8b..23b73af4 100644 --- a/demo/playground/src/jvmMain/kotlin/allThingsDemo.kt +++ b/demo/playground/src/jvmMain/kotlin/allThingsDemo.kt @@ -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,177 +9,166 @@ 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) +fun main() = makeVisionFile( + Paths.get("VisionForgeDemo.html"), + resourceLocation = ResourceLocation.EMBED +) { + markdown { + //language=markdown + """ + # VisionForge + + This is a demo for current VisionForge features. This text is written in [MarkDown](https://github.com/JetBrains/markdown) + """.trimIndent() } - context.makeVisionFile( - Paths.get("VisionForgeDemo.html"), - resourceLocation = ResourceLocation.EMBED - ) { - markdown { - //language=markdown - """ - # VisionForge - - This is a demo for current VisionForge features. This text is written in [MarkDown](https://github.com/JetBrains/markdown) - """.trimIndent() - } - - h2 { +"3D visualization with Three-js" } - vision("3D") { - solid { - box(100, 100, 100, name = "aBox"){ - z = 50.0 - } + h2 { +"3D visualization with Three-js" } + vision("3D") { + solid { + box(100, 100, 100, name = "aBox"){ + z = 50.0 } } + } - h2 { +"Interactive plots with Plotly" } - vision("plot") { - plotly { - scatter { - x(1, 2, 3, 4) - y(10, 15, 13, 17) - mode = ScatterMode.markers - name = "Team A" - text("A-1", "A-2", "A-3", "A-4", "A-5") - textposition = TextPosition.`top center` - textfont { - family = "Raleway, sans-serif" - } - marker { size = 12 } + h2 { +"Interactive plots with Plotly" } + vision("plot") { + plotly { + scatter { + x(1, 2, 3, 4) + y(10, 15, 13, 17) + mode = ScatterMode.markers + name = "Team A" + text("A-1", "A-2", "A-3", "A-4", "A-5") + textposition = TextPosition.`top center` + textfont { + family = "Raleway, sans-serif" } + marker { size = 12 } + } - scatter { - x(2, 3, 4, 5) - y(10, 15, 13, 17) - mode = ScatterMode.lines - name = "Team B" - text("B-a", "B-b", "B-c", "B-d", "B-e") - textposition = TextPosition.`bottom center` - textfont { - family = "Times New Roman" - } - marker { size = 12 } + scatter { + x(2, 3, 4, 5) + y(10, 15, 13, 17) + mode = ScatterMode.lines + name = "Team B" + text("B-a", "B-b", "B-c", "B-d", "B-e") + textposition = TextPosition.`bottom center` + textfont { + family = "Times New Roman" } + marker { size = 12 } + } - layout { - title = "Data Labels Hover" - xaxis { - range(0.75..5.25) - } - legend { - y = 0.5 - font { - family = "Arial, sans-serif" - size = 20 - color("grey") - } + layout { + title = "Data Labels Hover" + xaxis { + range(0.75..5.25) + } + legend { + y = 0.5 + font { + family = "Arial, sans-serif" + size = 20 + color("grey") } } } } - h2 { +"Interactive tables with Tabulator" } - vision("table") { - val x by ColumnHeader.value(ValueType.NUMBER) - val y by ColumnHeader.value(ValueType.NUMBER) - columnTable( - x to listOf(2, 3, 4, 5), - y to listOf(10, 15, 13, 17) - ) - } - markdown { - //language=markdown - """ - ## The code for everything above - ```kotlin - markdown { - //language=markdown - ""${'"'} - # VisionForge - - This is a demo for current VisionForge features. This text is written in [MarkDown](https://github.com/JetBrains/markdown) - ""${'"'}.trimIndent() + } + h2 { +"Interactive tables with Tabulator" } + vision("table") { + val x by ColumnHeader.value(ValueType.NUMBER) + val y by ColumnHeader.value(ValueType.NUMBER) + columnTable( + x to listOf(2, 3, 4, 5), + y to listOf(10, 15, 13, 17) + ) + } + markdown { + //language=markdown + """ + ## The code for everything above + ```kotlin + markdown { + //language=markdown + ""${'"'} + # VisionForge + + This is a demo for current VisionForge features. This text is written in [MarkDown](https://github.com/JetBrains/markdown) + ""${'"'}.trimIndent() + } + + h2 { +"3D visualization with Three-js" } + vision("3D") { + solid { + box(100, 100, 100, name = "aBox") } - - h2 { +"3D visualization with Three-js" } - vision("3D") { - solid { - box(100, 100, 100, name = "aBox") + } + + h2 { +"Interactive plots with Plotly" } + vision("plot") { + plotly { + scatter { + x(1, 2, 3, 4) + y(10, 15, 13, 17) + mode = ScatterMode.markers + name = "Team A" + text("A-1", "A-2", "A-3", "A-4", "A-5") + textposition = TextPosition.`top center` + textfont { + family = "Raleway, sans-serif" + } + marker { size = 12 } } - } - - h2 { +"Interactive plots with Plotly" } - vision("plot") { - plotly { - scatter { - x(1, 2, 3, 4) - y(10, 15, 13, 17) - mode = ScatterMode.markers - name = "Team A" - text("A-1", "A-2", "A-3", "A-4", "A-5") - textposition = TextPosition.`top center` - textfont { - family = "Raleway, sans-serif" - } - marker { size = 12 } + + scatter { + x(2, 3, 4, 5) + y(10, 15, 13, 17) + mode = ScatterMode.lines + name = "Team B" + text("B-a", "B-b", "B-c", "B-d", "B-e") + textposition = TextPosition.`bottom center` + textfont { + family = "Times New Roman" } - - scatter { - x(2, 3, 4, 5) - y(10, 15, 13, 17) - mode = ScatterMode.lines - name = "Team B" - text("B-a", "B-b", "B-c", "B-d", "B-e") - textposition = TextPosition.`bottom center` - textfont { - family = "Times New Roman" - } - marker { size = 12 } + marker { size = 12 } + } + + layout { + title = "Data Labels Hover" + xaxis { + range(0.75..5.25) } - - layout { - title = "Data Labels Hover" - xaxis { - range(0.75..5.25) - } - legend { - y = 0.5 - font { - family = "Arial, sans-serif" - size = 20 - color("grey") - } + legend { + y = 0.5 + font { + family = "Arial, sans-serif" + size = 20 + color("grey") } } } } - h2 { +"Interactive tables with Tabulator" } - vision("table") { - val x by ColumnHeader.value(ValueType.NUMBER) - val y by ColumnHeader.value(ValueType.NUMBER) - columnTable( - x to listOf(2, 3, 4, 5), - y to listOf(10, 15, 13, 17) - ) - } - ``` - """.trimIndent() - } + } + h2 { +"Interactive tables with Tabulator" } + vision("table") { + val x by ColumnHeader.value(ValueType.NUMBER) + val y by ColumnHeader.value(ValueType.NUMBER) + columnTable( + x to listOf(2, 3, 4, 5), + y to listOf(10, 15, 13, 17) + ) + } + ``` + """.trimIndent() } } \ No newline at end of file diff --git a/demo/playground/src/jvmMain/kotlin/formServer.kt b/demo/playground/src/jvmMain/kotlin/formServer.kt index c5276554..7397b6b6 100644 --- a/demo/playground/src/jvmMain/kotlin/formServer.kt +++ b/demo/playground/src/jvmMain/kotlin/formServer.kt @@ -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) } diff --git a/demo/playground/src/jvmMain/kotlin/gdmlCubes.kt b/demo/playground/src/jvmMain/kotlin/gdmlCubes.kt index 9de22003..d5d483bc 100644 --- a/demo/playground/src/jvmMain/kotlin/gdmlCubes.kt +++ b/demo/playground/src/jvmMain/kotlin/gdmlCubes.kt @@ -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){ - vision("canvas") { - GdmlShowCase.cubes().toVision() - } +fun main() = makeVisionFile(resourceLocation = ResourceLocation.SYSTEM){ + vision("canvas") { + requirePlugin(Solids) + GdmlShowCase.cubes().toVision() } } \ No newline at end of file diff --git a/demo/playground/src/jvmMain/kotlin/gdmlCurve.kt b/demo/playground/src/jvmMain/kotlin/gdmlCurve.kt index 1a646d97..81facd71 100644 --- a/demo/playground/src/jvmMain/kotlin/gdmlCurve.kt +++ b/demo/playground/src/jvmMain/kotlin/gdmlCurve.kt @@ -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 @@ -240,5 +235,4 @@ fun main() { } } } - } -} \ No newline at end of file + } \ No newline at end of file diff --git a/demo/playground/src/jvmMain/kotlin/gdmlIaxo.kt b/demo/playground/src/jvmMain/kotlin/gdmlIaxo.kt index a9070af7..6ee718a2 100644 --- a/demo/playground/src/jvmMain/kotlin/gdmlIaxo.kt +++ b/demo/playground/src/jvmMain/kotlin/gdmlIaxo.kt @@ -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() } } \ No newline at end of file diff --git a/demo/playground/src/jvmMain/kotlin/plotlyVision.kt b/demo/playground/src/jvmMain/kotlin/plotlyVision.kt index 4575b952..4b91c352 100644 --- a/demo/playground/src/jvmMain/kotlin/plotlyVision.kt +++ b/demo/playground/src/jvmMain/kotlin/plotlyVision.kt @@ -1,22 +1,15 @@ 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){ - vision { - plotly { - scatter { - x(1, 2, 3) - y(5, 8, 7) - } +fun main() = makeVisionFile(resourceLocation = ResourceLocation.SYSTEM) { + vision { + plotly { + scatter { + x(1, 2, 3) + y(5, 8, 7) } } } diff --git a/demo/playground/src/jvmMain/kotlin/randomSpheres.kt b/demo/playground/src/jvmMain/kotlin/randomSpheres.kt index 0185bdc8..2d9a5f05 100644 --- a/demo/playground/src/jvmMain/kotlin/randomSpheres.kt +++ b/demo/playground/src/jvmMain/kotlin/randomSpheres.kt @@ -2,37 +2,30 @@ 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( - Paths.get("randomSpheres.html"), - resourceLocation = ResourceLocation.SYSTEM - ) { - h1 { +"Happy new year!" } - div { - vision { - solid { - repeat(100) { - sphere(5, name = "sphere[$it]") { - x = random.nextDouble(-300.0, 300.0) - y = random.nextDouble(-300.0, 300.0) - z = random.nextDouble(-300.0, 300.0) - material { - color(random.nextInt()) - } - detail = 16 +fun main() = makeVisionFile( + Paths.get("randomSpheres.html"), + resourceLocation = ResourceLocation.SYSTEM +) { + h1 { +"Happy new year!" } + div { + vision { + solid { + repeat(100) { + sphere(5, name = "sphere[$it]") { + x = random.nextDouble(-300.0, 300.0) + y = random.nextDouble(-300.0, 300.0) + z = random.nextDouble(-300.0, 300.0) + material { + color(random.nextInt()) } + detail = 16 } } } diff --git a/demo/playground/src/jvmMain/kotlin/rootParser.kt b/demo/playground/src/jvmMain/kotlin/rootParser.kt index afe8528f..184557da 100644 --- a/demo/playground/src/jvmMain/kotlin/rootParser.kt +++ b/demo/playground/src/jvmMain/kotlin/rootParser.kt @@ -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 = 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 } } diff --git a/demo/playground/src/jvmMain/kotlin/serverExtensions.kt b/demo/playground/src/jvmMain/kotlin/serverExtensions.kt index 62d318ad..e8be7112 100644 --- a/demo/playground/src/jvmMain/kotlin/serverExtensions.kt +++ b/demo/playground/src/jvmMain/kotlin/serverExtensions.kt @@ -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()) diff --git a/demo/playground/src/jvmMain/kotlin/simpleCube.kt b/demo/playground/src/jvmMain/kotlin/simpleCube.kt index f804228b..e1fc91eb 100644 --- a/demo/playground/src/jvmMain/kotlin/simpleCube.kt +++ b/demo/playground/src/jvmMain/kotlin/simpleCube.kt @@ -1,21 +1,17 @@ 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){ - vision("canvas") { - solid { - box(100, 100, 100) - material { - emissiveColor("red") - } +fun main() = makeVisionFile(resourceLocation = ResourceLocation.SYSTEM) { + vision("canvas") { + solid { + box(100, 100, 100) + material { + emissiveColor("red") } } } diff --git a/demo/playground/src/jvmMain/kotlin/tables.kt b/demo/playground/src/jvmMain/kotlin/tables.kt index c386ffda..46cad89d 100644 --- a/demo/playground/src/jvmMain/kotlin/tables.kt +++ b/demo/playground/src/jvmMain/kotlin/tables.kt @@ -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) { diff --git a/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt b/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt index 93f2ec7c..fbacc5b1 100644 --- a/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt +++ b/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt @@ -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) diff --git a/demo/solid-showcase/src/jsMain/kotlin/space/kscience/visionforge/solid/demo/ThreeDemoApp.kt b/demo/solid-showcase/src/jsMain/kotlin/space/kscience/visionforge/solid/demo/ThreeDemoApp.kt index db6ebbff..eb27f4d6 100644 --- a/demo/solid-showcase/src/jsMain/kotlin/space/kscience/visionforge/solid/demo/ThreeDemoApp.kt +++ b/demo/solid-showcase/src/jsMain/kotlin/space/kscience/visionforge/solid/demo/ThreeDemoApp.kt @@ -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 -> diff --git a/jupyter/src/jvmMain/kotlin/JupyterPluginBase.kt b/jupyter/src/jvmMain/kotlin/JupyterPluginBase.kt index 4eb4c9f7..2fbe51c8 100644 --- a/jupyter/src/jvmMain/kotlin/JupyterPluginBase.kt +++ b/jupyter/src/jvmMain/kotlin/JupyterPluginBase.kt @@ -44,7 +44,7 @@ public abstract class JupyterPluginBase(final override val context: Context) : J render { 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 } } } diff --git a/jupyter/src/jvmMain/kotlin/VisionForgeForNotebook.kt b/jupyter/src/jvmMain/kotlin/VisionForgeForNotebook.kt index 8b17bc82..a91733ce 100644 --- a/jupyter/src/jvmMain/kotlin/VisionForgeForNotebook.kt +++ b/jupyter/src/jvmMain/kotlin/VisionForgeForNotebook.kt @@ -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 = diff --git a/jupyter/visionforge-jupyter-gdml/src/jvmMain/kotlin/GdmlForJupyter.kt b/jupyter/visionforge-jupyter-gdml/src/jvmMain/kotlin/GdmlForJupyter.kt index 9dc95d6d..0a112ba2 100644 --- a/jupyter/visionforge-jupyter-gdml/src/jvmMain/kotlin/GdmlForJupyter.kt +++ b/jupyter/visionforge-jupyter-gdml/src/jvmMain/kotlin/GdmlForJupyter.kt @@ -29,7 +29,9 @@ internal class GdmlForJupyter : JupyterPluginBase( ) render { gdmlModel -> - handler.produceHtml { vision(gdmlModel.toVision()) } + handler.produceHtml { + vision { gdmlModel.toVision() } + } } } } diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/HtmlFragment.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/HtmlFragment.kt index 343a29ce..b733e6a4 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/HtmlFragment.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/HtmlFragment.kt @@ -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() @@ -14,4 +14,9 @@ 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() } \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/HtmlVisionRenderer.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/HtmlVisionRenderer.kt index 6ec98684..f5abdf42 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/HtmlVisionRenderer.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/HtmlVisionRenderer.kt @@ -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 { val visionMap = HashMap() - val consumer = object : VisionTagConsumer(this@visionFragment, manager, idPrefix) { - override fun DIV.renderVision(name: Name, vision: Vision, outputMeta: Meta) { + val consumer = object : VisionTagConsumer(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 = consumer.visionFragment( - manager, + context, embedData, fetchDataUrl, fetchUpdatesUrl, idPrefix, - renderSctipt, + renderScript, fragment ) \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/Page.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/Page.kt index d575208e..bdd5e417 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/Page.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/Page.kt @@ -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, + public val context: Context, + public val headers: Map = emptyMap(), public val content: HtmlVisionFragment, ) { public fun render(root: TagConsumer): R = root.apply { head { meta { charset = "utf-8" - headers.values.forEach { - fragment(it) - } } - title(this@Page.title) + headers.values.forEach { + fragment(it) + } } 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() + } + } -@DFExperimental -public fun VisionManager.page( - title: String = "VisionForge page", - vararg headers: Pair, - content: HtmlVisionFragment, -): Page = Page(this, title, mapOf(*headers), content) \ No newline at end of file + /** + * 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() + } + } + + public fun title(title:String): HtmlFragment = { + title(title) + } + } +} \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/VisionTagConsumer.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/VisionTagConsumer.kt index 7a17f0f5..afdd686f 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/VisionTagConsumer.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/VisionTagConsumer.kt @@ -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> = 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( private val root: TagConsumer, - public val manager: VisionManager, + public val context: Context, private val idPrefix: String? = null, ) : TagConsumer by root { @@ -46,23 +67,26 @@ public abstract class VisionTagConsumer( /** * 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 TagConsumer.vision( + private fun TagConsumer.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( } } } - vision?.let { - renderVision(name, it, outputMeta) - } + renderVision(manager, name, vision, outputMeta) } /** @@ -83,14 +105,14 @@ public abstract class VisionTagConsumer( * TODO replace by multi-receiver */ @OptIn(DFExperimental::class) - public inline fun TagConsumer.vision( - name: Name, + public fun TagConsumer.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( */ @OptIn(DFExperimental::class) @VisionDSL - public inline fun TagConsumer.vision( - name: String = DEFAULT_VISION_NAME, - visionProvider: VisionOutput.() -> Vision, - ): T = vision(Name.parse(name), visionProvider) - public fun TagConsumer.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( //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" diff --git a/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/html/HtmlTagTest.kt b/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/html/HtmlTagTest.kt index d6cd8dd1..42a9ba1f 100644 --- a/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/html/HtmlTagTest.kt +++ b/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/html/HtmlTagTest.kt @@ -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 { val visionMap = HashMap() - val consumer = object : VisionTagConsumer(consumer, Global.fetch(VisionManager), idPrefix) { - override fun DIV.renderVision(name: Name, vision: Vision, outputMeta: Meta) { + val consumer = object : VisionTagConsumer(consumer, Global, idPrefix) { + override fun DIV.renderVision(manager: VisionManager, name: Name, vision: Vision, outputMeta: Meta) { visionMap[name] = vision renderer(name, vision, outputMeta) } diff --git a/visionforge-core/src/jvmMain/kotlin/space/kscience/visionforge/html/headers.kt b/visionforge-core/src/jvmMain/kotlin/space/kscience/visionforge/html/headers.kt index 9837c60a..d8be2a39 100644 --- a/visionforge-core/src/jvmMain/kotlin/space/kscience/visionforge/html/headers.kt +++ b/visionforge-core/src/jvmMain/kotlin/space/kscience/visionforge/html/headers.kt @@ -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, diff --git a/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/gdmlLoader.kt b/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/gdmlLoader.kt index d1b89c9e..f80a5511 100644 --- a/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/gdmlLoader.kt +++ b/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/gdmlLoader.kt @@ -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() \ No newline at end of file +public inline fun VisionOutput.gdml(block: Gdml.() -> Unit): SolidGroup { + requirePlugin(Solids) + return Gdml(block).toVision() +} \ No newline at end of file diff --git a/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt b/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt index eaa94573..ebb4773c 100644 --- a/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt +++ b/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt @@ -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)) \ No newline at end of file +): VisionOfPlotly { + requirePlugin(PlotlyPlugin) + return VisionOfPlotly(Plotly.plot(block)) +} \ No newline at end of file diff --git a/visionforge-server/src/main/kotlin/space/kscience/visionforge/three/server/VisionServer.kt b/visionforge-server/src/main/kotlin/space/kscience/visionforge/server/VisionServer.kt similarity index 82% rename from visionforge-server/src/main/kotlin/space/kscience/visionforge/three/server/VisionServer.kt rename to visionforge-server/src/main/kotlin/space/kscience/visionforge/server/VisionServer.kt index b5b78ba0..2abb6efb 100644 --- a/visionforge-server/src/main/kotlin/space/kscience/visionforge/three/server/VisionServer.kt +++ b/visionforge-server/src/main/kotlin/space/kscience/visionforge/server/VisionServer.kt @@ -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 = 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, + header: HtmlFragment, visionFragment: HtmlVisionFragment, ): Map { var visionMap: Map? = 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): Unit { + public fun serveVisions(route: String, visions: Map) { 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 = emptyList(), + header: HtmlFragment = {}, visionFragment: HtmlVisionFragment, ) { val visions = HashMap() @@ -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 */ diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solids.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solids.kt index c5b9b04b..12a22ab6 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solids.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solids.kt @@ -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) +} diff --git a/visionforge-tables/src/commonMain/kotlin/space/kscience/visionforge/tables/VisionOfTable.kt b/visionforge-tables/src/commonMain/kotlin/space/kscience/visionforge/tables/VisionOfTable.kt index a3147236..95888f67 100644 --- a/visionforge-tables/src/commonMain/kotlin/space/kscience/visionforge/tables/VisionOfTable.kt +++ b/visionforge-tables/src/commonMain/kotlin/space/kscience/visionforge/tables/VisionOfTable.kt @@ -87,7 +87,10 @@ public fun Table.toVision(): VisionOfTable = toVision { (it ?: Double.Na public inline fun VisionOutput.table( vararg headers: ColumnHeader, block: MutableRowTable.() -> 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, List>, ): VisionOfTable { + requirePlugin(TableVisionPlugin) val columns = dataAndHeaders.map { (header, data) -> ListColumn(header, data.map { Value.of(it) }) } diff --git a/visionforge-threejs/visionforge-threejs-server/src/jsMain/kotlin/space/kscience/visionforge/three/server/jsMain.kt b/visionforge-threejs/visionforge-threejs-server/src/jsMain/kotlin/space/kscience/visionforge/three/jsMain.kt similarity index 83% rename from visionforge-threejs/visionforge-threejs-server/src/jsMain/kotlin/space/kscience/visionforge/three/server/jsMain.kt rename to visionforge-threejs/visionforge-threejs-server/src/jsMain/kotlin/space/kscience/visionforge/three/jsMain.kt index d6d191f0..ed2908f5 100644 --- a/visionforge-threejs/visionforge-threejs-server/src/jsMain/kotlin/space/kscience/visionforge/three/server/jsMain.kt +++ b/visionforge-threejs/visionforge-threejs-server/src/jsMain/kotlin/space/kscience/visionforge/three/jsMain.kt @@ -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 diff --git a/visionforge-threejs/visionforge-threejs-server/src/jvmMain/kotlin/space/kscience/visionforge/three/server/serverExtensions.kt b/visionforge-threejs/visionforge-threejs-server/src/jvmMain/kotlin/space/kscience/visionforge/three/server/serverExtensions.kt deleted file mode 100644 index d6656c46..00000000 --- a/visionforge-threejs/visionforge-threejs-server/src/jvmMain/kotlin/space/kscience/visionforge/three/server/serverExtensions.kt +++ /dev/null @@ -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()) -} diff --git a/visionforge-threejs/visionforge-threejs-server/src/jvmMain/kotlin/space/kscience/visionforge/three/serverExtensions.kt b/visionforge-threejs/visionforge-threejs-server/src/jvmMain/kotlin/space/kscience/visionforge/three/serverExtensions.kt new file mode 100644 index 00000000..6c70e0ed --- /dev/null +++ b/visionforge-threejs/visionforge-threejs-server/src/jvmMain/kotlin/space/kscience/visionforge/three/serverExtensions.kt @@ -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()) +}