feat: major update with network graphs, themes, todo extensions, and more

## New Features

### Network Graph Visualization (Contacts, Calendar, Todo)
- D3.js force simulation for physics-based layout
- Zoom & pan with mouse/touchpad
- Keyboard shortcuts: +/- zoom, 0 reset, Esc deselect, / search, F focus
- Filtering by tags, company/location/project, connection strength
- Shared components in @manacore/shared-ui

### Central Tags API (mana-core-auth)
- CRUD endpoints for tags
- Schema: tags table with userId, name, color, app
- Shared tag components in @manacore/shared-ui

### Custom Themes System
- Theme editor with live preview and color picker
- Community theme gallery
- Theme sharing (public, unlisted, private)
- Backend API in mana-core-auth

### Todo App Extensions
- Glass-pill design for task input and items
- Settings page with 20+ preferences
- Task edit modal with inline editing
- Statistics page with visualizations
- PWA support with offline capabilities
- Multiple kanban boards

### Contacts App Features
- Duplicate detection
- Photo upload
- Batch operations
- Enhanced favorites page with multiple view modes
- Alphabet view improvements
- Search modal

### Help System
- @manacore/shared-help-content
- @manacore/shared-help-ui
- @manacore/shared-help-types

### Other Features
- Themes page for all apps
- Referral system frontend
- CommandBar (global search)
- Skeleton loaders
- Settings page improvements

## Bug Fixes
- Network graph simulation initialization
- Database schema TEXT for user_id columns (Better Auth compatibility)
- Various styling fixes

## Documentation
- Daily report for 2025-12-10
- CI/CD deployment guide

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Till-JS 2025-12-10 02:37:46 +01:00
parent e84371aa94
commit ee42b6cc76
381 changed files with 39284 additions and 6275 deletions

View file

