Jupyter renderers are working for lab and Idea

This commit is contained in:
Alexander Nozik 2023-07-22 15:39:43 +03:00
parent a49a4f1a7f
commit b3f68d879f
17 changed files with 226 additions and 187 deletions

View File

@ -13,7 +13,7 @@ val fxVersion by extra("11")
allprojects { allprojects {
group = "space.kscience" group = "space.kscience"
version = "0.3.0-dev-11" version = "0.3.0-dev-12"
} }
subprojects { subprojects {

View File

@ -51,7 +51,7 @@ kotlin {
implementation(projects.visionforgeMarkdown) implementation(projects.visionforgeMarkdown)
implementation(projects.visionforgeTables) implementation(projects.visionforgeTables)
implementation(projects.cernRootLoader) implementation(projects.cernRootLoader)
implementation(projects.visionforgeJupyter.visionforgeJupyterCommon) api(projects.visionforgeJupyter.visionforgeJupyterCommon)
} }
} }

View File

@ -2,77 +2,31 @@
"cells": [ "cells": [
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 2, "execution_count": null,
"metadata": { "metadata": {
"ExecuteTime": { "tags": []
"end_time": "2023-07-20T06:12:13.305060400Z",
"start_time": "2023-07-20T06:12:13.011273800Z"
}
}, },
"outputs": [], "outputs": [],
"source": [ "source": [
"@file:Repository(\"*mavenLocal\")\n", "@file:Repository(\"*mavenLocal\")\n",
"@file:Repository(\"https://repo.kotlin.link\")\n", "@file:Repository(\"https://repo.kotlin.link\")\n",
"@file:Repository(\"https://maven.pkg.jetbrains.space/spc/p/sci/dev\")\n", "@file:Repository(\"https://maven.pkg.jetbrains.space/spc/p/sci/dev\")\n",
"@file:DependsOn(\"space.kscience:visionforge-jupyter-common-jvm:0.3.0-dev-11\")" "@file:DependsOn(\"space.kscience:visionforge-jupyter-common-jvm:0.3.0-dev-12\")\n",
"//import space.kscience.visionforge.jupyter.JupyterCommonIntegration\n",
"//\n",
"//val integration = JupyterCommonIntegration()\n",
"//USE(integration.getDefinitions(notebook).first())"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 3, "execution_count": null,
"metadata": { "metadata": {
"collapsed": false, "tags": []
"jupyter": {
"outputs_hidden": false
}, },
"ExecuteTime": { "outputs": [],
"end_time": "2023-07-20T06:12:19.603077Z",
"start_time": "2023-07-20T06:12:19.419504300Z"
}
},
"outputs": [
{
"data": {
"text/html": "<p style=\"color: blue;\">Starting VisionForge server on http://localhost:7777</p>\n"
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [ "source": [
"vf.startServer()" "vf.fragment {\n",
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
},
"ExecuteTime": {
"end_time": "2023-07-20T06:12:21.490069100Z",
"start_time": "2023-07-20T06:12:20.694188600Z"
}
},
"outputs": [
{
"data": {
"text/html": "<div id=\"fragment[1645474043/2315898832]\">\n <h1>AAA</h1>\n <div id=\"output[vision[302766000]]\" class=\"visionforge-output\" data-output-name=\"vision[302766000]\" data-output-connect=\"ws://localhost:7777/content-0/ws\">\n <script type=\"text/json\" class=\"visionforge-output-data\">\n{\n \"type\": \"group.solid\",\n \"children\": {\n \"@ambientLight\": {\n \"type\": \"solid.light.ambient\"\n },\n \"@static[1326263213]\": {\n \"type\": \"solid.box\",\n \"xSize\": 100.0,\n \"ySize\": 100.0,\n \"zSize\": 200.0\n },\n \"@static[1813044036]\": {\n \"type\": \"solid.sphere\",\n \"properties\": {\n \"position\": {\n \"x\": 300\n }\n },\n \"radius\": 100.0\n }\n }\n}\n</script>\n </div>\n <div id=\"output[vision[1326029936]]\" class=\"visionforge-output\" data-output-name=\"vision[1326029936]\" data-output-connect=\"ws://localhost:7777/content-0/ws\">\n <script type=\"text/json\" class=\"visionforge-output-data\">\n{\n \"type\": \"vision.plotly\",\n \"meta\": {\n \"data\": {\n \"type\": \"scatter\",\n \"x\": [\n 1,\n 2,\n 3,\n 1\n ],\n \"y\": [\n 1,\n 2,\n 3,\n 4\n ],\n \"@index\": \"0\"\n }\n }\n}\n</script>\n </div>\n</div>\n<script type=\"text/javascript\">VisionForge.renderAllVisionsById(\"fragment[1645474043/2315898832]\");</script>\n"
},
"execution_count": 4,
"metadata": {
"text/html": {
"isolated": true
}
},
"output_type": "execute_result"
}
],
"source": [
"vf.page {\n",
" h1 { +\"AAA\" }\n", " h1 { +\"AAA\" }\n",
" vision {\n", " vision {\n",
" solid {\n", " solid {\n",
@ -100,24 +54,27 @@
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": null,
"metadata": { "metadata": {
"collapsed": false,
"jupyter": { "jupyter": {
"outputs_hidden": false "outputs_hidden": false
} },
"tags": []
}, },
"outputs": [], "outputs": [],
"source": [ "source": [
"vf.stopServer()" "Plotly.plot { \n",
" scatter{\n",
" x(1,2,3)\n",
" y(1,2,3)\n",
" }\n",
"}"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": null,
"metadata": {},
"outputs": [], "outputs": [],
"source": [], "source": []
"metadata": {
"collapsed": false
}
} }
], ],
"metadata": { "metadata": {

View File

@ -2,7 +2,6 @@ package space.kscience.visionforge.react
import kotlinx.css.* import kotlinx.css.*
import org.w3c.dom.Element import org.w3c.dom.Element
import org.w3c.dom.HTMLElement
import react.* import react.*
import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.request import space.kscience.dataforge.context.request
@ -29,7 +28,7 @@ public val ThreeCanvasComponent: FC<ThreeCanvasProps> = fc("ThreeCanvasComponent
useEffect(props.solid, props.options, elementRef) { useEffect(props.solid, props.options, elementRef) {
if (canvas == null) { if (canvas == null) {
val element = elementRef.current as? HTMLElement ?: error("Canvas element not found") val element = elementRef.current ?: error("Canvas element not found")
canvas = ThreeCanvas(three, element, props.options ?: Canvas3DOptions()) canvas = ThreeCanvas(three, element, props.options ?: Canvas3DOptions())
} }
} }

View File

@ -4,19 +4,28 @@ import kotlinx.html.FlowContent
import kotlinx.html.TagConsumer import kotlinx.html.TagConsumer
import kotlinx.html.stream.createHTML import kotlinx.html.stream.createHTML
public typealias HtmlFragment = TagConsumer<*>.() -> Unit /**
* A standalone HTML fragment
public fun HtmlFragment.renderToString(): String = createHTML().apply(this).finalize() */
public fun interface HtmlFragment {
public fun TagConsumer<*>.fragment(fragment: HtmlFragment) { public fun TagConsumer<*>.append()
fragment()
} }
public fun FlowContent.fragment(fragment: HtmlFragment) { /**
fragment(consumer) * Convenience method to append fragment to the given [consumer]
} */
public fun HtmlFragment.appendTo(consumer: TagConsumer<*>): Unit = consumer.append()
public operator fun HtmlFragment.plus(other: HtmlFragment): HtmlFragment = { /**
this@plus() * Create a string from this [HtmlFragment]
other() */
public fun HtmlFragment.renderToString(): String = createHTML().apply { append() }.finalize()
public fun TagConsumer<*>.appendFragment(fragment: HtmlFragment): Unit = fragment.appendTo(this)
public fun FlowContent.appendFragment(fragment: HtmlFragment): Unit = fragment.appendTo(consumer)
public operator fun HtmlFragment.plus(other: HtmlFragment): HtmlFragment = HtmlFragment {
this@plus.appendTo(this)
other.appendTo(this)
} }

View File

@ -2,18 +2,17 @@ package space.kscience.visionforge.html
import kotlinx.html.* import kotlinx.html.*
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.NameToken import space.kscience.dataforge.names.NameToken
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
import space.kscience.visionforge.Vision import space.kscience.visionforge.Vision
import space.kscience.visionforge.VisionManager import space.kscience.visionforge.VisionManager
public typealias HtmlVisionFragment = VisionTagConsumer<*>.() -> Unit public fun interface HtmlVisionFragment{
public fun VisionTagConsumer<*>.append()
@DFExperimental }
public fun HtmlVisionFragment(content: VisionTagConsumer<*>.() -> Unit): HtmlVisionFragment = content
public fun HtmlVisionFragment.appendTo(consumer: VisionTagConsumer<*>): Unit = consumer.append()
/** /**
* Render a fragment in the given consumer and return a map of extracted visions * Render a fragment in the given consumer and return a map of extracted visions
@ -84,7 +83,7 @@ public fun TagConsumer<*>.visionFragment(
} }
} }
fragment(consumer) fragment.appendTo(consumer)
} }
public fun FlowContent.visionFragment( public fun FlowContent.visionFragment(

View File

@ -17,7 +17,7 @@ public data class VisionPage(
/** /**
* Use a script with given [src] as a global header for all pages. * Use a script with given [src] as a global header for all pages.
*/ */
public fun scriptHeader(src: String, block: SCRIPT.() -> Unit = {}): HtmlFragment = { public fun scriptHeader(src: String, block: SCRIPT.() -> Unit = {}): HtmlFragment = HtmlFragment{
script { script {
type = "text/javascript" type = "text/javascript"
this.src = src this.src = src
@ -28,7 +28,7 @@ public data class VisionPage(
/** /**
* Use css with the given stylesheet link as a global header for all pages. * Use css with the given stylesheet link as a global header for all pages.
*/ */
public fun styleSheetHeader(href: String, block: LINK.() -> Unit = {}): HtmlFragment = { public fun styleSheetHeader(href: String, block: LINK.() -> Unit = {}): HtmlFragment = HtmlFragment{
link { link {
rel = "stylesheet" rel = "stylesheet"
this.href = href this.href = href
@ -36,7 +36,7 @@ public data class VisionPage(
} }
} }
public fun title(title:String): HtmlFragment = { public fun title(title:String): HtmlFragment = HtmlFragment{
title(title) title(title)
} }
} }

View File

@ -24,7 +24,7 @@ fun FlowContent.renderVisionFragment(
renderer(name, vision, outputMeta) renderer(name, vision, outputMeta)
} }
} }
fragment(consumer) fragment.appendTo(consumer)
return visionMap return visionMap
} }
@ -35,7 +35,7 @@ private fun VisionOutput.base(block: VisionGroup.() -> Unit) = context.visionMan
@DFExperimental @DFExperimental
class HtmlTagTest { class HtmlTagTest {
val fragment: HtmlVisionFragment = { val fragment = HtmlVisionFragment{
div { div {
h1 { +"Head" } h1 { +"Head" }
vision("ddd") { vision("ddd") {

View File

@ -286,8 +286,8 @@ public fun VisionClient.renderAllVisionsIn(element: Element) {
/** /**
* Render all visions in an element with a given [id] * Render all visions in an element with a given [id]
*/ */
public fun VisionClient.renderAllVisionsById(id: String): Unit = whenDocumentLoaded { public fun VisionClient.renderAllVisionsById(document: Document, id: String): Unit {
val element = getElementById(id) val element = document.getElementById(id)
if (element != null) { if (element != null) {
renderAllVisionsIn(element) renderAllVisionsIn(element)
} else { } else {

View File

@ -34,10 +34,10 @@ public interface HtmlVisionContext : ContextAware {
public typealias HtmlVisionContextFragment = context(HtmlVisionContext) TagConsumer<*>.() -> Unit public typealias HtmlVisionContextFragment = context(HtmlVisionContext) TagConsumer<*>.() -> Unit
context(HtmlVisionContext) //context(HtmlVisionContext)
public fun HtmlVisionFragment( //public fun HtmlVisionFragment(
content: TagConsumer<*>.() -> Unit, // content: TagConsumer<*>.() -> Unit,
): HtmlVisionFragment = content //): HtmlVisionFragment = HtmlVisionFragment { }
context(HtmlVisionContext) context(HtmlVisionContext)
private fun <T> TagConsumer<T>.vision( private fun <T> TagConsumer<T>.vision(

View File

@ -91,14 +91,14 @@ internal fun checkOrStoreFile(htmlPath: Path, filePath: Path, resource: String,
*/ */
internal fun fileScriptHeader( internal fun fileScriptHeader(
path: Path, path: Path,
): HtmlFragment = { ): HtmlFragment = HtmlFragment{
script { script {
type = "text/javascript" type = "text/javascript"
src = path.toString() src = path.toString()
} }
} }
internal fun embedScriptHeader(resource: String, classLoader: ClassLoader): HtmlFragment = { internal fun embedScriptHeader(resource: String, classLoader: ClassLoader): HtmlFragment = HtmlFragment{
script { script {
type = "text/javascript" type = "text/javascript"
unsafe { unsafe {
@ -113,7 +113,7 @@ internal fun fileCssHeader(
cssPath: Path, cssPath: Path,
resource: String, resource: String,
classLoader: ClassLoader, classLoader: ClassLoader,
): HtmlFragment = { ): HtmlFragment = HtmlFragment{
val relativePath = checkOrStoreFile(basePath, cssPath, resource, classLoader) val relativePath = checkOrStoreFile(basePath, cssPath, resource, classLoader)
link { link {
rel = "stylesheet" rel = "stylesheet"

View File

@ -78,7 +78,7 @@ public fun VisionPage.makeFile(
charset = "utf-8" charset = "utf-8"
} }
actualHeaders.values.forEach { actualHeaders.values.forEach {
fragment(it) appendFragment(it)
} }
} }
body { body {

View File

@ -1,6 +1,7 @@
package space.kscience.visionforge.jupyter package space.kscience.visionforge.jupyter
import kotlinx.browser.window import kotlinx.browser.window
import org.w3c.dom.Document
import org.w3c.dom.Element import org.w3c.dom.Element
import space.kscience.dataforge.context.AbstractPlugin import space.kscience.dataforge.context.AbstractPlugin
import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Context
@ -20,8 +21,8 @@ public class VFNotebookClient : AbstractPlugin() {
client.renderAllVisionsIn(element) client.renderAllVisionsIn(element)
} }
public fun renderAllVisionsById(id: String) { public fun renderAllVisionsById(document: Document, id: String) {
client.renderAllVisionsById(id) client.renderAllVisionsById(document, id)
} }
public fun renderAllVisions() { public fun renderAllVisions() {

View File

@ -11,6 +11,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.html.* import kotlinx.html.*
import kotlinx.html.stream.createHTML import kotlinx.html.stream.createHTML
import org.jetbrains.kotlinx.jupyter.api.HTML import org.jetbrains.kotlinx.jupyter.api.HTML
import org.jetbrains.kotlinx.jupyter.api.KotlinKernelHost
import org.jetbrains.kotlinx.jupyter.api.MimeTypedResult import org.jetbrains.kotlinx.jupyter.api.MimeTypedResult
import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.ContextAware import space.kscience.dataforge.context.ContextAware
@ -20,24 +21,33 @@ import space.kscience.dataforge.meta.*
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.visionforge.Vision import space.kscience.visionforge.Vision
import space.kscience.visionforge.VisionManager import space.kscience.visionforge.VisionManager
import space.kscience.visionforge.html.HtmlVisionFragment import space.kscience.visionforge.html.*
import space.kscience.visionforge.html.visionFragment
import space.kscience.visionforge.server.VisionRoute import space.kscience.visionforge.server.VisionRoute
import space.kscience.visionforge.server.serveVisionData import space.kscience.visionforge.server.serveVisionData
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.random.Random import kotlin.random.Random
import kotlin.random.nextUInt import kotlin.random.nextUInt
internal fun TagConsumer<*>.renderScriptForId(id: String) {
script { @Suppress("FunctionName")
type = "text/javascript" internal inline fun HTML(isolated: Boolean = false, block: TagConsumer<*>.() -> Unit): MimeTypedResult =
unsafe { +"VisionForge.renderAllVisionsById(\"$id\");" } HTML(createHTML().apply(block).finalize(), isolated)
}
internal fun KotlinKernelHost.displayHtml(block: TagConsumer<*>.() -> Unit) {
display(HTML(false, block), null)
}
public enum class VisionForgeCompatibility {
JUPYTER,
JUPYTER_LAB,
DATALORE,
IDEA
} }
/** /**
* A handler class that includes a server and common utilities * A handler class that includes a server and common utilities
*/ */
@Suppress("ExtractKtorModule")
public class VisionForge( public class VisionForge(
public val visionManager: VisionManager, public val visionManager: VisionManager,
meta: Meta = Meta.EMPTY, meta: Meta = Meta.EMPTY,
@ -51,31 +61,30 @@ public class VisionForge(
private var engine: ApplicationEngine? = null private var engine: ApplicationEngine? = null
public var isolateFragments: Boolean = false public var notebookMode: VisionForgeCompatibility = VisionForgeCompatibility.IDEA
override val coroutineContext: CoroutineContext get() = context.coroutineContext override val coroutineContext: CoroutineContext get() = context.coroutineContext
public fun legacyMode() {
isolateFragments = true
}
public fun isServerRunning(): Boolean = engine != null public fun isServerRunning(): Boolean = engine != null
public fun html(block: TagConsumer<*>.() -> Unit): MimeTypedResult = HTML(createHTML().apply(block).finalize())
public fun getProperty(name: String): TypedMeta<*>? = configuration[name] ?: context.properties[name] public fun getProperty(name: String): TypedMeta<*>? = configuration[name] ?: context.properties[name]
public fun startServer( internal fun startServer(
kernel: KotlinKernelHost,
host: String = getProperty("visionforge.host").string ?: "localhost", host: String = getProperty("visionforge.host").string ?: "localhost",
port: Int = getProperty("visionforge.port").int ?: VisionRoute.DEFAULT_PORT, port: Int = getProperty("visionforge.port").int ?: VisionRoute.DEFAULT_PORT,
): MimeTypedResult = html { ) {
if (engine != null) { if (engine != null) {
kernel.displayHtml {
p { p {
style = "color: red;" style = "color: red;"
+"Stopping current VisionForge server" +"Stopping current VisionForge server"
} }
} }
}
//val connector: EngineConnectorConfig = EngineConnectorConfig(host, port) //val connector: EngineConnectorConfig = EngineConnectorConfig(host, port)
engine?.stop(1000, 2000) engine?.stop(1000, 2000)
@ -83,23 +92,52 @@ public class VisionForge(
install(WebSockets) install(WebSockets)
}.start(false) }.start(false)
kernel.displayHtml {
p { p {
style = "color: blue;" style = "color: blue;"
+"Starting VisionForge server on http://$host:$port" +"Starting VisionForge server on port $port"
}
} }
} }
public fun stopServer() { internal fun stopServer(kernel: KotlinKernelHost) {
engine?.apply { engine?.apply {
logger.info { "Stopping VisionForge server" } logger.info { "Stopping VisionForge server" }
stop(1000, 2000) stop(1000, 2000)
engine = null engine = null
} }
kernel.displayHtml {
p {
style = "color: red;"
+"VisionForge server stopped"
}
}
} }
private fun produceHtmlString( internal fun TagConsumer<*>.renderScriptForId(id: String, iframeIsolation: Boolean = false) {
script {
type = "text/javascript"
if (iframeIsolation) {
//language=JavaScript
unsafe { +"parent.VisionForge.renderAllVisionsById(document, \"$id\");" }
} else {
//language=JavaScript
unsafe { +"VisionForge.renderAllVisionsById(document, \"$id\");" }
}
}
}
public fun produceHtml(
isolated: Boolean? = null,
fragment: HtmlVisionFragment, fragment: HtmlVisionFragment,
): String = createHTML().apply { ): MimeTypedResult {
val iframeIsolation = isolated
?: (notebookMode == VisionForgeCompatibility.JUPYTER || notebookMode == VisionForgeCompatibility.DATALORE)
return HTML(
iframeIsolation
) {
val id = "fragment[${fragment.hashCode()}/${Random.nextUInt()}]" val id = "fragment[${fragment.hashCode()}/${Random.nextUInt()}]"
div { div {
this.id = id this.id = id
@ -134,15 +172,11 @@ public class VisionForge(
visionFragment(visionManager, fragment = fragment) visionFragment(visionManager, fragment = fragment)
} }
} }
renderScriptForId(id) renderScriptForId(id, iframeIsolation = iframeIsolation)
}.finalize() }
}
public fun produceHtml(isolated: Boolean? = null, fragment: HtmlVisionFragment): MimeTypedResult =
HTML(produceHtmlString(fragment), isolated ?: isolateFragments)
public fun fragment(body: HtmlVisionFragment): MimeTypedResult = produceHtml(fragment = body)
public fun page(body: HtmlVisionFragment): MimeTypedResult = produceHtml(true, body)
public fun form(builder: FORM.() -> Unit): HtmlFormFragment = public fun form(builder: FORM.() -> Unit): HtmlFormFragment =
HtmlFormFragment("form[${counter++}]", builder = builder) HtmlFormFragment("form[${counter++}]", builder = builder)
} }

View File

@ -1,8 +1,7 @@
package space.kscience.visionforge.jupyter package space.kscience.visionforge.jupyter
import kotlinx.html.* import kotlinx.html.*
import kotlinx.html.stream.createHTML import org.jetbrains.kotlinx.jupyter.api.MimeTypedResult
import org.jetbrains.kotlinx.jupyter.api.HTML
import org.jetbrains.kotlinx.jupyter.api.declare import org.jetbrains.kotlinx.jupyter.api.declare
import org.jetbrains.kotlinx.jupyter.api.libraries.JupyterIntegration import org.jetbrains.kotlinx.jupyter.api.libraries.JupyterIntegration
import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Context
@ -31,11 +30,12 @@ public abstract class VisionForgeIntegration(
onLoaded { onLoaded {
declare("VisionForge" to handler, "vf" to handler) declare("VisionForge" to handler, "vf" to handler)
handler.startServer(this)
} }
onShutdown { onShutdown {
handler.stopServer() handler.stopServer(this)
} }
import( import(
@ -43,14 +43,14 @@ public abstract class VisionForgeIntegration(
"space.kscience.visionforge.html.*", "space.kscience.visionforge.html.*",
"space.kscience.visionforge.jupyter.*" "space.kscience.visionforge.jupyter.*"
) )
//
render<HtmlFragment> { fragment -> // render<HtmlFragment> { fragment ->
handler.produceHtml(fragment = fragment) // HTML(fragment.renderToString())
} // }
//
render<HtmlVisionFragment> { fragment -> // render<HtmlVisionFragment> { fragment ->
handler.produceHtml(fragment = fragment) // handler.produceHtml(fragment = fragment)
} // }
render<Vision> { vision -> render<Vision> { vision ->
handler.produceHtml { handler.produceHtml {
@ -59,13 +59,13 @@ public abstract class VisionForgeIntegration(
} }
render<VisionPage> { page -> render<VisionPage> { page ->
HTML(createHTML().apply { HTML(true) {
head { head {
meta { meta {
charset = "utf-8" charset = "utf-8"
} }
page.pageHeaders.values.forEach { page.pageHeaders.values.forEach {
fragment(it) appendFragment(it)
} }
} }
body { body {
@ -74,9 +74,11 @@ public abstract class VisionForgeIntegration(
this.id = id this.id = id
visionFragment(visionManager, fragment = page.content) visionFragment(visionManager, fragment = page.content)
} }
renderScriptForId(id) with(handler) {
renderScriptForId(id, true)
}
}
} }
}.finalize(), true)
} }
render<HtmlFormFragment> { fragment -> render<HtmlFormFragment> { fragment ->
@ -87,7 +89,7 @@ public abstract class VisionForgeIntegration(
+"The server is not running. Forms are not interactive. Start server with `VisionForge.startServer()." +"The server is not running. Forms are not interactive. Start server with `VisionForge.startServer()."
} }
} }
fragment(fragment.formBody) appendFragment(fragment.formBody)
vision(fragment.vision) vision(fragment.vision)
} }
} }
@ -96,10 +98,25 @@ public abstract class VisionForgeIntegration(
} }
} }
/**
* Create a fragment without a head to be embedded in the page
*/
@Suppress("UnusedReceiverParameter")
public fun VisionForge.html(body: TagConsumer<*>.() -> Unit): MimeTypedResult = HTML(false, body)
/**
* Create a fragment without a head to be embedded in the page
*/
public fun VisionForge.fragment(body: VisionTagConsumer<*>.() -> Unit): MimeTypedResult = produceHtml(false, body)
/** /**
* Create a standalone page in the notebook * Create a standalone page in the notebook
*/ */
public fun VisionForge.page( public fun VisionForge.page(
pageHeaders: Map<String, HtmlFragment> = emptyMap(), pageHeaders: Map<String, HtmlFragment> = emptyMap(),
content: HtmlVisionFragment body: VisionTagConsumer<*>.() -> Unit,
): VisionPage = VisionPage(visionManager, pageHeaders, content) ): VisionPage = VisionPage(visionManager, pageHeaders, body)

View File

@ -6,8 +6,12 @@ import space.kscience.dataforge.context.Context
import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.DFExperimental
import space.kscience.gdml.Gdml import space.kscience.gdml.Gdml
import space.kscience.plotly.Plot import space.kscience.plotly.Plot
import space.kscience.plotly.PlotlyPage
import space.kscience.plotly.StaticPlotlyRenderer
import space.kscience.tables.* import space.kscience.tables.*
import space.kscience.visionforge.gdml.toVision import space.kscience.visionforge.gdml.toVision
import space.kscience.visionforge.html.HtmlFragment
import space.kscience.visionforge.html.VisionPage
import space.kscience.visionforge.markup.MarkupPlugin import space.kscience.visionforge.markup.MarkupPlugin
import space.kscience.visionforge.plotly.PlotlyPlugin import space.kscience.visionforge.plotly.PlotlyPlugin
import space.kscience.visionforge.plotly.asVision import space.kscience.visionforge.plotly.asVision
@ -23,7 +27,7 @@ public class JupyterCommonIntegration : VisionForgeIntegration(CONTEXT.visionMan
override fun Builder.afterLoaded() { override fun Builder.afterLoaded() {
resources { resources {
js("three") { js("visionforge-common") {
classPath("js/visionforge-jupyter-common.js") classPath("js/visionforge-jupyter-common.js")
} }
} }
@ -55,6 +59,24 @@ public class JupyterCommonIntegration : VisionForgeIntegration(CONTEXT.visionMan
vision { plot.asVision() } vision { plot.asVision() }
} }
} }
render<PlotlyPage> { plotlyPage ->
val headers = plotlyPage.headers.associate { plotlyFragment ->
plotlyFragment.hashCode().toString(16) to HtmlFragment {
plotlyFragment.visit(this)
}
}
VisionPage(visionManager, headers) {
div{
p { +"Plotly page renderer is not recommended in VisionForge, use `vf.page{}`" }
}
div {
plotlyPage.fragment.render.invoke(this, StaticPlotlyRenderer)
}
}
}
} }
public companion object { public companion object {

View File

@ -3,6 +3,7 @@ package space.kscience.visionforge.three
import kotlinx.html.stream.createHTML import kotlinx.html.stream.createHTML
import space.kscience.visionforge.html.ResourceLocation import space.kscience.visionforge.html.ResourceLocation
import space.kscience.visionforge.html.VisionPage import space.kscience.visionforge.html.VisionPage
import space.kscience.visionforge.html.appendTo
import space.kscience.visionforge.html.importScriptHeader import space.kscience.visionforge.html.importScriptHeader
import kotlin.test.Test import kotlin.test.Test
@ -15,7 +16,7 @@ class TestServerExtensions {
VisionPage.importScriptHeader( VisionPage.importScriptHeader(
"js/visionforge-three.js", "js/visionforge-three.js",
ResourceLocation.SYSTEM ResourceLocation.SYSTEM
).invoke(this) ).appendTo(this)
}.finalize() }.finalize()