All checks were successful
Automated Container Build / build-and-push (push) Successful in 59s
350 lines
No EOL
13 KiB
Markdown
350 lines
No EOL
13 KiB
Markdown
# Study Desk - AI Contributor Guide
|
|
|
|
This file is the operating guide for AI agents and human contributors working in this repository.
|
|
|
|
## Project summary
|
|
|
|
Study Desk is a self-hosted, single-user study application built with Next.js, React, TypeScript, Prisma, and SQLite.
|
|
|
|
Core capabilities:
|
|
|
|
- Flashcard decks with Markdown content, self-grading, shuffle persistence, resume state, and card management
|
|
- Practice quizzes with multiple-choice and SATA questions, partial-credit scoring, rationales, category breakdowns, retakes, and attempt history
|
|
- JSON import, repair, validation, and generation workflows
|
|
- Material groups for organizing decks and quizzes
|
|
- Password-protected private pages
|
|
- Public, read-only share links for decks, quizzes, and groups
|
|
- Responsive light and dark themes
|
|
|
|
This is intentionally a single-user container, not a multi-tenant SaaS application. There is no `User` model. Content ownership is implicit in the local installation.
|
|
|
|
When documentation conflicts with implementation, prefer:
|
|
|
|
1. Current source code
|
|
2. `prisma/schema.prisma`
|
|
3. Applied migrations
|
|
4. This file
|
|
5. Older planning documents and the stock README
|
|
|
|
## Mandatory working rules
|
|
|
|
- Inspect `git status --short` before editing.
|
|
- Preserve unrelated user changes.
|
|
- Keep changes scoped to the requested behavior.
|
|
- Do not use destructive commands such as `git reset --hard` or `git checkout --` unless explicitly requested.
|
|
- Prefer narrow, patch-based edits over rewriting entire files.
|
|
- Do not commit secrets, `.env` files, database contents, generated credentials, or private study material.
|
|
- Do not manually edit generated Prisma files under `src/generated/prisma/`.
|
|
- Do not modify an already-applied migration. Change `prisma/schema.prisma`, then create a new migration.
|
|
- Prefer TypeScript types over `any`.
|
|
- Add dependencies only when the existing stack cannot reasonably solve the problem.
|
|
- When dependencies change, update `package-lock.json` and verify the build.
|
|
- Separate UI, route handling, services, and low-level utilities by responsibility.
|
|
- Ask a clarifying question only when missing information would materially change the implementation or create a risky assumption.
|
|
|
|
## Stack and source of truth
|
|
|
|
Confirm exact versions in `package.json`. The project currently uses:
|
|
|
|
- Next.js 16 App Router
|
|
- React 19
|
|
- TypeScript in strict mode
|
|
- Tailwind CSS v4
|
|
- Prisma 7 with SQLite and `@prisma/adapter-better-sqlite3`
|
|
- `iron-session` for encrypted session cookies
|
|
- Argon2 password hashing
|
|
- Zod validation
|
|
- `react-markdown` with `remark-gfm`
|
|
- `@dnd-kit` for sortable UI
|
|
- Node 22 in Docker
|
|
|
|
Important files:
|
|
|
|
- `package.json` - scripts and dependency versions
|
|
- `prisma/schema.prisma` - database source of truth
|
|
- `src/lib/db.ts` - shared Prisma client
|
|
- `src/lib/auth.ts` - session helpers
|
|
- `src/services/` - business logic and database access
|
|
- `src/app/api/` - route handlers
|
|
- `src/app/globals.css` - design tokens and global styling
|
|
- `src/config/studyModes.ts` - shared study-mode navigation
|
|
- `SKILL.md` - project workflow for adding API routes
|
|
- `study-app-implementation-plan.md` - historical architectural context only
|
|
- `README.md` - currently not authoritative
|
|
|
|
For framework-sensitive changes involving routing, layouts, dynamic params, middleware, server/client boundaries, or framework APIs, follow nearby repository patterns and consult version-matched Next.js documentation. Do not perform unrelated framework migrations.
|
|
|
|
## Local development
|
|
|
|
For a clean checkout:
|
|
|
|
```bash
|
|
npm ci
|
|
npx prisma generate
|
|
npm run dev
|
|
```
|
|
|
|
On Windows PowerShell, use `npm.cmd` and `npx.cmd` if script execution policy blocks the default shims.
|
|
|
|
Useful scripts:
|
|
|
|
```bash
|
|
npm run dev
|
|
npm run build
|
|
npm run start
|
|
npm run lint
|
|
```
|
|
|
|
There is currently no configured test runner. Use focused manual verification plus `npm run build`. Run lint as well, but distinguish pre-existing failures from failures introduced by the current change.
|
|
|
|
## Environment and authentication
|
|
|
|
Common environment variables:
|
|
|
|
- `DATABASE_URL` - SQLite URL
|
|
- `SESSION_SECRET` - `iron-session` secret, at least 32 characters outside throwaway local development
|
|
- `SECURE_COOKIES` - marks the session cookie secure when set to `true`
|
|
- `ADMIN_PASSWORD_HASH` - present in `.env.example`, but currently not used by the login flow
|
|
|
|
Current password behavior:
|
|
|
|
1. On first login, the submitted password is Argon2-hashed.
|
|
2. The hash is stored in the `Setting` row `admin_password_hash`.
|
|
3. Later logins verify against that stored hash.
|
|
|
|
Do not change password provisioning casually. Any change must update login, setup-status behavior, deployment documentation, and security handling together.
|
|
|
|
Authentication state is stored in the `study-app-session` cookie.
|
|
|
|
Public paths include:
|
|
|
|
- `/login`
|
|
- `/api/auth/*`
|
|
- `/shared/*`
|
|
- Static assets and Next internals
|
|
|
|
Everything else requires authentication unless explicitly designed and reviewed as public.
|
|
|
|
Do not log passwords, secrets, full share tokens, or private study content.
|
|
|
|
## Database and migration safety
|
|
|
|
The local `dev.db` may contain real study data.
|
|
|
|
- Do not delete, reset, replace, or recreate it during normal work.
|
|
- Back up the database before destructive migration work.
|
|
- Use the shared Prisma client from `@/lib/db`.
|
|
- Do not create additional `PrismaClient` instances in request handlers or components.
|
|
- Use transactions when partial writes would leave inconsistent state.
|
|
- Return 404 responses for missing records instead of exposing raw Prisma errors.
|
|
|
|
After changing the Prisma schema:
|
|
|
|
```bash
|
|
npx prisma generate
|
|
npx prisma migrate dev --name describe_the_change
|
|
```
|
|
|
|
Use `npx prisma migrate deploy` for deployed environments.
|
|
|
|
## Architecture conventions
|
|
|
|
### Server and client components
|
|
|
|
- Use Server Components by default.
|
|
- Add `"use client"` only when state, effects, event handlers, browser APIs, drag and drop, or client-side fetching are required.
|
|
- Keep database and authentication modules out of client import graphs.
|
|
- Do not access `window`, `localStorage`, `navigator`, or clipboard APIs from Server Components.
|
|
- Prefer `Link` for ordinary internal navigation.
|
|
- Use `useRouter` when navigation follows an action or must be imperative.
|
|
- Follow nearby Next.js 16 patterns for dynamic params and route context types.
|
|
|
|
### API routes and services
|
|
|
|
Preferred route-handler flow:
|
|
|
|
1. Parse and validate external input.
|
|
2. Delegate business logic to focused service or utility functions.
|
|
3. Return a useful status and JSON response.
|
|
|
|
Business logic and Prisma calls normally belong in `src/services/` or a focused `src/lib/` module. Reuse existing services before creating new abstractions.
|
|
|
|
Some older routes still contain direct Prisma calls or loose input handling. Improve the requested path without turning a small task into a broad refactor.
|
|
|
|
### Validation
|
|
|
|
- Reuse existing Zod schemas where available.
|
|
- Never rely only on client-side validation.
|
|
- Validate IDs, required strings, enums, arrays, and import payloads at the API boundary.
|
|
- Keep stored JSON parseable.
|
|
- Use `jsonRepair.ts` only for user- or LLM-generated import text, not to hide malformed database state.
|
|
|
|
## UI and accessibility
|
|
|
|
Use the existing design system in `src/app/globals.css`.
|
|
|
|
Common tokens include:
|
|
|
|
- `bg-bg-base`
|
|
- `bg-bg-surface`
|
|
- `bg-bg-surface-alt`
|
|
- `text-text-heading`
|
|
- `text-text-secondary`
|
|
- `text-text-muted`
|
|
- `primary`
|
|
- `primary-hover`
|
|
- `border`
|
|
- `border-light`
|
|
- `success`
|
|
- `error`
|
|
|
|
Preserve:
|
|
|
|
- Light and dark themes
|
|
- Responsive mobile layouts
|
|
- Existing card, modal, shadow, and rounded-corner language
|
|
- `editorial-title` for major serif headings
|
|
- Manrope for interface text
|
|
- Existing Markdown rendering through `ReactMarkdown`, `remarkGfm`, and `.markdown-content`
|
|
- Existing `@dnd-kit` patterns for sortable content
|
|
|
|
Accessibility requirements:
|
|
|
|
- Use semantic buttons and links.
|
|
- Preserve visible keyboard focus states.
|
|
- Give icon-only controls an accessible name with visible text or `aria-label`.
|
|
- Keep interactive controls keyboard accessible.
|
|
- Avoid horizontal overflow on study pages, library cards, modals, and action rows.
|
|
- Do not attach drag listeners to nested action buttons.
|
|
|
|
## Domain invariants
|
|
|
|
### Flashcards
|
|
|
|
- A deck contains ordered `Flashcard` rows with `front` and `back`.
|
|
- Study progress stores order and results as JSON.
|
|
- Study mode may be sequential or shuffled.
|
|
- Saved progress may contain stale card IDs after deletion. Resume helpers must filter and clamp stale IDs safely.
|
|
- Preserve both study and manage views.
|
|
- Preserve existing sharing entry points when adding new ones.
|
|
|
|
### Quizzes
|
|
|
|
- A quiz contains ordered questions with ordered answer options.
|
|
- `MULTIPLE_CHOICE` questions must have exactly one correct option.
|
|
- `SATA` questions may have multiple correct options.
|
|
- SATA uses partial-credit scoring in `src/lib/scoring.ts`.
|
|
- Partial retakes score only questions present in the submitted answer set.
|
|
- Attempts persist score, maximum score, submitted answers, retake state, and completion time.
|
|
- In-progress answers and position persist through `/api/progress`.
|
|
|
|
### Imports and generation
|
|
|
|
Flashcard import shape:
|
|
|
|
```json
|
|
{
|
|
"type": "flashcards",
|
|
"deckName": "Deck name",
|
|
"description": "Optional description",
|
|
"cards": [{ "front": "...", "back": "..." }]
|
|
}
|
|
```
|
|
|
|
Quiz import shape:
|
|
|
|
```json
|
|
{
|
|
"type": "quiz",
|
|
"quizName": "Quiz name",
|
|
"description": "Optional description",
|
|
"questions": [
|
|
{
|
|
"type": "multiple_choice",
|
|
"prompt": "...",
|
|
"category": "topic",
|
|
"options": [{ "text": "...", "correct": true }],
|
|
"rationale": "..."
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
The app does not call an LLM directly. The Generate UI prepares instructions and accepts generated JSON from the user.
|
|
|
|
Preserve Markdown inside JSON string values and render it through existing Markdown components.
|
|
|
|
Keep the current separation between `ImportModal`, `CreateTab`, `ImportTab`, and `GenerateTab`.
|
|
|
|
### Sharing
|
|
|
|
- `ShareLink` may target a `DECK`, `QUIZ`, or `GROUP`.
|
|
- Reuse `src/components/ui/ShareMenu.tsx`.
|
|
- Public routes are `/shared/<classSlug>/<type>/<token>`.
|
|
- Public viewers are read-only.
|
|
- Public viewers must not write authenticated progress.
|
|
- Validate that token, type, and class slug agree before returning shared content.
|
|
- A deck or quiz may appear publicly shared through its group. Preserve inherited group-share behavior and `?itemId=...` handling.
|
|
|
|
## Verification checklist
|
|
|
|
Before editing:
|
|
|
|
- Read `git status --short`.
|
|
- Inspect the target component, route, service, and schema paths relevant to the requested behavior.
|
|
- Check whether the change affects authentication, public sharing, migrations, generated Prisma output, or real study data.
|
|
|
|
During implementation:
|
|
|
|
- Keep route, service, utility, and component responsibilities clear.
|
|
- Add validation for new external input.
|
|
- Reuse existing design tokens, components, and services.
|
|
- Consider loading, error, empty, mobile, keyboard, light-theme, and dark-theme states.
|
|
- Avoid unrelated cleanup.
|
|
|
|
After editing:
|
|
|
|
```bash
|
|
npm run lint
|
|
npm run build
|
|
```
|
|
|
|
If lint already fails, report baseline failures separately.
|
|
|
|
For UI work, manually verify:
|
|
|
|
1. The affected authenticated route
|
|
2. The corresponding public/shared route when applicable
|
|
3. Loading, error, and empty states
|
|
4. Study resume and restart behavior when relevant
|
|
5. Drag and drop when relevant
|
|
6. Light and dark themes
|
|
7. A narrow mobile viewport
|
|
8. No horizontal overflow
|
|
9. Keyboard access for changed controls
|
|
|
|
## Known repository conditions
|
|
|
|
- `README.md` is still mostly stock create-next-app documentation.
|
|
- `study-app-implementation-plan.md` contains useful historical context but may be stale.
|
|
- `task.md` records completed implementation phases.
|
|
- `SKILL.md` describes the expected workflow for new API routes.
|
|
- The current lint baseline includes existing errors such as `any` usage and React effect-rule violations.
|
|
- A successful `next build` is the primary repository-wide compile and type check.
|
|
- The current middleware convention produces a known deprecation warning.
|
|
- `docker-compose.yml` may publish port `3000`, while the production Docker image uses port `3726`. Verify the mapping before relying on production Compose.
|
|
- Do not silently fix known baseline issues as unrelated cleanup.
|
|
|
|
## Extending the application
|
|
|
|
For a genuinely new study mode:
|
|
|
|
1. Add or update the Prisma model and migration when persistence is required.
|
|
2. Add a focused service in `src/services/`.
|
|
3. Add API routes under `src/app/api/`.
|
|
4. Add page routes and client components.
|
|
5. Add the mode to `src/config/studyModes.ts` when it belongs in shared navigation.
|
|
6. Extend progress, import, scoring, or sharing only when required.
|
|
7. Verify authentication, public/private boundaries, mobile layout, and both themes.
|
|
|
|
Do not add a new mode by placing unrelated logic into `deckService.ts`, `quizService.ts`, or a large page component. |