Skip to main content

High-Level Architecture

Meelio is a fully offline-first productivity application built as a Turborepo monorepo. All data is stored locally in the browser, ensuring privacy and functionality without internet connectivity. Meelio Architecture

Monorepo Structure

Meelio uses Turborepo with pnpm workspaces to manage a monorepo containing multiple apps and shared packages.
meelio/
├── apps/
│   ├── web/              # Vite + React web application (port 4000)
│   └── extension/        # Plasmo browser extension (Chrome, Edge)
├── packages/
│   ├── shared/           # Core business logic, stores, hooks, components
│   ├── ui/               # React component library (shadcn/ui based)
│   ├── logger/           # Logging utility
│   ├── eslint-config/    # Shared ESLint configuration
│   ├── typescript-config/# Shared TypeScript configuration
│   ├── tailwind-config/  # Shared Tailwind CSS configuration
│   └── prettier-config/  # Shared Prettier configuration
├── turbo.json           # Turborepo configuration
├── pnpm-workspace.yaml  # pnpm workspace configuration
└── package.json         # Root package.json
The monorepo structure enables code sharing between the web app and extension while maintaining clear boundaries and independent deployment.

Apps

Web Application

Location: apps/web/ Tech Stack:
  • React 18.3.1 - UI library
  • Vite 5.4.11 - Build tool and dev server
  • React Router 6.16.0 - Client-side routing
  • TypeScript 5.3.3 - Type safety
Key Features:
  • Runs on port 4000 (apps/web/package.json:6)
  • Vite with React SWC plugin for fast refresh
  • PWA support via vite-plugin-pwa
  • Compression and static file handling
Directory Structure:
apps/web/src/
├── components/      # App-specific components
├── layouts/         # Layout components
├── routes/          # Route components
│   ├── home/        # Home route
│   └── errors/      # Error pages
├── stores/          # App-specific stores
├── workers/         # Web workers
├── main.tsx         # Application entry point
└── app.tsx          # Root component

Browser Extension

Location: apps/extension/ Tech Stack:
  • Plasmo 0.90.5 - Extension framework
  • React 18.3.1 - UI library
  • Manifest V3 - Chrome extension manifest version
Supported Browsers:
  • Chrome
  • Edge
  • Firefox
  • Opera
  • Safari
  • Brave
Extension Features:
  • Custom new tab page
  • Site blocker
  • Tab stash functionality
  • Bookmarks management
  • Background workers
Permissions (from apps/extension/package.json:120):
  • storage - Local data storage
  • notifications - Push notifications
  • tabs (optional) - Tab management
  • tabGroups (optional) - Tab grouping
  • bookmarks (optional) - Bookmark access

Shared Packages

@repo/shared

The core package containing all business logic shared between apps. Package Structure (packages/shared/src/):
src/
├── api/                 # API clients and services
├── assets/              # Shared assets (images, icons)
│   └── images/
│       ├── avatars/
│       ├── category-icons/
│       └── sound-icons/
├── components/          # Shared React components
│   ├── common/          # Common UI components
│   └── core/            # Core feature components
│       ├── backgrounds/
│       ├── bookmarks/
│       ├── breathing-pod/
│       ├── calendar/
│       ├── clock/
│       ├── dock/
│       └── greetings/
├── context/             # React contexts
├── data/                # Static data and constants
├── hooks/               # Custom React hooks
├── i18n/                # Internationalization
├── lib/                 # Core libraries
│   ├── db/              # Database layer (Dexie)
│   ├── validations/     # Zod schemas
│   └── utils.ts
├── providers/           # React providers
├── services/            # Business logic services
├── stores/              # Zustand state stores
├── types/               # TypeScript type definitions
└── utils/               # Utility functions
Zustand Stores (packages/shared/src/stores/):
  • app.store.ts - Global app state
  • auth.store.ts - Authentication state
  • background.store.ts - Background management
  • bookmarks.store.ts - Bookmarks state
  • breathing.store.ts - Breathing exercise state
  • calendar.store.ts - Calendar state
  • category.store.ts - Category management
  • dock.store.ts - Dock configuration
  • greetings.store.ts - Greeting messages
  • note.store.ts - Notes management
  • onboarding.store.ts - Onboarding flow
  • quotes.store.ts - Quote of the day
  • search.store.ts - Search functionality
  • settings.store.ts - User settings
  • site-blocker.store.ts - Site blocking rules
  • soundscapes.store.ts - Ambient sounds
  • soundscapes-timer-integration.ts - Sound/timer sync
  • tab-stash.store.ts - Tab stashing
  • task.store.ts - Task management
  • timer.store.ts - Pomodoro timer
