This commit is contained in:
team3
2026-09-30 00:59:44 +02:00
parent 828e3978e9
commit d10be4ad65
4 changed files with 25 additions and 6 deletions

View File

@@ -1,8 +1,9 @@
.PHONY: install build run dev auth prod stop logs push pull
.PHONY: install build run dev auth prod stop logs push pull loeschen
PY := .venv/bin/python
COMPOSE = docker compose
SERVER ?= root@178.104.67.87
URL ?= https://buchwerk.marha.de
REMOTE ?= /var/www/buchwerk
# Themen zwischen lokal und Server abgleichen; TOPIC=<Name> beschränkt auf ein Thema.
# Ausgeschlossen: Arbeitsdateien der Agenten (arbeit/) und Logs. Ohne --delete: nichts wird entfernt.
@@ -47,3 +48,12 @@ push:
pull:
rsync -az --progress --mkpath $(SYNC_AUS) "$(SERVER):$(REMOTE)/$(SYNC_QUELLE)" "$(SYNC_QUELLE)"
@echo "Geholt: $(SERVER):$(REMOTE)/$(SYNC_QUELLE) → $(SYNC_QUELLE)"
# Fertige Bücher lassen sich in der Oberfläche nicht löschen. Nur hier, mit Rückfrage:
# make loeschen TOPIC="Godot 4" – entfernt das Thema auf dem Server (über die API) und lokal.
loeschen:
@[ -n "$(TOPIC)" ] || { echo "TOPIC=<Name> angeben"; exit 1; }
@printf 'Thema „%s“ samt Buch auf dem Server und lokal unwiderruflich löschen? [j/N] ' "$(TOPIC)"; read a; [ "$$a" = "j" ]
curl -fsS -X DELETE "$(URL)/api/topics/$$(python3 -c 'import sys,urllib.parse; print(urllib.parse.quote(sys.argv[1]))' "$(TOPIC)")" && echo
rm -rf "storage/$(TOPIC)"
@echo "Gelöscht: $(TOPIC)"

View File

@@ -110,7 +110,7 @@ function tokens(n) { return n >= 1e6 ? (n / 1e6).toFixed(1) + 'M' : n >= 1e3 ? M
@click="topic = t.name; navAuf = false">
<span class="punkt" :class="t.laeuft ? 'laeuft' : t.status"></span>
<span style="flex: 1">{{ t.titel }}</span>
<button class="klein sekundaer" title="Löschen" @click.stop="loeschen(t.name)">✕</button>
<button v-if="t.status !== 'fertig'" class="klein sekundaer" title="Löschen" @click.stop="loeschen(t.name)">✕</button>
</div>
<div v-if="!topics.length" style="color: var(--dim); padding: 8px">Noch kein Thema.</div>
</div>

View File

@@ -7,6 +7,15 @@ import Lesemodus from './Lesemodus.vue'
const props = defineProps({ topic: String, laeuft: Boolean, fortschritt: Number })
const daten = ref(null)
const lesemodus = ref(false)
// Vollbild nur aus der Klickgeste heraus (Browser-Vorgabe); iOS-Safari kennt die API nicht.
function lesemodusStart() {
lesemodus.value = true
document.documentElement.requestFullscreen?.().catch(() => {})
}
function lesemodusEnde() {
lesemodus.value = false
if (document.fullscreenElement) document.exitFullscreen().catch(() => {})
}
const navAuf = ref(false)
// Wischgeste (nur Buchansicht, nicht im Lesemodus): nach rechts öffnet das Inhaltsverzeichnis, nach links schließt es
let wisch = null
@@ -100,7 +109,7 @@ onUnmounted(() => window.removeEventListener('keydown', taste))
<nav class="guide-nav" :class="{ offen: navAuf }">
<button class="schliessen sekundaer klein" @click="navAuf = false">✕ Schließen</button>
<div class="werkzeuge">
<button class="klein" title="Lesemodus: Abschnitt für Abschnitt" @click="lesemodus = true" :disabled="!abschnitte.length">Lesen ▶</button>
<button class="klein" title="Lesemodus: Abschnitt für Abschnitt" @click="lesemodusStart" :disabled="!abschnitte.length">Lesen ▶</button>
<a class="knopf klein sekundaer" title="Als Markdown-Datei herunterladen" :href="api.guideMdUrl(topic)" download>MD</a>
<a v-if="daten?.pdf" class="knopf klein sekundaer" title="Als PDF herunterladen" :href="api.pdfUrl(topic)" download>PDF</a>
</div>
@@ -126,7 +135,7 @@ onUnmounted(() => window.removeEventListener('keydown', taste))
<div ref="lese" class="lese" @touchstart.passive="wischStart" @touchend.passive="wischEnde">
<div class="mobil-leiste">
<button class="sekundaer klein" @click="navAuf = !navAuf">☰ Inhalt</button>
<button class="klein" @click="lesemodus = true" :disabled="!abschnitte.length">Lesen ▶</button>
<button class="klein" @click="lesemodusStart" :disabled="!abschnitte.length">Lesen ▶</button>
<a class="knopf klein sekundaer" :href="api.guideMdUrl(topic)" download>MD</a>
<a v-if="daten?.pdf" class="knopf klein sekundaer" :href="api.pdfUrl(topic)" download>PDF</a>
</div>
@@ -175,6 +184,6 @@ onUnmounted(() => window.removeEventListener('keydown', taste))
</article>
</div>
<Lesemodus v-if="lesemodus && daten" :daten="daten" :topic="topic" @schliessen="lesemodus = false" />
<Lesemodus v-if="lesemodus && daten" :daten="daten" :topic="topic" @schliessen="lesemodusEnde" />
</div>
</template>

View File

@@ -43,7 +43,7 @@ function reset() { if (confirm('Lauf zurücksetzen? Alles außer dem Thema wird
<div class="aktionen">
<button v-if="!laeuft && !fertig" @click="emit('start')">{{ begonnen ? 'Fortsetzen' : 'Buch erzeugen' }}</button>
<button v-if="laeuft" class="sekundaer" @click="emit('pause')">Pause</button>
<button v-if="!laeuft && begonnen" class="sekundaer" @click="reset">Zurücksetzen</button>
<button v-if="!laeuft && begonnen && !fertig" class="sekundaer" @click="reset">Zurücksetzen</button>
<span class="spacer"></span>
<span class="badge" :class="statusFarbe[state.status]">{{ statusText[state.status] || state.status }}<template v-if="state.stufe && laeuft"> · {{ state.stufe }}</template></span>
<span class="badge">{{ usd(state.kosten_usd) }}</span>