@ -0,0 +1,383 @@
# Central Command Bar
Die zentrale Command Bar bietet eine einheitliche Schnellsuche und Navigation über alle Manacore-Apps hinweg. Sie wird mit `Cmd/Ctrl+K` aktiviert und bietet Suche, Quick Actions und Tastatur-Navigation.
## Architektur
```
┌─────────────────────────────────────────────────────────────┐
@manacore/shared-ui │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ CommandBar.svelte │ │
│ │ - Suche mit Debounce (150ms) │ │
│ │ - Quick Actions │ │
│ │ - Tastatur-Navigation │ │
│ │ - Ergebnis-Anzeige mit Avataren │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
┌────────────────────┼────────────────────┐
│ │ │
┌────▼────┐ ┌─────▼────┐ ┌─────▼────┐
│ Todo │ │ Calendar │ │ Contacts │
│ │ │ │ │ │
│ Sucht │ │ Sucht │ │ Sucht │
│ Tasks │ │ Events │ │ Kontakte │
└─────────┘ └──────────┘ └──────────┘
```
## Package
| Package | Beschreibung |
|---------|--------------|
| `@manacore/shared-ui` | CommandBar Svelte-Komponente + TypeScript-Typen |
## Keyboard Shortcut
| Shortcut | Aktion |
|----------|--------|
| `Cmd/Ctrl+K` | Command Bar öffnen |
| `↑↓` | Navigation durch Ergebnisse |
| `Enter` | Auswahl bestätigen |
| `Escape` | Schließen |
## TypeScript Interfaces
### CommandBarItem
Ein Suchergebnis:
```typescript
interface CommandBarItem {
id: string; // Eindeutige ID
title: string; // Haupttext (z.B. Name, Titel)
subtitle?: string; // Untertitel (z.B. Datum, E-Mail)
icon?: string; // Icon-Name
imageUrl?: string; // Avatar/Bild URL
isFavorite?: boolean; // Favorit-Markierung (zeigt Herz-Icon)
}
```
### QuickAction
Eine Schnellaktion (ohne Suche):
```typescript
interface QuickAction {
id: string; // Eindeutige ID
label: string; // Anzeigetext
icon: string; // Icon-Name
href?: string; // Link-Ziel (optional)
shortcut?: string; // Tastenkürzel-Anzeige
onclick?: () => void; // Click-Handler (alternativ zu href)
}
```
### Unterstützte Icons
Die CommandBar unterstützt folgende eingebaute Icons:
| Icon-Name | Beschreibung |
|-----------|--------------|
| `plus` | Plus-Zeichen (Neu erstellen) |
| `heart` | Herz (Favoriten) |
| `tag` | Tag/Label |
| `upload` | Upload (Import) |
| `calendar` | Kalender |
| `clock` | Uhr |
| `check` | Häkchen |
| `settings` | Zahnrad (Einstellungen) |
| `list` | Liste |
## Komponenten-Props
```typescript
interface Props {
open: boolean; // Sichtbarkeit
onClose: () => void; // Schließen-Handler
onSearch: (query: string) => Promise<CommandBarItem[]>; // Such-Funktion
onSelect: (item: CommandBarItem) => void; // Auswahl-Handler
quickActions?: QuickAction[]; // Schnellaktionen
placeholder?: string; // Suchfeld-Placeholder
emptyText?: string; // Text bei leeren Ergebnissen
searchingText?: string; // Text während Suche
}
```
## Nutzung
### Basis-Beispiel
```svelte
<script lang="ts">
import { goto } from '$app/navigation';
import { CommandBar } from '@manacore/shared-ui';
import type { CommandBarItem, QuickAction } from '@manacore/shared-ui';
let commandBarOpen = $state(false);
// Quick Actions definieren
const quickActions: QuickAction[] = [
{ id: 'new', label: 'Neu erstellen', icon: 'plus', href: '/new', shortcut: 'N' },
{ id: 'settings', label: 'Einstellungen', icon: 'settings', href: '/settings' },
];
// Such-Funktion implementieren
async function handleSearch(query: string): Promise<CommandBarItem[]> {
const results = await api.search(query);
return results.map((item) => ({
id: item.id,
title: item.name,
subtitle: item.description,
}));
}
// Auswahl verarbeiten
function handleSelect(item: CommandBarItem) {
goto(`/item/${item.id}`);
}
// Keyboard Shortcut registrieren
function handleKeydown(event: KeyboardEvent) {
if ((event.ctrlKey || event.metaKey) && event.key === 'k') {
event.preventDefault();
commandBarOpen = true;
}
}
</script>
<svelte:window onkeydown={handleKeydown} />
<CommandBar
bind:open={commandBarOpen}
onClose={() => (commandBarOpen = false)}
onSearch={handleSearch}
onSelect={handleSelect}
{quickActions}
placeholder="Suchen..."
emptyText="Keine Ergebnisse"
searchingText="Suche..."
/>
```
## App-spezifische Implementierungen
### Todo App
```typescript
// Quick Actions
const quickActions: QuickAction[] = [
{ id: 'new', label: 'Neue Aufgabe erstellen', icon: 'plus', href: '/task/new', shortcut: 'N' },
{ id: 'kanban', label: 'Kanban-Board', icon: 'list', href: '/kanban' },
{ id: 'stats', label: 'Statistiken', icon: 'chart', href: '/statistics' },
{ id: 'settings', label: 'Einstellungen', icon: 'settings', href: '/settings' },
];
// Suche: Tasks durchsuchen
async function handleSearch(query: string): Promise<CommandBarItem[]> {
const tasks = await getTasks({ search: query });
return tasks.slice(0, 10).map((task) => ({
id: task.id,
title: task.title,
subtitle: task.isCompleted
? '✓ Erledigt'
: task.dueDate
? new Date(task.dueDate).toLocaleDateString('de-DE')
: 'Kein Datum',
}));
}
// Auswahl: Zu Task navigieren
function handleSelect(item: CommandBarItem) {
goto(`/task/${item.id}`);
}
```
### Calendar App
```typescript
// Quick Actions
const quickActions: QuickAction[] = [
{ id: 'new', label: 'Neuen Termin erstellen', icon: 'plus', href: '/event/new', shortcut: 'N' },
{ id: 'today', label: 'Zu Heute springen', icon: 'calendar', onclick: () => viewStore.goToToday() },
{ id: 'agenda', label: 'Agenda anzeigen', icon: 'list', href: '/agenda' },
{ id: 'settings', label: 'Einstellungen', icon: 'settings', href: '/settings' },
];
// Suche: Events durchsuchen
async function handleSearch(query: string): Promise<CommandBarItem[]> {
const result = await searchEvents(query);
if (result.error || !result.data) return [];
return result.data.slice(0, 10).map((event) => ({
id: event.id,
title: event.title,
subtitle: format(new Date(event.startTime), 'dd. MMM yyyy, HH:mm', { locale: de }),
}));
}
// Auswahl: Zu Event navigieren
function handleSelect(item: CommandBarItem) {
goto(`/event/${item.id}`);
}
```
### Contacts App
```typescript
// Quick Actions
const quickActions: QuickAction[] = [
{ id: 'new', label: 'Neuen Kontakt erstellen', icon: 'plus', href: '/contacts/new', shortcut: 'N' },
{ id: 'favorites', label: 'Favoriten anzeigen', icon: 'heart', href: '/favorites' },
{ id: 'tags', label: 'Tags verwalten', icon: 'tag', href: '/tags' },
{ id: 'import', label: 'Kontakte importieren', icon: 'upload', href: '/data?tab=import' },
];
// Suche: Kontakte durchsuchen
async function handleSearch(query: string): Promise<CommandBarItem[]> {
const response = await contactsApi.list({ search: query, limit: 10 });
return (response.contacts || []).map((contact) => ({
id: contact.id,
title: contact.displayName ||
[contact.firstName, contact.lastName].filter(Boolean).join(' ') ||
contact.email || 'Unbekannt',
subtitle: contact.company || contact.email,
imageUrl: contact.photoUrl,
isFavorite: contact.isFavorite,
}));
}
// Auswahl: Kontakt-Modal öffnen
function handleSelect(item: CommandBarItem) {
goto(`/contacts/${item.id}`);
}
```
## Funktionen
### Suche
- **Debounce:** 150ms Verzögerung für Performance
- **Loading-State:** Spinner während der Suche
- **Empty State:** Konfigurierbare Meldung bei keinen Ergebnissen
- **Limit:** Ergebnisse werden typischerweise auf 10 begrenzt
### Quick Actions
Wenn kein Suchtext eingegeben ist, werden Quick Actions angezeigt:
- Navigation mit Pfeiltasten
- Ausführung mit Enter
- Keyboard-Shortcuts werden rechts angezeigt
### Ergebnis-Anzeige
- **Avatar:** Bild oder Initialen
- **Titel:** Haupttext
- **Untertitel:** Zusatzinfo (grau)
- **Favorit:** Herz-Icon wenn `isFavorite: true`
- **Hover:** Visuelles Feedback bei Maus-Over
### Keyboard Navigation
| Taste | Aktion |
|-------|--------|
| `↑` / `↓` | Durch Ergebnisse navigieren |
| `Enter` | Ausgewähltes Element öffnen |
| `Escape` | Command Bar schließen |
## Styling
Die Command Bar verwendet ein dunkles Theme mit CSS-Variablen:
```css
.command-modal {
background: #1a1a1a;
border: 1px solid #333;
border-radius: 12px;
max-width: 560px;
}
.command-result.selected {
background: #2a2a2a;
}
.result-avatar {
background: #3b82f6; /* Primary Color */
}
.result-favorite {
color: #ef4444; /* Rot für Herz */
}
```
### Animationen
- **Fade In:** Backdrop erscheint mit 0.15s
- **Slide In:** Modal gleitet von oben mit 0.2s
- **Loading Spinner:** Rotation Animation
## Integration in Layout
Typischerweise wird die CommandBar im App-Layout integriert:
```svelte
<!-- src/routes/(app)/+layout.svelte -->
<script lang="ts">
import { CommandBar } from '@manacore/shared-ui';
let commandBarOpen = $state(false);
function handleKeydown(event: KeyboardEvent) {
// Cmd/Ctrl+K funktioniert auch in Input-Feldern
if ((event.ctrlKey || event.metaKey) && event.key === 'k') {
event.preventDefault();
commandBarOpen = true;
}
}
</script>
<svelte:window onkeydown={handleKeydown} />
<!-- Haupt-Layout -->
<PillNavigation ... />
<main>
{@render children()}
</main>
<!-- Command Bar (global) -->
<CommandBar
bind:open={commandBarOpen}
onClose={() => (commandBarOpen = false)}
onSearch={handleSearch}
onSelect={handleSelect}
{quickActions}
/>
```
## Dateien
### @manacore/shared-ui
| Datei | Beschreibung |
|-------|--------------|
| `src/command-bar/CommandBar.svelte` | Hauptkomponente |
| `src/command-bar/index.ts` | Exports |
| `src/index.ts` | Package-Export |
## Vorteile
- **Einheitliche UX:** Gleiche Interaktion in allen Apps
- **Schnelle Navigation:** Sofortiger Zugriff auf häufige Aktionen
- **Tastatur-fokussiert:** Optimiert für Power-User
- **Flexibel:** App-spezifische Such-Logik und Quick Actions
- **Responsive:** Funktioniert auf Desktop und Mobile
## Best Practices
1. **Such-Performance:** Limit auf 10 Ergebnisse setzen
2. **Quick Actions:** Maximal 4-5 Aktionen für Übersichtlichkeit
3. **Sinnvolle Shortcuts:** Mnemonische Kürzel (N=Neu, S=Settings)
4. **Gute Subtitles:** Zusätzliche Info für eindeutige Identifikation
5. **Keyboard First:** Cmd+K sollte immer funktionieren, auch in Input-Feldern

