managarten/apps/picture/docs/MONOREPO_COMPLETE_GUIDE.md
Wuesteon ff80aeec1f refactor: restructure
monorepo with apps/ and services/
  directories
2025-11-26 03:03:24 +01:00

35 KiB

Picture Monorepo - Complete Architecture Guide

Erstellt: 2025-10-09 Status: Production Ready Letzte Aktualisierung: 2025-10-09


Inhaltsverzeichnis

  1. Executive Summary
  2. Architektur Übersicht
  3. Framework Compatibility Matrix
  4. App Details
  5. Shared Packages
  6. PNPM Workspace Setup
  7. UI Component Strategy
  8. Migration Steps
  9. Development Workflow
  10. Deployment Strategy
  11. Environment Variables
  12. Code Sharing Matrix
  13. Recommendations & Future Options

Executive Summary

Das Picture Projekt ist ein PNPM Workspace Monorepo mit drei unabhängigen Applikationen:

  1. Mobile App (React Native + Expo) - iOS & Android
  2. Web App (SvelteKit) - Vollständige Webanwendung
  3. Landing Page (Astro) - Marketing Website

Alle Apps teilen gemeinsame TypeScript Packages für maximalen Code Reuse bei minimaler Komplexität.

Wichtigste Erkenntnisse

Code Reuse: ~35-40% des Codes wird geteilt Framework-Unabhängigkeit: Jede App nutzt den optimalen Tech Stack Deployment-Flexibilität: Unabhängiges Deployment pro App ⚠️ UI Components: Können NICHT zwischen Frameworks geteilt werden


Architektur Übersicht

picture/
├── apps/
│   ├── mobile/                 # React Native (Expo)
│   │   ├── app/               # Expo Router
│   │   ├── components/
│   │   ├── hooks/
│   │   ├── package.json
│   │   └── 🎯 Uses @memoro/mobile-ui
│   │
│   ├── web/                   # SvelteKit
│   │   ├── src/
│   │   │   ├── lib/
│   │   │   │   ├── components/  # Svelte-specific UI
│   │   │   │   └── stores/
│   │   │   ├── routes/
│   │   │   │   ├── +layout.svelte
│   │   │   │   ├── +page.svelte
│   │   │   │   ├── auth/
│   │   │   │   ├── images/
│   │   │   │   └── profile/
│   │   │   └── app.html
│   │   ├── static/
│   │   ├── svelte.config.js
│   │   └── package.json
│   │
│   └── landing/               # Astro
│       ├── src/
│       │   ├── components/
│       │   │   ├── Hero.astro
│       │   │   ├── Features.astro
│       │   │   ├── Pricing.astro
│       │   │   └── Footer.astro
│       │   ├── layouts/
│       │   │   └── Layout.astro
│       │   ├── pages/
│       │   │   ├── index.astro       # Homepage
│       │   │   ├── features.astro
│       │   │   ├── pricing.astro
│       │   │   └── about.astro
│       │   └── styles/
│       ├── public/
│       ├── astro.config.mjs
│       └── package.json
│
├── packages/
│   ├── mobile-ui/              # @memoro/mobile-ui
│   │   ├── components/
│   │   │   ├── ui/            # 17 React Native components
│   │   │   └── navigation/
│   │   ├── cli/               # CLI tool for installation
│   │   └── 🎯 React Native ONLY
│   │
│   ├── shared/                # @picture/shared
│   │   ├── src/
│   │   │   ├── types/
│   │   │   │   ├── database.ts       # Supabase Types
│   │   │   │   ├── models.ts         # App Models
│   │   │   │   └── index.ts
│   │   │   ├── api/
│   │   │   │   ├── supabase.ts       # Supabase Config
│   │   │   │   ├── images.ts         # Image API
│   │   │   │   ├── auth.ts           # Auth API
│   │   │   │   └── index.ts
│   │   │   ├── utils/
│   │   │   │   ├── image.ts          # Image Utils
│   │   │   │   ├── validation.ts     # Validation
│   │   │   │   └── index.ts
│   │   │   └── index.ts
│   │   ├── package.json
│   │   └── tsconfig.json
│   │
│   ├── design-tokens/         # @memoro/design-tokens (Future)
│   │   ├── src/
│   │   │   ├── colors.ts
│   │   │   ├── spacing.ts
│   │   │   ├── typography.ts
│   │   │   └── index.ts
│   │   └── package.json
│   │
│   └── ui/                    # (empty/deprecated)
│
├── package.json               # Root Package.json
├── pnpm-workspace.yaml        # PNPM Workspace Config
├── .npmrc                     # NPM Config
├── tsconfig.base.json         # Base TypeScript Config
└── .env.example               # Environment Variables Template

