fixing client

This commit is contained in:
Stefan Mogeritsch 2025-09-17 01:31:53 +02:00
parent 763fe7f261
commit 91012d51e7
13 changed files with 186 additions and 119 deletions

View File

@ -1,8 +1,8 @@
@file:OptIn(ExperimentalWasmDsl::class)
import jdk.jfr.Experimental
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
import org.gradle.api.tasks.Copy
import org.gradle.api.tasks.Sync
import org.gradle.api.file.DuplicatesStrategy
plugins {
alias(libs.plugins.kotlin.multiplatform)
@ -50,60 +50,83 @@ kotlin {
}
js(IR) {
// Disable browser-based tests (Karma/Chrome) to avoid ChromeHeadless issues
browser {
commonWebpackConfig {
outputFileName = "meldestelle-client.js"
// Enable CSS support and optimization
cssSupport {
enabled.set(true)
}
// Webpack performance optimizations for smaller bundles
devServer?.apply {
open = false
port = 8080
}
}
testTask {
// Prevent launching ChromeHeadless (snap permission issues on some systems)
// Disable browser tests due to ChromeHeadless permission issues
enabled = false
}
}
// Run JS tests in Node.js instead (no browser needed)
nodejs {
testTask {
useMocha()
webpackTask {
// Production optimizations
args.add("--mode=production")
args.add("--optimization-minimize")
}
runTask {
// Development optimizations
args.add("--mode=development")
}
}
// Use Node.js for testing instead of browser
nodejs {
testTask {
useMocha {
timeout = "10s"
}
}
}
binaries.executable()
}
@OptIn(ExperimentalWasmDsl::class)
@OptIn(Experimental::class)
wasmJs {
// Disable browser-based tests for WASM as well to avoid Karma/Chrome
browser {
commonWebpackConfig {
outputFileName = "meldestelle-wasm.js"
// Enable CSS support for better bundling
cssSupport {
enabled.set(true)
}
// WASM-specific webpack optimizations handled by webpack.config.d files
}
testTask {
// Disable WASM browser tests due to environment issues
enabled = false
}
webpackTask {
// Production optimizations for WASM
args.add("--mode=production")
args.add("--optimization-minimize")
}
}
binaries.executable()
// WASM-specific compiler optimizations for smaller bundles
compilations.all {
compileTaskProvider.configure {
compilerOptions {
freeCompilerArgs.addAll(
"-Xwasm-use-new-exception-proposal", // Use efficient WASM exception handling
"-Xwasm-debugger-custom-formatters", // Optimize debug info for smaller size
"-Xwasm-enable-array-range-checks", // Optimize array bounds checking
"-Xwasm-generate-wat=false", // Skip WAT generation for smaller output
"-opt-in=kotlin.ExperimentalStdlibApi", // Enable stdlib optimizations
"-opt-in=kotlin.js.ExperimentalJsExport" // Enable JS export optimizations
"-Xwasm-use-new-exception-proposal",
"-Xwasm-debugger-custom-formatters",
"-Xwasm-enable-array-range-checks",
"-Xwasm-generate-wat=false",
"-opt-in=kotlin.ExperimentalStdlibApi",
"-opt-in=kotlin.js.ExperimentalJsExport"
)
}
}
}
binaries.executable()
}
sourceSets {

View File

@ -1,57 +1,36 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="64dp"
android:height="64dp"
android:viewportWidth="64"
android:viewportHeight="64">
<!-- Outer hexagon -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="600dp"
android:height="600dp"
android:viewportWidth="600"
android:viewportHeight="600">
<path
android:pathData="M56.25,18V46L32,60L7.75,46V18L32,4Z"
android:fillColor="#6075f2"/>
<!-- Right side face -->
android:pathData="M301.21,418.53C300.97,418.54 300.73,418.56 300.49,418.56C297.09,418.59 293.74,417.72 290.79,416.05L222.6,377.54C220.63,376.43 219,374.82 217.85,372.88C216.7,370.94 216.09,368.73 216.07,366.47L216.07,288.16C216.06,287.32 216.09,286.49 216.17,285.67C216.38,283.54 216.91,281.5 217.71,279.6L199.29,268.27L177.74,256.19C175.72,260.43 174.73,265.23 174.78,270.22L174.79,387.05C174.85,393.89 178.57,400.2 184.53,403.56L286.26,461.02C290.67,463.51 295.66,464.8 300.73,464.76C300.91,464.76 301.09,464.74 301.27,464.74C301.24,449.84 301.22,439.23 301.22,439.23L301.21,418.53Z"
android:fillColor="#041619"
android:fillType="nonZero"/>
<path
android:pathData="M41.5,26.5V37.5L32,43V60L56.25,46V18Z"
android:fillColor="#6b57ff"/>
<!-- Left side face with radial gradient -->
android:pathData="M409.45,242.91L312.64,188.23C303.64,183.15 292.58,183.26 283.68,188.51L187.92,245C183.31,247.73 179.93,251.62 177.75,256.17L177.74,256.19L199.29,268.27L217.71,279.6C217.83,279.32 217.92,279.02 218.05,278.74C218.24,278.36 218.43,277.98 218.64,277.62C219.06,276.88 219.52,276.18 220.04,275.51C221.37,273.8 223.01,272.35 224.87,271.25L289.06,233.39C290.42,232.59 291.87,231.96 293.39,231.51C295.53,230.87 297.77,230.6 300,230.72C302.98,230.88 305.88,231.73 308.47,233.2L373.37,269.85C375.54,271.08 377.49,272.68 379.13,274.57C379.68,275.19 380.18,275.85 380.65,276.53C380.86,276.84 381.05,277.15 381.24,277.47L397.79,266.39L420.34,252.93L420.31,252.88C417.55,248.8 413.77,245.35 409.45,242.91Z"
android:fillColor="#37BF6E"
android:fillType="nonZero"/>
<path
android:pathData="M32,43L22.5,37.5V26.5L7.75,18V46L32,60Z">
<aapt:attr name="android:fillColor">
<gradient
android:centerX="23.13"
android:centerY="18.44"
android:gradientRadius="42.13"
android:type="radial">
<item
android:offset="0"
android:color="#FF5383EC"/>
<item
android:offset="0.867"
android:color="#FF7F52FF"/>
</gradient>
</aapt:attr>
</path>
<!-- Top face with linear gradient -->
android:pathData="M381.24,277.47C381.51,277.92 381.77,278.38 382.01,278.84C382.21,279.24 382.39,279.65 382.57,280.06C382.91,280.88 383.19,281.73 383.41,282.59C383.74,283.88 383.92,285.21 383.93,286.57L383.93,361.1C383.96,363.95 383.35,366.77 382.16,369.36C381.93,369.86 381.69,370.35 381.42,370.83C379.75,373.79 377.32,376.27 374.39,378L310.2,415.87C307.47,417.48 304.38,418.39 301.21,418.53L301.22,439.23C301.22,439.23 301.24,449.84 301.27,464.74C306.1,464.61 310.91,463.3 315.21,460.75L410.98,404.25C419.88,399 425.31,389.37 425.22,379.03L425.22,267.85C425.17,262.48 423.34,257.34 420.34,252.93L397.79,266.39L381.24,277.47Z"
android:fillColor="#3870B2"
android:fillType="nonZero"/>
<path
android:pathData="M22.5,26.5L32,21L41.5,26.5L56.25,18L32,4L7.75,18Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="44.17"
android:startY="4.38"
android:endX="17.97"
android:endY="34.04"
android:type="linear">
<item
android:offset="0"
android:color="#FF33C3FF"/>
<item
android:offset="0.878"
android:color="#FF5383EC"/>
</gradient>
</aapt:attr>
</path>
<!-- Center diamond -->
android:pathData="M177.75,256.17C179.93,251.62 183.31,247.73 187.92,245L283.68,188.51C292.58,183.26 303.64,183.15 312.64,188.23L409.45,242.91C413.77,245.35 417.55,248.8 420.31,252.88L420.34,252.93L498.59,206.19C494.03,199.46 487.79,193.78 480.67,189.75L320.86,99.49C306.01,91.1 287.75,91.27 273.07,99.95L114.99,193.2C107.39,197.69 101.81,204.11 98.21,211.63L177.74,256.19L177.75,256.17ZM301.27,464.74C301.09,464.74 300.91,464.76 300.73,464.76C295.66,464.8 290.67,463.51 286.26,461.02L184.53,403.56C178.57,400.2 174.85,393.89 174.79,387.05L174.78,270.22C174.73,265.23 175.72,260.43 177.74,256.19L98.21,211.63C94.86,218.63 93.23,226.58 93.31,234.82L93.31,427.67C93.42,438.97 99.54,449.37 109.4,454.92L277.31,549.77C284.6,553.88 292.84,556.01 301.2,555.94L301.2,555.8C301.39,543.78 301.33,495.26 301.27,464.74Z"
android:strokeWidth="10"
android:fillColor="#00000000"
android:strokeColor="#083042"
android:fillType="nonZero"/>
<path
android:pathData="M32,21L41.5,26.5V37.5L32,43L22.5,37.5V26.5Z"
android:fillColor="#000000"/>
android:pathData="M498.59,206.19L420.34,252.93C423.34,257.34 425.17,262.48 425.22,267.85L425.22,379.03C425.31,389.37 419.88,399 410.98,404.25L315.21,460.75C310.91,463.3 306.1,464.61 301.27,464.74C301.33,495.26 301.39,543.78 301.2,555.8L301.2,555.94C309.48,555.87 317.74,553.68 325.11,549.32L483.18,456.06C497.87,447.39 506.85,431.49 506.69,414.43L506.69,230.91C506.6,222.02 503.57,213.5 498.59,206.19Z"
android:strokeWidth="10"
android:fillColor="#00000000"
android:strokeColor="#083042"
android:fillType="nonZero"/>
<path
android:pathData="M301.2,555.94C292.84,556.01 284.6,553.88 277.31,549.76L109.4,454.92C99.54,449.37 93.42,438.97 93.31,427.67L93.31,234.82C93.23,226.58 94.86,218.63 98.21,211.63C101.81,204.11 107.39,197.69 114.99,193.2L273.07,99.95C287.75,91.27 306.01,91.1 320.86,99.49L480.67,189.75C487.79,193.78 494.03,199.46 498.59,206.19C503.57,213.5 506.6,222.02 506.69,230.91L506.69,414.43C506.85,431.49 497.87,447.39 483.18,456.06L325.11,549.32C317.74,553.68 309.48,555.87 301.2,555.94Z"
android:strokeWidth="10"
android:fillColor="#00000000"
android:strokeColor="#083042"
android:fillType="nonZero"/>
</vector>

View File

@ -0,0 +1,17 @@
package at.mocode
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.window.CanvasBasedWindow
import androidx.compose.ui.window.ComposeViewport
import kotlinx.browser.document
@OptIn(ExperimentalComposeUiApi::class)
fun main() {
ComposeViewport(document.getElementById("ComposeTarget")!!) {
App()
}
// CanvasBasedWindow(canvasElementId = "ComposeTarget") {
// App()
// }
}

View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Meldestelle</title>
<link type="text/css" rel="stylesheet" href="styles.css">
</head>
<body>
<div id="ComposeTarget"></div>
<script type="application/javascript" src="meldestelle-wasm.js"></script>
</body>
</html>

View File

@ -0,0 +1,7 @@
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}

View File

@ -1,5 +1,7 @@
package at.mocode
import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.runtime.Composable
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
@ -10,4 +12,10 @@ fun main() = application {
) {
App()
}
}
}
@Preview
@Composable
fun AppDesktopPreview() {
App()
}

