A working document for the next few sprints.
Alright, so I went through index.html line by line. It's honestly in good shape — the design tokens hold together, the SM-2 logic is clean, the offline queue actually works, and the command palette is a nice touch. But there's a handful of things that are either broken, missing, or just aren't pulling their weight yet.
This document is my working plan. It's ordered by what I'd actually do first, not by how impressive it sounds on a feature list. Everything here is grounded in the current code — no "rewrite it in React" fantasies.
These are bugs, not features. Some of them are quiet — you won't notice them until something goes wrong in a way that's hard to trace. Do these before adding anything new.
In answerMc(), you save a reference to the card being answered, then call submitReview() after a 550ms timeout. But submitReview reads state.study.cards[state.study.index] again — it doesn't use the card you passed. If the user taps Next during that half-second, the wrong card gets graded. It's rare, but it's the kind of bug that shows up in a bug report six months from now and takes an hour to find.
Fix: change the signature to submitReview(quality, card) and use the passed card everywhere inside. Small change, removes a whole class of weirdness.
When you open the note editor for a new note, the favorite star is still clickable. Clicking it fires toggleFavorite('note', null), which shows "Save this first" — but the star still looks active. Small thing, but it's the kind of tiny friction that makes the app feel less careful than it is.
Fix: disable the star (with a disabled attribute and a subtle opacity change) until state.editingNoteId exists.
This is the weakest part of an otherwise solid feature. Right now it uses:
line.match(/^Q:\s*(.+?)\s*A:\s*(.+)$/i) || line.match(/^(.+?)\s*[-–:]\s+(.+)$/)
The second regex matches timestamps ("12:30"), URLs ("http://..."), hyphenated words ("state-of-the-art"), and any sentence with a colon. It'll silently produce garbage cards. Also, Q: ... A: ... on the same line is rare — most people write them on separate lines.
Replace it with a small state machine that handles:
Q: What is X? A: It is Y. Term - Definition Term: Definition Term | Definition
Skip any line where the "answer" is under three characters, or where there's no clear delimiter. It's maybe 40 lines of code and it's the difference between a feature people use and one they ignore.
You're storing base64 images inside notes.img and inside cards.q (via that IMG_MARK trick). It works, but Supabase has a 1MB row limit by default. A single 1200px JPEG at 0.82 quality is 150–400KB. Two images and you're bumping the ceiling. Also, every list query drags those images across the wire even when you're not rendering them.
Fix: move to Supabase Storage. Create two buckets — note-images and card-images, scoped by user ID. Store only the public URL in the row. This is the single most important change for scaling the app past a few hundred notes.
If you search for something, then click Dashboard in the rail, the search box still has the old query and the library view is still technically populated. It's a small thing, but it makes the app feel sticky in the wrong way.
Fix: clear #searchInput inside showView() whenever the target view isn't library.
If you edit a note online in another tab, then come back to this tab which has an offline edit queued, the offline edit wins without any warning. processOfflineQueue() just fires the update.
At minimum, compare updated_at timestamps before applying. If the server row is newer, drop the local edit and notify. It's not a full CRDT — it's just honest.
Ordered by ROI, not by excitement. Each of these is self-contained enough that you can ship it in a weekend.
This is the one I'd build today. It's small, it's visual, and it reinforces the study loop that the rest of the app is already built around.
recordStudyActivity() so it feeds the streak and the activity chart automatically.It's item #12 on your own list. It's self-contained. And unlike the more ambitious features, it makes the app feel alive on every screen without requiring the user to do anything.
Right now the logo is a serif italic "R" next to the word "ecall". It's fine, but it doesn't read as a logo — it reads as a heading.
--copper: #B87333 token, plus a soft --copper-bg tint.Two related colors, distinct roles. That's what makes a palette feel intentional instead of decorative.
You already have deck colors — gold, sage, brick. Extend the system one level down.
You can store it the same way you store MC data — a hidden suffix in the text field. Or just add a real color column. Either works; the column is cleaner.
Right now every feature is a full-page swap. You can't check your reminders without leaving the deck you're studying.
Build a persistent dock — vertical on the right edge for desktop, horizontal at the bottom for mobile. Five icons: Dashboard, Cards, Notes, Reminders, Pomodoro. Each one opens a slide-over panel (the same pattern you already use for studyPanel) instead of a full view change.
The active icon gets a copper underline. Everything else stays quiet.
Your list says "AI or edge functions." Start with the boring version first — it's 80% of the value for 5% of the effort.
Phase one alone will feel magical. Phase two is a nice-to-have.
A new view. Title, language, body, tags. Syntax highlighting via Shiki (lighter and prettier than Prism). A copy button, line numbers, and a dark code block that actually matches your paper theme.
Link snippets to notes and cards — "See snippet: binary search" from inside a card. Binary files go to Supabase Storage with metadata in a files table.
This is the feature that turns Recall from a study app into a proper second brain.
None of these are features, but all of them are the difference between "nice app" and "app I open every day."
| Touch | Why it matters |
|---|---|
| Italic serif for key words | You already use Newsreader for headings. Use italic serif for the user's name in the greeting, deck names in study, and the "due today" label. It becomes your typographic signature. |
| Subtle paper texture | An SVG noise filter on the --paper background. Makes the app feel physical. |
| Copper-to-gold gradient on the flame | The streak flame is currently flat gold. A gradient makes it feel earned. |
| Undo on delete | Add an Undo button to delete toasts. Queue the delete, apply after 5 seconds. Safety net for misclicks. |
| View Transitions API | Wrap showView() in document.startViewTransition(). One-line change, smooth transitions between views on modern browsers. |
| Haptic on card flip | navigator.vibrate(10) when flipping on mobile. Tiny, but noticeable. |
| Confetti on streak milestones | 7 days, 30 days, 100 days. A one-second canvas burst. Cheap, joyful. |
Keyboard shortcut P | Start or pause the Pomodoro from anywhere. |
Keyboard shortcut F | Toggle favorite on the current note or deck. |
| Command palette additions | "Start Pomodoro," "Toggle dark mode," "Export all data," "Delete all notes." |
This section matters as much as the others. Every feature you add is a feature you have to maintain, and some of them actively dilute the app.
Sharing decks publicly, following other users, leaderboards. All of it pulls attention away from the actual point, which is your own study loop.
You already have a command palette and search. A chat box would be a gimmick. The auto-tagging and "explain this card" features are useful because they're scoped. A general chat interface is not.
Streaks plus Pomodoro stats are enough. Badges and points feel cheap, and they train users to chase the badge instead of the learning.
Your reminders list plus the activity chart already cover the ground a calendar would. Adding one would just be a second way to see the same data.
One paper theme and one dark theme is plenty. Three themes is a design decision that starts to feel like an escape hatch for not committing to one.
Here's how I'd actually sequence this. Roughly a sprint per week, but realistically some weeks will slip and that's fine.
submitReview racesnippets table with Shiki highlightingIf you only do one thing this week: build the Pomodoro timer. It's the smallest change on this list, it's the most visible, and it makes the app feel more finished than any of the bigger features will.
Recall is closer to being done than it looks. The foundations are solid — the auth flow, the offline queue, the SM-2 logic, the design system. What's missing is mostly the layer between "technically works" and "feels like a product you'd recommend to a friend."
The Pomodoro timer, the copper accent, and the logo monogram alone will shift that feeling more than you'd expect. Everything after that is polish and scale.
Start small. Ship one thing this week. The rest can wait.