Framework Compatibility Matrix

Package Kompatibilität

Package Mobile (RN) Web (Svelte) Landing (Astro) Beschreibung
@memoro/mobile-ui Full support Incompatible Incompatible React Native UI Components
@picture/shared Yes Yes Yes Backend Logic, Types, APIs
@memoro/design-tokens Yes* Yes* Yes* Colors, Spacing, Typography

*Future implementation

Warum UI Components nicht geteilt werden können

@memoro/mobile-ui nutzt React Native Primitives:

import { View, Pressable, Text } from 'react-native';
import Animated from 'react-native-reanimated';
import { useSafeAreaInsets } from 'react-native-safe-area-context';

Web Apps nutzen andere Primitives:

<!-- SvelteKit -->
<div class="...">
  <button on:click={...}>
</div>
<!-- Astro -->
<div class="...">
  <button onclick="...">
</div>

Technische Einschränkungen:

  • View<div>
  • Pressable<button>
  • react-native-reanimated funktioniert nicht im Web Browser
  • Svelte Components funktionieren nicht in React Native
  • Astro Components funktionieren nicht in React Native

App Details

1. Mobile App (React Native + Expo)

Zweck: Native iOS & Android App

Tech Stack:

  • React Native 0.81
  • Expo SDK 54
  • Expo Router (File-based)
  • NativeWind (Tailwind)
  • Zustand (State)
  • Supabase Client
  • @memoro/mobile-ui (17 UI Components)

Features:

  • Auth (Login/Signup)
  • Image Upload
  • Image Gallery
  • Image Detail View mit Zoom
  • Profile Management
  • Offline Support (geplant)

UI Components:

# Install components via CLI
node packages/mobile-ui/cli/bin/cli.js add button
node packages/mobile-ui/cli/bin/cli.js add card
node packages/mobile-ui/cli/bin/cli.js add select

Verfügbare Components:

  • Button, Card, Text, Icon
  • Container, EmptyState, ErrorBanner
  • Badge, Tag, Slider, Skeleton
  • FAB, Select, ToggleGroup
  • Header, HeaderButton, TabBarIcon

Deployment:

  • EAS Build (Expo Application Services)
  • App Store & Google Play

2. Web App (SvelteKit)

Zweck: Vollständige Web-Anwendung (Desktop + Mobile Web)

Tech Stack:

  • SvelteKit 2.x
  • Svelte 5
  • Tailwind CSS v4
  • Supabase Client
  • Vite
  • TypeScript

Features:

  • Auth (Login/Signup)
  • Image Upload (Drag & Drop)
  • Image Gallery (Masonry Layout)
  • Image Detail View
  • Profile Management
  • SSR für SEO
  • Image Optimization

UI Strategy:

  • Location: apps/web/src/lib/components/ui/
  • Components: Button.svelte, Card.svelte, Input.svelte
  • Styling: Tailwind CSS v4
  • Custom Implementation: Svelte-spezifisch, nicht geteilt

Routing:

/                   # Home (Gallery)
/auth               # Login/Signup
/auth/callback      # OAuth Callback
/images             # Images Grid
/images/[id]        # Image Detail
/profile            # User Profile
/settings           # Settings

Deployment:

  • Cloudflare Pages (empfohlen)
  • Alternativ: Vercel, Netlify

3. Landing Page (Astro)

Zweck: Marketing Website, SEO-optimiert, statisch

Tech Stack:

  • Astro 5.x
  • Tailwind CSS v3
  • TypeScript
  • Optional: Svelte/React Components

Features:

  • Hero Section
  • Features Overview
  • Pricing Page
  • About Page
  • Blog (optional)
  • SEO Optimiert
  • Ultraschnell (Static)

UI Strategy:

  • Location: apps/landing/src/components/
  • Components: Hero.astro, CTA.astro, Features.astro
  • Styling: Tailwind CSS v3
  • Custom Implementation: Astro-spezifisch, nicht geteilt

Seiten:

/                   # Homepage
/features           # Features Detail
/pricing            # Pricing Plans
/about              # About Us
/blog/              # Blog (optional)
/docs/              # Documentation (optional)

Warum Astro?

  • Ultraschnell (0 JS by default)
  • 🎯 Perfect für Marketing
  • 🔍 SEO-optimiert out of the box
  • 🎨 Kann Svelte/React Components nutzen
  • 📦 Tiny Bundle Sizes

