Skip to content

Latest commit

 

History

History
455 lines (363 loc) · 27.5 KB

File metadata and controls

455 lines (363 loc) · 27.5 KB

NextGenNote

An AI-native notepad that thinks with you.

NextGenNote isn't just a place to store text — it's a workspace that turns raw emotion into reflective writing, links scattered ideas into a visual graph of thought, and organizes your notes the way your brain actually works: by mood, by collection, and by connection.


Table of Contents

  1. Overview
  2. Feature Walkthrough
  3. Tech Stack
  4. System Architecture
  5. Core Algorithms
  6. Project Structure
  7. Database Schema
  8. API Reference
  9. Environment Variables
  10. Local Setup
  11. Deployment
  12. Responsive Design Notes
  13. Roadmap

Overview

NextGenNote was built from scratch as a full-stack MERN application (MongoDB, Express, React, Node.js), designed to answer one question: what would a notepad look like if it were built in the AI era instead of the 1990s?

The result is a note-taking app with four pillars that don't typically coexist in the same product:

  • A real, working AI co-writer (AI Canvas) that turns a described emotion into a genuine reflective journal entry — not a gimmick, an actual Groq-powered LLM call with a carefully engineered system prompt
  • A knowledge graph (Connecting Thoughts) that visualizes how your notes relate to each other, using a custom-built force-directed physics simulation — no external graph library
  • Organizational depth — collections with custom colors, pinning, archiving, soft-delete trash with recovery, and full-text search — all built on top of a single flexible Note schema
  • Developers Mode where you can store and analyze your code properly you could convert it to your own respective language along with learning through workflows and commands.
  • A production-grade auth & UX layer — JWT sessions with real expiry checking, multi-account switching on one device, dark mode, customizable greetings/avatars, and a fully responsive layout that adapts from a 4K desktop down to a 360px phone screen with a native-feeling bottom tab bar
  • Installable PWA — a real web app manifest, custom app icons, and an auto-updating service worker (via vite-plugin-pwa) mean NextGenNote can be installed to a home screen or desktop and launches like a native app, with static assets cached for offline resilience

Feature Walkthrough

Screenshot (1661)

1. Landing Page

The first thing a new, logged-out visitor sees. A marketing-style hero section explaining the product, a feature checklist (Free/Open Source, Graph Builder, Custom Collections, AI Mode), a "Get Started Free" CTA, and a dashboard mockup preview.

image

2. Authentication

Glassmorphic split-screen Login and Signup pages, each with a dark animated brand panel on the left (desktop only) and a frosted-glass form card on the right. Sessions persist for 7 days via JWT; after that, the token is detected as expired on load and the user is transparently returned to the Landing page rather than seeing a broken dashboard.

image

3. Dashboard

The home base. A collapsible sidebar (desktop) or bottom tab bar (mobile) for navigation between All Notes, Starred, Archive, Trash, and custom Collections. Notes are split into Pinned and Other Notes sections, with a live search bar, a grid/bar layout toggle, and a 2/3/4/5-column density toggle for grid view on wide screens — both preferences persist per-browser via localStorage.

Screenshot (1743) image Screenshot (1744)

4. Developers Mode

A built-in, VS Code–styled code editor and snippet manager (/devmode), separate from the note-taking experience. Snippets are stored per-user in their own CodeSnippet collection, with a file-tree sidebar, an inline-renameable tab, line numbers, and a live cursor-position/line-count status bar. Three Groq-powered actions run server-side on top of whatever code is in the editor:

  • Auto language detection — debounced ~1s after typing stops, so it doesn't fire a model call on every keystroke
  • Convert — translates the current snippet into another supported language, shown side-by-side before you choose to apply it
  • Diagram — generates a Mermaid.js logic diagram summarizing the code's control flow, rendered inline in a modal
image

5. Note Editor

Notes open in view mode by default (read-only) and only become editable after explicitly clicking Edit — this prevents accidental edits from a stray click. From the editor, a note can be saved, archived, linked to other notes, or assigned to a collection. New notes are drafts until the user actually writes something and saves — nothing is persisted to the database on a blank "New Note" click.

Screenshot 2026-07-24 135118

6. AI Canvas

The signature feature. A togglable panel (side panel on desktop, full-screen sheet on mobile/tablet) where the user picks a mood chip and describes what's on their mind. That prompt is sent to a Groq-hosted Llama 3.3 70B model with a system prompt engineered to produce a warm, human-sounding reflective journal entry — not a clinical AI summary. The generated text can be saved directly as a note, tagged with the mood and an "AI Generated" badge.

