managarten/picture/docs/features/MONOREPO_ARCHITECTURE.md
Till-JS c712a2504a feat: integrate uload and picture, unify package naming
- Add uload project with apps/web structure
  - Reorganize from flat to monorepo structure
  - Remove PocketBase binary and local data
  - Update to pnpm and @uload/web namespace

- Add picture project to monorepo
  - Remove embedded git repository

- Unify all package names to @{project}/{app} schema:
  - @maerchenzauber/* (was @storyteller/*)
  - @manacore/* (was manacore-*, manacore)
  - @manadeck/* (was web, backend, manadeck)
  - @memoro/* (was memoro-web, landing, memoro)
  - @picture/* (already unified)
  - @uload/web

- Add convenient dev scripts for all apps:
  - pnpm dev:{project}:web
  - pnpm dev:{project}:landing
  - pnpm dev:{project}:mobile
  - pnpm dev:{project}:backend

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-25 04:00:36 +01:00

24 KiB

Monorepo Architecture Plan

Datum: 2025-10-08 Status: Planning Phase

Executive Summary

Wir bauen ein PNPM Workspace Monorepo mit drei unabhängigen Apps:

  1. Mobile App (React Native) - iOS & Android
  2. Web App (SvelteKit) - Hauptanwendung
  3. Landing Page (Astro) - Marketing Website

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


Architektur Übersicht

picture/
├── apps/
│   ├── mobile/                 # React Native (Expo)
│   │   ├── app/               # Expo Router
│   │   ├── components/
│   │   ├── hooks/
│   │   ├── package.json
│   │   └── ...existing code...
│   │
│   ├── web/                   # SvelteKit
│   │   ├── src/
│   │   │   ├── lib/
│   │   │   │   ├── components/
│   │   │   │   └── 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    # Features Page
│       │   │   ├── pricing.astro     # Pricing
│       │   │   └── about.astro       # About
│       │   └── styles/
│       ├── public/
│       ├── astro.config.mjs
│       └── package.json
│
├── packages/
│   ├── shared/                # Core Business Logic
│   │   ├── 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
│   │
│   └── ui/                    # Shared Design Tokens (optional)
│       ├── src/
│       │   ├── colors.ts
│       │   ├── typography.ts
│       │   └── index.ts
│       └── package.json
│
├── 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

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

Features:

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

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
  • 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

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 4.x
  • Tailwind CSS
  • TypeScript
  • Optional: Svelte/React Components

Features:

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

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: @picture/ui (Optional)

Zweck: Shared Design Tokens

Was wird geteilt:

// Colors
export const colors = {
  dark: {
    bg: '#000000',
    surface: '#242424',
    elevated: '#2a2a2a',
    border: '#383838',
  },
  // ...
};

// Typography
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',
    // ...
  }
};

// Spacing
export const spacing = {
  xs: '0.25rem',
  sm: '0.5rem',
  md: '1rem',
  // ...
};

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/ui": ["./packages/ui/src"]
    }
  }
}

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/ui/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:*",
    // ... existing dependencies
  }
}

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

2.1 Package Setup

// packages/shared/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"
  }
}

2.2 Supabase Types generieren

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

2.3 Basic API Client

// 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;
  }
}

2.4 Utils extrahieren

// 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
  };
}

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  # or adapter-auto

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

3.3 Configure Tailwind

npx tailwindcss init -p
// tailwind.config.js
export default {
  content: ['./src/**/*.{html,js,svelte,ts}'],
  theme: {
    extend: {},
  },
  plugins: [],
};

3.4 Package.json

