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';