{"id":48084354,"url":"https://github.com/andresjmorales/cognote","last_synced_at":"2026-04-04T15:06:10.299Z","repository":{"id":341686341,"uuid":"1171032605","full_name":"andresjmorales/cognote","owner":"andresjmorales","description":"CogNote: Open Source Music Note Practice Tool 🎹","archived":false,"fork":false,"pushed_at":"2026-03-23T17:03:28.000Z","size":1807,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-24T14:06:58.084Z","etag":null,"topics":["flashcards","key","memorization","musical","notation","quiz","signature"],"latest_commit_sha":null,"homepage":"https://cognote.fun","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/andresjmorales.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"buy_me_a_coffee":"andresjmorales"}},"created_at":"2026-03-02T19:55:35.000Z","updated_at":"2026-03-23T17:03:32.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/andresjmorales/cognote","commit_stats":null,"previous_names":["andresjmorales/cognote"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/andresjmorales/cognote","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andresjmorales%2Fcognote","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andresjmorales%2Fcognote/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andresjmorales%2Fcognote/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andresjmorales%2Fcognote/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/andresjmorales","download_url":"https://codeload.github.com/andresjmorales/cognote/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andresjmorales%2Fcognote/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31403952,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T10:20:44.708Z","status":"ssl_error","status_checked_at":"2026-04-04T10:20:06.846Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["flashcards","key","memorization","musical","notation","quiz","signature"],"created_at":"2026-04-04T15:06:09.859Z","updated_at":"2026-04-04T15:06:10.291Z","avatar_url":"https://github.com/andresjmorales.png","language":"TypeScript","funding_links":["https://buymeacoffee.com/andresjmorales"],"categories":[],"sub_categories":[],"readme":"# CogNote\n\n**Music note memorization for piano students — quizzes, flashcards, and spaced repetition.**\n\nCogNote is an open-source web app that helps piano teachers assign and track music exercises for their students. Teachers create customizable lesson plans — note identification drills, musical symbol quizzes, or both — share them via unique URLs, and monitor progress through an analytics dashboard. Students — primarily children ages 5–14 — open a link and immediately start practicing. No login, no signup.\n\n---\n\n## Features\n\n### For Teachers\n- **Student management** — Add students, track their practice history, and see per-note accuracy breakdowns\n- **Customizable lesson plans** — Two plan types: note identification (clef, key signature, specific notes) and musical symbols \u0026 concepts (dynamics, tempo, articulation, note values, and more)\n- **Reusable templates** — Create lesson plan templates and assign them to multiple students with one click\n- **Share via URL** — Each student gets a unique practice link; copy it to clipboard and send to parents\n- **Analytics dashboard** — See which notes students struggle with, session history, accuracy trends\n\n### For Students\n- **Quiz mode** — Multiple-choice note identification or symbol/concept questions with immediate feedback and score tracking\n- **Free practice** — Unlimited questions with no pressure, practice at your own pace\n- **Flashcard mode** — Spaced repetition (SM-2 algorithm) for both notes and symbols, with kid-friendly emoji ratings\n- **Zero friction** — Open the link, tap \"Start\", begin practicing. No account needed\n- **Kid-friendly UI** — Large buttons, friendly fonts, gentle feedback animations\n\n### Music Notation\n- Real staff rendering with [VexFlow](https://www.vexflow.com/) — treble and bass clefs, key signatures, accidentals, ledger lines\n- Clean, large notation sized for tablet screens\n- **Standalone symbol SVGs** — Clefs, notes, rests, dynamics, articulations, and more are rendered from pre-extracted Bravura (SMuFL) vector paths in `public/symbols/`. No runtime font loading; consistent on all devices including iOS (no “tofu” from missing Unicode music fonts).\n- Built-in library of 40+ musical symbols and concepts across 7 categories\n\n---\n\n## Spaced Repetition\n\nThe flashcard mode uses the **SM-2 algorithm** (same as Anki) and works for both note identification and symbol/concept plans. Students rate each card with kid-friendly labels:\n\n| Button | SM-2 Rating | Effect |\n|--------|------------|--------|\n| 😕 No clue | 1 (Again) | Card resets, shown again this session |\n| 🤔 Tricky | 2 (Hard) | Card resets, shown again this session |\n| 👍 Got it! | 4 (Good) | Card graduates, next review in 1–N days |\n| ⭐ Too easy! | 5 (Easy) | Card graduates, interval grows faster |\n\nSee [notes/spaced-repetition.md](notes/spaced-repetition.md) for full algorithm documentation.\n\n---\n\n## Tech Stack\n\n| Layer | Technology |\n|-------|-----------|\n| Framework | [Next.js 16](https://nextjs.org/) (App Router, Turbopack) |\n| Language | TypeScript |\n| Styling | Tailwind CSS 4 |\n| Music Notation | VexFlow 5 |\n| Database | [Supabase](https://supabase.com/) (PostgreSQL + Auth + Row Level Security) |\n| Hosting | Vercel (recommended) |\n\n---\n\n## Getting Started\n\n### Prerequisites\n\n- **Node.js 18+**\n- **Docker Desktop** — required by the Supabase CLI to run PostgreSQL, Auth, and the REST API locally\n\n### Setup\n\n```bash\n# Clone the repo\ngit clone https://github.com/youruser/cognote.git\ncd cognote\n\n# Install dependencies\nnpm install\n\n# Start local Supabase (pulls Docker images on first run — takes a few minutes)\nnpx supabase start\n\n# Apply database migrations and seed with test data\nnpx supabase db reset\n\n# Create your environment file\ncp .env.example .env.local\n```\n\nAfter `npx supabase start`, you'll see output with your local credentials. Update `.env.local` with the **Publishable** key (as `NEXT_PUBLIC_SUPABASE_ANON_KEY`) and the **Secret** key (as `SUPABASE_SERVICE_ROLE_KEY`). Generate a token encryption key:\n\n```bash\nnode -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\"\n```\n\nYour `.env.local` should look like:\n\n```\nNEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321\nNEXT_PUBLIC_SUPABASE_ANON_KEY=\u003cpublishable key from supabase start\u003e\nSUPABASE_SERVICE_ROLE_KEY=\u003csecret key from supabase start\u003e\nTOKEN_ENCRYPTION_KEY=\u003c64-char hex string you generated\u003e\n```\n\nThen start the dev server:\n\n```bash\nnpm run dev\n```\n\nOpen [http://localhost:3000](http://localhost:3000).\n\n### Seed Data (Local Development Only)\n\nRunning `npx supabase db reset` loads `supabase/seed.sql` with a test teacher account, sample students, lesson plans, and practice history so you can explore the full dashboard immediately. See `seed.sql` for credentials — these are for **local development only** and should never be used in production.\n\n### Clearing students and plans (e.g. production reset)\n\nTo wipe all students and lesson plans (and their assignments, sessions, and progress) while keeping teachers and auth intact, run `supabase/clear-data.sql` in the [Supabase SQL Editor](https://supabase.com/dashboard) or via:\n\n```bash\npsql $DATABASE_URL -f supabase/clear-data.sql\n```\n\nAfter clearing, **production** is ready for a fresh start. For **local dev**, run `npx supabase db reset` to reapply migrations and seed sample data.\n\n### Local Dev Tools\n\n| Tool | URL |\n|------|-----|\n| App | [http://localhost:3000](http://localhost:3000) |\n| Supabase Studio | [http://127.0.0.1:54323](http://127.0.0.1:54323) |\n| Mailpit (email) | [http://127.0.0.1:54324](http://127.0.0.1:54324) |\n\n---\n\n## Project Structure\n\n```\ncognote/\n├── app/\n│   ├── (teacher)/              # Teacher pages (auth required)\n│   │   ├── dashboard/          # Overview with stats and recent activity\n│   │   ├── students/           # Student list + detail views\n│   │   │   └── [id]/           # Per-student analytics\n│   │   └── plans/              # Lesson plan list, editor, detail views\n│   │       ├── [id]/\n│   │       └── new/\n│   ├── (student)/              # Student pages (no auth)\n│   │   └── practice/\n│   │       └── [token]/        # Quiz, free practice, flashcard modes\n│   ├── api/                    # API routes\n│   │   ├── auth/               # Teacher account setup\n│   │   ├── dashboard/          # Dashboard summary\n│   │   ├── plans/              # Lesson plan CRUD + assignment\n│   │   ├── practice/           # Student session + attempt tracking\n│   │   └── students/           # Student CRUD + analytics\n│   ├── login/                  # Teacher login/signup\n│   └── page.tsx                # Landing page\n├── components/\n│   ├── music/                  # VexFlow renderer, quiz engine, flashcards\n│   ├── teacher/                # Teacher-specific components\n│   └── ui/                     # Shared primitives (Button, Card, etc.)\n├── lib/\n│   ├── supabase/               # Client, server, and middleware helpers\n│   ├── music.ts                # Note utilities, answer generation, presets\n│   ├── symbol-paths.ts         # Auto-generated: Bravura SVG path data (do not edit)\n│   ├── symbols.ts              # Musical symbols \u0026 concepts library\n│   ├── srs.ts                  # SM-2 spaced repetition algorithm\n│   └── token.ts                # AES-256-GCM token encryption\n├── public/\n│   └── symbols/                # Standalone SVG files (Bravura glyphs)\n├── scripts/\n│   └── extract-bravura-glyphs.js  # One-time: extract Bravura → SVG + lib/symbol-paths.ts\n├── supabase/\n│   ├── migrations/             # SQL schema migrations\n│   ├── seed.sql                # Test data for local development\n│   └── clear-data.sql          # Wipe students \u0026 plans (prod-safe; teachers unchanged)\n└── notes/                      # Design docs and specs\n```\n\n---\n\n## Database Schema\n\n```\nteachers\n  ├── students (one-to-many)\n  ├── plans (one-to-many)\n  └── student_plans (students ↔ plans, many-to-many)\n        └── practice_sessions (one-to-many)\n        │     └── note_attempts (one-to-many)\n        └── flashcard_progress (one-to-many, per note or symbol)\n```\n\nAll teacher data is protected by **Row Level Security** — a teacher can only see their own students, lesson plans, and analytics. Student practice endpoints use token-based access with no authentication.\n\nMigrations live in `supabase/migrations/` and are applied with `npx supabase db reset` (local) or `npx supabase db push` (remote).\n\n**Local:** To apply new migrations (e.g. after pulling changes that add columns), run `npx supabase db reset`. This reapplies all migrations and refreshes the schema cache; it also re-runs the seed, so you’ll get fresh test data. If you see errors like “Could not find column X in the schema cache”, the DB is out of date — run `npx supabase db reset` (with Docker and `npx supabase start` running).\n\n---\n\n## API Routes\n\n### Teacher-side (authenticated)\n\n| Method | Route | Purpose |\n|--------|-------|---------|\n| GET | `/api/students` | List teacher's students |\n| POST | `/api/students` | Create student |\n| PUT | `/api/students/[id]` | Update student |\n| DELETE | `/api/students/[id]` | Remove student |\n| GET | `/api/students/[id]/analytics` | Student analytics |\n| GET | `/api/plans` | List teacher's lesson plans |\n| POST | `/api/plans` | Create lesson plan |\n| PUT | `/api/plans/[id]` | Update lesson plan |\n| DELETE | `/api/plans/[id]` | Remove lesson plan |\n| POST | `/api/plans/[id]/assign` | Assign lesson plan to student, get practice URL |\n| GET | `/api/dashboard/summary` | Dashboard metrics |\n\n### Student-side (token-based, no auth)\n\n| Method | Route | Purpose |\n|--------|-------|---------|\n| GET | `/api/practice/[token]` | Resolve token → student name + plan config |\n| POST | `/api/practice/[token]/session` | Start a practice session |\n| POST | `/api/practice/[token]/session/[id]/attempt` | Record a note attempt |\n| PUT | `/api/practice/[token]/session/[id]/complete` | Mark session complete |\n| GET | `/api/practice/[token]/flashcards` | Get flashcard state (notes or symbols) |\n| PUT | `/api/practice/[token]/flashcards` | Update flashcard after review (notes or symbols) |\n\n---\n\n## Deploying to Production\n\n### 1. Create a Supabase project\n\nSign up at [supabase.com](https://supabase.com) (free tier is sufficient) and create a new project.\n\n### 2. Push the schema\n\n```bash\nnpx supabase login\nnpx supabase link --project-ref \u003cyour-project-ref\u003e\nnpx supabase db push\n```\n\n`db push` applies migrations only — it does **not** run `seed.sql`. Your production database will have the schema but no test data.\n\n### 3. Configure Supabase Auth redirects\n\nIf you use email confirmation, Supabase must know where to redirect users after they click the confirmation link:\n\n1. Go to [Supabase Dashboard](https://supabase.com/dashboard) → your project → **Authentication** → **URL Configuration**\n2. Set **Site URL** to your production URL (e.g. `https://your-app.vercel.app`)\n3. Add your production URL to **Redirect URLs** (e.g. `https://your-app.vercel.app/**`)\n4. To support both local and production, add both: `http://localhost:3000/**` and `https://your-app.vercel.app/**`\n\nWithout this, confirmation links will redirect to localhost.\n\n### 4. Deploy to Vercel\n\nConnect your GitHub repo to [Vercel](https://vercel.com) and set these environment variables:\n\n| Variable | Where to get it |\n|----------|-----------------|\n| `NEXT_PUBLIC_SUPABASE_URL` | Supabase Dashboard → Settings → API → Project URL |\n| `NEXT_PUBLIC_SUPABASE_ANON_KEY` | Supabase Dashboard → Settings → API → anon (public) key |\n| `SUPABASE_SERVICE_ROLE_KEY` | Supabase Dashboard → Settings → API → service_role key (click Reveal) |\n| `TOKEN_ENCRYPTION_KEY` | Generate with `node -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\"` |\n\n**Important:** Use the keys from your **cloud** Supabase project, not from `.env.local` (which has local Docker keys). Only `TOKEN_ENCRYPTION_KEY` can be copied from local — or generate a new one for production.\n\nVariables without the `NEXT_PUBLIC_` prefix (like `SUPABASE_SERVICE_ROLE_KEY`) are server-only and never exposed to the browser.\n\n### 5. Try a Lesson\n\nThe landing page \"Try a Lesson\" button links to `/try`, a standalone practice page that works without any seed data or database records. Visitors pick a note set and get the same experience students see — Start Lesson, Free Practice, and Flashcards.\n\n---\n\n## Contributing\n\n1. Fork and clone the repo\n2. Run `npx supabase start` (requires Docker)\n3. Run `npx supabase db reset` to apply migrations and seed data\n4. Copy `.env.example` to `.env.local` and fill in the local credentials\n5. Run `npm run dev`\n6. Full stack is running locally — no cloud accounts needed\n\n### Adding a new migration\n\n```bash\nnpx supabase migration new \u003cdescription\u003e\n# Edit the generated SQL file in supabase/migrations/\nnpx supabase db reset  # Apply it\n```\n\n### Regenerating musical symbol SVGs\n\nSymbols (clefs, notes, dynamics, articulations, etc.) are rendered from Bravura glyphs extracted into `public/symbols/` and `lib/symbol-paths.ts`. To add or update symbols:\n\n```bash\nnpm install --save-dev opentype.js wawoff2\nnode scripts/extract-bravura-glyphs.js\n```\n\nEdit the `GLYPHS` map in `scripts/extract-bravura-glyphs.js` to add SMuFL code points (see [SMuFL](https://w3c.github.io/smufl/gitbook/)), then run the script. It reads Bravura from the VexFlow package, writes SVGs to `public/symbols/`, and regenerates `lib/symbol-paths.ts`. You can then remove the dev deps if desired.\n\n---\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandresjmorales%2Fcognote","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fandresjmorales%2Fcognote","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandresjmorales%2Fcognote/lists"}