This commit is contained in:
Team3
2026-07-05 13:26:32 +02:00
parent fb3e967fdb
commit 07e14fb82e
3 changed files with 89 additions and 37 deletions

View File

@@ -89,3 +89,46 @@
border: 1px solid var(--border-strong); border: 1px solid var(--border-strong);
padding: 2px 6px; padding: 2px 6px;
} }
/* Stufen-Segmente (Vollansicht): dezenter Rand + A/F/E-Badge. Global, weil TopicDetail
das Markup per v-html injiziert (scoped-Styles greifen dort nicht). */
.sub-stufe {
position: relative;
border-left: 3px solid color-mix(in srgb, var(--stufe-farbe) 55%, transparent);
padding-left: 0.75rem;
margin: 0.5rem 0;
border-radius: 2px;
}
.sub-stufe-badge {
position: absolute;
top: 0.1rem;
right: 0;
font-size: 0.62rem;
font-weight: 700;
color: var(--stufe-farbe);
border: 1px solid color-mix(in srgb, var(--stufe-farbe) 60%, transparent);
border-radius: 4px;
padding: 0 4px;
opacity: 0.55;
}
/* Neu freigeschaltete Subbausteine (Auto-Stufe): kräftigerer Rand + Stufen-Badge */
.sub-neu {
position: relative;
border-left: 3px solid var(--neu-farbe);
padding-left: 0.75rem;
margin: 0.5rem 0;
border-radius: 2px;
}
.sub-neu-badge {
position: absolute;
top: 0.1rem;
right: 0;
font-size: 0.62rem;
font-weight: 700;
color: var(--neu-farbe);
border: 1px solid var(--neu-farbe);
border-radius: 4px;
padding: 0 4px;
opacity: 0.8;
}

View File