View file

@ -0,0 +1,603 @@
# Central Help System
Das zentrale Help-System bietet eine einheitliche Hilfeseite für alle Manacore-Apps. Es unterstützt mehrsprachige Inhalte, Volltextsuche, und die Kombination von zentralen und app-spezifischen Inhalten.
## Architektur
```
┌─────────────────────────────────────────────────────────────┐
│ Zentrale Help-Inhalte │
│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │
│ │ FAQ (allgemein)│ │ Features │ │ Changelog │ │
│ │ - Account │ │ - Theming │ │ - v1.5.0 │ │
│ │ - Billing │ │ - Tags │ │ - v1.4.0 │ │
│ │ - Privacy │ │ - Sync │ │ ... │ │
│ └───────────────┘ └───────────────┘ └───────────────┘ │
└─────────────────────────────────────────────────────────────┘
mergeContent()
┌─────────────────────┼─────────────────────┐
│ │ │
┌────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐
│ Todo │ │ Calendar │ │ Contacts │
│ │ │ │ │ │
│ + App- │ │ + App- │ │ + App- │
│ spezif. │ │ spezif. │ │ spezif. │
│ FAQ │ │ Shortcuts │ │ Features │
└──────────┘ └───────────┘ └───────────┘
```
## Packages
| Package | Beschreibung |
|---------|--------------|
| `@manacore/shared-help-types` | TypeScript-Typen und Zod-Schemas |
| `@manacore/shared-help-content` | Content-Loader, Parser, Merger, Suche |
| `@manacore/shared-help-ui` | Svelte 5 UI-Komponenten |
| `@manacore/shared-help-mobile` | React Native Komponenten |
## Content-Typen
### FAQ (Frequently Asked Questions)
```typescript
interface FAQItem {
id: string;
language: 'de' | 'en' | 'fr' | 'it' | 'es';
question: string;
answer: string;
category: 'general' | 'account' | 'billing' | 'features' | 'technical' | 'privacy';
featured?: boolean;
tags?: string[];
relatedFaqs?: string[];
order?: number;
appSpecific?: boolean;
apps?: string[];
}
```
### Features
```typescript
interface FeatureItem {
id: string;
language: SupportedLanguage;
title: string;
description: string;
content: string;
icon?: string;
category: 'getting-started' | 'core' | 'advanced' | 'integration';
available?: boolean;
comingSoon?: boolean;
highlights?: string[];
learnMoreUrl?: string;
}
```
### Keyboard Shortcuts
```typescript
interface ShortcutsItem {
id: string;
language: SupportedLanguage;
category: 'navigation' | 'editing' | 'general' | 'app-specific';
title?: string;
shortcuts: Array<{
shortcut: string; // z.B. "Ctrl+S", "⌘+K"
action: string;
description?: string;
}>;
}
```
### Getting Started Guides
```typescript
interface GettingStartedItem {
id: string;
language: SupportedLanguage;
title: string;
description: string;
content: string;
difficulty: 'beginner' | 'intermediate' | 'advanced';
estimatedTime?: string;
prerequisites?: string[];
steps?: Array<{
title: string;
content: string;
duration?: string;
}>;
}
```
### Changelog
```typescript
interface ChangelogItem {
id: string;
language: SupportedLanguage;
version: string;
title: string;
releaseDate: Date;
type: 'major' | 'minor' | 'patch' | 'beta';
summary?: string;
content: string;
highlighted?: boolean;
changes?: {
features?: Array<{ title: string; description?: string }>;
improvements?: Array<{ title: string; description?: string }>;
bugfixes?: Array<{ title: string; description?: string }>;
};
platforms?: string[];
}
```
### Contact Info
```typescript
interface ContactInfo {
id: string;
language: SupportedLanguage;
title: string;
content: string;
supportEmail?: string;
supportUrl?: string;
discordUrl?: string;
twitterUrl?: string;
documentationUrl?: string;
responseTime?: string;
}
```
## Unterstützte Sprachen
```typescript
type SupportedLanguage = 'en' | 'de' | 'fr' | 'it' | 'es';
```
## Content-Struktur
### HelpContent Objekt
```typescript
interface HelpContent {
faq: FAQItem[];
features: FeatureItem[];
shortcuts: ShortcutsItem[];
gettingStarted: GettingStartedItem[];
changelog: ChangelogItem[];
contact: ContactInfo | null;
}
```
## Content Laden und Mergen
### Content Merger
Der Merger kombiniert zentrale und app-spezifische Inhalte:
```typescript
import { mergeContent, createEmptyContent } from '@manacore/shared-help-content';
// Zentrale Inhalte (für alle Apps)
const centralContent: HelpContent = {
faq: [
{ id: 'account-login', question: 'Wie melde ich mich an?', ... },
{ id: 'billing-cancel', question: 'Wie kündige ich?', ... },
],
features: [...],
// ...
};
// App-spezifische Inhalte
const appContent: Partial<HelpContent> = {
faq: [
{ id: 'todo-recurring', question: 'Wie erstelle ich wiederkehrende Tasks?', ... },
],
shortcuts: [
{ category: 'app-specific', shortcuts: [...] },
],
};
// Zusammenführen
const content = mergeContent(centralContent, appContent, {
appId: 'todo',
locale: 'de',
overrideById: true, // App-Content ersetzt zentralen mit gleicher ID
});
```
### Filterung
Inhalte werden automatisch gefiltert nach:
- **Sprache:** Nur Inhalte der aktuellen Locale
- **App:** `appSpecific: true` Inhalte nur wenn `apps` die aktuelle App enthält
- **Reihenfolge:** Sortiert nach `order` Property
## Suche
### Such-Index erstellen
```typescript
import { buildSearchIndex, search, createSearcher } from '@manacore/shared-help-content';
// Index erstellen
const index = buildSearchIndex(content, {
titleWeight: 2.0,
contentWeight: 1.0,
tagsWeight: 1.5,
threshold: 0.3,
minMatchCharLength: 2,
});
// Suchen
const results = search(index, 'wiederkehrend', {
limit: 10,
threshold: 0.4,
types: ['faq', 'guide'], // Optional: Nur bestimmte Typen
});
```
### SearchResult
```typescript
interface SearchResult {
id: string;
type: 'faq' | 'feature' | 'guide' | 'changelog';
title: string;
excerpt: string;
score: number;
highlight?: string;
item: FAQItem | FeatureItem | GettingStartedItem | ChangelogItem;
}
```
## UI-Komponenten
### HelpPage (Hauptkomponente)
Vollständige Hilfeseite mit allen Sektionen:
```svelte
<script>
import { HelpPage } from '@manacore/shared-help-ui';
import { helpContent, translations } from '$lib/help';
</script>
<HelpPage
content={helpContent}
appName="Todo"
appId="todo"
translations={translations}
searchEnabled={true}
showFAQ={true}
showFeatures={true}
showShortcuts={true}
showGettingStarted={true}
showChangelog={true}
showContact={true}
defaultSection="faq"
showBackButton={true}
onBack={() => goto('/')}
onSectionChange={(section) => console.log(section)}
/>
```
### Einzelne Sektionen
```svelte
<script>
import {
FAQSection,
FeaturesOverview,
KeyboardShortcuts,
GettingStartedGuide,
ChangelogSection,
ContactSection,
HelpSearch,
} from '@manacore/shared-help-ui';
</script>
<!-- FAQ mit Kategorien -->
<FAQSection
items={content.faq}
translations={translations}
showCategories={true}
maxItems={10}
expandFirst={true}
/>
<!-- Features-Übersicht -->
<FeaturesOverview
items={content.features}
translations={translations}
/>
<!-- Keyboard Shortcuts -->
<KeyboardShortcuts
items={content.shortcuts}
translations={translations}
/>
<!-- Getting Started Guides -->
<GettingStartedGuide
items={content.gettingStarted}
translations={translations}
/>
<!-- Changelog -->
<ChangelogSection
items={content.changelog}
translations={translations}
maxItems={5}
/>
<!-- Kontakt -->
<ContactSection
contact={content.contact}
translations={translations}
/>
<!-- Suche -->
<HelpSearch
content={content}
translations={translations}
placeholder="Hilfe durchsuchen..."
onResultSelect={(result) => navigateToResult(result)}
/>
```
## Übersetzungen
### HelpPageTranslations Interface
```typescript
interface HelpPageTranslations {
title: string;
subtitle?: string;
searchPlaceholder: string;
sections: {
faq: string;
features: string;
shortcuts: string;
gettingStarted: string;
changelog: string;
contact: string;
};
search: {
noResults: string;
resultsCount: string;
searching: string;
};
faq: {
noItems: string;
categories: {
general: string;
account: string;
billing: string;
features: string;
technical: string;
privacy: string;
};
};
features: {
noItems: string;
comingSoon: string;
learnMore: string;
};
shortcuts: {
noItems: string;
};
gettingStarted: {
noItems: string;
estimatedTime: string;
difficulty: {
beginner: string;
intermediate: string;
advanced: string;
};
};
changelog: {
noItems: string;
types: {
major: string;
minor: string;
patch: string;
beta: string;
};
};
contact: {
noInfo: string;
email: string;
responseTime: string;
};
common: {
back: string;
showMore: string;
showLess: string;
};
}
```
### Beispiel (Deutsch)
```typescript
const translations: HelpPageTranslations = {
title: 'Hilfe',
subtitle: 'Finde Antworten und lerne die App kennen',
searchPlaceholder: 'Suche in der Hilfe...',
sections: {
faq: 'Häufige Fragen',
features: 'Funktionen',
shortcuts: 'Tastaturkürzel',
gettingStarted: 'Erste Schritte',
changelog: 'Neuigkeiten',
contact: 'Kontakt',
},
search: {
noResults: 'Keine Ergebnisse gefunden',
resultsCount: '{count} Ergebnisse',
searching: 'Suche...',
},
faq: {
noItems: 'Keine FAQ-Einträge vorhanden',
categories: {
general: 'Allgemein',
account: 'Konto',
billing: 'Abrechnung',
features: 'Funktionen',
technical: 'Technisch',
privacy: 'Datenschutz',
},
},
// ... weitere
};
```
## Dateien
### @manacore/shared-help-types
| Datei | Beschreibung |
|-------|--------------|
| `src/content.ts` | Content-Typ Definitionen |
| `src/schemas.ts` | Zod-Validierungsschemas |
| `src/search.ts` | Such-bezogene Typen |
### @manacore/shared-help-content
| Datei | Beschreibung |
|-------|--------------|
| `src/parser.ts` | Markdown-Parser |
| `src/loader.ts` | Content-Loader für verschiedene Formate |
| `src/merger.ts` | Content-Merger (zentral + app-spezifisch) |
| `src/search.ts` | Volltextsuche mit Fuse.js |
### @manacore/shared-help-ui
| Datei | Beschreibung |
|-------|--------------|
| `src/pages/HelpPage.svelte` | Vollständige Hilfeseite |
| `src/components/FAQSection.svelte` | FAQ-Bereich |
| `src/components/FAQItem.svelte` | Einzelner FAQ-Eintrag |
| `src/components/FeaturesOverview.svelte` | Feature-Übersicht |
| `src/components/FeatureCard.svelte` | Feature-Karte |
| `src/components/KeyboardShortcuts.svelte` | Tastaturkürzel |
| `src/components/GettingStartedGuide.svelte` | Erste-Schritte-Guide |
| `src/components/ChangelogSection.svelte` | Changelog-Bereich |
| `src/components/ChangelogEntry.svelte` | Einzelner Changelog-Eintrag |
| `src/components/ContactSection.svelte` | Kontakt-Bereich |
| `src/components/HelpSearch.svelte` | Suchkomponente |
## Integration in eine App
### 1. Dependencies
```json
{
"dependencies": {
"@manacore/shared-help-types": "workspace:*",
"@manacore/shared-help-content": "workspace:*",
"@manacore/shared-help-ui": "workspace:*"
}
}
```
### 2. Content erstellen
```typescript
// src/lib/help/content.ts
import type { HelpContent } from '@manacore/shared-help-types';
export const appHelpContent: Partial<HelpContent> = {
faq: [
{
id: 'calendar-recurring',
language: 'de',
question: 'Wie erstelle ich wiederkehrende Termine?',
answer: 'Öffne einen Termin und aktiviere die Wiederholung...',
category: 'features',
appSpecific: true,
apps: ['calendar'],
},
],
shortcuts: [
{
id: 'calendar-shortcuts',
language: 'de',
category: 'app-specific',
title: 'Kalender-Shortcuts',
shortcuts: [
{ shortcut: 'N', action: 'Neuer Termin' },
{ shortcut: 'T', action: 'Zu Heute springen' },
{ shortcut: 'W', action: 'Wochenansicht' },
],
},
],
};
```
### 3. Hilfeseite erstellen
```svelte
<!-- src/routes/(app)/help/+page.svelte -->
<script lang="ts">
import { HelpPage } from '@manacore/shared-help-ui';
import { mergeContent } from '@manacore/shared-help-content';
import { centralContent } from '$lib/help/central';
import { appHelpContent } from '$lib/help/content';
import { translations } from '$lib/help/translations';
import { goto } from '$app/navigation';
const content = mergeContent(centralContent, appHelpContent, {
appId: 'calendar',
locale: 'de',
});
</script>
<HelpPage
{content}
appName="Calendar"
appId="calendar"
{translations}
searchEnabled={true}
onBack={() => goto('/')}
/>
```
## App-spezifische Inhalte
### Markierung
Inhalte können als app-spezifisch markiert werden:
```typescript
{
id: 'todo-labels',
appSpecific: true,
apps: ['todo', 'calendar'], // Nur in diesen Apps sichtbar
// ...
}
```
### Override by ID
Wenn `overrideById: true` (Standard), ersetzt app-spezifischer Content zentralen Content mit gleicher ID:
```typescript
// Zentral
{ id: 'feature-tags', title: 'Tags allgemein', ... }
// App-spezifisch
{ id: 'feature-tags', title: 'Tags in Todo', ... } // Ersetzt zentralen Content
```
## Vorteile
- **Wiederverwendbarkeit:** Zentrale FAQs (Account, Billing) nur einmal schreiben
- **Konsistenz:** Einheitliches Look & Feel der Hilfeseite
- **Mehrsprachigkeit:** 5 Sprachen unterstützt
- **Suche:** Integrierte Volltextsuche
- **Flexibilität:** App-spezifische Inhalte können hinzugefügt werden
- **Typ-Sicherheit:** Vollständige TypeScript-Typen und Zod-Validierung

