Desktop: dev loop (desktop-dev.sh, auto-restart on source edits), SHONAR_DEV engine --reload, build stamp in window title, detail screen reloads report rewrites + surfaces fetch errors
This commit is contained in:
parent
67b1eb65c7
commit
31e03262fd
3 changed files with 77 additions and 4 deletions
|
|
@ -328,7 +328,12 @@ class DesktopState(private val appDir: File = defaultAppDir()) {
|
||||||
}
|
}
|
||||||
stopEngine()
|
stopEngine()
|
||||||
apiProc = spawn(backend, File(venv, "uvicorn").absolutePath,
|
apiProc = spawn(backend, File(venv, "uvicorn").absolutePath,
|
||||||
listOf("shonar.main:app", "--port", "8000"), env)
|
listOf("shonar.main:app", "--port", "8000") +
|
||||||
|
// Dev mode: engine hot-reloads on backend source edits.
|
||||||
|
(if (System.getenv("SHONAR_DEV") == "1")
|
||||||
|
listOf("--reload", "--reload-dir", "shonar")
|
||||||
|
else emptyList()),
|
||||||
|
env)
|
||||||
var up = false
|
var up = false
|
||||||
var waited = 0.0
|
var waited = 0.0
|
||||||
while (waited < 120.0) {
|
while (waited < 120.0) {
|
||||||
|
|
@ -746,9 +751,23 @@ class DesktopState(private val appDir: File = defaultAppDir()) {
|
||||||
// If the file is mid-pipeline (pump running, or reprocess started
|
// If the file is mid-pipeline (pump running, or reprocess started
|
||||||
// elsewhere), show live stage/progress instead of a stale report.
|
// elsewhere), show live stage/progress instead of a stale report.
|
||||||
while (true) {
|
while (true) {
|
||||||
val jobs = runCatching { parseJobs(provider.fetchJobs(remoteId)) }
|
val fetched = runCatching { parseJobs(provider.fetchJobs(remoteId)) }
|
||||||
.getOrNull().orEmpty()
|
if (fetched.isFailure && d.jobs.isEmpty()) {
|
||||||
|
// Don't fail silently: a dead session used to render as
|
||||||
|
// an idle screen, indistinguishable from "never started".
|
||||||
|
d.error = "Couldn't read job status from the engine: " +
|
||||||
|
(fetched.exceptionOrNull()?.message ?: "unknown error")
|
||||||
|
} else if (fetched.isSuccess) {
|
||||||
|
d.error = null
|
||||||
|
}
|
||||||
|
val jobs = fetched.getOrNull().orEmpty()
|
||||||
if (_detail.value?.file != file) break
|
if (_detail.value?.file != file) break
|
||||||
|
// Pick up report rewrites made by the pump/completion paths
|
||||||
|
// while this screen sits open (they write the file, not us).
|
||||||
|
if (d.transcript == null) {
|
||||||
|
val fresh = runCatching { reportFile(file).readText() }.getOrNull()
|
||||||
|
if (fresh != null && fresh != d.reportText) d.reportText = fresh
|
||||||
|
}
|
||||||
d.jobs = jobs
|
d.jobs = jobs
|
||||||
jobLabel(jobs)?.let { d.busy = it } ?: run { d.busy = null }
|
jobLabel(jobs)?.let { d.busy = it } ?: run { d.busy = null }
|
||||||
_detail.value = d.copy()
|
_detail.value = d.copy()
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,17 @@ fun main() = application {
|
||||||
var zoom by remember { mutableStateOf(1f) }
|
var zoom by remember { mutableStateOf(1f) }
|
||||||
Window(
|
Window(
|
||||||
onCloseRequest = ::exitApplication,
|
onCloseRequest = ::exitApplication,
|
||||||
title = "SHONAR Desktop",
|
title = "SHONAR Desktop" + run {
|
||||||
|
// Build stamp so "which window is this?" is answerable at a glance.
|
||||||
|
val jar = java.io.File(
|
||||||
|
System.getProperty("java.class.path", "").split(":")
|
||||||
|
.firstOrNull { it.endsWith(".jar") } ?: "")
|
||||||
|
if (jar.exists()) {
|
||||||
|
val fmt = java.time.format.DateTimeFormatter.ofPattern("HH:mm")
|
||||||
|
.withZone(java.time.ZoneId.systemDefault())
|
||||||
|
" · build " + fmt.format(java.time.Instant.ofEpochMilli(jar.lastModified()))
|
||||||
|
} else ""
|
||||||
|
},
|
||||||
state = rememberWindowState(width = 1100.dp, height = 800.dp),
|
state = rememberWindowState(width = 1100.dp, height = 800.dp),
|
||||||
) {
|
) {
|
||||||
val baseDensity = LocalDensity.current
|
val baseDensity = LocalDensity.current
|
||||||
|
|
|
||||||
44
desktop/desktop-dev.sh
Executable file
44
desktop/desktop-dev.sh
Executable file
|
|
@ -0,0 +1,44 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# SHONAR desktop dev loop: runs the app and auto-restarts it when any
|
||||||
|
# Kotlin/Gradle source changes (inotifywait). Engine runs in dev mode
|
||||||
|
# (SHONAR_DEV=1 -> uvicorn --reload), so backend edits apply live too.
|
||||||
|
#
|
||||||
|
# Stop with Ctrl-C (or pkill -f desktop-dev.sh).
|
||||||
|
set -euo pipefail
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
export SHONAR_DEV=1
|
||||||
|
export DISPLAY="${DISPLAY:-:0}"
|
||||||
|
|
||||||
|
WATCH_DIRS=(
|
||||||
|
"app/src/main/kotlin"
|
||||||
|
"../../android/app/src/main/java/com/shonar"
|
||||||
|
)
|
||||||
|
FLAG="$(pwd)/.dev-restart-flag"
|
||||||
|
|
||||||
|
pkill -f "com.shonar.desktop.MainKt" 2>/dev/null || true
|
||||||
|
rm -f "$FLAG"
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
(
|
||||||
|
while inotifywait -qq -e close_write,move \
|
||||||
|
--include '\.kt$|\.kts$' "${WATCH_DIRS[@]}" 2>/dev/null; do
|
||||||
|
echo "[dev] source change -> restarting app…"
|
||||||
|
touch "$FLAG"
|
||||||
|
pkill -f "com.shonar.desktop.MainKt" 2>/dev/null || true
|
||||||
|
done
|
||||||
|
) &
|
||||||
|
WATCHER=$!
|
||||||
|
trap 'kill $WATCHER 2>/dev/null || true; rm -f "$FLAG"' EXIT
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
rm -f "$FLAG"
|
||||||
|
./gradlew run --offline
|
||||||
|
if [ -f "$FLAG" ]; then
|
||||||
|
continue # killed by a source edit: rebuild now
|
||||||
|
fi
|
||||||
|
rm -f "$FLAG"
|
||||||
|
# App closed cleanly or build failed — wait for the next edit.
|
||||||
|
# (Build failure: gradlew prints the error; the loop idles until you fix it.)
|
||||||
|
inotifywait -qq -e close_write,move --include '\.kt$|\.kts$' "${WATCH_DIRS[@]}"
|
||||||
|
done
|
||||||
Loading…
Add table
Add a link
Reference in a new issue