@@ -1,7 +1,7 @@
<script setup> <script setup>
import BlockPanel from './BlockPanel.vue' import BlockPanel from './BlockPanel.vue'
import { renderMarkdown, renderBlocks } from '../markdown.js' import { renderMarkdown, renderBlocks } from '../markdown.js'
import { stufeFuer } from '../levels.js' import { stufeFuer, SUB_RANK, VIEW_KURZ, VIEW_FARBE } from '../levels.js'
import { pruefeBlock, uebernehmeBlock, resetBlockProgress } from '../api.js' import { pruefeBlock, uebernehmeBlock, resetBlockProgress } from '../api.js'
import { clearPruef } from '../pruefungCache.js' import { clearPruef } from '../pruefungCache.js'
import { useConfirm } from '../composables/useConfirm.js' import { useConfirm } from '../composables/useConfirm.js'
@@ -98,7 +98,25 @@ const level = computed(() => {
const displayedText = computed(() => const displayedText = computed(() =>
props.ansicht === 'compact' ? (props.block.compact || props.block.md) : props.block.md, props.ansicht === 'compact' ? (props.block.compact || props.block.md) : props.block.md,
) )
const blocks = computed(() => renderBlocks(displayedText.value)) // Stufen-Segmente: mit subs wird je Sub separat gerendert (Rand+Badge in Stufen-Farbe),
// ohne subs (Legacy) bleibt der flache Pfad. Globaler Block-Index läuft über alle
// Segmente durch — Check/Vorschläge arbeiten unverändert über Index + raw.
const segments = computed(() => {
const compact = props.ansicht === 'compact'
const subs = props.block.subs
let i = 0
const seg = (level, text) => ({ level, blocks: renderBlocks(text).map((b) => ({ ...b, i: i++ })) })
if (!subs || !subs.length) return [seg('', displayedText.value)]
const out = []
const anchor = compact ? (props.block.anker_compact || '') : (props.block.anchor || '')
if (anchor.trim()) out.push(seg('', anchor))
for (const sub of subs) {
const body = compact ? (sub.compact || sub.md) : (sub.md || sub.compact)
if (body && body.trim()) out.push(seg(sub.level, body))
}
return out
})
const blocks = computed(() => segments.value.flatMap((s) => s.blocks))
// Field that edits are applied to (must match the displayed text). // Field that edits are applied to (must match the displayed text).
const spot = computed(() => (props.ansicht === 'compact' && props.block.compact) ? 'compact' : 'ausführlich') const spot = computed(() => (props.ansicht === 'compact' && props.block.compact) ? 'compact' : 'ausführlich')
@@ -176,21 +194,28 @@ watch(() => `${props.block.title}|${props.block.md}|${props.block.compact || ''}
<div class="fokus-body"> <div class="fokus-body">
<div ref="guideEl" class="fokus-col left"> <div ref="guideEl" class="fokus-col left">
<div class="markdown"> <div class="markdown">
<template v-for="(b, i) in blocks" :key="i"> <template v-for="(seg, si) in segments" :key="si">
<div class="md-block" v-html="b.html" @contextmenu.prevent="blockMenu(i, $event)"></div> <div :class="{ 'sub-stufe': !!seg.level }"
<div v-if="suggestions[i]" class="block-vorschlag"> :style="seg.level ? { '--stufe-farbe': VIEW_FARBE[SUB_RANK[seg.level] || 1] } : null">
<div v-if="suggestions[i].running" class="bv-status">Check Section</div> <span v-if="seg.level" class="sub-stufe-badge"
<template v-else> :title="`Stufe ${VIEW_KURZ[SUB_RANK[seg.level] || 1]}`">{{ VIEW_KURZ[SUB_RANK[seg.level] || 1] }}</span>
<p v-if="suggestions[i].error" class="bv-fehler">{{ suggestions[i].error }}</p> <template v-for="b in seg.blocks" :key="b.i">
<div class="markdown bv-new" v-html="renderMarkdown(suggestions[i].revised)"></div> <div class="md-block" v-html="b.html" @contextmenu.prevent="blockMenu(b.i, $event)"></div>
<div class="bv-aktionen"> <div v-if="suggestions[b.i]" class="block-vorschlag">
<button class="bv-btn ja" title="Apply" @click="applyBlock(i)"></button> <div v-if="suggestions[b.i].running" class="bv-status">Check Section</div>
<button class="bv-btn" title="Discard" @click="discardBlock(i)"></button> <template v-else>
<button class="bv-btn" :class="{ aktiv: suggestions[i].editOpen }" title="Add hint" @click="editBlock(i)"></button> <p v-if="suggestions[b.i].error" class="bv-fehler">{{ suggestions[b.i].error }}</p>
</div> <div class="markdown bv-new" v-html="renderMarkdown(suggestions[b.i].revised)"></div>
<div v-if="suggestions[i].editOpen" class="bv-edit"> <div class="bv-aktionen">
<input v-model="suggestions[i].hint" class="bv-input" placeholder="Extra info → check again" @keyup.enter="sendBlockEdit(i)" /> <button class="bv-btn ja" title="Apply" @click="applyBlock(b.i)"></button>
<button class="bv-btn ja" title="Check again" @click="sendBlockEdit(i)"></button> <button class="bv-btn" title="Discard" @click="discardBlock(b.i)"></button>
<button class="bv-btn" :class="{ aktiv: suggestions[b.i].editOpen }" title="Add hint" @click="editBlock(b.i)"></button>
</div>
<div v-if="suggestions[b.i].editOpen" class="bv-edit">
<input v-model="suggestions[b.i].hint" class="bv-input" placeholder="Extra info → check again" @keyup.enter="sendBlockEdit(b.i)" />
<button class="bv-btn ja" title="Check again" @click="sendBlockEdit(b.i)"></button>
</div>
</template>
</div> </div>
</template> </template>
</div> </div>

View File

@@ -56,6 +56,9 @@ function htmlFor(s) {
const body = renderMarkdown(compact ? (sub.compact || sub.md) : (sub.md || sub.compact)) const body = renderMarkdown(compact ? (sub.compact || sub.md) : (sub.md || sub.compact))
if (props.stufeAnsicht === 'auto' && lvl > 1 && rank === lvl) { if (props.stufeAnsicht === 'auto' && lvl > 1 && rank === lvl) {
parts.push(`<div class="sub-neu" style="--neu-farbe:${VIEW_FARBE[rank]}"><span class="sub-neu-badge" title="Neu ab Stufe ${VIEW_KURZ[rank]}">${VIEW_KURZ[rank]}</span>${body}</div>`) parts.push(`<div class="sub-neu" style="--neu-farbe:${VIEW_FARBE[rank]}"><span class="sub-neu-badge" title="Neu ab Stufe ${VIEW_KURZ[rank]}">${VIEW_KURZ[rank]}</span>${body}</div>`)
} else if (Number(props.stufeAnsicht) === 4) {
// Vollansicht: jede Stufe dezent kennzeichnen (Rand + Badge in Stufen-Farbe)
parts.push(`<div class="sub-stufe" style="--stufe-farbe:${VIEW_FARBE[rank]}"><span class="sub-stufe-badge" title="Stufe ${VIEW_KURZ[rank]}">${VIEW_KURZ[rank]}</span>${body}</div>`)
} else { } else {
parts.push(body) parts.push(body)
} }
@@ -765,24 +768,5 @@ function extractContext() {
background: var(--danger); background: var(--danger);
} }
/* Neu freigeschaltete Subbausteine (Auto-Stufe): dezenter Rand + Stufen-Badge */ /* .sub-neu/.sub-stufe: global in assets/markdown.css — scoped greift nicht auf v-html-Inhalt. */
.sub-neu {
position: relative;
border-left: 3px solid var(--neu-farbe);
padding-left: 0.75rem;
margin: 0.5rem 0;
border-radius: 2px;
}
.sub-neu-badge {
position: absolute;
top: 0.1rem;
right: 0;
font-size: 0.62rem;
font-weight: 700;
color: var(--neu-farbe);
border: 1px solid var(--neu-farbe);
border-radius: 4px;
padding: 0 4px;
opacity: 0.8;
}
</style> </style>