View file

@ -0,0 +1,90 @@
# Central Services
Dieses Verzeichnis dokumentiert zentrale Services, die von allen Manacore-Apps gemeinsam genutzt werden. Diese Services laufen in `mana-core-auth` und bieten einheitliche APIs.
## Übersicht
| Service | Beschreibung | Dokumentation |
|---------|--------------|---------------|
| **Tags** | Einheitliche Tags/Labels für Todo, Calendar, Contacts | [TAGS.md](./TAGS.md) |
| **Theming** | Theme-Varianten, Dark Mode, Accessibility, Custom Themes | [THEMING.md](./THEMING.md) |
| **Help** | Zentrale Hilfeseite mit FAQ, Features, Shortcuts, Changelog | [HELP.md](./HELP.md) |
| **Command Bar** | Globale Schnellsuche und Navigation (Cmd/Ctrl+K) | [COMMAND-BAR.md](./COMMAND-BAR.md) |
## Architektur-Prinzipien
### Zentralisierung
Bestimmte Daten und Funktionen werden zentral in `mana-core-auth` verwaltet:
- **User-bezogen:** Jeder Service speichert Daten pro User (`userId`)
- **App-übergreifend:** Daten sind in allen Apps verfügbar
- **API-basiert:** Zugriff erfolgt über REST-APIs
### Soft References
Da die Apps ihre eigenen Datenbanken haben, können keine Foreign Keys zu mana-core-auth erstellt werden:
```
Todo-DB mana-core-auth-DB
┌─────────────────┐ ┌─────────────────┐
│ task_to_tags │ │ tags │
│ │ │ │
│ tag_id ─ ─ ─ ─ ─│─ ─ ─ ─ ▶│ id │
│ (keine FK) │ │ │
└─────────────────┘ └─────────────────┘
```
**Konsequenz:** Apps müssen ungültige IDs beim Laden ignorieren.
### Shared Packages
Für jeden zentralen Service gibt es ein entsprechendes Client-Package:
| Service | Package |
|---------|---------|
| Tags | `@manacore/shared-tags` |
Diese Packages enthalten:
- TypeScript Types
- API Client Klasse
- Helper-Funktionen
## Lokale Entwicklung
### Alle zentralen Services starten
```bash
# Infrastruktur
pnpm docker:up
# Auth-Service (enthält alle zentralen APIs)
pnpm dev:auth
```
### Datenbank-Schema pushen
```bash
cd services/mana-core-auth
pnpm db:push
```
## Shared Packages
| Package | Beschreibung |
|---------|--------------|
| `@manacore/shared-tags` | Tags Client für zentrale Tags API |
| `@manacore/shared-theme` | Theme Store, A11y Store, User Settings |
| `@manacore/shared-theme-ui` | Svelte UI-Komponenten für Theming |
| `@manacore/shared-help-types` | TypeScript-Typen für Help-Inhalte |
| `@manacore/shared-help-content` | Content-Loader, Parser, Merger, Suche |
| `@manacore/shared-help-ui` | Svelte UI-Komponenten für Hilfeseite |
| `@manacore/shared-help-mobile` | React Native Komponenten für Hilfe |
## Hinzufügen neuer zentraler Services
1. **Schema erstellen:** `services/mana-core-auth/src/db/schema/<name>.schema.ts`
2. **Module erstellen:** `services/mana-core-auth/src/<name>/`
3. **In app.module.ts registrieren**
4. **Shared Package erstellen:** `packages/shared-<name>/`
5. **Dokumentation schreiben:** `docs/central-services/<NAME>.md`

