diff --git a/apps/cards/apps/web/package.json b/apps/cards/apps/web/package.json index 343fc0e81..5869ae34a 100644 --- a/apps/cards/apps/web/package.json +++ b/apps/cards/apps/web/package.json @@ -37,6 +37,7 @@ "@mana/shared-stores": "workspace:*", "@mana/shared-tailwind": "workspace:*", "@mana/shared-theme": "workspace:*", + "@mana/shared-theme-ui": "workspace:*", "@mana/shared-types": "workspace:*", "@mana/shared-utils": "workspace:*", "dexie": "^4.4.1", diff --git a/apps/cards/apps/web/src/app.css b/apps/cards/apps/web/src/app.css index aac1dd8e6..94506ec9d 100644 --- a/apps/cards/apps/web/src/app.css +++ b/apps/cards/apps/web/src/app.css @@ -1,33 +1,32 @@ @import 'tailwindcss'; +@import '@mana/shared-tailwind/themes.css'; +@import '@mana/shared-tailwind/sources.css'; -/* Phase-1 placeholder palette. Will swap for @mana/shared-theme tokens - once the theming pass lands in Etappe 3c. */ -@theme { - --color-cards-bg: #0a0a0a; - --color-cards-surface: #161616; - --color-cards-border: #2a2a2a; - --color-cards-fg: #f5f5f5; - --color-cards-muted: #a3a3a3; - --color-cards-accent: #6366f1; -} +/* Phase A — Cards now lives on the unified @mana/shared-theme tokens. + The placeholder --color-cards-* palette is gone; everything goes + through `--color-{background,foreground,surface,muted,…}` from + shared-tailwind. The runtime `createThemeStore({ appId: 'cards' })` + in +layout.svelte writes the live variant + mode onto the + document. */ -/* Cloze rendering classes — produced by @mana/cards-core/render. */ +/* Cloze rendering — produced by @mana/cards-core/render. Uses the + active app accent so the highlight follows the Cards brand. */ .cloze-blank { - background: rgba(99, 102, 241, 0.15); + background: hsl(var(--color-app-accent) / 0.18); border-radius: 0.25rem; padding: 0.05rem 0.4rem; - color: #a5b4fc; + color: hsl(var(--color-app-accent)); font-style: italic; } mark.cloze-active { - background: rgba(34, 197, 94, 0.2); - color: #86efac; + background: hsl(var(--color-success) / 0.2); + color: hsl(var(--color-success)); padding: 0.05rem 0.25rem; border-radius: 0.25rem; } -/* Minimal styling for HTML produced by marked() — Tailwind 4 ships +/* Minimal styling for HTML produced by marked() — Tailwind v4 ships without typography plugin so we set the basics by hand. */ .card-content :where(p, ul, ol) { margin-block: 0.5rem; @@ -41,19 +40,19 @@ mark.cloze-active { padding-inline-start: 1.25rem; } .card-content :where(code) { - background: rgba(255, 255, 255, 0.06); + background: hsl(var(--color-muted) / 0.6); padding: 0.1rem 0.3rem; border-radius: 0.25rem; font-size: 0.95em; } .card-content :where(pre) { - background: rgba(255, 255, 255, 0.04); + background: hsl(var(--color-muted) / 0.4); padding: 0.75rem; border-radius: 0.5rem; overflow-x: auto; } .card-content :where(a) { - color: #818cf8; + color: hsl(var(--color-app-accent)); text-decoration: underline; } .card-content :where(strong) { diff --git a/apps/cards/apps/web/src/app.html b/apps/cards/apps/web/src/app.html index 523daa393..470d4ca25 100644 --- a/apps/cards/apps/web/src/app.html +++ b/apps/cards/apps/web/src/app.html @@ -1,5 +1,5 @@ - + @@ -8,7 +8,7 @@ %sveltekit.head% - +
%sveltekit.body%
diff --git a/apps/cards/apps/web/src/lib/components/AiCardGen.svelte b/apps/cards/apps/web/src/lib/components/AiCardGen.svelte index 45b75dc67..dedcee6e7 100644 --- a/apps/cards/apps/web/src/lib/components/AiCardGen.svelte +++ b/apps/cards/apps/web/src/lib/components/AiCardGen.svelte @@ -101,12 +101,12 @@ } -
+
✨ Karten aus Text generieren {#if stage !== 'idle'}
{:else if stage === 'creating'} -
Lege Karten an…
+
Lege Karten an…
{:else if stage === 'done'}
✓ {createdCount} Karten angelegt.
- {/if} diff --git a/apps/cards/apps/web/src/lib/components/AnkiImport.svelte b/apps/cards/apps/web/src/lib/components/AnkiImport.svelte index 7b4c938a8..981074a5f 100644 --- a/apps/cards/apps/web/src/lib/components/AnkiImport.svelte +++ b/apps/cards/apps/web/src/lib/components/AnkiImport.svelte @@ -69,20 +69,20 @@ } -
+
Aus Anki importieren
{#if stage === 'idle'}
e.preventDefault()} ondrop={onDrop} onclick={() => fileInput?.click()} >
📦 .apkg-Datei hier ablegen oder klicken
-
+
Basic, Basic + Reverse, Cloze · Bilder + Audio werden mit übernommen.
@@ -94,25 +94,25 @@ onchange={onPick} /> {:else if stage === 'parsing'} -
Lese {fileName}…
+
Lese {fileName}…
{:else if stage === 'preview' && parsed}
- Gefunden in - {fileName}: + Gefunden in + {fileName}:
-
    +
    • {parsed.decks.length} {parsed.decks.length === 1 ? 'Deck' : 'Decks'}
    • {parsed.cards.length} {parsed.cards.length === 1 ? 'Karte' : 'Karten'}
    • {#if mediaCount > 0}
    • {mediaCount} Medien (Bilder/Audio)
    • {/if} {#if parsed.skipped > 0} -
    • {parsed.skipped} übersprungen (unbekannter Typ)
    • +
    • {parsed.skipped} übersprungen (unbekannter Typ)
    • {/if}
    {#if parsed.warnings.length > 0} -
    +
    Hinweise ({parsed.warnings.length})
      {#each parsed.warnings.slice(0, 10) as w (w)}
    • {w}
    • {/each} @@ -121,13 +121,13 @@ {/if}
{:else if stage === 'uploading-media'} -
+
Lade Medien hoch · {mediaProgress.uploaded} / {mediaProgress.total}
-
+
{:else if stage === 'importing'} -
+
Importiere {parsed?.cards.length ?? 0} Karten…
{:else if stage === 'done' && result} @@ -157,17 +157,17 @@ {result.decksCreated === 1 ? 'Deck' : 'Decks'} angelegt.
{#if result.mediaUploaded > 0 || result.mediaFailed > 0} -
+
{result.mediaUploaded} Medien übernommen{#if result.mediaFailed > 0} - · {result.mediaFailed} fehlgeschlagen + · {result.mediaFailed} fehlgeschlagen {/if}
{/if} {#if result.failed > 0} -
{result.failed} Karten konnten nicht angelegt werden.
+
{result.failed} Karten konnten nicht angelegt werden.
{/if}
{:else if stage === 'error'}
-
Fehler: {error}
+
Fehler: {error}
-

+

{new Date(c.createdAt).toLocaleString('de-DE')}

@@ -117,13 +117,13 @@ }} >
{#if stage === 'loading'} -
Lade Author-Profil…
+
Lade Author-Profil…
{:else if stage === 'become-author'}
-

+

Erstelle ein Author-Profil — andere User finden deine Decks unter - cards.mana.how/u/dein-slug. + cards.mana.how/u/dein-slug.

-
- +
-
+
Lade {cards.length} Karten hoch und prüfe Inhalt…
{:else if stage === 'done' && result} @@ -319,18 +321,18 @@
✓ Veröffentlicht als Version {result.version.semver}
-
+
{result.version.cardCount} Karten · Lizenz: {result.deck.license}
{#if result.moderation.verdict === 'flag'} -
+
Inhalt wurde zur Moderations-Prüfung markiert ({result.moderation.categories.join( ', ' )}). Das Deck ist veröffentlicht; ein Mensch schaut bei Gelegenheit drüber.
{/if}
{:else if stage === 'error'}
-
Fehler: {error}
+
Fehler: {error}
{:else} - {/if} @@ -76,29 +79,27 @@ role="dialog" aria-modal="true" > -
+

{cardContentHash ? 'Karte melden' : 'Deck melden'}

{#if done} -

+

Danke — die Moderation prüft den Bericht.

{:else}
{:else}

Diese Karte wird beim Merge aus dem Deck entfernt.

{/if}
-

Phase 1 · synct mit mana.how/cards

+

+ Phase 1 · synct mit mana.how/cards +

diff --git a/apps/cards/apps/web/src/routes/admin/reports/+page.svelte b/apps/cards/apps/web/src/routes/admin/reports/+page.svelte index 41b4b3e05..70d766f0c 100644 --- a/apps/cards/apps/web/src/routes/admin/reports/+page.svelte +++ b/apps/cards/apps/web/src/routes/admin/reports/+page.svelte @@ -57,12 +57,12 @@ function badgeClass(c: DeckReportItem['category']) { const map: Record = { - spam: 'bg-amber-500/15 text-amber-300', + spam: 'bg-amber-500/15 text-warning', copyright: 'bg-blue-500/15 text-blue-300', nsfw: 'bg-pink-500/15 text-pink-300', misinformation: 'bg-violet-500/15 text-violet-300', - hate: 'bg-red-500/15 text-red-300', - other: 'bg-neutral-800 text-neutral-300', + hate: 'bg-error/15 text-error', + other: 'bg-muted text-foreground/80', }; return map[c]; } @@ -76,34 +76,34 @@

Moderation-Inbox

{#if stage === 'ok'} - {/if}
{#if stage === 'loading'} -

Lädt…

+

Lädt…

{:else if stage === 'forbidden' || !isAdmin}

Nur Admins haben Zugang zur Moderation-Inbox.

{:else if stage === 'error'} -

+

{error}

{:else if reports.length === 0}

Keine offenen Reports.

{:else}
    {#each reports as r (r.id)} -
  • +
  • @@ -112,17 +112,17 @@ {r.deckTitle} {#if r.cardContentHash} - · Karte {r.cardContentHash.slice(0, 8)}… {/if}
    -

    +

    {new Date(r.createdAt).toLocaleString('de-DE')}

    @@ -130,19 +130,19 @@ {#if r.body}

    {r.body}

    {/if} {#if error} -

    {error}

    +

    {error}

    {/if}
    {:else}
    {#if error} -

    {error}

    +

    {error}

    {/if} -
    +
    Veröffentlicht: {new Date(deck.createdAt).toLocaleDateString('de-DE')} {#if !isOwner} @@ -248,7 +248,7 @@
    {#if deck.isTakedown} -

    +

    Dieses Deck wurde von der Moderation entfernt.

    {/if} @@ -261,7 +261,7 @@ {/if} -

    - ← Marktplatz +

    + ← Marktplatz

    diff --git a/apps/cards/apps/web/src/routes/decks/[id]/+page.svelte b/apps/cards/apps/web/src/routes/decks/[id]/+page.svelte index 48fe8c933..79387ec37 100644 --- a/apps/cards/apps/web/src/routes/decks/[id]/+page.svelte +++ b/apps/cards/apps/web/src/routes/decks/[id]/+page.svelte @@ -187,7 +187,9 @@
    - ← Decks + ← Decks {#if deck}
    @@ -197,11 +199,11 @@

    {deck.title}

    {#if deck.description} -

    {deck.description}

    +

    {deck.description}

    {/if}
{#if !isSubscribed} {/if} {#if dueCount === 0 && cards.length > 0} - Heute alles gelernt — schau später wieder rein.Heute alles gelernt — schau später wieder rein. {/if}
-
+
{cards.length}
-
Karten
+
Karten
-
-
{dueCount}
-
Fällig
+
+
{dueCount}
+
Fällig
{#if !isSubscribed}
{/each}
@@ -337,10 +340,11 @@ {#if newType === 'cloze'}
- +
{:else}
- +
- +
{/if} {#if attachError} -

{attachError}

+

{attachError}

{/if}
{/if} -
-

+
+

Karten ({cards.length})

{#if cards.length === 0} -
+
Noch keine Karten. Erstelle deine erste!
{:else} @@ -465,24 +469,24 @@ {#each cards as card, i (card.id)} {@const p = preview(card)}
  • - {i + 1}. + {i + 1}.
    {@html renderMarkdown(p.primary)}
    {#if p.secondary} -
    +
    {@html renderMarkdown(p.secondary)}
    {/if}
    - + {typeBadge(card.type)} {#if !isSubscribed}
    {/if} {:else} -
    +
    Deck nicht gefunden. - zurück + zurück
    {/if} diff --git a/apps/cards/apps/web/src/routes/explore/+page.svelte b/apps/cards/apps/web/src/routes/explore/+page.svelte index 8f0a8625c..a0f60ce9a 100644 --- a/apps/cards/apps/web/src/routes/explore/+page.svelte +++ b/apps/cards/apps/web/src/routes/explore/+page.svelte @@ -56,7 +56,7 @@

    Entdecken

    -

    +

    Decks aus dem Cards-Marktplatz — kostenlos lernen oder eigene veröffentlichen.

    @@ -72,11 +72,11 @@ type="search" bind:value={searchQuery} placeholder="Suche nach Titel oder Beschreibung…" - class="flex-1 rounded-lg border border-neutral-700 bg-neutral-950 px-3 py-2 text-sm outline-none focus:border-indigo-400" + class="flex-1 rounded-lg border border-border-strong bg-background px-3 py-2 text-sm outline-none focus:border-indigo-400" />

    {:else if stage === 'search'}
    -

    +

    {searchTotal} Treffer für „{searchQuery}"

    -
    @@ -105,7 +108,7 @@ {:else if stage === 'landing'} {#if featured.length > 0}
    -

    +

    🛡️ Featured · vom Mana-Verein empfohlen

    @@ -113,12 +116,15 @@ {/if}
    -

    📈 Trending · letzte 7 Tage

    - +

    📈 Trending · letzte 7 Tage

    +
    {/if} -

    - ← Eigene Decks +

    + ← Eigene Decks

    diff --git a/apps/cards/apps/web/src/routes/learn/[deckId]/+page.svelte b/apps/cards/apps/web/src/routes/learn/[deckId]/+page.svelte index 755244c5d..8c3dc299d 100644 --- a/apps/cards/apps/web/src/routes/learn/[deckId]/+page.svelte +++ b/apps/cards/apps/web/src/routes/learn/[deckId]/+page.svelte @@ -99,7 +99,7 @@
    {#if queue.length > 0 && !finished} -
    +
    {Math.min(currentIndex + 1, queue.length)} / {queue.length}
    {/if}
    {#if empty} -
    +
    Alles gelernt
    -

    +

    Komm später wieder — fällige Karten erscheinen automatisch.

    {:else if finished} -
    +
    Session abgeschlossen
    -

    +

    {sessionCount} Karten in {Math.round((Date.now() - sessionStartedAt) / 1000)} s.

    {/if} {:else} -
    Lade…
    +
    Lade…
    {/if}
    diff --git a/apps/cards/apps/web/src/routes/me/purchases/+page.svelte b/apps/cards/apps/web/src/routes/me/purchases/+page.svelte index 3ab9aa64b..5f0d29f0f 100644 --- a/apps/cards/apps/web/src/routes/me/purchases/+page.svelte +++ b/apps/cards/apps/web/src/routes/me/purchases/+page.svelte @@ -41,48 +41,46 @@

    Käufe & Auszahlungen

    {#if error} -

    +

    {error}

    {/if}
    -

    Käufe

    - Ausgegeben: {totalSpent} 💎 +

    Käufe

    + Ausgegeben: {totalSpent} 💎
    {#if loading} -

    +

    Lädt…

    {:else if purchases.length === 0} -

    +

    Du hast noch keine Decks gekauft.

    {:else}
      {#each purchases as p (p.id)} -
    • +
    • {p.deckTitle} -

      +

      v{p.versionSemver} · {new Date(p.purchasedAt).toLocaleDateString('de-DE')} {#if p.refundedAt} - Erstattet {/if}

      - {p.priceCredits} 💎 + {p.priceCredits} 💎
    • {/each}
    @@ -92,14 +90,14 @@ {#if payouts.length > 0 || (!loading && payouts.length === 0)}
    -

    +

    Author-Auszahlungen

    - Erhalten: {totalEarned} 💎 + Erhalten: {totalEarned} 💎
    {#if payouts.length === 0} -

    +

    Noch keine Auszahlungen — sobald jemand eines deiner kostenpflichtigen Decks kauft, landet die Author-Beteiligung hier.

    @@ -107,22 +105,22 @@
      {#each payouts as p (p.id)}
    • {p.deckTitle} -

      +

      Verkauf {p.priceCredits} 💎 · gutgeschrieben {new Date( p.grantedAt ).toLocaleDateString('de-DE')}

      - +{p.creditsGranted} 💎 + +{p.creditsGranted} 💎
    • {/each}
    diff --git a/apps/cards/apps/web/src/routes/u/[slug]/+page.svelte b/apps/cards/apps/web/src/routes/u/[slug]/+page.svelte index 13a1eb158..da381fdc1 100644 --- a/apps/cards/apps/web/src/routes/u/[slug]/+page.svelte +++ b/apps/cards/apps/web/src/routes/u/[slug]/+page.svelte @@ -64,13 +64,15 @@
    {#if stage === 'loading'} -

    Lade Profil…

    +

    Lade Profil…

    {:else if stage === 'not-found'} -

    - Profil @{slug} existiert nicht. +

    + Profil @{slug} existiert nicht.

    {:else if stage === 'error'} -

    +

    {error}

    {:else if author} @@ -79,11 +81,11 @@ {:else}
    {author.displayName.slice(0, 1).toUpperCase()}
    @@ -92,29 +94,29 @@

    {author.displayName}

    {#if author.verifiedMana} - + 🛡️ Mana {/if} {#if author.verifiedCommunity} - + ⭐ Community {/if}
    -

    +

    @{author.slug} · seit {new Date(author.joinedAt).toLocaleDateString('de-DE', { year: 'numeric', month: 'short', })}

    {#if author.bio} -

    {author.bio}

    +

    {author.bio}

    {/if}
    {#if authStore.isAuthenticated}