Deployment:

  • Cloudflare Pages (empfohlen)
  • Netlify
  • Vercel

Shared Packages

Package: @picture/shared

Zweck: Core Business Logic, Types, API

Was wird geteilt:

// Types
export type { Database, Image, User, Album } from './types';

// API Clients
export { createSupabaseClient } from './api/supabase';
export { ImageAPI } from './api/images';
export { AuthAPI } from './api/auth';

// Utils
export {
  calculateAspectRatio,
  validateImageSize,
  formatFileSize,
  generateBlurhash
} from './utils/image';

export {
  validateEmail,
  validatePassword,
  sanitizeInput
} from './utils/validation';

export {
  formatDate,
  formatRelativeTime
} from './utils/date';

Struktur:

packages/shared/
├── src/
│   ├── types/
│   │   ├── database.ts           # Supabase Generated
│   │   ├── models.ts             # App Models
│   │   └── index.ts
│   ├── api/
│   │   ├── base.ts               # Base API Client
│   │   ├── supabase.ts           # Supabase Setup
│   │   ├── images.ts             # Image Operations
│   │   ├── auth.ts               # Auth Operations
│   │   └── index.ts
│   ├── utils/
│   │   ├── image.ts              # Image Utils
│   │   ├── validation.ts         # Input Validation
│   │   ├── date.ts               # Date Formatting
│   │   └── index.ts
│   ├── constants/
│   │   ├── config.ts             # App Config
│   │   └── index.ts
│   └── index.ts
├── package.json
└── tsconfig.json

Package.json:

{
  "name": "@picture/shared",
  "version": "1.0.0",
  "type": "module",
  "main": "./src/index.ts",
  "types": "./src/index.ts",
  "exports": {
    ".": "./src/index.ts",
    "./types": "./src/types/index.ts",
    "./api": "./src/api/index.ts",
    "./utils": "./src/utils/index.ts"
  },
  "scripts": {
    "type-check": "tsc --noEmit",
    "lint": "eslint src"
  },
  "dependencies": {
    "@supabase/supabase-js": "^2.38.4"
  },
  "devDependencies": {
    "typescript": "^5.8.3"
  }
}

API Client Beispiel:

// packages/shared/src/api/supabase.ts
import { createClient } from '@supabase/supabase-js';
import type { Database } from '../types/database';

export function createSupabaseClient(url: string, key: string) {
  return createClient<Database>(url, key, {
    auth: {
      persistSession: true,
      autoRefreshToken: true,
    },
  });
}

// packages/shared/src/api/images.ts
import type { SupabaseClient } from '@supabase/supabase-js';
import type { Database } from '../types/database';

export class ImageAPI {
  constructor(private client: SupabaseClient<Database>) {}

  async getImages(userId: string) {
    const { data, error } = await this.client
      .from('images')
      .select('*')
      .eq('user_id', userId)
      .order('created_at', { ascending: false });

    if (error) throw error;
    return data;
  }

  async getImage(id: string) {
    const { data, error } = await this.client
      .from('images')
      .select('*')
      .eq('id', id)
      .single();

    if (error) throw error;
    return data;
  }

  async uploadImage(file: File | Blob, userId: string) {
    // Upload logic
  }

  async deleteImage(id: string) {
    const { error } = await this.client
      .from('images')
      .delete()
      .eq('id', id);

    if (error) throw error;
  }
}

Utils Beispiel:

// packages/shared/src/utils/image.ts
export function calculateAspectRatio(width: number, height: number): number {
  return width / height;
}

export function validateImageSize(size: number, maxSize: number = 10 * 1024 * 1024): boolean {
  return size <= maxSize;
}

export function formatFileSize(bytes: number): string {
  if (bytes === 0) return '0 Bytes';
  const k = 1024;
  const sizes = ['Bytes', 'KB', 'MB', 'GB'];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
}

// packages/shared/src/utils/validation.ts
export function validateEmail(email: string): boolean {
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return emailRegex.test(email);
}

export function validatePassword(password: string): {
  valid: boolean;
  errors: string[];
} {
  const errors: string[] = [];

  if (password.length < 8) {
    errors.push('Password must be at least 8 characters');
  }
  if (!/[A-Z]/.test(password)) {
    errors.push('Password must contain uppercase letter');
  }
  if (!/[a-z]/.test(password)) {
    errors.push('Password must contain lowercase letter');
  }
  if (!/[0-9]/.test(password)) {
    errors.push('Password must contain number');
  }

  return {
    valid: errors.length === 0,
    errors
  };
}