// apps/web/package.json
{
  "name": "@picture/web",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite dev",
    "build": "vite build",
    "preview": "vite preview",
    "type-check": "svelte-check --tsconfig ./tsconfig.json",
    "lint": "eslint ."
  },
  "dependencies": {
    "@picture/shared": "workspace:*",
    "@supabase/supabase-js": "^2.38.4",
    "@supabase/ssr": "^0.1.0"
  },
  "devDependencies": {
    "@sveltejs/adapter-cloudflare": "^4.0.0",
    "@sveltejs/kit": "^2.0.0",
    "@sveltejs/vite-plugin-svelte": "^3.0.0",
    "svelte": "^5.0.0",
    "svelte-check": "^3.6.0",
    "tailwindcss": "^3.4.0",
    "typescript": "^5.8.3",
    "vite": "^5.0.0"
  }
}

3.5 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.6 Basic Routes

<!-- apps/web/src/routes/+layout.svelte -->
<script lang="ts">
  import '../app.css';
</script>

<slot />

<!-- apps/web/src/routes/+page.svelte -->
<script lang="ts">
  import { ImageAPI } from '@picture/shared/api';
  import { getSupabase } from '$lib/supabase';

  let images = $state([]);

  async function loadImages() {
    const supabase = getSupabase();
    const api = new ImageAPI(supabase);
    images = await api.getImages('user-id');
  }
</script>

<h1>Welcome to Picture</h1>
<button onclick={loadImages}>Load Images</button>

{#each images as image}
  <div>{image.url}</div>
{/each}

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  # For SEO

# Optional: Svelte integration for interactive components
pnpm add -D @astrojs/svelte

4.3 Configure Astro

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

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

4.4 Package.json

// apps/landing/package.json
{
  "name": "@picture/landing",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "dev": "astro dev",
    "build": "astro build",
    "preview": "astro preview",
    "astro": "astro"
  },
  "dependencies": {
    "@astrojs/sitemap": "^3.0.0",
    "@astrojs/svelte": "^5.0.0",
    "@astrojs/tailwind": "^5.0.0",
    "astro": "^4.0.0",
    "svelte": "^5.0.0",
    "tailwindcss": "^3.4.0"
  }
}

4.5 Basic Pages

---
// apps/landing/src/pages/index.astro
import Layout from '../layouts/Layout.astro';
import Hero from '../components/Hero.astro';
import Features from '../components/Features.astro';
---

<Layout title="Picture - Beautiful Image Management">
  <Hero />
  <Features />
</Layout>
---
// apps/landing/src/layouts/Layout.astro
interface Props {
  title: string;
}

const { title } = Astro.props;
---

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width" />
    <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
    <title>{title}</title>
  </head>
  <body>
    <slot />
  </body>
</html>
---
// apps/landing/src/components/Hero.astro
---

<section class="hero">
  <h1>Beautiful Image Management</h1>
  <p>Store, organize, and share your images with Picture</p>
  <a href="https://app.picture.app" class="cta-button">
    Get Started
  </a>
</section>

<style>
  .hero {
    text-align: center;
    padding: 4rem 2rem;
  }

  h1 {
    font-size: 3rem;
    font-weight: bold;
    margin-bottom: 1rem;
  }

  p {
    font-size: 1.25rem;
    margin-bottom: 2rem;
  }

  .cta-button {
    display: inline-block;
    padding: 1rem 2rem;
    background: black;
    color: white;
    text-decoration: none;
    border-radius: 0.5rem;
  }
</style>

Development Workflow

Starting all apps

# Root directory
pnpm dev

Starting individual apps

pnpm dev:mobile    # React Native on Expo (Port 8081)
pnpm dev:web       # SvelteKit on http://localhost:5173
pnpm dev:landing   # Astro on http://localhost:4321

Landing Page Details:

  • Framework: Astro 5.x
  • Dev Server: http://localhost:4321
  • Hot Module Replacement (HMR) aktiviert
  • Tailwind CSS integriert
  • Build Output: dist/ (static files)

Building

pnpm build         # Build all
pnpm build:web     # Build web only
pnpm build:landing # Build landing only

Type Checking

pnpm type-check    # Check all

Linting

pnpm lint          # Lint all

Deployment Strategy

Mobile (React Native)

cd apps/mobile
eas build --platform ios --profile production
eas build --platform android --profile production
eas submit --platform ios
eas submit --platform android

