diff --git a/apps/web/src/routes/cards/new/+page.svelte b/apps/web/src/routes/cards/new/+page.svelte index f384baf..d78125f 100644 --- a/apps/web/src/routes/cards/new/+page.svelte +++ b/apps/web/src/routes/cards/new/+page.svelte @@ -15,6 +15,7 @@ import { toasts } from '$lib/stores/toasts.svelte.ts'; import { t } from '$lib/i18n/index.svelte.ts'; import ImageOcclusionEditor from '$lib/components/ImageOcclusionEditor.svelte'; + import CardSurface from '$lib/components/CardSurface.svelte'; type DeckLite = { id: string; name: string }; @@ -30,6 +31,7 @@ let answer = $state(''); let audioFileRef = $state(''); let saving = $state(false); + let previewFlipped = $state(false); // Multiple-Choice builder state let mcOptions = $state(['', '', '', '']); @@ -37,13 +39,20 @@ const frontHtml = $derived(renderMarkdown(front)); const backHtml = $derived(renderMarkdown(back)); + const answerHtml = $derived(renderMarkdown(answer)); const clusterIds = $derived(extractClusterIds(text)); const clozePreviewHtml = $derived.by(() => { const firstCluster = clusterIds[0]; - if (firstCluster === undefined) return ''; + if (firstCluster === undefined) return renderMarkdown(text); return renderMarkdown(renderClozePrompt(text, firstCluster)); }); + // Reset flip state when type changes + $effect(() => { + cardType; + previewFlipped = false; + }); + const TYPE_DESCRIPTIONS: Record = { basic: 'Klassische Karteikarte: Vorderseite → Rückseite.', 'basic-reverse': 'Wie Basic, aber beide Richtungen werden abgefragt (2 Reviews).', @@ -145,247 +154,354 @@ {t('card_new.back')}

{t('card_new.title')}

-
- -
-
- +
+ + + +
+
+ - -
-

{TYPE_DESCRIPTIONS[cardType]}

-
+ +
+

{TYPE_DESCRIPTIONS[cardType]}

+
- -
- {#if cardType === 'image-occlusion'} - + +
+ {#if cardType === 'image-occlusion'} + - {:else if cardType === 'cloze'} - + {:else if cardType === 'cloze'} + + - {#if clozePreviewHtml} -
- {t('card_new.cloze_preview_label', { first: clusterIds[0] ?? 1 })} -
{@html clozePreviewHtml}
-
- {/if} - - - - {:else if cardType === 'multiple-choice'} - - -
- Antwortoptionen - Markiere die richtige Antwort. Nicht befüllte Optionen werden ignoriert — KI ergänzt fehlende Distractors beim Lernen. -
- {#each mcOptions as opt, i} - {@const letter = ['A', 'B', 'C', 'D'][i]} - {@const isCorrect = mcCorrectIdx === i} -
- + {letter} + + {#if isCorrect && opt.trim()} + ✓ Richtig {/if} - - {letter} - - {#if isCorrect} - ✓ Richtig - {/if} -
- {/each} +
+ {/each} +
- - {:else if cardType === 'typing'} -
- - - -
- - {:else if cardType === 'audio-front'} - - - {#if back.trim()} -
- {t('card_new.preview_label')} -
{@html backHtml}
+ {:else if cardType === 'typing'} +
+ +
- {/if} - {:else} - -
+ {:else if cardType === 'audio-front'} - -
- {/if} -
- -
- - {t('card_new.cancel')} -
- + {:else} + +
+ + +
+ {/if} +
+ + +
+ + {t('card_new.cancel')} +
+ + + + +