working on web interface

This commit is contained in:
2026-08-24 19:12:37 -04:00
parent 92c14dfc81
commit d62575df16
42 changed files with 5683 additions and 177 deletions
+3
View File
@@ -0,0 +1,3 @@
build/*
**/build/*
+1 -5
View File
@@ -1,11 +1,7 @@
plugins {
kotlin("multiplatform") version "2.4.10" apply false
alias(libs.plugins.kotlin.multiplatform) apply false
alias(libs.plugins.compose) apply false
alias(libs.plugins.kotlin.compose) apply false
alias(libs.plugins.ktor) apply false
alias(libs.plugins.kotlin.serialization) apply false
}
repositories {
mavenCentral()
}
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>KotlinProject</title>
<link type="text/css" rel="stylesheet" href="styles.css">
</head>
<body style="text-align: center; align-content: center">
<h1>Web Client HTML</h1>
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 50 50" role="presentation">
<circle cx="25" cy="25" r="20" stroke="#ccc" stroke-width="4" fill="none"/>
<circle cx="25" cy="25" r="20" stroke="#333" stroke-width="4" fill="none" stroke-linecap="round"
stroke-dasharray="90 125">
<animateTransform attributeName="transform" type="rotate" from="0 25 25" to="360 25 25" dur="1s"
repeatCount="indefinite"/>
</circle>
</svg>
<script type="application/javascript" src="web.js"></script>
</body>
</html>
+5 -9
View File
@@ -1,5 +1,6 @@
plugins {
kotlin("jvm")
// alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.compose)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.kotlin.serialization)
@@ -7,23 +8,18 @@ plugins {
dependencies {
implementation(project(":shared"))
implementation("com.composables:icons-material-icons-filled-cmp:2.2.1")
implementation(project(":shared_ui"))
// implementation(libs.compose.icons.material)
// implementation("com.composables:icons-material-icons-filled-cmp:2.2.1")
implementation(libs.kotlinx.serialization)
implementation(project(":shared"))
implementation(libs.ktor.serialization.kotlinx.json)
implementation(libs.ktor.client.core)
implementation(libs.ktor.client.cio)
implementation(libs.ktor.client.content.negotiation)
implementation("com.composables:icons-material-icons-filled-cmp:2.2.1")
implementation("org.jetbrains.compose.material3:material3:1.9.0")
implementation(libs.compose.material3)
implementation(compose.desktop.currentOs)
}
repositories {
mavenCentral()
google()
}
compose.desktop {
application {
mainClass = "dev.geekback.dopewars.desktop.Main3Kt"
@@ -3,11 +3,8 @@ package dev.geekback.dopewars.desktop
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import dev.geekback.dopewars.desktop.ApiClient
import dev.geekback.dopewars.shared.Player
import dev.geekback.dopewars.shared.api.TravelRequest
import dev.geekback.dopewars.shared.api.TravelResult
import dev.geekback.dopewars.ui.PlayerUi
class ClientState(player: Player) {
var player by mutableStateOf(player)
@@ -19,7 +19,6 @@ import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import dev.geekback.dopewars.shared.Drug
import dev.geekback.dopewars.shared.DrugInfo
import dev.geekback.dopewars.shared.Drugs
import dev.geekback.dopewars.shared.Location
import dev.geekback.dopewars.shared.Locations
import dev.geekback.dopewars.shared.Market
@@ -2,23 +2,15 @@ package dev.geekback.dopewars.desktop
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.DrawerState
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import com.composables.icons.materialicons.MaterialIcons
import com.composables.icons.materialicons.filled.Menu
import dev.geekback.dopewars.shared.Drug
import dev.geekback.dopewars.shared.Location
import dev.geekback.dopewars.shared.Player
import kotlinx.coroutines.launch
import dev.geekback.dopewars.ui.AppTheme
fun main() {
@@ -6,7 +6,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.ModalDrawerSheet
import androidx.compose.material3.ModalNavigationDrawer
import androidx.compose.material3.NavigationDrawerItem
@@ -22,8 +21,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import com.composables.icons.materialicons.MaterialIcons
import com.composables.icons.materialicons.filled.Login
import dev.geekback.dopewars.ui.AppTheme
import dev.geekback.dopewars.ui.PlayerUi
import dev.geekback.dopewars.ui.TravelUi
import kotlinx.coroutines.launch
@@ -58,11 +56,13 @@ fun App() {
NavigationDrawerItem(
label = {
Row {
/*
Icon(
modifier = Modifier.padding(16.dp),
imageVector = MaterialIcons.Filled.Login,
imageVector = ,
contentDescription = "Login Icon"
)
*/
Text(
modifier = Modifier.padding(16.dp),
text = "Login"
@@ -1,90 +0,0 @@
package dev.geekback.dopewars.ui
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.onClick
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import com.composables.icons.materialicons.MaterialIcons
import com.composables.icons.materialicons.filled.Menu
import dev.geekback.dopewars.shared.Location
import dev.geekback.dopewars.shared.Locations
import dev.geekback.dopewars.shared.Player
import dev.geekback.dopewars.shared.api.TravelRequest
class TravelUi {
/*
Wizard
Holds the users hand through picking a new location
*/
@Composable
fun wizard(current: Player, onTicketPunched: (TravelRequest) -> Unit) {
var stage by remember { mutableStateOf(0) }
var pickedLocation by remember { mutableStateOf(0) }
val locations = Locations.all
var expanded by remember { mutableStateOf(false) }
Window(onCloseRequest = { onTicketPunched(TravelRequest(destination = current.location)) }, title = "Select New Location") {
Card(modifier = Modifier.padding(16.dp).fillMaxSize()) {
Column {
Text("Wizard")
var index = 0;
when (stage) {
0 -> {
Text(text = locations[pickedLocation].name)
IconButton(onClick = { expanded = !expanded }) {
Icon(MaterialIcons.Filled.Menu, contentDescription = "More options")
}
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false }
) {
locations.forEach {
DropdownMenuItem(
text = { Text("$it.name / $index")},
onClick = {
expanded = false
pickedLocation = (index - 1)
println("location index to $index")
}
)
index += 1;
}
}
Button(onClick = {
stage += 1
}) {
Text("Next Step")
}
}
1 -> {
Text("Stage 1")
Button(onClick = {
println("Selected Location - $pickedLocation")
onTicketPunched(TravelRequest(locations[pickedLocation]))
}) {
Text("Finish")
}
}
}
}
}
}
}
}
+2
View File
@@ -0,0 +1,2 @@
org.gradle.configuration-cache=true
org.gradle.jvmargs=-Xmx12g
+12 -1
View File
@@ -2,15 +2,19 @@
ktor = "3.5.1"
kotlin = "2.4.0"
compose = "1.11.1"
compose-material3 = "1.11.0-alpha07"
datetime = "0.8.0"
logback = "1.5.38"
logback = "1.6.3"
serialization = "1.11.0"
kotlin-wrappers = "2026.7.7"
[plugins]
compose = { id = "org.jetbrains.compose", version.ref="compose" }
ktor = { id = "io.ktor.plugin", version.ref="ktor" }
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref="kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref="kotlin" }
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref="kotlin"}
[libraries]
# KotlinX Modules
@@ -34,3 +38,10 @@ ktor-server-logging = { group = "io.ktor", name="ktor-server-call-logging", vers
ktor-client-core = { group = "io.ktor", name = "ktor-client-core", version.ref = "ktor" }
ktor-client-cio = { group = "io.ktor", name = "ktor-client-cio", version.ref="ktor" }
ktor-client-content-negotiation = { group = "io.ktor", name = "ktor-client-content-negotiation", version.ref="ktor" }
# Compose Extras
compose-icons-material = { group = "com.composables", name="icons-material-icons-filled-cmp", version="2.2.1" }
compose-ui = { module = "org.jetbrains.compose.ui:ui", version.ref = "compose" }
wrappers-browser = { module = "org.jetbrains.kotlin-wrappers:kotlin-browser", version.ref = "kotlin-wrappers" }
compose-material3 = { group = "org.jetbrains.compose.material3", name = "material3", version.ref = "compose-material3" }
compose-runtime = { group = "org.jetbrains.compose.runtime", name="runtime", version.ref="compose" }
compose-foundation = { group = "org.jetbrains.compose.foundation", name="foundation", version.ref="compose"}
+43 -18
View File
@@ -4,9 +4,19 @@ plugins {
application
}
repositories {
mavenCentral()
/*
* Files supplied by the web project.
*
* This configuration is resolvable by this project but isn't itself
* published for other projects to consume.
*/
val webFiles = configurations.create("webFiles")
/*
{
isCanBeResolved = true
isCanBeConsumed = false
}
*/
dependencies {
// Ktor
@@ -22,7 +32,12 @@ dependencies {
// Logging
implementation(libs.ktor.server.logging)
implementation(libs.logback)
// Swagger/OpenAPI
implementation(libs.ktor.server.swagger)
implementation(libs.ktor.server.openapi)
implementation(libs.ktor.server.routing.openapi)
implementation(libs.ktor.server.content.negotiation)
// Testing
testImplementation(libs.ktor.server.test.host)
@@ -31,29 +46,39 @@ dependencies {
// Websockets
implementation(libs.ktor.server.websockets)
// Swagger/OpenAPI
implementation(libs.ktor.server.openapi)
implementation(libs.ktor.server.routing.openapi)
implementation(libs.ktor.server.content.negotiation)
// General Use
implementation(libs.kotlinx.datetime)
// Shared application code
implementation(project(":shared"))
implementation("ch.qos.logback:logback-classic:1.6.3")
/*
* Web application's generated files.
*
* This is a build-time dependency, not a runtime dependency.
* The web project exposes the files through its webDistribution
* outgoing configuration.
*/
webFiles(project(path = ":web", configuration = "webDistribution"))
}
application {
mainClass = "dev.geekback.dopewars.server.ServerKt"
}
//
//// Copy the JS+HTML from web to here
//tasks.processResources {
// dependsOn(":web:wasmJsBrowserProductionWebpack")
//
// from(project(":web")
// .layout
// .buildDirectory
// .dir("dist/js/productionExecutable"))
//}
/*
* Copy the web distribution into the server's resources.
*
* The files will end up at:
*
* server/build/resources/main/web/
*
* and therefore inside the server JAR at:
*
* web/
*/
tasks.processResources {
from(webFiles) {
into("web")
}
}
+23
View File
@@ -0,0 +1,23 @@
# Swagger Codegen Ignore
# Generated by swagger-codegen https://github.com/swagger-api/swagger-codegen
# Use this file to prevent files from being overwritten by the generator.
# The patterns follow closely to .gitignore or .dockerignore.
# As an example, the C# client generator defines ApiClient.cs.
# You can make changes and tell Swagger Codgen to ignore just this file by uncommenting the following line:
#ApiClient.cs
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
#foo/*/qux
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
#foo/**/qux
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
# You can also negate patterns with an exclamation (!).
# For example, you can ignore all files in a docs folder with the file extension .md:
#docs/*.md
# Then explicitly reverse the ignore rule for a single file:
#!docs/README.md
+1
View File
@@ -0,0 +1 @@
3.0.81
File diff suppressed because one or more lines are too long
@@ -68,7 +68,7 @@ fun Application.module() {
*/
}
staticResources("/", "")
staticResources("/", "web")
}
}
-3
View File
@@ -1,3 +0,0 @@
<html>
<body><h1>This is the default page.</h1></body>
</html>
+5 -1
View File
@@ -1,5 +1,7 @@
import org.gradle.api.initialization.resolve.RepositoriesMode
rootProject.name = "dopewars"
include(":shared", ":server", ":desktop")
include(":shared", ":shared_ui", ":server", ":desktop", ":web")
pluginManagement {
repositories {
@@ -10,6 +12,8 @@ pluginManagement {
}
dependencyResolutionManagement {
// repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
+14 -6
View File
@@ -1,21 +1,29 @@
plugins {
kotlin("multiplatform")
alias(libs.plugins.kotlin.serialization)
}
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
repositories {
mavenCentral()
plugins {
// kotlin("multiplatform")
alias(libs.plugins.kotlin.multiplatform)
alias(libs.plugins.kotlin.serialization)
}
kotlin {
jvm()
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
browser()
}
sourceSets {
commonMain.dependencies {
implementation(libs.kotlinx.serialization)
implementation(libs.kotlinx.datetime)
}
wasmJsMain.dependencies {
implementation(libs.wrappers.browser)
}
jvmMain.dependencies {}
}
}
+30
View File
@@ -0,0 +1,30 @@
@file:OptIn(ExperimentalWasmDsl::class)
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
plugins {
alias(libs.plugins.kotlin.multiplatform)
}
kotlin {
jvm()
wasmJs {
}
sourceSets {
commonMain.dependencies {
implementation(project(":shared"))
implementation(libs.compose.material3)
implementation(libs.compose.runtime)
implementation(libs.compose.foundation)
implementation(libs.compose.ui)
}
wasmJsMain.dependencies {
implementation(libs.wrappers.browser)
}
jvmMain.dependencies {}
}
}
@@ -1,4 +1,4 @@
package dev.geekback.dopewars.desktop
package dev.geekback.dopewars.ui
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.border
@@ -1,7 +1,6 @@
package dev.geekback.dopewars.ui
import androidx.compose.runtime.Composable
import dev.geekback.dopewars.shared.DrugInfo
import dev.geekback.dopewars.ui.api.ApiUi
interface DopeWarsModelUi<T> {
@@ -0,0 +1,85 @@
package dev.geekback.dopewars.ui
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import dev.geekback.dopewars.shared.Locations
import dev.geekback.dopewars.shared.Player
import dev.geekback.dopewars.shared.api.TravelRequest
class TravelUi {
/*
Wizard
Holds the users hand through picking a new location
*/
@Composable
fun wizard(current: Player, onTicketPunched: (TravelRequest) -> Unit) {
var stage by remember { mutableStateOf(0) }
var pickedLocation by remember { mutableStateOf(0) }
val locations = Locations.all
var expanded by remember { mutableStateOf(false) }
// Window(onCloseRequest = { onTicketPunched(TravelRequest(destination = current.location)) }, title = "Select New Location") {
/*
Card(modifier = Modifier.padding(16.dp).fillMaxSize()) {
Column {
Text("Wizard")
var index = 0;
when (stage) {
0 -> {
Text(text = locations[pickedLocation].name)
IconButton(onClick = { expanded = !expanded }) {
// Icon(MaterialIcons.Filled.Menu, contentDescription = "More options")
}
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false }
) {
locations.forEach {
DropdownMenuItem(
text = { Text("$it.name / $index")},
onClick = {
expanded = false
pickedLocation = (index - 1)
println("location index to $index")
}
)
index += 1;
}
}
Button(onClick = {
stage += 1
}) {
Text("Next Step")
}
}
1 -> {
Text("Stage 1")
Button(onClick = {
println("Selected Location - $pickedLocation")
onTicketPunched(TravelRequest(locations[pickedLocation]))
}) {
Text("Finish")
}
}
}
}
}
*/
// }
}
}
@@ -9,8 +9,8 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import dev.geekback.dopewars.desktop.BorderedCard
import dev.geekback.dopewars.shared.api.MarketRequest
import dev.geekback.dopewars.ui.BorderedCard
import dev.geekback.dopewars.ui.DopeWarsModelUi
import dev.geekback.dopewars.ui.LocationUi
@@ -1,8 +1,8 @@
package dev.geekback.dopewars.ui.api
import androidx.compose.runtime.Composable
import dev.geekback.dopewars.desktop.BorderedCard
import dev.geekback.dopewars.shared.api.MarketResult
import dev.geekback.dopewars.ui.BorderedCard
import dev.geekback.dopewars.ui.DopeWarsModelUi
import dev.geekback.dopewars.ui.MarketUi
@@ -3,9 +3,9 @@ package dev.geekback.dopewars.ui.api
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import dev.geekback.dopewars.desktop.BorderedCard
import dev.geekback.dopewars.ui.DopeWarsModelUi
import dev.geekback.dopewars.shared.api.PlayerNameUpdateRequest
import dev.geekback.dopewars.ui.BorderedCard
class PlayerNameUpdateRequestUi(): DopeWarsModelUi<PlayerNameUpdateRequest> {
@Composable
@@ -1,10 +1,10 @@
package dev.geekback.dopewars.ui.api
import androidx.compose.runtime.Composable
import dev.geekback.dopewars.desktop.BorderedCard
import dev.geekback.dopewars.ui.DopeWarsModelUi
import dev.geekback.dopewars.ui.PlayerUi
import dev.geekback.dopewars.shared.api.PlayerNameUpdateResponse
import dev.geekback.dopewars.ui.BorderedCard
class PlayerNameUpdateResponseUi: DopeWarsModelUi<PlayerNameUpdateResponse> {
@Composable
@@ -8,8 +8,8 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import dev.geekback.dopewars.desktop.BorderedCard
import dev.geekback.dopewars.shared.api.TradeRequest
import dev.geekback.dopewars.ui.BorderedCard
import dev.geekback.dopewars.ui.DopeWarsModelUi
class TradeRequestUi: DopeWarsModelUi<TradeRequest> {
@@ -2,9 +2,9 @@ package dev.geekback.dopewars.ui.api
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import dev.geekback.dopewars.desktop.BorderedCard
import dev.geekback.dopewars.ui.DopeWarsModelUi
import dev.geekback.dopewars.shared.api.TravelRequest
import dev.geekback.dopewars.ui.BorderedCard
class TravelRequestUi: DopeWarsModelUi<TravelRequest> {
@Composable
@@ -8,8 +8,8 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import dev.geekback.dopewars.desktop.BorderedCard
import dev.geekback.dopewars.shared.api.TravelResult
import dev.geekback.dopewars.ui.BorderedCard
import dev.geekback.dopewars.ui.DopeWarsModelUi
import dev.geekback.dopewars.ui.LocationUi
import dev.geekback.dopewars.ui.MarketUi
@@ -38,15 +38,17 @@ class TravelResponseUi: DopeWarsModelUi<TravelResult> {
println("Update location to $it")
}
Row {
Button(onClick = {}) {
Text("Apply")
}
Button(onClick = {}) {
Text("Cancel")
}
}
//
//
// Row {
// Button(onClick = {}) {
// Text("Apply")
// }
//
// Button(onClick = {}) {
// Text("Cancel")
// }
// }
}
}
@@ -3,7 +3,6 @@ package dev.geekback.dopewars.ui
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.material3.Button
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.RangeSlider
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
@@ -13,7 +12,6 @@ import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import dev.geekback.dopewars.desktop.BorderedCard
import dev.geekback.dopewars.shared.DRUG_MAX_PRICE
import dev.geekback.dopewars.shared.Drug
import dev.geekback.dopewars.shared.DrugInfo
@@ -3,7 +3,6 @@ package dev.geekback.dopewars.ui
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import dev.geekback.dopewars.desktop.BorderedCard
import dev.geekback.dopewars.shared.InventoryItem
class InventoryUi: DopeWarsModelUi<List<InventoryItem>> {
@@ -10,7 +10,6 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import dev.geekback.dopewars.desktop.BorderedCard
import dev.geekback.dopewars.shared.Location
class LocationUi: DopeWarsModelUi<Location> {
@@ -4,7 +4,6 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import dev.geekback.dopewars.desktop.BorderedCard
import dev.geekback.dopewars.shared.Market
class MarketUi: DopeWarsModelUi<Market> {
@@ -4,9 +4,6 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import dev.geekback.dopewars.desktop.BorderedCard
import dev.geekback.dopewars.shared.Location
import dev.geekback.dopewars.shared.Market
import dev.geekback.dopewars.shared.Player
import kotlin.time.Instant
+66
View File
@@ -0,0 +1,66 @@
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
plugins {
alias(libs.plugins.kotlin.multiplatform)
alias(libs.plugins.compose)
alias(libs.plugins.kotlin.compose)
}
kotlin {
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
browser()
binaries.executable()
}
sourceSets {
commonMain.dependencies {
implementation(project(":shared"))
implementation(compose.runtime)
implementation(compose.foundation)
implementation(compose.material3)
implementation(compose.ui)
}
wasmJsMain.dependencies {
}
}
}
/*
* The directory produced by wasmJsBrowserDistribution.
*
* This is deliberately kept here rather than being referenced by
* the server project. The web project owns its build layout.
*/
val webDistributionDirectory =
layout.buildDirectory.dir("dist/wasmJs/productionExecutable")
/*
* Expose the web distribution as an outgoing artifact.
*
* Other projects can consume:
*
* project(path = ":web", configuration = "webDistribution")
*
* without knowing where the files actually live.
*/
val webDistributionTask =
tasks.named("wasmJsBrowserDistribution")
configurations {
create("webDistribution") {
isCanBeConsumed = true
isCanBeResolved = false
}
}
artifacts {
add(
"webDistribution",
webDistributionDirectory
) {
builtBy(webDistributionTask)
}
}
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>KotlinProject</title>
<link type="text/css" rel="stylesheet" href="styles.css">
</head>
<body style="text-align: center; align-content: center">
<h1>Web Client HTML</h1>
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 50 50" role="presentation">
<circle cx="25" cy="25" r="20" stroke="#ccc" stroke-width="4" fill="none"/>
<circle cx="25" cy="25" r="20" stroke="#333" stroke-width="4" fill="none" stroke-linecap="round"
stroke-dasharray="90 125">
<animateTransform attributeName="transform" type="rotate" from="0 25 25" to="360 25 25" dur="1s"
repeatCount="indefinite"/>
</circle>
</svg>
<script type="application/javascript" src="web.js"></script>
</body>
</html>
@@ -0,0 +1,24 @@
package dev.geekback.dopewars.web
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.material3.Text
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.ComposeViewport
@OptIn(ExperimentalComposeUiApi::class)
fun main() {
ComposeViewport {
App()
}
}
@Composable
fun App() {
MaterialTheme {
Text(modifier = Modifier.padding(16.dp), text = "Taxation is theft")
}
}
+21
View File
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>KotlinProject</title>
<link type="text/css" rel="stylesheet" href="styles.css">
</head>
<body style="text-align: center; align-content: center">
<h1>Web Client HTML</h1>
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 50 50" role="presentation">
<circle cx="25" cy="25" r="20" stroke="#ccc" stroke-width="4" fill="none"/>
<circle cx="25" cy="25" r="20" stroke="#333" stroke-width="4" fill="none" stroke-linecap="round"
stroke-dasharray="90 125">
<animateTransform attributeName="transform" type="rotate" from="0 25 25" to="360 25 25" dur="1s"
repeatCount="indefinite"/>
</circle>
</svg>
<script type="application/javascript" src="web.js"></script>
</body>
</html>
+7
View File
@@ -0,0 +1,7 @@
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}