Web (SvelteKit)

Cloudflare Pages (empfohlen):

cd apps/web
pnpm build

# Deploy via Cloudflare Pages Dashboard
# Build command: pnpm build
# Output directory: build

Environment Variables:

  • PUBLIC_SUPABASE_URL
  • PUBLIC_SUPABASE_ANON_KEY

Landing (Astro)

Cloudflare Pages:

cd apps/landing
pnpm build

# Deploy via Cloudflare Pages Dashboard
# Build command: pnpm build
# Output directory: dist

Static Site - super einfach:

  • Build einmal
  • Upload zu beliebigem Static Host
  • Kein Server needed

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 .env

# apps/mobile/.env
EXPO_PUBLIC_SUPABASE_URL=$SUPABASE_URL
EXPO_PUBLIC_SUPABASE_ANON_KEY=$SUPABASE_ANON_KEY

# apps/web/.env
PUBLIC_SUPABASE_URL=$SUPABASE_URL
PUBLIC_SUPABASE_ANON_KEY=$SUPABASE_ANON_KEY

# apps/landing/.env
# No env needed (static site)

Code Sharing Matrix

Feature Mobile Web Landing Shared
Supabase Types
API Client
Auth Logic
Image Utils
Validation
UI Components
Design Tokens (optional)

Code Reuse: ~35-40%


Timeline

Phase 1: Monorepo Setup (Tag 1)

  • PNPM Installation
  • Ordnerstruktur erstellen
  • Mobile Code migrieren
  • Workspace Config
  • Test: pnpm dev:mobile funktioniert

Time: ~3h

Phase 2: Shared Package (Tag 1-2)

  • Package erstellen
  • Supabase Types generieren
  • API Client bauen
  • Utils extrahieren
  • Mobile nutzt @picture/shared

Time: ~4h

Phase 3: SvelteKit App (Tag 2-4)

  • App initialisieren
  • Tailwind Setup
  • Supabase Integration
  • Basic Routes (Home, Auth)
  • Images Grid
  • Image Detail
  • Profile Page

Time: ~10h

Phase 4: Astro Landing (Tag 4-5)

  • App initialisieren
  • Tailwind Setup
  • Homepage
  • Features Page
  • Pricing Page
  • About Page

Time: ~6h

Phase 5: Deploy (Tag 5-6)

  • Mobile: EAS Build
  • Web: Cloudflare Pages
  • Landing: Cloudflare Pages
  • Environment Variables
  • Domains Setup

Time: ~4h

Total: 5-6 Tage


Advantages

Maximale Unabhängigkeit

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

Code Reuse

  • ~40% Code geteilt
  • Types einmal definieren
  • API Logic zentral

Optimal für Zweck

  • Mobile: Native Experience
  • Web: Full App mit SSR
  • Landing: Ultraschnell, SEO

Einfache Wartung

  • Clear Separation
  • Shared Logic zentral
  • Easy Updates

Skalierbar

  • Neue Apps einfach hinzufügen
  • Packages wachsen organisch
  • CI/CD pro App

Tech Stack Summary

Mobile (React Native):
  Framework: Expo SDK 54
  Routing: Expo Router
  Styling: NativeWind
  State: Zustand
  Deploy: EAS Build

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

Landing (Astro):
  Framework: Astro 4.x
  Styling: Tailwind CSS
  Components: Astro + optional Svelte
  Deploy: Cloudflare Pages (Static)

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

Next Steps

  1. Planung Complete
  2. ⏭️ PNPM installieren
  3. ⏭️ Ordnerstruktur aufbauen
  4. ⏭️ Mobile Code migrieren
  5. ⏭️ Shared Package erstellen
  6. ⏭️ SvelteKit initialisieren
  7. ⏭️ Astro initialisieren

Bereit? Lass uns mit Phase 1 starten! 🚀

Autor: Claude Code Status: Ready to implement Last Updated: 2025-10-08