The @repo/shared package uses TypeScript path exports (defined in package.json:51-63) to allow importing from specific subdirectories:
import { useTimer } from '@repo/shared/hooks/use-timer'
import { db } from '@repo/shared/lib/db'
import { taskStore } from '@repo/shared/stores/task.store'

@repo/ui

Component library based on shadcn/ui and Radix UI. Core Dependencies:
  • @radix-ui/* - Headless UI primitives
  • lucide-react - Icon library
  • tailwindcss - Styling
  • class-variance-authority - Variant management
  • tailwind-merge - Utility class merging
Exports (packages/ui/package.json:53-63):
import '@repo/ui/globals.css'
import { Button } from '@repo/ui/components/button'
import { cn } from '@repo/ui/lib/utils'

State Management

Zustand

Meelio uses Zustand 5.x for state management across all stores. Why Zustand?
  • Minimal boilerplate
  • TypeScript-first design
  • No provider wrapper needed
  • Easy to integrate with React
  • Excellent DevTools support
Store Pattern:
// Example from packages/shared/src/stores/
import { create } from 'zustand'

interface TimerState {
  timeLeft: number
  isRunning: boolean
  start: () => void
  pause: () => void
  reset: () => void
}

export const useTimerStore = create<TimerState>((set) => ({
  timeLeft: 1500,
  isRunning: false,
  start: () => set({ isRunning: true }),
  pause: () => set({ isRunning: false }),
  reset: () => set({ timeLeft: 1500, isRunning: false })
}))

Data Storage

IndexedDB via Dexie

Meelio uses Dexie 4.0.9 as a wrapper around IndexedDB for efficient local storage. Database Schema (packages/shared/src/lib/db/meelio.dexie.ts):
export class MeelioDB extends Dexie {
  siteBlocker!: Table<SiteBlocker, string>;
  tasks!: Table<Task>;
  focusSessions!: Table<PomodoroSession>;
  focusStats!: Table<DailySummary>;
  categories!: Table<Category, string>;
  sounds!: Table<CachedSound, string>;
  notes!: Table<Note, string>;
  tabStashes!: Table<TabStash, string>;
  bookmarks!: Table<CachedBookmark, string>;
  weather!: Table<CachedWeather, string>;

  constructor() {
    super("meelio");
    // Schema versions defined here
  }
}

export const db = new MeelioDB();
Key Tables:
  • tasks - Todo items with categories, due dates, and completion status
  • focusSessions - Pomodoro session history
  • focusStats - Daily focus statistics
  • notes - User notes with categories
  • sounds - Cached ambient soundscapes for offline playback
  • categories - Custom task/note categories
  • siteBlocker - Blocked website rules
  • tabStashes - Saved tab groups
  • bookmarks - Cached browser bookmarks
  • weather - Cached weather data
Database Utilities:
import { db, resetDatabase } from '@repo/shared/lib/db'

// Reset database (useful for testing)
await resetDatabase()

localStorage

Used for preferences and settings that don’t require complex querying:
  • User preferences
  • Theme settings
  • Onboarding state
  • UI configurations

Offline-First Design

Meelio is designed to work completely offline:
1

Local Data Storage

All user data is stored in IndexedDB and localStorage. No remote database required.
2

Cached Assets

Ambient sounds and backgrounds can be cached locally for offline playback.
3

Service Workers

The web app uses service workers (via vite-plugin-pwa) for offline functionality.
4

No Authentication Required

Core features work without user accounts. Optional sync features may require authentication.
Some features like calendar ICS sync and weather require network connectivity but gracefully degrade when offline.

Technology Choices

Core Stack

React 18.3.1

Modern React with Concurrent Features, Suspense, and automatic batching

TypeScript 5.3.3

Full type safety across the entire codebase

Vite 5.4.11

Lightning-fast dev server and optimized production builds

Tailwind CSS 3.3.3

Utility-first CSS framework with custom configuration

State & Data

  • Zustand 5.x - Lightweight state management
  • Dexie 4.0.9 - IndexedDB wrapper for complex queries
  • React Hook Form 7.47.0 - Form state management
  • Zod 3.22.4+ - Schema validation

UI Components

  • shadcn/ui - Headless component library
  • Radix UI - Accessible primitives
  • Lucide React 0.454.0 - Icon system
  • Framer Motion 11.11.9 - Animations
  • Recharts 2.13.0 - Data visualization

Build & Tooling

  • Turborepo 2.4.2 - Monorepo build system
  • pnpm 9.15.4 - Fast, disk-efficient package manager
  • Plasmo 0.90.5 - Browser extension framework
  • ESLint - Code linting
  • Prettier - Code formatting

Extension-Specific

  • @plasmohq/storage - Cross-browser storage API
  • Chrome APIs - Browser extension APIs

Additional Libraries

  • date-fns 4.1.0 - Date manipulation
  • i18next 23.10.1+ - Internationalization
  • axios 1.5.0 - HTTP client (for optional features)
  • ical.js 2.2.1 - Calendar ICS parsing
  • canvas-confetti 1.9.1 - Celebration animations

Build Pipeline

Turborepo orchestrates the build pipeline defined in turbo.json:
{
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**", "build/**", ".next/**"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    },
    "lint": {
      "dependsOn": ["^build"]
    }
  }
}
Build Order:
  1. Configuration packages (TypeScript, ESLint, Tailwind, Prettier)
  2. Shared packages (@repo/ui, @repo/shared, @repo/logger)
  3. Applications (web, extension)
Turborepo uses content-based hashing to cache build outputs. Only changed packages are rebuilt.

Development Patterns

Component Structure

// Feature component in @repo/shared
import { useState } from 'react'
import { useTimerStore } from '@repo/shared/stores/timer.store'
import { Button } from '@repo/ui/components/button'

export function Timer() {
  const { timeLeft, isRunning, start, pause } = useTimerStore()
  
  return (
    <div>
      <p>{formatTime(timeLeft)}</p>
      <Button onClick={isRunning ? pause : start}>
        {isRunning ? 'Pause' : 'Start'}
      </Button>
    </div>
  )
}

Store Pattern

// Zustand store with TypeScript
import { create } from 'zustand'
import { db } from '@repo/shared/lib/db'

interface TaskState {
  tasks: Task[]
  addTask: (task: Omit<Task, 'id'>) => Promise<void>
  deleteTask: (id: string) => Promise<void>
  loadTasks: () => Promise<void>
}

export const useTaskStore = create<TaskState>((set) => ({
  tasks: [],
  
  addTask: async (task) => {
    const id = await db.tasks.add({ ...task, id: uuid() })
    set((state) => ({ tasks: [...state.tasks, { ...task, id }] }))
  },
  
  deleteTask: async (id) => {
    await db.tasks.delete(id)
    set((state) => ({ tasks: state.tasks.filter(t => t.id !== id) }))
  },
  
  loadTasks: async () => {
    const tasks = await db.tasks.toArray()
    set({ tasks })
  }
}))

Database Queries

// Dexie queries
import { db } from '@repo/shared/lib/db'

// Get all incomplete tasks
const tasks = await db.tasks
  .where('completed')
  .equals(false)
  .sortBy('createdAt')

// Get tasks by category
const workTasks = await db.tasks
  .where('category')
  .equals('work')
  .toArray()

// Update a task
await db.tasks.update(taskId, {
  completed: true,
  updatedAt: Date.now()
})

Next Steps

Setup Guide

Set up your local development environment

Contributing

Learn how to contribute to Meelio

Components Overview

Explore component architecture

Core Features

Learn about core features

Features

Learn about Meelio’s features

Build docs developers (and LLMs) love