feat(cards): audio-front Card-Type

- CardTypeSchema: 'audio-front' als vollwertiger Type (fields: audio_ref + back + front?)
- subIndexCount: 'audio-front' → 1
- AudioFrontView.svelte: custom Play/Pause-Button, audio via /api/v1/media/:id,
  optionaler Hint-Text; Antwort-Markdown läuft über bestehenden answerHtml-Pfad
- Study-Page: isAudioFront + audioFrontData derived, AudioFrontView eingebunden

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-05-10 15:18:41 +02:00
parent 598acb410d
commit 170a2825a4
4 changed files with 122 additions and 4 deletions

View file

@ -147,7 +147,7 @@ export function subIndexCount(type: string): number {
throw new Error(
'subIndexCount("image-occlusion") not supported — use maskRegionCount(fields.mask_regions) from @cards/domain'
);
case 'audio':
case 'audio-front':
return 1;
case 'multiple-choice':
return 1;

View file

@ -10,6 +10,7 @@ export const CardTypeSchema = z.enum([
'basic-reverse',
'cloze',
'image-occlusion',
'audio-front',
]);
export type CardType = z.infer<typeof CardTypeSchema>;
@ -20,7 +21,7 @@ export const CardTypeFutureSchema = z.enum([
'cloze',
'type-in',
'image-occlusion',
'audio',
'audio-front',
'multiple-choice',
]);
@ -46,7 +47,7 @@ export function validateFieldsForType(
cloze: ['text'],
'type-in': ['question', 'expected'],
'image-occlusion': ['image_ref', 'mask_regions'],
audio: ['audio_ref'],
'audio-front': ['audio_ref', 'back'],
'multiple-choice': ['question', 'options', 'correct_index'],
};
const need = required[type] ?? [];