An AI-powered learning companion that generates structured learning paths from any topic, with curated articles, videos, an interactive study assistant, and cloud sync.
- AI Learning Paths — Enter any topic and get a structured curriculum with modules, estimated time, and difficulty rating
- Curated Resources — Each module is automatically populated with relevant articles and videos
- Knowledge Check — AI-generated quizzes (multiple choice, true/false, fill-in-the-blank) with instant feedback, explanations, and score tracking. Quiz results are cached per module so they load instantly on repeat visits. Trophy badge on module cards for scores ≥ 80%
- Study Assistant — Context-aware AI tutor for each module with per-module conversation threads and suggested follow-ups
- Progress Tracking — Mark modules complete with a visual progress bar
- Shareable Paths — Share any learning path via a public link; others can clone it to their own account
- Google Auth + Cloud Sync — Sign in with Google; all paths, progress, and quiz attempts are saved to Supabase
- Source Viewer — Read articles and watch videos in-app with full-screen modals
- Export — Download sessions as Markdown files
- Dark Mode — Light, dark, and system theme support
- Resizable Layout — Drag the study assistant panel to your preferred width
| Layer | Technology |
|---|---|
| Framework | React 19 + TypeScript + Vite |
| Styling | Tailwind CSS v3 + shadcn/ui |
| State | Zustand (Supabase is the persistence layer — no localStorage) |
| Backend | Supabase (Auth, PostgreSQL, RLS) |
| Routing | React Router v7 |
| AI | Claude API (learning path generation + study assistant) |
| Animations | Framer Motion |
| Deployment | Vercel + Vercel Analytics |
git clone https://github.com/phamthanhhang208/study-flow.git
cd study-flow
npm installCreate a .env file at the project root:
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_ANON_KEY=your-anon-keynpm run devOpen http://localhost:5173.
npm run buildRun the following SQL in your Supabase SQL editor (in order).
learning_paths
create table learning_paths (
id uuid primary key default gen_random_uuid(),
user_id uuid references auth.users(id) on delete cascade not null,
topic text not null,
title text,
overview text,
difficulty text,
estimated_total_minutes integer,
created_at timestamptz default now(),
updated_at timestamptz default now()
);
alter table learning_paths enable row level security;
create policy "Users own their paths"
on learning_paths for all
using (auth.uid() = user_id)
with check (auth.uid() = user_id);
create policy "Public can read shared paths"
on learning_paths for select
using (id in (select path_id from shared_paths));modules
create table modules (
id uuid primary key default gen_random_uuid(),
path_id uuid references learning_paths(id) on delete cascade not null,
title text not null,
description text,
order_index integer not null,
estimated_minutes integer,
search_query text,
difficulty text,
module_status text default 'complete',
resources jsonb,
created_at timestamptz default now()
);
alter table modules enable row level security;
create policy "Users own their modules"
on modules for all
using (path_id in (select id from learning_paths where user_id = auth.uid()))
with check (path_id in (select id from learning_paths where user_id = auth.uid()));
create policy "Public can view modules of shared paths"
on modules for select
using (path_id in (select path_id from shared_paths));module_progress
create table module_progress (
id uuid primary key default gen_random_uuid(),
user_id uuid references auth.users(id) on delete cascade not null,
module_id uuid references modules(id) on delete cascade not null,
completed_at timestamptz,
unique(user_id, module_id)
);
alter table module_progress enable row level security;
create policy "Users own their progress"
on module_progress for all
using (auth.uid() = user_id)
with check (auth.uid() = user_id);shared_paths
create table shared_paths (
id uuid primary key default gen_random_uuid(),
path_id uuid references learning_paths(id) on delete cascade not null,
user_id uuid references auth.users(id) not null,
slug text unique not null default substr(md5(random()::text), 1, 10),
created_at timestamptz default now()
);
alter table shared_paths enable row level security;
-- Unconditional SELECT is required to avoid RLS recursion with learning_paths
create policy "Public can read shared_paths"
on shared_paths for select
using (true);
create policy "Owner manages shared paths"
on shared_paths for all
using (user_id = auth.uid())
with check (user_id = auth.uid());module_quizzes
-- Cached quiz questions per module, shared across all users
create table module_quizzes (
id uuid default gen_random_uuid() primary key,
module_id uuid references modules(id) on delete cascade,
questions jsonb not null,
created_at timestamptz default now(),
unique(module_id)
);
alter table module_quizzes enable row level security;
create policy "Anyone can read quizzes"
on module_quizzes for select using (true);
create policy "Service role can manage quizzes"
on module_quizzes for all using (true);quiz_attempts
-- Each user's quiz attempts and scores per module
create table quiz_attempts (
id uuid default gen_random_uuid() primary key,
user_id uuid references auth.users(id) on delete cascade,
module_id uuid references modules(id) on delete cascade,
score integer not null,
total integer not null,
answers jsonb not null,
completed_at timestamptz default now()
);
alter table quiz_attempts enable row level security;
create policy "Users own their attempts"
on quiz_attempts for all using (auth.uid() = user_id);- In Supabase → Authentication → Providers, enable Google
- Add credentials from Google Cloud Console
- Add your site URL to the allowed redirect URLs in Supabase
src/
├── components/
│ ├── auth/ # LoginPage (Google OAuth gate)
│ ├── layout/ # Header, Sidebar, InputBar, WelcomeScreen
│ ├── learning/ # ModuleNavHorizontal, ContentTabs, KnowledgeCheck,
│ │ # StudyAssistant, OrchestrationProgress
│ ├── modals/ # SourceReaderModal, VideoPlayerModal, KeyboardShortcutsModal
│ ├── ui/ # shadcn/ui primitives
│ ├── ErrorBoundary.tsx
│ └── SettingsPage.tsx
├── context/
│ └── AuthContext.tsx
├── hooks/
│ ├── useStudyFlow.ts # Main app hook
│ ├── useAgentOrchestration.ts # Topic + question orchestration
│ └── useKeyboardShortcuts.ts
├── lib/
│ ├── api/ # Claude agents (moduleGenerator, tutorAgent, quizGenerator, types)
│ ├── db/ # Supabase data layer (learningPaths, moduleProgress, sharedPaths)
│ ├── store/ # Zustand stores (studyStore, settingsStore)
│ └── utils/
├── pages/
│ └── ShareView.tsx # Public read-only shared path + clone flow
└── App.tsx
| Shortcut | Action |
|---|---|
/ |
Focus input |
N |
New topic |
E |
Export session |
? |
Toggle shortcuts help |
Escape |
Close modals |
MIT