mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-23 18:06:42 +02:00
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:
parent
29b77f22e4
commit
75a3ea2957
244 changed files with 907 additions and 924 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "@manacore/manadeck-database",
|
||||
"name": "@manacore/cards-database",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ export { default as ManaIcon } from './ManaIcon.svelte';
|
|||
export {
|
||||
MemoroLogo,
|
||||
ManaCoreLogo,
|
||||
ManaDeckLogo,
|
||||
CardsLogo,
|
||||
UloadLogo,
|
||||
ChatLogo,
|
||||
PresiLogo,
|
||||
|
|
|
|||
|
|
@ -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} />
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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' },
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
export type AppId =
|
||||
| 'memoro'
|
||||
| 'manacore'
|
||||
| 'manadeck'
|
||||
| 'cards'
|
||||
| 'uload'
|
||||
| 'chat'
|
||||
| 'presi'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
```
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 |
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ export {
|
|||
createManaCoreStorage,
|
||||
createPictureStorage,
|
||||
createChatStorage,
|
||||
createManaDeckStorage,
|
||||
createCardsStorage,
|
||||
createPresiStorage,
|
||||
createCalendarStorage,
|
||||
createContactsStorage,
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ export const colors = {
|
|||
brand: {
|
||||
memoro: '#f8d62b', // Gold
|
||||
manacore: '#6366f1', // Indigo
|
||||
manadeck: '#6366f1', // Indigo
|
||||
cards: '#6366f1', // Indigo
|
||||
storyteller: '#6366f1', // Indigo
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 }),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue