diff --git a/apps/web/src/lib/components/ClozeCardForm.svelte b/apps/web/src/lib/components/ClozeCardForm.svelte
new file mode 100644
index 0000000..4ebcf03
--- /dev/null
+++ b/apps/web/src/lib/components/ClozeCardForm.svelte
@@ -0,0 +1,87 @@
+
+
+
+
+
+
diff --git a/apps/web/src/lib/components/MultipleChoiceCardForm.svelte b/apps/web/src/lib/components/MultipleChoiceCardForm.svelte
new file mode 100644
index 0000000..585843d
--- /dev/null
+++ b/apps/web/src/lib/components/MultipleChoiceCardForm.svelte
@@ -0,0 +1,185 @@
+
+
+
+
+
Antwortoptionen
+
Markiere die richtige Antwort. Leere Optionen werden ignoriert — KI ergänzt fehlende Distractors automatisch.
+
+ {#each mcOptions as opt, i}
+ {@const letter = ['A', 'B', 'C', 'D'][i]}
+ {@const isCorrect = mcCorrectIdx === i}
+
+
+ {letter}
+
+ {#if isCorrect && opt.trim()}
+ ✓ Richtig
+ {/if}
+
+ {/each}
+
+
+
+
diff --git a/apps/web/src/lib/components/marketplace/DiscussionThread.svelte b/apps/web/src/lib/components/marketplace/DiscussionThread.svelte
index fd88518..ab1a2aa 100644
--- a/apps/web/src/lib/components/marketplace/DiscussionThread.svelte
+++ b/apps/web/src/lib/components/marketplace/DiscussionThread.svelte
@@ -2,12 +2,12 @@
import { onMount } from 'svelte';
import {
- import { apiErrorMessage } from '$lib/api/error.ts';
hideDiscussion,
listDiscussions,
postDiscussion,
type Discussion,
} from '$lib/api/marketplace.ts';
+ import { apiErrorMessage } from '$lib/api/error.ts';
import { devUser } from '$lib/auth/dev-stub.svelte.ts';
import { toasts } from '$lib/stores/toasts.svelte.ts';
diff --git a/apps/web/src/lib/components/marketplace/PublishDeckModal.svelte b/apps/web/src/lib/components/marketplace/PublishDeckModal.svelte
index 482ef2d..a6fde26 100644
--- a/apps/web/src/lib/components/marketplace/PublishDeckModal.svelte
+++ b/apps/web/src/lib/components/marketplace/PublishDeckModal.svelte
@@ -2,13 +2,13 @@
import { onMount } from 'svelte';
import type { Card, Deck } from '@cards/domain';
import {
- import { apiErrorMessage } from '$lib/api/error.ts';
getMyAuthorProfile,
getMarketplaceDeck,
initMarketplaceDeck,
publishMarketplaceVersion,
type MarketplaceAuthor,
} from '$lib/api/marketplace.ts';
+ import { apiErrorMessage } from '$lib/api/error.ts';
import { toasts } from '$lib/stores/toasts.svelte.ts';
interface Props {
diff --git a/apps/web/src/lib/components/marketplace/PullRequestList.svelte b/apps/web/src/lib/components/marketplace/PullRequestList.svelte
index 59ec51d..bfe7ea9 100644
--- a/apps/web/src/lib/components/marketplace/PullRequestList.svelte
+++ b/apps/web/src/lib/components/marketplace/PullRequestList.svelte
@@ -2,13 +2,13 @@
import { onMount } from 'svelte';
import {
- import { apiErrorMessage } from '$lib/api/error.ts';
closePullRequest,
listPullRequests,
mergePullRequest,
rejectPullRequest,
type PullRequest,
} from '$lib/api/marketplace.ts';
+ import { apiErrorMessage } from '$lib/api/error.ts';
import { devUser } from '$lib/auth/dev-stub.svelte.ts';
import { toasts } from '$lib/stores/toasts.svelte.ts';
diff --git a/apps/web/src/routes/cards/[id]/edit/+page.svelte b/apps/web/src/routes/cards/[id]/edit/+page.svelte
index a76ecea..6d2292f 100644
--- a/apps/web/src/routes/cards/[id]/edit/+page.svelte
+++ b/apps/web/src/routes/cards/[id]/edit/+page.svelte
@@ -3,13 +3,13 @@
import { goto } from '$app/navigation';
import { page } from '$app/state';
import {
- import { apiErrorMessage } from '$lib/api/error.ts';
extractClusterIds,
maskRegionCount,
renderClozePrompt,
type Card,
type CardType,
} from '@cards/domain';
+ import { apiErrorMessage } from '$lib/api/error.ts';
import { getCard, updateCard, deleteCard } from '$lib/api/cards.ts';
import { devUser } from '$lib/auth/dev-stub.svelte.ts';
import { renderMarkdown } from '$lib/markdown.ts';
diff --git a/apps/web/src/routes/cards/new/+page.svelte b/apps/web/src/routes/cards/new/+page.svelte
index 41d8e30..44742f0 100644
--- a/apps/web/src/routes/cards/new/+page.svelte
+++ b/apps/web/src/routes/cards/new/+page.svelte
@@ -3,13 +3,13 @@
import { goto } from '$app/navigation';
import { page } from '$app/state';
import {
- import { apiErrorMessage } from '$lib/api/error.ts';
extractClusterIds,
maskRegionCount,
renderClozePrompt,
type CardType,
} from '@cards/domain';
import { createCard } from '$lib/api/cards.ts';
+ import { apiErrorMessage } from '$lib/api/error.ts';
import { listDecks, getDeck } from '$lib/api/decks.ts';
import { API_BASE } from '$lib/api/client.ts';
import { devUser } from '$lib/auth/dev-stub.svelte.ts';
@@ -17,6 +17,8 @@
import { toasts } from '$lib/stores/toasts.svelte.ts';
import { t } from '$lib/i18n/index.svelte.ts';
import ImageOcclusionEditor from '$lib/components/ImageOcclusionEditor.svelte';
+ import ClozeCardForm from '$lib/components/ClozeCardForm.svelte';
+ import MultipleChoiceCardForm from '$lib/components/MultipleChoiceCardForm.svelte';
import CardSurface from '$lib/components/CardSurface.svelte';
type DeckLite = { id: string; name: string };
@@ -193,81 +195,10 @@
{:else if cardType === 'cloze'}
-
-
+
{:else if cardType === 'multiple-choice'}
-
-
-
Antwortoptionen
-
Markiere die richtige Antwort. Leere Optionen werden ignoriert — KI ergänzt fehlende Distractors automatisch.
-
- {#each mcOptions as opt, i}
- {@const letter = ['A', 'B', 'C', 'D'][i]}
- {@const isCorrect = mcCorrectIdx === i}
-
-
- {letter}
-
- {#if isCorrect && opt.trim()}
- ✓ Richtig
- {/if}
-
- {/each}
-
-
+
{:else if cardType === 'typing'}
@@ -629,91 +560,6 @@
margin: 0;
}
- /* MC option builder */
- .mc-options {
- display: flex;
- flex-direction: column;
- gap: 0.5rem;
- margin-top: 0.5rem;
- }
-
- .mc-option {
- display: flex;
- align-items: center;
- gap: 0.625rem;
- padding: 0.5rem 0.625rem;
- border: 1px solid hsl(var(--color-border));
- border-radius: 0.5rem;
- background: hsl(var(--color-surface));
- transition: border-color 0.12s, background-color 0.12s;
- }
- .mc-option-correct {
- border-color: hsl(var(--color-success) / 0.5);
- background: hsl(var(--color-success) / 0.06);
- }
-
- .mc-radio {
- flex-shrink: 0;
- width: 1.125rem;
- height: 1.125rem;
- border-radius: 50%;
- border: 2px solid hsl(var(--color-border));
- background: hsl(var(--color-surface));
- cursor: pointer;
- display: flex;
- align-items: center;
- justify-content: center;
- transition: border-color 0.12s;
- padding: 0;
- color: hsl(var(--color-success));
- }
- .mc-radio:hover { border-color: hsl(var(--color-primary) / 0.6); }
- .mc-radio-selected {
- border-color: hsl(var(--color-success));
- background: hsl(var(--color-success) / 0.12);
- }
-
- .mc-letter {
- flex-shrink: 0;
- width: 1.375rem;
- height: 1.375rem;
- border-radius: 0.25rem;
- background: hsl(var(--color-surface));
- border: 1px solid hsl(var(--color-border));
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 0.6875rem;
- font-weight: 700;
- color: hsl(var(--color-muted-foreground));
- }
- .mc-letter-correct {
- background: hsl(var(--color-success) / 0.15);
- border-color: hsl(var(--color-success) / 0.4);
- color: hsl(var(--color-success));
- }
-
- .mc-input {
- flex: 1;
- border: none;
- background: transparent;
- color: hsl(var(--color-foreground));
- font: inherit;
- font-size: 0.875rem;
- padding: 0;
- min-width: 0;
- }
- .mc-input:focus { outline: none; }
- .mc-input::placeholder { color: hsl(var(--color-muted-foreground)); }
-
- .mc-badge {
- flex-shrink: 0;
- font-size: 0.6875rem;
- font-weight: 600;
- color: hsl(var(--color-success));
- white-space: nowrap;
- }
-
/* Actions */
.actions {
display: flex;
diff --git a/apps/web/src/routes/d/[slug]/+page.svelte b/apps/web/src/routes/d/[slug]/+page.svelte
index 5a83a1f..db69c2e 100644
--- a/apps/web/src/routes/d/[slug]/+page.svelte
+++ b/apps/web/src/routes/d/[slug]/+page.svelte
@@ -5,7 +5,6 @@
import { goto } from '$app/navigation';
import {
- import { apiErrorMessage } from '$lib/api/error.ts';
forkDeck,
getMarketplaceDeck,
getMarketplaceVersion,
@@ -20,6 +19,7 @@
type MarketplaceVersion,
type MarketplaceVersionCard,
} from '$lib/api/marketplace.ts';
+ import { apiErrorMessage } from '$lib/api/error.ts';
import { devUser } from '$lib/auth/dev-stub.svelte.ts';
import AuthorBadge from '$lib/components/marketplace/AuthorBadge.svelte';
import DiscussionThread from '$lib/components/marketplace/DiscussionThread.svelte';
diff --git a/apps/web/src/routes/explore/+page.svelte b/apps/web/src/routes/explore/+page.svelte
index a2cab8a..b3bfb5d 100644
--- a/apps/web/src/routes/explore/+page.svelte
+++ b/apps/web/src/routes/explore/+page.svelte
@@ -2,11 +2,11 @@
import { onMount } from 'svelte';
import {
- import { apiErrorMessage } from '$lib/api/error.ts';
browseDecks,
getExplore,
type DeckListEntry,
} from '$lib/api/marketplace.ts';
+ import { apiErrorMessage } from '$lib/api/error.ts';
import DeckListGrid from '$lib/components/marketplace/DeckListGrid.svelte';
import EmptyState from '$lib/components/marketplace/EmptyState.svelte';
import SkeletonGrid from '$lib/components/marketplace/SkeletonGrid.svelte';
diff --git a/apps/web/src/routes/me/published/+page.svelte b/apps/web/src/routes/me/published/+page.svelte
index 67469ac..e6ddae1 100644
--- a/apps/web/src/routes/me/published/+page.svelte
+++ b/apps/web/src/routes/me/published/+page.svelte
@@ -2,13 +2,13 @@
import { onMount } from 'svelte';
import { goto } from '$app/navigation';
import {
- import { apiErrorMessage } from '$lib/api/error.ts';
browseDecks,
getMyAuthorProfile,
upsertMyAuthorProfile,
type DeckListEntry,
type MarketplaceAuthor,
} from '$lib/api/marketplace.ts';
+ import { apiErrorMessage } from '$lib/api/error.ts';
import { devUser } from '$lib/auth/dev-stub.svelte.ts';
import { toasts } from '$lib/stores/toasts.svelte.ts';
diff --git a/apps/web/src/routes/study/[deckId]/+page.svelte b/apps/web/src/routes/study/[deckId]/+page.svelte
index 318a6ab..f3ec128 100644
--- a/apps/web/src/routes/study/[deckId]/+page.svelte
+++ b/apps/web/src/routes/study/[deckId]/+page.svelte
@@ -3,13 +3,13 @@
import { page } from '$app/state';
import { goto } from '$app/navigation';
import {
- import { apiErrorMessage } from '$lib/api/error.ts';
clusterIdForSubIndex,
maskForSubIndex,
renderClozePrompt,
renderClozeAnswer,
type Rating,
} from '@cards/domain';
+ import { apiErrorMessage } from '$lib/api/error.ts';
import { getDeck } from '$lib/api/decks.ts';
import { listDueReviews, gradeReview, type DueReview } from '$lib/api/reviews.ts';
import { devUser } from '$lib/auth/dev-stub.svelte.ts';
diff --git a/apps/web/src/routes/u/[slug]/+page.svelte b/apps/web/src/routes/u/[slug]/+page.svelte
index c1f5844..104fc46 100644
--- a/apps/web/src/routes/u/[slug]/+page.svelte
+++ b/apps/web/src/routes/u/[slug]/+page.svelte
@@ -4,7 +4,6 @@
import { page } from '$app/state';
import {
- import { apiErrorMessage } from '$lib/api/error.ts';
browseDecks,
followAuthor,
getAuthor,
@@ -13,6 +12,7 @@
type DeckListEntry,
type MarketplaceAuthor,
} from '$lib/api/marketplace.ts';
+ import { apiErrorMessage } from '$lib/api/error.ts';
import { devUser } from '$lib/auth/dev-stub.svelte.ts';
import DeckListGrid from '$lib/components/marketplace/DeckListGrid.svelte';
import { toasts } from '$lib/stores/toasts.svelte.ts';