A Next.js application that provides an interactive learning experience through AI-generated content with real-time streaming, fill-in-the-blank exercises, and contextual Q&A support.
This project creates an interactive learning experience where AI explanations are streamed character-by-character, with periodic fill-in-the-blank questions and contextual chat support. Students can ask questions about any part of the explanation through expandable accordions.
The current implementation focuses on coding explanations (specifically a Python function example), but the system is designed to be flexible for various educational content.
- Real-time Text Streaming: AI explanations appear with a typewriter effect.
- Interactive Blanks: Students fill in missing information during explanations.
- Contextual Q&A: Chat system that understands the current explanation context.
- Clean Architecture: Core streaming logic is separated into a reusable React hook (
useTextStreamer). - Component-Based UI: The interface is broken down into modular, maintainable components.
- User Authentication: Secure login system with session management.
- Responsive Design: Clean, dark-themed interface optimized for learning.
- Node.js 18+
- pnpm (recommended) or npm
- PostgreSQL database
- DeepSeek API key
-
Install dependencies:
pnpm install
-
Configure environment variables: Create a
.env.localfile in the root directory:DEEPSEEK_API_KEY=your_deepseek_api_key_here DATABASE_URL=postgresql://username:password@localhost:5432/database_name
-
Database Setup: Use a local or cloud-based PostgreSQL instance (e.g., Supabase, Neon). Update the
DATABASE_URLwith your connection string. -
Initialize Database: Generate and apply the database schema.
npx drizzle-kit generate npx drizzle-kit push
-
Start development server:
pnpm dev
-
Access the application:
- Main app: http://localhost:3000/streaming
- Authentication: http://localhost:3000/auth/login
The project follows a clean, feature-oriented structure.
app/: Contains all routes and API endpoints.app/streaming/page.tsx: The main page component for the learning interface.app/api/: Server-side logic for AI generation, authentication, and data access.
components/: Reusable React components.components/streaming/: Components specific to the streaming interface.
hooks/: Custom React hooks for shared logic.hooks/useTextStreamer.ts: The core hook managing the text streaming animation and state.
lib/: Shared utilities, like database connection and helpers.drizzle/: Database migration files generated by Drizzle ORM.
components/ βββ streaming/ # Streaming-specific components βββ ModernBlankInput.tsx # Fill-in-the-blank UI βββ ChatAccordion.tsx # Q&A chat interface βββ ModernAIOutputDisplay.tsx # Final AI answer display βββ PerformanceSummary.tsx # Student progress tracking
All the complex, low-level logic for text streaming is encapsulated in the hooks/useTextStreamer.ts custom hook. This keeps the main page component clean and focused on high-level state management.
The hook is responsible for:
- Processing the source text and detecting
BLANK(...)and paragraph breaks. - Managing timers for the character-by-character "typing" animation.
- Signaling to the parent component when user interaction (feedback or a blank input) is required.
- Providing simple functions (
injectIntoStream,completeParagraph,resetStream) for the parent to control the stream's flow.
This file serves as the main controller for the learning experience. It uses the useTextStreamer hook and is responsible for:
- Fetching question data from the API.
- Managing high-level application state (current question, completed content, etc.).
- Handling user events (e.g., submitting an answer, asking a question in the chat).
- Rendering the appropriate UI components based on the state from the hook and the application.
The app/api/generate/route.ts file handles all communication with the DeepSeek API for context-aware chat responses.
AI explanations use BLANK(answer) placeholders for interactive elements:
The function should return BLANK(-1) if the value is not found. The difference between the max and min keys is BLANK(6).
Key timing constants are defined in hooks/useTextStreamer.ts:
const TYPING_INTERVAL_MS = 20; // Time between typing intervals
const CHARS_PER_TYPE_INTERVAL = 3; // Characters rendered per intervalποΈ Database
The project uses Drizzle ORM to interact with a PostgreSQL database.
Schema: schema.ts - Defines the users, questions, and logs tables.
Database Client: lib/db.ts - Manages the database connection.
Migrations: Stored in the drizzle/ directory.
# Generate a new migration file based on schema changes
pnpm db:generate
# Push schema changes directly to the database (for development)
pnpm db:push
# Run all pending migrations
pnpm db:migrate
# Open Drizzle Studio to view and manage your data
pnpm db:studioEnsure these variables are set in your deployment environment:
DEEPSEEK_API_KEY=your_production_api_key
DATABASE_URL=your_production_database_url
NEXTAUTH_SECRET=your_secure_random_string # Generate with `openssl rand -base64 32`
NODE_ENV=productionInsert a new row into the questions table in your database:
INSERT INTO questions (question, content) VALUES (
'Your question title',
'Your explanation content with BLANK(answer) placeholders...'
);The new question will automatically be available in the application.
To change the streaming speed, adjust the constants in hooks/useTextStreamer.ts.
To modify the UI, edit the components in components/streaming/.
To add new interactions, extend the useTextStreamer hook and the StreamingPage component.
Note: This project was developed by Avval Halani. Please feel free to contact me with any questions.
- Node.js 18+
- pnpm (recommended) or npm
- PostgreSQL database
- DeepSeek API key
-
Install dependencies:
pnpm install
-
Configure environment variables: Create
.env.localin the root directory:DEEPSEEK_API_KEY=your_deepseek_api_key_here DATABASE_URL=postgresql://username:password@localhost:5432/database_name
-
Database Setup:
Install PostgreSQL locally and create a database:
# Update DATABASE_URL in .env.local DATABASE_URL=postgresql://username:password@localhost:5432/streaming_appUse a cloud provider like:
- Supabase (free tier available)
- Neon (serverless postgres)
Example with Supabase:
- Create account at supabase.com
- Create new project
- Copy connection string from Settings > Database
- Update
.env.local:
DATABASE_URL=postgresql://postgres:[password]@[host].supabase.co:5432/postgres
-
Initialize Database:
# Generate and run migrations npx drizzle-kit generate npx drizzle-kit push # Or use the migration command pnpm db:migrate
-
Seed Database (Optional):
Add sample questions to your database:
# You can manually insert data or create a seed script # Example questions are provided in the schema comments
-
Start development server:
pnpm dev
-
Access the application:
- Main app: http://localhost:3000/streaming
- Authentication: http://localhost:3000/auth/login
When setting up the project on a new machine:
- Install PostgreSQL or setup cloud database
- Create
.env.localwith correctDATABASE_URL - Run
pnpm installto install dependencies - Run
pnpm db:resetto create database tables - Create your first user account through the application
- Add questions to the database
- Start development with
pnpm dev
The application uses the following database tables:
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
email VARCHAR(255) NOT NULL UNIQUE,
password TEXT NOT NULL,
created_at TIMESTAMP DEFAULT now()
);CREATE TABLE questions (
id SERIAL PRIMARY KEY,
question TEXT NOT NULL,
content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT now()
);CREATE TABLE logs (
id SERIAL PRIMARY KEY,
user_id TEXT,
action TEXT NOT NULL,
details JSONB DEFAULT '{}'::jsonb,
level TEXT NOT NULL,
timestamp TIMESTAMP WITH TIME ZONE DEFAULT now()
);Example question entry:
INSERT INTO questions (question, content) VALUES (
'Explain the identical function',
'Let me walk you through this step by step...\n\nThe `identical` function takes two parameters: a dictionary `d` and a value `val`...'
);The project uses Drizzle ORM with the following configuration:
- Schema:
schema.ts- Database schema definitions - Database client:
lib/db.ts- Database connection - Migrations:
drizzle/- Auto-generated migration files
# Generate new migration
pnpm db:generate
# Push schema changes to database
pnpm db:push
# Run migrations
pnpm db:migrate
# View database in Drizzle Studio
pnpm db:studio
# Reset database (drops all data and recreates tables)
pnpm db:resetThe reset command completely wipes your database and recreates all tables with the proper schema:
# Reset database - WARNING: This deletes ALL data
pnpm db:resetWhat the reset does:
- β
Drops all existing tables (
users,questions,logs) - β Recreates empty tables with proper schema
- β Creates necessary indexes for performance
- β Does NOT insert sample data - you start with clean, empty tables
When to use reset:
- Setting up the project for the first time
- Switching to a new database
- During development when you need to start fresh
- After major schema changes
To use a different database:
-
Update environment variables:
DATABASE_URL=postgresql://new_username:new_password@new_host:5432/new_database
-
Run migrations:
npx drizzle-kit push
-
Seed with sample data (if needed)
- Main streaming interface:
app/streaming/page.tsx- Contains all main logic - AI API endpoint:
app/api/generate/route.ts- Handles DeepSeek integration - Authentication routes:
app/api/auth/- Login/logout endpoints - Questions API:
app/api/questions/- Database queries - Logging API:
app/api/logs/- Activity logging - Database schema:
schema.ts- Drizzle ORM schema - Database connection:
lib/db.ts- PostgreSQL connection - Logger utility:
lib/logger.ts- Logging functions
components/
βββ streaming/ # Streaming-specific components
β βββ BlankInput # Fill-in-the-blank interactions
β βββ ChatAccordion # Q&A chat interface
β βββ ParagraphFeedback # Feedback collection
β βββ PerformanceSummary # Student progress tracking
β βββ TextRenderer # Markdown text display
βββ ui/ # Reusable UI components (shadcn/ui)
βββ accordion.tsx # Base accordion component
All core functionality is in app/streaming/page.tsx:
- Text streaming with typewriter effect
- Blank detection and handling (
BLANK(answer)format) - Chat accordion management
- Performance tracking
- User authentication checks
- Activity logging
The app/api/generate/route.ts file handles:
- DeepSeek API integration
- Context-aware chat responses
- User authentication verification
- Request/response logging
- Error handling and fallbacks
- Login:
app/api/auth/login/route.ts - Logout:
app/api/auth/logout/route.ts - Session management: HTTP-only cookies
- Password hashing: bcryptjs
The application logs all user interactions:
- User actions: Question navigation, feedback, responses
- AI interactions: Requests, responses, errors
- Authentication: Login attempts, successes, failures
- Performance: Response times, error rates
AI explanations use BLANK(answer) placeholders for interactive elements:
The function should return BLANK(-1) if val is not found...
The difference between max and min keys is BLANK(6)...
Key timing constants in app/streaming/page.tsx:
const TYPING_INTERVAL_MS = 100; // Speed between typing intervals
const CHARS_PER_TYPE_INTERVAL = 30; // Characters per interval
const ANIMATION_CHARS_PER_SECOND = 30; // Animation speed
const FEEDBACK_DURATION_MS = 1500; // Feedback display timeDEEPSEEK_API_KEY=your_production_api_key
DATABASE_URL=your_production_database_url
NEXTAUTH_SECRET=your_secure_random_string
NODE_ENV=production-
Insert into database:
INSERT INTO questions (question, content) VALUES ( 'Your question title', 'Your explanation content with BLANK(answer) placeholders...' );
-
Questions automatically appear in the application
Modify lib/logger.ts to:
- Add new log levels
- Change log format
- Add custom fields
- Integrate with external services
Check the logs table for:
- User engagement metrics
- AI response performance
- Error patterns
- Feature usage statistics
Note: This project was developed by Avval Halani. Please feel free to contact me for any questions.