Package: @memoro/mobile-ui

Zweck: React Native UI Component Library

Verfügbare Components: 17 komponenten

  • UI: Button, Card, Text, Icon, Container, EmptyState, ErrorBanner, Badge, Tag, Slider, Skeleton, FAB, Select, ToggleGroup
  • Navigation: Header, HeaderButton, TabBarIcon

CLI Tool:

# List available components
node packages/mobile-ui/cli/bin/cli.js list

# Add single component
node packages/mobile-ui/cli/bin/cli.js add button

# Add multiple components
node packages/mobile-ui/cli/bin/cli.js add button card text

Features:

  • TypeScript support
  • CLI for easy installation
  • Expo/React Native only
  • Not compatible with web frameworks

Package: @memoro/design-tokens (Future)

Zweck: Shared Design System Values

Was kann geteilt werden:

// packages/design-tokens/colors.ts
export const colors = {
  primary: '#3B82F6',
  secondary: '#8B5CF6',
  dark: {
    bg: '#000000',
    surface: '#242424',
    elevated: '#2a2a2a',
    border: '#383838',
  },
  // ...
};

// packages/design-tokens/typography.ts
export const typography = {
  fonts: {
    body: 'Inter, system-ui, sans-serif',
    heading: 'Inter, system-ui, sans-serif',
  },
  sizes: {
    xs: '0.75rem',
    sm: '0.875rem',
    base: '1rem',
    lg: '1.125rem',
    xl: '1.25rem',
    // ...
  }
};

// packages/design-tokens/spacing.ts
export const spacing = {
  xs: '0.25rem',   // 4px
  sm: '0.5rem',    // 8px
  md: '1rem',      // 16px
  lg: '1.5rem',    // 24px
  xl: '2rem',      // 32px
  // ...
};

Verwendung in allen Apps:

// Mobile (React Native)
import { colors, spacing } from '@memoro/design-tokens';

const styles = StyleSheet.create({
  container: {
    backgroundColor: colors.dark.bg,
    padding: spacing.md,
  }
});
<!-- Web (Svelte) -->
<script>
  import { colors, spacing } from '@memoro/design-tokens';
</script>

<div style="background: {colors.dark.bg}; padding: {spacing.md}">
  Content
</div>
---
// Landing (Astro)
import { colors, spacing } from '@memoro/design-tokens';
---

<div style={`background: ${colors.dark.bg}; padding: ${spacing.md}`}>
  Content
</div>

PNPM Workspace Setup

1. Root package.json

{
  "name": "picture-monorepo",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "dev": "pnpm run --parallel dev",
    "dev:mobile": "pnpm --filter @picture/mobile dev",
    "dev:web": "pnpm --filter @picture/web dev",
    "dev:landing": "pnpm --filter @picture/landing dev",

    "build": "pnpm run --recursive build",
    "build:mobile": "pnpm --filter @picture/mobile build",
    "build:web": "pnpm --filter @picture/web build",
    "build:landing": "pnpm --filter @picture/landing build",

    "lint": "pnpm run --recursive lint",
    "type-check": "pnpm run --recursive type-check",

    "clean": "pnpm run --recursive clean && rm -rf node_modules"
  },
  "devDependencies": {
    "@types/node": "^20.0.0",
    "typescript": "^5.8.3",
    "prettier": "^3.2.5",
    "eslint": "^9.25.1"
  }
}

2. pnpm-workspace.yaml

packages:
  - 'apps/*'
  - 'packages/*'

3. .npmrc

# Use pnpm for all installs
auto-install-peers=true
shamefully-hoist=true
strict-peer-dependencies=false

4. tsconfig.base.json

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "lib": ["ES2022"],
    "moduleResolution": "bundler",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "paths": {
      "@picture/shared": ["./packages/shared/src"],
      "@picture/shared/*": ["./packages/shared/src/*"],
      "@memoro/mobile-ui": ["./packages/mobile-ui/src"],
      "@memoro/design-tokens": ["./packages/design-tokens/src"]
    }
  }
}

UI Component Strategy

Aktueller Ansatz: Framework-spezifische Implementierungen

Mobile App

Nutzt: @memoro/mobile-ui

  • 17 React Native components
  • CLI tool für Installation
  • Full TypeScript support
# Install components
node packages/mobile-ui/cli/bin/cli.js add button

Web App

