This commit is contained in:
team3
2026-06-21 18:32:55 +02:00
parent 3965bc5bf9
commit befd509c08
15 changed files with 1315 additions and 12 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -14,6 +14,7 @@
"katex": "^0.17.0",
"marked": "^18.0.4",
"marked-highlight": "^2.2.4",
"mermaid": "^11.15.0",
"vue": "^3.5.32"
},
"devDependencies": {

View File

@@ -1,5 +1,6 @@
<script setup>
import BausteinPanel from './BausteinPanel.vue'
import MermaidView from './MermaidView.vue'
import { renderMarkdown } from '../markdown.js'
const props = defineProps({
@@ -88,7 +89,8 @@ const standFarbe = computed(() => {
</div>
<div class="fokus-body">
<div ref="guideEl" class="fokus-col left">
<div class="markdown" v-html="renderMarkdown(ansicht === 'kompakt' ? (baustein.kompakt || baustein.md) : baustein.md)"></div>
<MermaidView v-if="ansicht === 'grafik'" :code="baustein.grafik || ''" />
<div v-else class="markdown" v-html="renderMarkdown(ansicht === 'kompakt' ? (baustein.kompakt || baustein.md) : baustein.md)"></div>
</div>
<div ref="rightEl" class="fokus-col right">
<BausteinPanel

View File

@@ -74,9 +74,10 @@ const aktSchwer = computed(() => {
return schwer.value
})
const modusRegel = computed(() => {
if (zeigeForm.value === 'quiz') return aktSchwer.value ? 'x von 4 · +3/1' : '1 von 4 · +1/1'
if (zeigeForm.value === 'lueckentext') return aktSchwer.value ? 'frei tippen · +3/1' : 'Begriff aus 4 · +1/1'
return '+1/+2/+3 · Nachfragen max +1 · schwach 1'
const m = imZufall.value ? '3' : '1' // Zufallband: Fehler kostet 3
if (zeigeForm.value === 'quiz') return aktSchwer.value ? `x von 4 · +3/${m}` : `1 von 4 · +1/${m}`
if (zeigeForm.value === 'lueckentext') return aktSchwer.value ? `frei tippen · +3/${m}` : `Begriff aus 4 · +1/${m}`
return imZufall.value ? '+1/+2/+3 · Fehler 3' : '+1/+2/+3 · Nachfragen max +1 · schwach 1'
})
// Ende des aktuellen Bands (nächster Meilenstein).
const bandZiel = computed(() => {

View File

@@ -0,0 +1,41 @@
<script setup>
import { ref, watch, onMounted } from 'vue'
import mermaid from 'mermaid'
mermaid.initialize({ startOnLoad: false, securityLevel: 'strict', theme: 'neutral' })
const props = defineProps({ code: { type: String, default: '' } })
const svg = ref('')
const fehler = ref(false)
let n = 0
// Async rendern; bricht das Mermaid-Parsing (kaputte Syntax) → Fallback statt Absturz.
async function render() {
const code = (props.code || '').trim()
svg.value = ''
fehler.value = false
if (!code) { fehler.value = true; return }
try {
const { svg: out } = await mermaid.render(`mmd-${Date.now()}-${n++}`, code)
svg.value = out
} catch {
fehler.value = true
}
}
watch(() => props.code, render)
onMounted(render)
</script>
<template>
<div class="mermaid-view">
<div v-if="svg" v-html="svg"></div>
<p v-else class="mermaid-fallback">Grafik nicht verfügbar Guide neu bauen.</p>
</div>
</template>
<style scoped>
.mermaid-view { display: flex; justify-content: center; padding: 0.5rem 0; }
.mermaid-view :deep(svg) { max-width: 100%; height: auto; }
.mermaid-fallback { color: var(--text-muted); font-size: 0.85rem; padding: 1rem 0; }
</style>

View File

@@ -5,6 +5,7 @@ import { renderMarkdown } from '../markdown.js'
import { useChat } from '../composables/useChat.js'
import BausteinPanel from './BausteinPanel.vue'
import BausteinFokus from './BausteinFokus.vue'
import MermaidView from './MermaidView.vue'
const props = defineProps({
previewGuide: { type: Object, default: null },
@@ -204,7 +205,8 @@ function extractContext() {
<span v-else-if="lernstand[s.title]?.verstanden" class="baustein-done verstanden" title="Vollständig verstanden (10/10)"> Verstanden</span>
<span v-else-if="lernstand[s.title]?.absolviert" class="baustein-done" title="Prüfung bestanden"> Absolviert</span>
</h3>
<div class="section-body markdown" v-html="renderMarkdown(!isOnePager && ansichtModus === 'kompakt' ? (s.kompakt || s.md) : s.md)"></div>
<MermaidView v-if="!isOnePager && ansichtModus === 'grafik'" :code="s.grafik || ''" />
<div v-else class="section-body markdown" v-html="renderMarkdown(!isOnePager && ansichtModus === 'kompakt' ? (s.kompakt || s.md) : s.md)"></div>
<BausteinPanel
v-if="!isOnePager"
mode="trigger"

View File

@@ -312,6 +312,14 @@ function saveQuelle() {
<rect x="2" y="12.9" width="8" height="1.4" rx="0.7" />
</svg>
</button>
<button :class="{ active: ansichtModus === 'grafik' }" title="Grafik (Diagramm)" @click="emit('setAnsicht', 'grafik')">
<svg viewBox="0 0 16 16" width="14" height="14" aria-hidden="true">
<circle cx="4" cy="4" r="2" />
<circle cx="12" cy="4" r="2" />
<circle cx="8" cy="12" r="2" />
<path d="M4 6 L8 10 M12 6 L8 10" stroke="currentColor" stroke-width="1.3" fill="none" />
</svg>
</button>
</div>
<button class="new-topic-toggle" :class="{ active: dlg }" title="Thema erstellen" @click="toggleErstellen">+</button>
</div>