Refactor vst-ui-base and test-app: remove inline CSS definitions in Kotlin, migrate styles to external vst-ui-base.css. Add WebResourceHandler for serving CSS. Enhance KeyboardComponent with keyboard event mapping. Increment version to 2.2.4.

This commit is contained in:
2026-02-18 12:49:27 +01:00
parent d5a377e974
commit bc4062b916
13 changed files with 438 additions and 506 deletions

View File

@@ -1,5 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="JS" type="JavascriptDebugType" engineId="98ca6316-2f89-46d9-a9e5-fa9e2b0625b3" uri="http://localhost:9999">
<mapping url="http://localhost:9999/vst-ui-base.css" local-file="$PROJECT_DIR$/vst-ui-base/src/jvmMain/resources/vst-ui-base.css" />
<method v="2">
<option name="Gradle.BeforeRunTask" enabled="true" tasks="jsBrowserDevelopmentExecutableDistribution" externalProjectPath="$PROJECT_DIR$/test-app" vmOptions="" scriptParameters="" />
</method>

View File

@@ -2,7 +2,7 @@ project.extra.set("devMode", true)
project.extra.set("enabledSourceMaps", true)
group = "nl.astraeus"
version = "2.2.3"
version = "2.2.4"
allprojects {
repositories {

View File

@@ -2,25 +2,29 @@
package nl.astraeus.vst.ui.view
import kotlinx.html.*
import kotlinx.browser.document
import kotlinx.html.InputType
import kotlinx.html.div
import kotlinx.html.h1
import kotlinx.html.hr
import kotlinx.html.input
import kotlinx.html.js.onChangeFunction
import kotlinx.html.js.onClickFunction
import kotlinx.html.js.onInputFunction
import kotlinx.html.option
import kotlinx.html.org.w3c.dom.events.Event
import nl.astraeus.css.properties.*
import nl.astraeus.css.style.Style
import nl.astraeus.css.style.cls
import kotlinx.html.select
import kotlinx.html.span
import kotlinx.html.style
import nl.astraeus.komp.HtmlBuilder
import nl.astraeus.komp.Komponent
import nl.astraeus.vst.ui.components.ExpKnobComponent
import nl.astraeus.vst.ui.components.KeyboardComponent
import nl.astraeus.vst.ui.components.KnobComponent
import nl.astraeus.vst.ui.css.Css
import nl.astraeus.vst.ui.css.Css.defineCss
import nl.astraeus.vst.ui.css.Css.noTextSelect
import nl.astraeus.vst.ui.css.CssName
import nl.astraeus.vst.ui.css.hover
import nl.astraeus.vst.ui.ws.WebsocketClient
import org.w3c.dom.HTMLInputElement
import org.w3c.dom.events.KeyboardEvent
import org.w3c.files.File
import org.w3c.files.FileList
import org.w3c.files.get
@@ -29,6 +33,17 @@ class MainView : Komponent() {
private var messages: MutableList<String> = ArrayList()
var started = true
var keyboardWidth = 500
val keyboard = KeyboardComponent(
keyboardWidth = keyboardWidth,
keyboardHeight = keyboardWidth / 2,
onNoteDown = { println("Note down: $it") },
onNoteUp = { println("Note up: $it") },
)
init {
document.body?.addEventListener("keydown", { event -> keyboard.handleKeyboardEvent(event as KeyboardEvent) })
document.body?.addEventListener("keyup", { event -> keyboard.handleKeyboardEvent(event as KeyboardEvent) })
}
fun addMessage(message: String) {
messages.add(message)
@@ -45,7 +60,7 @@ class MainView : Komponent() {
override fun HtmlBuilder.render() {
div(MainDivCss.name) {
style = "transform: scale(0.5);"
//style = "transform: scale(0.5);"
if (!started) {
div(StartSplashCss.name) {
div(StartBoxCss.name) {
@@ -89,26 +104,27 @@ class MainView : Komponent() {
}
span {
+"channel:"
/*
input {
type = InputType.number
value = VstChipWorklet.midiChannel.toString()
value = "1"
min = "1"
max = "16"
onInputFunction = { event ->
val target = event.target as HTMLInputElement
kotlin.io.println("onInput channel: $target")
VstChipWorklet.midiChannel = target.value.toInt()
println("onInput channel: $target")
//VstChipWorklet.midiChannel = target.value.toInt()
}
}
*/
}
span {
+"Upload file: "
input {
type = InputType.file
onChangeFunction = {
fileInputSelectHandler(it)
requestUpdate()
}
}
div {
+"Upload file: "
input {
type = InputType.file
onChangeFunction = {
fileInputSelectHandler(it)
requestUpdate()
}
}
}
@@ -133,12 +149,7 @@ class MainView : Komponent() {
hr {}
div {
include(
KeyboardComponent(
keyboardWidth = keyboardWidth,
keyboardHeight = keyboardWidth / 2,
onNoteDown = { println("Note down: $it") },
onNoteUp = { println("Note up: $it") },
)
keyboard
)
}
/*
@@ -226,132 +237,10 @@ class MainView : Komponent() {
companion object MainViewCss : CssName() {
object MainDivCss : CssName()
object ActiveCss : CssName()
object ButtonCss : CssName()
object ButtonBarCss : CssName()
object SelectedCss : CssName()
object NoteBarCss : CssName()
object StartSplashCss : CssName()
object StartBoxCss : CssName()
object StartButtonCss : CssName()
object ControlsCss : CssName()
init {
defineCss {
select("*") {
select("*:before") {
select("*:after") {
boxSizing(BoxSizing.borderBox)
}
}
}
select("html", "body") {
margin(0.px)
padding(0.px)
height(100.prc)
}
select("html", "body") {
backgroundColor(Css.currentStyle.mainBackgroundColor)
color(Css.currentStyle.mainFontColor)
fontFamily("JetbrainsMono, monospace")
fontSize(14.px)
fontWeight(FontWeight.bold)
//transition()
noTextSelect()
}
select("input", "textarea") {
backgroundColor(Css.currentStyle.inputBackgroundColor)
color(Css.currentStyle.mainFontColor)
border("none")
}
select(cls(ButtonCss)) {
margin(1.rem)
commonButton()
}
select(cls(ButtonBarCss)) {
margin(1.rem, 0.px)
commonButton()
}
select(cls(ActiveCss)) {
//backgroundColor(Css.currentStyle.selectedBackgroundColor)
}
select(cls(NoteBarCss)) {
minHeight(4.rem)
}
select(cls(MainDivCss)) {
margin(1.rem)
}
select("select") {
plain("appearance", "none")
border("0")
outline("0")
width(20.rem)
padding(0.5.rem, 2.rem, 0.5.rem, 0.5.rem)
backgroundImage("url('https://upload.wikimedia.org/wikipedia/commons/9/9d/Caret_down_font_awesome_whitevariation.svg')")
background("right 0.8em center/1.4em")
backgroundColor(Css.currentStyle.inputBackgroundColor)
color(Css.currentStyle.mainFontColor)
borderRadius(0.25.em)
}
select(cls(StartSplashCss)) {
position(Position.fixed)
left(0.px)
top(0.px)
width(100.vw)
height(100.vh)
zIndex(100)
backgroundColor(hsla(32, 0, 5, 0.65))
select(cls(StartBoxCss)) {
position(Position.relative)
left(25.vw)
top(25.vh)
width(50.vw)
height(50.vh)
backgroundColor(hsla(239, 50, 10, 1.0))
borderColor(Css.currentStyle.mainFontColor)
borderWidth(2.px)
select(cls(StartButtonCss)) {
position(Position.absolute)
left(50.prc)
top(50.prc)
transform(Transform("translate(-50%, -50%)"))
padding(1.rem)
backgroundColor(Css.currentStyle.buttonBackgroundColor)
cursor("pointer")
}
}
}
select(ControlsCss.cls()) {
display(Display.flex)
flexDirection(FlexDirection.row)
justifyContent(JustifyContent.flexStart)
alignItems(AlignItems.center)
margin(1.rem)
padding(1.rem)
backgroundColor(Css.currentStyle.mainBackgroundColor)
}
}
}
private fun Style.commonButton() {
display(Display.inlineBlock)
padding(1.rem)
backgroundColor(Css.currentStyle.buttonBackgroundColor)
borderColor(Css.currentStyle.buttonBorderColor)
borderWidth(Css.currentStyle.buttonBorderWidth)
color(Css.currentStyle.mainFontColor)
hover {
backgroundColor(Css.currentStyle.buttonBackgroundColor.hover())
}
and(SelectedCss.cls()) {
backgroundColor(Css.currentStyle.buttonBackgroundColor.hover().hover().hover())
}
}
}
}
}

View File

@@ -33,11 +33,7 @@ kotlin {
jvm {}
sourceSets {
val commonMain by getting {
dependencies {
api("nl.astraeus:kotlin-css-generator:1.0.10")
}
}
val commonMain by getting
val jsMain by getting {
dependencies {
api("nl.astraeus:kotlin-komponent:1.2.8")
@@ -52,8 +48,8 @@ kotlin {
dependencies {
api("org.jetbrains.kotlinx:kotlinx-datetime:0.6.0")
api("io.undertow:undertow-core:2.3.21.Final")
implementation("io.undertow:undertow-websockets-jsr:2.3.21.Final")
api("io.undertow:undertow-core:2.3.23.Final")
implementation("io.undertow:undertow-websockets-jsr:2.3.23.Final")
//implementation("org.jboss.xnio:xnio-nio:3.8.16.Final")
implementation("org.xerial:sqlite-jdbc:3.46.0.0")

View File

@@ -1,20 +1,19 @@
package nl.astraeus.vst.ui.components
import kotlinx.browser.document
import kotlinx.html.*
import kotlinx.html.classes
import kotlinx.html.div
import kotlinx.html.js.onMouseDownFunction
import kotlinx.html.js.onMouseMoveFunction
import kotlinx.html.js.onMouseUpFunction
import kotlinx.html.js.onWheelFunction
import kotlinx.html.org.w3c.dom.events.Event
import nl.astraeus.css.properties.*
import nl.astraeus.css.style.cls
import kotlinx.html.span
import kotlinx.html.style
import kotlinx.html.svg
import nl.astraeus.komp.HtmlBuilder
import nl.astraeus.komp.Komponent
import nl.astraeus.komp.currentElement
import nl.astraeus.vst.ui.css.ActiveCls
import nl.astraeus.vst.ui.css.Css
import nl.astraeus.vst.ui.css.Css.defineCss
import nl.astraeus.vst.ui.css.CssId
import nl.astraeus.vst.ui.css.CssName
import nl.astraeus.vst.ui.util.arc
@@ -327,6 +326,7 @@ open class BaseKnobComponent(
}
companion object : CssId("knob") {
object ActiveCls : CssName( "active")
object KnobCls : CssName()
object KnobSvgCls : CssName()
object KnobTextCls : CssName()
@@ -334,69 +334,6 @@ open class BaseKnobComponent(
object KnobVolumeCls : CssName()
object KnobVolumeBackgroundCls : CssName()
init {
defineCss {
select(cls(KnobCls)) {
position(Position.relative)
margin(5.px)
and(cls(ActiveCls)) {
backgroundColor(hsla(178, 70, 55, 0.1))
borderRadius(0.5.rem)
}
select(cls(KnobSvgCls)) {
plain("stroke", "none")
plain("stroke-opacity", "1.0")
plain("fill", "none")
plain("fill-opacity", "0.0")
position(Position.absolute)
backgroundColor(Color.transparent)
and(cls(ActiveCls)) {
color(Css.currentStyle.mainFontColor)
borderRadius(4.px)
}
}
select(cls(KnobTextCls)) {
position(Position.absolute)
width(100.prc)
textAlign(TextAlign.center)
fontSize(1.0.em)
color(Css.currentStyle.mainFontColor)
}
select(cls(KnobValueCls)) {
position(Position.absolute)
width(100.prc)
top((48).prc)
textAlign(TextAlign.center)
fontSize(1.1.em)
color(Css.currentStyle.mainFontColor)
}
}
select(cls(KnobVolumeCls)) {
plain("fill", Color.transparent)
plain("stroke", Css.currentStyle.mainFontColor)
color(Css.currentStyle.mainFontColor)
plain("stroke-width", "8")
//plain("stroke-dasharray", "4")
plain("fill-opacity", "0.5")
}
select(cls(KnobVolumeBackgroundCls)) {
plain("fill", Color.transparent)
plain("stroke", Css.currentStyle.mainFontColor.darken(40))
color(Css.currentStyle.mainFontColor.darken(20))
plain("stroke-width", "5")
//plain("stroke-dasharray", "4")
plain("fill-opacity", "0.5")
}
}
}
}
}

View File

@@ -7,35 +7,66 @@ import kotlinx.html.js.onMouseLeaveFunction
import kotlinx.html.js.onMouseUpFunction
import kotlinx.html.style
import kotlinx.html.svg
import nl.astraeus.css.properties.AlignItems
import nl.astraeus.css.properties.Display
import nl.astraeus.css.properties.FlexDirection
import nl.astraeus.css.properties.FontWeight
import nl.astraeus.css.properties.JustifyContent
import nl.astraeus.css.properties.Position
import nl.astraeus.css.properties.TextAlign
import nl.astraeus.css.properties.prc
import nl.astraeus.css.properties.px
import nl.astraeus.css.properties.rem
import nl.astraeus.css.style.cls
import nl.astraeus.komp.HtmlBuilder
import nl.astraeus.komp.Komponent
import nl.astraeus.vst.ui.css.Css
import nl.astraeus.vst.ui.css.Css.defineCss
import nl.astraeus.vst.ui.css.CssId
import nl.astraeus.vst.ui.css.CssName
import nl.astraeus.vst.ui.util.height
import nl.astraeus.vst.ui.util.rect
import nl.astraeus.vst.ui.util.width
import org.w3c.dom.events.KeyboardEvent
import org.w3c.dom.events.MouseEvent
private val KEYBOARD_MAPPING = mapOf(
"KeyZ" to 60,
"KeyS" to 61,
"KeyX" to 62,
"KeyD" to 63,
"KeyC" to 64,
"KeyV" to 65,
"KeyG" to 66,
"KeyB" to 67,
"KeyH" to 68,
"KeyN" to 69,
"KeyJ" to 70,
"KeyM" to 71, // b
"Comma" to 72,
"KeyL" to 73,
"Period" to 74,
"Semicolon" to 75,
"Slash" to 76,
"KeyQ" to 72,
"Digit2" to 73,
"KeyW" to 74,
"Digit3" to 75,
"KeyE" to 76,
"KeyR" to 77,
"Digit5" to 78,
"KeyT" to 79,
"Digit6" to 80,
"KeyY" to 81,
"Digit7" to 82,
"KeyU" to 83, // b
"KeyI" to 84,
"Digit9" to 85,
"KeyO" to 86,
"Digit0" to 87,
"KeyP" to 88,
"BracketLeft" to 89,
"Equal" to 90,
"BracketRight" to 91,
)
/**
* The keyboard component shows 1 octabe of a (piano) keyboard and
* calls the noteDown and noteUp methods when the keys are clicked
*/
class KeyboardComponent(
val title: String = "Keyboard",
initialOctave: Int = 4,
val initialOctave: Int = 4,
val keyboardWidth: Int = 210,
val keyboardHeight: Int = keyboardWidth / 2,
val rounding: Int = 4,
@@ -95,6 +126,13 @@ class KeyboardComponent(
requestUpdate()
}
fun handleKeyboardEvent(event: KeyboardEvent) {
KEYBOARD_MAPPING[event.code]?.also { midi ->
val transpose = (state.octave - initialOctave) * 12
console.log("Keyboard event: ", event, midi + transpose)
}
}
private fun releaseAllNotes() {
// Create a copy of the set to avoid concurrent modification
val notesToRelease = state.pressedNotes.toSet()
@@ -274,91 +312,5 @@ class KeyboardComponent(
// MIDI note numbers for C5 to B5 (one octave)
private val BASE_WHITE_KEYS = listOf(60, 62, 64, 65, 67, 69, 71) // C, D, E, F, G, A, B
private val BASE_BLACK_KEYS = listOf(61, 63, 66, 68, 70) // C#, D#, F#, G#, A#
init {
defineCss {
select(cls(KeyboardCls)) {
position(Position.relative)
margin(5.px)
select(cls(KeyboardControlsCls)) {
position(Position.relative)
display(Display.flex)
flexDirection(FlexDirection.row)
justifyContent(JustifyContent.spaceBetween)
alignItems(AlignItems.center)
width(100.prc)
height(50.px)
marginBottom(10.px)
}
select(cls(KeyboardInfoCls)) {
display(Display.flex)
flexDirection(FlexDirection.column)
alignItems(AlignItems.center)
justifyContent(JustifyContent.center)
flex("1")
}
select(cls(KeyboardTitleCls)) {
textAlign(TextAlign.center)
fontSize(1.2.rem)
color(Css.currentStyle.mainFontColor)
}
select(cls(KeyboardOctaveCls)) {
textAlign(TextAlign.center)
fontSize(1.0.rem)
color(Css.currentStyle.mainFontColor)
marginTop(5.px)
}
select(cls(OctaveButtonCls)) {
height(50.px)
plain("background-color", Css.currentStyle.buttonBackgroundColor.toString())
color(Css.currentStyle.mainFontColor)
border("1px solid ${Css.currentStyle.buttonBorderColor}")
textAlign(TextAlign.center)
lineHeight(50.px)
fontSize(1.5.rem)
fontWeight(FontWeight.bold)
cursor("pointer")
plain("-webkit-touch-callout", "none")
plain("-webkit-user-select", "none")
plain("-moz-user-select", "none")
plain("-ms-user-select", "none")
plain("user-select", "none")
}
select(cls(KeyboardKeysCls)) {
position(Position.relative)
}
}
select(cls(WhiteKeyCls)) {
plain("fill", "#FFFFFF")
plain("stroke", "#000000")
plain("stroke-width", "1")
}
select(cls(WhiteKeyPressedCls)) {
plain("fill", "#E6E6E6") // 10% darker than white
plain("stroke", "#000000")
plain("stroke-width", "1")
}
select(cls(BlackKeyCls)) {
plain("fill", "#000000")
plain("stroke", "#000000")
plain("stroke-width", "1")
}
select(cls(BlackKeyPressedCls)) {
plain("fill", "#333333") // 10% lighter than black
plain("stroke", "#000000")
plain("stroke-width", "1")
}
}
}
}
}

View File

@@ -1,120 +0,0 @@
package nl.astraeus.vst.ui.css
import kotlinx.browser.document
import nl.astraeus.css.properties.Color
import nl.astraeus.css.properties.Measurement
import nl.astraeus.css.properties.UserSelect
import nl.astraeus.css.properties.hsl
import nl.astraeus.css.properties.hsla
import nl.astraeus.css.properties.px
import nl.astraeus.css.style
import nl.astraeus.css.style.ConditionalStyle
import nl.astraeus.css.style.DescriptionProvider
import nl.astraeus.css.style.Style
class StyleDefinition(
val mainFontColor: Color = hsla(178, 70, 55, 1.0),
val mainBackgroundColor: Color = hsl(239, 50, 10),
//val entryFontColor: Color = hsl(Css.mainFontColorNumber, 70, 55),
val inputBackgroundColor : Color = mainBackgroundColor.lighten(15),
val buttonBackgroundColor : Color = mainBackgroundColor.lighten(15),
val buttonBorderColor : Color = mainFontColor.changeAlpha(0.25),
val buttonBorderWidth : Measurement = 1.px,
)
object NoTextSelectCls : CssName("no-text-select")
object SelectedCls : CssName("selected")
object ActiveCls : CssName( "active")
fun Color.hover(): Color = if (Css.currentStyle == Css.darkStyle) {
this.lighten(15)
} else {
this.darken(15)
}
object Css {
var dynamicStyles = mutableMapOf<DescriptionProvider, ConditionalStyle.() -> Unit>()
fun DescriptionProvider.defineCss(conditionalStyle: ConditionalStyle.() -> Unit) {
check(!dynamicStyles.containsKey(this)) {
"CssId with name ${this.description()} already defined!"
}
updateCss(conditionalStyle)
}
private fun DescriptionProvider.updateCss(conditionalStyle: ConditionalStyle.() -> Unit) {
val elementId = this.description()
var dynamicStyleElement = document.getElementById(elementId)
dynamicStyles[this] = conditionalStyle
if (dynamicStyleElement == null) {
dynamicStyleElement = document.createElement("style")
dynamicStyleElement.id = elementId
document.head?.append(dynamicStyleElement)
}
val css = style(conditionalStyle)
dynamicStyleElement.innerHTML = css.generateCss(minified = CssSettings.minified)
}
var darkStyle = StyleDefinition(
)
var lightStyle = StyleDefinition(
mainBackgroundColor = hsl(239+180, 50, 15),
)
var currentStyle: StyleDefinition = darkStyle
fun updateStyle() {
for ((cssId, dynStyle) in dynamicStyles) {
cssId.apply {
updateCss(dynStyle)
}
}
}
fun switchLayout() {
currentStyle = if (currentStyle == darkStyle) {
lightStyle
} else {
darkStyle
}
updateStyle()
}
fun Style.transition() {
transition("all 0.5s ease")
}
fun Style.noTextSelect() {
plain("-webkit-touch-callout", "none")
plain("-webkit-user-select", "none")
plain("-moz-user-select", "none")
plain("-ms-user-select", "none")
userSelect(UserSelect.none)
select("::selection") {
background("none")
}
}
object GenericCss : CssId("generic") {
init {
fun generateStyle(): String {
val css = style {
}
return css.generateCss(minified = CssSettings.minified)
}
}
}
}

View File

@@ -1,8 +1,5 @@
package nl.astraeus.vst.ui.css
import nl.astraeus.css.style.DescriptionProvider
import nl.astraeus.css.style.cls
private val CAPITAL_LETTER = Regex("[A-Z]")
fun String.hyphenize(): String {
@@ -39,6 +36,19 @@ private fun nextShortId(): String {
return result.toString()
}
interface DescriptionProvider {
fun description(): String
}
class ValueDescriptionProvider(
val value: String
) : DescriptionProvider {
override fun description() = value
}
fun cls(name: String): DescriptionProvider = ValueDescriptionProvider(".$name")
fun cls(name: DescriptionProvider): DescriptionProvider = ValueDescriptionProvider(".$name")
abstract class CssName(
overrideName: String? = null
) : DescriptionProvider {
@@ -61,6 +71,7 @@ abstract class CssName(
override fun description() = name
}
open class CssId(name: String) : DescriptionProvider {
val name: String = if (CssSettings.shortId) {
nextShortId()

View File

@@ -3,21 +3,8 @@ package nl.astraeus.vst.ui.view
import kotlinx.html.div
import kotlinx.html.js.onClickFunction
import kotlinx.html.org.w3c.dom.events.Event
import nl.astraeus.css.properties.Display
import nl.astraeus.css.properties.FlexDirection
import nl.astraeus.css.properties.Position
import nl.astraeus.css.properties.Transform
import nl.astraeus.css.properties.hsla
import nl.astraeus.css.properties.prc
import nl.astraeus.css.properties.px
import nl.astraeus.css.properties.rem
import nl.astraeus.css.properties.vh
import nl.astraeus.css.properties.vw
import nl.astraeus.css.style.cls
import nl.astraeus.komp.HtmlBuilder
import nl.astraeus.komp.Komponent
import nl.astraeus.vst.ui.css.Css
import nl.astraeus.vst.ui.css.Css.defineCss
import nl.astraeus.vst.ui.css.CssId
import nl.astraeus.vst.ui.css.CssName
@@ -53,46 +40,6 @@ class BaseVstView(
object StartSplashCss : CssName()
object StartBoxCss : CssName()
object StartButtonCss : CssName()
init {
defineCss {
select(BaseVstCss.cls()) {
display(Display.flex)
flexDirection(FlexDirection.column)
}
select(cls(StartSplashCss)) {
position(Position.fixed)
left(0.px)
top(0.px)
width(100.vw)
height(100.vh)
zIndex(100)
backgroundColor(hsla(32, 0, 5, 0.65))
select(cls(StartBoxCss)) {
position(Position.relative)
left(25.vw)
top(25.vh)
width(50.vw)
height(50.vh)
backgroundColor(hsla(239, 50, 10, 1.0))
borderColor(Css.currentStyle.mainFontColor)
borderWidth(2.px)
select(cls(StartButtonCss)) {
position(Position.absolute)
left(50.prc)
top(50.prc)
transform(Transform("translate(-50%, -50%)"))
padding(1.rem)
backgroundColor(Css.currentStyle.buttonBackgroundColor)
cursor("pointer")
}
}
}
}
}
}
}

View File

@@ -1,7 +1,13 @@
package nl.astraeus.vst.base.web
import kotlinx.html.*
import kotlinx.html.body
import kotlinx.html.head
import kotlinx.html.html
import kotlinx.html.link
import kotlinx.html.meta
import kotlinx.html.script
import kotlinx.html.stream.appendHTML
import kotlinx.html.title
fun generateIndex(
title: String,
@@ -15,6 +21,9 @@ fun generateIndex(
result.appendHTML(true).html {
head {
title { +title }
link(rel = "stylesheet", href = "/vst-ui-base.css") {
type = "text/css"
}
}
body {
script {
@@ -26,7 +35,7 @@ fun generateIndex(
} else {
result.appendHTML(true).html {
head {
title { +"VST Chip" }
title { +title }
meta {
httpEquiv = "refresh"
content = "0; url=/patch/$patch"

View File

@@ -44,6 +44,7 @@ class RequestHandler(
pathHandler.addExactPath("/", patchHandler)
pathHandler.addExactPath("/index.html", patchHandler)
pathHandler.addPrefixPath("/patch", patchHandler)
pathHandler.addPrefixPath("/vst-ui-base.css", WebResourceHandler("text/css", "vst-ui-base.css"))
//pathHandler.addPrefixPath("/data", urlDataHandler)
pathHandler.addExactPath("/ws", WebsocketConnectHandler)
}

View File

@@ -0,0 +1,27 @@
package nl.astraeus.vst.base.web
import io.undertow.server.HttpHandler
import io.undertow.server.HttpServerExchange
import io.undertow.util.Headers
import io.undertow.util.StatusCodes
import java.nio.ByteBuffer
class WebResourceHandler(
val mimeType: String,
val resourceName: String,
) : HttpHandler {
override fun handleRequest(exchange: HttpServerExchange) {
val stream = Thread.currentThread().contextClassLoader.getResourceAsStream(resourceName)
if (stream != null) {
exchange.responseHeaders.put(Headers.CONTENT_TYPE, mimeType)
exchange.responseSender.send(ByteBuffer.wrap(stream.readBytes()))
} else {
exchange.responseHeaders.put(Headers.CONTENT_TYPE, "text/plain")
exchange.statusCode = StatusCodes.NOT_FOUND
exchange.responseSender.send("Not found", Charsets.UTF_8)
}
}
}

View File

@@ -0,0 +1,282 @@
:root {
--vst-main-font-color: hsla(178, 70%, 55%, 1);
--vst-main-background-color: hsl(239, 50%, 10%);
--vst-input-background-color: hsl(239, 50%, 25%);
--vst-button-background-color: hsl(239, 50%, 25%);
--vst-button-border-color: hsla(178, 70%, 55%, 0.25);
--vst-button-border-width: 1px;
--vst-main-font-color-darken-20: hsl(178, 70%, 35%);
--vst-main-font-color-darken-40: hsl(178, 70%, 15%);
}
*, *:before,*:after {
box-sizing: border-box;
}
html,body {
margin: 0;
padding: 0;
height: 100%;
background-color: var(--vst-main-background-color);
color: var(--vst-main-font-color);
font-family: JetbrainsMono, monospace;
font-size: 14px;
font-weight: bold;
//transition()
//noTextSelect()
}
input, textarea {
background-color: var(--vst-input-background-color);
color: var(--vst-main-font-color);
border: none;
}
.vst-main-div-css {
margin: 1rem;
}
select {
appearance: none;
border: 0;
outline: 0;
width: 20rem;
padding: 0.5rem 2rem 0.5rem 0.5rem;
color: var(--vst-main-font-color);
border-radius: 0.25rem;
background: var(--vst-input-background-color);
}
/*
select(cls(ButtonCss)) {
margin(1.rem)
commonButton()
}
select(cls(ButtonBarCss)) {
margin(1.rem, 0.px)
commonButton()
}
select(cls(NoteBarCss)) {
minHeight(4.rem)
}
select(cls(StartSplashCss)) {
position(Position.fixed)
left(0.px)
top(0.px)
width(100.vw)
height(100.vh)
zIndex(100)
backgroundColor(hsla(32, 0, 5, 0.65))
select(cls(StartBoxCss)) {
position(Position.relative)
left(25.vw)
top(25.vh)
width(50.vw)
height(50.vh)
backgroundColor(hsla(239, 50, 10, 1.0))
borderColor(Css.currentStyle.mainFontColor)
borderWidth(2.px)
select(cls(StartButtonCss)) {
position(Position.absolute)
left(50.prc)
top(50.prc)
transform(Transform("translate(-50%, -50%)"))
padding(1.rem)
backgroundColor(Css.currentStyle.buttonBackgroundColor)
cursor("pointer")
}
}
}
select(ControlsCss.cls()) {
display(Display.flex)
flexDirection(FlexDirection.row)
justifyContent(JustifyContent.flexStart)
alignItems(AlignItems.center)
margin(1.rem)
padding(1.rem)
backgroundColor(Css.currentStyle.mainBackgroundColor)
}
}
}
*/
.vst-base-vst-css {
display: flex;
flex-direction: column;
}
.vst-start-splash-css {
position: fixed;
left: 0;
top: 0;
width: 100vw;
height: 100vh;
z-index: 100;
background-color: hsla(32, 0%, 5%, 0.65);
}
.vst-start-splash-css .vst-start-box-css {
position: relative;
left: 25vw;
top: 25vh;
width: 50vw;
height: 50vh;
background-color: hsla(239, 50%, 10%, 1);
border-color: var(--vst-main-font-color);
border-width: 2px;
}
.vst-start-splash-css .vst-start-box-css .vst-start-button-css {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
padding: 1rem;
background-color: var(--vst-button-background-color);
cursor: pointer;
}
.vst-knob-cls {
position: relative;
margin: 5px;
}
.vst-knob-cls.vst-active {
background-color: hsla(178, 70%, 55%, 0.1);
border-radius: 0.5rem;
}
.vst-knob-cls .vst-knob-svg-cls {
stroke: none;
stroke-opacity: 1;
fill: none;
fill-opacity: 0;
position: absolute;
background-color: transparent;
}
.vst-knob-cls .vst-knob-svg-cls.vst-active {
color: var(--vst-main-font-color);
border-radius: 4px;
}
.vst-knob-cls .vst-knob-text-cls {
position: absolute;
width: 100%;
text-align: center;
font-size: 1em;
color: var(--vst-main-font-color);
}
.vst-knob-cls .vst-knob-value-cls {
position: absolute;
width: 100%;
top: 48%;
text-align: center;
font-size: 1.1em;
color: var(--vst-main-font-color);
}
.vst-knob-volume-cls {
fill: transparent;
stroke: var(--vst-main-font-color);
color: var(--vst-main-font-color);
stroke-width: 8;
fill-opacity: 0.5;
}
.vst-knob-volume-background-cls {
fill: transparent;
stroke: var(--vst-main-font-color-darken-40);
color: var(--vst-main-font-color-darken-20);
stroke-width: 5;
fill-opacity: 0.5;
}
.vst-keyboard-cls {
position: relative;
margin: 5px;
}
.vst-keyboard-cls .vst-keyboard-controls-cls {
position: relative;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
width: 100%;
height: 50px;
margin-bottom: 10px;
}
.vst-keyboard-cls .vst-keyboard-info-cls {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
flex: 1;
}
.vst-keyboard-cls .vst-keyboard-title-cls {
text-align: center;
font-size: 1.2rem;
color: var(--vst-main-font-color);
}
.vst-keyboard-cls .vst-keyboard-octave-cls {
text-align: center;
font-size: 1rem;
color: var(--vst-main-font-color);
margin-top: 5px;
}
.vst-keyboard-cls .vst-octave-button-cls {
height: 50px;
background-color: var(--vst-button-background-color);
color: var(--vst-main-font-color);
border: 1px solid var(--vst-button-border-color);
text-align: center;
line-height: 50px;
font-size: 1.5rem;
font-weight: bold;
cursor: pointer;
-webkit-touch-callout: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.vst-keyboard-cls .vst-keyboard-keys-cls {
position: relative;
}
.vst-white-key-cls {
fill: #ffffff;
stroke: #000000;
stroke-width: 1;
}
.vst-white-key-pressed-cls {
fill: #e6e6e6;
stroke: #000000;
stroke-width: 1;
}
.vst-black-key-cls {
fill: #000000;
stroke: #000000;
stroke-width: 1;
}
.vst-black-key-pressed-cls {
fill: #333333;
stroke: #000000;
stroke-width: 1;
}