🔨 Strategie: Eigene Svelte Components

  • Location: apps/web/src/lib/components/ui/
  • Aktuell: Button.svelte, Card.svelte, Input.svelte
  • Styling: Tailwind CSS v4
  • Implementierung: Svelte-spezifisch

Landing Page

🔨 Strategie: Eigene Astro Components

  • Location: apps/landing/src/components/
  • Aktuell: Hero.astro, CTA.astro, Features.astro
  • Styling: Tailwind CSS v3
  • Implementierung: Astro-spezifisch

Was KANN geteilt werden

Backend Logic - @picture/shared

  • Supabase client
  • Database types
  • API utilities
  • Validation logic
  • Image utilities

Design Tokens (Zukünftig) - @memoro/design-tokens

  • Colors
  • Spacing
  • Typography
  • Breakpoints

Was NICHT geteilt werden kann

UI Components

  • React Native components funktionieren nicht im Web
  • Svelte components funktionieren nicht in React Native
  • Astro components funktionieren nicht in React Native

Warum?

Technische Einschränkungen:

// React Native
<View>             <div>
<Pressable>        <button>
<Text>             <span>
react-native-reanimated    Web Animations API

Migration Steps

Phase 1: Monorepo Setup (Tag 1, ~3h)

1.1 PNPM Installation

# Install PNPM globally
npm install -g pnpm

# Verify
pnpm --version

1.2 Ordnerstruktur

# Create apps directory
mkdir -p apps

# Create packages directory
mkdir -p packages/shared/src
mkdir -p packages/design-tokens/src

# Move existing code
mv app apps/mobile/
mv components apps/mobile/
mv hooks apps/mobile/
mv services apps/mobile/
mv utils apps/mobile/
mv store apps/mobile/
# ... all other mobile files

1.3 Workspace Config

  • pnpm-workspace.yaml erstellen
  • Root package.json anpassen
  • .npmrc erstellen
  • tsconfig.base.json erstellen

1.4 Mobile App Package

// apps/mobile/package.json
{
  "name": "@picture/mobile",
  "version": "1.0.0",
  "main": "expo-router/entry",
  "scripts": {
    "dev": "expo start",
    "ios": "expo run:ios",
    "android": "expo run:android",
    "build:dev": "eas build --profile development",
    "lint": "eslint .",
    "type-check": "tsc --noEmit"
  },
  "dependencies": {
    "@picture/shared": "workspace:*",
    "@memoro/mobile-ui": "workspace:*",
    // ... existing dependencies
  }
}

Phase 2: Shared Package (Tag 1-2, ~4h)

2.1 Supabase Types generieren

# In packages/shared/
npx supabase gen types typescript \
  --project-id mjuvnnjxwfwlmxjsgkqu \
  > src/types/database.ts

2.2 Utils extrahieren

  • Image Utils nach packages/shared/src/utils/image.ts
  • Validation Utils nach packages/shared/src/utils/validation.ts
  • Date Utils nach packages/shared/src/utils/date.ts

2.3 API Client erstellen

  • Supabase Client in packages/shared/src/api/supabase.ts
  • Image API in packages/shared/src/api/images.ts
  • Auth API in packages/shared/src/api/auth.ts

Phase 3: SvelteKit Setup (Tag 2-3, ~6h)

3.1 Create SvelteKit App

cd apps
pnpm create svelte@latest web

# Choose:
# - Skeleton project
# - TypeScript
# - ESLint + Prettier
# - Vitest (optional)

3.2 Install Dependencies

cd web
pnpm add @supabase/supabase-js @supabase/ssr
pnpm add -D tailwindcss autoprefixer postcss
pnpm add -D @sveltejs/adapter-cloudflare

# Add shared package
pnpm add @picture/shared@workspace:*

3.3 Configure Tailwind

npx tailwindcss init -p

3.4 Supabase Integration

// apps/web/src/lib/supabase.ts
import { createSupabaseClient } from '@picture/shared/api';
import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public';

export function getSupabase() {
  return createSupabaseClient(
    PUBLIC_SUPABASE_URL,
    PUBLIC_SUPABASE_ANON_KEY
  );
}

3.5 Basic Routes erstellen

  • +layout.svelte - Root Layout
  • +page.svelte - Homepage/Gallery
  • auth/+page.svelte - Auth
  • images/[id]/+page.svelte - Image Detail
  • profile/+page.svelte - Profile

Phase 4: Astro Landing Page (Tag 3-4, ~4h)

4.1 Create Astro Project

cd apps
pnpm create astro@latest landing

