diff --git a/visionforge-jupyter/src/jsMain/kotlin/VFNotebookClient.kt b/visionforge-jupyter/src/jsMain/kotlin/VFNotebookClient.kt index 84c6908b..7f06c6ac 100644 --- a/visionforge-jupyter/src/jsMain/kotlin/VFNotebookClient.kt +++ b/visionforge-jupyter/src/jsMain/kotlin/VFNotebookClient.kt @@ -31,9 +31,10 @@ public class VFNotebookClient : AbstractPlugin() { init { + console.info("Loading VisionForge global hooks") //register VisionForge in the browser window - window.asDynamic().vf = this - window.asDynamic().VisionForge = this + window.parent.asDynamic().vf = this + window.parent.asDynamic().VisionForge = this } @Suppress("NON_EXPORTABLE_TYPE") diff --git a/visionforge-jupyter/src/jvmMain/kotlin/VisionForge.kt b/visionforge-jupyter/src/jvmMain/kotlin/VisionForge.kt index d2acefde..49d5fe23 100644 --- a/visionforge-jupyter/src/jvmMain/kotlin/VisionForge.kt +++ b/visionforge-jupyter/src/jvmMain/kotlin/VisionForge.kt @@ -10,9 +10,7 @@ import io.ktor.server.websocket.WebSockets import kotlinx.coroutines.CoroutineScope import kotlinx.html.* import kotlinx.html.stream.createHTML -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.* import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.ContextAware import space.kscience.dataforge.context.info @@ -21,7 +19,8 @@ import space.kscience.dataforge.meta.* import space.kscience.dataforge.names.Name import space.kscience.visionforge.Vision import space.kscience.visionforge.VisionManager -import space.kscience.visionforge.html.* +import space.kscience.visionforge.html.HtmlVisionFragment +import space.kscience.visionforge.html.visionFragment import space.kscience.visionforge.server.VisionRoute import space.kscience.visionforge.server.serveVisionData import kotlin.coroutines.CoroutineContext @@ -50,6 +49,7 @@ public enum class VisionForgeCompatibility { @Suppress("ExtractKtorModule") public class VisionForge( public val visionManager: VisionManager, + public val notebook: Notebook, meta: Meta = Meta.EMPTY, ) : ContextAware, CoroutineScope { @@ -61,8 +61,6 @@ public class VisionForge( private var engine: ApplicationEngine? = null - public var notebookMode: VisionForgeCompatibility = VisionForgeCompatibility.IDEA - override val coroutineContext: CoroutineContext get() = context.coroutineContext @@ -75,19 +73,19 @@ public class VisionForge( host: String = getProperty("visionforge.host").string ?: "localhost", port: Int = getProperty("visionforge.port").int ?: VisionRoute.DEFAULT_PORT, ) { - if (engine != null) { + engine?.let { kernel.displayHtml { p { style = "color: red;" +"Stopping current VisionForge server" } } - + it.stop(1000, 2000) } //val connector: EngineConnectorConfig = EngineConnectorConfig(host, port) - engine?.stop(1000, 2000) + engine = context.embeddedServer(CIO, port, host) { install(WebSockets) }.start(false) @@ -115,16 +113,11 @@ public class VisionForge( } } - internal fun TagConsumer<*>.renderScriptForId(id: String, iframeIsolation: Boolean = false) { + internal fun TagConsumer<*>.renderScriptForId(id: String) { script { type = "text/javascript" - if (iframeIsolation) { - //language=JavaScript - unsafe { +"parent.VisionForge.renderAllVisionsById(document, \"$id\");" } - } else { - //language=JavaScript - unsafe { +"VisionForge.renderAllVisionsById(document, \"$id\");" } - } + //language=JavaScript + unsafe { +"parent.VisionForge.renderAllVisionsById(document, \"$id\");" } } } @@ -133,8 +126,10 @@ public class VisionForge( isolated: Boolean? = null, fragment: HtmlVisionFragment, ): MimeTypedResult { - val iframeIsolation = isolated - ?: (notebookMode == VisionForgeCompatibility.JUPYTER || notebookMode == VisionForgeCompatibility.DATALORE) + val iframeIsolation = isolated ?: when (notebook.jupyterClientType) { + JupyterClientType.DATALORE, JupyterClientType.JUPYTER_NOTEBOOK -> true + else -> false + } return HTML( iframeIsolation ) { @@ -172,7 +167,7 @@ public class VisionForge( visionFragment(visionManager, fragment = fragment) } } - renderScriptForId(id, iframeIsolation = iframeIsolation) + renderScriptForId(id) } } diff --git a/visionforge-jupyter/src/jvmMain/kotlin/VisionForgeIntegration.kt b/visionforge-jupyter/src/jvmMain/kotlin/VisionForgeIntegration.kt index e7175dd0..afc2ecc2 100644 --- a/visionforge-jupyter/src/jvmMain/kotlin/VisionForgeIntegration.kt +++ b/visionforge-jupyter/src/jvmMain/kotlin/VisionForgeIntegration.kt @@ -1,6 +1,7 @@ package space.kscience.visionforge.jupyter import kotlinx.html.* +import org.jetbrains.kotlinx.jupyter.api.KotlinKernelHost import org.jetbrains.kotlinx.jupyter.api.MimeTypedResult import org.jetbrains.kotlinx.jupyter.api.declare import org.jetbrains.kotlinx.jupyter.api.libraries.JupyterIntegration @@ -22,20 +23,30 @@ public abstract class VisionForgeIntegration( ) : JupyterIntegration(), ContextAware { override val context: Context get() = visionManager.context - protected val handler: VisionForge = VisionForge(visionManager) - protected abstract fun Builder.afterLoaded() + protected abstract fun Builder.afterLoaded(vf: VisionForge) final override fun Builder.onLoaded() { + val vf: VisionForge = VisionForge(visionManager, notebook) + onLoaded { - declare("VisionForge" to handler, "vf" to handler) - handler.startServer(this) + val kernel: KotlinKernelHost = this + declare("VisionForge" to vf, "vf" to vf) + vf.startServer(kernel) + vf.configuration.onChange(this) { name -> + if (name.toString() == "visionforge.port") { + kernel.displayHtml { + p { +"Property 'visionforge.port' changed. Restarting server" } + } + vf.startServer(kernel) + } + } } onShutdown { - handler.stopServer(this) + vf.stopServer(this) } import( @@ -53,7 +64,7 @@ public abstract class VisionForgeIntegration( // } render { vision -> - handler.produceHtml { + vf.produceHtml { vision(vision) } } @@ -74,16 +85,16 @@ public abstract class VisionForgeIntegration( this.id = id visionFragment(visionManager, fragment = page.content) } - with(handler) { - renderScriptForId(id, true) + with(vf) { + renderScriptForId(id) } } } } render { fragment -> - handler.produceHtml { - if (!handler.isServerRunning()) { + vf.produceHtml { + if (!vf.isServerRunning()) { p { style = "color: red;" +"The server is not running. Forms are not interactive. Start server with `VisionForge.startServer()." @@ -94,7 +105,7 @@ public abstract class VisionForgeIntegration( } } - afterLoaded() + afterLoaded(vf) } } diff --git a/visionforge-jupyter/visionforge-jupyter-common/src/jvmMain/kotlin/JupyterCommonIntegration.kt b/visionforge-jupyter/visionforge-jupyter-common/src/jvmMain/kotlin/JupyterCommonIntegration.kt index c326a816..6200bd5d 100644 --- a/visionforge-jupyter/visionforge-jupyter-common/src/jvmMain/kotlin/JupyterCommonIntegration.kt +++ b/visionforge-jupyter/visionforge-jupyter-common/src/jvmMain/kotlin/JupyterCommonIntegration.kt @@ -24,7 +24,7 @@ import space.kscience.visionforge.visionManager @DFExperimental public class JupyterCommonIntegration : VisionForgeIntegration(CONTEXT.visionManager) { - override fun Builder.afterLoaded() { + override fun Builder.afterLoaded(vf: VisionForge) { resources { js("visionforge-common") { @@ -43,19 +43,19 @@ public class JupyterCommonIntegration : VisionForgeIntegration(CONTEXT.visionMan ) render { gdmlModel -> - handler.produceHtml { + vf.produceHtml { vision { gdmlModel.toVision() } } } render> { table -> - handler.produceHtml { + vf.produceHtml { vision { table.toVision() } } } render { plot -> - handler.produceHtml { + vf.produceHtml { vision { plot.asVision() } } }