style: auto-format codebase with Prettier

Applied formatting to 1487+ files using pnpm format:write
  - TypeScript/JavaScript files
  - Svelte components
  - Astro pages
  - JSON configs
  - Markdown docs

  13 files still need manual review (Astro JSX comments)
This commit is contained in:
Wuesteon 2025-11-27 18:33:16 +01:00
parent 0241f5554c
commit d36b321d9d
3952 changed files with 661498 additions and 739751 deletions

View file

@ -7,6 +7,7 @@ Diese Dokumentation beschreibt alle Optimierungen, die implementiert wurden, um
**Datum:** Oktober 2025
**Status:** ✅ Implementiert & Erweitert
**Impact:**
- -60-80% schnellere Ladezeiten
- -95% weniger DB Queries
- -40-98% weniger Datenverbrauch
@ -45,6 +46,7 @@ Diese Dokumentation beschreibt alle Optimierungen, die implementiert wurden, um
### 1. expo-image Integration ⭐ Höchste Priorität
**Warum expo-image?**
- Built-in Memory + Disk Caching
- Native Performance
- Progressive Loading Support
@ -58,23 +60,25 @@ Diese Dokumentation beschreibt alle Optimierungen, die implementiert wurden, um
import { Image } from 'expo-image';
<Image
source={{ uri: thumbnailUrl }}
style={{ width: imageSize, height: imageSize }}
contentFit="cover"
transition={200}
cachePolicy="memory-disk"
placeholder={{ blurhash: 'L5H2EC=PM+yV0g-mq.wG9c010J}I' }}
placeholderContentFit="cover"
/>
source={{ uri: thumbnailUrl }}
style={{ width: imageSize, height: imageSize }}
contentFit="cover"
transition={200}
cachePolicy="memory-disk"
placeholder={{ blurhash: 'L5H2EC=PM+yV0g-mq.wG9c010J}I' }}
placeholderContentFit="cover"
/>;
```
**Vorteile:**
- ✅ Automatisches Memory + Disk Caching
- ✅ 200ms Fade-in Transition
- ✅ BlurHash Placeholder für sofortiges visuelles Feedback
- ✅ Bessere Performance als RN Image
**Dateien geändert:**
- `components/ImageCard.tsx` (2 Instanzen)
- `app/image/[id].tsx` (Detail Screen)
@ -83,70 +87,75 @@ import { Image } from 'expo-image';
### 2. Supabase Query Optimierung ⭐⭐ Sehr wichtig
**Problem:**
```tsx
// VORHER: 60+ Queries für 20 Bilder
const enhancedImages = await Promise.all(imageData.map(async (img) => {
const [_, likesData] = await Promise.all([
fetchImageTags(img.id), // Query 1
supabase.from('image_likes') // Query 2
.select('*', { count: 'exact' })
.eq('image_id', img.id)
]);
const enhancedImages = await Promise.all(
imageData.map(async (img) => {
const [_, likesData] = await Promise.all([
fetchImageTags(img.id), // Query 1
supabase
.from('image_likes') // Query 2
.select('*', { count: 'exact' })
.eq('image_id', img.id),
]);
const { data: userLike } = await supabase // Query 3
.from('image_likes')
.select('id')
.eq('image_id', img.id)
.eq('user_id', user.id)
.single();
// ... 3 Queries pro Bild!
}));
const { data: userLike } = await supabase // Query 3
.from('image_likes')
.select('id')
.eq('image_id', img.id)
.eq('user_id', user.id)
.single();
// ... 3 Queries pro Bild!
})
);
```
**Lösung: Batch Queries**
```tsx
// NACHHER: Nur 3 Queries total!
// 1. Batch fetch alle Tags parallel
await Promise.all(imageData.map(img => fetchImageTags(img.id)));
await Promise.all(imageData.map((img) => fetchImageTags(img.id)));
// 2. Alle Likes in EINER Query
const imageIds = imageData.map(img => img.id);
const imageIds = imageData.map((img) => img.id);
const [likesCountData, userLikesData] = await Promise.all([
supabase
.from('image_likes')
.select('image_id')
.in('image_id', imageIds), // Alle auf einmal!
supabase.from('image_likes').select('image_id').in('image_id', imageIds), // Alle auf einmal!
user ? supabase
.from('image_likes')
.select('image_id')
.in('image_id', imageIds)
.eq('user_id', user.id)
: Promise.resolve({ data: [] })
user
? supabase
.from('image_likes')
.select('image_id')
.in('image_id', imageIds)
.eq('user_id', user.id)
: Promise.resolve({ data: [] }),
]);
// 3. Lookup Maps für O(1) Access
const likesCountMap = new Map<string, number>();
likesCountData.data?.forEach(like => {
likesCountMap.set(like.image_id, (likesCountMap.get(like.image_id) || 0) + 1);
likesCountData.data?.forEach((like) => {
likesCountMap.set(like.image_id, (likesCountMap.get(like.image_id) || 0) + 1);
});
const userLikesSet = new Set(userLikesData.data?.map(like => like.image_id) || []);
const userLikesSet = new Set(userLikesData.data?.map((like) => like.image_id) || []);
// 4. Combine in O(n)
const enhancedImages = imageData.map(img => ({
...img,
likes_count: likesCountMap.get(img.id) || 0,
user_has_liked: userLikesSet.has(img.id)
const enhancedImages = imageData.map((img) => ({
...img,
likes_count: likesCountMap.get(img.id) || 0,
user_has_liked: userLikesSet.has(img.id),
}));
```
**Resultat:**
- **Vorher:** 60+ Queries
- **Nachher:** 3 Queries
- **Reduktion:** -95% 🔥
**Datei geändert:**
- `app/(tabs)/explore/index.tsx` (Lines 185-219)
---
@ -155,12 +164,12 @@ const enhancedImages = imageData.map(img => ({
**Strategie:**
| View Mode | Größe | Auflösung | Dateigröße | Ersparnis |
|-----------|-------|-----------|------------|-----------|
| `grid5` | tiny | 100x100px | ~10 KB | -98% |
| `grid3` | small | 200x200px | ~30 KB | -94% |
| `single` | medium | 400x400px | ~80 KB | -84% |
| Detail | full | Original | ~500 KB | 0% (volle Qualität) |
| View Mode | Größe | Auflösung | Dateigröße | Ersparnis |
| --------- | ------ | --------- | ---------- | ------------------- |
| `grid5` | tiny | 100x100px | ~10 KB | -98% |
| `grid3` | small | 200x200px | ~30 KB | -94% |
| `single` | medium | 400x400px | ~80 KB | -84% |
| Detail | full | Original | ~500 KB | 0% (volle Qualität) |
**Implementierung:**
@ -170,38 +179,39 @@ const enhancedImages = imageData.map(img => ({
export type ThumbnailSize = 'tiny' | 'small' | 'medium' | 'full';
export function getThumbnailUrl(
publicUrl: string | null,
size: ThumbnailSize = 'medium'
publicUrl: string | null,
size: ThumbnailSize = 'medium'
): string | null {
if (!publicUrl) return null;
if (!publicUrl) return null;
const dimensions: Record<ThumbnailSize, number> = {
tiny: 100, // grid5
small: 200, // grid3
medium: 400, // single
full: 0, // Original
};
const dimensions: Record<ThumbnailSize, number> = {
tiny: 100, // grid5
small: 200, // grid3
medium: 400, // single
full: 0, // Original
};
const targetSize = dimensions[size];
if (targetSize === 0) return publicUrl; // Full resolution
const targetSize = dimensions[size];
if (targetSize === 0) return publicUrl; // Full resolution
const url = new URL(publicUrl);
url.searchParams.set('width', targetSize.toString());
url.searchParams.set('height', targetSize.toString());
url.searchParams.set('resize', 'cover');
url.searchParams.set('quality', '80');
const url = new URL(publicUrl);
url.searchParams.set('width', targetSize.toString());
url.searchParams.set('height', targetSize.toString());
url.searchParams.set('resize', 'cover');
url.searchParams.set('quality', '80');
return url.toString();
return url.toString();
}
export function getSizeForViewMode(
viewMode: 'single' | 'grid3' | 'grid5'
): ThumbnailSize {
switch (viewMode) {
case 'grid5': return 'tiny';
case 'grid3': return 'small';
case 'single': return 'medium';
}
export function getSizeForViewMode(viewMode: 'single' | 'grid3' | 'grid5'): ThumbnailSize {
switch (viewMode) {
case 'grid5':
return 'tiny';
case 'grid3':
return 'small';
case 'single':
return 'medium';
}
}
```
@ -212,19 +222,21 @@ export function getSizeForViewMode(
const thumbnailUrl = getThumbnailUrl(publicUrl, getSizeForViewMode(viewMode));
<Image
source={{ uri: thumbnailUrl }}
// ... rest of props
/>
source={{ uri: thumbnailUrl }}
// ... rest of props
/>;
```
**Wie es funktioniert:**
Original URL:
```
https://xxx.supabase.co/storage/v1/object/public/generated-images/image.webp
```
Thumbnail URL (grid5):
```
https://xxx.supabase.co/storage/v1/object/public/generated-images/image.webp
?width=100
@ -236,6 +248,7 @@ https://xxx.supabase.co/storage/v1/object/public/generated-images/image.webp
Supabase generiert und cached diese Transformationen automatisch!
**Dateien:**
- `utils/image.ts` (neu erstellt)
- `components/ImageCard.tsx` (nutzt Thumbnails)
- `app/image/[id].tsx` (nutzt 'full' für Detail View)
@ -249,18 +262,17 @@ Supabase generiert und cached diese Transformationen automatisch!
```tsx
// app/(tabs)/explore/index.tsx & app/(tabs)/index/index.tsx
<FlatList
data={filteredImages}
renderItem={renderImage}
keyExtractor={(item) => item.id}
data={filteredImages}
renderItem={renderImage}
keyExtractor={(item) => item.id}
// Performance Props:
removeClippedSubviews={Platform.OS === 'android'} // Entfernt Views außerhalb Viewport
maxToRenderPerBatch={10} // Weniger Items pro Render-Batch
windowSize={5} // Kleineres Render-Fenster
initialNumToRender={6} // Schnellerer Initial Load
updateCellsBatchingPeriod={50} // Häufigere Updates
// Performance Props:
removeClippedSubviews={Platform.OS === 'android'} // Entfernt Views außerhalb Viewport
maxToRenderPerBatch={10} // Weniger Items pro Render-Batch
windowSize={5} // Kleineres Render-Fenster
initialNumToRender={6} // Schnellerer Initial Load
updateCellsBatchingPeriod={50} // Häufigere Updates
// ... rest of props
// ... rest of props
/>
```
@ -273,6 +285,7 @@ Supabase generiert und cached diese Transformationen automatisch!
- **updateCellsBatchingPeriod**: Wie oft die Render-Queue geleert wird (ms)
**Dateien geändert:**
- `app/(tabs)/explore/index.tsx`
- `app/(tabs)/index/index.tsx`
@ -282,29 +295,32 @@ Supabase generiert und cached diese Transformationen automatisch!
### Erwarteter Gewinn
| Metrik | Vorher | Nachher | Verbesserung |
|--------|--------|---------|--------------|
| **Initiales Laden** | ~3-4s | ~1-1.5s | **-60-70%** |
| **DB Queries (Explore)** | 60+ | 3 | **-95%** |
| **Scrolling FPS** | ~40 FPS | ~55-60 FPS | **+40-50%** |
| **Cache Hits (2nd Load)** | 0% | 80%+ | **+80%** |
| **Datenverbrauch (Grid5)** | ~10 MB | ~200 KB | **-98%** |
| **Datenverbrauch (Grid3)** | ~10 MB | ~600 KB | **-94%** |
| **Datenverbrauch (Single)** | ~10 MB | ~1.6 MB | **-84%** |
| Metrik | Vorher | Nachher | Verbesserung |
| --------------------------- | ------- | ---------- | ------------ |
| **Initiales Laden** | ~3-4s | ~1-1.5s | **-60-70%** |
| **DB Queries (Explore)** | 60+ | 3 | **-95%** |
| **Scrolling FPS** | ~40 FPS | ~55-60 FPS | **+40-50%** |
| **Cache Hits (2nd Load)** | 0% | 80%+ | **+80%** |
| **Datenverbrauch (Grid5)** | ~10 MB | ~200 KB | **-98%** |
| **Datenverbrauch (Grid3)** | ~10 MB | ~600 KB | **-94%** |
| **Datenverbrauch (Single)** | ~10 MB | ~1.6 MB | **-84%** |
### Real-World Szenario: 20 Bilder laden
**Grid5 View:**
- Vorher: 20 × 500 KB = 10 MB
- Nachher: 20 × 10 KB = 200 KB
- **Ersparnis: 9.8 MB (-98%)**
**Grid3 View:**
- Vorher: 20 × 500 KB = 10 MB
- Nachher: 20 × 30 KB = 600 KB
- **Ersparnis: 9.4 MB (-94%)**
**Single View:**
- Vorher: 20 × 500 KB = 10 MB
- Nachher: 20 × 80 KB = 1.6 MB
- **Ersparnis: 8.4 MB (-84%)**
@ -314,9 +330,11 @@ Supabase generiert und cached diese Transformationen automatisch!
## Code-Änderungen Übersicht
### Neue Dateien
- ✨ `utils/image.ts` - Thumbnail URL Generation
### Geänderte Dateien
1. `package.json` - expo-image Package hinzugefügt
2. `components/ImageCard.tsx` - expo-image + Thumbnail Support
3. `app/(tabs)/explore/index.tsx` - Batch Queries + FlatList Props
@ -324,9 +342,10 @@ Supabase generiert und cached diese Transformationen automatisch!
5. `app/image/[id].tsx` - expo-image + Full Resolution
### Dependencies
```json
{
"expo-image": "~3.0.9"
"expo-image": "~3.0.9"
}
```
@ -335,6 +354,7 @@ Supabase generiert und cached diese Transformationen automatisch!
## Testing Checklist
### Funktionalität
- [ ] Bilder laden korrekt in allen View-Modes (single, grid3, grid5)
- [ ] Thumbnails werden korrekt generiert
- [ ] Detail-Screen zeigt volle Auflösung
@ -342,12 +362,14 @@ Supabase generiert und cached diese Transformationen automatisch!
- [ ] BlurHash Placeholder wird angezeigt
### Performance
- [ ] Initiales Laden ist spürbar schneller
- [ ] Scrolling ist flüssiger (60 FPS)
- [ ] Weniger Datenverbrauch (check Developer Tools)
- [ ] Keine Memory Leaks
### Edge Cases
- [ ] Bilder ohne public_url zeigen Placeholder
- [ ] Offline-Modus zeigt gecachte Bilder
- [ ] Wechsel zwischen View-Modes funktioniert
@ -362,6 +384,7 @@ Supabase generiert und cached diese Transformationen automatisch!
**Problem:** Alle Bilder hatten denselben generic BlurHash
**Lösung:**
- Neue DB Column `blurhash` in `images` Tabelle
- BlurHash wird an ImageCard übergeben
- Individueller Placeholder pro Bild
@ -376,14 +399,15 @@ ALTER TABLE images ADD COLUMN IF NOT EXISTS blurhash TEXT;
```tsx
// ImageCard.tsx
<Image
source={{ uri: thumbnailUrl }}
placeholder={{
blurhash: blurhash || 'L5H2EC=PM+yV0g-mq.wG9c010J}I' // Fallback
}}
source={{ uri: thumbnailUrl }}
placeholder={{
blurhash: blurhash || 'L5H2EC=PM+yV0g-mq.wG9c010J}I', // Fallback
}}
/>
```
**Dateien:**
- Migration: `supabase/migrations/add_blurhash_to_images.sql`
- Utility: `utils/blurhash.ts`
- Updated: `components/ImageCard.tsx`, beide Screens
@ -404,16 +428,17 @@ const thumbnailUrl = getThumbnailUrl(publicUrl, getSizeForViewMode(viewMode));
const tinyThumbnailUrl = getThumbnailUrl(publicUrl, 'tiny'); // 100x100px
<Image
source={{ uri: thumbnailUrl }}
placeholder={
tinyThumbnailUrl
? { uri: tinyThumbnailUrl } // Progressive!
: { blurhash: blurhash || DEFAULT_BLURHASH }
}
/>
source={{ uri: thumbnailUrl }}
placeholder={
tinyThumbnailUrl
? { uri: tinyThumbnailUrl } // Progressive!
: { blurhash: blurhash || DEFAULT_BLURHASH }
}
/>;
```
**Ablauf:**
1. BlurHash erscheint sofort (0ms)
2. Tiny Thumbnail lädt (~50-100ms, ~2 KB)
3. Richtiges Thumbnail lädt (~200-500ms, ~10-80 KB)
@ -434,30 +459,31 @@ const tinyThumbnailUrl = getThumbnailUrl(publicUrl, 'tiny'); // 100x100px
```tsx
// app/(tabs)/index/index.tsx & explore/index.tsx
useEffect(() => {
if (!pagination.hasMore || pagination.loading) return;
if (!pagination.hasMore || pagination.loading) return;
const prefetchNextPage = async () => {
// Fetch IDs der nächsten Page
const { data } = await supabase
.from('images')
.select('id, public_url')
.range(nextPageStart, nextPageEnd);
const prefetchNextPage = async () => {
// Fetch IDs der nächsten Page
const { data } = await supabase
.from('images')
.select('id, public_url')
.range(nextPageStart, nextPageEnd);
// Prefetch Thumbnails
data?.forEach(img => {
const thumbnailUrl = getThumbnailUrl(img.public_url, thumbnailSize);
if (thumbnailUrl) {
Image.prefetch(thumbnailUrl);
}
});
};
// Prefetch Thumbnails
data?.forEach((img) => {
const thumbnailUrl = getThumbnailUrl(img.public_url, thumbnailSize);
if (thumbnailUrl) {
Image.prefetch(thumbnailUrl);
}
});
};
const timeoutId = setTimeout(prefetchNextPage, 500); // Debounced
return () => clearTimeout(timeoutId);
const timeoutId = setTimeout(prefetchNextPage, 500); // Debounced
return () => clearTimeout(timeoutId);
}, [pagination.page, viewMode]);
```
**Features:**
- Prefetcht erste 6 Bilder der nächsten Page
- 500ms Debounce um excessive Requests zu vermeiden
- Silent fail (nicht-kritisch)
@ -478,32 +504,32 @@ useEffect(() => {
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
import * as Haptics from 'expo-haptics';
const pinchGesture = Gesture.Pinch()
.onEnd((event) => {
// Debounce: min 300ms zwischen Gesten
if (now - lastGestureTime.current < 300) return;
const pinchGesture = Gesture.Pinch().onEnd((event) => {
// Debounce: min 300ms zwischen Gesten
if (now - lastGestureTime.current < 300) return;
// Pinch-Out (scale > 1.15): Zoom in = größere Bilder
if (event.scale > 1.15) {
if (galleryViewMode === 'grid5') setGalleryViewMode('grid3');
else if (galleryViewMode === 'grid3') setGalleryViewMode('single');
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
}
// Pinch-In (scale < 0.85): Zoom out = kleinere Bilder
else if (event.scale < 0.85) {
if (galleryViewMode === 'single') setGalleryViewMode('grid3');
else if (galleryViewMode === 'grid3') setGalleryViewMode('grid5');
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
}
});
// Pinch-Out (scale > 1.15): Zoom in = größere Bilder
if (event.scale > 1.15) {
if (galleryViewMode === 'grid5') setGalleryViewMode('grid3');
else if (galleryViewMode === 'grid3') setGalleryViewMode('single');
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
}
// Pinch-In (scale < 0.85): Zoom out = kleinere Bilder
else if (event.scale < 0.85) {
if (galleryViewMode === 'single') setGalleryViewMode('grid3');
else if (galleryViewMode === 'grid3') setGalleryViewMode('grid5');
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
}
});
// Wrap FlatList
<GestureDetector gesture={pinchGesture}>
<FlatList {...props} />
</GestureDetector>
<FlatList {...props} />
</GestureDetector>;
```
**Features:**
- Pinch-Out: grid5 → grid3 → single (größere Bilder)
- Pinch-In: single → grid3 → grid5 (kleinere Bilder)
- Haptisches Feedback bei jedem Wechsel
@ -511,6 +537,7 @@ const pinchGesture = Gesture.Pinch()
- Threshold: >1.15 für Zoom-In, <0.85 für Zoom-Out
**Dateien:**
- `app/(tabs)/index/index.tsx`
**Impact:** Natürliche iOS Photos-ähnliche UX, schneller View-Wechsel ohne Button-Klick
@ -520,19 +547,23 @@ const pinchGesture = Gesture.Pinch()
## Nächste mögliche Optimierungen
### 1. BlurHash Generation beim Upload (Server-Side)
- BlurHash automatisch bei Edge Function generieren
- Direkt in DB speichern
- Aktuell: Manuell/Client-Side
### 3. Progressive JPEG/WebP
- Bilder in progressivem Format hochladen
- Besseres Ladeverhalten
### 4. Image CDN
- CloudFlare Images oder imgix für zusätzliche Optimierung
- Automatische Format-Konvertierung (WebP, AVIF)
### 5. Lazy Loading für Tags/Likes
- Tags/Likes nur on-demand laden
- Reduziert initiale Query-Komplexität weiter
@ -545,6 +576,7 @@ const pinchGesture = Gesture.Pinch()
Supabase nutzt imgproxy unter der Haube:
**Unterstützte Parameter:**
- `width` - Zielbreite
- `height` - Zielhöhe
- `resize` - Resize-Mode (cover, contain, fill)
@ -552,27 +584,32 @@ Supabase nutzt imgproxy unter der Haube:
- `format` - Output-Format (webp, jpg, png)
**Caching:**
- Erste Transformation: ~500ms
- Weitere Requests: ~50ms (cached)
- Cache-Duration: 1 Jahr
**Limits:**
- Max Size: 2500x2500px
- Max File Size: 5MB
### expo-image Caching
**Memory Cache:**
- LRU (Least Recently Used) Policy
- Größe: ~50-100 Bilder
- Lebensdauer: Bis App-Neustart
**Disk Cache:**
- Location: `FileSystem.cacheDirectory`
- Größe: Unbegrenzt (aber OS kann löschen)
- Lebensdauer: Persistent
**Cache Management:**
```tsx
// Cache manuell leeren
import { Image } from 'expo-image';
@ -586,23 +623,27 @@ await Image.clearDiskCache();
## Troubleshooting
### Bilder laden nicht
1. Check Supabase Storage Permissions
2. Verify public_url ist korrekt
3. Check Network Tab für Fehler
4. Cache leeren und neu versuchen
### Thumbnails falsche Größe
1. Verify URL Parameter sind korrekt
2. Check Supabase Storage Transformations Settings
3. Test mit direkter URL im Browser
### Performance nicht besser
1. Enable React Native Performance Monitor
2. Check FlatList Props sind gesetzt
3. Verify expo-image ist installiert
4. Profile mit React DevTools
### Cache funktioniert nicht
1. Check `cachePolicy="memory-disk"`
2. Verify URLs sind stabil (keine Query-Params ändern)
3. Clear Cache und neu testen

View file

@ -7,30 +7,36 @@ Plan für ein geteiltes UI-Komponenten-System über 10+ Apps hinweg. Ziel ist es
## Strategie: CLI-Tool (shadcn-style) mit optionalem Tailwind-Preset
### Phase 1: CLI-Tool für Component Copy-Paste (Start hier)
**Zeitaufwand:** 1-2 Tage
Wir starten mit einem simplen Ansatz:
- Zentrales Git-Repo mit UI-Components
- CLI-Tool das Components in Apps kopiert
- Components gehören dann der App (volle Kontrolle)
- Keine NPM-Dependencies für Components
**Vorteile dieser Reihenfolge:**
- Schneller Start, kein Over-Engineering
- Wir lernen welche Design-Patterns sich wirklich wiederholen
- Kein zweites System am Anfang
- CLI-Tool validiert ob der Ansatz überhaupt funktioniert
### Phase 2: Tailwind-Preset (optional, später)
**Zeitaufwand:** 2-3 Stunden
**Wann:** Nach 1-3 Monaten, wenn wir sehen was sich wiederholt
Falls wir merken dass bestimmte Design-Tokens (Farben, Spacing, etc.) überall gleich sind:
- Extrahieren in ein kleines Tailwind-Config NPM package
- Components im Library-Repo updaten zu nutzen das Preset
- Bestehende Apps können updaten (optional)
**Migration ist einfach:**
- Preset Package erstellen
- Components refactoren: `bg-[#3B82F6]``bg-brand-primary`
- Apps installieren Preset und re-adden Components
@ -44,6 +50,7 @@ Falls wir merken dass bestimmte Design-Tokens (Farben, Spacing, etc.) überall g
**Repository:** `github.com/memoro/ui` (Monorepo)
**Repository Struktur:**
```
memoro-ui/
├── packages/
@ -90,42 +97,43 @@ memoro-ui/
```
**registry.json Beispiel:**
```json
{
"components": {
"ui": {
"button": {
"name": "Button",
"files": ["Button.tsx"],
"category": "ui",
"dependencies": [],
"description": "A pressable button component with variants"
},
"input": {
"name": "Input",
"files": ["Input.tsx"],
"category": "ui",
"dependencies": [],
"description": "Text input with label and error states"
}
},
"navigation": {
"header": {
"name": "Header",
"files": ["Header.tsx"],
"category": "navigation",
"dependencies": [],
"description": "App header with title and optional actions"
},
"tab-bar": {
"name": "TabBar",
"files": ["TabBar.tsx"],
"category": "navigation",
"dependencies": [],
"description": "Bottom tab bar navigation component"
}
}
}
"components": {
"ui": {
"button": {
"name": "Button",
"files": ["Button.tsx"],
"category": "ui",
"dependencies": [],
"description": "A pressable button component with variants"
},
"input": {
"name": "Input",
"files": ["Input.tsx"],
"category": "ui",
"dependencies": [],
"description": "Text input with label and error states"
}
},
"navigation": {
"header": {
"name": "Header",
"files": ["Header.tsx"],
"category": "navigation",
"dependencies": [],
"description": "App header with title and optional actions"
},
"tab-bar": {
"name": "TabBar",
"files": ["TabBar.tsx"],
"category": "navigation",
"dependencies": [],
"description": "Bottom tab bar navigation component"
}
}
}
}
```
@ -134,26 +142,31 @@ memoro-ui/
**Commands:**
**`npx @memoro/ui add <component>`**
- Kopiert Component-Code in `app/components/ui/`
- Prüft ob Component bereits existiert
- Fragt bei Konflikten nach (überschreiben/skip)
- Zeigt Success-Message mit Import-Beispiel
**`npx @memoro/ui list`**
- Zeigt alle verfügbaren Components
- Zeigt welche bereits in der App sind
- Zeigt Beschreibung jedes Components
**`npx @memoro/ui update <component>`**
- Updated einen existierenden Component
- Zeigt Diff der Änderungen
- Fragt nach Bestätigung
**`npx @memoro/ui diff <component>`**
- Zeigt Unterschiede zwischen lokaler und Library-Version
- Hilfreich um zu sehen ob lokale Anpassungen gemacht wurden
**Optional später:**
- `init` - Erstellt `components/ui/` Ordner
- `remove` - Entfernt Component aus App
- `sync` - Updated alle Components auf einmal
@ -161,6 +174,7 @@ memoro-ui/
### 3. Component Development Workflow
**Neuer Component:**
1. Entwickle Component in `ui-components/components/`
2. Teste in Preview-App
3. Schreibe README mit Usage-Beispielen
@ -168,12 +182,14 @@ memoro-ui/
5. Commit & Push
**Component nutzen:**
1. In App: `npx @memoro/ui add button`
2. Component liegt jetzt in `app/components/ui/Button.tsx`
3. Importieren: `import { Button } from '@/components/ui/Button'`
4. Bei Bedarf app-spezifisch anpassen
**Component updaten:**
1. Änderungen in Library-Repo
2. Apps können entscheiden ob sie updaten wollen
3. `npx @memoro/ui update button` in jeweiliger App
@ -184,27 +200,32 @@ memoro-ui/
**Jeder Component sollte haben:**
**Consistent API:**
- Props sind konsistent benannt über alle Components
- `variant`, `size`, `disabled` patterns
- `className` für custom Tailwind classes
- `children` wo sinnvoll
**TypeScript:**
- Vollständige Type definitions
- Exported Types für Props
- Generic Support wo nötig
**Accessibility:**
- ARIA labels wo nötig
- Keyboard navigation
- Screen reader support
**Styling:**
- NativeWind/Tailwind classes
- Responsive by default
- Dark mode ready (später)
**Documentation:**
- README.md mit:
- Beschreibung
- Props table
@ -214,12 +235,14 @@ memoro-ui/
### 5. Preview/Development App
**Expo App in ui-components/preview:**
- Live preview aller Components
- Test auf echten Devices
- QR-Code für schnelles Testen
- Optional: Storybook integration
**Zweck:**
- Entwickle Components in Isolation
- Visual testing
- Dokumentation als Live-Demo
@ -232,12 +255,14 @@ memoro-ui/
**UI Components (`packages/components/ui/`):**
**Layout:**
- Container
- Stack (VStack/HStack)
- Spacer
- Divider
**Input:**
- Button
- Input (TextInput)
- Checkbox
@ -245,6 +270,7 @@ memoro-ui/
- Slider
**Display:**
- Text (mit Typography variants)
- Card
- Badge
@ -252,17 +278,20 @@ memoro-ui/
- Image (mit Loading states)
**Feedback:**
- Alert
- Toast
- Spinner/Loading
- Progress
**Overlay:**
- Modal
- Sheet (Bottom sheet)
- Dropdown
**Navigation Components (`packages/components/navigation/`):**
- Header (mit Title, Back Button, Actions)
- TabBar (Bottom Tab Navigation)
- BackButton
@ -272,25 +301,30 @@ memoro-ui/
### 7. Naming Conventions
**Component Files:**
- PascalCase: `Button.tsx`, `TextInput.tsx`
- Co-located files: `Button.stories.tsx`, `Button.test.tsx`
**Registry IDs:**
- kebab-case: `button`, `text-input`
- Matches CLI usage: `npx ui add text-input`
**Variants:**
- lowercase: `primary`, `secondary`, `outline`
- Sizes: `sm`, `md`, `lg`, `xl`
### 8. Version Strategy (später relevant)
**Phase 1 (jetzt):**
- Keine Versionierung nötig
- Components werden kopiert = keine Breaking changes
- Apps besitzen den Code
**Phase 2 (wenn nötig):**
- Semantic versioning für CLI-Tool
- Component changelog in README
- Breaking changes werden dokumentiert
@ -299,11 +333,13 @@ memoro-ui/
### 9. Testing Strategy
**CLI-Tool:**
- Unit tests für file operations
- Integration tests für add/update commands
- Test mit dummy Expo app
**Components:**
- Visual testing in Preview app
- Optional: Jest + React Native Testing Library
- Manual testing auf iOS/Android
@ -333,6 +369,7 @@ memoro-ui/
- Wenn wiederverwendbar: zu Library hinzufügen
**Für neue Apps:**
- Start projekt
- Setup `.npmrc` mit `@memoro:registry=https://npm.pkg.github.com`
- `npx @memoro/ui init` (erstellt structure)
@ -342,18 +379,21 @@ memoro-ui/
### 11. Documentation
**README in Library Repo:**
- Was ist das System
- Wie installiert man CLI
- Quick start guide
- Component overview mit Links
**Per-Component README:**
- Props documentation
- Usage examples
- Variants showcase
- Do's and Don'ts
**Changelog:**
- Tracked in Library repo
- Breaking changes highlighted
- Migration guides wenn nötig
@ -361,6 +401,7 @@ memoro-ui/
### 12. Future Enhancements (Phase 2+)
**Wenn Tailwind-Preset hinzukommt:**
- Mini NPM package: `@memoro/tailwind-preset`
- Ebenfalls via GitHub Packages publiziert
- Components nutzen Design tokens
@ -368,6 +409,7 @@ memoro-ui/
- Migration guide für bestehende Components
**Weitere Features:**
- Theming system (Light/Dark mode)
- Animation presets
- Icon set integration
@ -375,6 +417,7 @@ memoro-ui/
- Data fetching patterns (optional)
**Tooling:**
- VSCode snippets für Components
- GitHub Actions für automated testing
- Automated screenshot testing
@ -385,17 +428,20 @@ memoro-ui/
## Success Metrics
**Phase 1 (CLI-Tool):**
- ✅ 10+ wiederverwendbare Components
- ✅ CLI-Tool funktioniert in allen Apps
- ✅ Mindestens 2 Apps nutzen das System
- ✅ Zeit für neue App-Features: -30%
**Phase 2 (Tailwind-Preset):**
- ✅ Design tokens extrahiert
- ✅ Konsistente Farben/Spacing über alle Apps
- ✅ Design updates in <1 Tag für alle Apps
**Overall:**
- ✅ Neue App in <1 Tag bootstrap-bar
- ✅ UI consistency über alle Apps
- ✅ Component reuse rate >60%
@ -406,27 +452,32 @@ memoro-ui/
## Timeline
**Week 1-2: Setup**
- UI-Components Repo erstellen
- CLI-Tool Grundstruktur
- Registry system
- Preview app setup
**Week 3-4: Core Components**
- 5 wichtigste Components entwickeln
- Testing in Preview app
- Documentation schreiben
**Week 5: First Migration**
- "picture" App als Test
- 2-3 Components migrieren
- Learnings dokumentieren
**Week 6+: Iteration**
- Mehr Components hinzufügen
- Weitere Apps migrieren
- CLI verbessern basierend auf Feedback
**Month 2-3: Optional Tailwind-Preset**
- Nur wenn es sich als nötig erweist
- Design tokens extrahieren
- Components refactoren
@ -437,17 +488,21 @@ memoro-ui/
## Decisions Made
### 1. Package Naming ✅
**Entscheidung:** `@memoro/ui`
**Reasoning:**
- Klarer, einprägsamer Name
- Namespace `@memoro` für alle zukünftigen Packages
- Konsistent für späteres `@memoro/tailwind-preset`
### 2. Registry ✅
**Entscheidung:** GitHub Packages
**Reasoning:**
- ✅ Kostenlos für private Repos
- ✅ Bereits in GitHub - keine extra Infrastruktur
- ✅ Einfache CI/CD Integration mit GitHub Actions
@ -455,18 +510,20 @@ memoro-ui/
- ✅ Kann später zu Private NPM migriert werden wenn nötig
**Setup Details:**
```json
// package.json im CLI-Tool
{
"name": "@memoro/ui",
"repository": "https://github.com/[username]/memoro-ui",
"publishConfig": {
"registry": "https://npm.pkg.github.com"
}
"name": "@memoro/ui",
"repository": "https://github.com/[username]/memoro-ui",
"publishConfig": {
"registry": "https://npm.pkg.github.com"
}
}
```
**Usage in Apps:**
```bash
# .npmrc in jeder App
@memoro:registry=https://npm.pkg.github.com
@ -480,10 +537,12 @@ npx @memoro/ui add button
```
**GitHub Personal Access Token (PAT) benötigt mit:**
- `read:packages` - Um Packages zu installieren
- `write:packages` - Um zu publizieren (nur für Maintainer)
**CI/CD Setup (GitHub Actions):**
```yaml
- name: Setup NPM for GitHub Packages
run: |
@ -494,59 +553,72 @@ npx @memoro/ui add button
---
### 3. Component Scope ✅
**Entscheidung:** UI-Components + Navigation Components
**Included:**
- ✅ UI-Components (Button, Input, Card, Badge, Avatar, etc.)
- ✅ Navigation Components (Header, TabBar, BackButton, etc.)
**Excluded (für jetzt):**
- ❌ Form validation helpers
- ❌ Data display (Lists, Tables, Pagination)
- ❌ Complex business logic components
**Reasoning:**
- Navigation components sind essentiell für jede App
- Wiederholen sich über alle Apps hinweg
- Bleiben UI-fokussiert ohne Business-Logik
- Können später erweitert werden wenn Bedarf entsteht
### 4. Testing Strategy ✅
**Entscheidung:** Manual Testing in Phase 1
**Phase 1:**
- Manual testing in Preview App
- Visual verification auf iOS/Android
- Component usage testing in real apps
**Phase 2 (später):**
- Automated tests wenn Library >5 Components hat
- Jest + React Native Testing Library
- Visual regression testing (optional)
**Reasoning:**
- Schneller Start ohne Testing-Overhead
- Preview App bietet gute visuelle Kontrolle
- Automated tests später wenn Library stabiler ist
### 5. Preview App ✅
**Entscheidung:** Lokale Expo App
**Setup:**
- Expo App im Monorepo unter `packages/preview/`
- Dev Client für native Features
- Hot reload während Component-Development
**Reasoning:**
- ✅ Mehr Kontrolle als Expo Snack
- ✅ Native Features testbar (z.B. Haptics, Gestures)
- ✅ Läuft im gleichen Repo - einfaches Development
- ✅ Kann mit Components in `packages/components/` direkt arbeiten
### 6. Repository Structure ✅
**Entscheidung:** Monorepo mit pnpm workspaces
**Structure:**
```
memoro-ui/
├── packages/
@ -569,12 +641,14 @@ memoro-ui/
```
**pnpm-workspace.yaml:**
```yaml
packages:
- 'packages/*'
```
**Reasoning:**
- ✅ Alles in einem Repo - einfacher zu entwickeln
- ✅ Shared dependencies zwischen Packages
- ✅ pnpm = schneller & effizienter als npm/yarn
@ -582,31 +656,36 @@ packages:
- ✅ CLI kann direkt auf Components zugreifen
### 7. GitHub Organization ✅
**Entscheidung:** GitHub Organization `@memoro`
**Setup:**
- Neue GitHub Org: `memoro` (oder `memoro-ui`)
- Repo: `github.com/memoro/ui` (oder ähnlich)
- Package: `@memoro/ui`
**Package Configuration:**
```json
{
"name": "@memoro/ui",
"repository": "https://github.com/memoro/ui",
"publishConfig": {
"registry": "https://npm.pkg.github.com"
}
"name": "@memoro/ui",
"repository": "https://github.com/memoro/ui",
"publishConfig": {
"registry": "https://npm.pkg.github.com"
}
}
```
**Reasoning:**
- ✅ Professioneller Auftritt
- ✅ Namespace für zukünftige Packages (`@memoro/tailwind-preset`)
- ✅ Einfacher Team-Management später
- ✅ Klare Trennung von Personal Projects
**GitHub Org Setup:**
1. Erstelle neue Org: https://github.com/organizations/plan
2. Invite Members (wenn Team)
3. Setup Package Permissions
@ -619,6 +698,7 @@ packages:
**Alle Haupt-Entscheidungen getroffen! ✅**
Optionale Entscheidungen für später:
- **Icon System:** Eigenes Icon-Set oder bestehende Library? (@expo/vector-icons, react-native-heroicons)
- **Animation Library:** Reanimated, Moti, oder custom?
- **TypeScript Strictness:** Wie streng? (strict mode, exactOptionalPropertyTypes, etc.)
@ -628,6 +708,7 @@ Optionale Entscheidungen für später:
## Next Steps
### Phase 1: Repository Setup
1. ✅ Plan dokumentiert
2. ✅ Alle Entscheidungen getroffen
3. ⏳ GitHub Organization `memoro` erstellen
@ -638,18 +719,21 @@ Optionale Entscheidungen für später:
- registry.json erstellen
### Phase 2: Preview App Setup
6. ⏳ Expo App in `packages/preview/` aufsetzen
- Expo Router konfigurieren
- NativeWind/Tailwind einrichten
- Tabs für UI & Navigation Components
### Phase 3: CLI-Tool Prototyp
7. ⏳ CLI-Tool Grundstruktur bauen
- TypeScript setup
- Commands: add, list, diff, update
- GitHub Packages publish konfigurieren
### Phase 4: Erste Components
8. ⏳ Ersten UI Component entwickeln (Button)
- Component code in `packages/components/ui/Button/`
- README schreiben
@ -662,6 +746,7 @@ Optionale Entscheidungen für später:
- registry.json eintragen
### Phase 5: Testing in Real App
10. ⏳ CLI publishen zu GitHub Packages
11. ⏳ In "picture" App testen
- `.npmrc` setup

View file

@ -12,6 +12,7 @@ Für eine Bilder-App mit gleichwertigen Mobile (React Native) und Web Anforderun
## Tech Stack Unabhängigkeit
### **SvelteKit** ✅ Unabhängiger
- **Compiler-basiert** - kompiliert zu Vanilla JS
- Keine Runtime Framework (React, Vue, etc.)
- Kleinere Abhängigkeiten
@ -19,6 +20,7 @@ Für eine Bilder-App mit gleichwertigen Mobile (React Native) und Web Anforderun
- Zukunftssicherer durch Web Standards
### **Next.js** ⚠️ React-Ökosystem
- Fest an React gebunden
- Braucht React Ökosystem (React Query, etc.)
- Größere Bundle Sizes
@ -29,6 +31,7 @@ Für eine Bilder-App mit gleichwertigen Mobile (React Native) und Web Anforderun
## Performance
### **SvelteKit** 🚀
- **Extrem schnell** - kein Virtual DOM
- Kleinere Bundles (20-30% weniger)
- Schnelleres First Paint
@ -36,6 +39,7 @@ Für eine Bilder-App mit gleichwertigen Mobile (React Native) und Web Anforderun
- Beispiel: 50KB vs 150KB initial
### **Next.js** 👍
- Gut, aber schwerer
- Virtual DOM Overhead
- Hydration kann langsam sein
@ -48,6 +52,7 @@ Für eine Bilder-App mit gleichwertigen Mobile (React Native) und Web Anforderun
### **SvelteKit**
**Vorteile:**
- **Weniger Boilerplate** - 30-40% weniger Code
- Intuitivere Syntax
- Eingebaute Animationen/Transitions
@ -55,17 +60,19 @@ Für eine Bilder-App mit gleichwertigen Mobile (React Native) und Web Anforderun
- Server Load Functions elegant
**Beispiel:**
```svelte
<script>
let count = 0; // Kein useState!
let count = 0; // Kein useState!
</script>
<button on:click={() => count++}>
{count}
{count}
</button>
```
**Nachteile:**
- Kleinere Community
- Weniger StackOverflow Antworten
- Weniger UI Libraries
@ -73,6 +80,7 @@ Für eine Bilder-App mit gleichwertigen Mobile (React Native) und Web Anforderun
### **Next.js**
**Vorteile:**
- **Riesige Community** - jedes Problem schon gelöst
- Tonnen von Libraries
- Mehr Devs verfügbar (Hiring)
@ -80,6 +88,7 @@ Für eine Bilder-App mit gleichwertigen Mobile (React Native) und Web Anforderun
- Besserer Support
**Nachteile:**
- Mehr Boilerplate
- Komplexer (App Router vs Pages Router)
- Hooks-Lernkurve
@ -90,6 +99,7 @@ Für eine Bilder-App mit gleichwertigen Mobile (React Native) und Web Anforderun
## Supabase Integration
### **Beide gleich gut**
- Supabase JS Client funktioniert überall
- SSR Auth beide gut
- Beide haben offizielle Guides
@ -97,10 +107,12 @@ Für eine Bilder-App mit gleichwertigen Mobile (React Native) und Web Anforderun
### **Unterschiede:**
**SvelteKit:**
- Hooks in `+page.server.ts` natürlicher
- Load Functions cleaner
**Next.js:**
- Mehr Beispiele online
- Mehr Tutorials verfügbar
@ -109,6 +121,7 @@ Für eine Bilder-App mit gleichwertigen Mobile (React Native) und Web Anforderun
## Routing & SSR
### **SvelteKit** 💚
- **File-based Routing** - `+page.svelte`
- Einfacher als Next.js
- Layouts intuitiver
@ -116,6 +129,7 @@ Für eine Bilder-App mit gleichwertigen Mobile (React Native) und Web Anforderun
- Weniger Magic
### **Next.js** 💛
- File-based Routing - aber komplizierter
- App Router vs Pages Router Verwirrung
- Mehr Konzepte (RSC, Server Actions)
@ -128,6 +142,7 @@ Für eine Bilder-App mit gleichwertigen Mobile (React Native) und Web Anforderun
### **Next.js** ✅ Größer
**UI Libraries:**
- Shadcn/ui (top!)
- Material UI
- Chakra UI
@ -136,6 +151,7 @@ Für eine Bilder-App mit gleichwertigen Mobile (React Native) und Web Anforderun
- Tausende mehr
**Sonstiges:**
- Jede Library hat React Support
- Auth: NextAuth perfekt integriert
- Payments: Stripe Beispiele überall
@ -143,6 +159,7 @@ Für eine Bilder-App mit gleichwertigen Mobile (React Native) und Web Anforderun
### **SvelteKit** ⚠️ Kleiner, wachsend
**UI Libraries:**
- Skeleton UI
- DaisyUI (Tailwind-based)
- Carbon Components
@ -150,6 +167,7 @@ Für eine Bilder-App mit gleichwertigen Mobile (React Native) und Web Anforderun
- Weniger Auswahl
**Aber:**
- Kann CSS Frameworks nutzen (Tailwind, UnoCSS)
- Viele Web Components nutzbar
@ -158,6 +176,7 @@ Für eine Bilder-App mit gleichwertigen Mobile (React Native) und Web Anforderun
## Image Handling (kritisch für Picture App!)
### **Next.js** ✅ Exzellent
- `next/image` Component eingebaut
- Automatische Optimierung
- WebP/AVIF Konvertierung
@ -166,6 +185,7 @@ Für eine Bilder-App mit gleichwertigen Mobile (React Native) und Web Anforderun
- **Produktionsreif out of the box**
### **SvelteKit** ⚠️ Braucht Setup
- Kein eingebautes Image Optimization
- Manuell mit Vite Plugins (vite-imagetools)
- Oder externe Services (Cloudinary, imgix)
@ -185,10 +205,12 @@ Für eine Bilder-App mit gleichwertigen Mobile (React Native) und Web Anforderun
### **Unterschiede:**
**Next.js:**
- Optimiert für Vercel
- Einige Features nur auf Vercel
**SvelteKit:**
- Adapter-System flexibler
- Läuft überall gleich gut
@ -197,12 +219,14 @@ Für eine Bilder-App mit gleichwertigen Mobile (React Native) und Web Anforderun
## Code Sharing mit React Native
### **Next.js** ✅ Einfacher
- Beide nutzen React
- Components **teilweise** portierbar
- Gleiche Patterns (Hooks)
- Logic besser teilbar
### **SvelteKit** ⚠️ Schwieriger
- Komplett andere Syntax
- Nur Business Logic teilbar
- UI muss komplett neu
@ -212,11 +236,13 @@ Für eine Bilder-App mit gleichwertigen Mobile (React Native) und Web Anforderun
## Hiring & Team
### **Next.js**
- Jeder React Dev kann Next.js
- Größerer Talent Pool
- Einfacher zu ersetzen
### **SvelteKit** ⚠️
- Kleinere Developer Base
- Schwieriger zu finden
- Aber: React Devs lernen es schnell
@ -226,12 +252,14 @@ Für eine Bilder-App mit gleichwertigen Mobile (React Native) und Web Anforderun
## Long-term Maintenance
### **SvelteKit** ✅ Stabiler
- Weniger Breaking Changes
- Klare Roadmap
- Web Standards fokussiert
- Weniger Refactoring nötig
### **Next.js** ⚠️ Schnelle Evolution
- App Router große Änderung (2023)
- React Server Components komplex
- Viel Churn
@ -241,24 +269,25 @@ Für eine Bilder-App mit gleichwertigen Mobile (React Native) und Web Anforderun
## Feature-Matrix für Picture App
| Feature | Next.js | SvelteKit | Gewinner |
|---------|---------|-----------|----------|
| Image Optimization | ✅ Exzellent | ⚠️ Manuell | Next.js |
| Performance | 👍 Gut | 🚀 Besser | SvelteKit |
| Supabase Integration | ✅ Gut | ✅ Gut | Unentschieden |
| Auth | ✅ NextAuth | ✅ Hooks | Unentschieden |
| Animations | 👍 Libraries | ✅ Native | SvelteKit |
| SEO | ✅ Gut | ✅ Gut | Unentschieden |
| Community Support | ✅ Riesig | ⚠️ Klein | Next.js |
| Bundle Size | ⚠️ Größer | ✅ Kleiner | SvelteKit |
| Code Sharing RN | ✅ React | ❌ Neu | Next.js |
| Developer Experience | 👍 Gut | ✅ Besser | SvelteKit |
| Feature | Next.js | SvelteKit | Gewinner |
| -------------------- | ------------ | ---------- | ------------- |
| Image Optimization | ✅ Exzellent | ⚠️ Manuell | Next.js |
| Performance | 👍 Gut | 🚀 Besser | SvelteKit |
| Supabase Integration | ✅ Gut | ✅ Gut | Unentschieden |
| Auth | ✅ NextAuth | ✅ Hooks | Unentschieden |
| Animations | 👍 Libraries | ✅ Native | SvelteKit |
| SEO | ✅ Gut | ✅ Gut | Unentschieden |
| Community Support | ✅ Riesig | ⚠️ Klein | Next.js |
| Bundle Size | ⚠️ Größer | ✅ Kleiner | SvelteKit |
| Code Sharing RN | ✅ React | ❌ Neu | Next.js |
| Developer Experience | 👍 Gut | ✅ Besser | SvelteKit |
---
## Entscheidungsmatrix
### **Wähle SvelteKit wenn:**
- ✅ Maximale Unabhängigkeit wichtig
- ✅ Performance kritisch
- ✅ Bereit für Image Optimization Setup
@ -266,6 +295,7 @@ Für eine Bilder-App mit gleichwertigen Mobile (React Native) und Web Anforderun
- ✅ Kleine, fokussierte Community okay
### **Wähle Next.js wenn:**
- ✅ Schnelle Time-to-Market wichtig
- ✅ Image Optimization out-of-the-box benötigt
- ✅ React-Synergien mit Mobile gewünscht
@ -302,6 +332,7 @@ Für eine Bilder-App mit gleichwertigen Mobile (React Native) und Web Anforderun
```
**Regeln:**
1. ❌ **Keine Next.js spezifischen Features** außer Image und Routing
2. ✅ **Business Logic in `/shared`** auslagern
3. ✅ **Vercel-unabhängig deployen** (z.B. Cloudflare, Netlify)
@ -329,6 +360,7 @@ Phase 3: Optional - Migration zu SvelteKit wenn Next.js nervt
**Warum NICHT Expo Web?**
Die App nutzt viele native-only Features:
- `react-native-worklets` (JSI/Native)
- `react-native-reanimated` (Native Animations)
- `react-native-pager-view` (Native Views)
@ -336,6 +368,7 @@ Die App nutzt viele native-only Features:
- Gesten, Zoom, Blur...
**Probleme:**
- 2-5 Tage Debugging für Mocks
- Ständige Workarounds
- Limitierte Features
@ -359,15 +392,18 @@ Die App nutzt viele native-only Features:
## Ressourcen
### Next.js
- [Next.js Docs](https://nextjs.org/docs)
- [Next.js + Supabase](https://supabase.com/docs/guides/getting-started/tutorials/with-nextjs)
- [Next.js Image Optimization](https://nextjs.org/docs/app/building-your-application/optimizing/images)
### SvelteKit (für Zukunft)
- [SvelteKit Docs](https://kit.svelte.dev/docs)
- [SvelteKit + Supabase](https://supabase.com/docs/guides/getting-started/tutorials/with-sveltekit)
### Monorepo Setup
- [Turborepo](https://turbo.build/repo/docs)
- [pnpm Workspaces](https://pnpm.io/workspaces)