View file

@ -0,0 +1,248 @@
# Central Tags API
Das zentrale Tags-System ermöglicht einheitliche Tags/Labels über alle Manacore-Apps hinweg. Ein Tag, der in Todo erstellt wird, ist automatisch auch in Calendar und Contacts verfügbar.
## Architektur
```
┌─────────────────────────────────────────────────────────────┐
│ mana-core-auth │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ tags Tabelle (zentral) │ │
│ │ - id, userId, name, color, icon, createdAt │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ GET /api/v1/tags │ POST/PUT/DELETE │
└────────────────────────────┼────────────────────────────────┘
┌────────────────────┼────────────────────┐
│ │ │
┌────▼────┐ ┌─────▼────┐ ┌─────▼────┐
│ Todo │ │ Calendar │ │ Contacts │
│ │ │ │ │ │
│ task_ │ │ event_ │ │ contact_ │
│ to_tags │ │ to_tags │ │ to_tags │
│ (tagId) │ │ (tagId) │ │ (tagId) │
└─────────┘ └──────────┘ └──────────┘
```
## API Endpoints
Alle Endpoints sind unter `http://localhost:3001/api/v1/tags` verfügbar und erfordern einen Bearer Token.
| Methode | Endpoint | Beschreibung |
|---------|----------|--------------|
| `GET` | `/tags` | Alle Tags des Users abrufen |
| `GET` | `/tags/:id` | Einzelnes Tag abrufen |
| `GET` | `/tags/by-ids?ids=id1,id2` | Mehrere Tags per ID abrufen |
| `POST` | `/tags` | Neues Tag erstellen |
| `POST` | `/tags/defaults` | Default-Tags erstellen |
| `PUT` | `/tags/:id` | Tag aktualisieren |
| `DELETE` | `/tags/:id` | Tag löschen |
## Tag-Objekt
```typescript
interface Tag {
id: string; // UUID
userId: string; // User-ID (aus JWT)
name: string; // Tag-Name (max 100 Zeichen)
color: string; // Hex-Farbe (#3B82F6)
icon?: string | null; // Optionales Phosphor-Icon
createdAt: Date;
updatedAt: Date;
}
```
## Default-Tags
Beim Aufruf von `POST /tags/defaults` werden folgende Standard-Tags erstellt:
| Name | Farbe | Hex |
|------|-------|-----|
| Arbeit | Blau | `#3B82F6` |
| Persönlich | Grün | `#10B981` |
| Familie | Pink | `#EC4899` |
| Wichtig | Rot | `#EF4444` |
## Client-Nutzung
### Shared Package
Das `@manacore/shared-tags` Package stellt einen Client bereit:
```typescript
import { createTagsClient } from '@manacore/shared-tags';
const tagsClient = createTagsClient({
authUrl: 'http://localhost:3001',
getToken: async () => authStore.getAccessToken(),
});
// Alle Tags laden
const tags = await tagsClient.getAll();
// Tag erstellen
const newTag = await tagsClient.create({
name: 'Meeting',
color: '#8B5CF6',
});
// Tags per IDs laden
const selectedTags = await tagsClient.getByIds(['id1', 'id2']);
// Tag aktualisieren
await tagsClient.update('tag-id', { color: '#22C55E' });
// Tag löschen
await tagsClient.delete('tag-id');
// Default-Tags erstellen
await tagsClient.createDefaults();
```
### In App-Stores
Die Apps nutzen den Client in ihren Stores:
**Todo (labels.svelte.ts):**
```typescript
import { createTagsClient, type Tag } from '@manacore/shared-tags';
// Label = Tag (Alias für Abwärtskompatibilität)
export type Label = Tag;
// Client lazy initialisieren
const client = createTagsClient({ ... });
export const labelsStore = {
async fetchLabels() {
const labels = await client.getAll();
// ...
}
};
```
**Calendar (event-tags.ts):**
```typescript
export type EventTag = Tag;
```
**Contacts (contacts.ts):**
```typescript
export type ContactTag = Tag;
```
## Junction Tables
Jede App behält ihre eigene Junction-Table für die Zuordnung:
### Todo: `task_to_tags`
```sql
CREATE TABLE task_to_tags (
task_id UUID REFERENCES tasks(id) ON DELETE CASCADE,
tag_id UUID NOT NULL, -- Soft reference zu mana-core-auth.tags
PRIMARY KEY (task_id, tag_id)
);
```
### Calendar: `event_to_tags`
```sql
CREATE TABLE event_to_tags (
event_id UUID REFERENCES events(id) ON DELETE CASCADE,
tag_id UUID NOT NULL, -- Soft reference zu mana-core-auth.tags
PRIMARY KEY (event_id, tag_id)
);
```
### Contacts: `contact_to_tags`
```sql
CREATE TABLE contact_to_tags (
contact_id UUID REFERENCES contacts(id) ON DELETE CASCADE,
tag_id UUID NOT NULL, -- Soft reference zu mana-core-auth.tags
PRIMARY KEY (contact_id, tag_id)
);
```
**Hinweis:** Da die Tags in einer anderen Datenbank liegen, sind keine Foreign Key Constraints möglich. Die Apps validieren Tag-IDs beim Laden und ignorieren ungültige IDs.
## Entwicklung & Testing
### Alle drei Apps gleichzeitig starten
```bash
pnpm dev:tags-test
```
Dieser Befehl:
1. Richtet alle Datenbanken ein (todo, calendar, contacts, auth)
2. Startet alle Services mit farbcodierten Logs
### Manuelles API-Testing
```bash
# Token holen
TOKEN=$(curl -s -X POST http://localhost:3001/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email": "test@example.com", "password": "password"}' | jq -r '.accessToken')
# Tags abrufen
curl http://localhost:3001/api/v1/tags \
-H "Authorization: Bearer $TOKEN"
# Tag erstellen
curl -X POST http://localhost:3001/api/v1/tags \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Projekt X", "color": "#F59E0B"}'
# Default-Tags erstellen
curl -X POST http://localhost:3001/api/v1/tags/defaults \
-H "Authorization: Bearer $TOKEN"
```
## Dateien
### Backend (mana-core-auth)
| Datei | Beschreibung |
|-------|--------------|
| `src/db/schema/tags.schema.ts` | Drizzle-Schema für tags-Tabelle |
| `src/tags/tags.module.ts` | NestJS Module |
| `src/tags/tags.controller.ts` | REST Controller |
| `src/tags/tags.service.ts` | Business Logic |
| `src/tags/dto/create-tag.dto.ts` | DTO für Tag-Erstellung |
| `src/tags/dto/update-tag.dto.ts` | DTO für Tag-Update |
### Shared Package
| Datei | Beschreibung |
|-------|--------------|
| `packages/shared-tags/src/types.ts` | TypeScript Interfaces |
| `packages/shared-tags/src/client.ts` | TagsClient Klasse |
| `packages/shared-tags/src/index.ts` | Exports |
### Frontend-Integrationen
| App | API-Client | Store |
|-----|------------|-------|
| Todo | `src/lib/api/labels.ts` | `src/lib/stores/labels.svelte.ts` |
| Calendar | `src/lib/api/event-tags.ts` | `src/lib/stores/event-tags.svelte.ts` |
| Contacts | `src/lib/api/contacts.ts` (tagsApi) | - |
## Migration von lokalen Tags
Wenn eine App vorher eigene Tags hatte:
1. **Daten exportieren:** Bestehende Tags aus der lokalen Tabelle exportieren
2. **Tags erstellen:** Per API in mana-core-auth erstellen
3. **IDs mappen:** Alte Tag-IDs auf neue IDs mappen
4. **Junction Tables aktualisieren:** Tag-IDs in Junction-Tables ersetzen
5. **Lokale Tabelle löschen:** Alte Tags-Tabelle entfernen
## Vorteile
- **Konsistenz:** Ein Tag "Arbeit" ist überall gleich
- **Einheitliche Farben:** Tags sehen in allen Apps identisch aus
- **Weniger Duplikation:** Code und Daten werden geteilt
- **Cross-App Features:** Möglich (z.B. "Zeige alles mit Tag X")

