Stopp nach der Gliederung als Häkchen neben „Buch erzeugen“
Das Häkchen steht im Lauf neben dem Startknopf und bei „Neue Version“ neben „Buch erzeugen“, nicht mehr im Anlegen-Formular. Es wird mit dem Start gespeichert und ist sichtbar, solange die Gliederung aussteht. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
3
API.md
3
API.md
@@ -17,12 +17,11 @@ Ohne `v2` gilt die neueste Version.
|
||||
- `POST /api/themen` `{name, hinweise, spezialisierung}` → Thema wie oben (legt `v1` an)
|
||||
– `name`: Hauptthema; `spezialisierung`: grenzt es ein, nur was dafür gebraucht wird; die Bezeichnung (`name` in der Antwort, Ordner) wird „Thema – Spezialisierung“
|
||||
– `hinweise`: wie das Thema zu verstehen ist; gelten je Version und sind bei einer neuen Version änderbar
|
||||
– `stopp_gliederung` (bool, optional): der Lauf hält nach der Gliederung an (`pausiert`); „Fortsetzen“ schreibt die Kapitel
|
||||
- `GET /api/themen/{slug}` → Thema wie oben
|
||||
- `DELETE /api/themen/{slug}` – ganzes Thema
|
||||
- `POST /api/themen/{slug}/versionen` `{hinweise?, stopp_gliederung?}` → `{"v": N}` – nächste Version ohne Start; ohne Angabe übernimmt sie die Hinweise der neuesten Version
|
||||
- `DELETE /api/themen/{slug}/v{N}` – nur diese Version
|
||||
- `POST /api/themen/{slug}/v{N}/start` – startet oder setzt fort (resümiert an der offenen Stufe)
|
||||
- `POST /api/themen/{slug}/v{N}/start` `{stopp_gliederung?}` – startet oder setzt fort (resümiert an der offenen Stufe); `stopp_gliederung: true` hält nach der Gliederung als `pausiert` an, „Fortsetzen“ schreibt die Kapitel
|
||||
- `POST /api/themen/{slug}/v{N}/pause` – bricht laufende Agenten ab; Neustart mit `start`
|
||||
- `POST /api/themen/{slug}/v{N}/reset` – löscht alles in der Version außer dem Zustand „neu“
|
||||
- `GET|PUT /api/themen/{slug}/v{N}/position` – Leseposition `{abschnitt, lesemodus}`
|
||||
|
||||
@@ -30,7 +30,6 @@ class ThemaNeu(BaseModel):
|
||||
name: str
|
||||
hinweise: str = ""
|
||||
spezialisierung: str = ""
|
||||
stopp_gliederung: bool = False
|
||||
|
||||
|
||||
class VersionNeu(BaseModel):
|
||||
@@ -38,6 +37,10 @@ class VersionNeu(BaseModel):
|
||||
stopp_gliederung: bool = False
|
||||
|
||||
|
||||
class Start(BaseModel):
|
||||
stopp_gliederung: bool | None = None # None: wie gespeichert
|
||||
|
||||
|
||||
class Position(BaseModel):
|
||||
abschnitt: str | None = None
|
||||
lesemodus: str | int | None = None # neu: key des Bildschirms, alt: Index
|
||||
@@ -91,7 +94,7 @@ async def thema_anlegen(body: ThemaNeu):
|
||||
if not state.name_ok(name) or not state.name_ok(state.bezeichnung(name, body.spezialisierung.strip())):
|
||||
raise HTTPException(400, "Ungültiger Name")
|
||||
try:
|
||||
return _thema_info(state.anlegen(name, body.hinweise, body.spezialisierung, body.stopp_gliederung))
|
||||
return _thema_info(state.anlegen(name, body.hinweise, body.spezialisierung))
|
||||
except ValueError as e:
|
||||
raise HTTPException(409, str(e))
|
||||
|
||||
@@ -137,10 +140,13 @@ async def version_loeschen(slug: str, n: int):
|
||||
|
||||
|
||||
@app.post("/api/themen/{slug}/v{n}/start")
|
||||
async def start(slug: str, n: int):
|
||||
async def start(slug: str, n: int, body: Start | None = None):
|
||||
key = _key(slug, n)
|
||||
if state.zustand(key)["status"] == "fertig":
|
||||
st = state.zustand(key)
|
||||
if st["status"] == "fertig":
|
||||
raise HTTPException(400, "Lauf ist fertig – erst zurücksetzen")
|
||||
if body and body.stopp_gliederung is not None:
|
||||
st["stopp_gliederung"] = body.stopp_gliederung
|
||||
pipeline.starten(key)
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ def themen() -> list[dict]:
|
||||
return aus
|
||||
|
||||
|
||||
def anlegen(name: str, hinweise: str, spezialisierung: str = "", stopp_gliederung: bool = False) -> dict:
|
||||
def anlegen(name: str, hinweise: str, spezialisierung: str = "") -> dict:
|
||||
"""name ist das Hauptthema („Game Design“), die Spezialisierung grenzt es ein; beides zusammen bildet die Bezeichnung."""
|
||||
spezialisierung = spezialisierung.strip()
|
||||
bez = bezeichnung(name, spezialisierung)
|
||||
@@ -150,7 +150,7 @@ def anlegen(name: str, hinweise: str, spezialisierung: str = "", stopp_gliederun
|
||||
t = {"name": bez, "thema": name, "spezialisierung": spezialisierung, "slug": slug_frei(bez), "titel": bez,
|
||||
"hinweise": hinweise.strip(), "angelegt": jetzt()}
|
||||
_schreiben(d / "thema.json", t)
|
||||
version_anlegen(bez, stopp_gliederung=stopp_gliederung)
|
||||
version_anlegen(bez)
|
||||
return t
|
||||
|
||||
|
||||
|
||||
@@ -36,7 +36,6 @@ const fehler = ref('')
|
||||
const neuName = ref('')
|
||||
const neuHinweise = ref('')
|
||||
const neuSpezialisierung = ref('')
|
||||
const neuStopp = ref(false)
|
||||
const navAuf = ref(false)
|
||||
let timer = null
|
||||
|
||||
@@ -106,11 +105,10 @@ async function anlegen() {
|
||||
const name = neuName.value.trim()
|
||||
if (!name) return
|
||||
try {
|
||||
const t = await api.anlegen(name, neuHinweise.value.trim(), neuSpezialisierung.value.trim(), neuStopp.value)
|
||||
const t = await api.anlegen(name, neuHinweise.value.trim(), neuSpezialisierung.value.trim())
|
||||
neuName.value = ''
|
||||
neuHinweise.value = ''
|
||||
neuSpezialisierung.value = ''
|
||||
neuStopp.value = false
|
||||
await topicsLaden()
|
||||
themaWaehlen(t.slug)
|
||||
} catch (e) { fehler.value = String(e.message || e) }
|
||||
@@ -175,7 +173,6 @@ function tokens(n) { return n >= 1e6 ? (n / 1e6).toFixed(1) + 'M' : n >= 1e3 ? M
|
||||
<input v-model="neuName" placeholder="Thema, z. B. Godot 4" @keyup.enter="anlegen" />
|
||||
<input v-model="neuHinweise" placeholder="Hinweise (optional), z. B. für Entwickler, ab Version 4.6" @keyup.enter="anlegen" />
|
||||
<input v-model="neuSpezialisierung" placeholder="Spezialisierung (optional), z. B. Kartenspiel" title="Grenzt das Thema ein; Bezeichnung wird „Thema – Spezialisierung“" @keyup.enter="anlegen" />
|
||||
<label class="haken" title="Günstiger Testlauf: Recherche und Gliederung, dann Pause"><input type="checkbox" v-model="neuStopp" /> Nach der Gliederung anhalten</label>
|
||||
<button @click="anlegen">Anlegen</button>
|
||||
</div>
|
||||
</aside>
|
||||
@@ -202,7 +199,7 @@ function tokens(n) { return n >= 1e6 ? (n / 1e6).toFixed(1) + 'M' : n >= 1e3 ? M
|
||||
<div v-if="fehler" class="fehler" @click="fehler = ''">{{ fehler }} — klicken zum Schließen</div>
|
||||
<main class="inhalt" id="inhalt" tabindex="-1">
|
||||
<Lauf v-if="tab === 'lauf' && state" :state="state"
|
||||
@start="aktion(() => api.start(slug, versionAktiv))"
|
||||
@start="(stopp) => aktion(() => api.start(slug, versionAktiv, { stopp_gliederung: stopp }))"
|
||||
@pause="aktion(() => api.pause(slug, versionAktiv))"
|
||||
@reset="aktion(() => api.reset(slug, versionAktiv))"
|
||||
@neue-version="neueVersion" />
|
||||
|
||||
@@ -18,12 +18,12 @@ const e = encodeURIComponent
|
||||
const v = (slug, n) => `/themen/${e(slug)}/v${n}`
|
||||
export const api = {
|
||||
themen: () => req('/themen'),
|
||||
anlegen: (name, hinweise = '', spezialisierung = '', stopp_gliederung = false) => req('/themen', { method: 'POST', body: { name, hinweise, spezialisierung, stopp_gliederung } }),
|
||||
anlegen: (name, hinweise = '', spezialisierung = '') => req('/themen', { method: 'POST', body: { name, hinweise, spezialisierung } }),
|
||||
loeschen: (slug) => req(`/themen/${e(slug)}`, { method: 'DELETE' }),
|
||||
versionAnlegen: (slug, eingaben) => req(`/themen/${e(slug)}/versionen`, { method: 'POST', body: eingaben }),
|
||||
versionLoeschen: (slug, n) => req(v(slug, n), { method: 'DELETE' }),
|
||||
state: (slug, n) => req(v(slug, n)),
|
||||
start: (slug, n) => req(v(slug, n) + '/start', { method: 'POST' }),
|
||||
start: (slug, n, body) => req(v(slug, n) + '/start', { method: 'POST', body }),
|
||||
pause: (slug, n) => req(v(slug, n) + '/pause', { method: 'POST' }),
|
||||
reset: (slug, n) => req(v(slug, n) + '/reset', { method: 'POST' }),
|
||||
buch: (slug, n) => req(v(slug, n) + '/buch'),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { render } from '../markdown.js'
|
||||
|
||||
const props = defineProps({ state: Object })
|
||||
@@ -34,6 +34,10 @@ const gruppen = computed(() => {
|
||||
return out.filter((g) => g.agenten.length)
|
||||
})
|
||||
const ereignisse = computed(() => (props.state?.ereignisse || []).slice(-100).reverse())
|
||||
// Stopp nach der Gliederung: wählbar, solange die Gliederung noch aussteht; wird mit dem Start gespeichert
|
||||
const stopp = ref(false)
|
||||
watch(() => props.state?.stopp_gliederung, (s) => { stopp.value = !!s }, { immediate: true })
|
||||
const vorGliederung = computed(() => (props.state?.stufen || []).find((s) => s.id === 'gliederung')?.status !== 'fertig')
|
||||
|
||||
function reset() { if (confirm('Lauf zurücksetzen? Alles außer dem Thema wird gelöscht.')) emit('reset') }
|
||||
// Neue Version: Formular mit den Hinweisen dieser Version vorbelegt; Thema und Spezialisierung bleiben.
|
||||
@@ -47,7 +51,8 @@ function neuStarten() { neuOffen.value = false; emit('neue-version', { hinweise:
|
||||
<template>
|
||||
<div class="lauf">
|
||||
<div class="aktionen">
|
||||
<button v-if="!laeuft && !fertig" @click="emit('start')">{{ begonnen ? 'Fortsetzen' : 'Buch erzeugen' }}</button>
|
||||
<button v-if="!laeuft && !fertig" @click="emit('start', stopp)">{{ begonnen ? 'Fortsetzen' : 'Buch erzeugen' }}</button>
|
||||
<label v-if="!laeuft && !fertig && vorGliederung" class="haken" title="Günstiger Testlauf: Recherche und Gliederung, dann Pause"><input type="checkbox" v-model="stopp" /> Nach der Gliederung anhalten</label>
|
||||
<button v-if="laeuft" class="sekundaer" @click="emit('pause')">Pause</button>
|
||||
<button v-if="!laeuft && begonnen && !fertig" class="sekundaer" @click="reset">Zurücksetzen</button>
|
||||
<button v-if="!laeuft && begonnen" class="sekundaer" title="Gleiches Thema, kompletter neuer Lauf; Hinweise änderbar" @click="neuOeffnen">Neue Version</button>
|
||||
@@ -61,15 +66,13 @@ function neuStarten() { neuOffen.value = false; emit('neue-version', { hinweise:
|
||||
<div v-if="neuOffen" class="neu-version">
|
||||
<div class="titel">Neue Version von „{{ state.name }}“</div>
|
||||
<input v-model="neuHinweise" placeholder="Hinweise (optional), z. B. für Entwickler, ab Version 4.6" @keyup.enter="neuStarten" />
|
||||
<label class="haken" title="Günstiger Testlauf: Recherche und Gliederung, dann Pause"><input type="checkbox" v-model="neuStopp" /> Nach der Gliederung anhalten</label>
|
||||
<div class="knoepfe"><button @click="neuStarten">Buch erzeugen</button><button class="sekundaer" @click="neuOffen = false">Abbrechen</button></div>
|
||||
<div class="knoepfe"><button @click="neuStarten">Buch erzeugen</button><label class="haken" title="Günstiger Testlauf: Recherche und Gliederung, dann Pause"><input type="checkbox" v-model="neuStopp" /> Nach der Gliederung anhalten</label><span class="spacer"></span><button class="sekundaer" @click="neuOffen = false">Abbrechen</button></div>
|
||||
</div>
|
||||
|
||||
<div class="eingaben">
|
||||
<b>Thema</b><span>{{ state.thema }}</span>
|
||||
<b>Spezialisierung</b><span>{{ state.spezialisierung || '–' }}</span>
|
||||
<b>Hinweise</b><span>{{ state.hinweise || '–' }}</span>
|
||||
<template v-if="state.stopp_gliederung"><b>Lauf</b><span>hält nach der Gliederung an</span></template>
|
||||
</div>
|
||||
|
||||
<div v-if="state.deutung" class="deutung">
|
||||
|
||||
@@ -84,7 +84,7 @@ a.knopf { display: inline-block; text-decoration: none; line-height: normal; }
|
||||
.lauf .eingaben b { font-weight: 600; color: var(--text); }
|
||||
.neu-version { max-width: 760px; margin: 0 0 16px; padding: 12px 14px; background: var(--panel); border: 1px solid var(--rand); border-radius: 8px; display: grid; gap: 8px; }
|
||||
.neu-version .titel { font-weight: 600; }
|
||||
.neu-version .knoepfe { display: flex; gap: 8px; }
|
||||
.neu-version .knoepfe { display: flex; gap: 8px; align-items: center; }
|
||||
.haken { display: flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--dim); cursor: pointer; }
|
||||
.haken input { accent-color: var(--akzent); }
|
||||
.deutung { max-width: 760px; margin: 0 0 16px; padding: 12px 14px; background: var(--panel); border: 1px solid var(--rand); border-radius: 8px; }
|
||||
|
||||
Reference in New Issue
Block a user