···11111. **Import**: User inputs a URL (Article) or pastes text.
12122. **Generate**: System extracts metadata (and optionally snapshots content).
13133. **Authoring**:
1414- * User highlights key sections in the source.
1515- * User creates **Notes** linked to highlights.
1616- * User generates **Cards** (Flashcards) from Notes or directly from source.
1414+ - User highlights key sections in the source.
1515+ - User creates **Notes** linked to highlights.
1616+ - User generates **Cards** (Flashcards) from Notes or directly from source.
17174. **Assembly**: User organizes Cards into a **Deck**.
18185. **Publish**: User sets visibility (e.g., Public) and publishes the Deck.
19196. **Result**: The Deck is now a shareable Artifact (ATProto record).
···74741. **Session Start**: User opens the app/daily study mode.
75752. **Review Queue**: System presents cards due for review based on SRS algorithm (e.g., SM-2).
76763. **Interaction**:
7777- * User sees **Front** of card.
7878- * User attempts recall.
7979- * User reveals **Back**.
7777+ - User sees **Front** of card.
7878+ - User attempts recall.
7979+ - User reveals **Back**.
80804. **Grading**: User self-grades (e.g., 0-5).
81815. **Update**: System schedules next review interval.
82826. **Progress**: User sees feedback (cards done, streak incremented).
8383- * *Note: All grading/progress data is strictly private.*
8383+ - *Note: All grading/progress data is strictly private.*
84848585### Detailed Flows
8686···103103104104#### Progress Tracking
105105106106-* **Due count**: Cards needing review today
106106+- **Due count**: Cards needing review today
107107108108-* **Streak**: Consecutive days studied
109109-* **Reviewed today**: Cards completed this session
110110-* **Interval growth**: SM-2 algorithm increases intervals for mastered cards
108108+- **Streak**: Consecutive days studied
109109+- **Reviewed today**: Cards completed this session
110110+- **Interval growth**: SM-2 algorithm increases intervals for mastered cards
111111112112#### Keyboard Shortcuts
113113···129129### High-Level Workflow
1301301311311. **Discovery**:
132132- * User follows a Curator.
133133- * User sees a new Deck in their "New from Follows" feed.
132132+ - User follows a Curator.
133133+ - User sees a new Deck in their "New from Follows" feed.
1341342. **Acquisition**: User saves/pins the Deck to their library.
1351353. **Contribution (Forking)**:
136136- * User identifies a gap or error in the Deck.
137137- * User **Forks** the Deck.
138138- * User edits cards or adds new ones.
139139- * User republishes the modified Deck (referencing the original).
136136+ - User identifies a gap or error in the Deck.
137137+ - User **Forks** the Deck.
138138+ - User edits cards or adds new ones.
139139+ - User republishes the modified Deck (referencing the original).
1401404. **Loop**: Original author (or others) can see the fork and potentially merge changes (future scope) or users can switch to the better fork.
141141142142## 4. Discussion & Moderation
···1481481. **Context**: A User is viewing a public Card or Deck.
1491492. **Discuss**: User adds a **Comment** (threaded) asking for clarification.
1501503. **Report** (Unhappy Path):
151151- * User encounters abusive content/spam.
152152- * User triggers **Report** flow.
153153- * Moderation system receives report.
154154- * Content may be hidden/labeled based on moderation actions.
151151+ - User encounters abusive content/spam.
152152+ - User triggers **Report** flow.
153153+ - Moderation system receives report.
154154+ - Content may be hidden/labeled based on moderation actions.
155155156156## 5. Lecture Study Workflow
157157···1611611621621. **Import**: User provides a Lecture URL (e.g., YouTube/Video).
1631632. **Structure**:
164164- * User creates an **Outline** of the lecture.
165165- * User adds **Timestamps** to segment the content.
164164+ - User creates an **Outline** of the lecture.
165165+ - User adds **Timestamps** to segment the content.
1661663. **Link**:
167167- * User creates Cards specific to timestamped segments.
168168- * Clicking context on a Card jumps video to the specific timestamp.
167167+ - User creates Cards specific to timestamped segments.
168168+ - Clicking context on a Card jumps video to the specific timestamp.
169169170170## Authentication
171171···1791791801801. Click avatar in header → "Logout"
1811812. → redirected to Landing page
182182+183183+## 6. Onboarding & Personalization
184184+185185+**Goal**: New users get a personalized experience based on their learning goals.
186186+187187+### High-Level Workflow
188188+189189+1. **First Login**: User authenticates for the first time.
190190+2. **Persona Selection**: User sees onboarding dialog with persona options:
191191+ - **Learner**: Focus on studying existing content
192192+ - **Creator**: Focus on building and sharing decks
193193+ - **Curator**: Focus on discovering and organizing content
194194+3. **Personalized Experience**: Empty states and tips adapt to chosen persona.
195195+4. **Progress**: User preferences stored in backend for consistency across sessions.
196196+197197+### Detailed Flows
198198+199199+#### First-Time Onboarding
200200+201201+1. User logs in successfully
202202+2. System fetches preferences from `/api/preferences`
203203+3. If `onboarding_completed_at` is null, show OnboardingDialog
204204+4. User selects persona → Submit
205205+5. Backend stores persona and marks onboarding complete
206206+6. Dialog closes, user sees personalized empty states
207207+208208+#### Persona-Aware Empty States
209209+210210+- **Home (Library)**: Tips and actions tailored to persona
211211+ - Learners: "Browse Discovery" and "Fork decks you like"
212212+ - Creators: "Create New Deck" and "Import from Article"
213213+ - Curators: "View Feed" and "Follow creators"
214214+215215+- **Review**: First-timer guidance explaining SRS for users with no reviews
216216+217217+## 7. Help & Support
218218+219219+**Goal**: Users can find answers to common questions.
220220+221221+### Detailed Flows
222222+223223+#### Accessing Help
224224+225225+1. Footer → "Help" link, or navigate to `/help`
226226+2. View FAQ organized by category:
227227+ - Getting Started
228228+ - Spaced Repetition
229229+ - AT Protocol & Privacy
230230+ - Community & Sharing
231231+3. Click questions to expand accordion answers
232232+233233+#### Beta Notice
234234+235235+- Help page displays prominent notice that Malfestio is in active development
236236+- Links to Bluesky and GitHub for community support
+5-34
docs/todo.md
···11# Product + Technical Roadmap
2233-## Protocol + Lexicon Strategy
44-55-- "Artifacts" are publishable records (ATProto Lexicon).
66-- "Learning state" is private (local DB + your backend sync; not public records).
77-- Records are distributed and hard to migrate globally; keep mutable/private state out.
88-- Lexicon evolution rules strongly encourage forward-compatible extensibility.
99-1010-### Namespace + NSID conventions
1111-1212-- `app.malfestio.note`
1313-- `app.malfestio.card`
1414-- `app.malfestio.deck`
1515-- `app.malfestio.source.article`
1616-- `app.malfestio.source.lecture`
1717-- `app.malfestio.collection`
1818-- `app.malfestio.thread.comment`
1919-2020-### Lexicon basics
2121-2222-- Lexicon defines record types + XRPC endpoints; JSON-schema-like constraints.
2323-- Use "optional fields" heavily; avoid enums that will calcify the product too early.
2424-- Versioning: add fields, don't rename; never rely on being able to rewrite history.
2525-2626-### Schema boundaries (important)
2727-2828-- **Public share layer**:
2929- - decks, cards, notes, collections, comments
3030-- **Private layer**:
3131- - review schedule, lapses, grades, per-card performance, streaks
3232-3333-### Auth direction
33+## Auth direction
344355- ATProto is moving toward OAuth for client↔PDS authorization.
366- Plan for OAuth support even if MVP starts centralized.
···71417242**App Vision Content:**
73437474-- [ ] Onboarding flow with persona selection (Learner/Creator/Curator)
7575-- [ ] Empty states with helpful prompts for new users
4444+- [x] Onboarding flow with persona selection (Learner/Creator/Curator)
4545+- [x] Empty states with helpful prompts for new users
4646+- [x] Help center/FAQ section (with beta development notice)
7647- [ ] Tutorial/walkthrough for first deck creation
7777-- [ ] Help center or FAQ section -> Should mention that the app is still in development and subject to change.
78487949**SEO & Meta:**
8050···1239312494- [ ] OAuth login directly to user's PDS (vs. local-only auth)
12595- [ ] Handle resolution via DNS TXT or `/.well-known/atproto-did`
9696+ - <https://malfestio.stormlightlabs.org>
12697- [ ] DPoP token binding for secure API calls
1279812899**Sync & Conflict Resolution:**
+30
lexicons/README.md
···2233This directory contains the Lexicon definitions for the malfestio's public records.
4455+## Protocol + Lexicon Strategy
66+77+- "Artifacts" are publishable records (ATProto Lexicon).
88+- "Learning state" is private (local DB + your backend sync; not public records).
99+- Records are distributed and hard to migrate globally; keep mutable/private state out.
1010+- Lexicon evolution rules strongly encourage forward-compatible extensibility.
1111+1212+### Namespace + NSID conventions
1313+1414+- `app.malfestio.note`
1515+- `app.malfestio.card`
1616+- `app.malfestio.deck`
1717+- `app.malfestio.source.article`
1818+- `app.malfestio.source.lecture`
1919+- `app.malfestio.collection`
2020+- `app.malfestio.thread.comment`
2121+2222+### Lexicon basics
2323+2424+- Lexicon defines record types + XRPC endpoints; JSON-schema-like constraints.
2525+- Use "optional fields" heavily; avoid enums that will calcify the product too early.
2626+- Versioning: add fields, don't rename; never rely on being able to rewrite history.
2727+2828+### Schema boundaries (important)
2929+3030+- **Public share layer**:
3131+ - decks, cards, notes, collections, comments
3232+- **Private layer**:
3333+ - review schedule, lapses, grades, per-card performance, streaks
3434+535## Evolution Rules
6367371. **Additive Changes Only**: You can add new optional fields to existing records.
+17
migrations/008_2025_12_30_user_prefs.sql
···11+-- User preferences for onboarding and personalization
22+-- Tracks onboarding completion and user persona selection
33+44+CREATE TABLE user_prefs (
55+ id UUID PRIMARY KEY,
66+ user_did TEXT NOT NULL UNIQUE,
77+ persona TEXT, -- 'learner' | 'creator' | 'curator' | NULL
88+ onboarding_completed_at TIMESTAMPTZ,
99+ tutorial_deck_completed BOOLEAN NOT NULL DEFAULT FALSE,
1010+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
1111+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
1212+);
1313+1414+CREATE INDEX idx_user_prefs_did ON user_prefs(user_did);
1515+1616+CREATE TRIGGER update_user_prefs_updated_at BEFORE UPDATE ON user_prefs
1717+ FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
+26-2
web/src/App.tsx
···11import { AppLayout } from "$components/layout/AppLayout";
22-import { authStore } from "$lib/store";
22+import { OnboardingDialog } from "$components/OnboardingDialog";
33+import type { Persona } from "$lib/model";
44+import { authStore, preferencesStore } from "$lib/store";
35import About from "$pages/About";
46import DeckNew from "$pages/DeckNew";
57import DeckView from "$pages/DeckView";
68import Discovery from "$pages/Discovery";
79import Feed from "$pages/Feed";
1010+import Help from "$pages/Help";
811import Home from "$pages/Home";
912import Import from "$pages/Import";
1013import Landing from "$pages/Landing";
···1619import Search from "$pages/Search";
1720import { Route, Router } from "@solidjs/router";
1821import type { Component } from "solid-js";
1919-import { Show } from "solid-js";
2222+import { createEffect, createSignal, onMount, Show } from "solid-js";
20232124const ProtectedRoute: Component<{ component: Component }> = (props) => {
2525+ const [showOnboarding, setShowOnboarding] = createSignal(false);
2626+2727+ onMount(async () => {
2828+ if (authStore.isAuthenticated()) {
2929+ await preferencesStore.fetchPreferences();
3030+ }
3131+ });
3232+3333+ createEffect(() => {
3434+ if (preferencesStore.needsOnboarding()) {
3535+ setShowOnboarding(true);
3636+ }
3737+ });
3838+3939+ const handleOnboardingComplete = (_persona: Persona) => {
4040+ setShowOnboarding(false);
4141+ preferencesStore.fetchPreferences();
4242+ };
4343+2244 return (
2345 <Show when={authStore.isAuthenticated()} fallback={<Landing />}>
2446 <AppLayout>
2547 <props.component />
2648 </AppLayout>
4949+ <OnboardingDialog open={showOnboarding()} onComplete={handleOnboardingComplete} />
2750 </Show>
2851 );
2952};
···3356 <Router>
3457 <Route path="/login" component={Login} />
3558 <Route path="/about" component={About} />
5959+ <Route path="/help" component={Help} />
3660 <Route path="/" component={() => <ProtectedRoute component={Home} />} />
3761 <Route path="/decks" component={() => <ProtectedRoute component={Home} />} />
3862 <Route path="/decks/new" component={() => <ProtectedRoute component={DeckNew} />} />