managarten/docs/central-services/COMMAND-BAR.md
Till-JS ee42b6cc76 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>
2025-12-10 02:37:46 +01:00

383 lines
12 KiB
Markdown

# 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