# Choose:
# - Empty template
# - TypeScript (Strict)
# - Install dependencies

4.2 Install Dependencies

cd landing
pnpm add -D tailwindcss autoprefixer postcss
pnpm add -D @astrojs/tailwind
pnpm add -D @astrojs/sitemap
pnpm add -D @astrojs/svelte  # Optional

4.3 Configure Astro

// apps/landing/astro.config.mjs
import { defineConfig } from 'astro/config';
import tailwind from '@astrojs/tailwind';
import sitemap from '@astrojs/sitemap';

export default defineConfig({
  site: 'https://picture.app',
  integrations: [tailwind(), sitemap()],
  output: 'static',
});

4.4 Pages erstellen

  • index.astro - Homepage
  • features.astro - Features
  • pricing.astro - Pricing
  • about.astro - About

Phase 5: Design Tokens (Optional, Tag 5, ~3h)

5.1 Package erstellen

mkdir -p packages/design-tokens/src

5.2 Tokens definieren

// packages/design-tokens/src/colors.ts
export const colors = {
  primary: '#3B82F6',
  secondary: '#8B5CF6',
  dark: {
    bg: '#000000',
    surface: '#242424',
    elevated: '#2a2a2a',
    border: '#383838',
  },
};

// packages/design-tokens/src/spacing.ts
export const spacing = {
  xs: '0.25rem',
  sm: '0.5rem',
  md: '1rem',
  lg: '1.5rem',
  xl: '2rem',
};

// packages/design-tokens/src/typography.ts
export const typography = {
  fonts: {
    body: 'Inter, system-ui, sans-serif',
    heading: 'Inter, system-ui, sans-serif',
  },
  sizes: {
    xs: '0.75rem',
    sm: '0.875rem',
    base: '1rem',
    lg: '1.125rem',
    xl: '1.25rem',
  },
};

5.3 In Apps integrieren

# Mobile
pnpm --filter @picture/mobile add @memoro/design-tokens@workspace:*

# Web
pnpm --filter @picture/web add @memoro/design-tokens@workspace:*

# Landing
pnpm --filter @picture/landing add @memoro/design-tokens@workspace:*

Development Workflow

Starting all apps

# Root directory
pnpm dev

Starting individual apps

# Mobile (React Native)
pnpm dev:mobile    # Expo on port 8081

# Web (SvelteKit)
pnpm dev:web       # http://localhost:5173

# Landing (Astro)
pnpm dev:landing   # http://localhost:4321

Building

# Build all apps
pnpm build

# Build individual apps
pnpm build:mobile
pnpm build:web
pnpm build:landing

Type Checking

# Check all packages
pnpm type-check

Linting

# Lint all packages
pnpm lint

Adding Dependencies

To a specific app:

# Add to mobile
pnpm --filter @picture/mobile add react-native-gesture-handler

# Add to web
pnpm --filter @picture/web add svelte-routing

# Add to landing
pnpm --filter @picture/landing add @astrojs/mdx

To shared package:

pnpm --filter @picture/shared add date-fns

To all packages:

pnpm add -w eslint

Deployment Strategy

Mobile (React Native + Expo)

Development Build:

cd apps/mobile
eas build --platform ios --profile development
eas build --platform android --profile development

Production Build:

eas build --platform ios --profile production
eas build --platform android --profile production

Submit to Stores:

eas submit --platform ios
eas submit --platform android

Web (SvelteKit)

Cloudflare Pages (empfohlen):

  1. Build locally:
cd apps/web
pnpm build
  1. Deploy via Cloudflare Pages Dashboard:
    • Build command: pnpm build
    • Output directory: build
    • Framework preset: SvelteKit

Environment Variables (Cloudflare):

PUBLIC_SUPABASE_URL=https://your-project.supabase.co
PUBLIC_SUPABASE_ANON_KEY=your-anon-key

Alternative: Vercel/Netlify:

# Vercel
vercel deploy

# Netlify
netlify deploy --prod

Landing (Astro)

Cloudflare Pages:

  1. Build:
cd apps/landing
pnpm build
  1. Deploy via Dashboard:
    • Build command: pnpm build
    • Output directory: dist
    • Framework preset: Astro

Static Site - Alternativen:

  • Upload dist/ zu beliebigem Static Host
  • AWS S3 + CloudFront
  • Netlify
  • Vercel
  • GitHub Pages

Vorteile Static Site:

  • Ultraschnell
  • 💰 Günstig (oft kostenlos)
  • 🔒 Sicher
  • 🚀 Einfach zu deployen