View file

@ -0,0 +1,497 @@
# Central Theming System
Das zentrale Theming-System ermöglicht einheitliches Aussehen und Benutzereinstellungen über alle Manacore-Apps hinweg. Es besteht aus mehreren Schichten: Theme-Varianten, Light/Dark-Modus, Accessibility-Einstellungen und Custom Themes.
## Architektur
```
┌─────────────────────────────────────────────────────────────┐
│ mana-core-auth │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ user_settings (JSON-Feld) │ │
│ │ - theme: { mode, colorScheme, pinnedThemes } │ │
│ │ - nav: { desktopPosition, sidebarCollapsed } │ │
│ │ - locale: "de" │ │
│ │ - general: { startPages, sounds, etc. } │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ custom_themes Tabelle (Community Themes) │ │
│ │ - lightColors, darkColors, author, downloads, etc. │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
┌────────────────────┼────────────────────┐
│ │ │
┌────▼────┐ ┌─────▼────┐ ┌─────▼────┐
│ Todo │ │ Calendar │ │ Contacts │
│ │ │ │ │ │
│ shared- │ │ shared- │ │ shared- │
│ theme │ │ theme │ │ theme │
└─────────┘ └──────────┘ └──────────┘
```
## Packages
| Package | Beschreibung |
|---------|--------------|
| `@manacore/shared-theme` | Theme Store, Types, Utilities, Konstanten |
| `@manacore/shared-theme-ui` | Svelte UI-Komponenten (ThemeSelector, ThemePage, etc.) |
## Theme-Varianten
Es gibt 8 vordefinierte Theme-Varianten:
### Standard-Varianten (PillNav)
| Name | Farbe | Icon | Hue |
|------|-------|------|-----|
| `lume` | Gold ✨ | sparkle | 47 |
| `nature` | Grün 🌿 | leaf | 122 |
| `stone` | Blau-Grau 🪨 | hexagon | 200 |
| `ocean` | Blau 🌊 | waves | 199 |
### Erweiterte Varianten (Themes-Seite)
| Name | Farbe | Icon | Hue |
|------|-------|------|-----|
| `sunset` | Coral/Orange 🌅 | sun | 15 |
| `midnight` | Violett 🌙 | moon | 260 |
| `rose` | Pink 🌹 | flower | 340 |
| `lavender` | Lavendel 💜 | sparkle | 270 |
## Theme-Modus
```typescript
type ThemeMode = 'light' | 'dark' | 'system';
```
- **light**: Heller Modus
- **dark**: Dunkler Modus
- **system**: Folgt der System-Einstellung
## Color Tokens
Jede Theme-Variante definiert diese HSL-Farbwerte für Light und Dark:
```typescript
interface ThemeColors {
primary: string; // Hauptfarbe
primaryForeground: string; // Text auf Primary
secondary: string; // Sekundärfarbe
secondaryForeground: string;
background: string; // Seitenhintergrund
foreground: string; // Haupttext
surface: string; // Karten-Hintergrund
surfaceHover: string; // Hover-Zustand
surfaceElevated: string; // Modals, Dropdowns
muted: string; // Deaktivierte Elemente
mutedForeground: string;
border: string; // Rahmen
borderStrong: string; // Starke Rahmen
error: string; // Fehler-Rot
success: string; // Erfolg-Grün
warning: string; // Warnung-Orange
input: string; // Input-Hintergrund
ring: string; // Focus-Ring
}
```
### HSL-Format
Farben werden als HSL-Strings ohne `hsl()` Wrapper gespeichert:
```typescript
// Format: "H S% L%"
const gold = '47 95% 58%';
const darkBlue = '199 100% 18%';
// CSS-Verwendung
--color-primary: 47 95% 58%;
background-color: hsl(var(--color-primary));
```
## Store-Nutzung
### Theme Store
```typescript
import { createThemeStore } from '@manacore/shared-theme';
// Store erstellen
export const theme = createThemeStore({
appId: 'calendar',
defaultMode: 'system',
defaultVariant: 'ocean',
});
// In Komponente initialisieren
onMount(() => {
const cleanup = theme.initialize();
return cleanup;
});
// Zugriff auf State
theme.mode // 'light' | 'dark' | 'system'
theme.variant // 'ocean' | 'nature' | ...
theme.effectiveMode // 'light' | 'dark' (aufgelöst)
theme.isDark // boolean
// Aktionen
theme.setMode('dark');
theme.setVariant('nature');
theme.toggleMode(); // Light ↔ Dark
theme.cycleMode(); // Light → Dark → System → Light
```
### App-spezifische Primary Color
```typescript
export const theme = createThemeStore({
appId: 'memoro',
primaryColor: {
light: '47 95% 58%', // Gold
dark: '47 95% 58%',
},
});
```
### User Settings Store (Server-Sync)
```typescript
import { createUserSettingsStore } from '@manacore/shared-theme';
export const userSettings = createUserSettingsStore({
appId: 'calendar',
authUrl: 'http://localhost:3001',
getAccessToken: () => authStore.getAccessToken(),
});
// Laden
await userSettings.load();
// Zugriff
userSettings.theme.mode // Theme-Modus
userSettings.theme.colorScheme // Variante
userSettings.nav.desktopPosition // 'top' | 'bottom'
userSettings.locale // 'de'
userSettings.general.soundsEnabled
// Aktualisieren (speichert auf Server)
await userSettings.updateGlobal({
theme: { mode: 'dark' }
});
// App-spezifische Überschreibung
await userSettings.updateAppOverride({
theme: { colorScheme: 'nature' }
});
```
## Accessibility (A11y)
### A11y Store
```typescript
import { createA11yStore } from '@manacore/shared-theme';
export const a11y = createA11yStore({ appId: 'calendar' });
// State
a11y.contrast // 'normal' | 'high'
a11y.colorblind // 'none' | 'deuteranopia' | 'protanopia' | 'monochrome'
a11y.reduceMotion // boolean
// Aktionen
a11y.setContrast('high');
a11y.setColorblind('deuteranopia');
a11y.setReduceMotion(true);
a11y.resetAll();
```
### A11y-Optionen
**Kontrast:**
- `normal`: Standard (WCAG AA 4.5:1)
- `high`: Erhöhter Kontrast (WCAG AAA 7:1)
**Farbenblindheit:**
- `none`: Keine Anpassung
- `deuteranopia`: Grün-Blindheit (~6% der Männer)
- `protanopia`: Rot-Blindheit (~1% der Männer)
- `monochrome`: Graustufen
**Reduzierte Bewegung:**
- Respektiert `prefers-reduced-motion`
- Kann manuell überschrieben werden
## Custom Themes
### Custom Themes Store
```typescript
import { createCustomThemesStore } from '@manacore/shared-theme';
export const customThemes = createCustomThemesStore({
authUrl: 'http://localhost:3001',
getAccessToken: () => authStore.getAccessToken(),
});
// Eigene Themes laden
await customThemes.loadCustomThemes();
// Theme erstellen
const newTheme = await customThemes.createTheme({
name: 'Mein Theme',
emoji: '🎨',
lightColors: { primary: '200 80% 50%', ... },
darkColors: { primary: '200 70% 60%', ... },
});
// Community Themes durchsuchen
await customThemes.browseCommunity({
sort: 'popular',
search: 'dark',
});
// Theme herunterladen
await customThemes.downloadTheme(themeId);
// Theme anwenden
customThemes.applyCustomTheme(theme);
```
### Theme Editor
Der Theme Editor erlaubt das visuelle Erstellen von Themes:
**Hauptfarben (immer sichtbar):**
- Primary, Background, Surface, Foreground
- Error, Success, Warning
**Erweiterte Farben (zugeklappt):**
- PrimaryForeground, Secondary, SecondaryForeground
- SurfaceHover, SurfaceElevated, Muted, MutedForeground
- Border, BorderStrong, Input, Ring
## UI-Komponenten
### ThemePage
Vollständige Themes-Seite mit allen Optionen:
```svelte
<script>
import { ThemePage } from '@manacore/shared-theme-ui';
import { theme, a11y } from '$lib/stores';
</script>
<ThemePage
themeStore={theme}
a11yStore={a11y}
showAccessibility={true}
showPinnedThemes={true}
/>
```
### ThemeSelector
Dropdown zur Theme-Auswahl:
```svelte
<script>
import { ThemeSelector } from '@manacore/shared-theme-ui';
import { theme } from '$lib/stores';
</script>
<ThemeSelector themeStore={theme} />
```
### ThemeModeSelector
Umschalter für Light/Dark/System:
```svelte
<script>
import { ThemeModeSelector } from '@manacore/shared-theme-ui';
import { theme } from '$lib/stores';
</script>
<ThemeModeSelector themeStore={theme} />
```
### ThemeToggle
Einfacher Dark/Light Toggle:
```svelte
<script>
import { ThemeToggle } from '@manacore/shared-theme-ui';
import { theme } from '$lib/stores';
</script>
<ThemeToggle themeStore={theme} />
```
### A11ySettings
Vollständige Accessibility-Einstellungen:
```svelte
<script>
import { A11ySettings } from '@manacore/shared-theme-ui';
import { a11y } from '$lib/stores';
</script>
<A11ySettings store={a11y} />
```
## CSS-Integration
### Tailwind CSS
Die Themes werden als CSS-Variablen angewendet:
```css
:root {
--color-primary: 199 98% 45%;
--color-background: 199 100% 97%;
--color-foreground: 199 100% 18%;
/* ... weitere Tokens */
}
.dark {
--color-primary: 199 98% 48%;
--color-background: 200 25% 7%;
--color-foreground: 0 0% 100%;
}
```
In Tailwind:
```html
<div class="bg-background text-foreground">
<button class="bg-primary text-primary-foreground">
Klick mich
</button>
</div>
```
### tailwind.config.js
```javascript
theme: {
extend: {
colors: {
background: 'hsl(var(--color-background))',
foreground: 'hsl(var(--color-foreground))',
primary: {
DEFAULT: 'hsl(var(--color-primary))',
foreground: 'hsl(var(--color-primary-foreground))',
},
// ... weitere
}
}
}
```
## Dateien
### @manacore/shared-theme
| Datei | Beschreibung |
|-------|--------------|
| `src/types.ts` | Alle TypeScript Interfaces |
| `src/constants.ts` | Theme-Definitionen (8 Varianten) |
| `src/store.svelte.ts` | Theme Store Factory |
| `src/a11y-store.svelte.ts` | Accessibility Store |
| `src/a11y-constants.ts` | A11y Konstanten |
| `src/a11y-utils.ts` | A11y Helper |
| `src/user-settings-store.svelte.ts` | Server-Sync Store |
| `src/custom-themes-store.svelte.ts` | Custom Themes Store |
| `src/utils.ts` | Theme Utilities |
| `src/app-routes.ts` | Start-Page Konfiguration |
### @manacore/shared-theme-ui
| Datei | Beschreibung |
|-------|--------------|
| `src/ThemeSelector.svelte` | Theme-Dropdown |
| `src/ThemeModeSelector.svelte` | Light/Dark/System Selector |
| `src/ThemeToggle.svelte` | Einfacher Toggle |
| `src/components/ThemeCard.svelte` | Theme-Vorschau Karte |
| `src/components/ThemeGrid.svelte` | Grid von Theme-Karten |
| `src/components/A11ySettings.svelte` | A11y Einstellungen |
| `src/components/editor/` | Theme Editor Komponenten |
| `src/components/community/` | Community Themes Komponenten |
| `src/pages/ThemePage.svelte` | Vollständige Themes-Seite |
| `src/pages/ThemeEditorPage.svelte` | Theme Editor Seite |
| `src/pages/CommunityThemesPage.svelte` | Community Themes |
## Integration in eine App
### 1. Dependencies installieren
```json
{
"dependencies": {
"@manacore/shared-theme": "workspace:*",
"@manacore/shared-theme-ui": "workspace:*"
}
}
```
### 2. Store erstellen
```typescript
// src/lib/stores/theme.ts
import { createThemeStore, createA11yStore } from '@manacore/shared-theme';
export const theme = createThemeStore({
appId: 'myapp',
defaultVariant: 'ocean',
});
export const a11y = createA11yStore({
appId: 'myapp',
});
```
### 3. Im Root-Layout initialisieren
```svelte
<!-- src/routes/+layout.svelte -->
<script>
import { onMount } from 'svelte';
import { theme, a11y } from '$lib/stores/theme';
onMount(() => {
const cleanupTheme = theme.initialize();
const cleanupA11y = a11y.initialize();
return () => {
cleanupTheme();
cleanupA11y();
};
});
</script>
<slot />
```
### 4. Themes-Seite hinzufügen
```svelte
<!-- src/routes/(app)/themes/+page.svelte -->
<script>
import { ThemePage } from '@manacore/shared-theme-ui';
import { theme, a11y } from '$lib/stores/theme';
</script>
<ThemePage themeStore={theme} a11yStore={a11y} />
```
## Vorteile
- **Konsistenz:** Alle Apps sehen einheitlich aus
- **User Experience:** Theme-Einstellungen werden gespeichert
- **Accessibility:** Barrierefreiheit ist eingebaut
- **Anpassbarkeit:** Nutzer können eigene Themes erstellen
- **Community:** Themes können geteilt werden