managarten/apps/mana
Till JS b4dd646fd7 feat(memoro): auto-generate voice memo titles via the LLM task queue
First real-world consumer of the @mana/shared-llm tier framework.
After STT transcription completes for a voice memo, the memos store
fire-and-forgets a generateTitleTask into the persistent task queue
with refType:'memo' + refId:memoId. A module-side watcher subscribed
via Dexie liveQuery to completed task rows writes the result back
into memo.title and deletes the queue row to mark it consumed.

What this commit ships:

  apps/mana/apps/web/src/lib/llm-tasks/generate-title.ts
    - generateTitleTask: minTier='none', contentClass='personal'
    - runLlm: sends a German system prompt asking for a 3-7 word
      title, defensive cleanup of any quotes/markdown the model
      might leak through despite the prompt
    - runRules: takes the first sentence (split on .!?\n), caps
      at maxWords/60-chars, returns a non-empty fallback string.
      Predictable and free, works on every device including the
      ones where the user has opted out of all LLM tiers.

  apps/mana/apps/web/src/lib/llm-task-registry.ts
    - Register generateTitleTask alongside extractDate + summarize
      so the queue processor can resolve the name back to the
      task object after a row is pulled from the persistent table.

  apps/mana/apps/web/src/lib/modules/memoro/stores/memos.svelte.ts
    - After transcribeMemo successfully writes the transcript +
      processingStatus:'completed', enqueue a generateTitleTask
      tagged with refType:'memo' + refId + priority:1. Skips the
      enqueue if the memo already has a non-empty title (so
      manually-titled memos aren't overwritten on re-transcription)
      or if the transcript came back empty.
    - Wrapped in try/catch — queue failures must NEVER break the
      transcription happy path.

  apps/mana/apps/web/src/lib/modules/memoro/llm-watcher.svelte.ts
    - startMemoroLlmWatcher() / stopMemoroLlmWatcher()
    - Subscribes via Dexie liveQuery to llmQueueDb.tasks rows
      where state='done', taskName='common.generateTitle',
      refType='memo'. For each row:
        1. Skip + delete row if result isn't a string (defensive)
        2. Skip + delete row if memo no longer exists (deleted
           between enqueue and result)
        3. Skip + delete row if memo already has a manual title
           (user typed one during the LLM round-trip)
        4. Otherwise: encryptRecord + memoTable.update with
           { title: result, updatedAt: now }, then delete the
           queue row to mark it consumed.
    - Module-scope subscription handle, idempotent start/stop.

  apps/mana/apps/web/src/routes/(app)/+layout.svelte
    - startMemoroLlmWatcher() in handleAuthReady's Phase A right
      after startLlmQueue(). The watcher needs to run regardless
      of whether the user is currently on /memoro — a memo
      transcribing in the background should auto-title even
      while the user is doing something else.
    - stopMemoroLlmWatcher() in onDestroy alongside stopLlmQueue().

End-to-end flow with a Tier 0 user (no AI enabled):

  1. User records a memo via voice capture
  2. memos.svelte.ts createWithTranscription() inserts the memo
     with processingStatus:'processing'
  3. transcribeMemo POSTs the audio to mana-stt, awaits the
     transcript
  4. Successful transcript → memos.svelte.ts writes
     { transcript, processingStatus:'completed' } to memoTable
  5. Same function enqueues generateTitleTask with the transcript
  6. LlmTaskQueue processor picks it up (the queue is running in
     the background since layout init), calls
     orchestrator.run(generateTitleTask, { text: transcript })
  7. Orchestrator: Tier 0 user → no LLM tier → falls through to
     runRules() which returns the first-sentence heuristic
  8. Queue marks the row done with the rules-tier title string
  9. Memoro watcher's liveQuery fires with the new completed row
  10. Watcher writes title + deletes the queue row
  11. ListView's existing useLiveQuery on memoTable picks up the
      title change automatically

End-to-end flow with a Browser-tier user:

  Steps 1-6 identical, then:
  7. Orchestrator: browser tier ready → calls
     generateTitleTask.runLlm with the BrowserBackend
  8. Web Worker (Phase 3) runs Gemma 4 E2B against a 32-token
     budget, returns a 3-7 word German title
  9-11. Same as Tier 0 — the title lands in memo.title without
        the user clicking anything

This is the validation the entire 4-phase architecture was built
for: a module-side auto-feature that's completely tier-agnostic,
fire-and-forget, persistent across reloads, and that gracefully
degrades from Gemma 4 down to a regex when the user has opted out.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 11:55:26 +02:00
..
apps feat(memoro): auto-generate voice memo titles via the LLM task queue 2026-04-09 11:55:26 +02:00
.gitignore feat: rename ManaCore to Mana across entire codebase 2026-04-05 20:00:13 +02:00
CLAUDE.md docs: trim CLAUDE.md files — remove stale + duplicated guidance 2026-04-08 11:59:51 +02:00
README.md chore: complete ManaCore → Mana rename (docs, go modules, plists, images) 2026-04-07 12:26:10 +02:00

Mana Apps

A unified application ecosystem built on a shared authentication system, supporting multiple branded applications across web and mobile platforms.

Overview

Mana Apps is a monorepo containing web and mobile applications that provide organization management, team collaboration, and credit transfer capabilities. The system supports multiple branded applications (Memoro, Cards, Storyteller, Mana) through a flexible multi-tenant architecture.

Applications

  • Web App (apps/web) - SvelteKit-based web application
  • Mobile App (apps/mobile) - React Native (Expo) app for iOS, Android, and web
  • Landing (apps/landing) - Landing page (planned)

Features

  • 🔐 Unified authentication with Supabase
  • 🏢 Organization management with role-based access
  • 👥 Team collaboration and member management
  • 💰 Mana credit system with transfers and balance tracking
  • 🎨 Multi-brand support with configurable themes
  • 📱 Cross-platform (Web, iOS, Android)
  • 🔄 Real-time updates across all platforms
  • 🧪 Comprehensive testing with Vitest and Playwright

Quick Start

Prerequisites

  • Node.js 20+
  • pnpm (for web app)
  • npm (for mobile app)
  • Supabase account with project configured
  • Expo CLI (for mobile development)

Setup

  1. Clone the repository

    git clone <repository-url>
    cd mana-core-apps
    
  2. Web App Setup

    cd apps/web
    pnpm install
    cp .env.example .env
    # Edit .env with your Supabase credentials
    pnpm dev
    
  3. Mobile App Setup

    cd apps/mobile
    npm install
    cp .env.example .env
    # Edit .env with your Supabase credentials
    npm start
    

Project Structure

mana-core-apps/
├── apps/
│   ├── web/                    # SvelteKit web application
│   │   ├── src/
│   │   │   ├── routes/        # File-based routing
│   │   │   │   ├── (auth)/    # Public auth pages
│   │   │   │   └── (app)/     # Protected pages
│   │   │   ├── lib/
│   │   │   │   ├── components/
│   │   │   │   ├── config/    # Multi-app configuration
│   │   │   │   ├── server/    # Server-only utilities
│   │   │   │   └── types/
│   │   │   └── hooks.server.ts # Auth middleware
│   │   └── package.json
│   │
│   ├── mobile/                 # React Native (Expo) app
│   │   ├── app/               # File-based routing (Expo Router)
│   │   │   ├── (drawer)/      # Drawer navigation
│   │   │   ├── auth/          # Auth screens
│   │   │   └── _layout.tsx    # Root layout with auth
│   │   ├── components/        # React components
│   │   ├── utils/            # Utilities (Supabase, storage)
│   │   └── package.json
│   │
│   └── landing/               # Landing page (planned)
│
├── CLAUDE.md                  # Developer documentation
└── README.md                  # This file

Technology Stack

Web App (apps/web)

Category Technology
Framework SvelteKit 2 with Svelte 5 (Runes)
Language TypeScript
Styling TailwindCSS 3 with PostCSS
Database Supabase (PostgreSQL)
Auth Supabase Auth with SSR
Testing Vitest (unit) + Playwright (E2E)
Build Tool Vite

Mobile App (apps/mobile)

Category Technology
Framework Expo 52 with React Native 0.76
Language TypeScript
Routing Expo Router 4 (file-based)
Styling NativeWind (TailwindCSS for RN)
Navigation React Navigation (drawer, tabs)
Database Supabase
Build EAS Build
Platforms iOS, Android, Web

Development

Web App Commands

cd apps/web

# Development
pnpm dev                # Start dev server (http://localhost:5173)
pnpm build              # Build for production
pnpm preview            # Preview production build

# Code Quality
pnpm check              # Type-check with svelte-check
pnpm check:watch        # Type-check in watch mode
pnpm lint               # Check formatting and lint
pnpm format             # Format code with Prettier

# Testing
pnpm test               # Run unit tests (Vitest)
pnpm test:ui            # Run tests with UI
pnpm test:e2e           # Run E2E tests (Playwright)

Mobile App Commands

cd apps/mobile

# Development
npm start               # Start Expo dev server
npm run ios             # Run on iOS simulator
npm run android         # Run on Android emulator
npm run web             # Run web version (http://localhost:19006)

# Building
npm run build:dev       # Build dev client
npm run build:preview   # Build for internal testing
npm run build:prod      # Build for production

# Code Quality
npm run lint            # Lint and check formatting
npm run format          # Fix linting and format code

# Setup
npm run prebuild        # Generate native projects

Environment Configuration

Both apps require Supabase configuration. Create .env files based on .env.example:

Web App (apps/web/.env)

PUBLIC_SUPABASE_URL=your_supabase_project_url
PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
MIDDLEWARE_URL=https://mana-middleware-111768794939.europe-west3.run.app
PUBLIC_APP_NAME=Mana Web
NODE_ENV=development

Mobile App (apps/mobile/.env)

EXPO_PUBLIC_SUPABASE_URL=your_supabase_project_url
EXPO_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key

Architecture

Multi-Tenant System

The system supports multiple branded applications sharing the same authentication backend:

  • Memoro - Voice recordings and memory management
  • Cards - AI-powered flashcard learning
  • Storyteller - Creative writing with AI assistance
  • Mana - Central account and organization management

App configurations are centralized in apps/web/src/lib/config/apps.ts, defining branding, features, and routing for each application.

Authentication Flow

Web (SvelteKit):

  1. Server-side authentication using @supabase/ssr
  2. Middleware in hooks.server.ts handles session validation
  3. Protected routes in (app) group require authentication
  4. JWT validation via safeGetSession() before allowing access

Mobile (Expo):

  1. Client-side authentication using @supabase/supabase-js
  2. Custom memory storage for session persistence
  3. AuthProvider in app/_layout.tsx manages auth state
  4. Automatic navigation based on authentication status

Database Schema

Key tables:

  • users - User profiles (linked via auth_id to Supabase Auth)
  • organizations - Organization entities
  • user_roles - User-organization relationships with roles
  • teams - Team entities within organizations
  • team_members - User-team memberships
  • credit_transactions - Mana credit transfer history

See CLAUDE.md for detailed architecture documentation.

Testing

Web App

cd apps/web

# Unit tests
pnpm test              # Run all tests
pnpm test:ui           # Open Vitest UI

# E2E tests
pnpm test:e2e          # Run Playwright tests
pnpm test:e2e --ui     # Run with Playwright UI

Mobile App

Mobile testing is primarily done through Expo Go or development builds:

cd apps/mobile
npm start              # Start dev server
# Then press 'i' for iOS or 'a' for Android

Deployment

Web App

Vercel (Recommended):

cd apps/web
vercel

Netlify:

cd apps/web
netlify deploy

Mobile App

iOS and Android (via EAS):

cd apps/mobile

# Preview build (internal testing)
npm run build:preview

# Production build
npm run build:prod

Configure EAS in eas.json with your build profiles.

Contributing

  1. Create a feature branch from main
  2. Make your changes
  3. Run linting and tests
  4. Submit a pull request

Code Style

  • Use TypeScript for type safety
  • Follow ESLint and Prettier configurations
  • Write tests for new features
  • Use conventional commit messages

Documentation

  • CLAUDE.md - Comprehensive developer guide for Claude Code
  • apps/web/README.md - Web-specific documentation
  • Individual component documentation in source files

Support

For questions or issues, please contact the development team or open an issue in the repository.

License

Private - All rights reserved