This commit is contained in:
team3
2026-07-03 12:50:32 +02:00
parent 9754cbcfae
commit 91b0d00aa1
27 changed files with 203 additions and 580 deletions

View File

@@ -1,6 +1,6 @@
<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, resetBlocksStage as apiResetBlocksStage, addBlocksResearch as apiAddResearch, requeueBlocksDead as apiRequeueDead, resetGuideBoard as apiResetGuideBoard, restartBlocksCard as apiRestartBlocksCard, resetGuideCard as apiResetGuideCard, removeGuideFormat as apiRemoveGuideFormat, cancelBlocks as apiCancelBausteine, deleteBlocks as apiDeleteBausteine, fetchProviders, fetchStats, fetchTopicProgress, fetchGuideLocks, 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, restartBlocksCard as apiRestartBlocksCard, resetGuideCard as apiResetGuideCard, removeGuideFormat as apiRemoveGuideFormat, cancelBlocks as apiCancelBausteine, deleteBlocks as apiDeleteBausteine, fetchProviders, fetchStats, fetchTopicProgress, 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'
@@ -34,7 +34,6 @@ const _storedLevel = localStorage.getItem('level')
const levelView = ref(_storedLevel === 'auto' || !_storedLevel ? 'auto' : Number(_storedLevel) || 'auto') // 'auto' | 1-4
const stats = ref(null)
const progress = ref({})
const locks = ref({}) // lock reasons per format (backend = single rule source)
const uiError = ref(null) // surface rejected actions (409/400)
// Run a loader, log + swallow its error (a failed background load must not break the UI).
@@ -166,11 +165,9 @@ async function loadBlocks() {
if (selectedTopic.value) {
blocks.value = await fetchBlocksStatus(selectedTopic.value)
progress.value = await fetchTopicProgress(selectedTopic.value)
locks.value = await fetchGuideLocks(selectedTopic.value)
} else {
blocks.value = { ...EMPTY_BLOCKS }
progress.value = {}
locks.value = {}
}
if (activeBlocks.value.length && !polling.running()) startPolling()
} catch (e) {
@@ -449,7 +446,6 @@ onMounted(async () => {
:selectedTopic="selectedTopic"
:stats="stats"
:fortschritt="progress"
:locks="locks"
:uiError="uiError"
:doneByFormat="doneByFormat"
:latestByFormat="latestByFormat"
@@ -533,9 +529,7 @@ onMounted(async () => {
<TopicDetail
v-else-if="selectedTopic"
:previewGuide="previewGuide"
:dark="darkMode"
:provider="provider"
:doneByFormat="doneByFormat"
:themaAbgeschlossen="!!progress.completed"
:ansichtModus="viewMode"
:stufeAnsicht="levelView"

View File

@@ -18,16 +18,6 @@ export async function fetchGuides() {
return res.json()
}
export async function fetchGuideSteps(topic) {
const res = await fetch(`${BASE}/guides/steps?topic=${encodeURIComponent(topic)}`)
return res.json()
}
export async function fetchGuideLocks(topic) {
const res = await fetch(`${BASE}/guides/locks?topic=${encodeURIComponent(topic)}`)
return res.json()
}
export async function createGuide(topic, format, instructions = '', provider = 'claude', abStep = null) {
const res = await fetch(`${BASE}/guides`, {
method: 'POST',

View File

@@ -1,7 +1,7 @@
<script setup>
import BlockPanel from './BlockPanel.vue'
import { renderMarkdown, renderBlocks } from '../markdown.js'
import { stufeFuer, LEVELS } from '../levels.js'
import { stufeFuer } from '../levels.js'
import { pruefeBlock, uebernehmeBlock, resetBlockProgress } from '../api.js'
import { clearPruef } from '../pruefungCache.js'
import { useConfirm } from '../composables/useConfirm.js'

View File

@@ -7,7 +7,7 @@ const props = defineProps({
topic: { type: String, required: true },
format: { type: String, default: 'Guide' },
})
const emit = defineEmits(['cancelGuide', 'startGuide', 'resetStage', 'preview', 'deleteGuide', 'resetCard'])
const emit = defineEmits(['cancelGuide', 'startGuide', 'resetStage', 'preview', 'removeFormat', 'resetCard'])
const board = ref(null)
let timer = null

View File

@@ -9,9 +9,7 @@ import BlockFocus from './BlockFocus.vue'
const props = defineProps({
previewGuide: { type: Object, default: null },
dark: { type: Boolean, default: false },
provider: { type: String, default: 'claude' },
doneByFormat: { type: Object, default: () => ({}) }, // format → finished guide (topic-related)
themaAbgeschlossen: { type: Boolean, default: false },
ansichtModus: { type: String, default: 'compact' }, // compact | erklärend
stufeAnsicht: { type: [Number, String], default: 'auto' }, // 'auto' | 1=A · 2=F · 3=E · 4=V

View File

@@ -8,7 +8,6 @@ const props = defineProps({
selectedTopic: { type: String, default: null },
stats: { type: Object, default: null },
fortschritt: { type: Object, default: () => ({}) },
locks: { type: Object, default: () => ({}) },
uiError: { type: String, default: null },
doneByFormat: { type: Object, default: () => ({}) },
latestByFormat: { type: Object, default: () => ({}) },

View File

@@ -19,12 +19,6 @@ export function stufeFuer(score, cap) {
return s
}
// Next not-yet-reached level (the goal) or null (= Master).
export function naechste(score, cap) {
for (const st of LEVELS) if (score < schwelle(st.floor, cap)) return st
return null
}
// Error-penalty display by progress (against cap_aktuell): ≤25%→5 · ≤50%→10 · ≤75%→15 · >75%→20.
export function malusRegel(score, cap) {
const pct = cap ? score / cap : 0

View File

@@ -73,10 +73,6 @@ export function renderMarkdownInline(text) {
}
// Strip markdown to plain text (code fences + inline marks) — for previews/search.
export function plainText(text) {
return (text || '').replace(/```[a-z]*\n?/g, '').replace(/[`*_#]/g, '')
}
// Split markdown into top-level blocks: each block { raw (exact source), html }.
// raw is lossless (tokens.map(raw).join('') === original) → block-precise replacement.
export function renderBlocks(text) {