Environment Variables

Root .env.example

# Supabase
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_KEY=your-service-key

# URLs
MOBILE_APP_URL=exp://localhost:8081
WEB_APP_URL=http://localhost:5173
LANDING_URL=http://localhost:4321

# Feature Flags
FEATURE_UPLOAD_ENABLED=true
FEATURE_ARCHIVE_ENABLED=true

App-specific Environment Files

Mobile (apps/mobile/.env):

EXPO_PUBLIC_SUPABASE_URL=$SUPABASE_URL
EXPO_PUBLIC_SUPABASE_ANON_KEY=$SUPABASE_ANON_KEY

Web (apps/web/.env):

PUBLIC_SUPABASE_URL=$SUPABASE_URL
PUBLIC_SUPABASE_ANON_KEY=$SUPABASE_ANON_KEY

Landing (apps/landing/.env):

# No env needed for static site
# Optional: PUBLIC_API_URL if needed for contact forms

Code Sharing Matrix

Was wird geteilt?

Feature Mobile Web Landing Package Status
Backend & Logic
Supabase Types @picture/shared Implemented
API Client @picture/shared Implemented
Auth Logic @picture/shared Implemented
Image Utils @picture/shared Implemented
Validation @picture/shared Implemented
Date Utils @picture/shared Implemented
Design System
Design Tokens @memoro/design-tokens 🔨 Planned
Colors @memoro/design-tokens 🔨 Planned
Spacing @memoro/design-tokens 🔨 Planned
Typography @memoro/design-tokens 🔨 Planned
UI Components
UI Components Framework-specific ⚠️ Cannot share
Mobile UI @memoro/mobile-ui Implemented
Web UI Custom Svelte Implemented
Landing UI Custom Astro Implemented

Code Reuse Statistik

Shared Code: ~35-40%

  • Backend Logic: 100% geteilt
  • Types: 100% geteilt
  • Utils: 80% geteilt
  • UI Components: 0% geteilt

Vorteile:

  • Single Source of Truth für Business Logic
  • Type Safety über alle Apps
  • Konsistente API Calls
  • Weniger Bugs durch geteilte Utils

Nachteile:

  • UI Components müssen separat implementiert werden
  • ⚠️ Design Konsistenz erfordert manuelle Synchronisation (ohne Design Tokens)

Recommendations & Future Options

Current Approach (Implemented)

Strategie: Framework-spezifische UI Components

Pros:

  • Einfach zu verstehen
  • Framework-optimierte Components
  • Keine zusätzliche Komplexität
  • Schnelle Entwicklung

Cons:

  • Doppelte Implementierung von UI
  • Manuelle Design Synchronisation
  • Mehr Maintenance

Status: Production-ready


Option 1: Design Tokens (Empfohlen für nächste Phase) 🎯

Strategie: Geteilte Design Values, eigene Components

Implementierung:

packages/
└── design-tokens/
    ├── colors.ts
    ├── spacing.ts
    ├── typography.ts
    ├── breakpoints.ts
    └── tailwind.preset.js

Verwendung:

// Mobile
import { colors, spacing } from '@memoro/design-tokens';

// Web
import { colors, spacing } from '@memoro/design-tokens';

// Landing
import { colors, spacing } from '@memoro/design-tokens';

Pros:

  • Visuelle Konsistenz (colors, spacing, typography)
  • Single Source of Truth für Design
  • Einfach zu updaten (Branding)
  • Geringe Komplexität

Cons:

  • ⚠️ Components müssen noch separat implementiert werden

Empfehlung: Implementieren in Phase 6

Timeline: ~3-4h


Option 2: Headless UI + Adapters (Erweitert) 🚀

Strategie: Geteilte Business Logic, Framework-spezifisches Rendering

Architektur:

packages/
├── ui-core/           # Headless logic (TypeScript)
│   ├── useButton.ts
│   ├── useSelect.ts
│   └── useModal.ts
├── mobile-ui/         # React Native rendering
├── web-ui/            # Svelte rendering
└── landing-ui/        # Astro rendering

Beispiel:

// packages/ui-core/useButton.ts
export function useButton(props: ButtonProps) {
  return {
    isPressed: state.pressed,
    isHovered: state.hovered,
    handlePress: () => { /* logic */ },
    ariaProps: { /* a11y */ }
  };
}

// Mobile (React Native)
const ButtonRN = (props) => {
  const { isPressed, handlePress, ariaProps } = useButton(props);
  return <Pressable onPress={handlePress} {...ariaProps} />;
};

