refactor: rename ManaDeck to Cards across entire monorepo

Rename the flashcard/deck management app from ManaDeck to Cards:
- Directory: apps/manadeck → apps/cards, packages/manadeck-database → packages/cards-database
- Packages: @manadeck/* → @cards/*, @manacore/manadeck-database → @manacore/cards-database
- Domain: manadeck.mana.how → cards.mana.how
- Storage: manadeck-storage → cards-storage
- Database: manadeck → cards
- All shared packages, infra configs, services, i18n, and docs updated
- 244 files changed, zero remaining manadeck references

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-01 11:45:21 +02:00
parent 29b77f22e4
commit 75a3ea2957
244 changed files with 907 additions and 924 deletions

View file

@ -1,9 +1,9 @@
# Database connection URL
# Format: postgresql://user:password@host:port/database
DATABASE_URL=postgresql://postgres:password@localhost:5432/manadeck
DATABASE_URL=postgresql://postgres:password@localhost:5432/cards
# Alternative name (used as fallback)
MANADECK_DATABASE_URL=postgresql://postgres:password@localhost:5432/manadeck
CARDS_DATABASE_URL=postgresql://postgres:password@localhost:5432/cards
# Supabase credentials (only needed for migration)
SUPABASE_URL=https://your-project.supabase.co

View file

@ -1,18 +1,18 @@
services:
postgres:
image: postgres:16-alpine
container_name: manadeck-postgres
container_name: cards-postgres
restart: unless-stopped
environment:
POSTGRES_USER: manadeck
POSTGRES_PASSWORD: manadeck_dev_password
POSTGRES_DB: manadeck
POSTGRES_USER: cards
POSTGRES_PASSWORD: cards_dev_password
POSTGRES_DB: cards
ports:
- "5433:5432"
volumes:
- manadeck_postgres_data:/var/lib/postgresql/data
- cards_postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U manadeck -d manadeck"]
test: ["CMD-SHELL", "pg_isready -U cards -d cards"]
interval: 10s
timeout: 5s
retries: 5
@ -20,20 +20,20 @@ services:
# Optional: pgAdmin for database management UI
pgadmin:
image: dpage/pgadmin4:latest
container_name: manadeck-pgadmin
container_name: cards-pgadmin
restart: unless-stopped
environment:
PGADMIN_DEFAULT_EMAIL: admin@manadeck.local
PGADMIN_DEFAULT_EMAIL: admin@cards.local
PGADMIN_DEFAULT_PASSWORD: admin
PGADMIN_CONFIG_SERVER_MODE: 'False'
ports:
- "5050:80"
volumes:
- manadeck_pgadmin_data:/var/lib/pgadmin
- cards_pgadmin_data:/var/lib/pgadmin
depends_on:
postgres:
condition: service_healthy
volumes:
manadeck_postgres_data:
manadeck_pgadmin_data:
cards_postgres_data:
cards_pgadmin_data:

View file

@ -1,5 +1,5 @@
{
"name": "@manacore/manadeck-database",
"name": "@manacore/cards-database",
"version": "1.0.0",
"private": true,
"type": "module",

View file

@ -10,10 +10,10 @@ let pgClient: ReturnType<typeof postgres> | null = null;
* Get the database URL from environment variables
*/
function getDatabaseUrl(): string {
const url = process.env.DATABASE_URL || process.env.MANADECK_DATABASE_URL;
const url = process.env.DATABASE_URL || process.env.CARDS_DATABASE_URL;
if (!url) {
throw new Error(
'Database URL not found. Set DATABASE_URL or MANADECK_DATABASE_URL environment variable.'
'Database URL not found. Set DATABASE_URL or CARDS_DATABASE_URL environment variable.'
);
}
return url;

View file

@ -1,4 +1,4 @@
// Main entry point for @manacore/manadeck-database
// Main entry point for @manacore/cards-database
// Export database client utilities
export { createClient, getDb, closeDb, type Database } from './client.js';

View file

@ -96,7 +96,7 @@ async function migrateTable<T>(
}
async function main() {
console.log('=== ManaDeck Data Migration ===');
console.log('=== Cards Data Migration ===');
console.log('From: Supabase');
console.log('To: PostgreSQL (Drizzle)');
console.log('==============================\n');

View file

@ -37,7 +37,7 @@ export enum CreditOperationType {
// NutriPhi - Food analysis
AI_FOOD_ANALYSIS = 'ai_food_analysis',
// ManaDeck - AI deck generation
// Cards - AI deck generation
AI_DECK_GENERATION = 'ai_deck_generation',
AI_CARD_GENERATION = 'ai_card_generation',
@ -271,13 +271,13 @@ export const OPERATION_METADATA: Record<CreditOperationType, OperationMetadata>
name: 'Generate AI Deck',
description: 'Generate a complete deck with AI (10 cards)',
category: CreditCategory.AI,
app: 'manadeck',
app: 'cards',
},
[CreditOperationType.AI_CARD_GENERATION]: {
name: 'Generate AI Card',
description: 'Generate a single card with AI',
category: CreditCategory.AI,
app: 'manadeck',
app: 'cards',
},
// Quote Explanation

View file

@ -111,7 +111,7 @@ export type StandardOperationType =
| 'CHARACTER_IMPORT'
| 'STORY_CREATION'
| 'STORY_CONTINUATION'
// ManaDeck operations
// Cards operations
| 'DECK_CREATION'
| 'CARD_GENERATION'
| 'AI_REVIEW'
@ -141,7 +141,7 @@ export const DEFAULT_OPERATION_PRICING: Record<string, number> = {
CHARACTER_IMPORT: 10,
STORY_CREATION: 10,
STORY_CONTINUATION: 5,
// ManaDeck
// Cards
DECK_CREATION: 5,
CARD_GENERATION: 2,
AI_REVIEW: 10,

View file

@ -1,72 +1,67 @@
<script lang="ts">
import { getManaApp, type AppIconId } from '@manacore/shared-branding';
import { X, Info, Warning, SignIn, UserPlus, Question, ArrowRight } from '@manacore/shared-icons';
import {
X,
Info,
SignIn,
UserPlus,
Question,
ArrowRight,
Lightning,
ShieldCheck,
Sparkle,
ArrowSquareOut,
} from '@manacore/shared-icons';
import { markGuestWelcomeSeen } from '../utils/guestWelcome';
import type { GuestWelcomeTranslations } from '../types';
const defaultTranslationsDE: GuestWelcomeTranslations = {
title: 'Willkommen',
guestModeTitle: 'Als Gast kannst du:',
guestModeTitle: 'Sofort loslegen:',
whatYouCanDo: 'Was du als Gast tun kannst',
dataWarningTitle: 'Hinweis',
dataWarningText:
'Daten werden nur in diesem Browser-Tab gespeichert und gehen beim Schließen verloren.',
'Daten werden nur lokal in diesem Browser gespeichert. Melde dich an, um sie geräteübergreifend zu synchronisieren.',
loginButton: 'Anmelden',
registerButton: 'Kostenloses Konto erstellen',
registerButton: 'Konto erstellen',
helpButton: 'Hilfe & Erste Schritte',
continueAsGuest: 'Weiter als Gast',
};
const defaultTranslationsEN: GuestWelcomeTranslations = {
title: 'Welcome',
guestModeTitle: 'As a guest you can:',
guestModeTitle: 'Get started instantly:',
whatYouCanDo: 'What you can do as a guest',
dataWarningTitle: 'Note',
dataWarningText: 'Data is only stored in this browser tab and will be lost when you close it.',
dataWarningText: 'Data is stored locally in this browser only. Sign in to sync across devices.',
loginButton: 'Sign In',
registerButton: 'Create Free Account',
registerButton: 'Sign Up',
helpButton: 'Help & Getting Started',
continueAsGuest: 'Continue as Guest',
};
/** Default features per app (German) */
const defaultFeaturesDE: Record<string, string[]> = {
contacts: [
'Kontakte erstellen und bearbeiten',
'Tags und Gruppen verwenden',
'Alle Features ausprobieren',
],
chat: ['Mit der KI chatten', 'Verschiedene Modelle ausprobieren', 'Nachrichten formatieren'],
todo: [
'Aufgaben erstellen und organisieren',
'Projekte und Labels nutzen',
'Subtasks und Deadlines setzen',
],
calendar: [
'Termine erstellen und bearbeiten',
'Verschiedene Ansichten nutzen',
'Erinnerungen setzen',
],
clock: ['Weltzeituhr anzeigen', 'Timer und Stoppuhr nutzen', 'Wecker einrichten'],
zitare: ['Inspirierende Zitate entdecken', 'Favoriten markieren', 'Zitate teilen'],
picture: [
'Bilder mit KI generieren',
'Verschiedene Stile ausprobieren',
'Bilder herunterladen',
],
manadeck: ['Karteikarten erstellen', 'Decks organisieren', 'Lernmodus testen'],
contacts: ['Alle Kontakte an einem Ort', 'Keine Anmeldung nötig', 'Deine Daten gehören dir'],
chat: ['Dein persönlicher KI-Assistent', 'Keine Anmeldung nötig', 'Deine Daten gehören dir'],
todo: ['Organisiere deinen Alltag', 'Keine Anmeldung nötig', 'Deine Daten gehören dir'],
calendar: ['Dein Kalender, deine Regeln', 'Keine Anmeldung nötig', 'Deine Daten gehören dir'],
clock: ['Zeit im Griff, überall', 'Keine Anmeldung nötig', 'Deine Daten gehören dir'],
zitare: ['Tägliche Inspiration für dich', 'Keine Anmeldung nötig', 'Deine Daten gehören dir'],
picture: ['Kreativität trifft KI', 'Keine Anmeldung nötig', 'Deine Daten gehören dir'],
cards: ['Lernen leicht gemacht', 'Keine Anmeldung nötig', 'Deine Daten gehören dir'],
};
/** Default features per app (English) */
const defaultFeaturesEN: Record<string, string[]> = {
contacts: ['Create and edit contacts', 'Use tags and groups', 'Try all features'],
chat: ['Chat with the AI', 'Try different models', 'Format messages'],
todo: ['Create and organize tasks', 'Use projects and labels', 'Set subtasks and deadlines'],
calendar: ['Create and edit events', 'Use different views', 'Set reminders'],
clock: ['View world clocks', 'Use timer and stopwatch', 'Set alarms'],
zitare: ['Discover inspiring quotes', 'Mark favorites', 'Share quotes'],
picture: ['Generate images with AI', 'Try different styles', 'Download images'],
manadeck: ['Create flashcards', 'Organize decks', 'Test learning mode'],
contacts: ['All your contacts in one place', 'No sign-up needed', 'Your data belongs to you'],
chat: ['Your personal AI assistant', 'No sign-up needed', 'Your data belongs to you'],
todo: ['Organize your day', 'No sign-up needed', 'Your data belongs to you'],
calendar: ['Your calendar, your rules', 'No sign-up needed', 'Your data belongs to you'],
clock: ['Time at your fingertips', 'No sign-up needed', 'Your data belongs to you'],
zitare: ['Daily inspiration for you', 'No sign-up needed', 'Your data belongs to you'],
picture: ['Where creativity meets AI', 'No sign-up needed', 'Your data belongs to you'],
cards: ['Learning made easy', 'No sign-up needed', 'Your data belongs to you'],
};
interface Props {
@ -125,11 +120,6 @@
]
);
// App description based on locale
const appDescription = $derived(
appInfo ? (locale === 'de' ? appInfo.longDescription.de : appInfo.longDescription.en) : ''
);
function handleContinueAsGuest() {
markGuestWelcomeSeen(appId);
onClose();
@ -212,10 +202,10 @@
<div
class="modal-backdrop"
onclick={handleBackdropClick}
onkeydown={(e) => e.key === 'Enter' && handleBackdropClick(e as unknown as MouseEvent)}
role="dialog"
aria-modal="true"
aria-labelledby="welcome-title"
aria-describedby="welcome-features"
tabindex="-1"
>
<!-- Modal Content -->
@ -236,55 +226,47 @@
<img src={appInfo.icon} alt={appInfo.name} class="app-icon" />
</div>
<!-- App Name & Description -->
<!-- App Name -->
<h2 id="welcome-title" class="app-name">{appInfo.name}</h2>
<p class="app-description">{appDescription}</p>
<!-- Divider -->
<div class="divider"></div>
<!-- Guest Features -->
<div class="features-section">
<div class="section-header">
<Info size={18} class="section-icon info-icon" />
<span class="section-title">{t.guestModeTitle}</span>
</div>
<div id="welcome-features" class="features-section">
<ul class="features-list">
{#each featureList as feature}
{#each featureList as feature, i}
<li class="feature-item">
<span class="feature-bullet"></span>
{#if i === 0}
<Sparkle size={16} weight="fill" class="feature-icon" />
{:else if i === 1}
<Lightning size={16} weight="fill" class="feature-icon" />
{:else}
<ShieldCheck size={16} weight="fill" class="feature-icon" />
{/if}
<span>{feature}</span>
</li>
{/each}
</ul>
</div>
<!-- Data Warning -->
<div class="warning-section">
<Warning size={18} class="section-icon warning-icon" />
<p class="warning-text">{t.dataWarningText}</p>
</div>
<!-- Action Buttons -->
<div class="buttons-section">
<button
type="button"
class="btn btn-primary"
style:--btn-color={appInfo.color}
onclick={handleLogin}
onclick={handleRegister}
>
<SignIn size={20} />
<span>{t.loginButton}</span>
<UserPlus size={20} />
<span>{t.registerButton}</span>
</button>
<button
type="button"
class="btn btn-secondary"
style:--btn-color={appInfo.color}
onclick={handleRegister}
onclick={handleLogin}
>
<UserPlus size={20} />
<span>{t.registerButton}</span>
<SignIn size={20} />
<span>{t.loginButton}</span>
</button>
{#if onHelp || helpHref}
@ -298,6 +280,23 @@
<span>{t.continueAsGuest}</span>
<ArrowRight size={18} />
</button>
<!-- Data hint + learn more -->
<div class="footer-hints">
<p class="data-hint">
<Info size={14} class="data-hint-icon" />
<span>{t.dataWarningText}</span>
</p>
<a
href="https://manacore-landing.pages.dev"
target="_blank"
rel="noopener noreferrer"
class="learn-more-link"
>
<span>{locale === 'de' ? 'Mehr über Mana erfahren' : 'Learn more about Mana'}</span>
<ArrowSquareOut size={12} />
</a>
</div>
</div>
</div>
</div>
@ -387,9 +386,9 @@
display: flex;
align-items: center;
justify-content: center;
width: 80px;
height: 80px;
margin: 0 auto 1rem;
width: 56px;
height: 56px;
margin: 0 auto 0.75rem;
border-radius: 1.25rem;
background: linear-gradient(
145deg,
@ -403,8 +402,8 @@
}
.app-icon {
width: 48px;
height: 48px;
width: 36px;
height: 36px;
object-fit: contain;
}
@ -420,61 +419,10 @@
color: rgba(255, 255, 255, 0.95);
}
.app-description {
margin: 0 0 1.25rem;
font-size: 0.9rem;
line-height: 1.5;
text-align: center;
color: rgba(0, 0, 0, 0.6);
}
:global(.dark) .app-description {
color: rgba(255, 255, 255, 0.6);
}
.divider {
height: 1px;
margin: 1rem 0;
background: linear-gradient(90deg, transparent, rgba(0, 0, 0, 0.1), transparent);
}
:global(.dark) .divider {
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.15), transparent);
}
.features-section {
margin-bottom: 1rem;
}
.section-header {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.75rem;
}
.section-title {
font-size: 0.875rem;
font-weight: 600;
color: rgba(0, 0, 0, 0.8);
}
:global(.dark) .section-title {
color: rgba(255, 255, 255, 0.8);
}
.section-icon {
flex-shrink: 0;
}
:global(.info-icon) {
color: #3b82f6;
}
:global(.warning-icon) {
color: #f59e0b;
}
.features-list {
margin: 0;
padding: 0;
@ -483,7 +431,7 @@
.feature-item {
display: flex;
align-items: flex-start;
align-items: center;
gap: 0.5rem;
padding: 0.35rem 0;
font-size: 0.875rem;
@ -494,34 +442,13 @@
color: rgba(255, 255, 255, 0.7);
}
.feature-bullet {
color: rgba(0, 0, 0, 0.3);
:global(.feature-icon) {
flex-shrink: 0;
color: rgba(0, 0, 0, 0.4);
}
:global(.dark) .feature-bullet {
color: rgba(255, 255, 255, 0.4);
}
.warning-section {
display: flex;
align-items: flex-start;
gap: 0.75rem;
padding: 0.875rem;
margin-bottom: 1.25rem;
border-radius: 0.875rem;
background: rgba(245, 158, 11, 0.1);
border: 1px solid rgba(245, 158, 11, 0.25);
}
.warning-text {
margin: 0;
font-size: 0.8rem;
line-height: 1.5;
color: rgba(0, 0, 0, 0.75);
}
:global(.dark) .warning-text {
color: rgba(255, 255, 255, 0.75);
:global(.dark .feature-icon) {
color: rgba(255, 255, 255, 0.45);
}
.buttons-section {
@ -530,13 +457,69 @@
gap: 0.625rem;
}
.footer-hints {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.5rem;
margin-top: 0.25rem;
}
.learn-more-link {
display: inline-flex;
align-items: center;
gap: 0.25rem;
font-size: 0.75rem;
color: rgba(0, 0, 0, 0.4);
text-decoration: none;
transition: color 0.2s ease;
}
.learn-more-link:hover {
color: rgba(0, 0, 0, 0.7);
}
:global(.dark) .learn-more-link {
color: rgba(255, 255, 255, 0.4);
}
:global(.dark) .learn-more-link:hover {
color: rgba(255, 255, 255, 0.7);
}
.data-hint {
display: flex;
align-items: flex-start;
justify-content: center;
gap: 0.375rem;
margin: 0.25rem 0 0;
font-size: 0.75rem;
line-height: 1.4;
color: rgba(0, 0, 0, 0.4);
text-align: center;
}
:global(.dark) .data-hint {
color: rgba(255, 255, 255, 0.4);
}
:global(.data-hint-icon) {
flex-shrink: 0;
margin-top: 0.1rem;
color: rgba(0, 0, 0, 0.3);
}
:global(.dark .data-hint-icon) {
color: rgba(255, 255, 255, 0.3);
}
.btn {
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
width: 100%;
padding: 0.875rem 1rem;
padding: 0.75rem 1rem;
border: none;
border-radius: 0.875rem;
font-size: 0.9375rem;
@ -652,13 +635,13 @@
}
.icon-container {
width: 70px;
height: 70px;
width: 48px;
height: 48px;
}
.app-icon {
width: 42px;
height: 42px;
width: 32px;
height: 32px;
}
.app-name {

View file

@ -28,7 +28,7 @@ Display an app's logo:
<AppLogo app="memoro" size={32} />
<AppLogo app="manacore" size={32} />
<AppLogo app="manadeck" size={32} />
<AppLogo app="cards" size={32} />
<AppLogo app="maerchenzauber" size={32} />
```
@ -76,7 +76,7 @@ console.log(memoro.primaryColor); // "#f8d62b"
| ---------------- | ------------- | ---------------- | ---------------- |
| `memoro` | Memoro | #f8d62b (Gold) | AI Voice Memos |
| `manacore` | ManaCore | #6366f1 (Indigo) | Central Hub |
| `manadeck` | ManaDeck | #8b5cf6 (Purple) | AI Flashcards |
| `cards` | Cards | #8b5cf6 (Purple) | AI Flashcards |
| `maerchenzauber` | Märchenzauber | #ec4899 (Pink) | AI Story Creator |
## Props
@ -113,7 +113,7 @@ console.log(memoro.primaryColor); // "#f8d62b"
## Types
```typescript
type AppId = 'memoro' | 'manacore' | 'manadeck' | 'maerchenzauber';
type AppId = 'memoro' | 'manacore' | 'cards' | 'maerchenzauber';
interface AppBranding {
id: AppId;

View file

@ -24,8 +24,8 @@ const chatSvg = `<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="n
// Presi icon (presentation/slides icon with gradient)
const presiSvg = `<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="130" y="130" width="764" height="764" rx="382" fill="url(#presiGrad)"/><rect x="280" y="300" width="464" height="344" rx="24" fill="white"/><rect x="320" y="340" width="180" height="120" rx="8" fill="#f97316" fill-opacity="0.3"/><rect x="320" y="480" width="120" height="16" rx="4" fill="#f97316" fill-opacity="0.5"/><rect x="320" y="512" width="200" height="12" rx="3" fill="#f97316" fill-opacity="0.3"/><rect x="320" y="536" width="160" height="12" rx="3" fill="#f97316" fill-opacity="0.3"/><circle cx="620" cy="400" r="60" fill="#f97316"/><rect x="580" y="480" width="100" height="80" rx="8" fill="#f97316" fill-opacity="0.4"/><path d="M512 688L512 744" stroke="white" stroke-width="24" stroke-linecap="round"/><circle cx="512" cy="784" r="32" fill="white"/><defs><linearGradient id="presiGrad" x1="130" y1="130" x2="894" y2="894" gradientUnits="userSpaceOnUse"><stop stop-color="#f97316"/><stop offset="1" stop-color="#ea580c"/></linearGradient></defs></svg>`;
// ManaDeck icon (cards/flashcards with gradient)
const manadeckSvg = `<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="130" y="130" width="764" height="764" rx="382" fill="url(#deckGrad)"/><rect x="280" y="340" width="320" height="420" rx="24" fill="white" transform="rotate(-8 280 340)"/><rect x="340" y="280" width="320" height="420" rx="24" fill="white" transform="rotate(4 340 280)"/><rect x="380" y="300" width="320" height="420" rx="24" fill="white"/><rect x="420" y="380" width="240" height="20" rx="4" fill="#8b5cf6" fill-opacity="0.6"/><rect x="420" y="420" width="180" height="16" rx="4" fill="#8b5cf6" fill-opacity="0.4"/><rect x="420" y="450" width="200" height="16" rx="4" fill="#8b5cf6" fill-opacity="0.4"/><rect x="420" y="520" width="240" height="120" rx="8" fill="#8b5cf6" fill-opacity="0.2"/><defs><linearGradient id="deckGrad" x1="130" y1="130" x2="894" y2="894" gradientUnits="userSpaceOnUse"><stop stop-color="#8b5cf6"/><stop offset="1" stop-color="#7c3aed"/></linearGradient></defs></svg>`;
// Cards icon (cards/flashcards with gradient)
const cardsSvg = `<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="130" y="130" width="764" height="764" rx="382" fill="url(#deckGrad)"/><rect x="280" y="340" width="320" height="420" rx="24" fill="white" transform="rotate(-8 280 340)"/><rect x="340" y="280" width="320" height="420" rx="24" fill="white" transform="rotate(4 340 280)"/><rect x="380" y="300" width="320" height="420" rx="24" fill="white"/><rect x="420" y="380" width="240" height="20" rx="4" fill="#8b5cf6" fill-opacity="0.6"/><rect x="420" y="420" width="180" height="16" rx="4" fill="#8b5cf6" fill-opacity="0.4"/><rect x="420" y="450" width="200" height="16" rx="4" fill="#8b5cf6" fill-opacity="0.4"/><rect x="420" y="520" width="240" height="120" rx="8" fill="#8b5cf6" fill-opacity="0.2"/><defs><linearGradient id="deckGrad" x1="130" y1="130" x2="894" y2="894" gradientUnits="userSpaceOnUse"><stop stop-color="#8b5cf6"/><stop offset="1" stop-color="#7c3aed"/></linearGradient></defs></svg>`;
// Picture icon (image/gallery with gradient)
const pictureSvg = `<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="130" y="130" width="764" height="764" rx="382" fill="url(#pictureGrad)"/><rect x="260" y="300" width="504" height="424" rx="24" fill="white"/><circle cx="380" cy="420" r="50" fill="#22c55e" fill-opacity="0.6"/><path d="M260 600L420 480L520 580L620 460L764 600V700C764 713.255 753.255 724 740 724H284C270.745 724 260 713.255 260 700V600Z" fill="#22c55e" fill-opacity="0.4"/><defs><linearGradient id="pictureGrad" x1="130" y1="130" x2="894" y2="894" gradientUnits="userSpaceOnUse"><stop stop-color="#22c55e"/><stop offset="1" stop-color="#16a34a"/></linearGradient></defs></svg>`;
@ -94,7 +94,7 @@ export const APP_ICONS = {
mana: svgToDataUrl(manaSvg),
chat: svgToDataUrl(chatSvg),
presi: svgToDataUrl(presiSvg),
manadeck: svgToDataUrl(manadeckSvg),
cards: svgToDataUrl(cardsSvg),
picture: svgToDataUrl(pictureSvg),
zitare: svgToDataUrl(zitareSvg),
wisekeep: svgToDataUrl(wisekeepSvg),

View file

@ -28,9 +28,9 @@ export const APP_BRANDING: Record<AppId, AppBranding> = {
logoStroke: true,
logoStrokeWidth: 2,
},
manadeck: {
id: 'manadeck',
name: 'ManaDeck',
cards: {
id: 'cards',
name: 'Cards',
tagline: 'AI Flashcards',
primaryColor: '#8b5cf6',
secondaryColor: '#a78bfa',

View file

@ -17,7 +17,7 @@ export { default as ManaIcon } from './ManaIcon.svelte';
export {
MemoroLogo,
ManaCoreLogo,
ManaDeckLogo,
CardsLogo,
UloadLogo,
ChatLogo,
PresiLogo,

View file

@ -10,4 +10,4 @@
let { size = 55, color, class: className = '' }: Props = $props();
</script>
<AppLogo app="manadeck" {size} {color} class={className} />
<AppLogo app="cards" {size} {color} class={className} />

View file

@ -4,7 +4,7 @@
*/
export { default as MemoroLogo } from './MemoroLogo.svelte';
export { default as ManaCoreLogo } from './ManaCoreLogo.svelte';
export { default as ManaDeckLogo } from './ManaDeckLogo.svelte';
export { default as CardsLogo } from './CardsLogo.svelte';
export { default as UloadLogo } from './UloadLogo.svelte';
export { default as ChatLogo } from './ChatLogo.svelte';
export { default as PresiLogo } from './PresiLogo.svelte';

View file

@ -138,8 +138,8 @@ export const MANA_APPS: ManaApp[] = [
requiredTier: 'alpha',
},
{
id: 'manadeck',
name: 'ManaDeck',
id: 'cards',
name: 'Cards',
description: {
de: 'KI Karteikarten',
en: 'AI Flashcards',
@ -148,7 +148,7 @@ export const MANA_APPS: ManaApp[] = [
de: 'Lerne intelligenter mit KI-generierten Karteikarten und Spaced Repetition.',
en: 'Learn smarter with AI-generated flashcards and spaced repetition.',
},
icon: APP_ICONS.manadeck,
icon: APP_ICONS.cards,
color: '#8b5cf6',
comingSoon: true,
status: 'development',
@ -609,7 +609,7 @@ export const APP_URLS: Record<AppIconId, { dev: string; prod: string }> = {
chat: { dev: 'http://localhost:5174', prod: 'https://chat.mana.how' },
memoro: { dev: 'http://localhost:5175', prod: 'https://memoro.mana.how' },
presi: { dev: 'http://localhost:5176', prod: 'https://presi.mana.how' },
manadeck: { dev: 'http://localhost:5177', prod: 'https://manadeck.mana.how' },
cards: { dev: 'http://localhost:5177', prod: 'https://cards.mana.how' },
picture: { dev: 'http://localhost:5185', prod: 'https://picture.mana.how' },
zitare: { dev: 'http://localhost:5180', prod: 'https://zitare.mana.how' },
wisekeep: { dev: 'http://localhost:5181', prod: 'https://wisekeep.mana.how' },

View file

@ -4,7 +4,7 @@
export type AppId =
| 'memoro'
| 'manacore'
| 'manadeck'
| 'cards'
| 'uload'
| 'chat'
| 'presi'

View file

@ -185,7 +185,7 @@ Use one of the pre-built theme CSS files for quick setup:
/* OR */
@import '@manacore/shared-landing-ui/themes/maerchenzauber';
/* OR */
@import '@manacore/shared-landing-ui/themes/manadeck';
@import '@manacore/shared-landing-ui/themes/cards';
```
Or import in your Astro layout:
@ -320,7 +320,7 @@ src/
│ ├── memoro.css
│ ├── manacore.css
│ ├── maerchenzauber.css
│ └── manadeck.css
│ └── cards.css
└── utils/
└── index.ts # TypeScript types
```

View file

@ -17,7 +17,7 @@
"./themes/memoro": "./src/themes/memoro.css",
"./themes/manacore": "./src/themes/manacore.css",
"./themes/maerchenzauber": "./src/themes/maerchenzauber.css",
"./themes/manadeck": "./src/themes/manadeck.css",
"./themes/cards": "./src/themes/cards.css",
"./themes/picture": "./src/themes/picture.css",
"./themes/chat": "./src/themes/chat.css",
"./themes/zitare": "./src/themes/zitare.css",

View file

@ -1,9 +1,9 @@
/**
* ManaDeck Theme - Purple Dark Theme
* Import this file in your landing page to use the ManaDeck color scheme
* Cards Theme - Purple Dark Theme
* Import this file in your landing page to use the Cards color scheme
*/
:root {
/* Primary colors - ManaDeck Purple */
/* Primary colors - Cards Purple */
--color-primary: #7C3AED;
--color-primary-hover: #8B5CF6;
--color-primary-glow: rgba(124, 58, 237, 0.3);

View file

@ -42,7 +42,7 @@ const DEEP_LINK_PATTERNS: Record<string, Record<string, string>> = {
documents: '/documents/{id}',
spaces: '/spaces/{id}',
},
manadeck: {
cards: {
decks: '/decks/{id}',
cards: '/decks/{id}', // Navigate to deck containing the card
},

View file

@ -100,7 +100,7 @@ export const networkLogger = {
},
};
// Individual function exports for backwards compatibility with manadeck pattern
// Individual function exports for backwards compatibility with cards pattern
export const debug = logger.debug;
export const info = logger.info;
export const warn = logger.warn;

View file

@ -20,7 +20,7 @@ Each app gets its own isolated bucket, created automatically by `minio-init`:
| `manacore-storage` | ManaCore | Avatars, auth assets |
| `picture-storage` | Picture | AI-generated images |
| `chat-storage` | Chat | User file uploads |
| `manadeck-storage` | ManaDeck | Card/deck assets |
| `cards-storage` | Cards | Card/deck assets |
| `presi-storage` | Presi | Presentation slides |
| `calendar-storage` | Calendar | Calendar attachments |
| `contacts-storage` | Contacts | Contact avatars/files |

View file

@ -103,7 +103,7 @@ export function createStorage(bucketKey: keyof typeof BUCKETS, publicUrl?: strin
export const createManaCoreStorage = (publicUrl?: string) => createStorage('MANACORE', publicUrl);
export const createPictureStorage = (publicUrl?: string) => createStorage('PICTURE', publicUrl);
export const createChatStorage = () => createStorage('CHAT');
export const createManaDeckStorage = () => createStorage('MANADECK');
export const createCardsStorage = () => createStorage('CARDS');
export const createPresiStorage = () => createStorage('PRESI');
export const createCalendarStorage = () => createStorage('CALENDAR');
export const createContactsStorage = () => createStorage('CONTACTS');

View file

@ -9,7 +9,7 @@ export {
createManaCoreStorage,
createPictureStorage,
createChatStorage,
createManaDeckStorage,
createCardsStorage,
createPresiStorage,
createCalendarStorage,
createContactsStorage,

View file

@ -129,7 +129,7 @@ export const BUCKETS = {
MANACORE: 'manacore-storage',
PICTURE: 'picture-storage',
CHAT: 'chat-storage',
MANADECK: 'manadeck-storage',
CARDS: 'cards-storage',
PRESI: 'presi-storage',
CALENDAR: 'calendar-storage',
CONTACTS: 'contacts-storage',

View file

@ -14,7 +14,7 @@ export const colors = {
brand: {
memoro: '#f8d62b', // Gold
manacore: '#6366f1', // Indigo
manadeck: '#6366f1', // Indigo
cards: '#6366f1', // Indigo
storyteller: '#6366f1', // Indigo
},

View file

@ -200,7 +200,7 @@ export const theme = createThemeStore(APP_THEME_CONFIGS.memoro);
// Available configs:
// - APP_THEME_CONFIGS.memoro (Gold)
// - APP_THEME_CONFIGS.manacore (Indigo)
// - APP_THEME_CONFIGS.manadeck (Indigo)
// - APP_THEME_CONFIGS.cards (Indigo)
// - APP_THEME_CONFIGS.maerchenzauber (Purple)
```

View file

@ -118,8 +118,8 @@ export const APP_ROUTES: Record<string, AppRouteConfig> = {
],
},
manadeck: {
appId: 'manadeck',
cards: {
appId: 'cards',
defaultRoute: '/decks',
availableRoutes: [
{ path: '/decks', label: 'Decks', icon: 'layers', alwaysVisible: true },

View file

@ -203,8 +203,8 @@ export const APP_THEME_CONFIGS = {
dark: '239 84% 67%' as HSLValue,
},
},
manadeck: {
appId: 'manadeck',
cards: {
appId: 'cards',
defaultVariant: 'ocean' as ThemeVariant,
primaryColor: {
light: '239 84% 67%' as HSLValue, // Indigo #6366f1

View file

@ -245,9 +245,9 @@ export const ContactsEvents = {
};
/**
* ManaDeck App Events
* Cards App Events
*/
export const ManaDeckEvents = {
export const CardsEvents = {
deckCreated: () => trackEvent('deck_created'),
deckDeleted: () => trackEvent('deck_deleted'),
deckStudied: (cardsCount: number) => trackEvent('deck_studied', { cards: cardsCount }),