Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
269dbc600f | ||
|
|
9b29a44429 | ||
|
|
e9fa161148 | ||
|
|
299d6d4878 | ||
|
|
ea63ae6e72 | ||
|
|
4d50cd9b71 | ||
|
|
8b3f3bcc35 | ||
|
|
f73a5117a1 | ||
|
|
2d1e13b9b5 | ||
|
|
b6c274eeb6 | ||
|
|
4b78a7c9dd | ||
|
|
87e2f2f391 | ||
|
|
f237c42093 | ||
|
|
a80f86e99e | ||
|
|
eb62ed99ce | ||
|
|
45fc32323f | ||
|
|
f1d69da019 | ||
|
|
9d131fdb99 | ||
|
|
29ef6307e0 | ||
|
|
c3c771002f | ||
|
|
554903b818 | ||
|
|
6b71ecd7c6 | ||
|
|
833b8343ac | ||
|
|
8e060890d3 | ||
|
|
abf0fc74e5 | ||
|
|
6242c8c182 | ||
|
|
15327d74d8 | ||
|
|
90c34ca572 | ||
|
|
2b8fa9c7e8 | ||
|
|
2f7aabcfe9 | ||
|
|
ef6d054598 | ||
|
|
201d707aea | ||
|
|
d8b0902ac9 | ||
|
|
01c75bd796 | ||
|
|
0a609306a6 | ||
|
|
5bdc6de862 | ||
|
|
cc01fb9418 | ||
|
|
5e8e1349bc | ||
|
|
ddbb71fb78 | ||
|
|
3289453448 | ||
|
|
611af8ee72 | ||
|
|
69b96a592d | ||
|
|
3151f6a9cc | ||
|
|
726968288c | ||
|
|
ad086f337a | ||
|
|
66d09820c3 | ||
|
|
5ec73e9234 | ||
|
|
17569d0658 | ||
|
|
14ea464c0a | ||
|
|
da9a3c0b12 | ||
|
|
f5a9ffb776 | ||
|
|
19181b79c8 | ||
|
|
1509678578 | ||
|
|
c4d5dd4a3c | ||
|
|
ca734ca547 | ||
|
|
8d347f5162 | ||
|
|
4d368ab6f6 | ||
|
|
cabacdc44b | ||
|
|
71dac2021d | ||
|
|
a8d8d0e340 | ||
|
|
97274d1c80 | ||
|
|
aa12fcbd36 | ||
|
|
3d1c2d1fb6 | ||
|
|
f6fcbdcad0 | ||
|
|
30e6ba0809 | ||
|
|
3cb0fc9f30 | ||
|
|
5f578c3327 | ||
|
|
e56402058a | ||
|
|
19ae21e797 | ||
|
|
b89a31d4ae | ||
|
|
07e06a6ed4 | ||
|
|
89102d6612 | ||
|
|
feda173b3b | ||
|
|
02f812ce96 | ||
|
|
7250bed701 | ||
|
|
2b11b1a234 | ||
|
|
18e8aadf18 | ||
|
|
4840a292df | ||
|
|
76ca1995ca | ||
|
|
0f333b1f24 | ||
|
|
1f8eaa423f | ||
|
|
4e4d3670a2 | ||
|
|
cd9a6a80e3 | ||
|
|
429618d5f8 | ||
|
|
1e814a77cc | ||
|
|
41b0c57725 | ||
|
|
470822f4a9 | ||
|
|
946bf9ec38 | ||
|
|
49585940f7 | ||
|
|
9257d62216 | ||
|
|
3a9d2e7652 | ||
|
|
4b2e3c0ded | ||
|
|
6978df4c3c | ||
|
|
1f13189ec3 | ||
|
|
c08e8081fe | ||
|
|
d8c9b5a936 | ||
|
|
fa823e1296 | ||
|
|
de89ab957b | ||
|
|
96920495d0 | ||
|
|
92044de6b8 | ||
|
|
8e8ef300ec | ||
|
|
982220430e | ||
|
|
3f55c24e36 | ||
|
|
0c65612368 | ||
|
|
3071b7dd37 | ||
|
|
5daac24e73 | ||
|
|
bcd2828305 | ||
|
|
b5f4e9af46 | ||
|
|
92f6a6a16b | ||
|
|
019a20d027 | ||
|
|
1378feaf6a | ||
|
|
22221e6fae | ||
|
|
d773ff5391 | ||
|
|
be84f0cca6 | ||
|
|
1419c16d04 | ||
|
|
89f209014e | ||
|
|
21aec46763 | ||
|
|
333b5e50c4 | ||
|
|
e94550b7b2 |
@@ -0,0 +1,10 @@
|
||||
FROM mcr.microsoft.com/devcontainers/typescript-node:24
|
||||
|
||||
RUN corepack enable
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
git \
|
||||
curl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
EXPOSE 3000
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "Reactive Resume",
|
||||
"service": "reactive_resume",
|
||||
"dockerComposeFile": "docker-compose.yml",
|
||||
"workspaceFolder": "/workspace",
|
||||
|
||||
"forwardPorts": [3000, 4000, 5432, 8333],
|
||||
"portsAttributes": {
|
||||
"3000": { "label": "Reactive Resume", "onAutoForward": "openBrowser" },
|
||||
"4000": { "label": "Browserless (Printer)" },
|
||||
"5432": { "label": "PostgreSQL" },
|
||||
"8333": { "label": "SeaweedFS (S3)" }
|
||||
},
|
||||
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": ["biomejs.biome", "bradlc.vscode-tailwindcss", "lokalise.i18n-ally"],
|
||||
"settings": {
|
||||
"biome.enabled": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.biome": "explicit",
|
||||
"source.fixAll.biome": "explicit",
|
||||
"source.organizeImports.biome": "explicit"
|
||||
},
|
||||
"editor.defaultFormatter": "biomejs.biome",
|
||||
"typescript.tsdk": "node_modules/typescript/lib"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"postCreateCommand": "corepack enable && pnpm install"
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
services:
|
||||
reactive_resume:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: .devcontainer/Dockerfile
|
||||
volumes:
|
||||
- ..:/workspace:cached
|
||||
command: sleep infinity
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
browserless:
|
||||
condition: service_started
|
||||
seaweedfs:
|
||||
condition: service_healthy
|
||||
seaweedfs_create_bucket:
|
||||
condition: service_completed_successfully
|
||||
environment:
|
||||
TZ: Etc/UTC
|
||||
APP_URL: http://localhost:3000
|
||||
PRINTER_APP_URL: http://reactive_resume:3000
|
||||
PRINTER_ENDPOINT: ws://browserless:3000?token=1234567890
|
||||
DATABASE_URL: postgresql://postgres:postgres@postgres:5432/postgres
|
||||
AUTH_SECRET: change-me-to-a-secure-secret-key-in-production
|
||||
S3_ACCESS_KEY_ID: seaweedfs
|
||||
S3_SECRET_ACCESS_KEY: seaweedfs
|
||||
S3_REGION: us-east-1
|
||||
S3_ENDPOINT: http://seaweedfs:8333
|
||||
S3_BUCKET: reactive-resume
|
||||
S3_FORCE_PATH_STYLE: "true"
|
||||
|
||||
postgres:
|
||||
image: postgres:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: postgres
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql
|
||||
healthcheck:
|
||||
test: ["CMD", "pg_isready", "-U", "postgres", "-d", "postgres"]
|
||||
start_period: 10s
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
browserless:
|
||||
image: ghcr.io/browserless/chromium:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
QUEUED: "10"
|
||||
HEALTH: "true"
|
||||
CONCURRENT: "5"
|
||||
TOKEN: "1234567890"
|
||||
healthcheck:
|
||||
test:
|
||||
["CMD", "curl", "-f", "http://localhost:3000/pressure?token=1234567890"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
seaweedfs:
|
||||
image: chrislusf/seaweedfs:latest
|
||||
restart: unless-stopped
|
||||
command: server -s3 -filer -dir=/data -ip=0.0.0.0
|
||||
environment:
|
||||
AWS_ACCESS_KEY_ID: seaweedfs
|
||||
AWS_SECRET_ACCESS_KEY: seaweedfs
|
||||
volumes:
|
||||
- seaweedfs_data:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://localhost:8888"]
|
||||
start_period: 10s
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
seaweedfs_create_bucket:
|
||||
image: quay.io/minio/mc:latest
|
||||
restart: on-failure
|
||||
entrypoint: >
|
||||
/bin/sh -c "
|
||||
until mc alias set seaweedfs http://seaweedfs:8333 seaweedfs seaweedfs; do
|
||||
echo 'Waiting for SeaweedFS...';
|
||||
sleep 2;
|
||||
done;
|
||||
mc mb seaweedfs/reactive-resume --ignore-existing;
|
||||
"
|
||||
depends_on:
|
||||
seaweedfs:
|
||||
condition: service_healthy
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
seaweedfs_data:
|
||||
@@ -66,6 +66,10 @@ FLAG_DISABLE_SIGNUPS="false"
|
||||
# This flag disables email/password login. Disables email verification, forgot password, and reset password flows. Users can still sign up via social auth (Google/GitHub/Custom OAuth), unless FLAG_DISABLE_SIGNUPS is also set to true.
|
||||
FLAG_DISABLE_EMAIL_AUTH="false"
|
||||
|
||||
# This flag disables the image processing.
|
||||
# This is useful if you are using a machine with limited resources, like a Raspberry Pi.
|
||||
FLAG_DISABLE_IMAGE_PROCESSING="false"
|
||||
|
||||
# --- Others ---
|
||||
# Google Cloud API Key (optional)
|
||||
# This is not used within Reactive Resume, but in src/scripts/fonts/generate.ts to generate a list of fonts served by Google Fonts.
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
locales/*.po linguist-generated=true
|
||||
@@ -2,8 +2,6 @@ name: Build Docker Image
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
tags: [ "v*.*.*" ]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@@ -204,4 +202,14 @@ jobs:
|
||||
- name: Inspect image
|
||||
run: |
|
||||
docker buildx imagetools inspect ghcr.io/${{ env.IMAGE }}:v${{ steps.version.outputs.version }}
|
||||
docker buildx imagetools inspect docker.io/${{ env.IMAGE }}:v${{ steps.version.outputs.version }}
|
||||
docker buildx imagetools inspect docker.io/${{ env.IMAGE }}:v${{ steps.version.outputs.version }}
|
||||
|
||||
- name: Redeploy Stack
|
||||
uses: appleboy/ssh-action@v1
|
||||
with:
|
||||
key: ${{ secrets.SSH_KEY }}
|
||||
host: ${{ secrets.SSH_HOST }}
|
||||
username: ${{ secrets.SSH_USER }}
|
||||
script: |
|
||||
cd docker
|
||||
./manage_stack.sh up reactive_resume
|
||||
@@ -5,6 +5,7 @@ dist
|
||||
.output
|
||||
.vercel
|
||||
.cursor
|
||||
TODO.md
|
||||
coverage
|
||||
.netlify
|
||||
.DS_Store
|
||||
@@ -12,5 +13,7 @@ coverage
|
||||
node_modules
|
||||
!.env.example
|
||||
public/sw.js
|
||||
public/sw.js.map
|
||||
scripts/**/*.json
|
||||
public/workbox-*.js
|
||||
public/workbox-*.js
|
||||
public/workbox-*.js.map
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
const nextPackages = ["@monaco-editor/react"];
|
||||
|
||||
const betaPackages = ["drizzle-orm", "drizzle-kit", "@better-auth/core", "@better-auth/passkey", "better-auth"];
|
||||
const betaPackages = ["vite", "drizzle-orm", "drizzle-kit", "@better-auth/core", "@better-auth/passkey", "better-auth"];
|
||||
|
||||
/** @type {import('npm-check-updates').RunOptions} */
|
||||
module.exports = {
|
||||
|
||||
@@ -10,22 +10,21 @@
|
||||
"*.css": "tailwindcss"
|
||||
},
|
||||
"files.readonlyInclude": {
|
||||
"locales/*.po": true,
|
||||
"pnpm-lock.yaml": true,
|
||||
"**/routeTree.gen.ts": true
|
||||
"**/routeTree.gen.ts": true,
|
||||
"pnpm-lock.yaml": true
|
||||
},
|
||||
"files.watcherExclude": {
|
||||
"locales/*.po": true,
|
||||
"**/routeTree.gen.ts": true,
|
||||
"pnpm-lock.yaml": true,
|
||||
"**/routeTree.gen.ts": true
|
||||
"locales/**.po": true
|
||||
},
|
||||
"i18n-ally.enabledParsers": ["po"],
|
||||
"i18n-ally.localesPaths": ["locales"],
|
||||
"i18n-ally.sourceLanguage": "en-US",
|
||||
"search.exclude": {
|
||||
"locales/*.po": true,
|
||||
"**/routeTree.gen.ts": true,
|
||||
"pnpm-lock.yaml": true,
|
||||
"**/routeTree.gen.ts": true
|
||||
"locales/**.po": true
|
||||
},
|
||||
"tailwindCSS.classFunctions": ["cn", "cva"],
|
||||
"tailwindCSS.experimental.classRegex": [
|
||||
@@ -34,5 +33,5 @@
|
||||
["cn\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"]
|
||||
],
|
||||
"tailwindCSS.experimental.configFile": "src/styles/globals.css",
|
||||
"typescript.tsdk": "node_modules/typescript/lib"
|
||||
"typescript.experimental.useTsgo": true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
Reactive Resume is a free, open-source resume builder built with TanStack Start (React 19 + Vite 8), using ORPC for type-safe RPC APIs, Drizzle ORM with PostgreSQL, Nitro as the server runtime, and Better Auth for authentication. It is a PWA with 47 supported locales and 13 resume templates.
|
||||
|
||||
## Development Commands
|
||||
|
||||
```bash
|
||||
# Start development server (runs on port 3000)
|
||||
pnpm dev
|
||||
|
||||
# Build for production
|
||||
pnpm build
|
||||
|
||||
# Start production server
|
||||
pnpm start
|
||||
|
||||
# Linting and formatting (uses Biome)
|
||||
pnpm lint
|
||||
|
||||
# Type checking (uses tsgo)
|
||||
pnpm typecheck
|
||||
|
||||
# Database operations
|
||||
pnpm db:generate # Generate migration files
|
||||
pnpm db:migrate # Run migrations
|
||||
pnpm db:push # Push schema changes directly
|
||||
pnpm db:studio # Open Drizzle Studio
|
||||
|
||||
# Extract i18n strings for translation
|
||||
pnpm lingui:extract
|
||||
|
||||
# Find unused exports / dead code
|
||||
dotenvx run -- pnpm knip
|
||||
```
|
||||
|
||||
**There is no test framework configured.** No unit, integration, or E2E tests exist in the codebase.
|
||||
|
||||
## Local Development Setup
|
||||
|
||||
1. Copy `.env.example` to `.env` and configure environment variables
|
||||
2. Start required services: `docker compose -f compose.dev.yml up -d`
|
||||
- PostgreSQL (port 5432)
|
||||
- Browserless/Chromium for PDF generation (port 4000)
|
||||
- SeaweedFS for S3-compatible storage (port 8333)
|
||||
- Mailpit for email testing (ports 1025, 8025)
|
||||
- Adminer for DB management (port 8080)
|
||||
3. Run `pnpm dev`
|
||||
|
||||
Database migrations run automatically on server startup via the Nitro plugin at `plugins/1.migrate.ts`.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Directory Structure
|
||||
|
||||
- `src/routes/` - TanStack Router file-based routing
|
||||
- `src/integrations/` - External service integrations (auth, database, ORPC, AI, email, import)
|
||||
- `src/integrations/orpc/router/` - oRPC server routers (procedure definitions)
|
||||
- `src/integrations/orpc/services/` - oRPC server services (business logic)
|
||||
- `src/integrations/orpc/dto/` - Data transfer objects
|
||||
- `src/integrations/orpc/context.ts` - Auth and request context setup
|
||||
- `src/components/` - React components organized by feature
|
||||
- `src/components/ui/` - Shadcn UI components (Radix + Phosphor icons)
|
||||
- `src/schema/` - Zod schemas for validation
|
||||
- `src/hooks/` - Custom React hooks
|
||||
- `plugins/` - Nitro server plugins (auto-migration on startup)
|
||||
- `migrations/` - Drizzle database migrations
|
||||
- `locales/` - i18n translation files (managed by Lingui)
|
||||
- `docs/` - Documentation (Mintlify)
|
||||
|
||||
### Key Integrations (`src/integrations/`)
|
||||
|
||||
- **auth/** - Better Auth configuration (session-based + API key via `x-api-key` header)
|
||||
- **drizzle/** - Database schema and client (PostgreSQL)
|
||||
- **orpc/** - Type-safe RPC router with procedures for ai, auth, flags, printer, resume, statistics, storage
|
||||
- **query/** - TanStack Query client configuration
|
||||
- **ai/** - AI provider integrations (OpenAI, Anthropic, Google Gemini, Ollama)
|
||||
- **email/** - Nodemailer integration (falls back to console logging if SMTP is not configured)
|
||||
- **import/** - Resume file parsing/import
|
||||
|
||||
### ORPC Procedure Types
|
||||
|
||||
Three procedure types exist in `src/integrations/orpc/context.ts`:
|
||||
- `publicProcedure` - No authentication required
|
||||
- `protectedProcedure` - Requires authenticated user (session or API key)
|
||||
- `serverOnlyProcedure` - Server-side calls only
|
||||
|
||||
Procedures follow this pattern:
|
||||
```ts
|
||||
const handler = protectedProcedure
|
||||
.route({ method: "GET", path: "/resumes/{id}", tags: ["Resumes"], ... })
|
||||
.input(schema)
|
||||
.output(schema)
|
||||
.handler(async ({ context, input }) => { ... })
|
||||
```
|
||||
|
||||
### Resume Data Model
|
||||
|
||||
The resume schema is defined in `src/schema/resume/data.ts`. Key concepts:
|
||||
- **ResumeData** - Complete resume data including basics, sections, customSections, metadata
|
||||
- **Sections** - Built-in sections (profiles, experience, education, skills, etc.)
|
||||
- **CustomSections** - User-created sections that follow one of the built-in section types
|
||||
- **Metadata** - Template, layout, typography, design settings, custom CSS
|
||||
|
||||
### Resume Templates
|
||||
|
||||
13 templates in `src/components/resume/templates/` (Pokemon-themed names):
|
||||
azurill, bronzor, chikorita, ditgar, ditto, gengar, glalie, kakuna, lapras, leafish, onyx, pikachu, rhyhorn
|
||||
|
||||
Shared rendering components live in `src/components/resume/shared/`.
|
||||
|
||||
### Database Schema
|
||||
|
||||
Defined in `src/integrations/drizzle/schema.ts`:
|
||||
- `user`, `session`, `account`, `verification`, `twoFactor`, `passkey`, `apikey` - Better Auth tables
|
||||
- `resume` - Stores Resume Data as JSONB (defined in `src/schema/resume/data.ts`)
|
||||
- `resumeStatistics` - Views/Download tracking
|
||||
|
||||
### Routing
|
||||
|
||||
Uses TanStack Router with file-based routing. Key routes:
|
||||
- `/_home/` - Public landing pages
|
||||
- `/auth/` - Authentication flows
|
||||
- `/dashboard/` - User dashboard and resume management
|
||||
- `/builder/$resumeId/` - Resume editor
|
||||
- `/printer/$resumeId/` - PDF rendering endpoint
|
||||
- `/api/` - Public API endpoints
|
||||
- `/mcp/` - MCP server endpoint for LLM integration
|
||||
|
||||
Routes use `createFileRoute()` with `beforeLoad()` for auth guards and `loader()` for server-side data fetching.
|
||||
|
||||
### MCP Server
|
||||
|
||||
An MCP (Model Context Protocol) server is available at `/mcp/` for LLM-based resume interaction. It requires an `x-api-key` header for authentication. Configuration is in `src/routes/mcp/` with helper modules for resources, prompts, and tools.
|
||||
|
||||
### State Management
|
||||
|
||||
- **Zustand** - Client-side state (resume editor state in `src/components/resume/store/`)
|
||||
- **Zundo** - Undo/redo history for resume edits (built on Zustand)
|
||||
- **TanStack Query** - Server state and caching (configured via ORPC integration)
|
||||
|
||||
### Global Providers
|
||||
|
||||
Defined in `src/routes/__root.tsx`:
|
||||
- I18nProvider (Lingui), ThemeProvider, MotionConfig, IconContext (Phosphor Icons)
|
||||
- ConfirmDialogProvider, PromptDialogProvider, DialogManager, CommandPalette, Toaster
|
||||
|
||||
## Code Style
|
||||
|
||||
- Uses **Biome** for linting and formatting (`biome.json`)
|
||||
- Tab indentation, double quotes, 120 character line width
|
||||
- Imports are auto-organized; unused imports are errors
|
||||
- a11y rules are disabled
|
||||
- Path alias: `@/` maps to `src/`
|
||||
- Tailwind CSS v4 with sorted class names (enforced by Biome's `useSortedClasses`)
|
||||
- Uses `cn()` utility (from `@/utils/style`) for conditional class names
|
||||
- Uses `cva()` for component variants
|
||||
- Shadcn UI components in `src/components/ui/` (Radix UI + Phosphor icons, zinc base color)
|
||||
- i18n strings use Lingui macros: `<Trans>`, `t`, `msg`
|
||||
- TypeScript strict mode enabled; `noUnusedLocals` and `noUnusedParameters` enforced
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Key variables (see `.env.example` for full list):
|
||||
- `APP_URL` - Application URL
|
||||
- `DATABASE_URL` - PostgreSQL connection string
|
||||
- `AUTH_SECRET` - Secret for authentication
|
||||
- `PRINTER_ENDPOINT` - WebSocket endpoint for PDF printer service
|
||||
- `PRINTER_APP_URL` - Internal URL for printer to reach the app (important for Docker)
|
||||
- `S3_*` - S3-compatible storage configuration (falls back to local `/data` filesystem)
|
||||
- `SMTP_*` - Email configuration (falls back to console logging)
|
||||
- `GOOGLE_CLIENT_ID/SECRET` - Google OAuth (optional)
|
||||
- `GITHUB_CLIENT_ID/SECRET` - GitHub OAuth (optional)
|
||||
- `OAUTH_*` - Custom OAuth provider (optional)
|
||||
- `FLAG_DEBUG_PRINTER` - Debug PDF printing endpoint
|
||||
- `FLAG_DISABLE_SIGNUPS` - Block new account registration
|
||||
- `FLAG_DISABLE_EMAIL_AUTH` - Disable email/password login
|
||||
- `FLAG_DISABLE_IMAGE_PROCESSING` - Disable image processing
|
||||
|
||||
## Build & Deployment
|
||||
|
||||
- **Build output**: `.output/` directory (Nitro server bundle)
|
||||
- **Production start**: `node .output/server/index.mjs`
|
||||
- **Docker**: Multi-stage Dockerfile with Node 24-slim base
|
||||
- **Health check**: `GET /api/health`
|
||||
- **PWA**: Configured via vite-plugin-pwa with auto-update, standalone display, dark theme
|
||||
@@ -60,4 +60,4 @@ EXPOSE 3000/tcp
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
|
||||
CMD curl -f http://localhost:3000/api/health || exit 1
|
||||
|
||||
ENTRYPOINT ["node", "-r", "reflect-metadata", ".output/server/index.mjs"]
|
||||
ENTRYPOINT ["node", ".output/server/index.mjs"]
|
||||
|
||||
@@ -1,15 +1,3 @@
|
||||
<div align="center" markdown="1">
|
||||
<a href="https://go.warp.dev/Reactive-Resume">
|
||||
<img alt="Warp Sponsorship" width="400" src="https://github.com/warpdotdev/brand-assets/blob/main/Github/Sponsor/Warp-Github-LG-03.png?raw=true" />
|
||||
</a>
|
||||
|
||||
### [Warp, built for coding with multiple AI agents.](https://go.warp.dev/Reactive-Resume)
|
||||
|
||||
[Available for MacOS, Linux, & Windows](https://go.warp.dev/Reactive-Resume)
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
<a href="https://rxresu.me">
|
||||
<img src="public/opengraph/banner.jpg" alt="Reactive Resume" />
|
||||
@@ -26,15 +14,15 @@
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<img src="https://img.shields.io/github/stars/amruthpillai/Reactive-Resume?style=flat-square" alt="Stars" />
|
||||
<img src="https://img.shields.io/github/package-json/v/amruthpillai/reactive-resume?style=flat-square" alt="Reactive Resume version">
|
||||
<img src="https://img.shields.io/github/stars/amruthpillai/Reactive-Resume?style=flat-square" alt="GitHub Stars">
|
||||
<img src="https://img.shields.io/github/license/amruthpillai/Reactive-Resume?style=flat-square" alt="License" />
|
||||
<img src="https://img.shields.io/docker/pulls/amruthpillai/reactive-resume?style=flat-square" alt="Docker Pulls" />
|
||||
<a href="https://discord.gg/hzwkZbyvUW"><img src="https://img.shields.io/discord/1173518977851473940?label=Discord&style=flat-square&logo=discord" alt="Discord" /></a>
|
||||
<a href="https://discord.gg/aSyA5ZSxpb"><img src="https://img.shields.io/discord/1173518977851473940?style=flat-square&label=discord" alt="Discord" /></a>
|
||||
<a href="https://crowdin.com/project/reactive-resume"><img src="https://badges.crowdin.net/reactive-resume/localized.svg?style=flat-square" alt="Crowdin" /></a>
|
||||
<a href="https://opencollective.com/reactive-resume"><img src="https://img.shields.io/opencollective/all/reactive-resume?style=flat-square" alt="Open Collective" /></a>
|
||||
<a href="https://github.com/sponsors/AmruthPillai"><img src="https://img.shields.io/github/sponsors/AmruthPillai?style=flat-square&label=sponsors" alt="Sponsors" /></a>
|
||||
<a href="https://opencollective.com/reactive-resume"><img src="https://img.shields.io/opencollective/backers/reactive-resume?style=flat-square&label=donations" alt="Donations" /></a>
|
||||
</p>
|
||||
|
||||
<a href="https://www.producthunt.com/products/reactive-resume?embed=true&utm_source=badge-featured&utm_medium=badge&utm_campaign=badge-reactive-resume-v5-2" target="_blank" rel="noopener"><img alt="Reactive Resume v5 - A free and open-source resume builder. | Product Hunt" width="250" height="54" src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=1065182&theme=light&t=1768850745585"></a>
|
||||
</div>
|
||||
|
||||
---
|
||||
@@ -157,6 +145,8 @@ docker compose up -d
|
||||
open http://localhost:3000
|
||||
```
|
||||
|
||||
[](https://app.ona.com/#https://github.com/amruthpillai/reactive-resume)
|
||||
|
||||
For detailed setup instructions, environment configuration, and self-hosting guides, see the [documentation](https://docs.rxresu.me).
|
||||
|
||||
## Tech Stack
|
||||
@@ -180,7 +170,7 @@ Comprehensive guides are available at [docs.rxresu.me](https://docs.rxresu.me):
|
||||
| Guide | Description |
|
||||
| --------------------------------------------------------------------------- | --------------------------------- |
|
||||
| [Getting Started](https://docs.rxresu.me/getting-started) | First-time setup and basic usage |
|
||||
| [Self-Hosting](https://docs.rxresu.me/guides/self-hosting-with-docker) | Deploy on your own server |
|
||||
| [Self-Hosting](https://docs.rxresu.me/self-hosting/docker) | Deploy on your own server |
|
||||
| [Development Setup](https://docs.rxresu.me/contributing/development) | Local development environment |
|
||||
| [Project Architecture](https://docs.rxresu.me/contributing/architecture) | Codebase structure and patterns |
|
||||
| [Exporting Your Resume](https://docs.rxresu.me/guides/exporting-your-resume)| PDF and JSON export options |
|
||||
@@ -203,7 +193,7 @@ docker pull amruthpillai/reactive-resume:latest
|
||||
docker pull ghcr.io/amruthpillai/reactive-resume:latest
|
||||
```
|
||||
|
||||
See the [self-hosting guide](https://docs.rxresu.me/guides/self-hosting-with-docker) for complete instructions.
|
||||
See the [self-hosting guide](https://docs.rxresu.me/self-hosting/docker) for complete instructions.
|
||||
|
||||
## Support
|
||||
|
||||
@@ -225,6 +215,16 @@ Other ways to support:
|
||||
- Improve documentation
|
||||
- Help with translations
|
||||
|
||||
## Star History
|
||||
|
||||
<a href="https://www.star-history.com/#amruthpillai/reactive-resume&type=date&legend=top-left">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=amruthpillai/reactive-resume&type=date&theme=dark&legend=top-left" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=amruthpillai/reactive-resume&type=date&legend=top-left" />
|
||||
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=amruthpillai/reactive-resume&type=date&legend=top-left" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions make open-source thrive. Whether fixing a typo or adding a feature, all contributions are welcome.
|
||||
|
||||
@@ -8,7 +8,13 @@
|
||||
},
|
||||
"files": {
|
||||
"ignoreUnknown": true,
|
||||
"includes": ["**", "!**/webfontlist.json", "!**/docs/spec.json", "!**/src/routeTree.gen.ts"]
|
||||
"includes": [
|
||||
"**",
|
||||
"!**/docs/spec.json",
|
||||
"!**/webfontlist.json",
|
||||
"!**/schema/schema.json",
|
||||
"!**/src/routeTree.gen.ts"
|
||||
]
|
||||
},
|
||||
"formatter": {
|
||||
"lineWidth": 120,
|
||||
@@ -18,8 +24,7 @@
|
||||
"actions": {
|
||||
"recommended": true,
|
||||
"source": {
|
||||
"organizeImports": "on",
|
||||
"useSortedProperties": "on"
|
||||
"organizeImports": "on"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -32,6 +37,7 @@
|
||||
},
|
||||
"correctness": {
|
||||
"noUnknownTypeSelector": "off",
|
||||
"useExhaustiveDependencies": "off",
|
||||
"noUnusedImports": {
|
||||
"fix": "safe",
|
||||
"level": "error",
|
||||
|
||||
@@ -20,9 +20,9 @@ services:
|
||||
image: postgres:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: postgres
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
- POSTGRES_DB=postgres
|
||||
- POSTGRES_USER=postgres
|
||||
- POSTGRES_PASSWORD=postgres
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql
|
||||
ports:
|
||||
@@ -40,9 +40,9 @@ services:
|
||||
ports:
|
||||
- "4000:3000"
|
||||
environment:
|
||||
- QUEUED=30
|
||||
- QUEUED=10
|
||||
- HEALTH=true
|
||||
- CONCURRENT=20
|
||||
- CONCURRENT=5
|
||||
- TOKEN=1234567890
|
||||
healthcheck:
|
||||
test:
|
||||
@@ -53,11 +53,11 @@ services:
|
||||
|
||||
# As an alternative to browserless, you can also use a lightweight image like chromedp/headless-shell:latest
|
||||
# See https://docs.rxresu.me/self-hosting/docker#alternative-printer-options for more information.
|
||||
# chrome:
|
||||
# image: chromedp/headless-shell:latest
|
||||
# restart: unless-stopped
|
||||
# ports:
|
||||
# - "9222:9222"
|
||||
chrome:
|
||||
image: chromedp/headless-shell:latest
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "9222:9222"
|
||||
|
||||
seaweedfs:
|
||||
image: chrislusf/seaweedfs:latest
|
||||
@@ -77,7 +77,7 @@ services:
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
seaweedfs-create-bucket:
|
||||
seaweedfs_create_bucket:
|
||||
image: quay.io/minio/mc:latest
|
||||
restart: on-failure
|
||||
entrypoint: >
|
||||
|
||||
@@ -5,9 +5,9 @@ services:
|
||||
image: postgres:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: postgres
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
- POSTGRES_DB=postgres
|
||||
- POSTGRES_USER=postgres
|
||||
- POSTGRES_PASSWORD=postgres
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql
|
||||
healthcheck:
|
||||
@@ -21,11 +21,10 @@ services:
|
||||
image: ghcr.io/browserless/chromium:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- QUEUED=30
|
||||
- QUEUED=10
|
||||
- HEALTH=true
|
||||
- CONCURRENT=20
|
||||
- CONCURRENT=5
|
||||
- TOKEN=1234567890
|
||||
- EXTERNAL=http://localhost:4000
|
||||
healthcheck:
|
||||
test:
|
||||
["CMD", "curl", "-f", "http://localhost:3000/pressure?token=1234567890"]
|
||||
@@ -57,7 +56,7 @@ services:
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
seaweedfs-create-bucket:
|
||||
seaweedfs_create_bucket:
|
||||
image: quay.io/minio/mc:latest
|
||||
restart: on-failure
|
||||
entrypoint: >
|
||||
@@ -71,7 +70,7 @@ services:
|
||||
seaweedfs:
|
||||
condition: service_healthy
|
||||
|
||||
app:
|
||||
reactive_resume:
|
||||
image: amruthpillai/reactive-resume:latest
|
||||
# image: ghcr.io/amruthpillai/reactive-resume:latest
|
||||
environment:
|
||||
@@ -79,7 +78,7 @@ services:
|
||||
- TZ=Etc/UTC
|
||||
- NODE_ENV=production
|
||||
- APP_URL=http://localhost:3000
|
||||
- PRINTER_APP_URL=http://app:3000
|
||||
- PRINTER_APP_URL=http://reactive_resume:3000
|
||||
# Printer
|
||||
- PRINTER_ENDPOINT=ws://browserless:3000?token=1234567890
|
||||
# - PRINTER_ENDPOINT=http://chrome:9222 # Or, if you're using chromedp/headless-shell
|
||||
@@ -102,7 +101,7 @@ services:
|
||||
condition: service_healthy
|
||||
browserless:
|
||||
condition: service_healthy
|
||||
seaweedfs-create-bucket:
|
||||
seaweedfs_create_bucket:
|
||||
condition: service_completed_successfully
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
---
|
||||
title: "Changelog"
|
||||
description: "List of all notable changes and updates to Reactive Resume"
|
||||
rss: true
|
||||
---
|
||||
|
||||
<Update label="v5.0.10" description="24th February 2026">
|
||||
- **Fixes**
|
||||
- Show section titles for summary-type custom sections in the resume builder. [#2744](https://github.com/amruthpillai/reactive-resume/pull/2744)
|
||||
- Prevent browser password managers and Edge autofill/save prompts from appearing on AI settings API key fields. [#2719](https://github.com/amruthpillai/reactive-resume/pull/2719)
|
||||
- Replace deprecated Tailwind CSS classes: use `inset-s-*`/`inset-e-*` instead of `start-*`/`end-*`.
|
||||
- Fix PDF downloader to work correctly in offline mode. [#2743](https://github.com/amruthpillai/reactive-resume/pull/2743)
|
||||
- Make bold formatting visible for `<strong>` in the resume rich text editor (uses plain `font-weight: bold` fallback if the CSS variable is unset). Fixes [#2730](https://github.com/amruthpillai/reactive-resume/issues/2730)
|
||||
- Prevent credentials sign-in from dropping `session_token` Set-Cookie, improves login reliability. [#2718](https://github.com/amruthpillai/reactive-resume/pull/2718)
|
||||
- Remove redundant `resume-` prefix from download filename; add spacing between pages in shared view. [#2709](https://github.com/amruthpillai/reactive-resume/pull/2709)
|
||||
- Normalize autocomplete tokens for login and register forms. [#2714](https://github.com/amruthpillai/reactive-resume/pull/2714)
|
||||
- Fix improper chips reordering and update dependency/translations. [#2711](https://github.com/amruthpillai/reactive-resume/issues/2711)
|
||||
- Fix issue with clipping of heading in Lapras resume template.
|
||||
- Remove error-causing plugins in oRPC integration.
|
||||
- Remove duplicate database indexes; add index for `created_at` on user and resume tables.
|
||||
|
||||
- **Features & Improvements**
|
||||
- Add new feature flag: `FLAG_DISABLE_IMAGE_PROCESSING` (allows disabling of image processing site-wide).
|
||||
- Update Discord invite link in the app and documentation.
|
||||
- Add comprehensive codebase and architecture documentation in `CLAUDE.md`.
|
||||
- Sync latest translations from Crowdin (updated and added multiple languages).
|
||||
|
||||
- **Other**
|
||||
- Refactor: rename auth utility from `originWith` to `withHostname`, preserve localhost/127.0.0.1 sibling trust.
|
||||
- General dependency updates and code style improvements.
|
||||
- Documentation and README updates.
|
||||
</Update>
|
||||
|
||||
|
||||
<Update label="v5.0.9" description="9th February 2026">
|
||||
- Add Computer Modern web fonts to the font selector, allowing the user to choose from a variety of "Computer Modern" (LaTeX) fonts.
|
||||
- This lets you create a resume that looks just like a LaTeX document, with the same fonts and styles.
|
||||
- Update dependencies to the latest versions.
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.8" description="9th February 2026">
|
||||
- Remove Passkey support from the authentication system, as it was causing issues with the authentication provider.
|
||||
- Update dependencies to the latest versions.
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.7" description="9th February 2026">
|
||||
- Introduce a new **MCP (Model Context Protocol) server** that lets you manage and edit resumes from any MCP-compatible AI tool — Claude Desktop, Cursor, Codex, and more. Supports listing, reading, creating, deleting, locking/unlocking, and patching resumes via natural language. [(guide)](/guides/using-the-mcp-server)
|
||||
- Add an **AI Chat** panel to the resume builder, allowing you to modify your resume through conversational AI directly within the editor. The chat uses tool-calling to apply JSON Patch operations to your resume in real-time, with visual feedback for each change.
|
||||
- Add a system prompt and `patch_resume` tool for the AI chat, enabling structured, minimal-diff resume edits following RFC 6902 JSON Patch operations.
|
||||
- Chat history is now persisted per resume in localStorage, so conversations are preserved across sessions.
|
||||
- Fix rendering issues in the Lapras and Onyx resume templates.
|
||||
- Improvements to the Combobox and ScrollArea UI components.
|
||||
- Fix an issue with skills item rendering in the shared resume components.
|
||||
- Update authentication configuration and auth route handling.
|
||||
- Update the JSON Schema to reflect the latest resume data model.
|
||||
- Update dependencies to the latest versions.
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.6" description="8th February 2026">
|
||||
- Implement Atomic Resume Patching API for fine-grained resume updates, allowing partial, atomic updates to resumes via a new PATCH endpoint. [#2692](https://github.com/amruthpillai/reactive-resume/pull/2692)
|
||||
- The API endpoint `PUT /resume/{id}` now returns the updated resume object instead of void. Also, resume routing/services now use explicit DTOs for input and output schemas. [#2688](https://github.com/amruthpillai/reactive-resume/pull/2688)
|
||||
- Add error logging for API server errors (server-side only) to improve debugging and reliability.
|
||||
- Refactor and clean up imports/exports for clarity and maintainability.
|
||||
- Added `.devcontainer` configuration for improved contributor development environment.
|
||||
- Update dependencies to the latest versions.
|
||||
- Add build status badge and documentation link to README.
|
||||
- Sync latest translations from Crowdin (notably: French, other languages).
|
||||
- Other minor fixes and improvements.
|
||||
</Update>
|
||||
|
||||
|
||||
<Update label="v5.0.5" description="31st January 2026">
|
||||
- Implement Cover Letter functionality in the resume builder, allowing the user to create cover letters as custom sections. [(link)](/guides/adding-a-cover-letter)
|
||||
- Implement full-screen mode for the rich text editor in the resume builder, allowing the user to write in a more focused environment.
|
||||
- Implement a new custom section type: `summary`, which allows the user to add another "summary" like section to the resume.
|
||||
- Implement a `useFormBlocker` hook to prevent the user from closing a dialog while the form has unsaved changes.
|
||||
- Fix an issue where keywords spacing was not consistent in the interests section. [#2631](https://github.com/amruthpillai/reactive-resume/pull/2631)
|
||||
- Fix an issue where the AI connection test was not working correctly, also return appropriate error messages for AI provider issues.
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.4" description="28th January 2026">
|
||||
- Bring back Undo/Redo functionality in the resume builder for improved editing experience.
|
||||
- Arrange the sidebar builder dynamically based on the section type in each template. [#2564](https://github.com/amruthpillai/reactive-resume/pull/2603)
|
||||
- Remove extra spacing when proficiency is empty. [#2607](https://github.com/amruthpillai/reactive-resume/pull/2626)
|
||||
- Fix rendering in Pikachu template: conditionally render header and page picture using `isFirstPage`, and respect the `fullWidth` property for page layout.
|
||||
- Fixes to templates to improve layout and rendering consistency.
|
||||
- Fix GitHub OAuth login for users migrated from previous versions.
|
||||
- Improve communication with the printer service and reduce resource usage for better PDF generation reliability.
|
||||
- Update and sync translations from Crowdin (Afrikaans, Persian, Portuguese/Brazilian, and other languages).
|
||||
- Update translation sources and configuration.
|
||||
- Fix "empty" Git merge remnants in codebase.
|
||||
- Update package dependencies and fix self-hosting guide links in README.
|
||||
- Remove dead code, update screenshots, and add PWA (Progressive Web App) support.
|
||||
- Update links to PDF example files in documentation.
|
||||
- Other bug fixes and minor improvements (#2542, #2573, #2598).
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.3" description="25th January 2026">
|
||||
- Implement the ability to print Free-Form PDFs which do not have a fixed page height, allowing the user to fit the content on a page as they see fit. [(link)](/guides/selecting-page-format)
|
||||
- Allow the user to override the default endpoint for all AI providers, not just Ollama. Also display the default endpoint for each provider in the AI settings page.
|
||||
- Updated the chip input component, to allow the user to add, edit, remove or reorder keywords for skills and other sections.
|
||||
- Improved RTL support across the app, thanks to @obreo for the contribution. [(link)](https://github.com/amruthpillai/reactive-resume/pull/2583)
|
||||
- Updates to the translation configuration to remove line numbers from the translation files, as this was causing unnecessary diffs.
|
||||
- Updated the video on the homepage to be lighter and faster to load, while still maintaining the same quality.
|
||||
- Increased the screenshot TTL for resumes, to avoid regenerating screenshots unnecessarily unless the resume has been updated recently (in the last hour).
|
||||
- Update dependencies and translations to the latest versions.
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.2" description="24th January 2026">
|
||||
- Added an agent skill `skills/resume-builder` for agentic AI assistants to build resumes for Reactive Resume through conversational AI.
|
||||
- Added a new guide on how to fit content on a page, to avoid issues when exporting to PDF. [(link)](/guides/fitting-content-on-a-page)
|
||||
- Display an alert when the content is too tall for a page, to help the user fit the content on a page.
|
||||
- Fix an issue with the Ditgar template, where the page was not respecting the `fullWidth` setting.
|
||||
- Updated the JSON Schema to conform to a proper format.
|
||||
- Updated the Discord Server invite link to a new one.
|
||||
- Updated dependencies to the latest versions.
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.1" description="23th January 2026">
|
||||
- Updated translations from Crowdin.
|
||||
- Added a Community Spotlight section to the documentation.
|
||||
- Remove `-r require-metadata` from the Dockerfile as it was not needed.
|
||||
- Fixed inconsistencies in the docker compose examples in the documentation.
|
||||
- Fixed an issue with usernames not allowing hyphens in them.
|
||||
- Fixed issues with the printer service, when using the `getResumeScreenshot` or `printResumeAsPDF` endpoints.
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.0" description="22th January 2026">
|
||||
This has been a major overhaul from the previous version of Reactive Resume. The app has been completely redesigned and rebuilt from scratch, to be more intuitive and user-friendly.
|
||||
|
||||
**Here are some of the key changes from the previous version:**
|
||||
- 2 new templates: _Ditgar_ and _Lapras_
|
||||
- Authentication via Passkeys
|
||||
- New user interface and refreshed design
|
||||
- API Access and Reference Documentation
|
||||
- AI Integration with OpenAI, Google, Anthropic, and Ollama
|
||||
- A better font selector, with real-time preview of the font
|
||||
- More comprehensive documentation, with guides for specific features
|
||||
- An improved templates gallery, with clear overview of each template
|
||||
- Ability to import resumes from PDF or DOCX (requires AI Integration)
|
||||
- Reliable server infrastructure, with improved performance and scalability
|
||||
- Ability to move items between sections and pages, for better organization
|
||||
- A more powerful CSS Editor, with better autocomplete and syntax highlighting
|
||||
- Extended Custom Sections to include a type, for better customization and organization
|
||||
- Ability to choose the kind of icons to display for level indicators (stars, circles, custom icons, etc.)
|
||||
- Ability to resize how wide/narrow the sidebar should be on the resume, or convert a page to be full width
|
||||
|
||||
There's still a lot more that I'm forgetting, but I'm sure you'll find out soon enough as you explore the new version. I hope you enjoy building your resume now, better than ever. If you have any feedback, please feel free to [contact me](https://amruthpillai.com/#contact) or [open an issue](https://github.com/amruthpillai/reactive-resume/issues) on GitHub.
|
||||
</Update>
|
||||
@@ -299,13 +299,13 @@ pnpm run typecheck
|
||||
|
||||
```bash
|
||||
docker compose -f compose.dev.yml logs seaweedfs
|
||||
docker compose -f compose.dev.yml logs seaweedfs-create-bucket
|
||||
docker compose -f compose.dev.yml logs seaweedfs_create_bucket
|
||||
```
|
||||
|
||||
If the bucket wasn't created, restart the bucket creation service:
|
||||
|
||||
```bash
|
||||
docker compose -f compose.dev.yml restart seaweedfs-create-bucket
|
||||
docker compose -f compose.dev.yml restart seaweedfs_create_bucket
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"navigation": {
|
||||
"tabs": [
|
||||
{
|
||||
"icon": "book",
|
||||
"tab": "Documentation",
|
||||
"groups": [
|
||||
{
|
||||
@@ -34,8 +35,8 @@
|
||||
"pages": [
|
||||
"getting-started/index",
|
||||
"getting-started/quickstart",
|
||||
"guides/accessing-the-previous-version",
|
||||
"getting-started/changelog"
|
||||
"guides/checking-service-status",
|
||||
"guides/accessing-the-previous-version"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -52,7 +53,10 @@
|
||||
"pages": [
|
||||
"guides/creating-your-first-resume",
|
||||
"guides/choosing-a-template",
|
||||
"guides/selecting-page-format",
|
||||
"guides/moving-items-between-sections",
|
||||
"guides/fitting-content-on-a-page",
|
||||
"guides/adding-a-cover-letter",
|
||||
"guides/exporting-your-resume",
|
||||
"guides/sharing-your-resume-publicly",
|
||||
"guides/using-private-notes",
|
||||
@@ -65,7 +69,13 @@
|
||||
},
|
||||
{
|
||||
"group": "Integrations",
|
||||
"pages": ["guides/using-the-api", "guides/using-ai", "guides/json-resume-schema"]
|
||||
"pages": [
|
||||
"guides/using-the-api",
|
||||
"guides/using-the-patch-api",
|
||||
"guides/using-the-mcp-server",
|
||||
"guides/using-ai",
|
||||
"guides/json-resume-schema"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Self-Hosting",
|
||||
@@ -86,6 +96,12 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"icon": "clock",
|
||||
"tab": "Changelog",
|
||||
"pages": ["changelog/index"]
|
||||
},
|
||||
{
|
||||
"icon": "code",
|
||||
"tab": "API Reference",
|
||||
"openapi": "/spec.json"
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
---
|
||||
title: "Changelog"
|
||||
description: "List of all notable changes and updates to Reactive Resume"
|
||||
rss: true
|
||||
---
|
||||
|
||||
<Update label="v5.0.0" description="26th January 2026">
|
||||
This has been a major overhaul from the previous version of Reactive Resume. The app has been completely redesigned and rebuilt from scratch, to be more intuitive and user-friendly.
|
||||
|
||||
**Here are some of the key changes from the previous version:**
|
||||
- 2 new templates: _Ditgar_ and _Lapras_
|
||||
- Authentication via Passkeys
|
||||
- New user interface and refreshed design
|
||||
- API Access and Reference Documentation
|
||||
- AI Integration with OpenAI, Google, Anthropic, and Ollama
|
||||
- A better font selector, with real-time preview of the font
|
||||
- More comprehensive documentation, with guides for specific features
|
||||
- An improved templates gallery, with clear overview of each template
|
||||
- Ability to import resumes from PDF or DOCX (requires AI Integration)
|
||||
- Reliable server infrastructure, with improved performance and scalability
|
||||
- Ability to move items between sections and pages, for better organization
|
||||
- A more powerful CSS Editor, with better autocomplete and syntax highlighting
|
||||
- Extended Custom Sections to include a type, for better customization and organization
|
||||
- Ability to choose the kind of icons to display for level indicators (stars, circles, custom icons, etc.)
|
||||
- Ability to resize how wide/narrow the sidebar should be on the resume, or convert a page to be full width
|
||||
|
||||
There's still a lot more that I'm forgetting, but I'm sure you'll find out soon enough as you explore the new version. I hope you enjoy building your resume now, better than ever. If you have any feedback, please feel free to [contact me](https://amruthpillai.com/#contact) or [open an issue](https://github.com/amruthpillai/reactive-resume/issues) on GitHub.
|
||||
</Update>
|
||||
@@ -104,7 +104,7 @@ Reactive Resume is built with modern web technologies:
|
||||
<Card title="Reddit" icon="reddit" href="https://reddit.com/r/reactiveresume">
|
||||
Join our Reddit community to get help and connect with other users.
|
||||
</Card>
|
||||
<Card title="Discord" icon="discord" href="https://discord.gg/hzwkZbyvUW">
|
||||
<Card title="Discord" icon="discord" href="https://discord.gg/aSyA5ZSxpb">
|
||||
Join our Discord server to get help and connect with other users.
|
||||
</Card>
|
||||
<Card title="Sponsor" icon="heart" href="https://opencollective.com/reactive-resume">
|
||||
|
||||
@@ -193,6 +193,7 @@ Here's a complete list of environment variables you can configure:
|
||||
| `FLAG_DEBUG_PRINTER` | Used for debugging the printer route | `false` |
|
||||
| `FLAG_DISABLE_SIGNUPS` | Disables new user signups | `false` |
|
||||
| `FLAG_DISABLE_EMAIL_AUTH` | Disables email/password login (SSO only) | `false` |
|
||||
| `FLAG_DISABLE_IMAGE_PROCESSING` | Disables image processing | `false` |
|
||||
|
||||
> **Note:** Some variables are only required for using related features (OAuth, SMTP, S3, etc.) and can be left unset if unused.
|
||||
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
---
|
||||
title: "Adding a cover letter"
|
||||
description: "Learn how to create a cover letter as a custom section in Reactive Resume, format it professionally, and place it on a separate page."
|
||||
---
|
||||
|
||||
A cover letter is a personalized document that accompanies your resume when applying for a job. It introduces you to the employer, highlights your relevant qualifications, and explains why you're a good fit for the position. Reactive Resume lets you create cover letters as custom sections, giving you full control over formatting and placement.
|
||||
|
||||
## Why include a cover letter?
|
||||
|
||||
- **Stand out**: A well-written cover letter demonstrates genuine interest in the position
|
||||
- **Personalize your application**: Tailor your message to each employer and role
|
||||
- **Explain gaps or transitions**: Address career changes or gaps in your resume
|
||||
- **Show communication skills**: Demonstrate your ability to write clearly and professionally
|
||||
|
||||
## Creating a cover letter section
|
||||
|
||||
<Steps>
|
||||
<Step title="Open the resume builder">
|
||||
Navigate to your resume and open it in the builder.
|
||||
</Step>
|
||||
|
||||
<Step title="Scroll to Custom Sections">
|
||||
In the left sidebar, scroll down to find the **Custom Sections** area at the bottom.
|
||||
</Step>
|
||||
|
||||
<Step title="Add a new custom section">
|
||||
Click <Badge>Add a new custom section</Badge> to open the section creation dialog.
|
||||
</Step>
|
||||
|
||||
<Step title="Select 'Cover Letter' as the type">
|
||||
In the dialog:
|
||||
- Enter a **Title** for your section (e.g., "Cover Letter" or "Cover Letter - [Company Name]")
|
||||
- Select **Cover Letter** from the **Type** dropdown
|
||||
- Click **Create** to add the section
|
||||
</Step>
|
||||
|
||||
<Step title="Add a cover letter item">
|
||||
In your new cover letter section, click <Badge>Add a new item</Badge> to create your cover letter.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Formatting your cover letter
|
||||
|
||||
A cover letter has two main fields:
|
||||
|
||||
### Recipient field
|
||||
|
||||
The **Recipient** field is where you enter the recipient's information. This typically includes:
|
||||
- Date (optional)
|
||||
- Hiring manager's name
|
||||
- Their job title
|
||||
- Company name
|
||||
- Company address
|
||||
|
||||
<Tip>
|
||||
Use line breaks to format the address block. For example:
|
||||
|
||||
```
|
||||
January 31, 2026
|
||||
|
||||
Jane Smith
|
||||
Senior Hiring Manager
|
||||
Acme Corporation
|
||||
123 Main Street, Suite 400
|
||||
New York, NY 10001
|
||||
```
|
||||
</Tip>
|
||||
|
||||
### Content field
|
||||
|
||||
The **Content** field is where you write the body of your cover letter. Include:
|
||||
- **Salutation**: "Dear Ms. Smith," or "Dear Hiring Team,"
|
||||
- **Opening paragraph**: State the position you're applying for and how you learned about it
|
||||
- **Body paragraphs**: Highlight relevant experience, skills, and achievements
|
||||
- **Closing paragraph**: Express enthusiasm and include a call to action
|
||||
- **Sign-off**: "Sincerely," followed by your name
|
||||
|
||||
<Info>
|
||||
The recipient and content are both rendered directly on your resume without a section header, giving your cover letter a clean, professional appearance.
|
||||
</Info>
|
||||
|
||||
## Using fullscreen mode for writing
|
||||
|
||||
For a distraction-free writing experience, use the fullscreen mode in the rich text editor:
|
||||
|
||||
<Steps>
|
||||
<Step title="Open the cover letter item">
|
||||
Click on your cover letter item in the sidebar to open the edit dialog.
|
||||
</Step>
|
||||
|
||||
<Step title="Expand the editor">
|
||||
In either the Recipient or Content field, click the **expand icon** (arrows pointing outward) in the bottom-right corner of the editor.
|
||||
</Step>
|
||||
|
||||
<Step title="Write in fullscreen">
|
||||
The editor will expand to nearly fill your screen, giving you a focused writing environment with all formatting tools available.
|
||||
</Step>
|
||||
|
||||
<Step title="Exit fullscreen">
|
||||
Click the **collapse icon** or press **Escape** to return to the normal view.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Formatting options
|
||||
|
||||
The rich text editor supports various formatting options for your cover letter:
|
||||
|
||||
- **Text styling**: Bold, italic, underline, strikethrough, highlight
|
||||
- **Headings**: H1 through H6 (though typically not needed in a cover letter)
|
||||
- **Alignment**: Left, center, right, or justified text
|
||||
- **Lists**: Bullet points and numbered lists
|
||||
- **Links**: Add hyperlinks to your portfolio or LinkedIn
|
||||
|
||||
## Moving your cover letter to a separate page
|
||||
|
||||
Cover letters are typically on their own page, separate from the resume content. To achieve this:
|
||||
|
||||
<Steps>
|
||||
<Step title="Create a second page">
|
||||
In the right sidebar, go to the **Layout** section and add a new page to your resume.
|
||||
</Step>
|
||||
|
||||
<Step title="Move the cover letter section">
|
||||
Use the **Move to** feature to relocate your cover letter section to the second page.
|
||||
|
||||
<Tip>
|
||||
See [Moving items between sections](/guides/moving-items-between-sections) for detailed instructions on how to move sections between pages.
|
||||
</Tip>
|
||||
</Step>
|
||||
|
||||
<Step title="Verify the layout">
|
||||
Check the preview to ensure your cover letter appears on its own page with proper formatting.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Warning>
|
||||
When exporting as PDF, pages are rendered in order. If you want the cover letter first, place it on page 1 and move your resume content to subsequent pages.
|
||||
</Warning>
|
||||
|
||||
## Tips for effective cover letters
|
||||
|
||||
<Tip>
|
||||
**Keep it concise**: Aim for 250-400 words. Recruiters spend about one minute reading cover letters.
|
||||
</Tip>
|
||||
|
||||
<Tip>
|
||||
**Tailor each letter**: Customize your cover letter for each application. Reference specific job requirements and company values.
|
||||
</Tip>
|
||||
|
||||
<Tip>
|
||||
**Use the same styling**: Your cover letter will inherit the fonts and colors from your resume template, ensuring a cohesive look.
|
||||
</Tip>
|
||||
|
||||
<Tip>
|
||||
**Proofread carefully**: Spelling and grammar errors can disqualify your application. Review your letter before exporting.
|
||||
</Tip>
|
||||
|
||||
## Managing multiple cover letters
|
||||
|
||||
Since cover letters are custom sections, you can:
|
||||
|
||||
- Create multiple cover letter sections for different job applications
|
||||
- Name each section descriptively (e.g., "Cover Letter - Google", "Cover Letter - Meta")
|
||||
- Show or hide sections as needed when exporting
|
||||
- Duplicate sections to use as templates for new applications
|
||||
@@ -0,0 +1,94 @@
|
||||
---
|
||||
title: "Checking Service Status"
|
||||
description: "Learn how to check the status of Reactive Resume's servers and what to do if the service is experiencing issues"
|
||||
---
|
||||
|
||||
## Status Page
|
||||
|
||||
You can monitor the health and availability of Reactive Resume's servers at any time by visiting our status page:
|
||||
|
||||
<Card title="Status Page" icon="signal" href="https://status.rxresu.me">
|
||||
View real-time server metrics including uptime, CPU usage, memory usage, and more.
|
||||
</Card>
|
||||
|
||||
The status page provides information such as:
|
||||
|
||||
- **Uptime**: How long the servers have been running without interruption
|
||||
- **CPU Usage**: Current processor utilization
|
||||
- **Memory Usage**: RAM consumption across services
|
||||
- **Response Times**: How quickly the servers are responding to requests
|
||||
|
||||
---
|
||||
|
||||
## What to Do If Servers Are Down
|
||||
|
||||
If you notice the servers are experiencing high load or downtime, here are some recommendations:
|
||||
|
||||
<Steps>
|
||||
<Step title="Check the status page">
|
||||
Visit [status.rxresu.me](https://status.rxresu.me) to confirm if there's an ongoing issue. The page will show you the current state of all services.
|
||||
</Step>
|
||||
|
||||
<Step title="Wait and try again later">
|
||||
If the servers are under heavy load, the best course of action is to wait a bit and try again later. Peak usage times can cause temporary slowdowns.
|
||||
|
||||
<Info>
|
||||
Reactive Resume is a **free, open-source service** used by thousands of people worldwide. During peak times, the servers may experience higher than usual load.
|
||||
</Info>
|
||||
</Step>
|
||||
|
||||
<Step title="Check for announcements">
|
||||
For major outages or planned maintenance, announcements may be posted on our [GitHub repository](https://github.com/amruthpillai/reactive-resume).
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
---
|
||||
|
||||
## A Note on Server Capacity
|
||||
|
||||
<Warning>
|
||||
Reactive Resume is a **free service** that runs on limited server resources. As an open-source project maintained by a single developer, it's not feasible to invest in powerful dedicated servers without community support.
|
||||
</Warning>
|
||||
|
||||
The reality is:
|
||||
|
||||
- **High demand**: Thousands of users rely on this service daily
|
||||
- **Limited resources**: As a free service, server capacity is constrained
|
||||
- **No corporate backing**: This isn't a venture-funded startup with unlimited cloud budgets
|
||||
|
||||
If you find Reactive Resume valuable and want to help keep the servers running smoothly (and maybe even help scale them up), please consider supporting the project.
|
||||
|
||||
---
|
||||
|
||||
## Support the Project
|
||||
|
||||
Your donations directly help cover server costs, improve infrastructure, and keep Reactive Resume free for everyone.
|
||||
|
||||
<Card title="Donate on Open Collective" icon="heart" href="https://opencollective.com/reactive-resume">
|
||||
Support Reactive Resume's development and server costs through Open Collective. Every contribution helps keep the service running.
|
||||
</Card>
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="One-time Donation" icon="gift">
|
||||
Make a single contribution of any amount to help with immediate server costs.
|
||||
</Card>
|
||||
<Card title="Recurring Support" icon="repeat">
|
||||
Become a backer with a monthly contribution to provide sustainable support.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
---
|
||||
|
||||
## Self-Hosting as an Alternative
|
||||
|
||||
If you need guaranteed uptime or want to avoid shared server limitations, you can always self-host Reactive Resume on your own infrastructure.
|
||||
|
||||
<Card title="Self-Hosting Guide" icon="server" href="/self-hosting/docker">
|
||||
Learn how to deploy Reactive Resume on your own servers using Docker.
|
||||
</Card>
|
||||
|
||||
Self-hosting gives you:
|
||||
|
||||
- **Full control** over your data and infrastructure
|
||||
- **Guaranteed availability** based on your own server capacity
|
||||
- **No shared resources** with other users
|
||||
@@ -0,0 +1,155 @@
|
||||
---
|
||||
title: "Fitting content on a page"
|
||||
description: "Learn how to reorganize and format your resume content to fit within a single page, whether you're using A4 or Letter format."
|
||||
---
|
||||
|
||||
When your resume content overflows the page, Reactive Resume displays a warning just below the page. This guide explains how to reorganize and adjust your content so everything fits within your chosen page format.
|
||||
|
||||
<Frame caption="Screenshot of the overflow warning message in the resume builder">
|
||||
<img src="/images/guides/fitting-content-on-a-page/screenshot-1.webp" alt="Screenshot of the overflow warning message in the resume builder" />
|
||||
</Frame>
|
||||
|
||||
<Warning>
|
||||
While Reactive Resume supports multi-page resumes, each page has a fixed height based on your chosen format (A4 or Letter). If a single page's content exceeds this height, parts of your resume may be rendered improperly when printed or exported.
|
||||
</Warning>
|
||||
|
||||
## Quick fixes
|
||||
|
||||
Here are several ways to fit your content within a page, ordered from simplest to most involved.
|
||||
|
||||
### 1. Switch to Free-Form format
|
||||
|
||||
If you don't plan on printing your resume, the simplest solution is to switch to **Free-Form** format. Free-Form creates a single continuous page with no height limit, eliminating overflow concerns entirely.
|
||||
|
||||
With Free-Form:
|
||||
|
||||
- Your entire resume renders as one seamless document
|
||||
- No awkward page breaks to manage
|
||||
- ATS parsers and AI scanners can still read your content perfectly
|
||||
- You focus on content, not arbitrary page constraints
|
||||
|
||||
To switch formats, go to the **Page** section in the right sidebar and change the **Format** to Free-Form. For more details, see [Selecting the right page format](/guides/selecting-page-format).
|
||||
|
||||
<Tip>
|
||||
Since most resumes are viewed digitally today, Free-Form is often the best choice unless you specifically need to print physical copies.
|
||||
</Tip>
|
||||
|
||||
### 2. Shorten text blocks
|
||||
|
||||
Long paragraphs take up space without adding proportional value. Review each section and cut ruthlessly:
|
||||
|
||||
- **Use bullet points** instead of paragraphs. Bullets are easier to scan and take less vertical space.
|
||||
- **Remove filler words.** "Was responsible for managing" becomes "Managed."
|
||||
- **Focus on impact.** Keep measurable achievements; cut generic descriptions.
|
||||
- **Limit bullets per entry.** Three to five bullets per job is usually enough.
|
||||
|
||||
<Tip>
|
||||
Read each bullet point and ask: "Does this help me get an interview?" If not, cut it.
|
||||
</Tip>
|
||||
|
||||
### 3. Use multi-column layouts
|
||||
|
||||
Some sections work better in multiple columns, especially lists of short items.
|
||||
|
||||
In the left sidebar, find the section you want to adjust, click on the section heading (not an item), and change the **Columns** setting.
|
||||
|
||||
<Frame caption="Screenshot of the columns setting for a section">
|
||||
<img src="/images/guides/fitting-content-on-a-page/screenshot-2.webp" alt="Screenshot of the columns setting for a section" />
|
||||
</Frame>
|
||||
|
||||
Good candidates for multi-column layouts:
|
||||
|
||||
| Section | Recommended Columns |
|
||||
|---------|---------------------|
|
||||
| Skills | > 2 columns |
|
||||
| Languages | > 3 columns |
|
||||
| Interests | > 2 columns |
|
||||
| Profiles | > 3 columns |
|
||||
| Certifications (if brief) | > 2 columns |
|
||||
|
||||
<Info>
|
||||
Multi-column layouts work best for sections with short, uniform items. Sections with long descriptions (like Experience or Projects) usually work better in a single column.
|
||||
</Info>
|
||||
|
||||
### 4. Move items to another page
|
||||
|
||||
If you have more content than fits on one page, move less important items to page two. This keeps your first page focused on your most relevant experience.
|
||||
|
||||
Use the **Move to** feature to relocate items:
|
||||
|
||||
1. Open the item's dropdown menu (three-dot icon)
|
||||
2. Hover over **Move to**
|
||||
3. Select the destination page
|
||||
|
||||
For detailed instructions, see [Moving items between sections](/guides/moving-items-between-sections).
|
||||
|
||||
<Tip>
|
||||
Keep your most recent and relevant experience on page one. Move older positions or less critical sections (like older projects or volunteer work) to subsequent pages.
|
||||
</Tip>
|
||||
|
||||
### 5. Adjust layout and design settings
|
||||
|
||||
The right sidebar contains settings that control how much space your content uses. Small adjustments here can make a big difference.
|
||||
|
||||
Open the right sidebar and explore these options:
|
||||
|
||||
| Setting | Where to find it | Effect |
|
||||
|---------|------------------|--------|
|
||||
| **Font size** | Typography | Smaller fonts fit more text per line and per page |
|
||||
| **Line height** | Typography | Tighter line spacing reduces vertical space |
|
||||
| **Margins** | Page | Smaller margins give you more usable area |
|
||||
| **Section gaps** | Page | Reducing gaps between sections saves space |
|
||||
| **Sidebar width** | Layout | Adjusting the sidebar ratio can balance content better |
|
||||
| **Picture size** | Picture (left sidebar) | A smaller photo leaves more room for text |
|
||||
|
||||
<Info>
|
||||
**Reducing font size is the best option** when you need to fit more content while keeping A4 or Letter format. Reducing body font from 11pt to 10.5pt (or even 10pt) can free up significant space while remaining readable. The editor supports 0.1pt increments, so you can fine-tune precisely.
|
||||
</Info>
|
||||
|
||||
### 6. Hide less important sections
|
||||
|
||||
If you're still short on space, consider hiding sections that aren't essential for your target role:
|
||||
|
||||
- **Interests** — Nice to have, but rarely a deciding factor
|
||||
- **References** — "Available upon request" is assumed; you don't need to list them
|
||||
- **Older certifications** — Keep only those relevant to the job
|
||||
- **Volunteer work** — Include only if it strengthens your application
|
||||
|
||||
To hide a section, click on the section heading in the left sidebar and toggle the **Hidden** switch.
|
||||
|
||||
## Finding the right balance
|
||||
|
||||
If you need to stick with A4 or Letter format, start with content changes (steps 2-4) before adjusting design settings (steps 5-6). The best resumes fit their content naturally rather than forcing everything into a cramped layout.
|
||||
|
||||
Try this order:
|
||||
|
||||
1. Consider switching to Free-Form if printing isn't required
|
||||
2. Cut unnecessary text first
|
||||
3. Reorganize with columns where appropriate
|
||||
4. Move secondary content to page two if needed
|
||||
5. Fine-tune font size and spacing last
|
||||
|
||||
<Tip>
|
||||
Use the live preview to see changes as you make them. Small adjustments add up—reducing font size by 0.5pt combined with slightly smaller margins can recover enough space for several lines of content.
|
||||
</Tip>
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Content still overflows after trying everything
|
||||
|
||||
If you've tried all the above and content still overflows:
|
||||
|
||||
- **Re-evaluate what's essential.** Every item should earn its place. Cut aggressively.
|
||||
- **Try a different template.** Some templates are more space-efficient than others.
|
||||
|
||||
### The preview looks different from the PDF
|
||||
|
||||
The PDF export matches the preview exactly. If they appear different, try:
|
||||
|
||||
- Refreshing the page
|
||||
- Checking that all fonts have loaded
|
||||
- Ensuring your browser zoom is at 100%
|
||||
|
||||
### I made the font too small and now it's hard to read
|
||||
|
||||
Resume fonts should stay between 9pt and 12pt for body text. If you've gone below 9pt to fit content, you're trying to include too much. Go back to step 1 and cut more content instead.
|
||||
@@ -0,0 +1,143 @@
|
||||
---
|
||||
title: "Selecting the right page format"
|
||||
description: "Learn about the three page format options in Reactive Resume—A4, Letter, and Free-Form—and choose the best one for your needs."
|
||||
---
|
||||
|
||||
Reactive Resume offers three page format options: **A4**, **Letter**, and **Free-Form**. Each format affects how your resume is rendered and exported as a PDF. This guide explains the differences and helps you choose the right format for your situation.
|
||||
|
||||
## Available formats
|
||||
|
||||
### A4
|
||||
|
||||
A4 is the international standard paper size used in most countries outside North America. When you select A4, your resume pages conform to these dimensions:
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Width | 210mm (794px) |
|
||||
| Height | 297mm (1123px) |
|
||||
|
||||
Choose A4 if you're applying to jobs internationally or in regions that use the metric system.
|
||||
|
||||
### Letter
|
||||
|
||||
Letter is the standard paper size in the United States and Canada. When you select Letter, your resume pages conform to these dimensions:
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Width | 216mm (816px) |
|
||||
| Height | 279mm (1056px) |
|
||||
|
||||
Choose Letter if you're applying to jobs in North America.
|
||||
|
||||
### Free-Form
|
||||
|
||||
Free-Form is a modern format designed for digital-first resumes. Instead of conforming to a physical page size, Free-Form produces a **single continuous page** with no height limit. The width matches A4 (210mm), but the height extends as needed to fit all your content.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Width | 210mm (794px) |
|
||||
| Height | Unlimited |
|
||||
|
||||
<Info>
|
||||
With Free-Form, there are no page breaks. Your entire resume renders as one seamless document, regardless of how much content you have.
|
||||
</Info>
|
||||
|
||||
## Why Free-Form exists
|
||||
|
||||
The reality of modern job applications is that **nobody prints resumes anymore**. Your resume is almost always viewed digitally—on a screen, in an applicant tracking system (ATS), or parsed by AI-powered screening tools.
|
||||
|
||||
When your resume is processed digitally:
|
||||
|
||||
- **ATS parsers** extract text content regardless of page dimensions
|
||||
- **AI scanners** analyze the full document as a single unit
|
||||
- **Recruiters** scroll through PDFs on their screens rather than printing them
|
||||
- **PDF parsing tools** read the entire content regardless of page height
|
||||
|
||||
Since physical page constraints no longer matter for most use cases, Free-Form lets you focus on content rather than worrying about fitting everything into arbitrary page limits.
|
||||
|
||||
<Tip>
|
||||
If you don't plan on printing your resume, Free-Form is often the simplest choice. You never have to worry about content overflowing or awkward page breaks.
|
||||
</Tip>
|
||||
|
||||
## How to change your page format
|
||||
|
||||
<Steps>
|
||||
<Step title="Open your resume in the builder">
|
||||
Navigate to your Dashboard and click on the resume you want to edit.
|
||||
</Step>
|
||||
|
||||
<Step title="Open the right sidebar">
|
||||
In the resume builder, look for the right sidebar on the right side of the screen.
|
||||
</Step>
|
||||
|
||||
<Step title="Navigate to the Page section">
|
||||
In the right sidebar, find and click on the **Page** section to expand it.
|
||||
</Step>
|
||||
|
||||
<Step title="Select your format">
|
||||
Find the **Format** dropdown and select your preferred option: A4, Letter, or Free-Form.
|
||||
|
||||
<Frame caption="Screenshot of the Format dropdown in the Page section">
|
||||
<img src="/images/guides/selecting-page-format/screenshot-1.webp" alt="Screenshot of the Format dropdown in the Page section" />
|
||||
</Frame>
|
||||
</Step>
|
||||
|
||||
<Step title="Review the preview">
|
||||
Your resume preview updates immediately to reflect the new format. Check that your content displays correctly.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Choosing the right format
|
||||
|
||||
Use this quick reference to decide which format fits your needs:
|
||||
|
||||
| Situation | Recommended Format |
|
||||
|-----------|-------------------|
|
||||
| Applying to jobs in North America | Letter |
|
||||
| Applying to jobs internationally | A4 |
|
||||
| Digital-only applications (no printing) | Free-Form |
|
||||
| Uploading to ATS or job portals | Free-Form |
|
||||
| Need to print physical copies | A4 or Letter |
|
||||
| Long resume with lots of content | Free-Form |
|
||||
| Traditional industries (law, finance) | A4 or Letter |
|
||||
|
||||
<Warning>
|
||||
If you switch from Free-Form to A4 or Letter, your content will be split across multiple pages. Review the result carefully to ensure page breaks don't occur in awkward places.
|
||||
</Warning>
|
||||
|
||||
## Example PDFs
|
||||
|
||||
Download these sample resumes to see how different formats affect the final PDF output:
|
||||
|
||||
<CardGroup cols={1}>
|
||||
<Card arrow horizontal title="A4 Format Example" icon="file-pdf" href="https://github.com/user-attachments/files/24849548/a4.pdf">
|
||||
A sample resume in A4 format showing traditional page breaks and constraints.
|
||||
</Card>
|
||||
<Card arrow horizontal title="Free-Form Example" icon="file-lines" href="https://github.com/user-attachments/files/24849552/free-form.pdf">
|
||||
A sample resume in Free-Form format showing a continuous single-page layout.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Frequently asked questions
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Does Free-Form work with ATS systems?">
|
||||
Yes. ATS systems parse the text content of your PDF, not the page dimensions. Free-Form resumes are fully compatible with applicant tracking systems.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I switch formats after creating my resume?">
|
||||
Absolutely. You can change the format at any time from the Page section in the right sidebar. Your content is preserved—only the layout changes.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="What if a job posting asks for a 'one-page resume'?">
|
||||
If the employer specifically requests a one-page resume and expects a traditional format, use A4 or Letter and ensure your content fits within a single page. See [Fitting content on a page](/guides/fitting-content-on-a-page) for tips.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Does Free-Form affect the file size?">
|
||||
Slightly. A longer single page may produce a marginally larger PDF than a paginated version of the same content, but the difference is negligible for typical resume lengths.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Which format should I use for LinkedIn?">
|
||||
LinkedIn doesn't display uploaded resumes in their original format—it extracts the content. Free-Form works perfectly for LinkedIn uploads.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
@@ -3,6 +3,14 @@ title: "Setting up passkeys"
|
||||
description: "Learn how to register passkeys (WebAuthn) to sign in securely using biometrics or your device PIN"
|
||||
---
|
||||
|
||||
<Warning>
|
||||
<strong>Passkeys Temporarily Disabled:</strong> Passkey registration and sign-in are currently unavailable in Reactive Resume due to an upstream issue with our authentication provider. We are closely monitoring the situation and will re-enable passkeys once it is resolved.
|
||||
|
||||
For more details and technical updates, see the <a href="https://github.com/better-auth/better-auth/issues/7463" target="_blank" rel="noopener">GitHub issue #7463</a>.
|
||||
</Warning>
|
||||
|
||||
---
|
||||
|
||||
<Steps>
|
||||
<Step title="Sign in to the dashboard">
|
||||
Head over to [https://rxresu.me](https://rxresu.me) and sign in with your account credentials.
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
---
|
||||
title: "Using the MCP Server"
|
||||
description: "Connect Reactive Resume to AI tools like Claude Desktop, Cursor, and Codex using the Model Context Protocol"
|
||||
---
|
||||
|
||||
The Reactive Resume MCP server lets you manage and modify your resumes through any MCP-compatible AI tool — Claude Desktop, Cursor, Codex, and more. It connects to the Reactive Resume API and exposes tools for listing, reading, and patching resumes using natural language.
|
||||
|
||||
## What is MCP?
|
||||
|
||||
The [Model Context Protocol (MCP)](https://modelcontextprotocol.io) is a standard that lets LLM-powered tools connect to external services. Instead of being limited to the built-in chat UI, you can use any MCP client to interact with your resumes.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
<Steps>
|
||||
<Step title="Create an API key">
|
||||
Head over to [https://rxresu.me](https://rxresu.me) (or your self-hosted instance), sign in, and navigate to **Settings → API Keys**. Click **Create a new API key**, give it a name, and copy the secret — it's only shown once.
|
||||
|
||||
For the full walkthrough, see [Using the API](/guides/using-the-api).
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Configuration
|
||||
|
||||
There are two ways to connect, depending on whether your MCP client supports the Streamable HTTP transport natively.
|
||||
|
||||
### Method 1: Streamable HTTP (recommended)
|
||||
|
||||
If your client supports the `url` field (e.g. **Cursor**), use this — no extra dependencies required:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"reactive-resume": {
|
||||
"url": "https://rxresu.me/mcp",
|
||||
"headers": {
|
||||
"x-api-key": "your-api-key"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Method 2: mcp-remote
|
||||
|
||||
If your client only supports `command` / `args` (e.g. **Claude Desktop**), use [`mcp-remote`](https://www.npmjs.com/package/mcp-remote) as a bridge. This requires [Node.js](https://nodejs.org) **20 or later**.
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"reactive-resume": {
|
||||
"command": "npx",
|
||||
"args": [
|
||||
"mcp-remote",
|
||||
"https://rxresu.me/mcp",
|
||||
"--header",
|
||||
"x-api-key:your-api-key"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<Info>
|
||||
Replace `your-api-key` with the API key you created in the prerequisites step.
|
||||
</Info>
|
||||
|
||||
### Where to put the config
|
||||
|
||||
| Client | Config file |
|
||||
| --- | --- |
|
||||
| Cursor | `.cursor/mcp.json` in your project or home directory |
|
||||
| Claude Desktop | `claude_desktop_config.json` ([docs](https://modelcontextprotocol.io/docs/tools/claude-desktop)) |
|
||||
| Other MCP clients | Refer to the client's documentation |
|
||||
|
||||
## Self-Hosting
|
||||
|
||||
If you're running a self-hosted Reactive Resume instance, replace `https://rxresu.me/mcp` with your instance URL:
|
||||
|
||||
```json
|
||||
{
|
||||
"url": "https://resume.example.com/mcp",
|
||||
"headers": {
|
||||
"x-api-key": "your-api-key"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Available Tools
|
||||
|
||||
The MCP server exposes the following tools:
|
||||
|
||||
| Tool | Description |
|
||||
| --- | --- |
|
||||
| `list_resumes` | List all resumes with IDs, names, tags, and status. Supports filtering by tags and sorting by last updated, creation date, or name |
|
||||
| `get_resume` | Get the full data of a specific resume by ID |
|
||||
| `create_resume` | Create a new, empty resume with a name and slug. Optionally pre-fill with sample data |
|
||||
| `duplicate_resume` | Create a copy of an existing resume with a new name and slug |
|
||||
| `patch_resume` | Apply JSON Patch (RFC 6902) operations to modify a resume's data |
|
||||
| `delete_resume` | Permanently delete a resume and all associated files. **Irreversible** |
|
||||
| `lock_resume` | Lock a resume to prevent edits, patches, and deletion |
|
||||
| `unlock_resume` | Unlock a previously locked resume to re-enable editing |
|
||||
| `export_resume_pdf` | Generate a PDF from the resume and return a download URL |
|
||||
| `get_resume_screenshot` | Get a visual preview of the resume's first page as a WebP image URL |
|
||||
| `get_resume_statistics` | Get view and download statistics for a resume |
|
||||
|
||||
## Available Resources
|
||||
|
||||
| Resource | Description |
|
||||
| --- | --- |
|
||||
| `resume://{id}` | The full resume data as a readable JSON resource. Lists all resumes and supports reading individual ones by ID |
|
||||
| `resume://schema` | The ResumeData JSON Schema — reference this to understand valid paths and value types for JSON Patch operations |
|
||||
|
||||
## Available Prompts
|
||||
|
||||
Prompts are pre-built workflows that provide the AI with structured instructions and context. Each prompt embeds the resume data and schema automatically.
|
||||
|
||||
| Prompt | Description |
|
||||
| --- | --- |
|
||||
| `build_resume` | Guide you step-by-step through building a resume from scratch — basics, summary, experience, education, skills, and design |
|
||||
| `improve_resume` | Review your resume and suggest concrete improvements to wording, impact, metrics, and structure |
|
||||
| `tailor_resume` | Adapt your resume to match a specific job description with keyword optimization and ATS targeting. Requires the job description as input |
|
||||
| `review_resume` | Get a structured, professional critique with a scorecard (1–10 across seven dimensions) and prioritized recommendations. **Read-only** — no changes are made |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
Once your MCP client is connected, you can use natural language to interact with your resumes:
|
||||
|
||||
### Browsing
|
||||
|
||||
- "List my resumes"
|
||||
- "Show me my resume named 'Software Engineer'"
|
||||
- "What skills are listed on my resume?"
|
||||
- "Show me the stats for my resume"
|
||||
|
||||
### Creating & Managing
|
||||
|
||||
- "Create a new resume called 'Frontend Engineer 2026'"
|
||||
- "Duplicate my 'Software Engineer' resume for a product manager role"
|
||||
- "Lock my finalized resume so it can't be accidentally edited"
|
||||
- "Delete my old draft resume"
|
||||
|
||||
### Editing
|
||||
|
||||
- "Update my name to Jane Doe"
|
||||
- "Change my headline to Senior Software Engineer"
|
||||
- "Add TypeScript to my skills with an Advanced proficiency level"
|
||||
- "Add a new experience entry for my role as Staff Engineer at Acme Corp from Jan 2024 to Present"
|
||||
- "Remove the third item from my skills section"
|
||||
|
||||
### Styling
|
||||
|
||||
- "Change the template to bronzor"
|
||||
- "Set the primary color to blue"
|
||||
- "Hide the interests section"
|
||||
|
||||
### Exporting
|
||||
|
||||
- "Export my resume as a PDF"
|
||||
- "Show me a screenshot of my resume"
|
||||
|
||||
### Using Prompts
|
||||
|
||||
- "Help me build my resume from scratch" (uses `build_resume`)
|
||||
- "Review my resume and give me a score" (uses `review_resume`)
|
||||
- "Improve the wording on my resume" (uses `improve_resume`)
|
||||
- "Tailor my resume for this job description: ..." (uses `tailor_resume`)
|
||||
|
||||
<Tip>
|
||||
The AI will use `get_resume` to inspect your current resume before making changes with `patch_resume`. This ensures the correct JSON paths are used.
|
||||
</Tip>
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Issue | Solution |
|
||||
| --- | --- |
|
||||
| "API error (401)" | Your API key is invalid or expired. Create a new one in **Settings → API Keys** |
|
||||
| "API error (404)" | The resume ID doesn't exist. Use `list_resumes` to find valid IDs |
|
||||
| "API error (403)" | The resume is locked. Unlock it in the Reactive Resume dashboard |
|
||||
| Connection refused | Check that the URL is correct and the instance is running |
|
||||
| "ReferenceError: File is not defined" when using `mcp-remote` | You're running Node.js 18. `mcp-remote` requires **Node.js 20 or later** — upgrade with `nvm use 20` or `nvm alias default 20` |
|
||||
@@ -0,0 +1,191 @@
|
||||
---
|
||||
title: "Using the Patch API"
|
||||
description: "Learn how to partially update your resume using JSON Patch (RFC 6902) operations"
|
||||
---
|
||||
|
||||
The Patch API lets you make small, targeted changes to your resume without sending the entire data object. Instead of replacing the whole resume with a `PUT`, you send a list of **JSON Patch** operations that describe exactly what to change.
|
||||
|
||||
This is based on the [JSON Patch (RFC 6902)](https://datatracker.ietf.org/doc/html/rfc6902) standard.
|
||||
|
||||
## When to Use PATCH vs PUT
|
||||
|
||||
| Use case | Method |
|
||||
| --- | --- |
|
||||
| Update a single field (e.g., name, headline) | **PATCH** |
|
||||
| Add or remove an item in a section | **PATCH** |
|
||||
| Change template, colors, or fonts | **PATCH** |
|
||||
| Replace the entire resume data at once | **PUT** |
|
||||
|
||||
<Info>
|
||||
The PATCH endpoint only modifies the resume `data` (the JSONB column). To update top-level resume properties like `name`, `slug`, `tags`, or `isPublic`, use the existing `PUT /resume/{id}` endpoint.
|
||||
</Info>
|
||||
|
||||
## Authentication
|
||||
|
||||
All requests require your API key in the `x-api-key` header. See [Using the API](/guides/using-the-api) for how to create one.
|
||||
|
||||
<Info>
|
||||
If you're self-hosting, replace `https://rxresu.me` with your instance URL. The API is served under `/api/openapi`.
|
||||
</Info>
|
||||
|
||||
## Endpoint
|
||||
|
||||
```
|
||||
PATCH /api/openapi/resume/{id}
|
||||
```
|
||||
|
||||
### Request Body
|
||||
|
||||
The resume ID is taken from the URL path, so the request body only requires the `operations` array:
|
||||
|
||||
```json
|
||||
{
|
||||
"operations": [
|
||||
{ "op": "replace", "path": "/basics/name", "value": "Jane Doe" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Each operation is an object with the following properties:
|
||||
|
||||
| Property | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `op` | Yes | The operation to perform: `add`, `remove`, `replace`, `move`, `copy`, or `test` |
|
||||
| `path` | Yes | A JSON Pointer (RFC 6901) to the target location in the resume data |
|
||||
| `value` | For `add`, `replace`, `test` | The value to use for the operation |
|
||||
| `from` | For `move`, `copy` | A JSON Pointer to the source location |
|
||||
|
||||
## Examples
|
||||
|
||||
### Replace a Basic Field
|
||||
|
||||
Update the resume holder's name and headline:
|
||||
|
||||
```bash
|
||||
curl -X PATCH "https://rxresu.me/api/openapi/resume/YOUR_RESUME_ID" \
|
||||
-H "x-api-key: YOUR_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"operations": [
|
||||
{ "op": "replace", "path": "/basics/name", "value": "Jane Doe" },
|
||||
{ "op": "replace", "path": "/basics/headline", "value": "Senior Software Engineer" }
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
### Add an Experience Entry
|
||||
|
||||
Append a new item to the experience section:
|
||||
|
||||
```bash
|
||||
curl -X PATCH "https://rxresu.me/api/openapi/resume/YOUR_RESUME_ID" \
|
||||
-H "x-api-key: YOUR_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"operations": [
|
||||
{
|
||||
"op": "add",
|
||||
"path": "/sections/experience/items/-",
|
||||
"value": {
|
||||
"id": "a1b2c3d4-0000-0000-0000-000000000000",
|
||||
"hidden": false,
|
||||
"company": "Acme Corp",
|
||||
"position": "Staff Engineer",
|
||||
"location": "San Francisco, CA",
|
||||
"period": "Jan 2024 - Present",
|
||||
"website": { "url": "https://acme.example.com", "label": "Acme Corp" },
|
||||
"description": "<p>Leading the platform team.</p>"
|
||||
}
|
||||
}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
<Tip>
|
||||
The path `/sections/experience/items/-` uses the special `-` index, which means "append to the end of the array".
|
||||
To insert at a specific position, use a numeric index like `/sections/experience/items/0` for the beginning.
|
||||
</Tip>
|
||||
|
||||
### Remove an Item from a Section
|
||||
|
||||
Remove the second skill (index `1`) from the skills section:
|
||||
|
||||
```bash
|
||||
curl -X PATCH "https://rxresu.me/api/openapi/resume/YOUR_RESUME_ID" \
|
||||
-H "x-api-key: YOUR_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"operations": [
|
||||
{ "op": "remove", "path": "/sections/skills/items/1" }
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
### Update Metadata (Template, Colors, Fonts)
|
||||
|
||||
Switch the template and update the primary color:
|
||||
|
||||
```bash
|
||||
curl -X PATCH "https://rxresu.me/api/openapi/resume/YOUR_RESUME_ID" \
|
||||
-H "x-api-key: YOUR_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"operations": [
|
||||
{ "op": "replace", "path": "/metadata/template", "value": "bronzor" },
|
||||
{ "op": "replace", "path": "/metadata/design/colors/primary", "value": "rgba(37, 99, 235, 1)" }
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
### Test-Then-Replace (Optimistic Concurrency)
|
||||
|
||||
The `test` operation checks that a value matches before proceeding. If the test fails, the entire patch is rejected. This is useful to avoid overwriting changes made by another client:
|
||||
|
||||
```bash
|
||||
curl -X PATCH "https://rxresu.me/api/openapi/resume/YOUR_RESUME_ID" \
|
||||
-H "x-api-key: YOUR_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"operations": [
|
||||
{ "op": "test", "path": "/basics/name", "value": "Albert Einstein" },
|
||||
{ "op": "replace", "path": "/basics/name", "value": "Jane Doe" }
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
If `/basics/name` is not `"Albert Einstein"` at the time of the request, the entire patch will fail with a `400` error and no changes will be applied.
|
||||
|
||||
### Move an Item Within a Section
|
||||
|
||||
Move the first experience item to the third position:
|
||||
|
||||
```bash
|
||||
curl -X PATCH "https://rxresu.me/api/openapi/resume/YOUR_RESUME_ID" \
|
||||
-H "x-api-key: YOUR_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"operations": [
|
||||
{ "op": "move", "from": "/sections/experience/items/0", "path": "/sections/experience/items/2" }
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Status | Error Code | Description |
|
||||
| --- | --- | --- |
|
||||
| `400` | `INVALID_PATCH_OPERATIONS` | The operations are structurally invalid, target a non-existent path, or produce resume data that fails schema validation. |
|
||||
| `401` | `UNAUTHORIZED` | Missing or invalid API key. |
|
||||
| `404` | `NOT_FOUND` | The resume does not exist or does not belong to the authenticated user. |
|
||||
| `403` | `RESUME_LOCKED` | The resume is locked and cannot be modified. Unlock it first. |
|
||||
|
||||
<Warning>
|
||||
All operations in a single request are applied atomically. If any operation fails (including a `test`), none of the operations are applied.
|
||||
</Warning>
|
||||
|
||||
## Tips
|
||||
|
||||
- **Fetch first, then patch.** Use `GET /resume/{id}` to inspect the current structure before crafting your operations. This helps you target the correct paths and array indices.
|
||||
- **Use `test` for safety.** When updating a value you expect to be a specific value, combine `test` + `replace` to avoid accidentally overwriting concurrent changes.
|
||||
- **Batch related changes.** You can send multiple operations in a single request. They are applied in order, so later operations can depend on earlier ones.
|
||||
- **The `-` index appends.** When adding items to arrays, use `-` as the index (e.g., `/sections/skills/items/-`) to append to the end.
|
||||
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 21 KiB |
@@ -153,14 +153,14 @@ FLAG_DISABLE_EMAIL_AUTH="false"
|
||||
```yaml compose.yml
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16
|
||||
image: postgres:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: postgres
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
- postgres_data:/var/lib/postgresql
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"]
|
||||
interval: 10s
|
||||
@@ -184,7 +184,7 @@ services:
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
app:
|
||||
reactive-resume:
|
||||
image: amruthpillai/reactive-resume:latest
|
||||
# image: ghcr.io/amruthpillai/reactive-resume:latest
|
||||
restart: unless-stopped
|
||||
@@ -363,6 +363,7 @@ openssl rand -hex 32
|
||||
- **`FLAG_DEBUG_PRINTER`**: Bypasses the printer-only access restriction (useful when debugging `/printer/{resumeId}`). Recommended: keep `"false"` in production.
|
||||
- **`FLAG_DISABLE_SIGNUPS`**: Disables new signups (web app and server). Useful for private instances.
|
||||
- **`FLAG_DISABLE_EMAIL_AUTH`**: Disables email/password login entirely. Also disables email verification, forgot password, and reset password flows. Users can still sign up via social auth (Google/GitHub/Custom OAuth), unless FLAG_DISABLE_SIGNUPS is also set to true. Useful when only SSO is required.
|
||||
- **`FLAG_DISABLE_IMAGE_PROCESSING`**: Disables image processing. This is useful if you are using a machine with limited resources, like a Raspberry Pi.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
|
||||
@@ -59,14 +59,14 @@ services:
|
||||
- "traefik.http.middlewares.auth.basicauth.users=${TRAEFIK_DASHBOARD_AUTH}"
|
||||
|
||||
postgres:
|
||||
image: postgres:16
|
||||
image: postgres:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: postgres
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
- POSTGRES_DB=postgres
|
||||
- POSTGRES_USER=postgres
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
- postgres_data:/var/lib/postgresql
|
||||
networks:
|
||||
- reactive_resume_network
|
||||
healthcheck:
|
||||
@@ -79,10 +79,11 @@ services:
|
||||
image: ghcr.io/browserless/chromium:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TIMEOUT=120000
|
||||
- CONCURRENT=10
|
||||
- QUEUED=10
|
||||
- HEALTH=true
|
||||
- TOKEN=your-secret-token
|
||||
- CONCURRENT=5
|
||||
# Optional: Set a token for authentication
|
||||
# - TOKEN=your-secret-token
|
||||
networks:
|
||||
- reactive_resume_network
|
||||
healthcheck:
|
||||
@@ -91,18 +92,18 @@ services:
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
app:
|
||||
reactive_resume:
|
||||
image: amruthpillai/reactive-resume:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- APP_URL=https://resume.${DOMAIN}
|
||||
- PRINTER_APP_URL=http://app:3000
|
||||
- PRINTER_APP_URL=http://reactive_resume:3000
|
||||
- DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/postgres
|
||||
- PRINTER_ENDPOINT=http://printer:3000
|
||||
- AUTH_SECRET=${AUTH_SECRET}
|
||||
# Add other optional env vars as needed (SMTP, S3, OAuth, etc.)
|
||||
volumes:
|
||||
- app_data:/app/data
|
||||
- reactive_resume_data:/app/data
|
||||
networks:
|
||||
- reactive_resume_network
|
||||
depends_on:
|
||||
@@ -129,7 +130,7 @@ networks:
|
||||
volumes:
|
||||
traefik_letsencrypt:
|
||||
postgres_data:
|
||||
app_data:
|
||||
reactive_resume_data:
|
||||
```
|
||||
|
||||
**Environment variables (`.env`):**
|
||||
@@ -162,18 +163,16 @@ services:
|
||||
- ./certs:/etc/nginx/certs:ro
|
||||
networks:
|
||||
- reactive_resume_network
|
||||
depends_on:
|
||||
- app
|
||||
|
||||
postgres:
|
||||
image: postgres:16
|
||||
image: postgres:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: postgres
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
- postgres_data:/var/lib/postgresql
|
||||
networks:
|
||||
- reactive_resume_network
|
||||
healthcheck:
|
||||
@@ -186,30 +185,26 @@ services:
|
||||
image: ghcr.io/browserless/chromium:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TIMEOUT=120000
|
||||
- CONCURRENT=10
|
||||
- QUEUED=10
|
||||
- HEALTH=true
|
||||
- TOKEN=your-secret-token
|
||||
- CONCURRENT=5
|
||||
# Optional: Set a token for authentication
|
||||
# - TOKEN=your-secret-token
|
||||
networks:
|
||||
- reactive_resume_network
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000/pressure?token=your-secret-token"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
app:
|
||||
reactive_resume:
|
||||
image: amruthpillai/reactive-resume:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- APP_URL=https://resume.${DOMAIN}
|
||||
- PRINTER_APP_URL=http://app:3000
|
||||
- PRINTER_APP_URL=http://reactive_resume:3000
|
||||
- DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/postgres
|
||||
- PRINTER_ENDPOINT=http://printer:3000
|
||||
- AUTH_SECRET=${AUTH_SECRET}
|
||||
# Add other optional env vars as needed (SMTP, S3, OAuth, etc.)
|
||||
volumes:
|
||||
- app_data:/app/data
|
||||
- reactive_resume_data:/app/data
|
||||
networks:
|
||||
- reactive_resume_network
|
||||
depends_on:
|
||||
@@ -229,7 +224,7 @@ networks:
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
app_data:
|
||||
reactive_resume_data:
|
||||
```
|
||||
|
||||
**nginx configuration (`nginx.conf`):**
|
||||
@@ -241,7 +236,7 @@ events {
|
||||
|
||||
http {
|
||||
upstream reactive_resume {
|
||||
server app:3000;
|
||||
server reactive_resume:3000;
|
||||
}
|
||||
|
||||
# Redirect HTTP to HTTPS
|
||||
@@ -312,8 +307,7 @@ This example demonstrates a production-grade Docker Swarm deployment with multip
|
||||
```yaml compose-swarm.yml lines expandable
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:18
|
||||
command: postgres -c config_file=/etc/postgresql.conf
|
||||
image: postgres:latest
|
||||
networks:
|
||||
- reactive_resume_network
|
||||
volumes:
|
||||
@@ -322,37 +316,24 @@ services:
|
||||
- POSTGRES_DB=$POSTGRES_DB
|
||||
- POSTGRES_USER=$POSTGRES_USER
|
||||
- POSTGRES_PASSWORD=$POSTGRES_PASSWORD
|
||||
configs:
|
||||
- source: reactive_resume_postgres_config
|
||||
target: /etc/postgresql.conf
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U $POSTGRES_USER -d $POSTGRES_DB"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 1
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
delay: 5s
|
||||
max_attempts: 3
|
||||
window: 120s
|
||||
|
||||
printer:
|
||||
image: ghcr.io/browserless/chromium:latest
|
||||
networks:
|
||||
- reactive_resume_network
|
||||
environment:
|
||||
- TIMEOUT=120000
|
||||
- CONCURRENT=10
|
||||
- QUEUED=10
|
||||
- HEALTH=true
|
||||
- CONCURRENT=5
|
||||
# Optional: Set a token for authentication
|
||||
# - TOKEN=your-secret-token
|
||||
healthcheck:
|
||||
@@ -361,19 +342,9 @@ services:
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 2
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
delay: 5s
|
||||
max_attempts: 3
|
||||
window: 120s
|
||||
replicas: 1
|
||||
|
||||
seaweedfs:
|
||||
image: chrislusf/seaweedfs:latest
|
||||
@@ -391,19 +362,9 @@ services:
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 1
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
delay: 5s
|
||||
max_attempts: 3
|
||||
window: 120s
|
||||
|
||||
seaweedfs_create_bucket:
|
||||
image: quay.io/minio/mc:latest
|
||||
@@ -420,19 +381,14 @@ services:
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 1
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
delay: 10s
|
||||
max_attempts: 5
|
||||
window: 120s
|
||||
|
||||
app:
|
||||
reactive_resume:
|
||||
image: ghcr.io/amruthpillai/reactive-resume:latest
|
||||
networks:
|
||||
- traefik_network
|
||||
- reactive_resume_network
|
||||
volumes:
|
||||
- reactive_resume_app_data:/app/data
|
||||
- reactive_resume_data:/app/data
|
||||
environment:
|
||||
- APP_URL=$APP_URL
|
||||
# If using browserless with token auth, include the token in the URL:
|
||||
@@ -461,27 +417,9 @@ services:
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 2
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
delay: 5s
|
||||
max_attempts: 3
|
||||
window: 120s
|
||||
update_config:
|
||||
parallelism: 1
|
||||
delay: 10s
|
||||
failure_action: rollback
|
||||
order: start-first
|
||||
rollback_config:
|
||||
parallelism: 1
|
||||
delay: 10s
|
||||
replicas: 1
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.app.rule=Host(`rxresu.me`)"
|
||||
@@ -507,8 +445,8 @@ volumes:
|
||||
name: reactive_resume_postgres_data
|
||||
reactive_resume_seaweedfs_data:
|
||||
name: reactive_resume_seaweedfs_data
|
||||
reactive_resume_app_data:
|
||||
name: reactive_resume_app_data
|
||||
reactive_resume_data:
|
||||
name: reactive_resume_data
|
||||
```
|
||||
|
||||
**Deploy the stack:**
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import { defineConfig } from "drizzle-kit";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
invariant(process.env.DATABASE_URL, "DATABASE_URL is not set");
|
||||
|
||||
export default defineConfig({
|
||||
out: "./migrations",
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
"tailwind": { "entry": ["src/styles.css"] },
|
||||
"project": ["src/**/*.{js,jsx,ts,tsx,mdx,css}"],
|
||||
"ignoreBinaries": ["mint"],
|
||||
"ignoreDependencies": ["npm-check-updates", "reflect-metadata", "node-addon-api", "node-gyp"],
|
||||
"ignore": ["src/schema/**/*.ts", "src/components/**/*.{ts,tsx}", "src/routeTree.gen.ts"]
|
||||
"ignoreDependencies": ["npm-check-updates", "node-addon-api", "node-gyp"],
|
||||
"ignore": ["src/schema/**/*.ts", "src/components/**/*.{ts,tsx}"]
|
||||
}
|
||||
|
||||
@@ -62,6 +62,9 @@ export default defineConfig({
|
||||
"zu-ZA": "en-US",
|
||||
default: "en-US",
|
||||
},
|
||||
formatOptions: {
|
||||
lineNumbers: false,
|
||||
},
|
||||
catalogs: [
|
||||
{
|
||||
path: "<rootDir>/locales/{locale}",
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
DROP INDEX "user_email_index";--> statement-breakpoint
|
||||
DROP INDEX "user_username_index";--> statement-breakpoint
|
||||
DROP INDEX "resume_statistics_resume_id_index";--> statement-breakpoint
|
||||
DROP INDEX "verification_identifier_index";--> statement-breakpoint
|
||||
CREATE INDEX "resume_created_at_index" ON "resume" ("created_at");--> statement-breakpoint
|
||||
CREATE INDEX "user_created_at_index" ON "user" ("created_at");
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"name": "reactive-resume",
|
||||
"description": "Reactive Resume is a free and open-source resume builder that simplifies the process of creating, updating, and sharing your resume.",
|
||||
"version": "5.0.1",
|
||||
"version": "5.0.10",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"packageManager": "pnpm@10.28.1+sha512.7d7dbbca9e99447b7c3bf7a73286afaaf6be99251eb9498baefa7d406892f67b879adb3a1d7e687fc4ccc1a388c7175fbaae567a26ab44d1067b54fcb0d6a316",
|
||||
"packageManager": "pnpm@10.30.2+sha512.36cdc707e7b7940a988c9c1ecf88d084f8514b5c3f085f53a2e244c2921d3b2545bc20dd4ebe1fc245feec463bb298aecea7a63ed1f7680b877dc6379d8d0cb4",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/amruthpillai/reactive-resume.git"
|
||||
@@ -26,118 +26,122 @@
|
||||
"docs:dev": "cd docs && npx mint dev",
|
||||
"knip": "knip",
|
||||
"lingui:extract": "lingui extract --clean --overwrite",
|
||||
"lint": "biome check --write .",
|
||||
"lint": "biome check --write",
|
||||
"start": "node .output/server/index.mjs",
|
||||
"typecheck": "tsc --noEmit"
|
||||
"typecheck": "tsgo --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ai-sdk/anthropic": "^3.0.23",
|
||||
"@ai-sdk/google": "^3.0.13",
|
||||
"@ai-sdk/openai": "^3.0.18",
|
||||
"@aws-sdk/client-s3": "^3.975.0",
|
||||
"@better-auth/core": "1.5.0-beta.9",
|
||||
"@better-auth/passkey": "1.5.0-beta.9",
|
||||
"@ai-sdk/anthropic": "^3.0.46",
|
||||
"@ai-sdk/google": "^3.0.30",
|
||||
"@ai-sdk/openai": "^3.0.31",
|
||||
"@ai-sdk/react": "^3.0.99",
|
||||
"@aws-sdk/client-s3": "^3.996.0",
|
||||
"@better-auth/core": "1.5.0-beta.18",
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"@fontsource-variable/ibm-plex-sans": "^5.2.8",
|
||||
"@hookform/resolvers": "^5.2.2",
|
||||
"@lingui/core": "^5.9.0",
|
||||
"@lingui/react": "^5.9.0",
|
||||
"@lingui/core": "^5.9.2",
|
||||
"@lingui/react": "^5.9.2",
|
||||
"@monaco-editor/react": "4.8.0-rc.3",
|
||||
"@orpc/client": "^1.13.4",
|
||||
"@orpc/json-schema": "^1.13.4",
|
||||
"@orpc/openapi": "^1.13.4",
|
||||
"@orpc/server": "^1.13.4",
|
||||
"@orpc/tanstack-query": "^1.13.4",
|
||||
"@orpc/zod": "^1.13.4",
|
||||
"@orpc/client": "^1.13.5",
|
||||
"@orpc/json-schema": "^1.13.5",
|
||||
"@orpc/openapi": "^1.13.5",
|
||||
"@orpc/server": "^1.13.5",
|
||||
"@orpc/tanstack-query": "^1.13.5",
|
||||
"@orpc/zod": "^1.13.5",
|
||||
"@phosphor-icons/react": "^2.1.10",
|
||||
"@phosphor-icons/web": "^2.1.2",
|
||||
"@sindresorhus/slugify": "^3.0.0",
|
||||
"@t3-oss/env-core": "^0.13.10",
|
||||
"@tanstack/react-query": "5.90.20",
|
||||
"@tanstack/react-router": "^1.156.0",
|
||||
"@tanstack/react-router-ssr-query": "^1.156.0",
|
||||
"@tanstack/react-start": "^1.156.0",
|
||||
"@tanstack/zod-adapter": "^1.156.0",
|
||||
"@tiptap/extension-highlight": "^3.17.0",
|
||||
"@tiptap/extension-table": "^3.17.0",
|
||||
"@tiptap/extension-text-align": "^3.17.0",
|
||||
"@tiptap/pm": "^3.17.0",
|
||||
"@tiptap/react": "^3.17.0",
|
||||
"@tiptap/starter-kit": "^3.17.0",
|
||||
"ai": "^6.0.49",
|
||||
"ai-sdk-ollama": "^3.3.0",
|
||||
"@tanstack/react-query": "5.90.21",
|
||||
"@tanstack/react-router": "^1.162.8",
|
||||
"@tanstack/react-router-ssr-query": "^1.162.8",
|
||||
"@tanstack/react-start": "^1.162.8",
|
||||
"@tanstack/zod-adapter": "^1.162.8",
|
||||
"@tiptap/extension-highlight": "^3.20.0",
|
||||
"@tiptap/extension-table": "^3.20.0",
|
||||
"@tiptap/extension-text-align": "^3.20.0",
|
||||
"@tiptap/pm": "^3.20.0",
|
||||
"@tiptap/react": "^3.20.0",
|
||||
"@tiptap/starter-kit": "^3.20.0",
|
||||
"ai": "^6.0.97",
|
||||
"ai-sdk-ollama": "^3.7.1",
|
||||
"bcrypt": "^6.0.0",
|
||||
"better-auth": "1.5.0-beta.9",
|
||||
"better-auth": "1.5.0-beta.18",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.1.1",
|
||||
"dompurify": "^3.3.1",
|
||||
"drizzle-orm": "^1.0.0-beta.12-a5629fb",
|
||||
"drizzle-orm": "^1.0.0-beta.15-859cf75",
|
||||
"drizzle-zod": "^0.8.3",
|
||||
"@modelcontextprotocol/sdk": "^1.27.0",
|
||||
"es-toolkit": "^1.44.0",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-json-patch": "^3.1.1",
|
||||
"fuse.js": "^7.1.0",
|
||||
"immer": "^11.1.3",
|
||||
"immer": "^11.1.4",
|
||||
"input-otp": "^1.4.2",
|
||||
"js-cookie": "^3.0.5",
|
||||
"monaco-editor": "^0.55.1",
|
||||
"motion": "^12.29.0",
|
||||
"nodemailer": "^7.0.12",
|
||||
"pg": "^8.17.2",
|
||||
"puppeteer-core": "^24.36.0",
|
||||
"motion": "^12.34.3",
|
||||
"nodemailer": "^8.0.1",
|
||||
"pg": "^8.18.0",
|
||||
"puppeteer-core": "^24.37.5",
|
||||
"qrcode.react": "^4.2.0",
|
||||
"radix-ui": "^1.4.3",
|
||||
"react": "^19.2.3",
|
||||
"react-dom": "^19.2.3",
|
||||
"react-hook-form": "^7.71.1",
|
||||
"react-hotkeys-hook": "^5.2.3",
|
||||
"react-resizable-panels": "^4.4.2",
|
||||
"react-window": "^2.2.5",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
"react-hook-form": "^7.71.2",
|
||||
"react-hotkeys-hook": "^5.2.4",
|
||||
"react-resizable-panels": "^4.6.5",
|
||||
"react-window": "^2.2.7",
|
||||
"react-zoom-pan-pinch": "^3.7.0",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"shadcn": "^3.7.0",
|
||||
"shadcn": "^3.8.5",
|
||||
"sharp": "^0.34.5",
|
||||
"sonner": "^2.0.7",
|
||||
"tailwind-merge": "^3.4.0",
|
||||
"tailwindcss": "^4.1.18",
|
||||
"tiny-invariant": "^1.3.3",
|
||||
"tailwind-merge": "^3.5.0",
|
||||
"tailwindcss": "^4.2.1",
|
||||
"ts-pattern": "^5.9.0",
|
||||
"tw-animate-css": "^1.4.0",
|
||||
"unique-names-generator": "^4.7.1",
|
||||
"usehooks-ts": "^3.1.1",
|
||||
"uuid": "^13.0.0",
|
||||
"workbox-window": "^7.4.0",
|
||||
"zod": "^4.3.6",
|
||||
"zustand": "^5.0.10"
|
||||
"zundo": "^2.3.0",
|
||||
"zustand": "^5.0.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^2.3.12",
|
||||
"@lingui/babel-plugin-lingui-macro": "^5.9.0",
|
||||
"@lingui/cli": "^5.9.0",
|
||||
"@lingui/vite-plugin": "^5.9.0",
|
||||
"@tailwindcss/vite": "^4.1.18",
|
||||
"@biomejs/biome": "^2.4.4",
|
||||
"@lingui/babel-plugin-lingui-macro": "^5.9.2",
|
||||
"@lingui/cli": "^5.9.2",
|
||||
"@lingui/vite-plugin": "^5.9.2",
|
||||
"@tailwindcss/vite": "^4.2.1",
|
||||
"@types/bcrypt": "^6.0.0",
|
||||
"@types/js-cookie": "^3.0.6",
|
||||
"@types/node": "^25.0.10",
|
||||
"@types/nodemailer": "^7.0.5",
|
||||
"@types/node": "^25.3.0",
|
||||
"@types/nodemailer": "^7.0.11",
|
||||
"@types/pg": "^8.16.0",
|
||||
"@types/react": "^19.2.9",
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@vitejs/plugin-react": "^5.1.2",
|
||||
"@typescript/native-preview": "7.0.0-dev.20260224.1",
|
||||
"@vitejs/plugin-react": "^5.1.4",
|
||||
"babel-plugin-macros": "^3.1.0",
|
||||
"drizzle-kit": "^1.0.0-beta.12-a5629fb",
|
||||
"knip": "^5.82.1",
|
||||
"drizzle-kit": "^1.0.0-beta.15-859cf75",
|
||||
"knip": "^5.85.0",
|
||||
"nitro": "npm:nitro-nightly@latest",
|
||||
"node-addon-api": "^8.5.0",
|
||||
"node-gyp": "^12.1.0",
|
||||
"npm-check-updates": "^19.3.1",
|
||||
"node-gyp": "^12.2.0",
|
||||
"npm-check-updates": "^19.4.1",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "npm:rolldown-vite@latest",
|
||||
"vite": "^8.0.0-beta.15",
|
||||
"vite-plugin-pwa": "^1.2.0"
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"vite": "npm:rolldown-vite@latest"
|
||||
"vite": "^8.0.0-beta.15"
|
||||
},
|
||||
"onlyBuiltDependencies": [
|
||||
"@prisma/engines",
|
||||
|
||||
|
After Width: | Height: | Size: 385 KiB |
|
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 117 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 415 KiB |