// Web (Svelte)
const ButtonSvelte = (props) => {
  const { isPressed, handlePress, ariaProps } = useButton(props);
  return <button onclick={handlePress} {...ariaProps} />;
};

Pros:

  • Geteilte Business Logic
  • Konsistentes Verhalten
  • Framework-optimiertes Rendering
  • A11y einmal implementiert

Cons:

  • Hohe Komplexität
  • Mehr Packages zu managen
  • Longer Development Time
  • Overhead für kleine Teams

Empfehlung: ⚠️ Nur wenn UI Konsistenz kritisch wird

Timeline: ~2-3 Wochen


Phase 1-4: DONE

  • Monorepo Setup
  • Apps (Mobile, Web, Landing)
  • Shared Package
  • Mobile UI Library

Phase 5: 🎯 NEXT (3-4h)

Design Tokens Package

  • Extract colors, spacing, typography
  • Create @memoro/design-tokens
  • Integrate in all apps
  • Visual consistency ohne Component Sharing

Phase 6: 🔮 FUTURE (Optional)

Headless UI (nur bei Bedarf)

  • Nur wenn UI Consistency kritisch wird
  • Nur wenn Team wächst
  • Nur wenn Budget vorhanden

Timeline Summary

Completed

  • Phase 1: Monorepo Setup (3h)
  • Phase 2: Shared Package (4h)
  • Phase 3: SvelteKit App (10h)
  • Phase 4: Astro Landing (6h)
  • Phase 5: Mobile UI Library (bereits vorhanden)

Total: ~23h Development Time

Next Steps 🎯

  • Phase 6: Design Tokens Package (3-4h)
  • Phase 7: Deployment Setup (4h)
  • Phase 8: CI/CD Pipeline (6h)

Estimated Total: ~36-40h für komplettes Setup


Advantages of Current Architecture

Maximale Unabhängigkeit

  • Jede App kann unabhängig deployed werden
  • Kein Vendor Lock-in
  • Web Standards überall

Code Reuse (~40%)

  • Types einmal definieren
  • API Logic zentral
  • Utils geteilt
  • Validation zentral

Optimal für Zweck

  • Mobile: Native Experience mit Expo
  • Web: Full App mit SSR (SEO)
  • Landing: Ultraschnell, Static (Marketing)

Einfache Wartung

  • Clear Separation of Concerns
  • Shared Logic zentral
  • Easy Updates
  • Framework-agnostic Backend

Skalierbar

  • Neue Apps einfach hinzufügen
  • Packages wachsen organisch
  • CI/CD pro App
  • Independent Teams möglich

Tech Stack Summary

Mobile (React Native):
  Framework: Expo SDK 54
  Routing: Expo Router
  Styling: NativeWind
  State: Zustand
  UI: @memoro/mobile-ui (17 components)
  Deploy: EAS Build → App Store / Google Play

Web (SvelteKit):
  Framework: SvelteKit 2.x / Svelte 5
  Styling: Tailwind CSS v4
  UI: Custom Svelte Components
  Build: Vite
  Deploy: Cloudflare Pages

Landing (Astro):
  Framework: Astro 5.x
  Styling: Tailwind CSS v3
  UI: Custom Astro Components
  Output: Static Site
  Deploy: Cloudflare Pages

Shared:
  Language: TypeScript 5.x
  Backend: Supabase
  Package Manager: PNPM
  Workspace: PNPM Workspaces
  Version Control: Git

Support & Resources

Documentation

Tools

  • @memoro/mobile-ui CLI: node packages/mobile-ui/cli/bin/cli.js
  • Supabase CLI: npx supabase
  • Expo CLI: npx expo

Commands Quick Reference

# Development
pnpm dev              # All apps
pnpm dev:mobile       # Mobile only
pnpm dev:web          # Web only
pnpm dev:landing      # Landing only

# Building
pnpm build            # All apps
pnpm build:mobile     # Mobile build
pnpm build:web        # Web build
pnpm build:landing    # Landing build

# Quality
pnpm lint             # Lint all
pnpm type-check       # Type check all
pnpm test             # Test all (if configured)

# Maintenance
pnpm clean            # Clean all node_modules

Authors & Changelog

Initial Documentation: 2025-10-08 Consolidated Guide: 2025-10-09

Authors:

  • Claude Code (AI Assistant)
  • Till Schneider (Project Owner)

Status: Production Ready


🚀 Ready to build amazing things!