image

7. Collections

User-created groupings (e.g., "DSA," "Mood," "Reading List") each with a custom accent color. Notes inside a collection render with a colored top border matching that collection. Notes can be created directly inside a collection, or added to one after the fact via an "Add existing note" picker.

Screenshot 2026-07-24 134952

8. Connecting Thoughts

An Obsidian-style knowledge graph. Every note is a node; explicit links between notes (created from inside the editor) are edges. Nodes glow more intensely the more connections they have ("hub" notes), and the whole graph is rendered as an interactive, draggable force-directed simulation built entirely in raw SVG + requestAnimationFrame — no D3, no graph library. Includes filters for "Strong Links" (2+ connections) and "Orphan Nodes" (zero connections), plus live stats (Total Notes, Connections, Clusters).

9. Notifications

A real-time-feeling activity log: "First note created!", "You pinned a note!", "You archived a note!", etc. — triggered server-side any time a relevant action occurs, with per-item delete and a "Clear all" action, respecting a user-level notification on/off toggle in Settings.

Screenshot 2026-07-24 135159 Screenshot 2026-07-24 135144

10. Settings & Profile

Dark/light mode toggle, notification toggle, a 7-avatar picker, a customizable dashboard greeting (default "Hola"), and a full Profile page with editable display name, account verification badge, and multi-account switching — the app remembers every account a user has logged into on that device and lets them swap between them without re-entering a password.

12. Mobile Experience

Below the sm breakpoint, the sidebar disappears entirely in favor of a bottom tab bar (Notes / AI / Alerts / More), with a slide-up sheet for secondary navigation (Starred/Archive/Trash, Workspace, Connecting Thoughts, Developer Mode, Settings). Each collection row in the Workspace list has its own delete control on mobile, matching what's available on the desktop sidebar. The AI Canvas becomes a full-screen sheet through tablet widths. The Connecting Thoughts graph gets a collapsible filter drawer instead of a fixed side panel, and node-dragging supports real touch events, not just mouse.


Tech Stack

Layer Technology
Frontend framework React (Vite)
Styling Tailwind CSS v4
Icons lucide-react
Routing React Router v6
HTTP client Axios (with request/response interceptors)
Backend framework Express.js
Database MongoDB Atlas + Mongoose ODM
Authentication JWT (jsonwebtoken) + bcrypt password hashing
AI provider Groq API (Llama 3.3 70B Versatile)
Frontend hosting Vercel
Backend hosting Render

System Architecture

┌─────────────────────┐         HTTPS / JSON          ┌──────────────────────┐
│   React SPA          │ ─────────────────────────────▶│   Express API         │
│   (Vercel)            │                                │   (Render)             │
│                       │ ◀───────────────────────────── │                        │
│  - AuthContext        │      JWT in Authorization       │  - authMiddleware      │
│  - ThemeContext        │      header on every call       │    (verifies JWT)      │
│  - axios interceptors  │                                │  - Controllers         │
│    (attach token,      │                                │    (auth, note,        │
│     handle 401)        │                                │     collection, user,  │
└──────────┬───────────┘                                │     notification, ai)  │
           │                                              └──────────┬───────────┘
           │ localStorage                                            │
           │ (token, user,                                           │ Mongoose
           │  saved accounts,                                        │
           │  recent notes,                                          ▼
           │  grid density)                              ┌──────────────────────┐
           │                                              │   MongoDB Atlas        │
           │                                              │                        │
           │                                              │  Collections:          │
           │                                              │  - users               │
           │                                              │  - notes               │
           │                                              │  - collections         │
           │                                              │  - notifications       │
           │                                              └──────────────────────┘
           │
           │  external call (server-side only)
           ▼
┌──────────────────────┐
│   Groq API             │
│   (Llama 3.3 70B)       │
│   — AI Canvas note      │
│      generation          │
└──────────────────────┘

