forked from kscience/visionforge
Refactor pages
This commit is contained in:
parent
960d17855b
commit
3c51060e2e
@ -6,6 +6,7 @@
|
|||||||
- MeshLine for thick lines
|
- MeshLine for thick lines
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
- API update for server and pages
|
||||||
- Edges moved to solids module for easier construction
|
- Edges moved to solids module for easier construction
|
||||||
- Visions **must** be rooted in order to subscribe to updates.
|
- Visions **must** be rooted in order to subscribe to updates.
|
||||||
- Visions use flows instead of direct subscriptions.
|
- Visions use flows instead of direct subscriptions.
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
import space.kscience.gradle.isInDevelopment
|
||||||
|
import space.kscience.gradle.useApache2Licence
|
||||||
|
import space.kscience.gradle.useSPCTeam
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("space.kscience.gradle.project")
|
id("space.kscience.gradle.project")
|
||||||
// id("org.jetbrains.kotlinx.kover") version "0.5.0"
|
// id("org.jetbrains.kotlinx.kover") version "0.5.0"
|
||||||
@ -6,9 +10,9 @@ plugins {
|
|||||||
val dataforgeVersion by extra("0.6.0-dev-15")
|
val dataforgeVersion by extra("0.6.0-dev-15")
|
||||||
val fxVersion by extra("11")
|
val fxVersion by extra("11")
|
||||||
|
|
||||||
allprojects{
|
allprojects {
|
||||||
group = "space.kscience"
|
group = "space.kscience"
|
||||||
version = "0.3.0-dev-2"
|
version = "0.3.0-dev-3"
|
||||||
}
|
}
|
||||||
|
|
||||||
subprojects {
|
subprojects {
|
||||||
@ -21,16 +25,26 @@ subprojects {
|
|||||||
maven("https://maven.jzy3d.org/releases")
|
maven("https://maven.jzy3d.org/releases")
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>{
|
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
|
||||||
kotlinOptions{
|
kotlinOptions {
|
||||||
freeCompilerArgs = freeCompilerArgs + "-Xcontext-receivers"
|
freeCompilerArgs = freeCompilerArgs + "-Xcontext-receivers"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ksciencePublish {
|
ksciencePublish {
|
||||||
github("visionforge")
|
pom("https://github.com/SciProgCentre/visionforge") {
|
||||||
space()
|
useApache2Licence()
|
||||||
|
useSPCTeam()
|
||||||
|
}
|
||||||
|
github(githubProject = "visionforge", githubOrg = "SciProgCentre")
|
||||||
|
space(
|
||||||
|
if (isInDevelopment) {
|
||||||
|
"https://maven.pkg.jetbrains.space/spc/p/sci/dev"
|
||||||
|
} else {
|
||||||
|
"https://maven.pkg.jetbrains.space/spc/p/sci/maven"
|
||||||
|
}
|
||||||
|
)
|
||||||
sonatype()
|
sonatype()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,8 +53,3 @@ apiValidation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
readme.readmeTemplate = file("docs/templates/README-TEMPLATE.md")
|
readme.readmeTemplate = file("docs/templates/README-TEMPLATE.md")
|
||||||
|
|
||||||
|
|
||||||
//rootProject.extensions.configure<org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension> {
|
|
||||||
// versions.webpackCli.version = "4.10.0"
|
|
||||||
//}
|
|
@ -218,6 +218,7 @@ private object RootDecoder {
|
|||||||
|
|
||||||
var value: Any? = null
|
var value: Any? = null
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
fun <T> getOrPutValue(builder: (JsonElement) -> T): T {
|
fun <T> getOrPutValue(builder: (JsonElement) -> T): T {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
value = builder(element)
|
value = builder(element)
|
||||||
|
@ -4,7 +4,7 @@ import kotlinx.html.*
|
|||||||
import space.kscience.dataforge.context.Global
|
import space.kscience.dataforge.context.Global
|
||||||
import space.kscience.dataforge.context.fetch
|
import space.kscience.dataforge.context.fetch
|
||||||
import space.kscience.visionforge.VisionManager
|
import space.kscience.visionforge.VisionManager
|
||||||
import space.kscience.visionforge.html.Page
|
import space.kscience.visionforge.html.VisionPage
|
||||||
import space.kscience.visionforge.html.formFragment
|
import space.kscience.visionforge.html.formFragment
|
||||||
import space.kscience.visionforge.onPropertyChange
|
import space.kscience.visionforge.onPropertyChange
|
||||||
import space.kscience.visionforge.server.close
|
import space.kscience.visionforge.server.close
|
||||||
@ -15,7 +15,7 @@ fun main() {
|
|||||||
val visionManager = Global.fetch(VisionManager)
|
val visionManager = Global.fetch(VisionManager)
|
||||||
|
|
||||||
val server = visionManager.serve {
|
val server = visionManager.serve {
|
||||||
page(header = Page.scriptHeader("js/visionforge-playground.js")) {
|
page(VisionPage.scriptHeader("js/visionforge-playground.js")) {
|
||||||
val form = formFragment("form") {
|
val form = formFragment("form") {
|
||||||
label {
|
label {
|
||||||
htmlFor = "fname"
|
htmlFor = "fname"
|
||||||
|
@ -1,15 +1,92 @@
|
|||||||
package space.kscience.visionforge.examples
|
package space.kscience.visionforge.examples
|
||||||
|
|
||||||
import space.kscience.plotly.scatter
|
import space.kscience.dataforge.meta.Value
|
||||||
|
import space.kscience.plotly.layout
|
||||||
|
import space.kscience.plotly.models.*
|
||||||
import space.kscience.visionforge.html.ResourceLocation
|
import space.kscience.visionforge.html.ResourceLocation
|
||||||
import space.kscience.visionforge.plotly.plotly
|
import space.kscience.visionforge.plotly.plotly
|
||||||
|
|
||||||
fun main() = makeVisionFile(resourceLocation = ResourceLocation.SYSTEM) {
|
fun main() = makeVisionFile(resourceLocation = ResourceLocation.SYSTEM) {
|
||||||
vision {
|
vision {
|
||||||
|
val trace1 = Violin {
|
||||||
|
text("sample length: 32")
|
||||||
|
marker {
|
||||||
|
line {
|
||||||
|
width = 2
|
||||||
|
color("#bebada")
|
||||||
|
}
|
||||||
|
symbol = Symbol.valueOf("line-ns")
|
||||||
|
}
|
||||||
|
orientation = Orientation.h
|
||||||
|
hoveron = ViolinHoveron.`points+kde`
|
||||||
|
meanline {
|
||||||
|
visible = true
|
||||||
|
}
|
||||||
|
legendgroup = "F"
|
||||||
|
scalegroup = "F"
|
||||||
|
points = ViolinPoints.all
|
||||||
|
pointpos = 1.2
|
||||||
|
jitter = 0
|
||||||
|
box {
|
||||||
|
visible = true
|
||||||
|
}
|
||||||
|
scalemode = ViolinScaleMode.count
|
||||||
|
showlegend = false
|
||||||
|
side = ViolinSide.positive
|
||||||
|
y0 = Value.of(0)
|
||||||
|
line {
|
||||||
|
color("#bebada")
|
||||||
|
}
|
||||||
|
name = "F"
|
||||||
|
|
||||||
|
x(10.07, 34.83, 10.65, 12.43, 24.08, 13.42, 12.48, 29.8, 14.52, 11.38,
|
||||||
|
20.27, 11.17, 12.26, 18.26, 8.51, 10.33, 14.15, 13.16, 17.47, 27.05, 16.43,
|
||||||
|
8.35, 18.64, 11.87, 19.81, 43.11, 13.0, 12.74, 13.0, 16.4, 16.47, 18.78)
|
||||||
|
}
|
||||||
|
|
||||||
|
val trace2 = Violin {
|
||||||
|
text("sample length: 32")
|
||||||
|
marker {
|
||||||
|
line {
|
||||||
|
width = 2
|
||||||
|
color("#8dd3c7")
|
||||||
|
}
|
||||||
|
symbol = Symbol.valueOf("line-ns")
|
||||||
|
}
|
||||||
|
orientation = Orientation.h
|
||||||
|
hoveron = ViolinHoveron.`points+kde`
|
||||||
|
meanline {
|
||||||
|
visible = true
|
||||||
|
}
|
||||||
|
legendgroup = "M"
|
||||||
|
scalegroup = "M"
|
||||||
|
points = ViolinPoints.all
|
||||||
|
pointpos = -1.2
|
||||||
|
jitter = 0
|
||||||
|
box {
|
||||||
|
visible = true
|
||||||
|
}
|
||||||
|
scalemode = ViolinScaleMode.count
|
||||||
|
showlegend = false
|
||||||
|
side = ViolinSide.negative
|
||||||
|
y0 = Value.of(0)
|
||||||
|
|
||||||
|
line {
|
||||||
|
color("#8dd3c7")
|
||||||
|
}
|
||||||
|
name = "M"
|
||||||
|
|
||||||
|
x(27.2, 22.76, 17.29, 19.44, 16.66, 32.68, 15.98, 13.03, 18.28, 24.71,
|
||||||
|
21.16, 11.69, 14.26, 15.95, 8.52, 22.82, 19.08, 16.0, 34.3, 41.19, 9.78,
|
||||||
|
7.51, 28.44, 15.48, 16.58, 7.56, 10.34, 13.51, 18.71, 20.53)
|
||||||
|
}
|
||||||
|
|
||||||
plotly {
|
plotly {
|
||||||
scatter {
|
traces(trace1, trace2)
|
||||||
x(1, 2, 3)
|
layout {
|
||||||
y(5, 8, 7)
|
width = 800
|
||||||
|
height = 800
|
||||||
|
title = "Advanced Violin Plot"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ package space.kscience.visionforge.examples
|
|||||||
|
|
||||||
import space.kscience.dataforge.context.Global
|
import space.kscience.dataforge.context.Global
|
||||||
import space.kscience.visionforge.html.HtmlVisionFragment
|
import space.kscience.visionforge.html.HtmlVisionFragment
|
||||||
import space.kscience.visionforge.html.Page
|
|
||||||
import space.kscience.visionforge.html.ResourceLocation
|
import space.kscience.visionforge.html.ResourceLocation
|
||||||
|
import space.kscience.visionforge.html.VisionPage
|
||||||
import space.kscience.visionforge.html.importScriptHeader
|
import space.kscience.visionforge.html.importScriptHeader
|
||||||
import space.kscience.visionforge.makeFile
|
import space.kscience.visionforge.makeFile
|
||||||
import java.awt.Desktop
|
import java.awt.Desktop
|
||||||
@ -16,10 +16,10 @@ public fun makeVisionFile(
|
|||||||
show: Boolean = true,
|
show: Boolean = true,
|
||||||
content: HtmlVisionFragment,
|
content: HtmlVisionFragment,
|
||||||
): Unit {
|
): Unit {
|
||||||
val actualPath = Page(Global, content = content).makeFile(path) { actualPath ->
|
val actualPath = VisionPage(Global, content = content).makeFile(path) { actualPath ->
|
||||||
mapOf(
|
mapOf(
|
||||||
"title" to Page.title(title),
|
"title" to VisionPage.title(title),
|
||||||
"playground" to Page.importScriptHeader("js/visionforge-playground.js", resourceLocation, actualPath),
|
"playground" to VisionPage.importScriptHeader("js/visionforge-playground.js", resourceLocation, actualPath),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (show) Desktop.getDesktop().browse(actualPath.toFile().toURI())
|
if (show) Desktop.getDesktop().browse(actualPath.toFile().toURI())
|
||||||
|
@ -10,8 +10,8 @@ import space.kscience.dataforge.meta.Null
|
|||||||
import space.kscience.dataforge.misc.DFExperimental
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
import space.kscience.visionforge.Colors
|
import space.kscience.visionforge.Colors
|
||||||
import space.kscience.visionforge.html.Page
|
import space.kscience.visionforge.html.VisionPage
|
||||||
import space.kscience.visionforge.html.plus
|
import space.kscience.visionforge.server.DataServeMode
|
||||||
import space.kscience.visionforge.server.close
|
import space.kscience.visionforge.server.close
|
||||||
import space.kscience.visionforge.server.openInBrowser
|
import space.kscience.visionforge.server.openInBrowser
|
||||||
import space.kscience.visionforge.server.serve
|
import space.kscience.visionforge.server.serve
|
||||||
@ -37,7 +37,8 @@ fun main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val server = satContext.visionManager.serve {
|
val server = satContext.visionManager.serve {
|
||||||
page(header = Page.threeJsHeader + Page.styleSheetHeader("css/styles.css")) {
|
dataMode = DataServeMode.UPDATE
|
||||||
|
page(VisionPage.threeJsHeader, VisionPage.styleSheetHeader("css/styles.css")) {
|
||||||
div("flex-column") {
|
div("flex-column") {
|
||||||
h1 { +"Satellite detector demo" }
|
h1 { +"Satellite detector demo" }
|
||||||
vision { sat }
|
vision { sat }
|
||||||
|
@ -6,4 +6,4 @@ kotlin.incremental.js.ir=true
|
|||||||
org.gradle.parallel=true
|
org.gradle.parallel=true
|
||||||
org.gradle.jvmargs=-Xmx4G
|
org.gradle.jvmargs=-Xmx4G
|
||||||
|
|
||||||
toolsVersion=0.12.0-kotlin-1.7.20-Beta
|
toolsVersion=0.13.3-kotlin-1.7.20
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,5 +1,5 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
@ -49,7 +49,7 @@ public abstract class JupyterPluginBase(final override val context: Context) : J
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render<Page> { page ->
|
render<VisionPage> { page ->
|
||||||
HTML(page.render(createHTML()), true)
|
HTML(page.render(createHTML()), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import kotlinx.css.padding
|
|||||||
import kotlinx.css.properties.border
|
import kotlinx.css.properties.border
|
||||||
import kotlinx.css.px
|
import kotlinx.css.px
|
||||||
import kotlinx.html.js.onClickFunction
|
import kotlinx.html.js.onClickFunction
|
||||||
import org.w3c.dom.events.Event
|
import kotlinx.html.org.w3c.dom.events.Event
|
||||||
import org.w3c.files.Blob
|
import org.w3c.files.Blob
|
||||||
import org.w3c.files.BlobPropertyBag
|
import org.w3c.files.BlobPropertyBag
|
||||||
import react.FC
|
import react.FC
|
||||||
|
@ -3,7 +3,6 @@ package space.kscience.visionforge.bootstrap
|
|||||||
import org.w3c.dom.Element
|
import org.w3c.dom.Element
|
||||||
import react.RBuilder
|
import react.RBuilder
|
||||||
import react.dom.client.createRoot
|
import react.dom.client.createRoot
|
||||||
import react.key
|
|
||||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||||
import space.kscience.dataforge.meta.isEmpty
|
import space.kscience.dataforge.meta.isEmpty
|
||||||
import space.kscience.visionforge.Vision
|
import space.kscience.visionforge.Vision
|
||||||
|
@ -3,7 +3,7 @@ package space.kscience.visionforge.react
|
|||||||
import kotlinx.css.Align
|
import kotlinx.css.Align
|
||||||
import kotlinx.css.alignItems
|
import kotlinx.css.alignItems
|
||||||
import kotlinx.html.js.onClickFunction
|
import kotlinx.html.js.onClickFunction
|
||||||
import org.w3c.dom.events.Event
|
import kotlinx.html.org.w3c.dom.events.Event
|
||||||
import react.*
|
import react.*
|
||||||
import react.dom.a
|
import react.dom.a
|
||||||
import react.dom.attrs
|
import react.dom.attrs
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package space.kscience.visionforge.react
|
package space.kscience.visionforge.react
|
||||||
|
|
||||||
import kotlinx.html.js.onChangeFunction
|
import kotlinx.html.js.onChangeFunction
|
||||||
|
import kotlinx.html.org.w3c.dom.events.Event
|
||||||
import org.w3c.dom.HTMLOptionElement
|
import org.w3c.dom.HTMLOptionElement
|
||||||
import org.w3c.dom.HTMLSelectElement
|
import org.w3c.dom.HTMLSelectElement
|
||||||
import org.w3c.dom.asList
|
import org.w3c.dom.asList
|
||||||
import org.w3c.dom.events.Event
|
|
||||||
import react.FC
|
import react.FC
|
||||||
import react.dom.attrs
|
import react.dom.attrs
|
||||||
import react.dom.option
|
import react.dom.option
|
||||||
|
@ -10,7 +10,7 @@ import kotlinx.coroutines.launch
|
|||||||
import kotlinx.css.*
|
import kotlinx.css.*
|
||||||
import kotlinx.css.properties.TextDecoration
|
import kotlinx.css.properties.TextDecoration
|
||||||
import kotlinx.html.js.onClickFunction
|
import kotlinx.html.js.onClickFunction
|
||||||
import org.w3c.dom.events.Event
|
import kotlinx.html.org.w3c.dom.events.Event
|
||||||
import react.*
|
import react.*
|
||||||
import react.dom.attrs
|
import react.dom.attrs
|
||||||
import space.kscience.dataforge.meta.*
|
import space.kscience.dataforge.meta.*
|
||||||
|
@ -4,8 +4,8 @@ import kotlinx.css.pct
|
|||||||
import kotlinx.css.width
|
import kotlinx.css.width
|
||||||
import kotlinx.html.InputType
|
import kotlinx.html.InputType
|
||||||
import kotlinx.html.js.onChangeFunction
|
import kotlinx.html.js.onChangeFunction
|
||||||
|
import kotlinx.html.org.w3c.dom.events.Event
|
||||||
import org.w3c.dom.HTMLInputElement
|
import org.w3c.dom.HTMLInputElement
|
||||||
import org.w3c.dom.events.Event
|
|
||||||
import react.FC
|
import react.FC
|
||||||
import react.dom.attrs
|
import react.dom.attrs
|
||||||
import react.fc
|
import react.fc
|
||||||
|
@ -7,7 +7,7 @@ import kotlinx.css.cursor
|
|||||||
import kotlinx.css.properties.TextDecorationLine
|
import kotlinx.css.properties.TextDecorationLine
|
||||||
import kotlinx.css.properties.textDecoration
|
import kotlinx.css.properties.textDecoration
|
||||||
import kotlinx.html.js.onClickFunction
|
import kotlinx.html.js.onClickFunction
|
||||||
import org.w3c.dom.events.Event
|
import kotlinx.html.org.w3c.dom.events.Event
|
||||||
import react.*
|
import react.*
|
||||||
import react.dom.attrs
|
import react.dom.attrs
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
|
@ -7,9 +7,9 @@ import kotlinx.css.width
|
|||||||
import kotlinx.html.InputType
|
import kotlinx.html.InputType
|
||||||
import kotlinx.html.js.onChangeFunction
|
import kotlinx.html.js.onChangeFunction
|
||||||
import kotlinx.html.js.onKeyDownFunction
|
import kotlinx.html.js.onKeyDownFunction
|
||||||
|
import kotlinx.html.org.w3c.dom.events.Event
|
||||||
import org.w3c.dom.HTMLInputElement
|
import org.w3c.dom.HTMLInputElement
|
||||||
import org.w3c.dom.HTMLSelectElement
|
import org.w3c.dom.HTMLSelectElement
|
||||||
import org.w3c.dom.events.Event
|
|
||||||
import react.FC
|
import react.FC
|
||||||
import react.Props
|
import react.Props
|
||||||
import react.dom.attrs
|
import react.dom.attrs
|
||||||
|
@ -4,7 +4,6 @@ import org.w3c.dom.Element
|
|||||||
import react.RBuilder
|
import react.RBuilder
|
||||||
import react.dom.client.createRoot
|
import react.dom.client.createRoot
|
||||||
import react.dom.p
|
import react.dom.p
|
||||||
import react.key
|
|
||||||
import ringui.Island
|
import ringui.Island
|
||||||
import ringui.SmartTabs
|
import ringui.SmartTabs
|
||||||
import ringui.Tab
|
import ringui.Tab
|
||||||
|
@ -8,7 +8,7 @@ import kotlinx.css.padding
|
|||||||
import kotlinx.css.properties.border
|
import kotlinx.css.properties.border
|
||||||
import kotlinx.css.px
|
import kotlinx.css.px
|
||||||
import kotlinx.html.js.onClickFunction
|
import kotlinx.html.js.onClickFunction
|
||||||
import org.w3c.dom.events.Event
|
import kotlinx.html.org.w3c.dom.events.Event
|
||||||
import org.w3c.files.Blob
|
import org.w3c.files.Blob
|
||||||
import org.w3c.files.BlobPropertyBag
|
import org.w3c.files.BlobPropertyBag
|
||||||
import react.FC
|
import react.FC
|
||||||
|
@ -9,7 +9,7 @@ kotlin {
|
|||||||
commonMain {
|
commonMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
api("space.kscience:dataforge-context:$dataforgeVersion")
|
api("space.kscience:dataforge-context:$dataforgeVersion")
|
||||||
api(npmlibs.kotlinx.html)
|
api("org.jetbrains.kotlinx:kotlinx-html:0.8.0")
|
||||||
api("org.jetbrains.kotlin-wrappers:kotlin-css")
|
api("org.jetbrains.kotlin-wrappers:kotlin-css")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,14 +118,14 @@ private fun CoroutineScope.collectChange(
|
|||||||
name: Name,
|
name: Name,
|
||||||
source: Vision,
|
source: Vision,
|
||||||
mutex: Mutex,
|
mutex: Mutex,
|
||||||
collector: () -> VisionChangeBuilder,
|
collector: VisionChangeBuilder,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
//Collect properties change
|
//Collect properties change
|
||||||
source.onPropertyChange(this) { propertyName ->
|
source.properties.changes.onEach { propertyName ->
|
||||||
val newItem = source.properties.own?.get(propertyName)
|
val newItem = source.properties.own?.get(propertyName)
|
||||||
collector().propertyChanged(name, propertyName, newItem)
|
collector.propertyChanged(name, propertyName, newItem)
|
||||||
}
|
}.launchIn(this)
|
||||||
|
|
||||||
val children = source.children
|
val children = source.children
|
||||||
//Subscribe for children changes
|
//Subscribe for children changes
|
||||||
@ -141,7 +141,7 @@ private fun CoroutineScope.collectChange(
|
|||||||
collectChange(fullName, after, mutex, collector)
|
collectChange(fullName, after, mutex, collector)
|
||||||
}
|
}
|
||||||
mutex.withLock {
|
mutex.withLock {
|
||||||
collector().setChild(fullName, after)
|
collector.setChild(fullName, after)
|
||||||
}
|
}
|
||||||
}?.launchIn(this)
|
}?.launchIn(this)
|
||||||
}
|
}
|
||||||
@ -156,7 +156,7 @@ public fun Vision.flowChanges(
|
|||||||
coroutineScope {
|
coroutineScope {
|
||||||
val collector = VisionChangeBuilder()
|
val collector = VisionChangeBuilder()
|
||||||
val mutex = Mutex()
|
val mutex = Mutex()
|
||||||
collectChange(Name.EMPTY, this@flowChanges, mutex) { collector }
|
collectChange(Name.EMPTY, this@flowChanges, mutex, collector)
|
||||||
|
|
||||||
//Send initial vision state
|
//Send initial vision state
|
||||||
val initialChange = VisionChange(vision = deepCopy(manager))
|
val initialChange = VisionChange(vision = deepCopy(manager))
|
||||||
@ -167,10 +167,10 @@ public fun Vision.flowChanges(
|
|||||||
delay(collectionDuration)
|
delay(collectionDuration)
|
||||||
//Propagate updates only if something is changed
|
//Propagate updates only if something is changed
|
||||||
if (!collector.isEmpty()) {
|
if (!collector.isEmpty()) {
|
||||||
|
mutex.withLock {
|
||||||
//emit changes
|
//emit changes
|
||||||
emit(collector.deepCopy(manager))
|
emit(collector.deepCopy(manager))
|
||||||
//Reset the collector
|
//Reset the collector
|
||||||
mutex.withLock {
|
|
||||||
collector.reset()
|
collector.reset()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,14 @@ package space.kscience.visionforge.html
|
|||||||
import kotlinx.html.*
|
import kotlinx.html.*
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.context.Context
|
||||||
|
|
||||||
public data class Page(
|
/**
|
||||||
|
* A structure representing a single page with Visions to be rendered.
|
||||||
|
*
|
||||||
|
* @param pageHeaders static headers for this page.
|
||||||
|
*/
|
||||||
|
public data class VisionPage(
|
||||||
public val context: Context,
|
public val context: Context,
|
||||||
public val headers: Map<String, HtmlFragment> = emptyMap(),
|
public val pageHeaders: Map<String, HtmlFragment> = emptyMap(),
|
||||||
public val content: HtmlVisionFragment,
|
public val content: HtmlVisionFragment,
|
||||||
) {
|
) {
|
||||||
public fun <R> render(root: TagConsumer<R>): R = root.apply {
|
public fun <R> render(root: TagConsumer<R>): R = root.apply {
|
||||||
@ -13,7 +18,7 @@ public data class Page(
|
|||||||
meta {
|
meta {
|
||||||
charset = "utf-8"
|
charset = "utf-8"
|
||||||
}
|
}
|
||||||
headers.values.forEach {
|
pageHeaders.values.forEach {
|
||||||
fragment(it)
|
fragment(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -114,7 +114,7 @@ internal fun fileCssHeader(
|
|||||||
/**
|
/**
|
||||||
* Make a script header from a resource file, automatically copying file to appropriate location
|
* Make a script header from a resource file, automatically copying file to appropriate location
|
||||||
*/
|
*/
|
||||||
public fun Page.Companion.importScriptHeader(
|
public fun VisionPage.Companion.importScriptHeader(
|
||||||
scriptResource: String,
|
scriptResource: String,
|
||||||
resourceLocation: ResourceLocation,
|
resourceLocation: ResourceLocation,
|
||||||
htmlPath: Path? = null,
|
htmlPath: Path? = null,
|
||||||
|
@ -3,7 +3,7 @@ package space.kscience.visionforge
|
|||||||
import kotlinx.html.stream.createHTML
|
import kotlinx.html.stream.createHTML
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
import space.kscience.visionforge.html.HtmlFragment
|
import space.kscience.visionforge.html.HtmlFragment
|
||||||
import space.kscience.visionforge.html.Page
|
import space.kscience.visionforge.html.VisionPage
|
||||||
import java.awt.Desktop
|
import java.awt.Desktop
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
@ -54,8 +54,11 @@ import java.nio.file.Path
|
|||||||
// }
|
// }
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export a [VisionPage] to a file
|
||||||
|
*/
|
||||||
@DFExperimental
|
@DFExperimental
|
||||||
public fun Page.makeFile(
|
public fun VisionPage.makeFile(
|
||||||
path: Path?,
|
path: Path?,
|
||||||
defaultHeaders: ((Path) -> Map<String, HtmlFragment>)? = null,
|
defaultHeaders: ((Path) -> Map<String, HtmlFragment>)? = null,
|
||||||
): Path {
|
): Path {
|
||||||
@ -64,7 +67,7 @@ public fun Page.makeFile(
|
|||||||
} ?: Files.createTempFile("tempPlot", ".html")
|
} ?: Files.createTempFile("tempPlot", ".html")
|
||||||
|
|
||||||
val actualDefaultHeaders = defaultHeaders?.invoke(actualFile)
|
val actualDefaultHeaders = defaultHeaders?.invoke(actualFile)
|
||||||
val actualPage = if (actualDefaultHeaders == null) this else copy(headers = actualDefaultHeaders + headers)
|
val actualPage = if (actualDefaultHeaders == null) this else copy(pageHeaders = actualDefaultHeaders + pageHeaders)
|
||||||
|
|
||||||
val htmlString = actualPage.render(createHTML())
|
val htmlString = actualPage.render(createHTML())
|
||||||
|
|
||||||
@ -73,7 +76,7 @@ public fun Page.makeFile(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@DFExperimental
|
@DFExperimental
|
||||||
public fun Page.show(path: Path? = null) {
|
public fun VisionPage.show(path: Path? = null) {
|
||||||
val actualPath = makeFile(path)
|
val actualPath = makeFile(path)
|
||||||
Desktop.getDesktop().browse(actualPath.toFile().toURI())
|
Desktop.getDesktop().browse(actualPath.toFile().toURI())
|
||||||
}
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package space.kscience.visionforge.markup
|
||||||
|
|
||||||
|
import space.kscience.visionforge.VisionPlugin
|
||||||
|
|
||||||
|
public expect class MarkupPlugin: VisionPlugin
|
@ -16,7 +16,7 @@ import space.kscience.visionforge.markup.VisionOfMarkup.Companion.COMMONMARK_FOR
|
|||||||
import space.kscience.visionforge.markup.VisionOfMarkup.Companion.GFM_FORMAT
|
import space.kscience.visionforge.markup.VisionOfMarkup.Companion.GFM_FORMAT
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
public class MarkupPlugin : VisionPlugin(), ElementVisionRenderer {
|
public actual class MarkupPlugin : VisionPlugin(), ElementVisionRenderer {
|
||||||
public val visionClient: VisionClient by require(VisionClient)
|
public val visionClient: VisionClient by require(VisionClient)
|
||||||
override val tag: PluginTag get() = Companion.tag
|
override val tag: PluginTag get() = Companion.tag
|
||||||
override val visionSerializersModule: SerializersModule get() = markupSerializersModule
|
override val visionSerializersModule: SerializersModule get() = markupSerializersModule
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
package space.kscience.visionforge.markup
|
||||||
|
|
||||||
|
import kotlinx.serialization.modules.SerializersModule
|
||||||
|
import space.kscience.dataforge.context.Context
|
||||||
|
import space.kscience.dataforge.context.PluginFactory
|
||||||
|
import space.kscience.dataforge.context.PluginTag
|
||||||
|
import space.kscience.dataforge.meta.Meta
|
||||||
|
import space.kscience.visionforge.VisionPlugin
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
public actual class MarkupPlugin : VisionPlugin() {
|
||||||
|
override val visionSerializersModule: SerializersModule get() = markupSerializersModule
|
||||||
|
|
||||||
|
override val tag: PluginTag get() = Companion.tag
|
||||||
|
|
||||||
|
public companion object : PluginFactory<MarkupPlugin> {
|
||||||
|
override val tag: PluginTag = PluginTag("vision.plotly", PluginTag.DATAFORGE_GROUP)
|
||||||
|
|
||||||
|
override val type: KClass<out MarkupPlugin> = MarkupPlugin::class
|
||||||
|
|
||||||
|
override fun build(context: Context, meta: Meta): MarkupPlugin = MarkupPlugin()
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,10 @@
|
|||||||
package space.kscience.visionforge.server
|
package space.kscience.visionforge.server
|
||||||
|
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.Application
|
||||||
|
import io.ktor.server.application.call
|
||||||
|
import io.ktor.server.application.install
|
||||||
|
import io.ktor.server.application.log
|
||||||
import io.ktor.server.cio.CIO
|
import io.ktor.server.cio.CIO
|
||||||
import io.ktor.server.engine.ApplicationEngine
|
import io.ktor.server.engine.ApplicationEngine
|
||||||
import io.ktor.server.engine.embeddedServer
|
import io.ktor.server.engine.embeddedServer
|
||||||
@ -24,7 +27,6 @@ import kotlinx.coroutines.withContext
|
|||||||
import kotlinx.html.*
|
import kotlinx.html.*
|
||||||
import kotlinx.html.stream.createHTML
|
import kotlinx.html.stream.createHTML
|
||||||
import space.kscience.dataforge.meta.*
|
import space.kscience.dataforge.meta.*
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
|
||||||
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.VisionChange
|
import space.kscience.visionforge.VisionChange
|
||||||
@ -32,6 +34,7 @@ import space.kscience.visionforge.VisionManager
|
|||||||
import space.kscience.visionforge.flowChanges
|
import space.kscience.visionforge.flowChanges
|
||||||
import space.kscience.visionforge.html.HtmlFragment
|
import space.kscience.visionforge.html.HtmlFragment
|
||||||
import space.kscience.visionforge.html.HtmlVisionFragment
|
import space.kscience.visionforge.html.HtmlVisionFragment
|
||||||
|
import space.kscience.visionforge.html.VisionPage
|
||||||
import space.kscience.visionforge.html.visionFragment
|
import space.kscience.visionforge.html.visionFragment
|
||||||
import space.kscience.visionforge.server.VisionServer.Companion.DEFAULT_PAGE
|
import space.kscience.visionforge.server.VisionServer.Companion.DEFAULT_PAGE
|
||||||
import java.awt.Desktop
|
import java.awt.Desktop
|
||||||
@ -39,6 +42,23 @@ import java.net.URI
|
|||||||
import kotlin.time.Duration.Companion.milliseconds
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
|
||||||
|
|
||||||
|
public enum class DataServeMode {
|
||||||
|
/**
|
||||||
|
* Embed the initial state of the vision inside its html tag.
|
||||||
|
*/
|
||||||
|
EMBED,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch data on vision load. Do not embed data.
|
||||||
|
*/
|
||||||
|
FETCH,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect to server to get pushes. The address of the server is embedded in the tag.
|
||||||
|
*/
|
||||||
|
UPDATE
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A ktor plugin container with given [routing]
|
* A ktor plugin container with given [routing]
|
||||||
* @param serverUrl a server url including root route
|
* @param serverUrl a server url including root route
|
||||||
@ -63,25 +83,20 @@ public class VisionServer internal constructor(
|
|||||||
*/
|
*/
|
||||||
public var cacheFragments: Boolean by meta.boolean(true)
|
public var cacheFragments: Boolean by meta.boolean(true)
|
||||||
|
|
||||||
/**
|
public var dataMode: DataServeMode by meta.enum(DataServeMode.UPDATE)
|
||||||
* Embed the initial state of the vision inside its html tag. Default: `true`
|
|
||||||
*/
|
private val serverHeaders: MutableMap<String, HtmlFragment> = mutableMapOf()
|
||||||
public var dataEmbed: Boolean by meta.boolean(true, Name.parse("data.embed"))
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch data on vision load. Overrides embedded data. Default: `false`
|
* Set up a default header that is automatically added to all pages on this server
|
||||||
*/
|
*/
|
||||||
public var dataFetch: Boolean by meta.boolean(false, Name.parse("data.fetch"))
|
public fun header(key: String, block: HtmlFragment) {
|
||||||
|
serverHeaders[key] = block
|
||||||
/**
|
}
|
||||||
* Connect to server to get pushes. The address of the server is embedded in the tag. Default: `true`
|
|
||||||
*/
|
|
||||||
public var dataUpdate: Boolean by meta.boolean(true, Name.parse("data.update"))
|
|
||||||
|
|
||||||
private fun HTML.visionPage(
|
private fun HTML.visionPage(
|
||||||
title: String,
|
|
||||||
pagePath: String,
|
pagePath: String,
|
||||||
header: HtmlFragment,
|
headers: Map<String, HtmlFragment>,
|
||||||
visionFragment: HtmlVisionFragment,
|
visionFragment: HtmlVisionFragment,
|
||||||
): Map<Name, Vision> {
|
): Map<Name, Vision> {
|
||||||
var visionMap: Map<Name, Vision>? = null
|
var visionMap: Map<Name, Vision>? = null
|
||||||
@ -89,17 +104,18 @@ public class VisionServer internal constructor(
|
|||||||
head {
|
head {
|
||||||
meta {
|
meta {
|
||||||
charset = "utf-8"
|
charset = "utf-8"
|
||||||
header()
|
|
||||||
}
|
}
|
||||||
title(title)
|
(serverHeaders + headers).values.forEach {
|
||||||
consumer.header()
|
consumer.it()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
//Load the fragment and remember all loaded visions
|
//Load the fragment and remember all loaded visions
|
||||||
visionMap = visionFragment(
|
visionMap = visionFragment(
|
||||||
context = visionManager.context,
|
context = visionManager.context,
|
||||||
embedData = true,
|
embedData = dataMode == DataServeMode.EMBED,
|
||||||
fetchUpdatesUrl = "$serverUrl$pagePath/ws",
|
fetchDataUrl = if (dataMode != DataServeMode.EMBED) "$serverUrl$pagePath/data" else null,
|
||||||
|
fetchUpdatesUrl = if (dataMode == DataServeMode.UPDATE) "$serverUrl$pagePath/ws" else null,
|
||||||
fragment = visionFragment
|
fragment = visionFragment
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -110,7 +126,6 @@ public class VisionServer internal constructor(
|
|||||||
/**
|
/**
|
||||||
* Server a map of visions without providing explicit html page for them
|
* Server a map of visions without providing explicit html page for them
|
||||||
*/
|
*/
|
||||||
@OptIn(DFExperimental::class)
|
|
||||||
private fun serveVisions(route: Route, visions: Map<Name, Vision>): Unit = route {
|
private fun serveVisions(route: Route, visions: Map<Name, Vision>): Unit = route {
|
||||||
application.log.info("Serving visions $visions at $route")
|
application.log.info("Serving visions $visions at $route")
|
||||||
|
|
||||||
@ -125,7 +140,7 @@ public class VisionServer internal constructor(
|
|||||||
val data = it.data.decodeToString()
|
val data = it.data.decodeToString()
|
||||||
application.log.debug("Received update: \n$data")
|
application.log.debug("Received update: \n$data")
|
||||||
val change = visionManager.jsonFormat.decodeFromString(
|
val change = visionManager.jsonFormat.decodeFromString(
|
||||||
VisionChange.serializer(),data
|
VisionChange.serializer(), data
|
||||||
)
|
)
|
||||||
vision.update(change)
|
vision.update(change)
|
||||||
}
|
}
|
||||||
@ -191,12 +206,11 @@ public class VisionServer internal constructor(
|
|||||||
}.finalize()
|
}.finalize()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serve a page, potentially containing any number of visions at a given [pagePath] with given [headers].
|
* Serve a page, potentially containing any number of visions at a given [route] with given [header].
|
||||||
*/
|
*/
|
||||||
public fun page(
|
public fun page(
|
||||||
pagePath: String = DEFAULT_PAGE,
|
route: String = DEFAULT_PAGE,
|
||||||
title: String = "VisionForge server page '$pagePath'",
|
headers: Map<String, HtmlFragment>,
|
||||||
header: HtmlFragment = {},
|
|
||||||
visionFragment: HtmlVisionFragment,
|
visionFragment: HtmlVisionFragment,
|
||||||
) {
|
) {
|
||||||
val visions = HashMap<Name, Vision>()
|
val visions = HashMap<Name, Vision>()
|
||||||
@ -204,13 +218,13 @@ public class VisionServer internal constructor(
|
|||||||
val cachedHtml: String? = if (cacheFragments) {
|
val cachedHtml: String? = if (cacheFragments) {
|
||||||
//Create and cache page html and map of visions
|
//Create and cache page html and map of visions
|
||||||
createHTML(true).html {
|
createHTML(true).html {
|
||||||
visions.putAll(visionPage(title, pagePath, header, visionFragment))
|
visions.putAll(visionPage(route, headers, visionFragment))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
root.route(pagePath) {
|
root.route(route) {
|
||||||
serveVisions(this, visions)
|
serveVisions(this, visions)
|
||||||
//filled pages
|
//filled pages
|
||||||
get {
|
get {
|
||||||
@ -218,7 +232,7 @@ public class VisionServer internal constructor(
|
|||||||
//re-create html and vision list on each call
|
//re-create html and vision list on each call
|
||||||
call.respondHtml {
|
call.respondHtml {
|
||||||
visions.clear()
|
visions.clear()
|
||||||
visions.putAll(visionPage(title, pagePath, header, visionFragment))
|
visions.putAll(visionPage(route, headers, visionFragment))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//Use cached html
|
//Use cached html
|
||||||
@ -226,8 +240,22 @@ public class VisionServer internal constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun page(
|
||||||
|
vararg headers: HtmlFragment,
|
||||||
|
route: String = DEFAULT_PAGE,
|
||||||
|
title: String = "VisionForge server page '$route'",
|
||||||
|
visionFragment: HtmlVisionFragment,
|
||||||
|
) {
|
||||||
|
page(route, mapOf("title" to VisionPage.title(title)) + headers.associateBy { it.hashCode().toString() }, visionFragment)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render given [VisionPage] at server
|
||||||
|
*/
|
||||||
|
public fun page(route: String, page: VisionPage) {
|
||||||
|
page(route = route, headers = page.pageHeaders, visionFragment = page.content)
|
||||||
}
|
}
|
||||||
|
|
||||||
public companion object {
|
public companion object {
|
||||||
|
@ -3,6 +3,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
kotlin{
|
kotlin{
|
||||||
|
explicitApi = org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode.Disabled
|
||||||
js{
|
js{
|
||||||
binaries.library()
|
binaries.library()
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,9 @@ public object ThreeCanvasLabelFactory : ThreeFactory<SolidLabel> {
|
|||||||
|
|
||||||
override fun build(three: ThreePlugin, vision: SolidLabel, observe: Boolean): Object3D {
|
override fun build(three: ThreePlugin, vision: SolidLabel, observe: Boolean): Object3D {
|
||||||
val canvas = document.createElement("canvas") as HTMLCanvasElement
|
val canvas = document.createElement("canvas") as HTMLCanvasElement
|
||||||
|
canvas.width = 200
|
||||||
|
canvas.height = 200
|
||||||
|
|
||||||
canvas.getContext("2d").apply {
|
canvas.getContext("2d").apply {
|
||||||
this as CanvasRenderingContext2D
|
this as CanvasRenderingContext2D
|
||||||
font = "Bold ${vision.fontSize}pt ${vision.fontFamily}"
|
font = "Bold ${vision.fontSize}pt ${vision.fontFamily}"
|
||||||
@ -43,15 +46,13 @@ public object ThreeCanvasLabelFactory : ThreeFactory<SolidLabel> {
|
|||||||
val texture = Texture(canvas)
|
val texture = Texture(canvas)
|
||||||
texture.needsUpdate = true
|
texture.needsUpdate = true
|
||||||
|
|
||||||
val material = MeshBasicMaterial().apply {
|
val mesh = Mesh(
|
||||||
|
PlaneGeometry(canvas.width, canvas.height),
|
||||||
|
MeshBasicMaterial().apply {
|
||||||
map = texture
|
map = texture
|
||||||
side = DoubleSide
|
side = DoubleSide
|
||||||
transparent = true
|
transparent = true
|
||||||
}
|
}
|
||||||
|
|
||||||
val mesh = Mesh(
|
|
||||||
PlaneGeometry(canvas.width, canvas.height),
|
|
||||||
material
|
|
||||||
)
|
)
|
||||||
|
|
||||||
mesh.updatePosition(vision)
|
mesh.updatePosition(vision)
|
||||||
|
@ -8,7 +8,7 @@ import java.awt.Desktop
|
|||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
|
||||||
public val Page.Companion.threeJsHeader: HtmlFragment get() = scriptHeader("js/visionforge-three.js")
|
public val VisionPage.Companion.threeJsHeader: HtmlFragment get() = scriptHeader("js/visionforge-three.js")
|
||||||
|
|
||||||
|
|
||||||
@DFExperimental
|
@DFExperimental
|
||||||
@ -19,10 +19,10 @@ public fun makeThreeJsFile(
|
|||||||
show: Boolean = true,
|
show: Boolean = true,
|
||||||
content: HtmlVisionFragment,
|
content: HtmlVisionFragment,
|
||||||
): Unit {
|
): Unit {
|
||||||
val actualPath = Page(Global, content = content).makeFile(path) { actualPath ->
|
val actualPath = VisionPage(Global, content = content).makeFile(path) { actualPath ->
|
||||||
mapOf(
|
mapOf(
|
||||||
"title" to Page.title(title),
|
"title" to VisionPage.title(title),
|
||||||
"threeJs" to Page.importScriptHeader("js/visionforge-three.js", resourceLocation, actualPath)
|
"threeJs" to VisionPage.importScriptHeader("js/visionforge-three.js", resourceLocation, actualPath)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (show) Desktop.getDesktop().browse(actualPath.toFile().toURI())
|
if (show) Desktop.getDesktop().browse(actualPath.toFile().toURI())
|
||||||
|
Loading…
Reference in New Issue
Block a user