This commit is contained in:
team3
2026-07-02 03:05:57 +02:00
parent afa8b36105
commit 41c9f29a37
38 changed files with 4671 additions and 2634 deletions

View File

@@ -1,10 +1,11 @@
<script setup>
import { ref, computed, watch, onMounted } from 'vue'
import { fetchGuides, fetchTopics, deleteTopic as apiDeleteTopic, createGuide as apiCreate, deleteGuide, cancelGuide as apiCancel, fetchBlocksStatus, fetchActiveBlocks, createBlocks as apiCreateBausteine, resetBlocksFromStep as apiResetBausteineAbStep, cancelBlocks as apiCancelBausteine, deleteBlocks as apiDeleteBausteine, fetchProviders, fetchStats, fetchTopicProgress, fetchGuideLocks, fetchGuideSteps, fetchFolders, updateSource as apiUpdateQuelle } from './api.js'
import { fetchGuides, fetchTopics, deleteTopic as apiDeleteTopic, createGuide as apiCreate, deleteGuide, cancelGuide as apiCancel, fetchBlocksStatus, fetchActiveBlocks, createBlocks as apiCreateBausteine, resetBlocksStage as apiResetBlocksStage, addBlocksResearch as apiAddResearch, requeueBlocksDead as apiRequeueDead, resetGuideBoard as apiResetGuideBoard, cancelBlocks as apiCancelBausteine, deleteBlocks as apiDeleteBausteine, fetchProviders, fetchStats, fetchTopicProgress, fetchGuideLocks, fetchGuideSteps, fetchFolders, updateSource as apiUpdateQuelle } from './api.js'
import { usePolling } from './composables/usePolling.js'
import TopicSidebar from './components/TopicSidebar.vue'
import TopicDetail from './components/TopicDetail.vue'
import BlocksOverview from './components/BlocksOverview.vue'
import GuideBoard from './components/GuideBoard.vue'
import ElementsSidebar from './components/elements/ElementsSidebar.vue'
import ElementsOverview from './components/ElementsOverview.vue'
import GeneralExamPanel from './components/GeneralExamPanel.vue'
@@ -27,7 +28,8 @@ const activeBlocks = ref([])
const provider = ref(localStorage.getItem('provider') || 'claude')
const providers = ref([])
const folders = ref({ projekt: [], uni: [] }) // folders for the sources picker
const mainView = ref('blocks') // blocks | elements | general | detail — exclusive main-area view
const mainView = ref('blocks') // blocks | guideboard | elements | general | detail — exclusive main-area view
const guideBoardFormat = ref('Guide')
const viewMode = ref('compact') // compact | erklärend — per topic, default compact
const levelView = ref(Number(localStorage.getItem('level')) || 4) // 1=A · 2=F · 3=E · 4=V (levels view)
const stats = ref(null)
@@ -212,24 +214,47 @@ async function handleResetBlocks() {
await loadBlocks()
}
async function handleResetFromStep(step) {
async function handleResetStage({ board, stage, restart = false }) {
if (!selectedTopic.value) return
uiError.value = null
try {
await apiResetBausteineAbStep(selectedTopic.value, step) // only reset, no regeneration
await apiResetBlocksStage(selectedTopic.value, board, stage)
if (restart) await apiCreateBausteine(selectedTopic.value, '', provider.value, undefined, undefined, false) // Continue: Queue abarbeiten
} catch (e) {
uiError.value = e.message
return
}
await loadBlocks()
if (restart) startPolling()
}
async function handleBlocksClick({ instructions, abPhase = null, abStep = null, toStep = null }) {
async function handleAddResearch() {
if (!selectedTopic.value) return
uiError.value = null
try {
// Source is already fixed here; abPhase/abStep set the start, toStep an optional end limit.
await apiCreateBausteine(selectedTopic.value, instructions, provider.value, undefined, undefined, abPhase, abStep, toStep)
await apiAddResearch(selectedTopic.value, provider.value)
} catch (e) {
uiError.value = e.message
return
}
await loadBlocks()
startPolling()
}
async function handleRequeueDead() {
if (!selectedTopic.value) return
await apiRequeueDead(selectedTopic.value)
await apiCreateBausteine(selectedTopic.value, '', provider.value, undefined, undefined, false)
await loadBlocks()
startPolling()
}
async function handleBlocksClick({ instructions = '', research = true }) {
if (!selectedTopic.value) return
uiError.value = null
try {
// research=true = Start/mehr Research anhängen; false = Continue (Queue abarbeiten).
await apiCreateBausteine(selectedTopic.value, instructions, provider.value, undefined, undefined, research)
} catch (e) {
uiError.value = e.message
return
@@ -278,14 +303,14 @@ function handleOpenBlocksView() {
previewGuide.value = null
}
async function handleFormatClick({ format, instructions, abStep = null }) {
async function handleFormatClick({ format, instructions = '', abStep = null }) {
if (!selectedTopic.value) return
// No duplicate start: if a generation is already running for topic+format, ignore
const running = guides.value.some(
(g) => g.topic === selectedTopic.value && g.format === format
&& (g.status === 'generating' || g.status === 'queued'),
)
if (running) return
if (running) { handleOpenGuideBoard(format); return }
uiError.value = null
try {
await apiCreate(selectedTopic.value, format, instructions, provider.value, abStep)
@@ -293,10 +318,32 @@ async function handleFormatClick({ format, instructions, abStep = null }) {
uiError.value = e.message
return
}
handleOpenGuideBoard(format) // Start → direkt aufs Live-Board
await loadGuides()
startPolling()
}
function handleOpenGuideBoard(format = 'Guide') {
if (!selectedTopic.value) return
guideBoardFormat.value = format
mainView.value = 'guideboard'
previewGuide.value = null
}
async function handleGuideBoardReset({ format, abStage }) {
uiError.value = null
try {
await apiResetGuideBoard(selectedTopic.value, format, abStage)
} catch (e) {
uiError.value = e.message
}
}
function handleGuideBoardPreview() {
const g = doneByFormat.value[guideBoardFormat.value]
if (g) handlePreview(g)
}
function handlePreview(guide) {
previewGuide.value = guide
mainView.value = 'detail'
@@ -399,6 +446,7 @@ onMounted(async () => {
@createThema="handleCreateTopic"
@updateSource="handleUpdateSource"
@openBausteineView="handleOpenBlocksView"
@openGuideBoard="handleOpenGuideBoard"
@formatClick="handleFormatClick"
@bausteineClick="handleBlocksClick"
@cancelBlocks="handleCancelBlocks"
@@ -416,18 +464,29 @@ onMounted(async () => {
<BlocksOverview
v-if="selectedTopic && mainView === 'blocks'"
:topic="selectedTopic"
:steps="blocks.feine_steps || []"
:generating="blocks.generating"
:progress="blocks.progress"
:ready="blocks.ready"
:partial="blocks.partial"
@close="mainView = 'detail'"
@restartFrom="(r) => handleBlocksClick({ instructions: '', abStep: r.from, toStep: r.to })"
@resetFrom="handleResetFromStep"
@restartAll="() => handleBlocksClick({ abPhase: blocks.ready ? 1 : null })"
@resetStage="handleResetStage"
@restartAll="() => handleBlocksClick({ research: true })"
@continueAll="() => handleBlocksClick({ research: false })"
@addResearch="handleAddResearch"
@requeueDead="handleRequeueDead"
@removeAll="handleResetBlocks"
@cancel="handleCancelBlocks"
/>
<GuideBoard
v-else-if="selectedTopic && mainView === 'guideboard'"
:topic="selectedTopic"
:format="guideBoardFormat"
@close="mainView = 'blocks'"
@cancelGuide="handleCancel"
@startGuide="handleFormatClick"
@resetStage="handleGuideBoardReset"
@preview="handleGuideBoardPreview"
/>
<ElementsOverview
v-else-if="selectedTopic && mainView === 'elements'"
:topic="selectedTopic"