🧭 Compass Learning

AI-Powered Learning Companion for ADHD Learners

User Journeys

Setup Flow: Creating a Learner Profile

Browser
User lands on /+page.svelte renders progress indicator (Step 1 of 2)
Component
SetupForm.svelteCollects knowledge anchors (min 3) + reading level. Validates with $derived runes.
Browser
User clicks "Continue" — step advances to 2, renders TopicInput
Component
TopicInput.svelteCollects learning topic + optional URL. Validates topic not empty.
Store
updateLearner()Saves {anchors, readingLevel, currentTopic} to learnerStore
Store
clearConversation()Resets conversation history
Navigation
goto('/learn')SvelteKit navigates to learning interface

Learning Flow: A Single Learning Turn

Page
/learn mounts — onMount checks isProfileComplete, redirects to / if missing
Component
OptionButtons.svelteUser clicks an option (e.g., "Go deeper")
Store
addUserTurn(choice)Appends user turn to conversation (max 15 sliding window)
Fetch
POST /api/chatSends {profile, history, topic, userChoice} as JSON
API
+server.ts validates request — Checks profile, history, topic, userChoice fields
Provider
createProvider()Factory reads AI_PROVIDER env, returns OpenAI or Anthropic instance
AI Logic
buildMessages()Constructs system prompt (companion.md + learner context) + history + user choice
External
AI API call — OpenAI function calling or Anthropic tool_use with respond_to_learner tool
Parse
Parse tool response — Extract {explanation, checkIn?, options[]} from structured output
Response
Return JSON — 200 OK with AIResponse, or ErrorResponse on failure
Store
addAssistantTurn(response)Appends assistant turn, keeps last 15
Render
ChatMessage.svelte renders markdown — OptionButtons display 2-5 new clickable options. Auto-scroll via tick().

Content Extraction Flow

Component
TopicInput.svelteUser pastes a URL in optional URL field
Fetch
POST /api/fetch-urlSends {url} to backend
API
+server.ts validates URL — Checks url field is present and string
Extractor
validateUrl()Blocks localhost, private IPs, non-HTTP(S) protocols
Extractor
fetchWithTimeout()10s timeout with AbortController
External
HTTP fetch to target URL — Uses CompassLearning/1.0 User-Agent
Parse
JSDOM + ReadabilityParse HTML, extract article content, generate excerpt (200 chars)
Response
Return ExtractedContent{url, title, content, excerpt}

Error Handling & Recovery

External
AI API call fails — Rate limit, auth error, or network failure
Handler
getErrorMessage()Maps error types to user-friendly messages
Response
Return ErrorResponse{error: true, message, code} with 400/500 status
UI
Error panel renders — Red box with message + "Try Again" button
Retry
User clicks "Try Again" — handleRetry() calls startLearning() again

Start Over Flow

UI
User clicks "Start Over" in header — handleStartOver() triggered
Store
clearConversation()Sets conversationStore to []
Store
resetLearner()Sets learnerStore to null
Navigation
goto('/')Returns to setup page for fresh start

Technology Stack

Framework

SvelteKit2.49.1
Svelte5 (Runes)
Adapteradapter-node
Vite7.2.6

Language

TypeScript5.9.3
Strict Modeenabled
Module Resolutionbundler

Styling

Tailwind CSS4.1.18
PostCSS@tailwindcss/postcss
Autoprefixer10.4.23

AI Providers

OpenAI SDK6.16.0
Model (default)gpt-4o
Anthropic SDK0.71.2
Model (default)claude-3.5-sonnet

Content Processing

@mozilla/readability0.6.0
jsdom27.4.0

Dev Tools

ESLint9.39.2 (flat config)
Prettier3.7.4
Vitest4.0.16
svelte-check4.3.4

Deployment

PlatformRender
RuntimeNode.js 20+
Buildnpm run build
Startnode build

State Management

TypeSvelte writable stores
PersistenceNone (ephemeral)
Context Window15 turns max
DatabaseNone (POC)
🧭
Click any component in the diagram to see its details
Use layer toggles to filter. Switch views in the header.