View File

@ -1,12 +1,18 @@
package at.mocode
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.window.CanvasBasedWindow
import androidx.compose.ui.window.ComposeViewport
import kotlinx.browser.document
@OptIn(ExperimentalComposeUiApi::class)
fun main() {
ComposeViewport(document.body!!) {
// ComposeViewport(document.getElementById("ComposeTarget")!!) {
// App()
// }
CanvasBasedWindow(canvasElementId = "ComposeTarget") {
App()
}
}
}

View File

@ -5,8 +5,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Meldestelle</title>
<link type="text/css" rel="stylesheet" href="styles.css">
<script type="application/javascript" src="meldestelle-wasm.js"></script>
</head>
<body>
<canvas id="ComposeTarget"></canvas>
<script type="application/javascript" src="meldestelle-wasm.js"></script>
</body>
</html>

View File

@ -65,10 +65,10 @@ config.optimization = {
// Performance optimization
config.performance = {
...(config.performance || {}),
// Increase hint limits for WASM (which is naturally larger)
maxAssetSize: 2000000, // 2MB for individual assets
maxEntrypointSize: 2000000, // 2MB for entrypoints
hints: 'warning'
// Realistic hint limits for WASM bundles (which are naturally larger)
maxAssetSize: 20000000, // 20MB for individual assets (WASM files can be large)
maxEntrypointSize: 5000000, // 5MB for entrypoints
hints: 'warning' // Show warnings but don't fail the build
};
// Resolve optimization for faster builds
@ -95,8 +95,10 @@ if (config.mode === 'production') {
// Production-specific optimizations
config.output = {
...(config.output || {}),
// Better file names for caching
filename: '[name].[contenthash:8].js',
// Use conditional filename to match HTML template expectations for main chunk only
filename: (chunkData) => {
return chunkData.chunk.name === 'main' ? 'meldestelle-wasm.js' : '[name].[contenthash:8].js';
},
chunkFilename: '[name].[contenthash:8].chunk.js'
};

View File

@ -54,39 +54,39 @@ services:
# ===================================================================
# Desktop Application (Kotlin Desktop + VNC)
# ===================================================================
desktop-app:
build:
context: .
dockerfile: dockerfiles/clients/desktop-app/Dockerfile
container_name: meldestelle-desktop-app
environment:
# API Configuration - fallback to external gateway if not in same compose network
API_BASE_URL: http://${GATEWAY_HOST:-api-gateway}:${GATEWAY_PORT:-8081}
# VNC Configuration
DISPLAY: ":99"
VNC_PORT: "5901"
NOVNC_PORT: "6080"
# App Information
APP_TITLE: ${APP_NAME:-Meldestelle}
APP_VERSION: ${APP_VERSION:-1.0.0}
ports:
- "6080:6080" # Web-based VNC (noVNC)
- "5901:5901" # VNC direct access
networks:
- meldestelle-network
# depends_on removed for standalone client deployment
# When using multi-file setup, api-gateway dependency is handled externally
healthcheck:
test: [ "CMD", "/opt/health-check.sh" ]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
restart: unless-stopped
labels:
- "traefik.enable=true"
- "traefik.http.routers.desktop-app.rule=Host(`localhost`) && PathPrefix(`/desktop`)"
- "traefik.http.services.desktop-app.loadbalancer.server.port=6080"
# desktop-app:
# build:
# context: .
# dockerfile: dockerfiles/clients/desktop-app/Dockerfile
# container_name: meldestelle-desktop-app
# environment:
# # API Configuration - fallback to external gateway if not in same compose network
# API_BASE_URL: http://${GATEWAY_HOST:-api-gateway}:${GATEWAY_PORT:-8081}
# # VNC Configuration
# DISPLAY: ":99"
# VNC_PORT: "5901"
# NOVNC_PORT: "6080"
# # App Information
# APP_TITLE: ${APP_NAME:-Meldestelle}
# APP_VERSION: ${APP_VERSION:-1.0.0}
# ports:
# - "6080:6080" # Web-based VNC (noVNC)
# - "5901:5901" # VNC direct access
# networks:
# - meldestelle-network
# # depends_on removed for standalone client deployment
# # When using multi-file setup, api-gateway dependency is handled externally
# healthcheck:
# test: [ "CMD", "/opt/health-check.sh" ]
# interval: 30s
# timeout: 10s
# retries: 3
# start_period: 60s
# restart: unless-stopped
# labels:
# - "traefik.enable=true"
# - "traefik.http.routers.desktop-app.rule=Host(`localhost`) && PathPrefix(`/desktop`)"
# - "traefik.http.services.desktop-app.loadbalancer.server.port=6080"
# ===================================================================
# Auth Server (Custom Keycloak Extension)

View File

@ -28,8 +28,8 @@ RUN chmod +x ./gradlew
# Dependencies downloaden (für besseres Caching)
RUN ./gradlew :client:dependencies --no-configure-on-demand
# Kotlin/JS Web-App kompilieren
RUN ./gradlew :client:jsBrowserDistribution --no-configure-on-demand
# Kotlin/WASM Web-App kompilieren
RUN ./gradlew :client:wasmJsBrowserDistribution --no-configure-on-demand
# ===================================================================
# Stage 2: Runtime Stage - Nginx für Static Files + API Proxy
@ -40,7 +40,7 @@ FROM nginx:1.25-alpine
RUN apk add --no-cache curl
# Kopiere kompilierte Web-App von Build-Stage
COPY --from=builder /app/client/build/dist/js/productionExecutable/ /usr/share/nginx/html/
COPY --from=builder /app/client/build/dist/wasmJs/productionExecutable/ /usr/share/nginx/html/
# Kopiere Nginx-Konfiguration
COPY dockerfiles/clients/web-app/nginx.conf /etc/nginx/nginx.conf

View File

@ -1,9 +1,13 @@
#Android
android.useAndroidX=true
android.nonTransitiveRClass=true
# Kotlin Configuration
kotlin.code.style=official
kotlin.daemon.jvmargs=-Xmx3072M -XX:+UseParallelGC -XX:MaxMetaspaceSize=1024M
# Gradle Configuration
org.gradle.jvmargs=-Xmx3072M -Dfile.encoding=UTF-8 -XX:+UseParallelGC -XX:MaxMetaspaceSize=1024M -XX:+HeapDumpOnOutOfMemoryError -Xshare:off -Djava.awt.headless=true
org.gradle.jvmargs=-Xmx3072M -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx2048M" -XX:+UseParallelGC -XX:MaxMetaspaceSize=1024M -XX:+HeapDumpOnOutOfMemoryError -Xshare:off -Djava.awt.headless=true
org.gradle.parallel=true
org.gradle.caching=true
# org.gradle.configureondemand=true # Deprecated - removed for Gradle 9.0 compatibility
@ -24,6 +28,7 @@ io.ktor.development=true
# IDE Configuration
kotlin.mpp.androidSourceSetLayoutVersion=2
kotlin.mpp.enableCInteropCommonization=true
org.jetbrains.kotlin.wasm.check.wasm.binary.format=false
kotlin.native.ignoreDisabledTargets=true
idea.project.settings.delegate.build.run.actions.to.gradle=true
@ -34,6 +39,7 @@ kotlin.build.report.single_file=false
# Compose Experimental Features
org.jetbrains.compose.experimental.jscanvas.enabled=true
org.jetbrains.compose.experimental.wasm.enabled=true
# Java Toolchain: ensure Gradle auto-downloads a full JDK when needed
org.gradle.java.installations.auto-download=true

View File

@ -3,6 +3,9 @@
# Last updated: 2025-07-31
[versions]
# --- Android Ecosystem ---
agp = "8.1.4"
# --- Kotlin Ecosystem ---
kotlin = "2.2.10"
kotlinx = "1.9.0"
@ -277,6 +280,8 @@ spring-cloud-gateway = [
[plugins]
androidApplication = { id = "com.android.application", version.ref = "agp" }
androidLibrary = { id = "com.android.library", version.ref = "agp" }
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }