0.2.0 #71
@ -1,7 +1,5 @@
|
||||
plugins {
|
||||
id("ru.mipt.npm.gradle.project")
|
||||
// kotlin("multiplatform") version "1.5.30" apply false
|
||||
// kotlin("js") version "1.5.30" apply false
|
||||
}
|
||||
|
||||
val dataforgeVersion by extra("0.5.2")
|
||||
|
@ -46,6 +46,7 @@ private object RootDecoder {
|
||||
private val refCache: List<RefEntry>,
|
||||
) : KSerializer<T> by tSerializer {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun deserialize(decoder: Decoder): T {
|
||||
val input = decoder as JsonDecoder
|
||||
val element = input.decodeJsonElement()
|
||||
|
@ -9,7 +9,7 @@ import org.w3c.files.FileReader
|
||||
import org.w3c.files.get
|
||||
import react.Props
|
||||
import react.dom.h2
|
||||
import react.functionComponent
|
||||
import react.fc
|
||||
import react.useMemo
|
||||
import react.useState
|
||||
import space.kscience.dataforge.context.Context
|
||||
@ -34,7 +34,7 @@ external interface GDMLAppProps : Props {
|
||||
}
|
||||
|
||||
@JsExport
|
||||
val GDMLApp = functionComponent<GDMLAppProps>("GDMLApp") { props ->
|
||||
val GDMLApp = fc<GDMLAppProps>("GDMLApp") { props ->
|
||||
val visionManager = useMemo(props.context) { props.context.fetch(Solids).visionManager }
|
||||
var deferredVision: Deferred<Solid?> by useState {
|
||||
CompletableDeferred(props.vision)
|
||||
|
@ -3,7 +3,7 @@ import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.css.*
|
||||
import react.Props
|
||||
import react.functionComponent
|
||||
import react.fc
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.plotly.layout
|
||||
import space.kscience.plotly.models.Trace
|
||||
@ -20,7 +20,7 @@ external interface DemoProps : Props {
|
||||
var context: Context
|
||||
}
|
||||
|
||||
val GravityDemo = functionComponent<DemoProps> { props ->
|
||||
val GravityDemo = fc<DemoProps> { props ->
|
||||
val velocityTrace = Trace{
|
||||
name = "velocity"
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import org.intellij.markdown.flavours.gfm.GFMFlavourDescriptor
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.HTMLElement
|
||||
import react.Props
|
||||
import react.functionComponent
|
||||
import react.fc
|
||||
import react.useEffect
|
||||
import react.useRef
|
||||
import space.kscience.visionforge.markup.VisionOfMarkup
|
||||
@ -20,7 +20,7 @@ external interface MarkupProps : Props {
|
||||
var markup: VisionOfMarkup?
|
||||
}
|
||||
|
||||
val Markup = functionComponent<MarkupProps>("Markup") { props ->
|
||||
val Markup = fc<MarkupProps>("Markup") { props ->
|
||||
val elementRef = useRef<Element>(null)
|
||||
|
||||
useEffect(props.markup, elementRef) {
|
||||
|
@ -14,7 +14,7 @@ external interface PlotlyProps : Props {
|
||||
}
|
||||
|
||||
|
||||
val Plotly = functionComponent<PlotlyProps>("Plotly") { props ->
|
||||
val Plotly = fc<PlotlyProps>("Plotly") { props ->
|
||||
val elementRef = useRef<Element>(null)
|
||||
|
||||
useEffect(props.plot, elementRef) {
|
||||
|
@ -52,8 +52,6 @@ kotlin {
|
||||
jsMain {
|
||||
dependencies {
|
||||
implementation(project(":ui:ring"))
|
||||
implementation(npmlibs.ktor.client.js)
|
||||
implementation(npmlibs.ktor.client.serialization)
|
||||
implementation(project(":visionforge-threejs"))
|
||||
//implementation(devNpm("webpack-bundle-analyzer", "4.4.0"))
|
||||
}
|
||||
|
@ -1,17 +1,19 @@
|
||||
package ru.mipt.npm.muon.monitor
|
||||
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.request.get
|
||||
import kotlinx.browser.window
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.await
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.css.*
|
||||
import kotlinx.html.js.onClickFunction
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.w3c.fetch.RequestInit
|
||||
import react.Props
|
||||
import react.dom.attrs
|
||||
import react.dom.button
|
||||
import react.dom.p
|
||||
import react.functionComponent
|
||||
import react.fc
|
||||
import react.useMemo
|
||||
import react.useState
|
||||
import space.kscience.dataforge.context.Context
|
||||
@ -31,13 +33,12 @@ import kotlin.math.PI
|
||||
external interface MMAppProps : Props {
|
||||
var model: Model
|
||||
var context: Context
|
||||
var connection: HttpClient
|
||||
var selected: Name?
|
||||
}
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
@JsExport
|
||||
val MMApp = functionComponent<MMAppProps>("Muon monitor") { props ->
|
||||
val MMApp = fc<MMAppProps>("Muon monitor") { props ->
|
||||
|
||||
val mmOptions = useMemo {
|
||||
Canvas3DOptions {
|
||||
@ -75,9 +76,21 @@ val MMApp = functionComponent<MMAppProps>("Muon monitor") { props ->
|
||||
attrs {
|
||||
onClickFunction = {
|
||||
context.launch {
|
||||
val event = props.connection.get<Event>(
|
||||
"http://localhost:8080/event"
|
||||
)
|
||||
// val event = props.connection.get<Event>(
|
||||
// "http://localhost:8080/event"
|
||||
// )
|
||||
val event = window.fetch(
|
||||
"http://localhost:8080/event",
|
||||
RequestInit("GET")
|
||||
).then { response ->
|
||||
if (response.ok) {
|
||||
response.text()
|
||||
} else {
|
||||
error("Failed to get event")
|
||||
}
|
||||
}.then { body ->
|
||||
Json.decodeFromString(Event.serializer(), body)
|
||||
}.await()
|
||||
events = events + event
|
||||
props.model.displayEvent(event)
|
||||
}
|
||||
|
@ -1,10 +1,6 @@
|
||||
package ru.mipt.npm.muon.monitor
|
||||
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.features.json.JsonFeature
|
||||
import io.ktor.client.features.json.serializer.KotlinxSerializer
|
||||
import kotlinx.browser.document
|
||||
import react.child
|
||||
import react.dom.render
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.context.fetch
|
||||
@ -15,12 +11,6 @@ import space.kscience.visionforge.startApplication
|
||||
|
||||
private class MMDemoApp : Application {
|
||||
|
||||
private val connection = HttpClient {
|
||||
install(JsonFeature) {
|
||||
serializer = KotlinxSerializer()
|
||||
}
|
||||
}
|
||||
|
||||
override fun start(state: Map<String, Any>) {
|
||||
|
||||
val context = Context("MM-demo") {
|
||||
@ -35,7 +25,6 @@ private class MMDemoApp : Application {
|
||||
child(MMApp) {
|
||||
attrs {
|
||||
this.model = model
|
||||
this.connection = this@MMDemoApp.connection
|
||||
this.context = context
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ fun main() {
|
||||
}
|
||||
}
|
||||
|
||||
server.show()
|
||||
server.openInBrowser()
|
||||
|
||||
GlobalScope.launch {
|
||||
while (isActive) {
|
||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
@ -9,12 +9,12 @@ import kotlinx.html.js.onClickFunction
|
||||
import org.w3c.dom.events.Event
|
||||
import org.w3c.files.Blob
|
||||
import org.w3c.files.BlobPropertyBag
|
||||
import react.FunctionComponent
|
||||
import react.FC
|
||||
import react.Props
|
||||
import react.RBuilder
|
||||
import react.dom.attrs
|
||||
import react.dom.button
|
||||
import react.functionComponent
|
||||
import react.fc
|
||||
import space.kscience.dataforge.meta.withDefault
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.encodeToString
|
||||
@ -47,7 +47,7 @@ public external interface CanvasControlsProps : Props {
|
||||
public var vision: Vision?
|
||||
}
|
||||
|
||||
public val CanvasControls: FunctionComponent<CanvasControlsProps> = functionComponent("CanvasControls") { props ->
|
||||
public val CanvasControls: FC<CanvasControlsProps> = fc("CanvasControls") { props ->
|
||||
flexColumn {
|
||||
flexRow {
|
||||
css {
|
||||
|
@ -18,7 +18,7 @@ public external interface TabProps : PropsWithChildren {
|
||||
}
|
||||
|
||||
@JsExport
|
||||
public val Tab: FunctionComponent<TabProps> = functionComponent { props ->
|
||||
public val Tab: FC<TabProps> = fc { props ->
|
||||
props.children()
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ public external interface TabPaneProps : PropsWithChildren {
|
||||
}
|
||||
|
||||
@JsExport
|
||||
public val TabPane: FunctionComponent<TabPaneProps> = functionComponent("TabPane") { props ->
|
||||
public val TabPane: FC<TabPaneProps> = fc("TabPane") { props ->
|
||||
var activeTab: String? by useState(props.activeTab)
|
||||
|
||||
val children: Array<out ReactElement?> = Children.map(props.children) {
|
||||
|
@ -2,11 +2,11 @@ package space.kscience.visionforge.bootstrap
|
||||
|
||||
import kotlinx.css.*
|
||||
import kotlinx.css.properties.border
|
||||
import react.FunctionComponent
|
||||
import react.FC
|
||||
import react.PropsWithChildren
|
||||
import react.RBuilder
|
||||
import react.dom.h2
|
||||
import react.functionComponent
|
||||
import react.fc
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.isEmpty
|
||||
import space.kscience.visionforge.Vision
|
||||
@ -24,7 +24,7 @@ public external interface ThreeControlsProps : PropsWithChildren {
|
||||
}
|
||||
|
||||
@JsExport
|
||||
public val ThreeControls: FunctionComponent<ThreeControlsProps> = functionComponent { props ->
|
||||
public val ThreeControls: FC<ThreeControlsProps> = fc { props ->
|
||||
tabPane(if (props.selected != null) "Properties" else null) {
|
||||
tab("Canvas") {
|
||||
card("Canvas configuration") {
|
||||
|
@ -42,7 +42,7 @@ public external interface MetaViewerProps : Props {
|
||||
public var descriptor: MetaDescriptor?
|
||||
}
|
||||
|
||||
private val MetaViewerItem: FunctionComponent<MetaViewerProps> = functionComponent("MetaViewerItem") { props ->
|
||||
private val MetaViewerItem: FC<MetaViewerProps> = fc("MetaViewerItem") { props ->
|
||||
metaViewerItem(props)
|
||||
}
|
||||
|
||||
@ -127,7 +127,7 @@ private fun RBuilder.metaViewerItem(props: MetaViewerProps) {
|
||||
}
|
||||
|
||||
@JsExport
|
||||
public val MetaViewer: FunctionComponent<MetaViewerProps> = functionComponent("MetaViewer") { props ->
|
||||
public val MetaViewer: FC<MetaViewerProps> = fc("MetaViewer") { props ->
|
||||
child(MetaViewerItem) {
|
||||
attrs {
|
||||
this.key = ""
|
||||
|
@ -76,8 +76,7 @@ public fun RBuilder.nameCrumbs(name: Name?, link: (Name) -> Unit): Unit = styled
|
||||
}
|
||||
|
||||
@JsExport
|
||||
public val ThreeCanvasWithControls: FunctionComponent<ThreeCanvasWithControlsProps> =
|
||||
functionComponent("ThreeViewWithControls") { props ->
|
||||
public val ThreeCanvasWithControls: FC<ThreeCanvasWithControlsProps> = fc("ThreeViewWithControls") { props ->
|
||||
var selected by useState { props.selected }
|
||||
var solid: Solid? by useState(null)
|
||||
|
||||
|
@ -9,12 +9,12 @@ import kotlinx.html.js.onClickFunction
|
||||
import org.w3c.dom.events.Event
|
||||
import org.w3c.files.Blob
|
||||
import org.w3c.files.BlobPropertyBag
|
||||
import react.FunctionComponent
|
||||
import react.FC
|
||||
import react.Props
|
||||
import react.RBuilder
|
||||
import react.dom.attrs
|
||||
import react.dom.button
|
||||
import react.functionComponent
|
||||
import react.fc
|
||||
import ringui.Island
|
||||
import ringui.SmartTabs
|
||||
import ringui.Tab
|
||||
@ -52,7 +52,7 @@ internal external interface CanvasControlsProps : Props {
|
||||
public var vision: Vision?
|
||||
}
|
||||
|
||||
internal val CanvasControls: FunctionComponent<CanvasControlsProps> = functionComponent("CanvasControls") { props ->
|
||||
internal val CanvasControls: FC<CanvasControlsProps> = fc("CanvasControls") { props ->
|
||||
flexColumn {
|
||||
flexRow {
|
||||
css {
|
||||
@ -94,7 +94,7 @@ public external interface ThreeControlsProps : Props {
|
||||
}
|
||||
|
||||
@JsExport
|
||||
public val ThreeControls: FunctionComponent<ThreeControlsProps> = functionComponent { props ->
|
||||
public val ThreeControls: FC<ThreeControlsProps> = fc { props ->
|
||||
SmartTabs("Tree") {
|
||||
props.vision?.let {
|
||||
Tab("Tree") {
|
||||
|
@ -15,7 +15,7 @@ kotlin {
|
||||
commonMain {
|
||||
dependencies {
|
||||
api("space.kscience:dataforge-context:$dataforgeVersion")
|
||||
api("org.jetbrains.kotlinx:kotlinx-html:${ru.mipt.npm.gradle.KScienceVersions.htmlVersion}")
|
||||
api(npmlibs.kotlinx.html)
|
||||
api("org.jetbrains.kotlin-wrappers:kotlin-css")
|
||||
}
|
||||
}
|
||||
|
@ -2,12 +2,9 @@ plugins {
|
||||
id("ru.mipt.npm.gradle.jvm")
|
||||
}
|
||||
|
||||
val ktorVersion = ru.mipt.npm.gradle.KScienceVersions.ktorVersion
|
||||
|
||||
dependencies {
|
||||
api(project(":visionforge-core"))
|
||||
api("io.ktor:ktor-server-cio:$ktorVersion")
|
||||
//api("io.ktor:ktor-server-netty:$ktorVersion")
|
||||
api("io.ktor:ktor-html-builder:$ktorVersion")
|
||||
api("io.ktor:ktor-websockets:$ktorVersion")
|
||||
api(npmlibs.ktor.server.cio)
|
||||
api(npmlibs.ktor.html.builder)
|
||||
api(npmlibs.ktor.websockets)
|
||||
}
|
@ -38,10 +38,7 @@ import space.kscience.visionforge.html.*
|
||||
import space.kscience.visionforge.three.server.VisionServer.Companion.DEFAULT_PAGE
|
||||
import java.awt.Desktop
|
||||
import java.net.URI
|
||||
import kotlin.collections.set
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
import kotlin.time.ExperimentalTime
|
||||
|
||||
|
||||
/**
|
||||
@ -53,10 +50,30 @@ public class VisionServer internal constructor(
|
||||
private val rootRoute: String,
|
||||
) : Configurable, CoroutineScope by application {
|
||||
override val meta: ObservableMutableMeta = MutableMeta()
|
||||
|
||||
/**
|
||||
* Update minimal interval between updates in milliseconds (if there are no updates, push will not happen
|
||||
*/
|
||||
public var updateInterval: Long by meta.long(300, key = UPDATE_INTERVAL_KEY)
|
||||
|
||||
/**
|
||||
* Cache page fragments. If false, pages will be reconstructed on each call. Default: `true`
|
||||
*/
|
||||
public var cacheFragments: Boolean by meta.boolean(true)
|
||||
|
||||
/**
|
||||
* Embed the initial state of the vision inside its html tag. Default: `true`
|
||||
*/
|
||||
public var dataEmbed: Boolean by meta.boolean(true, Name.parse("data.embed"))
|
||||
|
||||
/**
|
||||
* Fetch data on vision load. Overrides embedded data. Default: `false`
|
||||
*/
|
||||
public var dataFetch: Boolean by meta.boolean(false, Name.parse("data.fetch"))
|
||||
|
||||
/**
|
||||
* Connect to server to get pushes. The address of the server is embedded in the tag. Default: `true`
|
||||
*/
|
||||
public var dataConnect: Boolean by meta.boolean(true, Name.parse("data.connect"))
|
||||
|
||||
/**
|
||||
@ -64,6 +81,9 @@ public class VisionServer internal constructor(
|
||||
*/
|
||||
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)
|
||||
}
|
||||
@ -102,7 +122,7 @@ public class VisionServer internal constructor(
|
||||
* Server a map of visions without providing explicit html page for them
|
||||
*/
|
||||
@OptIn(DFExperimental::class)
|
||||
public fun serveVisions(route: Route, visions: Map<Name, Vision>): Unit = route {
|
||||
internal fun serveVisions(route: Route, visions: Map<Name, Vision>): Unit = route {
|
||||
application.log.info("Serving visions $visions at $route")
|
||||
|
||||
//Update websocket
|
||||
@ -158,7 +178,12 @@ public class VisionServer internal constructor(
|
||||
* Create a static html page and serve visions produced in the process
|
||||
*/
|
||||
@DFExperimental
|
||||
public fun createHtmlAndServe(route: String, title: String, headers: List<HtmlFragment>, visionFragment: HtmlVisionFragment): String{
|
||||
public fun createHtmlAndServe(
|
||||
route: String,
|
||||
title: String,
|
||||
headers: List<HtmlFragment>,
|
||||
visionFragment: HtmlVisionFragment,
|
||||
): String {
|
||||
val htmlString = createHTML().apply {
|
||||
html {
|
||||
visionPage(title, headers, visionFragment).also {
|
||||
@ -171,7 +196,7 @@ public class VisionServer internal constructor(
|
||||
}
|
||||
|
||||
/**
|
||||
* Serv visions in a given [route] without providing a page template
|
||||
* Serve visions in a given [route] without providing a page template
|
||||
*/
|
||||
public fun serveVisions(route: String, visions: Map<Name, Vision>): Unit {
|
||||
application.routing {
|
||||
@ -245,6 +270,9 @@ public inline fun VisionServer.useScript(src: String, crossinline block: SCRIPT.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
@ -256,7 +284,7 @@ public inline fun VisionServer.useCss(href: String, crossinline block: LINK.() -
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach plotly application to given server
|
||||
* Attach VisionForge server application to given server
|
||||
*/
|
||||
public fun Application.visionServer(context: Context, route: String = DEFAULT_PAGE): VisionServer {
|
||||
if (featureOrNull(WebSockets) == null) {
|
||||
@ -286,6 +314,9 @@ public fun Application.visionServer(context: Context, route: String = DEFAULT_PA
|
||||
return VisionServer(visionManager, this, route)
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a stand-alone VisionForge server at given host/port
|
||||
*/
|
||||
public fun VisionManager.serve(
|
||||
host: String = "localhost",
|
||||
port: Int = 7777,
|
||||
@ -294,10 +325,16 @@ public fun VisionManager.serve(
|
||||
visionServer(context).apply(block)
|
||||
}.start()
|
||||
|
||||
public fun ApplicationEngine.show() {
|
||||
/**
|
||||
* Connect to a given Ktor server using browser
|
||||
*/
|
||||
public fun ApplicationEngine.openInBrowser() {
|
||||
val connector = environment.connectors.first()
|
||||
val uri = URI("http", null, connector.host, connector.port, null, null, null)
|
||||
Desktop.getDesktop().browse(uri)
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the server with default timeouts
|
||||
*/
|
||||
public fun ApplicationEngine.close(): Unit = stop(1000, 5000)
|
Loading…
Reference in New Issue
Block a user