Request flow for a typical authenticated action (e.g., pinning a note):

  1. User clicks "Pin" in NoteCardDashboard.handlePin(note) fires
  2. togglePinNote(note._id) calls the shared api axios instance
  3. Axios request interceptor reads token from localStorage and attaches it as Authorization: Bearer <token>
  4. Request hits Express → authMiddleware verifies the JWT signature and expiry, decodes userId, attaches it to req
  5. noteController.togglePin flips isPinned, saves via Mongoose, and calls notify() to log a notification (respecting the user's notificationsEnabled preference)
  6. Response returns → frontend calls refresh(), which re-fetches notes, counts, and notifications in parallel

Core Algorithms

1. Force-Directed Graph Layout (Connecting Thoughts)

The knowledge graph is not powered by any charting/graph library — it's a hand-rolled physics simulation running on every animation frame:

  • Repulsion: every pair of nodes exerts an inverse-square repulsive force on each other (force = k / distance²), so nodes naturally spread out and never stack on top of each other
  • Spring attraction: every edge (link between two notes) acts like a spring with a target rest-length of 150px — nodes connected by an edge are pulled toward that distance, neither collapsing together nor drifting apart indefinitely
  • Centering force: a weak force pulls every node gently toward the center of the canvas, preventing the whole graph from drifting off-screen over time
  • Damping: velocity is multiplied by 0.85 every frame, acting as friction so the simulation settles into a stable layout instead of oscillating forever
  • Dragging override: while a node is actively being dragged (mouse or touch), physics forces are suspended for that node and its position is set directly from pointer coordinates; releasing it re-enables physics

This runs inside a useEffect + requestAnimationFrame loop, recalculating all node positions ~60 times per second while the graph page is open.

Cluster detection uses a simple graph traversal (iterative DFS via an explicit stack, to avoid recursion depth issues): starting from each unvisited node, it walks all reachable neighbors via the adjacency list built from edges, marking them visited, and increments a cluster counter each time it has to start a new, unvisited component. This is the standard "count connected components" algorithm.

2. JWT Session Expiry (Client-Side)

Rather than trusting localStorage blindly, the app decodes the JWT's exp claim client-side on every app load (jwt-decode) and compares it against the current time before rendering any route. If expired, the token is purged immediately and the user is treated as logged-out — landing on the public Landing page rather than a broken authenticated view. A secondary axios response interceptor catches any 401 that slips through (e.g., server-side revocation or clock drift) as defense-in-depth.

3. Recent Notes (Bounded LRU-style Cache)

The sidebar's "Recent Notes" list is a simple bounded most-recently-used cache implemented directly in React state + localStorage: opening a note removes any existing entry for that note ID, unshifts it to the front, and slices the array to a maximum of 5 entries — giving O(n) insert on a tiny fixed-size list, which is effectively constant time in practice.

4. Collection & View Filtering

Rather than maintaining separate database collections per view (Starred/Archive/Trash/Collection), every note carries boolean flags (isPinned, isArchived, isDeleted) and an optional collectionId. A single getNotes endpoint builds a different Mongoose query filter object depending on the requested view — this keeps the schema simple and avoids data duplication or complex joins, at the cost of slightly more logic in the query-builder function.

5. AI Prompt Engineering (AI Canvas)

The Groq call uses a fixed system prompt instructing the model to act as "a thoughtful journaling assistant" that converts a described feeling into a 2-4 paragraph reflective note in the user's own voice — explicitly told to avoid titles, greetings, or sign-offs, and to avoid sounding clinical or overly poetic. The user's selected mood chip (if any) is prepended to their free-text prompt before being sent, giving the model extra context without requiring the user to type it out themselves.


Project Structure

NextGenNote/
├── backend/
│   ├── config/
│   │   └── db.js                     # (optional) Mongo connection helper
│   ├── controllers/
│   │   ├── authController.js         # register, login, googleLogin
│   │   ├── noteController.js         # CRUD, pin/archive/trash, graph data, linking
│   │   ├── collectionController.js   # create/list/delete collections
│   │   ├── notificationController.js # list/delete/clear notifications
│   │   ├── userController.js         # profile + settings (avatar, theme, greeting)
│   │   ├── aiController.js           # Groq API call for AI Canvas
│   │   └── devModeController.js      # snippet CRUD, language detect, convert, diagram
│   ├── middleware/
│   │   └── authMiddleware.js         # JWT verification, attaches req.userId
│   ├── models/
│   │   ├── User.js
│   │   ├── Note.js
│   │   ├── Collection.js
│   │   ├── Notification.js
│   │   └── CodeSnippet.js            # Developer Mode snippets
│   ├── routes/
│   │   ├── authRoutes.js
│   │   ├── noteRoutes.js
│   │   ├── collectionRoutes.js
│   │   ├── notificationRoutes.js
│   │   ├── userRoutes.js
│   │   ├── aiRoutes.js
│   │   └── devModeRoutes.js
│   ├── utils/
│   │   └── notify.js                 # shared notification-creation helper
│   ├── .env                          # MONGO_URI, JWT_SECRET, GROQ_API_KEY, PORT
│   ├── package.json
│   └── server.js                     # Express app entrypoint, incl. GET /health
│
├── .github/
│   └── workflows/
│       └── keep-alive.yml            # pings /health every 14 min to prevent Render cold starts
│
├── frontend/
│   ├── src/
│   │   ├── api/
│   │   │   ├── axios.js              # shared instance + interceptors
│   │   │   ├── notes.js
│   │   │   ├── collections.js
│   │   │   ├── notifications.js
│   │   │   ├── user.js
│   │   │   ├── ai.js
│   │   │   └── devmode.js
│   │   ├── components/
│   │   │   ├── Sidebar.jsx
│   │   │   ├── MobileTabBar.jsx      # bottom nav + "More" sheet (Developer Mode, delete collection, etc.)
│   │   │   ├── NoteCard.jsx          # grid view
│   │   │   ├── NoteBar.jsx           # bar/list view
│   │   │   ├── NewNoteCard.jsx
│   │   │   ├── AICanvas.jsx
│   │   │   ├── BlockEditor.jsx       # rich block-based note editing
│   │   │   ├── BlockViewer.jsx       # read-only block rendering
│   │   │   ├── MermaidDiagram.jsx    # renders Developer Mode's generated diagrams
│   │   │   ├── ProfileMenu.jsx
│   │   │   ├── NotificationPanel.jsx
│   │   │   ├── ConfirmDialog.jsx
│   │   │   ├── CollectionModal.jsx
│   │   │   ├── AddNotesToCollectionModal.jsx
│   │   │   └── ProtectedRoute.jsx
│   │   ├── context/
│   │   │   ├── AuthContext.jsx / AuthContextStore.js
│   │   │   └── ThemeContext.jsx / ThemeContextStore.js
│   │   ├── constants/
│   │   │   └── avatars.js
│   │   ├── utils/
│   │   │   ├── accounts.js           # multi-account localStorage helpers
│   │   │   └── tokenValid.js         # JWT expiry check
│   │   ├── pages/
│   │   │   ├── Landing.jsx
│   │   │   ├── Login.jsx
│   │   │   ├── Signup.jsx
│   │   │   ├── Dashboard.jsx
│   │   │   ├── NoteEditor.jsx
│   │   │   ├── Settings.jsx
│   │   │   ├── ProfileSettings.jsx
│   │   │   ├── ConnectingThoughts.jsx
│   │   │   └── DevMode.jsx
│   │   ├── App.jsx                   # route definitions
│   │   ├── main.jsx
│   │   └── index.css                 # Tailwind entry
│   ├── .env / .env.production        # VITE_API_URL
│   ├── vite.config.js
│   └── package.json
│
└── README.md

Database Schema

User

name, email (unique), password (hashed, optional for OAuth), googleId,
avatar, darkMode, notificationsEnabled, dashboardGreeting, timestamps

Note

userId (ref User), title, content, isAIGenerated, mood,
isPinned, isArchived, isDeleted, collectionId (ref Collection),
linkedNotes [ref Note], timestamps

Collection

userId (ref User), name, color, timestamps

Notification

userId (ref User), message, timestamps

CodeSnippet

userId (ref User), title (default "Untitled Snippet"), code,
language (default "plaintext"), timestamps

API Reference

Method Route Description
POST /api/auth/register Create account
POST /api/auth/login Email/password login
POST /api/auth/google Google OAuth login
GET /api/notes List notes (supports ?filter= or ?collectionId=)
GET /api/notes/counts Starred/archive/trash counts
GET /api/notes/graph/data Nodes + edges for Connecting Thoughts
POST /api/notes Create note
GET /api/notes/:id Get single note
PUT /api/notes/:id Update title/content
PATCH /api/notes/:id/pin Toggle pin
PATCH /api/notes/:id/archive Archive note
PATCH /api/notes/:id/restore Restore from archive/trash
PATCH /api/notes/:id/collection Assign/remove collection
PATCH /api/notes/:id/link Toggle link to another note
DELETE /api/notes/:id Move to trash
DELETE /api/notes/:id/permanent Permanently delete
DELETE /api/notes/trash/empty Empty trash
GET / POST / DELETE /api/collections List / create / delete collections
GET /api/notifications List notifications
DELETE /api/notifications/:id Delete one
DELETE /api/notifications/clear Clear all
GET /api/user/me Get profile + settings
PATCH /api/user/settings Update avatar/theme/greeting/notifications
POST /api/ai/generate Generate an AI note from a mood + prompt
GET /api/devmode/languages List supported languages for Developer Mode
GET / POST /api/devmode List / create code snippets
GET / PUT / DELETE /api/devmode/:id Get / update / delete a snippet
POST /api/devmode/detect-language Auto-detect the language of a code snippet
POST /api/devmode/translate Convert a snippet from one language to another
POST /api/devmode/diagram Generate a Mermaid logic diagram from a snippet
GET /health Lightweight liveness check, no DB call — used by the keep-alive workflow (see below)
GET / Basic "API is running" check

All routes except /auth/*, /health, and / require a valid Authorization: Bearer <token> header.


Environment Variables

backend/.env

PORT=5000
MONGO_URI=mongodb+srv://<user>:<pass>@cluster0.xxxxx.mongodb.net/nextgennote
JWT_SECRET=your_secret_key
GROQ_API_KEY=your_groq_key
GOOGLE_CLIENT_ID=your_google_client_id   # optional, if OAuth is enabled

frontend/.env / .env.production

VITE_API_URL=http://localhost:5000/api          # local
VITE_API_URL=https://your-backend.onrender.com/api   # production
VITE_GOOGLE_CLIENT_ID=your_google_client_id      # optional

GitHub repository secret (Settings → Secrets and variables → Actions, not a .env file)

RENDER_BACKEND_URL=https://your-backend.onrender.com   # used by .github/workflows/keep-alive.yml

Local Setup

# Backend
cd backend
npm install
npm run dev        # http://localhost:5000

# Frontend (separate terminal)
cd frontend
npm install
npm run dev         # http://localhost:5173

Deployment

  • Backend: deployed on Render as a Node web service, root directory backend, build command npm install, start command npm start. Environment variables set directly in the Render dashboard.
  • Frontend: deployed on Vercel, root directory frontend, framework preset Vite, build command npm run build, output directory dist. VITE_API_URL pointed at the live Render backend URL.
  • CORS on the backend is configured to accept requests from the deployed Vercel origin.

Keeping the backend warm (Render free tier)

Render's free instance type spins down after 15 minutes of inactivity, which otherwise means the first request after idle can take 30-50+ seconds to respond. To avoid that without paying for an always-on plan:

  • GET /health on the backend is a lightweight, DB-free liveness check
  • .github/workflows/keep-alive.yml runs on a GitHub Actions schedule (*/14 * * * *) and hits that endpoint, keeping the instance from ever going idle long enough to sleep
  • The ping retries up to 3 times with a 60-second timeout per attempt, so an occasional cold wake (if a scheduled run is ever delayed past the 15-minute window) still succeeds instead of reporting a false failure
  • Requires a RENDER_BACKEND_URL repository secret (Settings → Secrets and variables → Actions) set to the live Render URL, e.g. https://nextgennote-3.onrender.com
  • GitHub automatically disables scheduled workflows after 60 days with no commits to the repo — if pings ever silently stop, check the Actions tab and re-enable it

Responsive Design Notes

The app was built desktop-first and then systematically retrofitted for mobile/tablet:

  • Sidebar is hidden entirely below the sm breakpoint, replaced by a fixed bottom tab bar (Notes / AI / Alerts / More) with a slide-up sheet for secondary navigation
  • AI Canvas is a docked 320px side panel on large desktop screens (lg: and up) but becomes a full-screen sheet with a dismissible backdrop on everything below that, including tablets — this avoids the panel fighting the sidebar for space at in-between widths
  • Connecting Thoughts graph shrinks its rendered height progressively across breakpoints and gains real touch-event support (onTouchStart/Move/End) for dragging nodes on phones/tablets, not just mouse
  • Grid density control (3/4/5 columns) is desktop-only (lg: and up) and persisted per-browser via localStorage, since it has no meaningful effect below that width

Roadmap

Planned but not yet built:

  • Mood timeline / emotional insight charts
  • AI-generated weekly digest summarizing the week's notes
  • "On this day" note resurfacing
  • Voice-to-note via the browser's native Speech Recognition API
  • Daily writing streak tracking

Built as a hands-on learning project covering full-stack architecture, JWT authentication, MongoDB schema design, custom physics-based data visualization, and LLM Integration.