mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 17:41:09 +02:00
chore(brand): rename Cards → Cardecky (display, infra, license-IDs)
- App display name → Cardecky in mana-apps.ts, MODULE_REGISTRY, alle Docs - Domains: cardecky.mana.how (App), cardecky-api.mana.how (Marketplace API), cardecky.com (Marketing-Landing — cloudflared-route + nginx-Block vorbereitet, DNS muss noch gesetzt werden) - 301-Redirect cards.mana.how → cardecky.mana.how (nginx + cloudflared) für alte Bookmarks; kann nach 6–12 Monaten wieder raus - SPDX license IDs Cards-Personal-Use/Pro-Only-1.0 → Cardecky-* via Drizzle 0001-Migration (DROP CHECK → UPDATE rows → SET DEFAULT → ADD CHECK), inkl. _journal- und 0001_snapshot-Update - In-mana cards-Modul: dezenter Banner zur Standalone-App (GUIDELINES §12), einmal schließbar via localStorage - Docker-CORS-Listen, sso-origins.ts, Prometheus-Target aktualisiert Technische IDs bleiben bewusst: appId 'cards', schema mana_platform.cards.*, Verzeichnis apps/cards/, Package @cards/web, services/cards-server, Env-Vars CARDS_*, UMAMI_WEBSITE_ID_CARDS*, Class CardsEvents — Mana-Konvention (Brand ≠ technischer Identifier). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a6a003fa5e
commit
61f2772789
52 changed files with 2149 additions and 141 deletions
|
|
@ -1,4 +1,4 @@
|
|||
# Cards — Konkurrenz-Analyse (Mai 2026)
|
||||
# Cardecky — Konkurrenz-Analyse (Mai 2026)
|
||||
|
||||
> Stand: 2026-05-07. Quellen primär aus offiziellen Pricing-Seiten, G2/Trustpilot/Reddit/HN sowie Wikipedia/Crunchbase. Wo Daten fehlen oder nicht öffentlich sind, ist das explizit vermerkt. Preise schwanken regional/saisonal — die hier genannten Zahlen sind Listenpreise USD, sofern nicht anders angegeben.
|
||||
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
- **Anki bleibt der unschlagbare technische Gold-Standard**, aber UX-Schwächen (FSRS-„Difficulty Hell", Plugin-Hölle, kein natives Cloud-Sync mit Bildern) und der $25 iOS-Preis sind reale Lücken, in die wir stoßen können. Die Übergabe an AnkiHub im Februar 2026 könnte mittelfristig die Open-Source-Dynamik verändern — Beobachten lohnt.
|
||||
- **Quizlet hat seine eigene Userbase verärgert**: Trustpilot 1.4/5, massive Beschwerden über Paywalls für Funktionen, die früher gratis waren. Genau dieses Vertrauensvakuum füllen Knowt und potenziell wir.
|
||||
- **AI-Karten-Generierung ist Tischeinsatz, kein Differenzierer mehr.** Quizlet, Quizgecko, Knowt, RemNote, Wisdolia, sogar Memrise haben es. PDF-Import + KI ist erwartete Baseline.
|
||||
- **Die „beautiful Anki"-Lücke ist umkämpft**: Mochi (5$/mo), RemNote (8$/mo), Noji (vormals AnkiPro). Cards mit _kostenlosem_ Sync sticht heraus — niemand sonst bietet die Kombination Markdown + FSRS + Cloud-Sync gratis. Das ist unsere wichtigste objektive Differenzierung.
|
||||
- **Die „beautiful Anki"-Lücke ist umkämpft**: Mochi (5$/mo), RemNote (8$/mo), Noji (vormals AnkiPro). Cardecky mit _kostenlosem_ Sync sticht heraus — niemand sonst bietet die Kombination Markdown + FSRS + Cloud-Sync gratis. Das ist unsere wichtigste objektive Differenzierung.
|
||||
- **Brand-Sniping ist real und schädlich**: AnkiPro (jetzt Noji) und AnkiApp (jetzt AlgoApp) haben sich einen Ruf als „Anki-Klone, die täuschen" erarbeitet — inkl. eines 10-tägigen Sync-Outages bei AnkiPro im Mai 2025. Lehre für uns: nie Anki im Namen führen, Kompatibilität sauber kommunizieren.
|
||||
|
||||
---
|
||||
|
|
@ -36,7 +36,7 @@
|
|||
| **Cerego** | Enterprise B2B Adaptive Learning | proprietär | — | ab $8.33/mo Indiv., Enterprise on req. | Sehr gering (B2B) |
|
||||
| **NeuraCache** | Notion/Obsidian-Sync für SR | proprietär | Limited | 14d Trial → Pro (Preis nicht klar dokumentiert) | Gering |
|
||||
|
||||
> Threat-Ranking: nur **Anki, Quizlet, Mochi, Knowt** sind Top-Bedrohungen für Cards' Kernzielgruppe. RemNote, Quizgecko, AnkiPro/Noji sind Nebenfront.
|
||||
> Threat-Ranking: nur **Anki, Quizlet, Mochi, Knowt** sind Top-Bedrohungen für Cardeckys Kernzielgruppe. RemNote, Quizgecko, AnkiPro/Noji sind Nebenfront.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -84,7 +84,7 @@ Quellen: [Quizlet Wikipedia](https://en.wikipedia.org/wiki/Quizlet) · [Trustpil
|
|||
- **User loben:** Notes + Cards in _einem_ Workflow; flexible nested Outline-Struktur; PDF-Annotation; AI-Generierung aus Notizen/PDFs.
|
||||
- **User kritisieren:** Steile Lernkurve („nichts versteht man in 10 Min"); UI als überladen empfunden; **Performance-Probleme** (langsam beim Laden großer Datenbanken, iPad-Stabilität); Bugs nach Beta-Updates; non-English-Support schwach.
|
||||
- **Firma & Geschichte:** Gegründet 2019 von Martin Schneider (MIT) und Moritz Wallawitsch (Berlin, HTW). Sitz: USA. **$2.8M Seed (Sept 2021)** unter General Catalyst. Hat 2025 ~$2M Revenue mit ~18 Personen erreicht.
|
||||
- **Bedrohungsgrad: Mittel.** Andere Zielgruppe (PKM-Power-User, Studenten, die Notes wollen). Cards ist fokussierter — wir müssen die „nur-Karten"-Nische gegen ihre Hybrid-Erweiterung verteidigen.
|
||||
- **Bedrohungsgrad: Mittel.** Andere Zielgruppe (PKM-Power-User, Studenten, die Notes wollen). Cardecky ist fokussierter — wir müssen die „nur-Karten"-Nische gegen ihre Hybrid-Erweiterung verteidigen.
|
||||
|
||||
Quellen: [RemNote Pricing](https://www.remnote.com/pricing) · [Crunchbase RemNote](https://www.crunchbase.com/organization/remnote) · [RemNote Reviews Product Hunt](https://www.producthunt.com/products/remnote/reviews) · [RemNote Performance-Forum](https://forum.remnote.io/t/remnote-is-my-dream-pkm-yet-its-too-slow-am-i-doing-something-wrong/10920) · [Latka RemNote $2M ARR](https://getlatka.com/companies/remnote.com)
|
||||
|
||||
|
|
@ -164,7 +164,7 @@ Quellen: [SuperMemo Wikipedia](https://en.wikipedia.org/wiki/SuperMemo) · [Supe
|
|||
- **User loben:** Schickes Mobile-UI; einfacher Onboarding-Flow; Cross-Device-Sync „out of the box"; community Decks.
|
||||
- **User kritisieren:** **Brand-Verwirrung** (User dachten, sie laden „echtes" Anki herunter); **10-Tage-Sync-Outage Mai 2025** mit Datenverlust für viele User; Lock-in (Export-Tools wurden vom Anbieter blockiert, ein Migrations-Tool erhielt einen **Rickroll-Response** von AnkiPro); offizielles Anki-Team distanziert sich.
|
||||
- **Firma & Geschichte:** Anki Pro UAB; Co-Founder **Maksim Abramchuk** (im Crunchbase) und **Andrew Bond** (LinkedIn). 2021 gestartet, 2024/25 Rebrand zu **Noji**. Sitz nicht eindeutig öffentlich (LinkedIn-Indikatoren UK/Osteuropa).
|
||||
- **Bedrohungsgrad: Mittel.** Nicht weil sie technisch besser sind, sondern weil Anki-Suchende auf sie reinfallen. **Lehre für Cards: Brand-Hygiene**. Wir sind „Cards" — nie „Anki" im Marketing, klare Trennung kommunizieren, Anki-Import sauber als Bridge dokumentieren.
|
||||
- **Bedrohungsgrad: Mittel.** Nicht weil sie technisch besser sind, sondern weil Anki-Suchende auf sie reinfallen. **Lehre für Cardecky: Brand-Hygiene**. Wir sind „Cardecky" — nie „Anki" im Marketing, klare Trennung kommunizieren, Anki-Import sauber als Bridge dokumentieren.
|
||||
|
||||
Quellen: [Anki knockoffs (offizielle Anki FAQ)](https://faqs.ankiweb.net/anki-knockoffs.html) · [AnkiPro Ripoff Forum](https://forums.ankiweb.net/t/ankipro-another-ripoff-anki-app/11791) · [Anki Users Get Rickrolled](https://broderic.blog/post/anki-users-get-rickrolled/) · [Noji App Store](https://apps.apple.com/us/app/noji-flashcards-anki-method/id1573585542) · [Crunchbase Anki Pro](https://www.crunchbase.com/organization/anki-pro) · [Speakada: Official Anki vs Fake Apps](https://speakada.com/official-anki-vs-fake-apps-the-critical-mistake-costing-language-learners-hours/)
|
||||
|
||||
|
|
@ -298,7 +298,7 @@ Quellen: [NeuraCache](https://neuracache.com/) · [NeuraCache App Store](https:/
|
|||
|
||||
---
|
||||
|
||||
## 4. Schluss-Empfehlung: 3 Differenzierungs-Hebel für Cards
|
||||
## 4. Schluss-Empfehlung: 3 Differenzierungs-Hebel für Cardecky
|
||||
|
||||
### Hebel 1: **„Free Sync" konsequent ausspielen**
|
||||
|
||||
|
|
@ -318,13 +318,13 @@ Anki bleibt Power-User-Standard, aber Anki-User klagen über UX, FSRS-Tweaking u
|
|||
|
||||
**Action:**
|
||||
|
||||
- Eine dezidierte Landingpage `cards.mana.how/from-anki` mit ehrlichem Vergleich (was wir besser machen, was Anki noch besser kann), Migrationsanleitung, und expliziter Distanzierung von AnkiPro/AnkiApp/Noji.
|
||||
- Eine ehrliche Story dazu („Wir sind nicht Anki. Wir sind Cards. Aber wir respektieren deine Anki-Karten."). Das positioniert uns als seriöse Alternative gegen die Brand-Sniper.
|
||||
- Eine dezidierte Landingpage `cardecky.com/from-anki` mit ehrlichem Vergleich (was wir besser machen, was Anki noch besser kann), Migrationsanleitung, und expliziter Distanzierung von AnkiPro/AnkiApp/Noji.
|
||||
- Eine ehrliche Story dazu („Wir sind nicht Anki. Wir sind Cardecky. Aber wir respektieren deine Anki-Karten."). Das positioniert uns als seriöse Alternative gegen die Brand-Sniper.
|
||||
- Für Bonus-Punkte: Imports von Mochi-Decks und Quizlet-Sets ebenfalls anbieten — Knowt lebt davon, wir können das auch.
|
||||
|
||||
### Hebel 3: **„Local-First PWA" als Tech-Identität, nicht nur Implementierungsdetail**
|
||||
|
||||
Cards' Local-First + PWA-Architektur ist konzeptionell anders als Quizlet/Knowt (Web-First) und besser als Mochi auf iOS (App-Store-Friktion). Wir sind installierbar, offline-funktional, ohne App Store. Das schlägt mehrere Fliegen:
|
||||
Cardeckys Local-First + PWA-Architektur ist konzeptionell anders als Quizlet/Knowt (Web-First) und besser als Mochi auf iOS (App-Store-Friktion). Wir sind installierbar, offline-funktional, ohne App Store. Das schlägt mehrere Fliegen:
|
||||
|
||||
- Kein iOS-30%-Tax (vs AnkiMobile-Modell, das deshalb $25 kostet)
|
||||
- Kein Vendor-Lock-in (Daten bleiben im Browser/lokal nutzbar)
|
||||
|
|
@ -337,7 +337,7 @@ Cards' Local-First + PWA-Architektur ist konzeptionell anders als Quizlet/Knowt
|
|||
|
||||
## Bonus: Was wir _nicht_ tun sollten
|
||||
|
||||
- **Nicht „Anki" im Namen führen** — siehe AnkiPro/AnkiApp Reputation. „Cards" ist neutral und reicht.
|
||||
- **Nicht „Anki" im Namen führen** — siehe AnkiPro/AnkiApp Reputation. „Cardecky" ist neutral, freundlich, und distanziert sich klar.
|
||||
- **Nicht die SR-Algorithmus-Race spielen** — FSRS v6 reicht. SuperMemo SM-20 ist kein Marketing-Argument für 99% der User.
|
||||
- **Nicht in Sprach-Lernen pivotieren** — Memrise und Duolingo besitzen das Feld, andere Mechaniken nötig.
|
||||
- **Nicht alle AI-Features paywallen** — Knowt zeigt: ein großzügiges Free-Tier mit AI ist der Hebel gegen Quizlet.
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
# Cards — Projekt-Leitlinien
|
||||
# Cardecky — Projekt-Leitlinien
|
||||
|
||||
Verbindliche Regeln für den Spinoff. Ziel: in wenigen Wochen ein
|
||||
ausspielbares Web-MVP, das ausschließlich seinen *Core Gameloop*
|
||||
beherrscht und alles andere von zentralen Mana-Bausteinen erbt.
|
||||
|
||||
**Status:** Planungsphase, noch kein Code.
|
||||
**Name:** Cards.
|
||||
**Domain:** `cards.mana.how` (Subdomain unter `*.mana.how`, SSO über mana-auth).
|
||||
**Name:** Cardecky.
|
||||
**App-Domain:** `cardecky.mana.how` (Subdomain unter `*.mana.how`, SSO über mana-auth).
|
||||
**Marketing-Landing:** `cardecky.com` (eigene Domain, statisch, SEO/Akquise — keine Auth, leitet auf `cardecky.mana.how` für die App).
|
||||
**Zugang:** offen für jeden eingeloggten Mana-User (`requiredTier: 'public'`, kein Beta-Gate).
|
||||
|
||||
## 1. Mission in einem Satz
|
||||
|
|
@ -20,7 +21,7 @@ Wie bei einem Spielprototyp gilt: alles, was nicht zum Loop gehört,
|
|||
wird zurückgestellt. Erst wenn der Loop sich gut anfühlt und Nutzer ihn
|
||||
freiwillig wiederholen, wird gebaut, was drumherum gehört.
|
||||
|
||||
### Der Core Gameloop von Cards
|
||||
### Der Core Gameloop von Cardecky
|
||||
|
||||
```
|
||||
Start
|
||||
|
|
@ -86,7 +87,7 @@ Jede dieser Features ist legitim — aber nur, wenn der Loop steht.
|
|||
3. **Bevorzugt was im Verein schon läuft.** Neue Technologie nur einführen, wenn ein konkreter Engpass es verlangt und kein vorhandenes Tool es löst.
|
||||
4. **Zentrale Mana-Dienste statt Eigenbau.** Auth, Sync, Analytics, Notifications, Media usw. werden NICHT neu gebaut — siehe §5.
|
||||
5. **Local-First wie der Rest des Verein-Stacks.** IndexedDB als Quelle der Wahrheit, Sync nach Postgres im Hintergrund.
|
||||
6. **`cards.mana.how` als Subdomain unter `*.mana.how`.** Kein eigenes Auth-System, kein eigenes Hosting-Setup — Eintrag in `PRODUCTION_TRUSTED_ORIGINS` + Cloudflare-Tunnel-Route reichen.
|
||||
6. **`cardecky.mana.how` als Subdomain unter `*.mana.how`.** Kein eigenes Auth-System, kein eigenes Hosting-Setup — Eintrag in `PRODUCTION_TRUSTED_ORIGINS` + Cloudflare-Tunnel-Route reichen.
|
||||
7. **Eine UI-Schicht, ein Theme.** Wir verwenden `@mana/shared-theme(-ui)` und `@mana/shared-ui` so weit es geht — kein paralleles Design-System.
|
||||
8. **Erweiterbare Daten, simples UI.** Das Datenmodell denkt zukünftige Kartentypen mit (siehe §6), das UI zeigt in Phase 1 nur die vier definierten Typen.
|
||||
|
||||
|
|
@ -150,9 +151,9 @@ Verein-Services).
|
|||
## 5. Zentrale Mana-Bausteine (Pflicht in Phase 1)
|
||||
|
||||
### Services (laufen bereits, nur konsumieren)
|
||||
| Service | Port | Wofür in Cards |
|
||||
| Service | Port | Wofür in Cardecky |
|
||||
|---|---|---|
|
||||
| `mana-auth` | 3001 | SSO, JWT, Sessions, Tier-Claims. Cards-Origin in `PRODUCTION_TRUSTED_ORIGINS` eintragen. |
|
||||
| `mana-auth` | 3001 | SSO, JWT, Sessions, Tier-Claims. Cardecky-Origin in `PRODUCTION_TRUSTED_ORIGINS` eintragen. |
|
||||
| `mana-sync` | 3050 | Sync der `cards`-AppId-Daten (Decks, Karten, Reviews, StudyBlocks). |
|
||||
| `mana-user` | 3062 | Profilinfos / Settings. |
|
||||
| `mana-analytics` | 3064 | Page-Views, Loop-Events (siehe §11). |
|
||||
|
|
@ -164,7 +165,7 @@ Verein-Services).
|
|||
| `mana-media` | 3015 | **Erst wenn Bilder in Karten erlaubt sind.** |
|
||||
|
||||
### Workspace-Pakete (`@mana/*`)
|
||||
| Paket | Wofür in Cards |
|
||||
| Paket | Wofür in Cardecky |
|
||||
|---|---|
|
||||
| `@mana/shared-auth` | Client-seitiger Auth-Hook (SSO-Flow, JWT-Handling). |
|
||||
| `@mana/shared-auth-ui` | Login/Logout-Komponenten. |
|
||||
|
|
@ -198,7 +199,7 @@ Verein-Services).
|
|||
|
||||
### Datenpfad
|
||||
|
||||
Cards übernimmt 1:1 das Mana-Datenpfad-Pattern:
|
||||
Cardecky übernimmt 1:1 das Mana-Datenpfad-Pattern:
|
||||
|
||||
```
|
||||
User-Aktion → Store → encryptRecord → Dexie → Hooks (_pendingChanges)
|
||||
|
|
@ -267,12 +268,12 @@ beim ersten Schema-Upgrade auf `type='basic'` mit
|
|||
## 7. Daten-Contract mit dem mana-Modul
|
||||
|
||||
Wichtig: das **bestehende `cards`-Modul in der Mana-Web-App bleibt
|
||||
erhalten**. Cards-Standalone und mana-Modul schreiben in dieselben
|
||||
erhalten**. Cardecky und das mana-Modul schreiben in dieselben
|
||||
Postgres-Tabellen.
|
||||
|
||||
Daher gilt:
|
||||
- Schema-Änderungen werden **gemeinsam** im mana-Modul und im
|
||||
Cards-Standalone-Code rolled out (nie nur auf einer Seite).
|
||||
Cardecky-Code rolled out (nie nur auf einer Seite).
|
||||
- Encryption-Registry-Einträge müssen in beiden Frontends identisch
|
||||
sein (Field-Allowlist).
|
||||
- Migrationen über `docs/DATABASE_MIGRATIONS.md`.
|
||||
|
|
@ -285,7 +286,7 @@ gibt es nie zwei Wahrheiten zur Datenstruktur.
|
|||
|
||||
Phase 1 ist fertig, wenn:
|
||||
|
||||
1. Ein eingeloggter Mana-User kann auf `cards.mana.how`
|
||||
1. Ein eingeloggter Mana-User kann auf `cardecky.mana.how`
|
||||
- mindestens ein Deck anlegen,
|
||||
- Karten manuell hinzufügen (Basic, Basic+Reverse, Cloze, Type-In),
|
||||
- Markdown im Front/Back nutzen (Bold, Listen, Code, Links),
|
||||
|
|
@ -300,7 +301,7 @@ Phase 1 ist fertig, wenn:
|
|||
- „Login → Deck anlegen → Basic-Karte → Lernsession → bewerten"
|
||||
- „Cloze-Karte mit zwei Clustern → erzeugt zwei Subkarten"
|
||||
- „Type-In: korrekte Antwort = grün, falsche = rot"
|
||||
8. Container baut & läuft auf dem Mac mini hinter Cloudflare Tunnel (`cards.mana.how`).
|
||||
8. Container baut & läuft auf dem Mac mini hinter Cloudflare Tunnel (`cardecky.mana.how`).
|
||||
|
||||
Alles andere ist Phase 2.
|
||||
|
||||
|
|
@ -360,7 +361,7 @@ konkrete Frage entsteht, die Daten beantworten sollen.
|
|||
|
||||
## 12. Hinweis im mana-Modul
|
||||
|
||||
Sobald `cards.mana.how` live ist, bekommt das mana-Modul einen
|
||||
Sobald `cardecky.mana.how` live ist, bekommt das mana-Modul einen
|
||||
**dezenten** Hinweis (z.B. ein Banner oder Badge über der ListView):
|
||||
"Cards gibt es jetzt auch als eigenständige App". Kein Pop-up, kein
|
||||
"Cardecky gibt es jetzt auch als eigenständige App". Kein Pop-up, kein
|
||||
forcierter Redirect — User entscheiden selbst.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Cards
|
||||
# Cardecky
|
||||
|
||||
Spaced-repetition flashcards on **cards.mana.how**.
|
||||
Spaced-repetition flashcards on **cardecky.mana.how**.
|
||||
|
||||
Phase-1 standalone web app. The frontend lives here; data, auth, and
|
||||
sync are shared with the rest of the Mana stack:
|
||||
|
|
@ -9,7 +9,7 @@ sync are shared with the rest of the Mana stack:
|
|||
- **Sync:** mana-sync, app-id `cards`
|
||||
- **Storage:** `mana_platform.cards.*` (Postgres, RLS)
|
||||
|
||||
The same `cards` data backs the **mana** built-in Cards module at
|
||||
The same `cards` data backs the **mana** built-in Cardecky module at
|
||||
`mana.how/cards`. Schema changes ship to both frontends together — see
|
||||
`apps/cards/GUIDELINES.md`.
|
||||
|
||||
|
|
@ -30,5 +30,5 @@ will land in Phase 2/3.
|
|||
|
||||
```bash
|
||||
pnpm install
|
||||
pnpm --filter @cards/web dev # cards.mana.how on http://localhost:5180
|
||||
pnpm --filter @cards/web dev # cardecky.mana.how on http://localhost:5180
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# syntax=docker/dockerfile:1
|
||||
# Cards Standalone — cards.mana.how. Mirrors apps/manavoxel/apps/web/Dockerfile.
|
||||
# Cardecky Standalone — cardecky.mana.how. Mirrors apps/manavoxel/apps/web/Dockerfile.
|
||||
|
||||
# ─── Stage 1: Build ──────────────────────────────────────────
|
||||
FROM sveltekit-base:local AS builder
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import type { Handle } from '@sveltejs/kit';
|
|||
*
|
||||
* `@mana/shared-auth-ui` reads `window.__PUBLIC_MANA_AUTH_URL__` to know
|
||||
* where to POST /api/v1/auth/login (and friends). Without this hook the
|
||||
* client falls back to a relative URL → 404 on cards.mana.how.
|
||||
* client falls back to a relative URL → 404 on cardecky.mana.how.
|
||||
*
|
||||
* `process.env.PUBLIC_MANA_*_URL_CLIENT` come from the container
|
||||
* environment (docker-compose.macmini.yml). $env/static/public would
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* Thin client for cards-server (https://cards-api.mana.how / dev :3072).
|
||||
* Thin client for cards-server (https://cardecky-api.mana.how / dev :3072).
|
||||
*
|
||||
* The auth-store provides the JWT; we never read tokens from storage
|
||||
* here directly so there's only one place that knows about token
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@
|
|||
// svelte-ignore state_referenced_locally
|
||||
let deckDescription = $state(deck.description ?? '');
|
||||
let deckLanguage = $state('de');
|
||||
let deckLicense = $state<'CC0-1.0' | 'CC-BY-4.0' | 'CC-BY-SA-4.0' | 'Cards-Personal-Use-1.0'>(
|
||||
let deckLicense = $state<'CC0-1.0' | 'CC-BY-4.0' | 'CC-BY-SA-4.0' | 'Cardecky-Personal-Use-1.0'>(
|
||||
'CC-BY-4.0'
|
||||
);
|
||||
let deckSemver = $state('1.0.0');
|
||||
|
|
@ -153,7 +153,7 @@
|
|||
<div class="space-y-4 text-sm">
|
||||
<p class="text-foreground/80">
|
||||
Erstelle ein Author-Profil — andere User finden deine Decks unter
|
||||
<code class="rounded bg-muted px-1 text-xs">cards.mana.how/u/dein-slug</code>.
|
||||
<code class="rounded bg-muted px-1 text-xs">cardecky.mana.how/u/dein-slug</code>.
|
||||
</p>
|
||||
<div>
|
||||
<label for="author-slug" class="mb-1 block text-xs text-muted-foreground">
|
||||
|
|
@ -206,7 +206,7 @@
|
|||
<div class="space-y-4 text-sm">
|
||||
<p class="text-muted-foreground">
|
||||
Veröffentlicht als <code class="rounded bg-muted px-1 text-xs"
|
||||
>cards.mana.how/d/{deckSlug || '...'}</code
|
||||
>cardecky.mana.how/d/{deckSlug || '...'}</code
|
||||
>
|
||||
</p>
|
||||
<div>
|
||||
|
|
@ -263,7 +263,7 @@
|
|||
<option value="CC-BY-4.0">CC-BY 4.0 — frei mit Namensnennung</option>
|
||||
<option value="CC-BY-SA-4.0">CC-BY-SA 4.0 — share-alike</option>
|
||||
<option value="CC0-1.0">CC0 — gemeinfrei</option>
|
||||
<option value="Cards-Personal-Use-1.0">Personal Use — nur lernen</option>
|
||||
<option value="Cardecky-Personal-Use-1.0">Personal Use — nur lernen</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
* Auth Store — uses the shared Mana auth factory.
|
||||
*
|
||||
* SSO: tokens land in the shared `*.mana.how` storage so a user already
|
||||
* signed into mana.how / cards.mana.how lands directly in the app
|
||||
* signed into mana.how / cardecky.mana.how lands directly in the app
|
||||
* without re-typing credentials. The factory wires up the token
|
||||
* manager + refresh + storage adapter for us.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Cards-Marktplatz — Plan
|
||||
# Cardecky-Marktplatz — Plan
|
||||
|
||||
> **Status**: Plan, kein Code. Stand 2026-05-07.
|
||||
> **Goal-Setting**: Vollvision, kein MVP-Druck. Wir bauen die optimale Lösung.
|
||||
|
|
@ -38,7 +38,7 @@
|
|||
2. **Public-Decks leben separat** vom Local-First-Sync-Pfad (eigene Postgres-Tabellen, eigene Service, eigene RLS-Policies). Kein Vermischen mit `mana_sync.sync_changes`.
|
||||
3. **Subscribed Decks sind unidirektional**: Author → Subscribers. Updates fließen einseitig. Wer ändern will, forkt.
|
||||
4. **Content-Hash überall.** Jede Karte und jede Version bekommt einen deterministischen SHA-256 → Trust + Cache + Diff kostenlos.
|
||||
5. **Lizenzen sind explizit + maschinen-lesbar** (SPDX-IDs: `CC0-1.0`, `CC-BY-4.0`, `CC-BY-SA-4.0`, plus eigener `Cards-Personal-Use-1.0` für Default-Käufe und `Cards-Pro-Only-1.0` für paid Decks).
|
||||
5. **Lizenzen sind explizit + maschinen-lesbar** (SPDX-IDs: `CC0-1.0`, `CC-BY-4.0`, `CC-BY-SA-4.0`, plus eigener `Cardecky-Personal-Use-1.0` für Default-Käufe und `Cardecky-Pro-Only-1.0` für paid Decks).
|
||||
6. **AI ist Moderator, nicht Gatekeeper** — KI-First-Pass + Human-Review-Eskalation. Niemals KI-allein-Take-down.
|
||||
7. **Search ist von der DB entkoppelt** — Read-Only-Index, asynchron befüllt. Bricht der Search-Service, läuft der Marktplatz weiter.
|
||||
8. **mana-credits ist die einzige Geld-Schnittstelle** — niemals Stripe direkt im cards-server. Alles geht über `/api/v1/credits/use`, `/credits/grant`, `/credits/reservations/*`.
|
||||
|
|
@ -93,7 +93,7 @@ public_decks (
|
|||
takedown_at timestamptz,
|
||||
takedown_reason text,
|
||||
created_at timestamptz DEFAULT now(),
|
||||
CONSTRAINT price_requires_license CHECK (price_credits = 0 OR license = 'Cards-Pro-Only-1.0')
|
||||
CONSTRAINT price_requires_license CHECK (price_credits = 0 OR license = 'Cardecky-Pro-Only-1.0')
|
||||
)
|
||||
|
||||
public_deck_versions (
|
||||
|
|
@ -418,7 +418,7 @@ POST /v1/notifications/:id/read — Mark read
|
|||
PATCH /v1/notifications/preferences — Settings (welche Events triggern Push)
|
||||
```
|
||||
|
||||
## 8. UI / Routes (Cards-Frontend)
|
||||
## 8. UI / Routes (Cardecky-Frontend)
|
||||
|
||||
```
|
||||
/explore — Featured + Trending + Tag-Tree + Search-Bar
|
||||
|
|
@ -485,7 +485,7 @@ Marktplatz ohne Decks ist nutzlos. Drei parallele Hebel:
|
|||
- Integration-Tests (Drizzle + Vitest)
|
||||
- mana-auth-JWT-Middleware (`@mana/shared-hono`)
|
||||
- Container in `docker-compose.macmini.yml`
|
||||
- Cloudflare-Tunnel-Route `cards-api.mana.how` → `:3072`
|
||||
- Cloudflare-Tunnel-Route `cardecky-api.mana.how` → `:3072`
|
||||
|
||||
### Phase β — Author-Workflow ✅ shipped
|
||||
|
||||
|
|
@ -510,7 +510,7 @@ Marktplatz ohne Decks ist nutzlos. Drei parallele Hebel:
|
|||
|
||||
### Phase δ — Subscribe + Updates + Smart-Merge ✅ shipped
|
||||
|
||||
- ✅ „Abonnieren"-Button → lädt aktuelle Version in lokale Cards-DB
|
||||
- ✅ „Abonnieren"-Button → lädt aktuelle Version in lokale Cardecky-DB
|
||||
- 🟡 Update-Detection: Polling beim Öffnen der Deck-Page; **kein** WebSocket-Push (kommt in θ/ι)
|
||||
- ✅ **Smart-Merge**: Diff zwischen Versionen → unveränderte Karten behalten FSRS-State; geänderte erben FSRS-State über Ord-Pairing-Heuristik; neue + entfernte werden korrekt behandelt
|
||||
- ✅ Diff-View „+N · ~N · −N" mit Apply-Button auf der Deck-Page
|
||||
|
|
@ -565,7 +565,7 @@ Marktplatz ohne Decks ist nutzlos. Drei parallele Hebel:
|
|||
### Phasen die später kommen (explizit nicht in diesem Plan)
|
||||
|
||||
- **Phase λ — Co-Learn-Sessions**: WebSocket-Multiplayer, gemeinsam lernen, Sehen-was-andere-machen
|
||||
- **Phase μ — Mobile-Apps**: Expo-App (Cards-Standalone-Mobile)
|
||||
- **Phase μ — Mobile-Apps**: Expo-App (Cardecky-Standalone-Mobile)
|
||||
- **Phase ν — Author-Tools**: Bulk-Edit-UI für Authoren mit großen Decks, Style-Templates, Author-Analytics-Deep-Dive
|
||||
- **Phase ξ — Lern-Battles**: Asynchroner Wettkampf-Modus
|
||||
|
||||
|
|
@ -629,7 +629,7 @@ Marktplatz ohne Decks ist nutzlos. Drei parallele Hebel:
|
|||
|
||||
| Phase | Status | Was läuft | Was fehlt |
|
||||
|-------|--------|-----------|-----------|
|
||||
| α — Skelett | ✅ | cards-server lebt auf 3072, Schema gepushed, JWT-Auth, Container in `docker-compose.macmini.yml`, Tunnel-Route `cards-api.mana.how` | — |
|
||||
| α — Skelett | ✅ | cards-server lebt auf 3072, Schema gepushed, JWT-Auth, Container in `docker-compose.macmini.yml`, Tunnel-Route `cardecky-api.mana.how` | — |
|
||||
| β — Author-Workflow | ✅ | Profil-Claim, Publish, Lizenz, Preis, AI-Mod-Verdict | Tag-Picker im Publish, Author-Dashboard-Stats |
|
||||
| γ — Discovery | ✅ | `/explore`, Stars, Follows, Author-Profile, Trending | tsvector-FTS, Tag-Tree, Activity-Feed |
|
||||
| δ — Subscribe + Smart-Merge | ✅ | Pull, Smart-Merge mit FSRS-State-Erhalt, Diff-View | WebSocket-Push, Update-Mails |
|
||||
|
|
@ -640,7 +640,7 @@ Marktplatz ohne Decks ist nutzlos. Drei parallele Hebel:
|
|||
| ι — Optimierung | ⏳ | — | Search-Service, CDN, Rate-Limiting, Materialized Views |
|
||||
| λ / μ / ν / ξ | ⏳ | — | später (Co-Learn, Mobile, Author-Tools, Lern-Battles) |
|
||||
|
||||
**Live-Domains**: `cards.mana.how` (Web) · `cards-api.mana.how` (API).
|
||||
**Live-Domains**: `cardecky.mana.how` (Web) · `cardecky-api.mana.how` (API).
|
||||
|
||||
**Nächste sinnvolle Schritte (Empfehlung)**:
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"name": "cards",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"description": "Cards — Spaced-Repetition flashcards on cards.mana.how. Standalone Phase-1 frontend; data shared with the mana cards module via mana-sync.",
|
||||
"description": "Cardecky — Spaced-Repetition flashcards on cardecky.mana.how (Marketing-Landing: cardecky.com). Standalone Phase-1 frontend; data shared with the mana cards module via mana-sync.",
|
||||
"scripts": {
|
||||
"dev": "pnpm run --filter=@cards/* --parallel dev"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ All landing pages and static sites are deployed to **Cloudflare Pages** using Di
|
|||
| Chat | `@chat/landing` | `chat-landing` | chat.mana.how |
|
||||
| Picture | `@picture/landing` | `picture-landing` | picture.mana.how |
|
||||
| Mana | `@mana/landing` | `mana-landing` | mana.how |
|
||||
| Cards | `@cards/landing` | `cards-landing` | cards.mana.how |
|
||||
| Cardecky | `@cards/landing` | `cardecky-landing` | cardecky.com |
|
||||
| Quotes | `@quotes/landing` | `quotes-landing` | quotes.mana.how |
|
||||
| Docs | `@mana/docs` | `mana-docs` | docs.mana.how |
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ const ecosystemApps = [
|
|||
{ label: 'Picture', href: 'https://picture.mana.how', status: 'alpha' },
|
||||
{ label: 'Storage', href: 'https://storage.mana.how', status: 'alpha' },
|
||||
{ label: 'Presi', href: 'https://presi.mana.how', status: 'alpha' },
|
||||
{ label: 'Cards', href: 'https://cards.mana.how', status: 'alpha' },
|
||||
{ label: 'Cardecky', href: 'https://cardecky.mana.how', status: 'alpha' },
|
||||
{ label: 'Mukke', href: 'https://mukke.mana.how', status: 'alpha' },
|
||||
{ label: 'Photos', href: 'https://photos.mana.how', status: 'alpha' },
|
||||
{ label: 'SkillTree', href: 'https://skilltree.mana.how', status: 'alpha' },
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ const sections: Section[] = [
|
|||
{
|
||||
label: 'Lernen',
|
||||
apps: [
|
||||
{ name: 'Cards', icon: 'ph:cards-bold', tagline: 'KI Karteikarten', url: 'https://cards.mana.how' },
|
||||
{ name: 'Cardecky', icon: 'ph:cards-bold', tagline: 'KI Karteikarten', url: 'https://cardecky.mana.how' },
|
||||
{ name: 'SkilltTree', icon: 'ph:tree-structure-bold', tagline: 'Skill-Tracking', url: 'https://skilltree.mana.how' },
|
||||
{ name: 'Quotes', icon: 'ph:quotes-bold', tagline: 'Zitate & Inspiration', url: 'https://quotes.mana.how' },
|
||||
],
|
||||
|
|
|
|||
|
|
@ -261,7 +261,7 @@ export const appConfigs: Record<string, AppConfig> = {
|
|||
},
|
||||
],
|
||||
dashboardRoute: '/',
|
||||
website: 'https://cards.mana.how',
|
||||
website: 'https://cardecky.mana.how',
|
||||
},
|
||||
|
||||
todo: {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
<!--
|
||||
Cards — Workbench ListView
|
||||
Deck list with card counts and due-now indicator.
|
||||
Cardecky — Workbench ListView (in-mana cards module).
|
||||
Deck list with card counts and due-now indicator. Per GUIDELINES §12,
|
||||
shows a dezenten Hinweis auf die Cardecky-Standalone-App, einmal
|
||||
schließbar (localStorage).
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { useLiveQueryWithDefault } from '@mana/local-store/svelte';
|
||||
|
|
@ -11,6 +13,19 @@
|
|||
|
||||
let { navigate }: ViewProps = $props();
|
||||
|
||||
const STANDALONE_HINT_KEY = 'cardecky-standalone-hint-dismissed';
|
||||
let standaloneHintDismissed = $state(
|
||||
typeof localStorage !== 'undefined' && localStorage.getItem(STANDALONE_HINT_KEY) === '1'
|
||||
);
|
||||
function dismissStandaloneHint() {
|
||||
standaloneHintDismissed = true;
|
||||
try {
|
||||
localStorage.setItem(STANDALONE_HINT_KEY, '1');
|
||||
} catch {
|
||||
// localStorage unavailable (private mode etc.) — UI-state alone reicht.
|
||||
}
|
||||
}
|
||||
|
||||
const decksQuery = useLiveQueryWithDefault(async () => {
|
||||
const all = await db.table<LocalDeck>('cardDecks').toArray();
|
||||
return all.filter((d) => !d.deletedAt);
|
||||
|
|
@ -54,6 +69,28 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
{#if !standaloneHintDismissed}
|
||||
<div
|
||||
class="mb-2 flex items-start gap-2 rounded-md border border-border bg-muted/30 px-3 py-2 text-xs"
|
||||
role="note"
|
||||
>
|
||||
<span class="flex-1 text-muted-foreground">
|
||||
Cardecky gibt es jetzt auch als eigenständige App auf
|
||||
<a
|
||||
href="https://cardecky.mana.how"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
class="underline hover:text-foreground">cardecky.mana.how</a
|
||||
> — gleiche Daten, fokussierte UI.
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
onclick={dismissStandaloneHint}
|
||||
class="text-muted-foreground hover:text-foreground"
|
||||
aria-label="Hinweis schließen">×</button
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
<BaseListView items={decks} getKey={(d) => d.id} emptyTitle="Keine Decks">
|
||||
{#snippet header()}
|
||||
<span class="flex-1">{decks.length} Decks</span>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Cards module — types are now sourced from `@mana/cards-core` so the
|
||||
* standalone cards.mana.how app and this in-mana module stay in sync.
|
||||
* Cardecky / cards module — types are now sourced from `@mana/cards-core`
|
||||
* so the standalone cardecky.mana.how app and this in-mana module stay in sync.
|
||||
*
|
||||
* This file is a thin re-export to keep existing
|
||||
* `from './types'` / `from '$lib/modules/cards/types'` imports working.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue