initial commit

This commit is contained in:
Hartmut Bauer committed 2026-10-09 17:13:39 +02:00
commit d84861949f
92 files changed
+8425

No files matched your search

+27
View File
@@ -0,0 +1,27 @@
plugins {
alias(libs.plugins.kotlin.multiplatform)
}
kotlin {
js {
browser()
binaries.executable()
}
sourceSets {
jsMain.dependencies {
implementation(npm("htmx.org", libs.versions.htmx.get()))
implementation(npm("html5-qrcode", libs.versions.html5Qrcode.get()))
// implementation(npm("@popperjs/core", "2.11.8"))
// implementation(npm("bootstrap", "5.3.8"))
// implementation(npm("tailwindcss", "*"))
}
}
sourceSets {
commonMain.dependencies {
implementation(project(":shared"))
}
}
}
+30
View File
@@ -0,0 +1,30 @@
@file:JsModule("html5-qrcode")
@file:JsNonModule
package net.keinekekse.app
/* AI sagt:
In Kotlin/JS werden Named Exports eines npm-Pakets deklariert, indem die
Annotation auf Dateiebene (@file:JsModule) gesetzt wird. Dadurch weiß der
Compiler, dass jede Top-Level-Klasse in dieser Datei ein benannter Export
des Moduls ist.
*/
external class Html5QrcodeScanner(
elementId: String,
config: dynamic,
verbose: Boolean = definedExternally,
) {
fun render(
successCallback: (decodedText: String, decodedResult: dynamic) -> Unit,
errorCallback: ((errorMessage: String) -> Unit)? = definedExternally,
)
fun clear(): dynamic
}
external class Html5Qrcode(
elementId: String,
config: dynamic = definedExternally,
) {
// Falls später benötigt
}
+5
View File
@@ -0,0 +1,5 @@
package net.keinekekse.app
@JsModule("htmx.org")
@JsNonModule
external object htmx
+71
View File
@@ -0,0 +1,71 @@
package net.keinekekse.app
import kotlinx.browser.document
import kotlinx.browser.window
import net.keinekekse.app.books.isbnScanResultPath
fun main() {
window.asDynamic().renderScanner = ::renderScanner
// htmx reference is needed for import
console.log("htmx loaded ", htmx)
// event handling for htmx
var rowCount = 1
document.addEventListener("htmx:beforeSwap", {
document.getElementById("total-count")?.innerHTML = "Total: ${(++rowCount) * 10}"
})
// Scroll to the bottom of the page after adding content
document.addEventListener("htmx:afterSwap", {
val scrollHeight = document.body?.scrollHeight ?: 0
window.scrollTo(0.0, scrollHeight.toDouble())
})
}
/*
Die fn wird in den Namensraum <project>.<package> veröffentlicht
-> web.net.keinekekse.app
Wenn die fn ohne den Rattenschwanz aufgerufen werden soll, muss sie
in main() so exportiert werden:
js("window.renderScanner = renderScanner")
oder
window.asDynamic().renderScanner = ::renderScanner
Des Weiteren benötigt sie ein DOM-Element (id="reader"), daher ist es sinnvoll,
sie erst bei geladenem DOM auszuführen:
document.addEventListener('DOMContentLoaded', function () {
renderScanner();
})
Beim Import von htmx wird der gesamte Import an ein Objekt namens "default"
gehängt. Da sich auch Ktor auf diesen Import verlässt, ist es hier besser,
zu kapitulieren und das Objekt als htmx.asDynamic().default.<whatever> zu
verwenden.
*/
@OptIn(ExperimentalJsExport::class)
@JsExport
fun renderScanner(scanTarget: String = "reader") {
// Ausgabebereich fürs Ergebnis zurücksetzen
document.getElementById("scan-result")?.innerHTML =
"<p>Bitte Barcode vor die Kamera halten...</p>"
// Scanner öffnen
val config = js("{ fps: 10, qrbox: { width: 300, height: 180 } }")
val scanner = Html5QrcodeScanner(scanTarget, config, /*debug*/ false)
// Ergebnis verarbeiten
scanner.render({ decodedText, _ ->
scanner.clear()
document.getElementById("scan-result")?.innerHTML =
"<p>Hole Daten für ISBN: $decodedText</p>"
console.log("Scanned: $decodedText")
// console.log("htmx: ", htmx)
htmx.asDynamic().default.ajax(
"GET",
"${isbnScanResultPath}/" + js("encodeURIComponent(decodedText)"),
js("{ target: '#scan-result', swap: 'innerHTML' }")
)
}, { errorMessage ->
console.log("Error: $errorMessage")
})
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB