diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 486620c86..497563031 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -14,16 +14,17 @@
"customizations": {
"vscode": {
- "extensions": ["biomejs.biome", "bradlc.vscode-tailwindcss", "lokalise.i18n-ally"],
+ "extensions": [
+ "oxc.oxc-vscode",
+ "bradlc.vscode-tailwindcss",
+ "lokalise.i18n-ally",
+ "typescriptteam.native-preview"
+ ],
"settings": {
- "biome.enabled": true,
+ "editor.defaultFormatter": "oxc.oxc-vscode",
"editor.codeActionsOnSave": {
- "source.biome": "explicit",
- "source.fixAll.biome": "explicit",
- "source.organizeImports.biome": "explicit"
- },
- "editor.defaultFormatter": "biomejs.biome",
- "typescript.tsdk": "node_modules/typescript/lib"
+ "source.fixAll.oxc": "always"
+ }
}
}
},
diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml
index fa0ab42ec..b069ba585 100644
--- a/.devcontainer/docker-compose.yml
+++ b/.devcontainer/docker-compose.yml
@@ -54,8 +54,7 @@ services:
CONCURRENT: "5"
TOKEN: "1234567890"
healthcheck:
- test:
- ["CMD", "curl", "-f", "http://localhost:3000/pressure?token=1234567890"]
+ test: ["CMD", "curl", "-f", "http://localhost:3000/pressure?token=1234567890"]
interval: 10s
timeout: 5s
retries: 10
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index 189c84f84..0348c6de0 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1,2 +1,2 @@
github: AmruthPillai
-open_collective: reactive-resume
\ No newline at end of file
+open_collective: reactive-resume
diff --git a/.github/ISSUE_TEMPLATE/2-feature-request.yml b/.github/ISSUE_TEMPLATE/2-feature-request.yml
index 7b2839953..e4a0665c4 100644
--- a/.github/ISSUE_TEMPLATE/2-feature-request.yml
+++ b/.github/ISSUE_TEMPLATE/2-feature-request.yml
@@ -20,4 +20,4 @@ body:
label: Feature Description
description: A detailed description of the feature you would like to see in Reactive Resume. Please provide as much detail as possible as it will help me implement the feature faster.
validations:
- required: true
\ No newline at end of file
+ required: true
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index ec4bb386b..3ba13e0ce 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1 +1 @@
-blank_issues_enabled: false
\ No newline at end of file
+blank_issues_enabled: false
diff --git a/.github/workflows/autofix.yml b/.github/workflows/autofix.yml
new file mode 100644
index 000000000..50f4cdb5b
--- /dev/null
+++ b/.github/workflows/autofix.yml
@@ -0,0 +1,30 @@
+name: autofix.ci
+
+on:
+ pull_request:
+ push:
+ branches: ["main"]
+
+permissions:
+ contents: read
+
+jobs:
+ autofix:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v6
+
+ - uses: pnpm/action-setup@v4
+
+ - uses: actions/setup-node@v6
+ with:
+ node-version: lts/*
+ cache: pnpm
+
+ - run: pnpm install --frozen-lockfile
+
+ - run: pnpm run lint:fix
+ - run: pnpm run fmt:fix
+
+ - uses: autofix-ci/action@7a166d7532b277f34e16238930461bf77f9d7ed8
diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml
index 117c2ecc1..8607fa602 100644
--- a/.github/workflows/docker-build.yml
+++ b/.github/workflows/docker-build.yml
@@ -146,7 +146,7 @@ jobs:
VERSION="${{ steps.version.outputs.version }}"
MAJOR=$(echo "$VERSION" | cut -d. -f1)
MINOR=$(echo "$VERSION" | cut -d. -f2)
-
+
echo "major=$MAJOR" >> "$GITHUB_OUTPUT"
echo "minor=$MINOR" >> "$GITHUB_OUTPUT"
@@ -181,7 +181,7 @@ jobs:
--annotation "index:org.opencontainers.image.version=${{ steps.version.outputs.version }}" \
$(printf 'ghcr.io/${{ env.IMAGE }}@sha256:%s ' *) \
$(printf 'docker.io/${{ env.IMAGE }}@sha256:%s ' *)
-
+
# Get the digest of the multi-arch manifest
GHCR_DIGEST=$(docker buildx imagetools inspect ghcr.io/${{ env.IMAGE }}:v${{ steps.version.outputs.version }} --format '{{json .Manifest.Digest}}' | tr -d '"')
DOCKER_DIGEST=$(docker buildx imagetools inspect docker.io/${{ env.IMAGE }}:v${{ steps.version.outputs.version }} --format '{{json .Manifest.Digest}}' | tr -d '"')
@@ -195,7 +195,7 @@ jobs:
run: |
# Sign GHCR image
cosign sign --yes ghcr.io/${{ env.IMAGE }}@${{ steps.manifest.outputs.ghcr_digest }}
-
+
# Sign Docker Hub image
cosign sign --yes docker.io/${{ env.IMAGE }}@${{ steps.manifest.outputs.docker_digest }}
@@ -212,4 +212,4 @@ jobs:
username: ${{ secrets.SSH_USER }}
script: |
cd docker
- ./manage_stack.sh up reactive_resume
\ No newline at end of file
+ ./manage_stack.sh up reactive_resume
diff --git a/.oxfmtrc.json b/.oxfmtrc.json
new file mode 100644
index 000000000..49987fb55
--- /dev/null
+++ b/.oxfmtrc.json
@@ -0,0 +1,25 @@
+{
+ "$schema": "./node_modules/oxfmt/configuration_schema.json",
+ "useTabs": true,
+ "printWidth": 120,
+ "trailingComma": "all",
+ "ignorePatterns": [],
+ "sortPackageJson": {
+ "sortScripts": true
+ },
+ "sortTailwindcss": {
+ "stylesheet": "./src/styles/globals.css",
+ "functions": ["clsx", "cva", "cn"]
+ },
+ "sortImports": {
+ "groups": [
+ "type-import",
+ ["value-builtin", "value-external"],
+ "type-internal",
+ "value-internal",
+ ["type-parent", "type-sibling", "type-index"],
+ ["value-parent", "value-sibling", "value-index"],
+ "unknown"
+ ]
+ }
+}
diff --git a/.oxlintrc.json b/.oxlintrc.json
new file mode 100644
index 000000000..416d1b358
--- /dev/null
+++ b/.oxlintrc.json
@@ -0,0 +1,54 @@
+{
+ "$schema": "./node_modules/oxlint/configuration_schema.json",
+ "categories": {},
+ "env": {
+ "builtin": true
+ },
+ "globals": {},
+ "ignorePatterns": [],
+ "options": {
+ "typeAware": true
+ },
+ "plugins": [
+ "eslint",
+ "typescript",
+ "unicorn",
+ "oxc",
+ "react",
+ "react-perf",
+ "import",
+ "jsdoc",
+ "node",
+ "promise",
+ "vitest"
+ ],
+ "rules": {
+ "react/exhaustive-deps": "off"
+ },
+ "settings": {
+ "jsdoc": {
+ "augmentsExtendsReplacesDocs": false,
+ "exemptDestructuredRootsFromChecks": false,
+ "ignoreInternal": false,
+ "ignorePrivate": false,
+ "ignoreReplacesDocs": true,
+ "implementsReplacesDocs": false,
+ "overrideReplacesDocs": true,
+ "tagNamePreference": {}
+ },
+ "jsx-a11y": {
+ "attributes": {},
+ "components": {},
+ "polymorphicPropName": ""
+ },
+ "react": {
+ "version": "19.2.4",
+ "formComponents": [],
+ "linkComponents": ["Link"],
+ "componentWrapperFunctions": []
+ },
+ "vitest": {
+ "typecheck": true
+ }
+ }
+}
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
index ef3ea0582..1d7e2ea1f 100644
--- a/.vscode/extensions.json
+++ b/.vscode/extensions.json
@@ -1,3 +1,8 @@
{
- "recommendations": ["biomejs.biome", "bradlc.vscode-tailwindcss", "lokalise.i18n-ally"]
+ "recommendations": [
+ "oxc.oxc-vscode",
+ "bradlc.vscode-tailwindcss",
+ "lokalise.i18n-ally",
+ "typescriptteam.native-preview"
+ ]
}
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 391f897d6..0de7c1f70 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,14 +1,12 @@
{
"[typescript]": {
- "editor.defaultFormatter": "biomejs.biome"
+ "editor.defaultFormatter": "oxc.oxc-vscode"
},
- "biome.enabled": true,
"editor.codeActionsOnSave": {
- "source.biome": "explicit",
- "source.fixAll.biome": "explicit",
- "source.organizeImports.biome": "explicit"
+ "source.fixAll.oxc": "always"
},
- "editor.defaultFormatter": "biomejs.biome",
+ "editor.defaultFormatter": "oxc.oxc-vscode",
+ "editor.formatOnSave": true,
"files.associations": {
"*.css": "tailwindcss"
},
@@ -24,7 +22,7 @@
"i18n-ally.enabledParsers": ["po"],
"i18n-ally.localesPaths": ["locales"],
"i18n-ally.sourceLanguage": "en-US",
- "js/ts.experimental.useTsgo": true,
+ "oxc.fmt.configPath": ".oxfmtrc.json",
"search.exclude": {
"**/routeTree.gen.ts": true,
"locales/**.po": true,
@@ -36,5 +34,6 @@
["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"],
["cn\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"]
],
- "tailwindCSS.experimental.configFile": "src/styles/globals.css"
+ "tailwindCSS.experimental.configFile": "src/styles/globals.css",
+ "typescript.experimental.useTsgo": true
}
diff --git a/AGENTS.md b/AGENTS.md
index 88483544d..768f16d97 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -32,12 +32,13 @@ Copy `.env.example` to `.env` if not present. Key notes for local dev:
See `scripts` in `package.json`. Key ones:
-| Task | Command |
-|---|---|
-| Dev server | `pnpm dev` (port 3000) |
-| Lint (Biome) | `pnpm lint` |
-| Typecheck | `pnpm typecheck` |
-| DB migrations | `pnpm db:generate` / `pnpm db:migrate` (auto-runs on dev start) |
+| Task | Command |
+| -------------- | --------------------------------------------------------------- |
+| Dev server | `pnpm dev` (port 3000) |
+| Lint (Oxlint) | `pnpm lint` |
+| Format (Oxfmt) | `pnpm fmt` |
+| Typecheck | `pnpm typecheck` |
+| DB migrations | `pnpm db:generate` / `pnpm db:migrate` (auto-runs on dev start) |
### Gotchas
diff --git a/README.md b/README.md
index 89b165245..b3cf3e5fa 100644
--- a/README.md
+++ b/README.md
@@ -151,29 +151,29 @@ For detailed setup instructions, environment configuration, and self-hosting gui
## Tech Stack
-| Category | Technology |
-| ---------------- | ------------------------------------ |
-| Framework | TanStack Start (React 19, Vite) |
-| Runtime | Node.js |
-| Language | TypeScript |
-| Database | PostgreSQL with Drizzle ORM |
-| API | ORPC (Type-safe RPC) |
-| Auth | Better Auth |
-| Styling | Tailwind CSS |
-| UI Components | Radix UI |
-| State Management | Zustand + TanStack Query |
+| Category | Technology |
+| ---------------- | ------------------------------- |
+| Framework | TanStack Start (React 19, Vite) |
+| Runtime | Node.js |
+| Language | TypeScript |
+| Database | PostgreSQL with Drizzle ORM |
+| API | ORPC (Type-safe RPC) |
+| Auth | Better Auth |
+| Styling | Tailwind CSS |
+| UI Components | Radix UI |
+| State Management | Zustand + TanStack Query |
## Documentation
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/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 |
+| Guide | Description |
+| ---------------------------------------------------------------------------- | -------------------------------- |
+| [Getting Started](https://docs.rxresu.me/getting-started) | First-time setup and basic usage |
+| [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 |
## Self-Hosting
diff --git a/biome.json b/biome.json
deleted file mode 100644
index d29a83034..000000000
--- a/biome.json
+++ /dev/null
@@ -1,69 +0,0 @@
-{
- "$schema": "node_modules/@biomejs/biome/configuration_schema.json",
- "vcs": {
- "enabled": true,
- "clientKind": "git",
- "useIgnoreFile": true,
- "defaultBranch": "main"
- },
- "files": {
- "ignoreUnknown": true,
- "includes": [
- "**",
- "!**/docs/spec.json",
- "!**/webfontlist.json",
- "!**/schema/schema.json",
- "!**/src/routeTree.gen.ts"
- ]
- },
- "formatter": {
- "lineWidth": 120,
- "indentStyle": "tab"
- },
- "assist": {
- "actions": {
- "recommended": true,
- "source": {
- "organizeImports": "on"
- }
- }
- },
- "linter": {
- "rules": {
- "a11y": "off",
- "recommended": true,
- "suspicious": {
- "noArrayIndexKey": "off"
- },
- "correctness": {
- "noUnknownTypeSelector": "off",
- "useExhaustiveDependencies": "off",
- "noUnusedImports": {
- "fix": "safe",
- "level": "error",
- "options": {}
- }
- },
- "nursery": {
- "useSortedClasses": {
- "fix": "safe",
- "level": "warn",
- "options": {
- "functions": ["cn", "cva"],
- "attributes": ["class", "className"]
- }
- }
- }
- }
- },
- "javascript": {
- "formatter": {
- "quoteStyle": "double"
- }
- },
- "css": {
- "parser": {
- "tailwindDirectives": true
- }
- }
-}
diff --git a/compose.dev.yml b/compose.dev.yml
index 56d1bb91a..d5d17630b 100644
--- a/compose.dev.yml
+++ b/compose.dev.yml
@@ -45,8 +45,7 @@ services:
- CONCURRENT=5
- TOKEN=1234567890
healthcheck:
- test:
- ["CMD", "curl", "-f", "http://localhost:3000/pressure?token=1234567890"]
+ test: ["CMD", "curl", "-f", "http://localhost:3000/pressure?token=1234567890"]
interval: 10s
timeout: 5s
retries: 10
diff --git a/compose.yml b/compose.yml
index 4595b74a1..049beb15d 100644
--- a/compose.yml
+++ b/compose.yml
@@ -26,8 +26,7 @@ services:
- CONCURRENT=5
- TOKEN=1234567890
healthcheck:
- test:
- ["CMD", "curl", "-f", "http://localhost:3000/pressure?token=1234567890"]
+ test: ["CMD", "curl", "-f", "http://localhost:3000/pressure?token=1234567890"]
interval: 10s
timeout: 5s
retries: 10
diff --git a/docs/changelog/index.mdx b/docs/changelog/index.mdx
index 9435eb010..687147f6f 100644
--- a/docs/changelog/index.mdx
+++ b/docs/changelog/index.mdx
@@ -11,25 +11,22 @@ rss: true
- Added a dedicated `src/server.ts` server entry using `FastResponse` for better runtime compatibility.
- Sidebar button list now scrolls internally on overflow.
- - **Fixes**
- - Printer endpoint now resolves to IP when using chromedp, improving compatibility with proxied deployments.
- - Switched OpenAI integration to `chat/completions` for resume import and AI features.
- - AI PDF/DOCX resume parsing is now more robust via direct file send (multimodal input).
- - Resume import normalization hardened: section defaults merged, required fields enforced, missing IDs/flags autogenerated.
- - Fixed UI migration regressions (accordion behavior, button interaction, right sidebar scrollbar).
- - Fixed multi-page PDF crashes and Gemini API ingestion errors.
- - Fixed role title styling, role spacing, and issues with skills item rendering and template headings.
+- **Fixes**
+ - Printer endpoint now resolves to IP when using chromedp, improving compatibility with proxied deployments.
+ - Switched OpenAI integration to `chat/completions` for resume import and AI features.
+ - AI PDF/DOCX resume parsing is now more robust via direct file send (multimodal input).
+ - Resume import normalization hardened: section defaults merged, required fields enforced, missing IDs/flags autogenerated.
+ - Fixed UI migration regressions (accordion behavior, button interaction, right sidebar scrollbar).
+ - Fixed multi-page PDF crashes and Gemini API ingestion errors.
+ - Fixed role title styling, role spacing, and issues with skills item rendering and template headings.
- - **Maintenance & Dependencies**
- - Updated dependencies and lockfile.
- - Synced translation strings across locales, including multiple contributions from Crowdin.
- - General dependency and code style improvements, plus removal of redundant indexes and plugins.
+- **Maintenance & Dependencies**
+ - Updated dependencies and lockfile.
+ - Synced translation strings across locales, including multiple contributions from Crowdin.
+ - General dependency and code style improvements, plus removal of redundant indexes and plugins.
- - **Community & Contributors**
- - Users can now view other public resumes while logged in.
- - New Contributors: @yang-summer, @FrancocDev, @obitton, @jondycz.
- - Full changelog: https://docs.rxresu.me/changelog
-
+- **Community & Contributors** - Users can now view other public resumes while logged in. - New Contributors: @yang-summer, @FrancocDev, @obitton, @jondycz. - Full changelog: https://docs.rxresu.me/changelog
+
- **Fixes**
@@ -46,131 +43,128 @@ rss: true
- 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.
-
+- **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.
+
- - 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.
+ - 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.
- - Remove Passkey support from the authentication system, as it was causing issues with the authentication provider.
- - Update dependencies to the latest versions.
+ - Remove Passkey support from the authentication system, as it was causing issues with the authentication provider. -
+ Update dependencies to the latest versions.
- - 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.
+ - 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.
- - 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.
+ - 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.
-
- - 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.
+ - 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.
- - 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).
+ - 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).
- - 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.
+ - 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.
- - 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.
+ - 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.
- - 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.
+ - 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.
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
+**Here are some of the key changes from the previous version:**
- 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.
-
\ No newline at end of file
+- 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.
+
+
diff --git a/docs/community/spotlight.mdx b/docs/community/spotlight.mdx
index 4a6d19208..e6b5c97e0 100644
--- a/docs/community/spotlight.mdx
+++ b/docs/community/spotlight.mdx
@@ -4,7 +4,9 @@ description: "A showcase of articles, videos, and social media posts from the Re
---
-Have you created something that makes use of Reactive Resume, or spread the word about it in your own way? I'd love to feature it here! Send me an email at [hello@amruthpillai.com](mailto:hello@amruthpillai.com) and I'll make sure to add it to this page.
+ Have you created something that makes use of Reactive Resume, or spread the word about it in your own way? I'd love to
+ feature it here! Send me an email at [hello@amruthpillai.com](mailto:hello@amruthpillai.com) and I'll make sure to add
+ it to this page.
---
@@ -16,16 +18,15 @@ A collection of blog posts and articles written by the community about Reactive
{/* Add article cards here */}
- {/* Example:
+{/\* Example:
+
A brief description of the article.
*/}
-
-No articles have been featured yet. Be the first to contribute!
-
+No articles have been featured yet. Be the first to contribute!
---
@@ -36,16 +37,15 @@ Video tutorials, reviews, and walkthroughs created by the community.
{/* Add video cards here */}
- {/* Example:
+{/\* Example:
+
A brief description of the video.
*/}
-
-No videos have been featured yet. Be the first to contribute!
-
+No videos have been featured yet. Be the first to contribute!
---
@@ -54,15 +54,14 @@ No videos have been featured yet. Be the first to contribute!
Posts and threads from social media platforms sharing experiences with Reactive Resume.
- {/* Add social media cards here */}
-
- {/* Example:
+ {/* Add social media cards here */}
+
+ {/* Example:
+
A brief description of the post.
*/}
-
-No social media posts have been featured yet. Be the first to contribute!
-
+No social media posts have been featured yet. Be the first to contribute!
diff --git a/docs/contributing/architecture.mdx b/docs/contributing/architecture.mdx
index 4bf8efe77..8ff759617 100644
--- a/docs/contributing/architecture.mdx
+++ b/docs/contributing/architecture.mdx
@@ -12,18 +12,14 @@ This guide provides a comprehensive overview of Reactive Resume's architecture a
Reactive Resume is built with a modern, type-safe stack:
-
- - **React 19** with TanStack Start
- - **TypeScript** for type safety
- - **Tailwind CSS** for styling
- - **Radix UI** for accessible components
-
-
- - **ORPC** for type-safe RPC
- - **Drizzle ORM** with PostgreSQL
- - **Better Auth** for authentication
- - **Sharp** for image processing
-
+
+ - **React 19** with TanStack Start - **TypeScript** for type safety - **Tailwind CSS** for styling - **Radix UI**
+ for accessible components
+
+
+ - **ORPC** for type-safe RPC - **Drizzle ORM** with PostgreSQL - **Better Auth** for authentication - **Sharp** for
+ image processing
+
---
@@ -70,89 +66,61 @@ flowchart TD
### Root Level
-| Directory | Purpose |
-|-----------|---------|
-| `src/` | Main application source code |
-| `public/` | Static assets served directly |
-| `locales/` | Translation files (.po format) |
-| `migrations/` | Database migration files |
-| `docs/` | Mintlify documentation |
-| `data/` | Local data storage (fonts, uploads) |
-| `scripts/` | Utility scripts |
+| Directory | Purpose |
+| ------------- | ----------------------------------- |
+| `src/` | Main application source code |
+| `public/` | Static assets served directly |
+| `locales/` | Translation files (.po format) |
+| `migrations/` | Database migration files |
+| `docs/` | Mintlify documentation |
+| `data/` | Local data storage (fonts, uploads) |
+| `scripts/` | Utility scripts |
### Source Code (`src/`)
-
- Reusable React components organized by category:
-
- - **`ui/`** — Base UI components (Button, Card, Dialog, etc.)
- - **`resume/`** — Resume-specific components (sections, templates)
- - **`input/`** — Form input components (ColorPicker, RichInput)
- - **`layout/`** — Layout components (Sidebar, LoadingScreen)
- - **`animation/`** — Animation components (Spotlight, TextMask)
- - **`theme/`** — Theme management components
- - **`typography/`** — Font management components
-
-
-
- File-based routing using TanStack Router:
-
- - **`__root.tsx`** — Root layout with providers
- - **`_home/`** — Public home page routes
- - **`auth/`** — Authentication routes (login, register, etc.)
- - **`dashboard/`** — User dashboard routes
- - **`builder/`** — Resume builder routes (the main editor)
- - **`printer/`** — PDF printing route
- - **`api/`** — API routes
-
-
-
- Third-party service integrations:
-
- - **`auth/`** — Better Auth client configuration
- - **`drizzle/`** — Database schema and utilities
- - **`orpc/`** — API router, client, and services
- - **`ai/`** — AI service integrations
- - **`import/`** — Resume import utilities
-
-
-
- Modal dialog components:
-
- - **`auth/`** — Authentication dialogs
- - **`resume/`** — Resume management dialogs
- - **`api-key/`** — API key management dialogs
- - **`manager.tsx`** — Dialog manager component
- - **`store.ts`** — Dialog state management (Zustand)
-
-
-
- Zod schemas for validation:
-
- - **`resume/`** — Resume data schemas
- - **`icons.ts`** — Icon definitions
- - **`templates.ts`** — Template definitions
-
-
-
- Custom React hooks:
-
- - `use-confirm.tsx` — Confirmation dialog hook
- - `use-prompt.tsx` — Prompt dialog hook
- - `use-mobile.tsx` — Mobile detection hook
- - `use-safe-context.tsx` — Safe context consumption
-
-
-
- Utility functions:
-
- - `env.ts` — Environment variable validation
- - `locale.ts` — Locale utilities
- - `theme.ts` — Theme utilities
- - `string.ts` — String manipulation
- - `file.ts` — File handling utilities
-
+
+ Reusable React components organized by category: - **`ui/`** — Base UI components (Button, Card, Dialog, etc.) -
+ **`resume/`** — Resume-specific components (sections, templates) - **`input/`** — Form input components
+ (ColorPicker, RichInput) - **`layout/`** — Layout components (Sidebar, LoadingScreen) - **`animation/`** — Animation
+ components (Spotlight, TextMask) - **`theme/`** — Theme management components - **`typography/`** — Font management
+ components
+
+
+
+ File-based routing using TanStack Router: - **`__root.tsx`** — Root layout with providers - **`_home/`** — Public
+ home page routes - **`auth/`** — Authentication routes (login, register, etc.) - **`dashboard/`** — User dashboard
+ routes - **`builder/`** — Resume builder routes (the main editor) - **`printer/`** — PDF printing route - **`api/`**
+ — API routes
+
+
+
+ Third-party service integrations: - **`auth/`** — Better Auth client configuration - **`drizzle/`** — Database
+ schema and utilities - **`orpc/`** — API router, client, and services - **`ai/`** — AI service integrations -
+ **`import/`** — Resume import utilities
+
+
+
+ Modal dialog components: - **`auth/`** — Authentication dialogs - **`resume/`** — Resume management dialogs -
+ **`api-key/`** — API key management dialogs - **`manager.tsx`** — Dialog manager component - **`store.ts`** — Dialog
+ state management (Zustand)
+
+
+
+ Zod schemas for validation: - **`resume/`** — Resume data schemas - **`icons.ts`** — Icon definitions -
+ **`templates.ts`** — Template definitions
+
+
+
+ Custom React hooks: - `use-confirm.tsx` — Confirmation dialog hook - `use-prompt.tsx` — Prompt dialog hook -
+ `use-mobile.tsx` — Mobile detection hook - `use-safe-context.tsx` — Safe context consumption
+
+
+
+ Utility functions: - `env.ts` — Environment variable validation - `locale.ts` — Locale utilities - `theme.ts` —
+ Theme utilities - `string.ts` — String manipulation - `file.ts` — File handling utilities
+
+
---
@@ -163,16 +131,14 @@ flowchart TD
Routes are automatically generated from the file structure in `src/routes/`. TanStack Router conventions:
-| Pattern | Description | Example |
-|---------|-------------|---------|
-| `index.tsx` | Index route | `/dashboard` |
-| `$param.tsx` | Dynamic parameter | `/builder/$resumeId` |
-| `_layout/` | Layout group (prefix) | `_home/` |
-| `__root.tsx` | Root layout | Wraps all routes |
+| Pattern | Description | Example |
+| ------------ | --------------------- | -------------------- |
+| `index.tsx` | Index route | `/dashboard` |
+| `$param.tsx` | Dynamic parameter | `/builder/$resumeId` |
+| `_layout/` | Layout group (prefix) | `_home/` |
+| `__root.tsx` | Root layout | Wraps all routes |
-
- Never edit `src/routeTree.gen.ts` manually — it's auto-generated when you run the dev server.
-
+Never edit `src/routeTree.gen.ts` manually — it's auto-generated when you run the dev server.
### API Layer (ORPC)
@@ -203,11 +169,11 @@ const { data } = useQuery(orpc.resume.findMany.queryOptions());
Reactive Resume uses a hybrid approach:
-| Type | Tool | Use Case |
-|------|------|----------|
-| Server State | TanStack Query | API data, caching, sync |
-| Client State | Zustand | UI state, dialogs, preferences |
-| Form State | React Hook Form | Form inputs and validation |
+| Type | Tool | Use Case |
+| ------------ | --------------- | ------------------------------ |
+| Server State | TanStack Query | API data, caching, sync |
+| Client State | Zustand | UI state, dialogs, preferences |
+| Form State | React Hook Form | Form inputs and validation |
### Database Schema
@@ -217,10 +183,10 @@ The database schema is defined using Drizzle ORM in `src/integrations/drizzle/sc
import { pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
export const resume = pgTable("resume", {
- id: uuid("id").primaryKey().defaultRandom(),
- title: text("title").notNull(),
- slug: text("slug").notNull(),
- // ... more fields
+ id: uuid("id").primaryKey().defaultRandom(),
+ title: text("title").notNull(),
+ slug: text("slug").notNull(),
+ // ... more fields
});
```
@@ -240,12 +206,12 @@ export const resume = pgTable("resume", {
import { cn } from "@/utils/style";
interface MyComponentProps {
- title: string;
- className?: string;
+ title: string;
+ className?: string;
}
export const MyComponent = ({ title, className }: MyComponentProps) => {
- return
;
}
```
@@ -280,11 +246,9 @@ import { z } from "zod";
import { publicProcedure, router } from "../server";
export const myRouter = router({
- hello: publicProcedure
- .input(z.object({ name: z.string() }))
- .handler(async ({ input }) => {
- return { message: `Hello, ${input.name}!` };
- }),
+ hello: publicProcedure.input(z.object({ name: z.string() })).handler(async ({ input }) => {
+ return { message: `Hello, ${input.name}!` };
+ }),
});
```
@@ -300,81 +264,59 @@ import { Trans } from "@lingui/react/macro";
// In component
const title = t`Welcome`;
-Click here to continue
+Click here to continue;
```
---
## Configuration Files
-| File | Purpose |
-|------|---------|
-| `vite.config.ts` | Vite bundler configuration |
-| `tsconfig.json` | TypeScript configuration |
-| `biome.json` | Linter and formatter settings |
-| `drizzle.config.ts` | Drizzle ORM configuration |
-| `lingui.config.ts` | Lingui i18n configuration |
-| `components.json` | shadcn/ui component configuration |
+| File | Purpose |
+| ------------------- | --------------------------------- |
+| `vite.config.ts` | Vite bundler configuration |
+| `tsconfig.json` | TypeScript configuration |
+| `.oxfmtrc.json` | Formatter settings |
+| `.oxlintrc.json` | Linter settings |
+| `drizzle.config.ts` | Drizzle ORM configuration |
+| `lingui.config.ts` | Lingui i18n configuration |
+| `components.json` | shadcn/ui component configuration |
---
## Contributing Guidelines
-
- Fork the repository on GitHub and clone your fork locally.
-
-
-
- Create a feature branch from `main`:
- ```bash
- git checkout -b feature/my-feature
- ```
-
-
-
- Implement your changes following the patterns described above.
-
-
-
- Ensure the app works correctly with your changes:
- ```bash
- pnpm run dev
- pnpm run lint
- pnpm run typecheck
- ```
-
-
-
- Write clear commit messages and push to your fork.
-
-
-
- Open a PR against the main repository with a clear description of your changes.
-
+ Fork the repository on GitHub and clone your fork locally.
+
+
+ Create a feature branch from `main`: ```bash git checkout -b feature/my-feature ```
+
+
+ Implement your changes following the patterns described above.
+
+
+ Ensure the app works correctly with your changes: ```bash pnpm run dev pnpm run lint pnpm run typecheck ```
+
+
+ Write clear commit messages and push to your fork.
+
+
+ Open a PR against the main repository with a clear description of your changes.
+
+
-
- Make sure to read any `CONTRIBUTING.md` file in the repository for additional guidelines.
-
+Make sure to read any `CONTRIBUTING.md` file in the repository for additional guidelines.
---
## Need Help?
-
- Ask questions and discuss ideas with the community.
-
-
- Report bugs or request features.
-
+
+ Ask questions and discuss ideas with the community.
+
+
+ Report bugs or request features.
+
diff --git a/docs/contributing/development.mdx b/docs/contributing/development.mdx
index 63723d67e..49d0c62ee 100644
--- a/docs/contributing/development.mdx
+++ b/docs/contributing/development.mdx
@@ -4,11 +4,8 @@ description: "Set up a local development environment for Reactive Resume"
---
- **Prerequisites**:
- - [Node.js](https://nodejs.org/) v20 or higher
- - [pnpm](https://pnpm.io/) v10.28.0 or higher (package manager)
- - [Docker](https://docs.docker.com/get-docker/) and Docker Compose
- - [Git](https://git-scm.com/)
+ **Prerequisites**: - [Node.js](https://nodejs.org/) v20 or higher - [pnpm](https://pnpm.io/) v10.28.0 or higher
+ (package manager) - [Docker](https://docs.docker.com/get-docker/) and Docker Compose - [Git](https://git-scm.com/)
This guide walks you through setting up Reactive Resume for local development. Whether you're contributing to the project or customizing it for your needs, these steps will get you up and running.
@@ -87,14 +84,15 @@ This guide walks you through setting up Reactive Resume for local development. W
SMTP_HOST=localhost
SMTP_PORT=1025
```
-
+
**PDF Generation Note**: The `PRINTER_APP_URL` variable is required when running Reactive Resume outside of Docker while the printer service is running inside Docker (which is the case when using `compose.dev.yml`). The printer needs to reach your local app to render resumes for PDF generation. Since Docker containers cannot access `localhost` on your host machine directly, you must set `PRINTER_APP_URL` to `http://host.docker.internal:3000`. This special hostname allows Docker containers to communicate with services running on your host machine.
-
+
**Email Testing**: The development stack includes [Mailpit](https://mailpit.axllent.org/), an email testing tool. All emails sent by the application will be captured and viewable at [http://localhost:8025](http://localhost:8025). No emails will actually be sent to real addresses during development.
+
@@ -122,32 +120,33 @@ Here are the most commonly used scripts during development:
### Development
-| Command | Description |
-|---------|-------------|
-| `pnpm run dev` | Start the development server with hot reload |
-| `pnpm run build` | Build the application for production |
-| `pnpm run start` | Start the production server |
-| `pnpm run lint` | Run Biome linter and formatter |
-| `pnpm run typecheck` | Run TypeScript type checking |
+| Command | Description |
+| -------------------- | -------------------------------------------- |
+| `pnpm run dev` | Start the development server with hot reload |
+| `pnpm run build` | Build the application for production |
+| `pnpm run start` | Start the production server |
+| `pnpm run lint` | Run Oxlint linter and formatter |
+| `pnpm run fmt` | Run Oxfmt formatter |
+| `pnpm run typecheck` | Run TypeScript type checking |
### Database
-| Command | Description |
-|---------|-------------|
+| Command | Description |
+| ---------------------- | -------------------------------------------- |
| `pnpm run db:generate` | Generate migration files from schema changes |
-| `pnpm run db:migrate` | Apply pending migrations |
-| `pnpm run db:studio` | Open Drizzle Studio (database GUI) |
+| `pnpm run db:migrate` | Apply pending migrations |
+| `pnpm run db:studio` | Open Drizzle Studio (database GUI) |
### Internationalization
-| Command | Description |
-|---------|-------------|
+| Command | Description |
+| ------------------------- | -------------------------------------- |
| `pnpm run lingui:extract` | Extract translatable strings from code |
### Documentation
-| Command | Description |
-|---------|-------------|
+| Command | Description |
+| ------------------- | ------------------------------------------ |
| `pnpm run docs:dev` | Start the Mintlify docs development server |
---
@@ -210,9 +209,7 @@ This opens a web-based GUI at [https://local.drizzle.studio](https://local.drizz
pnpm run db:migrate
```
-
- Always review generated migrations before applying them, especially when working with existing data.
-
+Always review generated migrations before applying them, especially when working with existing data.
---
@@ -232,7 +229,7 @@ import { Trans } from "@lingui/react/macro";
const message = t`Hello, World!`;
// For JSX content
-Welcome to Reactive Resume
+Welcome to Reactive Resume;
```
### Extracting Translations
@@ -251,11 +248,12 @@ Translation files are located in the `locales/` directory in `.po` format.
### Linting & Formatting
-Uses [Biome](https://biomejs.dev/) for linting and formatting:
+Uses [Oxlint](https://oxlint.dev/) for linting and [Oxfmt](https://oxfmt.dev/) for formatting:
```bash
# Check and auto-fix issues
pnpm run lint
+pnpm run fmt
```
### Type Checking
@@ -267,7 +265,9 @@ pnpm run typecheck
```
- Configure your IDE to use Biome for automatic formatting on save. For VS Code, install the [Biome extension](https://marketplace.visualstudio.com/items?itemName=biomejs.biome).
+ Configure your IDE to use Oxlint for automatic linting and Oxfmt for automatic formatting on save. For VS Code,
+ install the [Oxlint extension](https://marketplace.visualstudio.com/items?itemName=oxc.oxc-vscode) and configure the
+ settings in `.vscode/settings.json`.
---
@@ -275,53 +275,27 @@ pnpm run typecheck
## Troubleshooting
-
- Another process is using port 3000. Either stop that process or start the dev server on a different port:
-
- ```bash
- PORT=3001 pnpm run dev
- ```
-
-
-
- Ensure Docker containers are running:
-
- ```bash
- docker compose -f compose.dev.yml ps
- docker compose -f compose.dev.yml up -d
- ```
-
- Check that PostgreSQL is healthy and accessible on port 5432.
-
-
-
- Verify SeaweedFS is running and the bucket exists:
-
- ```bash
- docker compose -f compose.dev.yml logs seaweedfs
- 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
- ```
-
-
-
- The route tree may need regeneration. Run the dev server which auto-generates routes:
-
- ```bash
- pnpm run dev
- ```
-
- Or run type checking to see specific errors:
-
- ```bash
- pnpm run typecheck
- ```
-
+
+ Another process is using port 3000. Either stop that process or start the dev server on a different port: ```bash
+ PORT=3001 pnpm run dev ```
+
+
+
+ Ensure Docker containers are running: ```bash docker compose -f compose.dev.yml ps docker compose -f compose.dev.yml
+ up -d ``` Check that PostgreSQL is healthy and accessible on port 5432.
+
+
+
+ Verify SeaweedFS is running and the bucket exists: ```bash docker compose -f compose.dev.yml logs seaweedfs 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 ```
+
+
+
+ The route tree may need regeneration. Run the dev server which auto-generates routes: ```bash pnpm run dev ``` Or
+ run type checking to see specific errors: ```bash pnpm run typecheck ```
+
+
---
@@ -329,18 +303,10 @@ pnpm run typecheck
## Next Steps
-
- Deep dive into the project architecture and codebase structure.
-
-
- View the source code and contribute to the project.
-
+
+ Deep dive into the project architecture and codebase structure.
+
+
+ View the source code and contribute to the project.
+
diff --git a/docs/contributing/translations.mdx b/docs/contributing/translations.mdx
index 785297016..25189f96e 100644
--- a/docs/contributing/translations.mdx
+++ b/docs/contributing/translations.mdx
@@ -12,7 +12,8 @@ Reactive Resume is used by people all around the world, and translations help ma
Reactive Resume uses [Crowdin](https://crowdin.com/) as its localization management platform. Crowdin provides a user-friendly interface where translators can contribute translations without needing to write code or work with files directly.
- The Reactive Resume Crowdin project is available at [https://crowdin.com/project/reactive-resume](https://crowdin.com/project/reactive-resume).
+ The Reactive Resume Crowdin project is available at
+ [https://crowdin.com/project/reactive-resume](https://crowdin.com/project/reactive-resume).
Once translations are submitted and approved on Crowdin, they are automatically synced to the codebase and will be available in the next release of the app.
@@ -22,33 +23,36 @@ Once translations are submitted and approved on Crowdin, they are automatically
## Getting Started
-
- If you don't already have an account, sign up at [crowdin.com](https://crowdin.com/). You can register using your email or sign up with Google, Facebook, Twitter, GitHub, or GitLab.
-
-
- For detailed instructions on creating an account and getting started, see Crowdin's official [For Translators](https://support.crowdin.com/for-translators/) documentation.
-
-
-
-
- Navigate to the [Reactive Resume project on Crowdin](https://crowdin.com/project/reactive-resume) and click **Join** to become a contributor.
-
-
-
- From the project dashboard, click on the language you want to translate. You'll see a list of files that need translation along with the progress for each.
-
-
-
- Click on a file to open the Crowdin Editor. You'll see the source text (English) on the left and a text field for your translation on the right.
-
- - Translate the text accurately while preserving any placeholders or formatting
- - Use the suggestions from Translation Memory and Machine Translation as a starting point
- - Vote on existing translations if you agree with them
-
-
-
- Your translations are saved automatically as you work. Once reviewed, they'll be included in the next app release.
-
+
+ If you don't already have an account, sign up at [crowdin.com](https://crowdin.com/). You can register using your
+ email or sign up with Google, Facebook, Twitter, GitHub, or GitLab.
+
+ For detailed instructions on creating an account and getting started, see Crowdin's official [For
+ Translators](https://support.crowdin.com/for-translators/) documentation.
+
+
+
+
+ Navigate to the [Reactive Resume project on Crowdin](https://crowdin.com/project/reactive-resume) and click **Join**
+ to become a contributor.
+
+
+
+ From the project dashboard, click on the language you want to translate. You'll see a list of files that need
+ translation along with the progress for each.
+
+
+
+ Click on a file to open the Crowdin Editor. You'll see the source text (English) on the left and a text field for
+ your translation on the right. - Translate the text accurately while preserving any placeholders or formatting - Use
+ the suggestions from Translation Memory and Machine Translation as a starting point - Vote on existing translations
+ if you agree with them
+
+
+
+ Your translations are saved automatically as you work. Once reviewed, they'll be included in the next app release.
+
+
---
@@ -90,7 +94,8 @@ Some technical terms (like "PDF", "URL", "JSON") are often kept in English acros
If your language is not listed in the Crowdin project, you can request it to be added.
- Before requesting a new language, please check if it's already available in the [Crowdin project](https://crowdin.com/project/reactive-resume).
+ Before requesting a new language, please check if it's already available in the [Crowdin
+ project](https://crowdin.com/project/reactive-resume).
To request a new language:
@@ -110,7 +115,8 @@ Once approved, the language will be added to Crowdin and you can begin translati
Translations submitted on Crowdin are synced to the codebase periodically. Once merged, they will be included in the next release of Reactive Resume.
- There may be a delay between submitting translations and seeing them live in the app. This is normal and depends on the release cycle.
+ There may be a delay between submitting translations and seeing them live in the app. This is normal and depends on
+ the release cycle.
---
@@ -118,18 +124,18 @@ Translations submitted on Crowdin are synced to the codebase periodically. Once
## Tips for Effective Translation
-
- Crowdin often shows context, screenshots, or comments to help you understand where the text appears in the app.
-
-
- Review translations by other contributors and vote for accurate ones to help maintain quality.
-
-
- Use Crowdin's comment feature to ask about unclear strings or discuss translations with other contributors.
-
-
- Check the project glossary (if available) to ensure terminology is used consistently across the app.
-
+
+ Crowdin often shows context, screenshots, or comments to help you understand where the text appears in the app.
+
+
+ Review translations by other contributors and vote for accurate ones to help maintain quality.
+
+
+ Use Crowdin's comment feature to ask about unclear strings or discuss translations with other contributors.
+
+
+ Check the project glossary (if available) to ensure terminology is used consistently across the app.
+
---
@@ -137,20 +143,12 @@ Translations submitted on Crowdin are synced to the codebase periodically. Once
## Need Help?
-
- Official Crowdin documentation for translators.
-
-
- Report issues or request new languages.
-
+
+ Official Crowdin documentation for translators.
+
+
+ Report issues or request new languages.
+
---
diff --git a/docs/getting-started/index.mdx b/docs/getting-started/index.mdx
index e5588294d..e6df85efa 100644
--- a/docs/getting-started/index.mdx
+++ b/docs/getting-started/index.mdx
@@ -4,7 +4,7 @@ description: "Welcome to the documentation for Reactive Resume, a free and open-
---
-
+
## What is Reactive Resume?
@@ -12,50 +12,55 @@ description: "Welcome to the documentation for Reactive Resume, a free and open-
**Reactive Resume** is a free and open-source resume builder designed to simplify the process of creating, updating, and sharing your professional resume. Built with privacy as a core principle, it gives you complete control over your data while providing a powerful, modern experience.
-
- Your data stays yours. No tracking, no ads, and complete transparency with our open-source codebase.
-
-
- Choose from professionally designed templates that make your resume stand out from the crowd.
-
-
- See changes instantly as you type. What you see is exactly what you'll get when you export.
-
-
- Download your resume as PDF, share it via a unique link, or print it directly from your browser.
-
+
+ Your data stays yours. No tracking, no ads, and complete transparency with our open-source codebase.
+
+
+ Choose from professionally designed templates that make your resume stand out from the crowd.
+
+
+ See changes instantly as you type. What you see is exactly what you'll get when you export.
+
+
+ Download your resume as PDF, share it via a unique link, or print it directly from your browser.
+
## Key Features
-
+
-
- Reactive Resume is licensed under MIT. You can use it for free, modify it, and even host your own instance. The entire codebase is available on [GitHub](https://github.com/amruthpillai/reactive-resume).
-
-
-
- Choose from a variety of professionally designed templates including Azurill, Bronzor, Chikorita, Ditto, Gengar, Glalie, Kakuna, Lapras, Leafish, Onyx, Pikachu, and Rhyhorn - each with unique layouts and styles.
-
-
-
- Format your content with bold, italic, links, lists, and more using our intuitive rich text editor powered by Tiptap.
-
-
-
- Reactive Resume supports multiple languages, making it accessible to users worldwide. Contribute translations to help us reach more people.
-
-
-
- Work comfortably in any lighting condition with built-in dark mode support that's easy on your eyes.
-
-
-
- Deploy your own instance of Reactive Resume using Docker. Keep complete control over your data and infrastructure.
-
+
+ Reactive Resume is licensed under MIT. You can use it for free, modify it, and even host your own instance. The
+ entire codebase is available on [GitHub](https://github.com/amruthpillai/reactive-resume).
+
+
+
+ Choose from a variety of professionally designed templates including Azurill, Bronzor, Chikorita, Ditto, Gengar,
+ Glalie, Kakuna, Lapras, Leafish, Onyx, Pikachu, and Rhyhorn - each with unique layouts and styles.
+
+
+
+ Format your content with bold, italic, links, lists, and more using our intuitive rich text editor powered by
+ Tiptap.
+
+
+
+ Reactive Resume supports multiple languages, making it accessible to users worldwide. Contribute translations to
+ help us reach more people.
+
+
+
+ Work comfortably in any lighting condition with built-in dark mode support that's easy on your eyes.
+
+
+
+ Deploy your own instance of Reactive Resume using Docker. Keep complete control over your data and infrastructure.
+
+
## Getting Started
@@ -63,55 +68,47 @@ description: "Welcome to the documentation for Reactive Resume, a free and open-
Whether you want to use our hosted version or run your own instance, Reactive Resume has got you covered.
-
- Get up and running in minutes with our hosted version or deploy your own instance.
-
-
- Set up a local development environment to contribute or customize Reactive Resume.
-
+
+ Get up and running in minutes with our hosted version or deploy your own instance.
+
+
+ Set up a local development environment to contribute or customize Reactive Resume.
+
## Tech Stack
Reactive Resume is built with modern web technologies:
-| Category | Technology |
-|-------------------|------------|
-| Framework | TanStack Start (React 19, Vite) |
-| Runtime | Node.js |
-| Language | TypeScript |
-| Database | PostgreSQL with Drizzle ORM |
-| API | ORPC (Type-safe RPC) |
-| Auth | Better Auth |
-| Styling | Tailwind CSS |
-| UI Components | Radix UI |
-| State Management | Zustand + TanStack Query |
+| Category | Technology |
+| ---------------- | ------------------------------- |
+| Framework | TanStack Start (React 19, Vite) |
+| Runtime | Node.js |
+| Language | TypeScript |
+| Database | PostgreSQL with Drizzle ORM |
+| API | ORPC (Type-safe RPC) |
+| Auth | Better Auth |
+| Styling | Tailwind CSS |
+| UI Components | Radix UI |
+| State Management | Zustand + TanStack Query |
## Community & Support
-
- Star the repo, report issues, and contribute to the project.
-
-
- Join our Reddit community to get help and connect with other users.
-
-
- Join our Discord server to get help and connect with other users.
-
-
- Support the development and long-term sustainability of Reactive Resume.
-
+
+ Star the repo, report issues, and contribute to the project.
+
+
+ Join our Reddit community to get help and connect with other users.
+
+
+ Join our Discord server to get help and connect with other users.
+
+
+ Support the development and long-term sustainability of Reactive Resume.
+
- **Need help?** Feel free to reach out via [email](mailto:hello@amruthpillai.com) or open an issue on GitHub.
+ **Need help?** Feel free to reach out via [email](mailto:hello@amruthpillai.com) or open an issue on GitHub.
diff --git a/docs/getting-started/quickstart.mdx b/docs/getting-started/quickstart.mdx
index 8d1707a6c..52fb0b10b 100644
--- a/docs/getting-started/quickstart.mdx
+++ b/docs/getting-started/quickstart.mdx
@@ -8,12 +8,12 @@ description: "Get started with Reactive Resume in minutes — use our hosted ver
Reactive Resume offers flexibility in how you want to use it. Choose the option that best fits your needs:
-
- The fastest way to get started. **Recommended for most users.**
-
-
- Deploy your own instance with complete control. **Requires some technical knowledge.**
-
+
+ The fastest way to get started. **Recommended for most users.**
+
+
+ Deploy your own instance with complete control. **Requires some technical knowledge.**
+
---
@@ -23,32 +23,27 @@ Reactive Resume offers flexibility in how you want to use it. Choose the option
The easiest way to use Reactive Resume is through our cloud version at [rxresu.me](https://rxresu.me). **This service is completely free and will always remain free.**
-
- Visit [rxresu.me](https://rxresu.me) and sign up for free using your email, or sign in with your GitHub or Google account.
-
-
-
- Click the **Create Resume** button on your dashboard. Give your resume a name and select a template to get started.
-
-
-
- Use our intuitive builder to add your:
- - Personal information
- - Work experience
- - Education
- - Skills
- - Projects
- - And more...
-
-
-
- When you're ready, export your resume as a PDF or share it via a unique public link.
-
+
+ Visit [rxresu.me](https://rxresu.me) and sign up for free using your email, or sign in with your GitHub or Google
+ account.
+
+
+
+ Click the **Create Resume** button on your dashboard. Give your resume a name and select a template to get started.
+
+
+
+ Use our intuitive builder to add your: - Personal information - Work experience - Education - Skills - Projects -
+ And more...
+
+
+
+ When you're ready, export your resume as a PDF or share it via a unique public link.
+
+
-
- Your resume updates in real-time as you type. The preview panel shows exactly how your final PDF will look.
-
+Your resume updates in real-time as you type. The preview panel shows exactly how your final PDF will look.
---
@@ -64,77 +59,50 @@ Before you begin, ensure you have the following installed:
- [Docker Compose](https://docs.docker.com/compose/install/) (v2.0 or higher)
- There is no difference in features between the cloud-hosted version and the self-hosted option. Both provide the same privacy, customization, and functionality. Choose whichever deployment type suits your needs!
+ There is no difference in features between the cloud-hosted version and the self-hosted option. Both
+ provide the same privacy, customization, and functionality. Choose whichever deployment type suits your needs!
-
### Quick Deployment
-
- ```bash
- git clone https://github.com/amruthpillai/reactive-resume.git
- cd Reactive-Resume
- ```
-
-
-
- Create a `.env` file in the root directory with the following variables:
-
- ```bash
- # Application
- APP_URL=http://localhost:3000
-
- # Database
- DATABASE_URL=postgresql://postgres:postgres@postgres:5432/postgres
-
- # Authentication (generate a secure secret)
- AUTH_SECRET=your-secure-secret-key-here
-
- # Storage (S3-compatible)
- S3_ACCESS_KEY_ID=seaweedfs
- S3_SECRET_ACCESS_KEY=seaweedfs
- S3_ENDPOINT=http://seaweedfs:8333
- S3_BUCKET=reactive-resume
- S3_FORCE_PATH_STYLE=true
- ```
-
-
- For production deployments, always use strong, unique values for `AUTH_SECRET` and database credentials.
-
-
-
-
- ```bash
- docker compose up -d
- ```
-
- This will start:
- - **PostgreSQL** — Database for storing user data and resumes
- - **SeaweedFS** — S3-compatible storage for file uploads
- - **Printer** — PDF and screenshot generation service (browserless/chromium)
- - **Reactive Resume** — The main application
-
-
-
- Once all services are running, access your Reactive Resume instance at:
-
- ```
- http://localhost:3000
- ```
-
+
+ ```bash git clone https://github.com/amruthpillai/reactive-resume.git cd Reactive-Resume ```
+
+
+
+ Create a `.env` file in the root directory with the following variables: ```bash # Application
+ APP_URL=http://localhost:3000 # Database DATABASE_URL=postgresql://postgres:postgres@postgres:5432/postgres #
+ Authentication (generate a secure secret) AUTH_SECRET=your-secure-secret-key-here # Storage (S3-compatible)
+ S3_ACCESS_KEY_ID=seaweedfs S3_SECRET_ACCESS_KEY=seaweedfs S3_ENDPOINT=http://seaweedfs:8333
+ S3_BUCKET=reactive-resume S3_FORCE_PATH_STYLE=true ```
+
+ For production deployments, always use strong, unique values for `AUTH_SECRET` and database credentials.
+
+
+
+
+ ```bash docker compose up -d ``` This will start: - **PostgreSQL** — Database for storing user data and resumes -
+ **SeaweedFS** — S3-compatible storage for file uploads - **Printer** — PDF and screenshot generation service
+ (browserless/chromium) - **Reactive Resume** — The main application
+
+
+
+ Once all services are running, access your Reactive Resume instance at: ``` http://localhost:3000 ```
+
+
### Docker Compose Services
Here's what each service in the stack does:
-| Service | Port | Description |
-|---------|------|-------------|
-| `postgres` | 5432 | PostgreSQL database for storing all application data |
-| `seaweedfs` | 8333 | S3-compatible object storage for file uploads |
-| `printer` | 4000 | Headless Chromium service for PDF and screenshot generation |
-| `app` | 3000 | The main Reactive Resume application |
+| Service | Port | Description |
+| ----------- | ---- | ----------------------------------------------------------- |
+| `postgres` | 5432 | PostgreSQL database for storing all application data |
+| `seaweedfs` | 8333 | S3-compatible object storage for file uploads |
+| `printer` | 4000 | Headless Chromium service for PDF and screenshot generation |
+| `app` | 3000 | The main Reactive Resume application |
### Health Checks
@@ -154,55 +122,61 @@ Here's a complete list of environment variables you can configure:
### Required Variables
-| Variable | Description | Example |
-|----------|-------------|---------|
-| `DATABASE_URL` | PostgreSQL connection string | `postgresql://user:pass@host:5432/db` |
-| `AUTH_SECRET` | Secret key for authentication | Generate with `openssl rand -base64 32` |
-| `APP_URL` | Public URL of your Application | `https://rxresu.me` |
-| `PRINTER_ENDPOINT` | URL of the printer service | `http://printer:3000` |
+| Variable | Description | Example |
+| ------------------ | ------------------------------ | --------------------------------------- |
+| `DATABASE_URL` | PostgreSQL connection string | `postgresql://user:pass@host:5432/db` |
+| `AUTH_SECRET` | Secret key for authentication | Generate with `openssl rand -base64 32` |
+| `APP_URL` | Public URL of your Application | `https://rxresu.me` |
+| `PRINTER_ENDPOINT` | URL of the printer service | `http://printer:3000` |
### Optional Variables
-| Variable | Description | Default |
-|----------|-------------|---------|
-| `PRINTER_APP_URL` | Public URL for the printer to access the Application | — |
-| `GOOGLE_CLIENT_ID` | Google OAuth Client ID | — |
-| `GOOGLE_CLIENT_SECRET` | Google OAuth Client Secret | — |
-| `GITHUB_CLIENT_ID` | GitHub OAuth Client ID | — |
-| `GITHUB_CLIENT_SECRET` | GitHub OAuth Client Secret | — |
-| `OAUTH_PROVIDER_NAME` | Custom OAuth Provider Name | — |
-| `OAUTH_CLIENT_ID` | Custom OAuth Client ID | — |
-| `OAUTH_CLIENT_SECRET` | Custom OAuth Client Secret | — |
-| `OAUTH_DISCOVERY_URL` | OIDC Discovery URL (use this OR manual URLs below) | — |
-| `OAUTH_AUTHORIZATION_URL` | OAuth Authorization URL (manual config) | — |
-| `OAUTH_TOKEN_URL` | OAuth Token URL (manual config) | — |
-| `OAUTH_USER_INFO_URL` | OAuth User Info URL (manual config) | — |
-| `OAUTH_SCOPES` | OAuth Scopes (space-separated) | `openid profile email` |
-| `SMTP_HOST` | SMTP Server Host (for email features) | — |
-| `SMTP_PORT` | SMTP Server Port | `587` |
-| `SMTP_USER` | SMTP Username | — |
-| `SMTP_PASS` | SMTP Password | — |
-| `SMTP_FROM` | Default FROM address for emails | — |
-| `SMTP_SECURE` | Use secure SMTP connection (`true` or `false`) | `false` |
-| `S3_ACCESS_KEY_ID` | S3 Access Key | — |
-| `S3_SECRET_ACCESS_KEY` | S3 Secret Key | — |
-| `S3_REGION` | S3 Region | `us-east-1` |
-| `S3_ENDPOINT` | S3-compatible Endpoint URL | — |
-| `S3_BUCKET` | S3 Bucket Name | — |
-| `S3_FORCE_PATH_STYLE` | Use path-style URLs for S3 (set `true` for MinIO/SeaweedFS) | `false` |
-| `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` |
+| Variable | Description | Default |
+| ------------------------------- | ----------------------------------------------------------- | ---------------------- |
+| `PRINTER_APP_URL` | Public URL for the printer to access the Application | — |
+| `GOOGLE_CLIENT_ID` | Google OAuth Client ID | — |
+| `GOOGLE_CLIENT_SECRET` | Google OAuth Client Secret | — |
+| `GITHUB_CLIENT_ID` | GitHub OAuth Client ID | — |
+| `GITHUB_CLIENT_SECRET` | GitHub OAuth Client Secret | — |
+| `OAUTH_PROVIDER_NAME` | Custom OAuth Provider Name | — |
+| `OAUTH_CLIENT_ID` | Custom OAuth Client ID | — |
+| `OAUTH_CLIENT_SECRET` | Custom OAuth Client Secret | — |
+| `OAUTH_DISCOVERY_URL` | OIDC Discovery URL (use this OR manual URLs below) | — |
+| `OAUTH_AUTHORIZATION_URL` | OAuth Authorization URL (manual config) | — |
+| `OAUTH_TOKEN_URL` | OAuth Token URL (manual config) | — |
+| `OAUTH_USER_INFO_URL` | OAuth User Info URL (manual config) | — |
+| `OAUTH_SCOPES` | OAuth Scopes (space-separated) | `openid profile email` |
+| `SMTP_HOST` | SMTP Server Host (for email features) | — |
+| `SMTP_PORT` | SMTP Server Port | `587` |
+| `SMTP_USER` | SMTP Username | — |
+| `SMTP_PASS` | SMTP Password | — |
+| `SMTP_FROM` | Default FROM address for emails | — |
+| `SMTP_SECURE` | Use secure SMTP connection (`true` or `false`) | `false` |
+| `S3_ACCESS_KEY_ID` | S3 Access Key | — |
+| `S3_SECRET_ACCESS_KEY` | S3 Secret Key | — |
+| `S3_REGION` | S3 Region | `us-east-1` |
+| `S3_ENDPOINT` | S3-compatible Endpoint URL | — |
+| `S3_BUCKET` | S3 Bucket Name | — |
+| `S3_FORCE_PATH_STYLE` | Use path-style URLs for S3 (set `true` for MinIO/SeaweedFS) | `false` |
+| `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.
- **Hybrid Setup Note**: The `PRINTER_APP_URL` variable is required when running Reactive Resume outside of Docker while the printer service is running inside Docker. In this scenario, the printer needs to reach your local app to render resumes for PDF generation. Since Docker containers cannot access `localhost` on your host machine directly, you must set `PRINTER_APP_URL` to `http://host.docker.internal:3000`. This special hostname allows Docker containers to communicate with services running on your host machine.
+ **Hybrid Setup Note**: The `PRINTER_APP_URL` variable is required when running Reactive Resume outside of Docker while
+ the printer service is running inside Docker. In this scenario, the printer needs to reach your local app to render
+ resumes for PDF generation. Since Docker containers cannot access `localhost` on your host machine directly, you must
+ set `PRINTER_APP_URL` to `http://host.docker.internal:3000`. This special hostname allows Docker containers to
+ communicate with services running on your host machine.
- **Alternative Printer Options**: If you don't want to use browserless, you can use any headless Chrome/Chromium instance with its remote debugging port open. For example, run `chromium --remote-debugging-port=9222` and point `PRINTER_ENDPOINT` to that instance.
+ **Alternative Printer Options**: If you don't want to use browserless, you can use any headless Chrome/Chromium
+ instance with its remote debugging port open. For example, run `chromium --remote-debugging-port=9222` and point
+ `PRINTER_ENDPOINT` to that instance.
---
@@ -210,22 +184,15 @@ Here's a complete list of environment variables you can configure:
## Next Steps
-
- Set up a development environment to contribute or customize Reactive Resume.
-
-
- Learn about the project structure and architecture.
-
+
+ Set up a development environment to contribute or customize Reactive Resume.
+
+
+ Learn about the project structure and architecture.
+
- **Having trouble?** Check our [GitHub Issues](https://github.com/amruthpillai/reactive-resume/issues) or reach out via [email](mailto:hello@amruthpillai.com).
+ **Having trouble?** Check our [GitHub Issues](https://github.com/amruthpillai/reactive-resume/issues) or reach out via
+ [email](mailto:hello@amruthpillai.com).
diff --git a/docs/guides/accessing-the-previous-version.mdx b/docs/guides/accessing-the-previous-version.mdx
index 5a506f3f4..c304ff95c 100644
--- a/docs/guides/accessing-the-previous-version.mdx
+++ b/docs/guides/accessing-the-previous-version.mdx
@@ -7,9 +7,7 @@ description: "Learn how to access the previous version (v4) of Reactive Resume i
If you've been using Reactive Resume for a while, you may have resumes saved in the previous version of the application. The good news is that **version 4 (v4) is still fully accessible** and will remain online for the foreseeable future.
-
- The previous version of Reactive Resume is available at [https://v4.rxresu.me](https://v4.rxresu.me).
-
+The previous version of Reactive Resume is available at [https://v4.rxresu.me](https://v4.rxresu.me).
## Why keep the old version running?
@@ -24,7 +22,8 @@ There are several reasons why v4 remains available:
The previous version will continue to run for as long as possible—until the maintainer runs out of breath or funds to keep the server active. There are no immediate plans to shut it down.
- While v4 will remain accessible for the foreseeable future, I recommend gradually transitioning to the latest version to benefit from new features, improvements, and ongoing support.
+ While v4 will remain accessible for the foreseeable future, I recommend gradually transitioning to the latest version
+ to benefit from new features, improvements, and ongoing support.
## Accessing your v4 resumes
@@ -40,6 +39,7 @@ The previous version will continue to run for as long as possible—until the ma
If you used social login (Google, GitHub, etc.) in v4, use the same method to sign in.
+
@@ -57,7 +57,8 @@ If you'd like to move your resumes to the latest version of Reactive Resume, you
4. Use the import feature to upload your JSON file (select the "Reactive Resume v4 (JSON)" option)
- Your existing resumes should already be available in the new version. If you don't see them, you can manually import them using the steps above.
+ Your existing resumes should already be available in the new version. If you don't see them, you can manually import
+ them using the steps above.
## Questions or issues?
diff --git a/docs/guides/adding-a-cover-letter.mdx b/docs/guides/adding-a-cover-letter.mdx
index d91282003..290abdcfd 100644
--- a/docs/guides/adding-a-cover-letter.mdx
+++ b/docs/guides/adding-a-cover-letter.mdx
@@ -19,20 +19,18 @@ A cover letter is a personalized document that accompanies your resume when appl
Navigate to your resume and open it in the builder.
-
- In the left sidebar, scroll down to find the **Custom Sections** area at the bottom.
-
+
+ In the left sidebar, scroll down to find the **Custom Sections** area at the bottom.
+
-
- Click Add a new custom section to open the section creation dialog.
-
+
+ Click Add a new custom section to open the section creation dialog.
+
-
- 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
-
+
+ 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
+
In your new cover letter section, click Add a new item to create your cover letter.
@@ -46,6 +44,7 @@ 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
@@ -55,20 +54,22 @@ The **Recipient** field is where you enter the recipient's information. This typ
Use line breaks to format the address block. For example:
- ```
- January 31, 2026
+```
+January 31, 2026
+
+Jane Smith
+Senior Hiring Manager
+Acme Corporation
+123 Main Street, Suite 400
+New York, NY 10001
+```
- Jane Smith
- Senior Hiring Manager
- Acme Corporation
- 123 Main Street, Suite 400
- New York, NY 10001
- ```
### 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
@@ -76,7 +77,8 @@ The **Content** field is where you write the body of your cover letter. Include:
- **Sign-off**: "Sincerely," followed by your name
- The recipient and content are both rendered directly on your resume without a section header, giving your cover letter a clean, professional appearance.
+ The recipient and content are both rendered directly on your resume without a section header, giving your cover letter
+ a clean, professional appearance.
## Using fullscreen mode for writing
@@ -88,13 +90,15 @@ For a distraction-free writing experience, use the fullscreen mode in the rich t
Click on your cover letter item in the sidebar to open the edit dialog.
-
- In either the Recipient or Content field, click the **expand icon** (arrows pointing outward) in the bottom-right corner of the editor.
-
+
+ In either the Recipient or Content field, click the **expand icon** (arrows pointing outward) in the bottom-right
+ corner of the editor.
+
-
- The editor will expand to nearly fill your screen, giving you a focused writing environment with all formatting tools available.
-
+
+ The editor will expand to nearly fill your screen, giving you a focused writing environment with all formatting tools
+ available.
+
Click the **collapse icon** or press **Escape** to return to the normal view.
@@ -126,6 +130,7 @@ Cover letters are typically on their own page, separate from the resume content.
See [Moving items between sections](/guides/moving-items-between-sections) for detailed instructions on how to move sections between pages.
+
@@ -134,25 +139,27 @@ Cover letters are typically on their own page, separate from the resume content.
- 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.
+ 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.
## Tips for effective cover letters
+**Keep it concise**: Aim for 250-400 words. Recruiters spend about one minute reading cover letters.
+
- **Keep it concise**: Aim for 250-400 words. Recruiters spend about one minute reading cover letters.
+ **Tailor each letter**: Customize your cover letter for each application. Reference specific job requirements and
+ company values.
- **Tailor each letter**: Customize your cover letter for each application. Reference specific job requirements and company values.
+ **Use the same styling**: Your cover letter will inherit the fonts and colors from your resume template, ensuring a
+ cohesive look.
- **Use the same styling**: Your cover letter will inherit the fonts and colors from your resume template, ensuring a cohesive look.
-
-
-
- **Proofread carefully**: Spelling and grammar errors can disqualify your application. Review your letter before exporting.
+ **Proofread carefully**: Spelling and grammar errors can disqualify your application. Review your letter before
+ exporting.
## Managing multiple cover letters
diff --git a/docs/guides/checking-service-status.mdx b/docs/guides/checking-service-status.mdx
index 5817fd54a..3eda3eb45 100644
--- a/docs/guides/checking-service-status.mdx
+++ b/docs/guides/checking-service-status.mdx
@@ -8,7 +8,7 @@ description: "Learn how to check the status of Reactive Resume's servers and wha
You can monitor the health and availability of Reactive Resume's servers at any time by visiting our status page:
- View real-time server metrics including uptime, CPU usage, memory usage, and more.
+ View real-time server metrics including uptime, CPU usage, memory usage, and more.
The status page provides information such as:
@@ -35,6 +35,7 @@ If you notice the servers are experiencing high load or downtime, here are some
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.
+
@@ -47,7 +48,8 @@ If you notice the servers are experiencing high load or downtime, here are some
## A Note on Server Capacity
- 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.
+ 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.
The reality is:
@@ -65,16 +67,17 @@ If you find Reactive Resume valuable and want to help keep the servers running s
Your donations directly help cover server costs, improve infrastructure, and keep Reactive Resume free for everyone.
- Support Reactive Resume's development and server costs through Open Collective. Every contribution helps keep the service running.
+ Support Reactive Resume's development and server costs through Open Collective. Every contribution helps keep the
+ service running.
-
- Make a single contribution of any amount to help with immediate server costs.
-
-
- Become a backer with a monthly contribution to provide sustainable support.
-
+
+ Make a single contribution of any amount to help with immediate server costs.
+
+
+ Become a backer with a monthly contribution to provide sustainable support.
+
---
@@ -84,7 +87,7 @@ Your donations directly help cover server costs, improve infrastructure, and kee
If you need guaranteed uptime or want to avoid shared server limitations, you can always self-host Reactive Resume on your own infrastructure.
- Learn how to deploy Reactive Resume on your own servers using Docker.
+ Learn how to deploy Reactive Resume on your own servers using Docker.
Self-hosting gives you:
diff --git a/docs/guides/choosing-a-template.mdx b/docs/guides/choosing-a-template.mdx
index 45960e4fa..b1cdf842c 100644
--- a/docs/guides/choosing-a-template.mdx
+++ b/docs/guides/choosing-a-template.mdx
@@ -16,11 +16,12 @@ Changing your resume template is simple and can be done at any time without losi
+
-
- In the resume builder, look for the right sidebar. This is where you'll find all the design and layout options.
-
+
+ In the resume builder, look for the right sidebar. This is where you'll find all the design and layout options.
+
In the right sidebar, find and click on the **Template** section to expand it.
@@ -28,6 +29,7 @@ Changing your resume template is simple and can be done at any time without losi
+
@@ -36,6 +38,7 @@ Changing your resume template is simple and can be done at any time without losi
+
@@ -48,6 +51,7 @@ Changing your resume template is simple and can be done at any time without losi
Different templates may display your content differently. Some templates work better with shorter content, while others are designed to handle more detailed information.
+
@@ -61,13 +65,15 @@ Changing your resume template is simple and can be done at any time without losi
- **Tech/Startups**: Modern, balanced templates like Chikorita or Leafish
-
- If you have a lot of experience to showcase, choose a template with efficient space usage. For shorter resumes, templates with more white space can make your content feel more substantial.
-
+
+ If you have a lot of experience to showcase, choose a template with efficient space usage. For shorter resumes,
+ templates with more white space can make your content feel more substantial.
+
-
- Your resume is part of your personal brand. Choose a template that reflects your personality while remaining professional and appropriate for your target roles.
-
+
+ Your resume is part of your personal brand. Choose a template that reflects your personality while remaining
+ professional and appropriate for your target roles.
+
Don't choose a template based on how it looks empty. Fill in your actual content and see how it flows across pages. What looks great with sample data might not work as well with your specific information.
@@ -81,7 +87,8 @@ Changing your resume template is simple and can be done at any time without losi
Reactive Resume includes a variety of professionally designed templates. Each template can be further customized with your choice of colors, fonts, and layout options.
- All templates support the same features and sections. The difference is purely in how they present your information visually.
+ All templates support the same features and sections. The difference is purely in how they present your information
+ visually.
@@ -89,49 +96,49 @@ Reactive Resume includes a variety of professionally designed templates. Each te
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
@@ -144,13 +151,11 @@ Reactive Resume includes a variety of professionally designed templates. Each te
After selecting a template, you can customize it further:
-| Setting | Description |
-|---------|-------------|
-| **Colors** | Change the primary color scheme to match your personal brand |
-| **Typography** | Choose from various Google Fonts for headings and body text |
-| **Layout** | Adjust sidebar width, section order, and page margins |
-| **Spacing** | Fine-tune gaps between sections and elements |
+| Setting | Description |
+| -------------- | ------------------------------------------------------------ |
+| **Colors** | Change the primary color scheme to match your personal brand |
+| **Typography** | Choose from various Google Fonts for headings and body text |
+| **Layout** | Adjust sidebar width, section order, and page margins |
+| **Spacing** | Fine-tune gaps between sections and elements |
-
- For more advanced customization options, check out [Using Custom CSS](/guides/using-custom-css).
-
+For more advanced customization options, check out [Using Custom CSS](/guides/using-custom-css).
diff --git a/docs/guides/creating-an-account.mdx b/docs/guides/creating-an-account.mdx
index 1e73e70ad..9b74ebee1 100644
--- a/docs/guides/creating-an-account.mdx
+++ b/docs/guides/creating-an-account.mdx
@@ -23,6 +23,7 @@ description: "Learn how to create an account on Reactive Resume and get started
Make sure to choose a username you're happy with, as it will be part of your public resume URL (e.g., `rxresu.me/your-username/resume-slug`).
+
@@ -58,12 +59,12 @@ description: "Learn how to create an account on Reactive Resume and get started
## Account Security Tips
-
- Create a password that's at least 8 characters long and includes a mix of letters, numbers, and special characters.
-
-
- Setup two-factor authentication or passkeys on your account to add an extra layer of security.
-
+
+ Create a password that's at least 8 characters long and includes a mix of letters, numbers, and special characters.
+
+
+ Setup two-factor authentication or passkeys on your account to add an extra layer of security.
+
---
@@ -71,15 +72,19 @@ description: "Learn how to create an account on Reactive Resume and get started
## Troubleshooting
-
- If you see an error that your username is already taken, try a different variation. Usernames must be unique across all users.
-
-
-
- If your email is already registered, you can use the **Forgot Password** link on the login page to reset your password and regain access to your account.
-
-
-
- Check your spam folder first. If you still don't see it, you can request a new verification email from your account settings.
-
+
+ If you see an error that your username is already taken, try a different variation. Usernames must be unique across
+ all users.
+
+
+
+ If your email is already registered, you can use the **Forgot Password** link on the login page to reset your
+ password and regain access to your account.
+
+
+
+ Check your spam folder first. If you still don't see it, you can request a new verification email from your account
+ settings.
+
+
diff --git a/docs/guides/creating-your-first-resume.mdx b/docs/guides/creating-your-first-resume.mdx
index eebc40020..aa3f6e284 100644
--- a/docs/guides/creating-your-first-resume.mdx
+++ b/docs/guides/creating-your-first-resume.mdx
@@ -10,6 +10,7 @@ description: "Learn how to create your first resume in Reactive Resume, set a na
If you haven't created an account yet, follow the guide on [Creating an Account](/guides/creating-an-account).
+
@@ -18,11 +19,12 @@ description: "Learn how to create your first resume in Reactive Resume, set a na
This is where you can create, organize, and manage multiple resumes for different roles or versions.
+
-
- In the Resumes Dashboard, click on the Create a new resume card to open the creation form.
-
+
+ In the Resumes Dashboard, click on the Create a new resume card to open the creation form.
+
Fill in the resume name. This can be a generic name (e.g., “General Resume”) or something tied to the position you’re applying for.
@@ -30,6 +32,7 @@ description: "Learn how to create your first resume in Reactive Resume, set a na
If you can’t think of a name yet, click the Magic Wand button to generate a random name for you.
+
@@ -38,6 +41,7 @@ description: "Learn how to create your first resume in Reactive Resume, set a na
If you choose to publicly share your resume, it will be accessible at `https://rxresu.me/{username}/{slug}`.
+
@@ -46,6 +50,7 @@ description: "Learn how to create your first resume in Reactive Resume, set a na
You can filter resumes by tags later from the Dashboard.
+
diff --git a/docs/guides/deleting-your-account.mdx b/docs/guides/deleting-your-account.mdx
index 153a2b901..c94b3328c 100644
--- a/docs/guides/deleting-your-account.mdx
+++ b/docs/guides/deleting-your-account.mdx
@@ -8,9 +8,10 @@ description: "Learn how to permanently delete your Reactive Resume account and a
Head over to [https://rxresu.me](https://rxresu.me) and sign in with your account credentials.
-
- Once you're in the dashboard, look for the sidebar navigation on the left. Under the Settings section, click on Danger Zone.
-
+
+ Once you're in the dashboard, look for the sidebar navigation on the left. Under the Settings section,
+ click on Danger Zone.
+
On the Danger Zone page, type delete into the confirmation input.
@@ -18,6 +19,7 @@ description: "Learn how to permanently delete your Reactive Resume account and a
The Delete Account button will stay disabled until the confirmation text matches exactly.
+
@@ -26,10 +28,10 @@ description: "Learn how to permanently delete your Reactive Resume account and a
This action cannot be undone. All your data will be permanently deleted.
+
After deletion completes, you will be signed out and redirected to the homepage.
-
diff --git a/docs/guides/exporting-your-resume.mdx b/docs/guides/exporting-your-resume.mdx
index 36ec862a8..6b37b5d3d 100644
--- a/docs/guides/exporting-your-resume.mdx
+++ b/docs/guides/exporting-your-resume.mdx
@@ -19,7 +19,8 @@ You'll see two export options:
Click the **PDF** button to generate and download a PDF version of your resume.
- The PDF is rendered server-side using your current resume data and template settings. What you see in the preview is what you'll get in the PDF, split into multiple pages if necessary.
+ The PDF is rendered server-side using your current resume data and template settings. What you see in the preview is
+ what you'll get in the PDF, split into multiple pages if necessary.
### When to use PDF exports
@@ -29,9 +30,7 @@ Click the **PDF** button to generate and download a PDF version of your resume.
- **Printing** - PDFs are print-ready and maintain exact layout on paper
- **Email attachments** - Easy to attach and share via email
-
- Before exporting, double-check your resume in the live preview.
-
+Before exporting, double-check your resume in the live preview.
## Exporting as JSON
@@ -52,9 +51,10 @@ The JSON file contains your **complete resume data**, including:
Keep local backups of your resume data. If you ever need to restore or compare versions, you can import the JSON file back into Reactive Resume.
-
- You can import a JSON export back into Reactive Resume to create a new resume based on that data. This is useful for creating variations or restoring from a backup.
-
+
+ You can import a JSON export back into Reactive Resume to create a new resume based on that data. This is useful for
+ creating variations or restoring from a backup.
+
JSON files are structured data that AI assistants can read and understand. You can share your resume JSON with an AI to get suggestions, rewrites, or entirely new content tailored to a specific job.
@@ -82,12 +82,14 @@ One of the most powerful uses for JSON exports is working with AI assistants lik
Upload the JSON file to ChatGPT, Claude, or paste its contents into the chat. You can say something like:
> "Here is my resume in JSON format. Please review it and suggest improvements for a Senior Software Engineer role at a fintech company."
+
The AI can make changes directly to the JSON structure. For example:
> "Please update my experience descriptions to be more quantitative and action-oriented. Return the modified JSON."
+
@@ -97,36 +99,1084 @@ One of the most powerful uses for JSON exports is working with AI assistants lik
4. Upload the modified JSON file
A new resume will be created with the AI's changes applied.
+
- Always review AI-generated changes before using them. AI assistants can make mistakes or suggest content that doesn't accurately reflect your experience.
+ Always review AI-generated changes before using them. AI assistants can make mistakes or suggest content that doesn't
+ accurately reflect your experience.
### Example prompts for AI assistants
Here are some prompts you can use after sharing your resume JSON:
-| Goal | Example Prompt |
-|------|----------------|
-| General review | "Review my resume and suggest 3-5 improvements to make it more impactful." |
-| Tailor to a job | "Here's a job description for [role]. Update my resume JSON to better match this position." |
+| Goal | Example Prompt |
+| --------------------- | ---------------------------------------------------------------------------------------------------------- |
+| General review | "Review my resume and suggest 3-5 improvements to make it more impactful." |
+| Tailor to a job | "Here's a job description for [role]. Update my resume JSON to better match this position." |
| Improve bullet points | "Rewrite my experience descriptions to start with strong action verbs and include metrics where possible." |
-| Add keywords | "Add relevant keywords for [industry/role] to my skills and experience sections." |
-| Shorten resume | "Condense my resume to fit on one page while keeping the most important information." |
-| Create a summary | "Write a compelling 2-3 sentence professional summary based on my experience." |
+| Add keywords | "Add relevant keywords for [industry/role] to my skills and experience sections." |
+| Shorten resume | "Condense my resume to fit on one page while keeping the most important information." |
+| Create a summary | "Write a compelling 2-3 sentence professional summary based on my experience." |
## JSON schema reference
The exported JSON follows the Reactive Resume schema. Here's a high-level overview of the structure:
```json
-{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"picture":{"type":"object","properties":{"hidden":{"type":"boolean","description":"Whether to hide the picture from the resume."},"url":{"type":"string","description":"The URL to the picture to display on the resume. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide."},"size":{"type":"number","minimum":32,"maximum":512,"description":"The size of the picture to display on the resume, defined in points (pt)."},"rotation":{"type":"number","minimum":0,"maximum":360,"description":"The rotation of the picture to display on the resume, defined in degrees (°)."},"aspectRatio":{"type":"number","minimum":0.5,"maximum":2.5,"description":"The aspect ratio of the picture to display on the resume, defined as width / height (e.g. 1.5 for 1.5:1 or 0.5 for 1:2)."},"borderRadius":{"type":"number","minimum":0,"maximum":100,"description":"The border radius of the picture to display on the resume, defined in points (pt)."},"borderColor":{"type":"string","description":"The color of the border of the picture to display on the resume, defined as rgba(r, g, b, a)."},"borderWidth":{"type":"number","minimum":0,"description":"The width of the border of the picture to display on the resume, defined in points (pt)."},"shadowColor":{"type":"string","description":"The color of the shadow of the picture to display on the resume, defined as rgba(r, g, b, a)."},"shadowWidth":{"type":"number","minimum":0,"description":"The width of the shadow of the picture to display on the resume, defined in points (pt)."}},"required":["hidden","url","size","rotation","aspectRatio","borderRadius","borderColor","borderWidth","shadowColor","shadowWidth"],"additionalProperties":false,"description":"Configuration for photograph displayed on the resume"},"basics":{"type":"object","properties":{"name":{"type":"string","description":"The full name of the author of the resume."},"headline":{"type":"string","description":"The headline of the author of the resume."},"email":{"anyOf":[{"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},{"type":"string","const":""}],"description":"The email address of the author of the resume. Leave blank to hide."},"phone":{"type":"string","description":"The phone number of the author of the resume. Leave blank to hide."},"location":{"type":"string","description":"The location of the author of the resume."},"website":{"type":"object","properties":{"url":{"type":"string","description":"The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide."},"label":{"type":"string","description":"The label to display for the URL. Leave blank to display the URL as-is."}},"required":["url","label"],"additionalProperties":false,"description":"The website of the author of the resume."},"customFields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the custom field. Usually generated as a UUID."},"icon":{"type":"string","description":"The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."},"text":{"type":"string","description":"The text to display for the custom field."}},"required":["id","icon","text"],"additionalProperties":false},"description":"The custom fields to display on the resume."}},"required":["name","headline","email","phone","location","website","customFields"],"additionalProperties":false,"description":"Basic information about the author, such as name, email, phone, location, and website"},"summary":{"type":"object","properties":{"title":{"type":"string","description":"The title of the summary of the resume."},"columns":{"type":"number","description":"The number of columns the summary should span across."},"hidden":{"type":"boolean","description":"Whether to hide the summary from the resume."},"content":{"type":"string","description":"The content of the summary of the resume. This should be a HTML-formatted string. Leave blank to hide."}},"required":["title","columns","hidden","content"],"additionalProperties":false,"description":"Summary section of the resume, useful for a short bio or introduction"},"sections":{"type":"object","properties":{"profiles":{"type":"object","properties":{"title":{"type":"string","description":"The title of the section."},"columns":{"type":"number","description":"The number of columns the section should span across."},"hidden":{"type":"boolean","description":"Whether to hide the section from the resume."},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"icon":{"type":"string","description":"The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."},"network":{"type":"string","minLength":1,"description":"The name of the network or platform."},"username":{"type":"string","description":"The username of the author on the network or platform."},"website":{"type":"object","properties":{"url":{"type":"string","description":"The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide."},"label":{"type":"string","description":"The label to display for the URL. Leave blank to display the URL as-is."}},"required":["url","label"],"additionalProperties":false,"description":"The link to the profile of the author on the network or platform, if any."}},"required":["id","hidden","icon","network","username","website"],"additionalProperties":false},"description":"The items to display in the profiles section."}},"required":["title","columns","hidden","items"],"additionalProperties":false,"description":"The section to display the profiles of the author."},"experience":{"type":"object","properties":{"title":{"type":"string","description":"The title of the section."},"columns":{"type":"number","description":"The number of columns the section should span across."},"hidden":{"type":"boolean","description":"Whether to hide the section from the resume."},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"company":{"type":"string","minLength":1,"description":"The name of the company or organization."},"position":{"type":"string","description":"The position held at the company or organization."},"location":{"type":"string","description":"The location of the company or organization."},"period":{"type":"string","description":"The period of time the author was employed at the company or organization."},"website":{"type":"object","properties":{"url":{"type":"string","description":"The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide."},"label":{"type":"string","description":"The label to display for the URL. Leave blank to display the URL as-is."}},"required":["url","label"],"additionalProperties":false,"description":"The website of the company or organization, if any."},"description":{"type":"string","description":"The description of the experience. This should be a HTML-formatted string. Leave blank to hide."}},"required":["id","hidden","company","position","location","period","website","description"],"additionalProperties":false},"description":"The items to display in the experience section."}},"required":["title","columns","hidden","items"],"additionalProperties":false,"description":"The section to display the experience of the author."},"education":{"type":"object","properties":{"title":{"type":"string","description":"The title of the section."},"columns":{"type":"number","description":"The number of columns the section should span across."},"hidden":{"type":"boolean","description":"Whether to hide the section from the resume."},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"school":{"type":"string","minLength":1,"description":"The name of the school or institution."},"degree":{"type":"string","description":"The degree or qualification obtained."},"area":{"type":"string","description":"The area of study or specialization."},"grade":{"type":"string","description":"The grade or score achieved."},"location":{"type":"string","description":"The location of the school or institution."},"period":{"type":"string","description":"The period of time the education was obtained over."},"website":{"type":"object","properties":{"url":{"type":"string","description":"The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide."},"label":{"type":"string","description":"The label to display for the URL. Leave blank to display the URL as-is."}},"required":["url","label"],"additionalProperties":false,"description":"The website of the school or institution, if any."},"description":{"type":"string","description":"The description of the education. This should be a HTML-formatted string. Leave blank to hide."}},"required":["id","hidden","school","degree","area","grade","location","period","website","description"],"additionalProperties":false},"description":"The items to display in the education section."}},"required":["title","columns","hidden","items"],"additionalProperties":false,"description":"The section to display the education of the author."},"projects":{"type":"object","properties":{"title":{"type":"string","description":"The title of the section."},"columns":{"type":"number","description":"The number of columns the section should span across."},"hidden":{"type":"boolean","description":"Whether to hide the section from the resume."},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"name":{"type":"string","minLength":1,"description":"The name of the project."},"period":{"type":"string","description":"The period of time the project was worked on."},"website":{"type":"object","properties":{"url":{"type":"string","description":"The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide."},"label":{"type":"string","description":"The label to display for the URL. Leave blank to display the URL as-is."}},"required":["url","label"],"additionalProperties":false,"description":"The link to the project, if any."},"description":{"type":"string","description":"The description of the project. This should be a HTML-formatted string. Leave blank to hide."}},"required":["id","hidden","name","period","website","description"],"additionalProperties":false},"description":"The items to display in the projects section."}},"required":["title","columns","hidden","items"],"additionalProperties":false,"description":"The section to display the projects of the author."},"skills":{"type":"object","properties":{"title":{"type":"string","description":"The title of the section."},"columns":{"type":"number","description":"The number of columns the section should span across."},"hidden":{"type":"boolean","description":"Whether to hide the section from the resume."},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"icon":{"type":"string","description":"The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."},"name":{"type":"string","minLength":1,"description":"The name of the skill."},"proficiency":{"type":"string","description":"The proficiency level of the skill. Can be any text, such as 'Beginner', 'Intermediate', 'Advanced', etc."},"level":{"default":0,"description":"The proficiency level of the skill, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden.","type":"number","minimum":0,"maximum":5},"keywords":{"default":[],"description":"The keywords associated with the skill, if any. These are displayed as tags below the name.","type":"array","items":{"type":"string"}}},"required":["id","hidden","icon","name","proficiency","level","keywords"],"additionalProperties":false},"description":"The items to display in the skills section."}},"required":["title","columns","hidden","items"],"additionalProperties":false,"description":"The section to display the skills of the author."},"languages":{"type":"object","properties":{"title":{"type":"string","description":"The title of the section."},"columns":{"type":"number","description":"The number of columns the section should span across."},"hidden":{"type":"boolean","description":"Whether to hide the section from the resume."},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"language":{"type":"string","minLength":1,"description":"The name of the language the author knows."},"fluency":{"type":"string","description":"The fluency level of the language. Can be any text, such as 'Native', 'Fluent', 'Conversational', etc. or can also be a CEFR level (A1, A2, B1, B2, C1, C2)."},"level":{"default":0,"description":"The proficiency level of the language, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden.","type":"number","minimum":0,"maximum":5}},"required":["id","hidden","language","fluency","level"],"additionalProperties":false},"description":"The items to display in the languages section."}},"required":["title","columns","hidden","items"],"additionalProperties":false,"description":"The section to display the languages of the author."},"interests":{"type":"object","properties":{"title":{"type":"string","description":"The title of the section."},"columns":{"type":"number","description":"The number of columns the section should span across."},"hidden":{"type":"boolean","description":"Whether to hide the section from the resume."},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"icon":{"type":"string","description":"The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."},"name":{"type":"string","minLength":1,"description":"The name of the interest/hobby."},"keywords":{"default":[],"description":"The keywords associated with the interest/hobby, if any. These are displayed as tags below the name.","type":"array","items":{"type":"string"}}},"required":["id","hidden","icon","name","keywords"],"additionalProperties":false},"description":"The items to display in the interests section."}},"required":["title","columns","hidden","items"],"additionalProperties":false,"description":"The section to display the interests of the author."},"awards":{"type":"object","properties":{"title":{"type":"string","description":"The title of the section."},"columns":{"type":"number","description":"The number of columns the section should span across."},"hidden":{"type":"boolean","description":"Whether to hide the section from the resume."},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"title":{"type":"string","minLength":1,"description":"The title of the award."},"awarder":{"type":"string","description":"The awarder of the award."},"date":{"type":"string","description":"The date when the award was received."},"website":{"type":"object","properties":{"url":{"type":"string","description":"The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide."},"label":{"type":"string","description":"The label to display for the URL. Leave blank to display the URL as-is."}},"required":["url","label"],"additionalProperties":false,"description":"The website of the award, if any."},"description":{"type":"string","description":"The description of the award. This should be a HTML-formatted string. Leave blank to hide."}},"required":["id","hidden","title","awarder","date","website","description"],"additionalProperties":false},"description":"The items to display in the awards section."}},"required":["title","columns","hidden","items"],"additionalProperties":false,"description":"The section to display the awards of the author."},"certifications":{"type":"object","properties":{"title":{"type":"string","description":"The title of the section."},"columns":{"type":"number","description":"The number of columns the section should span across."},"hidden":{"type":"boolean","description":"Whether to hide the section from the resume."},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"title":{"type":"string","minLength":1,"description":"The title of the certification."},"issuer":{"type":"string","description":"The issuer of the certification."},"date":{"type":"string","description":"The date when the certification was received."},"website":{"type":"object","properties":{"url":{"type":"string","description":"The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide."},"label":{"type":"string","description":"The label to display for the URL. Leave blank to display the URL as-is."}},"required":["url","label"],"additionalProperties":false,"description":"The website of the certification, if any."},"description":{"type":"string","description":"The description of the certification. This should be a HTML-formatted string. Leave blank to hide."}},"required":["id","hidden","title","issuer","date","website","description"],"additionalProperties":false},"description":"The items to display in the certifications section."}},"required":["title","columns","hidden","items"],"additionalProperties":false,"description":"The section to display the certifications of the author."},"publications":{"type":"object","properties":{"title":{"type":"string","description":"The title of the section."},"columns":{"type":"number","description":"The number of columns the section should span across."},"hidden":{"type":"boolean","description":"Whether to hide the section from the resume."},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"title":{"type":"string","minLength":1,"description":"The title of the publication."},"publisher":{"type":"string","description":"The publisher of the publication."},"date":{"type":"string","description":"The date when the publication was published."},"website":{"type":"object","properties":{"url":{"type":"string","description":"The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide."},"label":{"type":"string","description":"The label to display for the URL. Leave blank to display the URL as-is."}},"required":["url","label"],"additionalProperties":false,"description":"The link to the publication, if any."},"description":{"type":"string","description":"The description of the publication. This should be a HTML-formatted string. Leave blank to hide."}},"required":["id","hidden","title","publisher","date","website","description"],"additionalProperties":false},"description":"The items to display in the publications section."}},"required":["title","columns","hidden","items"],"additionalProperties":false,"description":"The section to display the publications of the author."},"volunteer":{"type":"object","properties":{"title":{"type":"string","description":"The title of the section."},"columns":{"type":"number","description":"The number of columns the section should span across."},"hidden":{"type":"boolean","description":"Whether to hide the section from the resume."},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"organization":{"type":"string","minLength":1,"description":"The name of the organization or company."},"location":{"type":"string","description":"The location of the organization or company."},"period":{"type":"string","description":"The period of time the author was volunteered at the organization or company."},"website":{"type":"object","properties":{"url":{"type":"string","description":"The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide."},"label":{"type":"string","description":"The label to display for the URL. Leave blank to display the URL as-is."}},"required":["url","label"],"additionalProperties":false,"description":"The link to the organization or company, if any."},"description":{"type":"string","description":"The description of the volunteer experience. This should be a HTML-formatted string. Leave blank to hide."}},"required":["id","hidden","organization","location","period","website","description"],"additionalProperties":false},"description":"The items to display in the volunteer section."}},"required":["title","columns","hidden","items"],"additionalProperties":false,"description":"The section to display the volunteer experience of the author."},"references":{"type":"object","properties":{"title":{"type":"string","description":"The title of the section."},"columns":{"type":"number","description":"The number of columns the section should span across."},"hidden":{"type":"boolean","description":"Whether to hide the section from the resume."},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"name":{"type":"string","minLength":1,"description":"The name of the reference, or a note such as 'Available upon request'."},"description":{"type":"string","description":"The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string. Leave blank to hide."}},"required":["id","hidden","name","description"],"additionalProperties":false},"description":"The items to display in the references section."}},"required":["title","columns","hidden","items"],"additionalProperties":false,"description":"The section to display the references of the author."}},"required":["profiles","experience","education","projects","skills","languages","interests","awards","certifications","publications","volunteer","references"],"additionalProperties":false,"description":"Various sections of the resume, such as experience, education, projects, etc."},"customSections":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string","description":"The title of the section."},"columns":{"type":"number","description":"The number of columns the section should span across."},"hidden":{"type":"boolean","description":"Whether to hide the section from the resume."},"id":{"type":"string","description":"The unique identifier for the custom section. Usually generated as a UUID."},"content":{"type":"string","description":"The content of the custom section. This should be a HTML-formatted string. Leave blank to hide."}},"required":["title","columns","hidden","id","content"],"additionalProperties":false},"description":"Custom sections of the resume, such as a custom section for notes, etc."},"metadata":{"type":"object","properties":{"template":{"default":"onyx","description":"The template to use for the resume. Determines the overall design and appearance of the resume.","type":"string","enum":["azurill","bronzor","chikorita","ditto","gengar","glalie","kakuna","lapras","leafish","onyx","pikachu","rhyhorn"]},"layout":{"type":"object","properties":{"sidebarWidth":{"default":35,"description":"The width of the sidebar column, defined as a percentage of the page width.","type":"number","minimum":10,"maximum":50},"pages":{"type":"array","items":{"type":"object","properties":{"fullWidth":{"type":"boolean","description":"Whether the layout of the page should be full width. If true, the main column will span the entire width of the page. This means that there should be no items in the sidebar column."},"main":{"type":"array","items":{"type":"string"},"description":"The items to display in the main column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)."},"sidebar":{"type":"array","items":{"type":"string"},"description":"The items to display in the sidebar column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)."}},"required":["fullWidth","main","sidebar"],"additionalProperties":false},"description":"The pages to display in the layout."}},"required":["sidebarWidth","pages"],"additionalProperties":false,"description":"The layout of the resume. Determines the structure and arrangement of the sections on the resume."},"css":{"type":"object","properties":{"enabled":{"type":"boolean","description":"Whether to enable custom CSS for the resume."},"value":{"type":"string","description":"The custom CSS to apply to the resume. This should be a valid CSS string."}},"required":["enabled","value"],"additionalProperties":false,"description":"Custom CSS to apply to the resume. Can be used to override the default styles of the template."},"page":{"type":"object","properties":{"gapX":{"type":"number","minimum":0,"description":"The horizontal gap between the sections of the page, defined in points (pt)."},"gapY":{"type":"number","minimum":0,"description":"The vertical gap between the sections of the page, defined in points (pt)."},"marginX":{"type":"number","minimum":0,"description":"The horizontal margin of the page, defined in points (pt)."},"marginY":{"type":"number","minimum":0,"description":"The vertical margin of the page, defined in points (pt)."},"format":{"type":"string","enum":["a4","letter"],"description":"The format of the page. Can be 'a4' or 'letter'."},"locale":{"default":"en-US","anyOf":[{"type":"string","const":"en-US"},{"type":"string","const":"de-DE"},{"type":"string","const":"zu-ZA"}],"description":"The language used in the resume, used for displaying pre-translated section headings, if not overridden."}},"required":["gapX","gapY","marginX","marginY","format","locale"],"additionalProperties":false,"description":"The page settings of the resume. Determines the margins, format, and locale of the resume."},"design":{"type":"object","properties":{"level":{"type":"object","properties":{"icon":{"type":"string","description":"The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."},"type":{"type":"string","enum":["hidden","circle","square","rectangle","rectangle-full","progress-bar","icon"],"description":"The type of the level design. 'hidden' will hide the level design, 'circle' will display a circle, 'square' will display a square, 'rectangle' will display a rectangle, 'rectangle-full' will display a full rectangle, 'progress-bar' will display a progress bar, and 'icon' will display an icon. If 'icon' is selected, the icon to display should be specified in the 'icon' field."}},"required":["icon","type"],"additionalProperties":false},"colors":{"type":"object","properties":{"primary":{"type":"string","description":"The primary color of the design, defined as rgba(r, g, b, a)."},"text":{"type":"string","description":"The text color of the design, defined as rgba(r, g, b, a). Usually set to black: rgba(0, 0, 0, 1)."},"background":{"type":"string","description":"The background color of the design, defined as rgba(r, g, b, a). Usually set to white: rgba(255, 255, 255, 1)."}},"required":["primary","text","background"],"additionalProperties":false}},"required":["level","colors"],"additionalProperties":false,"description":"The design settings of the resume. Determines the colors, level designs, and typography of the resume."},"typography":{"type":"object","properties":{"body":{"type":"object","properties":{"fontFamily":{"type":"string","description":"The family of the font to use. Must be a font that is available on Google Fonts."},"fontWeights":{"default":["400"],"description":"The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font.","type":"array","items":{"type":"string","enum":["100","200","300","400","500","600","700","800","900"]}},"fontSize":{"default":11,"description":"The size of the font to use, defined in points (pt).","type":"number","minimum":6,"maximum":24},"lineHeight":{"default":1.5,"description":"The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x).","type":"number","minimum":0.5,"maximum":4}},"required":["fontFamily","fontWeights","fontSize","lineHeight"],"additionalProperties":false,"description":"The typography for the body of the resume."},"heading":{"type":"object","properties":{"fontFamily":{"type":"string","description":"The family of the font to use. Must be a font that is available on Google Fonts."},"fontWeights":{"default":["400"],"description":"The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font.","type":"array","items":{"type":"string","enum":["100","200","300","400","500","600","700","800","900"]}},"fontSize":{"default":11,"description":"The size of the font to use, defined in points (pt).","type":"number","minimum":6,"maximum":24},"lineHeight":{"default":1.5,"description":"The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x).","type":"number","minimum":0.5,"maximum":4}},"required":["fontFamily","fontWeights","fontSize","lineHeight"],"additionalProperties":false,"description":"The typography for the headings of the resume."}},"required":["body","heading"],"additionalProperties":false,"description":"The typography settings of the resume. Determines the fonts and sizes of the body and headings of the resume."},"notes":{"type":"string","description":"Personal notes for the resume. Can be used to add any additional information or instructions for the resume. These notes are not displayed on the resume, they are only visible to the author of the resume when editing the resume. This should be a HTML-formatted string."}},"required":["template","layout","css","page","design","typography","notes"],"additionalProperties":false,"description":"Metadata for the resume, such as template, layout, typography, etc. This section describes the overall design and appearance of the resume."},"version":{"type":"string","const":"5.0.0","description":"The version of the Reactive Resume JSON Schema"},"$schema":{"type":"string","const":"http://localhost:3000/schema.json","description":"The URL of the Reactive Resume JSON Schema, used for validation and documentation purposes."}},"required":["picture","basics","summary","sections","customSections","metadata","version","$schema"],"additionalProperties":false}
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "type": "object",
+ "properties": {
+ "picture": {
+ "type": "object",
+ "properties": {
+ "hidden": { "type": "boolean", "description": "Whether to hide the picture from the resume." },
+ "url": {
+ "type": "string",
+ "description": "The URL to the picture to display on the resume. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide."
+ },
+ "size": {
+ "type": "number",
+ "minimum": 32,
+ "maximum": 512,
+ "description": "The size of the picture to display on the resume, defined in points (pt)."
+ },
+ "rotation": {
+ "type": "number",
+ "minimum": 0,
+ "maximum": 360,
+ "description": "The rotation of the picture to display on the resume, defined in degrees (°)."
+ },
+ "aspectRatio": {
+ "type": "number",
+ "minimum": 0.5,
+ "maximum": 2.5,
+ "description": "The aspect ratio of the picture to display on the resume, defined as width / height (e.g. 1.5 for 1.5:1 or 0.5 for 1:2)."
+ },
+ "borderRadius": {
+ "type": "number",
+ "minimum": 0,
+ "maximum": 100,
+ "description": "The border radius of the picture to display on the resume, defined in points (pt)."
+ },
+ "borderColor": {
+ "type": "string",
+ "description": "The color of the border of the picture to display on the resume, defined as rgba(r, g, b, a)."
+ },
+ "borderWidth": {
+ "type": "number",
+ "minimum": 0,
+ "description": "The width of the border of the picture to display on the resume, defined in points (pt)."
+ },
+ "shadowColor": {
+ "type": "string",
+ "description": "The color of the shadow of the picture to display on the resume, defined as rgba(r, g, b, a)."
+ },
+ "shadowWidth": {
+ "type": "number",
+ "minimum": 0,
+ "description": "The width of the shadow of the picture to display on the resume, defined in points (pt)."
+ }
+ },
+ "required": [
+ "hidden",
+ "url",
+ "size",
+ "rotation",
+ "aspectRatio",
+ "borderRadius",
+ "borderColor",
+ "borderWidth",
+ "shadowColor",
+ "shadowWidth"
+ ],
+ "additionalProperties": false,
+ "description": "Configuration for photograph displayed on the resume"
+ },
+ "basics": {
+ "type": "object",
+ "properties": {
+ "name": { "type": "string", "description": "The full name of the author of the resume." },
+ "headline": { "type": "string", "description": "The headline of the author of the resume." },
+ "email": {
+ "anyOf": [
+ {
+ "type": "string",
+ "format": "email",
+ "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
+ },
+ { "type": "string", "const": "" }
+ ],
+ "description": "The email address of the author of the resume. Leave blank to hide."
+ },
+ "phone": {
+ "type": "string",
+ "description": "The phone number of the author of the resume. Leave blank to hide."
+ },
+ "location": { "type": "string", "description": "The location of the author of the resume." },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "additionalProperties": false,
+ "description": "The website of the author of the resume."
+ },
+ "customFields": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the custom field. Usually generated as a UUID."
+ },
+ "icon": {
+ "type": "string",
+ "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
+ },
+ "text": { "type": "string", "description": "The text to display for the custom field." }
+ },
+ "required": ["id", "icon", "text"],
+ "additionalProperties": false
+ },
+ "description": "The custom fields to display on the resume."
+ }
+ },
+ "required": ["name", "headline", "email", "phone", "location", "website", "customFields"],
+ "additionalProperties": false,
+ "description": "Basic information about the author, such as name, email, phone, location, and website"
+ },
+ "summary": {
+ "type": "object",
+ "properties": {
+ "title": { "type": "string", "description": "The title of the summary of the resume." },
+ "columns": { "type": "number", "description": "The number of columns the summary should span across." },
+ "hidden": { "type": "boolean", "description": "Whether to hide the summary from the resume." },
+ "content": {
+ "type": "string",
+ "description": "The content of the summary of the resume. This should be a HTML-formatted string. Leave blank to hide."
+ }
+ },
+ "required": ["title", "columns", "hidden", "content"],
+ "additionalProperties": false,
+ "description": "Summary section of the resume, useful for a short bio or introduction"
+ },
+ "sections": {
+ "type": "object",
+ "properties": {
+ "profiles": {
+ "type": "object",
+ "properties": {
+ "title": { "type": "string", "description": "The title of the section." },
+ "columns": { "type": "number", "description": "The number of columns the section should span across." },
+ "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "icon": {
+ "type": "string",
+ "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
+ },
+ "network": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the network or platform."
+ },
+ "username": {
+ "type": "string",
+ "description": "The username of the author on the network or platform."
+ },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "additionalProperties": false,
+ "description": "The link to the profile of the author on the network or platform, if any."
+ }
+ },
+ "required": ["id", "hidden", "icon", "network", "username", "website"],
+ "additionalProperties": false
+ },
+ "description": "The items to display in the profiles section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "additionalProperties": false,
+ "description": "The section to display the profiles of the author."
+ },
+ "experience": {
+ "type": "object",
+ "properties": {
+ "title": { "type": "string", "description": "The title of the section." },
+ "columns": { "type": "number", "description": "The number of columns the section should span across." },
+ "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "company": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the company or organization."
+ },
+ "position": { "type": "string", "description": "The position held at the company or organization." },
+ "location": { "type": "string", "description": "The location of the company or organization." },
+ "period": {
+ "type": "string",
+ "description": "The period of time the author was employed at the company or organization."
+ },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "additionalProperties": false,
+ "description": "The website of the company or organization, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the experience. This should be a HTML-formatted string. Leave blank to hide."
+ }
+ },
+ "required": ["id", "hidden", "company", "position", "location", "period", "website", "description"],
+ "additionalProperties": false
+ },
+ "description": "The items to display in the experience section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "additionalProperties": false,
+ "description": "The section to display the experience of the author."
+ },
+ "education": {
+ "type": "object",
+ "properties": {
+ "title": { "type": "string", "description": "The title of the section." },
+ "columns": { "type": "number", "description": "The number of columns the section should span across." },
+ "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "school": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the school or institution."
+ },
+ "degree": { "type": "string", "description": "The degree or qualification obtained." },
+ "area": { "type": "string", "description": "The area of study or specialization." },
+ "grade": { "type": "string", "description": "The grade or score achieved." },
+ "location": { "type": "string", "description": "The location of the school or institution." },
+ "period": { "type": "string", "description": "The period of time the education was obtained over." },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "additionalProperties": false,
+ "description": "The website of the school or institution, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the education. This should be a HTML-formatted string. Leave blank to hide."
+ }
+ },
+ "required": [
+ "id",
+ "hidden",
+ "school",
+ "degree",
+ "area",
+ "grade",
+ "location",
+ "period",
+ "website",
+ "description"
+ ],
+ "additionalProperties": false
+ },
+ "description": "The items to display in the education section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "additionalProperties": false,
+ "description": "The section to display the education of the author."
+ },
+ "projects": {
+ "type": "object",
+ "properties": {
+ "title": { "type": "string", "description": "The title of the section." },
+ "columns": { "type": "number", "description": "The number of columns the section should span across." },
+ "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "name": { "type": "string", "minLength": 1, "description": "The name of the project." },
+ "period": { "type": "string", "description": "The period of time the project was worked on." },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "additionalProperties": false,
+ "description": "The link to the project, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the project. This should be a HTML-formatted string. Leave blank to hide."
+ }
+ },
+ "required": ["id", "hidden", "name", "period", "website", "description"],
+ "additionalProperties": false
+ },
+ "description": "The items to display in the projects section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "additionalProperties": false,
+ "description": "The section to display the projects of the author."
+ },
+ "skills": {
+ "type": "object",
+ "properties": {
+ "title": { "type": "string", "description": "The title of the section." },
+ "columns": { "type": "number", "description": "The number of columns the section should span across." },
+ "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "icon": {
+ "type": "string",
+ "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
+ },
+ "name": { "type": "string", "minLength": 1, "description": "The name of the skill." },
+ "proficiency": {
+ "type": "string",
+ "description": "The proficiency level of the skill. Can be any text, such as 'Beginner', 'Intermediate', 'Advanced', etc."
+ },
+ "level": {
+ "default": 0,
+ "description": "The proficiency level of the skill, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden.",
+ "type": "number",
+ "minimum": 0,
+ "maximum": 5
+ },
+ "keywords": {
+ "default": [],
+ "description": "The keywords associated with the skill, if any. These are displayed as tags below the name.",
+ "type": "array",
+ "items": { "type": "string" }
+ }
+ },
+ "required": ["id", "hidden", "icon", "name", "proficiency", "level", "keywords"],
+ "additionalProperties": false
+ },
+ "description": "The items to display in the skills section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "additionalProperties": false,
+ "description": "The section to display the skills of the author."
+ },
+ "languages": {
+ "type": "object",
+ "properties": {
+ "title": { "type": "string", "description": "The title of the section." },
+ "columns": { "type": "number", "description": "The number of columns the section should span across." },
+ "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "language": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the language the author knows."
+ },
+ "fluency": {
+ "type": "string",
+ "description": "The fluency level of the language. Can be any text, such as 'Native', 'Fluent', 'Conversational', etc. or can also be a CEFR level (A1, A2, B1, B2, C1, C2)."
+ },
+ "level": {
+ "default": 0,
+ "description": "The proficiency level of the language, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden.",
+ "type": "number",
+ "minimum": 0,
+ "maximum": 5
+ }
+ },
+ "required": ["id", "hidden", "language", "fluency", "level"],
+ "additionalProperties": false
+ },
+ "description": "The items to display in the languages section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "additionalProperties": false,
+ "description": "The section to display the languages of the author."
+ },
+ "interests": {
+ "type": "object",
+ "properties": {
+ "title": { "type": "string", "description": "The title of the section." },
+ "columns": { "type": "number", "description": "The number of columns the section should span across." },
+ "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "icon": {
+ "type": "string",
+ "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
+ },
+ "name": { "type": "string", "minLength": 1, "description": "The name of the interest/hobby." },
+ "keywords": {
+ "default": [],
+ "description": "The keywords associated with the interest/hobby, if any. These are displayed as tags below the name.",
+ "type": "array",
+ "items": { "type": "string" }
+ }
+ },
+ "required": ["id", "hidden", "icon", "name", "keywords"],
+ "additionalProperties": false
+ },
+ "description": "The items to display in the interests section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "additionalProperties": false,
+ "description": "The section to display the interests of the author."
+ },
+ "awards": {
+ "type": "object",
+ "properties": {
+ "title": { "type": "string", "description": "The title of the section." },
+ "columns": { "type": "number", "description": "The number of columns the section should span across." },
+ "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "title": { "type": "string", "minLength": 1, "description": "The title of the award." },
+ "awarder": { "type": "string", "description": "The awarder of the award." },
+ "date": { "type": "string", "description": "The date when the award was received." },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "additionalProperties": false,
+ "description": "The website of the award, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the award. This should be a HTML-formatted string. Leave blank to hide."
+ }
+ },
+ "required": ["id", "hidden", "title", "awarder", "date", "website", "description"],
+ "additionalProperties": false
+ },
+ "description": "The items to display in the awards section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "additionalProperties": false,
+ "description": "The section to display the awards of the author."
+ },
+ "certifications": {
+ "type": "object",
+ "properties": {
+ "title": { "type": "string", "description": "The title of the section." },
+ "columns": { "type": "number", "description": "The number of columns the section should span across." },
+ "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "title": { "type": "string", "minLength": 1, "description": "The title of the certification." },
+ "issuer": { "type": "string", "description": "The issuer of the certification." },
+ "date": { "type": "string", "description": "The date when the certification was received." },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "additionalProperties": false,
+ "description": "The website of the certification, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the certification. This should be a HTML-formatted string. Leave blank to hide."
+ }
+ },
+ "required": ["id", "hidden", "title", "issuer", "date", "website", "description"],
+ "additionalProperties": false
+ },
+ "description": "The items to display in the certifications section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "additionalProperties": false,
+ "description": "The section to display the certifications of the author."
+ },
+ "publications": {
+ "type": "object",
+ "properties": {
+ "title": { "type": "string", "description": "The title of the section." },
+ "columns": { "type": "number", "description": "The number of columns the section should span across." },
+ "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "title": { "type": "string", "minLength": 1, "description": "The title of the publication." },
+ "publisher": { "type": "string", "description": "The publisher of the publication." },
+ "date": { "type": "string", "description": "The date when the publication was published." },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "additionalProperties": false,
+ "description": "The link to the publication, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the publication. This should be a HTML-formatted string. Leave blank to hide."
+ }
+ },
+ "required": ["id", "hidden", "title", "publisher", "date", "website", "description"],
+ "additionalProperties": false
+ },
+ "description": "The items to display in the publications section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "additionalProperties": false,
+ "description": "The section to display the publications of the author."
+ },
+ "volunteer": {
+ "type": "object",
+ "properties": {
+ "title": { "type": "string", "description": "The title of the section." },
+ "columns": { "type": "number", "description": "The number of columns the section should span across." },
+ "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "organization": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the organization or company."
+ },
+ "location": { "type": "string", "description": "The location of the organization or company." },
+ "period": {
+ "type": "string",
+ "description": "The period of time the author was volunteered at the organization or company."
+ },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "additionalProperties": false,
+ "description": "The link to the organization or company, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the volunteer experience. This should be a HTML-formatted string. Leave blank to hide."
+ }
+ },
+ "required": ["id", "hidden", "organization", "location", "period", "website", "description"],
+ "additionalProperties": false
+ },
+ "description": "The items to display in the volunteer section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "additionalProperties": false,
+ "description": "The section to display the volunteer experience of the author."
+ },
+ "references": {
+ "type": "object",
+ "properties": {
+ "title": { "type": "string", "description": "The title of the section." },
+ "columns": { "type": "number", "description": "The number of columns the section should span across." },
+ "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the reference, or a note such as 'Available upon request'."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string. Leave blank to hide."
+ }
+ },
+ "required": ["id", "hidden", "name", "description"],
+ "additionalProperties": false
+ },
+ "description": "The items to display in the references section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "additionalProperties": false,
+ "description": "The section to display the references of the author."
+ }
+ },
+ "required": [
+ "profiles",
+ "experience",
+ "education",
+ "projects",
+ "skills",
+ "languages",
+ "interests",
+ "awards",
+ "certifications",
+ "publications",
+ "volunteer",
+ "references"
+ ],
+ "additionalProperties": false,
+ "description": "Various sections of the resume, such as experience, education, projects, etc."
+ },
+ "customSections": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "title": { "type": "string", "description": "The title of the section." },
+ "columns": { "type": "number", "description": "The number of columns the section should span across." },
+ "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." },
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the custom section. Usually generated as a UUID."
+ },
+ "content": {
+ "type": "string",
+ "description": "The content of the custom section. This should be a HTML-formatted string. Leave blank to hide."
+ }
+ },
+ "required": ["title", "columns", "hidden", "id", "content"],
+ "additionalProperties": false
+ },
+ "description": "Custom sections of the resume, such as a custom section for notes, etc."
+ },
+ "metadata": {
+ "type": "object",
+ "properties": {
+ "template": {
+ "default": "onyx",
+ "description": "The template to use for the resume. Determines the overall design and appearance of the resume.",
+ "type": "string",
+ "enum": [
+ "azurill",
+ "bronzor",
+ "chikorita",
+ "ditto",
+ "gengar",
+ "glalie",
+ "kakuna",
+ "lapras",
+ "leafish",
+ "onyx",
+ "pikachu",
+ "rhyhorn"
+ ]
+ },
+ "layout": {
+ "type": "object",
+ "properties": {
+ "sidebarWidth": {
+ "default": 35,
+ "description": "The width of the sidebar column, defined as a percentage of the page width.",
+ "type": "number",
+ "minimum": 10,
+ "maximum": 50
+ },
+ "pages": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "fullWidth": {
+ "type": "boolean",
+ "description": "Whether the layout of the page should be full width. If true, the main column will span the entire width of the page. This means that there should be no items in the sidebar column."
+ },
+ "main": {
+ "type": "array",
+ "items": { "type": "string" },
+ "description": "The items to display in the main column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)."
+ },
+ "sidebar": {
+ "type": "array",
+ "items": { "type": "string" },
+ "description": "The items to display in the sidebar column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)."
+ }
+ },
+ "required": ["fullWidth", "main", "sidebar"],
+ "additionalProperties": false
+ },
+ "description": "The pages to display in the layout."
+ }
+ },
+ "required": ["sidebarWidth", "pages"],
+ "additionalProperties": false,
+ "description": "The layout of the resume. Determines the structure and arrangement of the sections on the resume."
+ },
+ "css": {
+ "type": "object",
+ "properties": {
+ "enabled": { "type": "boolean", "description": "Whether to enable custom CSS for the resume." },
+ "value": {
+ "type": "string",
+ "description": "The custom CSS to apply to the resume. This should be a valid CSS string."
+ }
+ },
+ "required": ["enabled", "value"],
+ "additionalProperties": false,
+ "description": "Custom CSS to apply to the resume. Can be used to override the default styles of the template."
+ },
+ "page": {
+ "type": "object",
+ "properties": {
+ "gapX": {
+ "type": "number",
+ "minimum": 0,
+ "description": "The horizontal gap between the sections of the page, defined in points (pt)."
+ },
+ "gapY": {
+ "type": "number",
+ "minimum": 0,
+ "description": "The vertical gap between the sections of the page, defined in points (pt)."
+ },
+ "marginX": {
+ "type": "number",
+ "minimum": 0,
+ "description": "The horizontal margin of the page, defined in points (pt)."
+ },
+ "marginY": {
+ "type": "number",
+ "minimum": 0,
+ "description": "The vertical margin of the page, defined in points (pt)."
+ },
+ "format": {
+ "type": "string",
+ "enum": ["a4", "letter"],
+ "description": "The format of the page. Can be 'a4' or 'letter'."
+ },
+ "locale": {
+ "default": "en-US",
+ "anyOf": [
+ { "type": "string", "const": "en-US" },
+ { "type": "string", "const": "de-DE" },
+ { "type": "string", "const": "zu-ZA" }
+ ],
+ "description": "The language used in the resume, used for displaying pre-translated section headings, if not overridden."
+ }
+ },
+ "required": ["gapX", "gapY", "marginX", "marginY", "format", "locale"],
+ "additionalProperties": false,
+ "description": "The page settings of the resume. Determines the margins, format, and locale of the resume."
+ },
+ "design": {
+ "type": "object",
+ "properties": {
+ "level": {
+ "type": "object",
+ "properties": {
+ "icon": {
+ "type": "string",
+ "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
+ },
+ "type": {
+ "type": "string",
+ "enum": ["hidden", "circle", "square", "rectangle", "rectangle-full", "progress-bar", "icon"],
+ "description": "The type of the level design. 'hidden' will hide the level design, 'circle' will display a circle, 'square' will display a square, 'rectangle' will display a rectangle, 'rectangle-full' will display a full rectangle, 'progress-bar' will display a progress bar, and 'icon' will display an icon. If 'icon' is selected, the icon to display should be specified in the 'icon' field."
+ }
+ },
+ "required": ["icon", "type"],
+ "additionalProperties": false
+ },
+ "colors": {
+ "type": "object",
+ "properties": {
+ "primary": {
+ "type": "string",
+ "description": "The primary color of the design, defined as rgba(r, g, b, a)."
+ },
+ "text": {
+ "type": "string",
+ "description": "The text color of the design, defined as rgba(r, g, b, a). Usually set to black: rgba(0, 0, 0, 1)."
+ },
+ "background": {
+ "type": "string",
+ "description": "The background color of the design, defined as rgba(r, g, b, a). Usually set to white: rgba(255, 255, 255, 1)."
+ }
+ },
+ "required": ["primary", "text", "background"],
+ "additionalProperties": false
+ }
+ },
+ "required": ["level", "colors"],
+ "additionalProperties": false,
+ "description": "The design settings of the resume. Determines the colors, level designs, and typography of the resume."
+ },
+ "typography": {
+ "type": "object",
+ "properties": {
+ "body": {
+ "type": "object",
+ "properties": {
+ "fontFamily": {
+ "type": "string",
+ "description": "The family of the font to use. Must be a font that is available on Google Fonts."
+ },
+ "fontWeights": {
+ "default": ["400"],
+ "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font.",
+ "type": "array",
+ "items": { "type": "string", "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }
+ },
+ "fontSize": {
+ "default": 11,
+ "description": "The size of the font to use, defined in points (pt).",
+ "type": "number",
+ "minimum": 6,
+ "maximum": 24
+ },
+ "lineHeight": {
+ "default": 1.5,
+ "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x).",
+ "type": "number",
+ "minimum": 0.5,
+ "maximum": 4
+ }
+ },
+ "required": ["fontFamily", "fontWeights", "fontSize", "lineHeight"],
+ "additionalProperties": false,
+ "description": "The typography for the body of the resume."
+ },
+ "heading": {
+ "type": "object",
+ "properties": {
+ "fontFamily": {
+ "type": "string",
+ "description": "The family of the font to use. Must be a font that is available on Google Fonts."
+ },
+ "fontWeights": {
+ "default": ["400"],
+ "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font.",
+ "type": "array",
+ "items": { "type": "string", "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }
+ },
+ "fontSize": {
+ "default": 11,
+ "description": "The size of the font to use, defined in points (pt).",
+ "type": "number",
+ "minimum": 6,
+ "maximum": 24
+ },
+ "lineHeight": {
+ "default": 1.5,
+ "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x).",
+ "type": "number",
+ "minimum": 0.5,
+ "maximum": 4
+ }
+ },
+ "required": ["fontFamily", "fontWeights", "fontSize", "lineHeight"],
+ "additionalProperties": false,
+ "description": "The typography for the headings of the resume."
+ }
+ },
+ "required": ["body", "heading"],
+ "additionalProperties": false,
+ "description": "The typography settings of the resume. Determines the fonts and sizes of the body and headings of the resume."
+ },
+ "notes": {
+ "type": "string",
+ "description": "Personal notes for the resume. Can be used to add any additional information or instructions for the resume. These notes are not displayed on the resume, they are only visible to the author of the resume when editing the resume. This should be a HTML-formatted string."
+ }
+ },
+ "required": ["template", "layout", "css", "page", "design", "typography", "notes"],
+ "additionalProperties": false,
+ "description": "Metadata for the resume, such as template, layout, typography, etc. This section describes the overall design and appearance of the resume."
+ },
+ "version": { "type": "string", "const": "5.0.0", "description": "The version of the Reactive Resume JSON Schema" },
+ "$schema": {
+ "type": "string",
+ "const": "http://localhost:3000/schema.json",
+ "description": "The URL of the Reactive Resume JSON Schema, used for validation and documentation purposes."
+ }
+ },
+ "required": ["picture", "basics", "summary", "sections", "customSections", "metadata", "version", "$schema"],
+ "additionalProperties": false
+}
```
- You can access the complete JSON schema at [`https://rxresu.me/schema.json`](https://rxresu.me/schema.json). Referencing this is helpful for validating your resume JSON or providing a detailed schema reference when collaborating with AI assistants.
+ You can access the complete JSON schema at [`https://rxresu.me/schema.json`](https://rxresu.me/schema.json).
+ Referencing this is helpful for validating your resume JSON or providing a detailed schema reference when
+ collaborating with AI assistants.
## Re-importing your JSON
@@ -138,23 +1188,17 @@ To import a JSON file back into Reactive Resume:
Navigate to your resumes dashboard.
-
- Find the import card and click it to open the import dialog.
-
+Find the import card and click it to open the import dialog.
-
- In the format options, select **Reactive Resume (JSON)** if your file was exported from the current version.
-
+
+ In the format options, select **Reactive Resume (JSON)** if your file was exported from the current version.
+
-
- Click the upload area and select your JSON file.
-
+Click the upload area and select your JSON file.
Click **Import** to create a new resume from the file.
-
- Importing a JSON file creates a **new resume**. Your existing resumes are not affected.
-
+Importing a JSON file creates a **new resume**. Your existing resumes are not affected.
diff --git a/docs/guides/fitting-content-on-a-page.mdx b/docs/guides/fitting-content-on-a-page.mdx
index 038cd4282..18f22642e 100644
--- a/docs/guides/fitting-content-on-a-page.mdx
+++ b/docs/guides/fitting-content-on-a-page.mdx
@@ -6,11 +6,16 @@ description: "Learn how to reorganize and format your resume content to fit with
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.
-
+
- 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.
+ 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.
## Quick fixes
@@ -31,7 +36,8 @@ With Free-Form:
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).
- Since most resumes are viewed digitally today, Free-Form is often the best choice unless you specifically need to print physical copies.
+ Since most resumes are viewed digitally today, Free-Form is often the best choice unless you specifically need to
+ print physical copies.
### 2. Shorten text blocks
@@ -43,9 +49,7 @@ Long paragraphs take up space without adding proportional value. Review each sec
- **Focus on impact.** Keep measurable achievements; cut generic descriptions.
- **Limit bullets per entry.** Three to five bullets per job is usually enough.
-
- Read each bullet point and ask: "Does this help me get an interview?" If not, cut it.
-
+Read each bullet point and ask: "Does this help me get an interview?" If not, cut it.
### 3. Use multi-column layouts
@@ -54,21 +58,25 @@ 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.
-
+
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 |
+| Section | Recommended Columns |
+| ------------------------- | ------------------- |
+| Skills | > 2 columns |
+| Languages | > 3 columns |
+| Interests | > 2 columns |
+| Profiles | > 3 columns |
+| Certifications (if brief) | > 2 columns |
- 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.
+ 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.
### 4. Move items to another page
@@ -84,7 +92,8 @@ Use the **Move to** feature to relocate items:
For detailed instructions, see [Moving items between sections](/guides/moving-items-between-sections).
- 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.
+ 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.
### 5. Adjust layout and design settings
@@ -93,17 +102,19 @@ The right sidebar contains settings that control how much space your content use
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 |
+| 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 |
- **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.
+ **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.
### 6. Hide less important sections
@@ -130,7 +141,8 @@ Try this order:
5. Fine-tune font size and spacing last
- 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.
+ 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.
## Troubleshooting
diff --git a/docs/guides/json-resume-schema.mdx b/docs/guides/json-resume-schema.mdx
index 65068425b..a09ff37df 100644
--- a/docs/guides/json-resume-schema.mdx
+++ b/docs/guides/json-resume-schema.mdx
@@ -22,9 +22,7 @@ This is the future I'm hoping to build toward.
The Reactive Resume JSON Schema is publicly available at:
-
- [https://rxresu.me/schema.json](https://rxresu.me/schema.json)
-
+[https://rxresu.me/schema.json](https://rxresu.me/schema.json)
This endpoint returns a complete JSON Schema (draft 2020-12 compatible) that describes the structure of a Reactive Resume document. It includes:
@@ -39,8 +37,8 @@ The schema includes a `version` field to track compatibility:
```json
{
- "$schema": "https://rxresu.me/schema.json",
- "version": "5.0.0"
+ "$schema": "https://rxresu.me/schema.json",
+ "version": "5.0.0"
}
```
@@ -61,7 +59,7 @@ const validate = ajv.compile(schema);
const isValid = validate(resumeData);
if (!isValid) {
- console.error(validate.errors);
+ console.error(validate.errors);
}
```
@@ -71,11 +69,11 @@ Add the `$schema` property to your resume JSON files to get autocompletion and v
```json
{
- "$schema": "https://rxresu.me/schema.json",
- "version": "5.0.0",
- "basics": {
- "name": "John Doe"
- }
+ "$schema": "https://rxresu.me/schema.json",
+ "version": "5.0.0",
+ "basics": {
+ "name": "John Doe"
+ }
}
```
@@ -92,31 +90,31 @@ The schema enables third-party integrations:
The resume data is organized into these top-level sections:
-| Section | Description |
-|---------|-------------|
-| `$schema` | URL to the JSON Schema for validation |
-| `version` | Schema version (currently `5.0.0`) |
-| `picture` | Profile photo configuration |
-| `basics` | Name, headline, contact info, custom fields |
-| `summary` | Professional summary or bio |
-| `sections` | Standard sections (experience, education, etc.) |
-| `customSections` | User-defined additional sections |
-| `metadata` | Template, layout, design, typography settings |
+| Section | Description |
+| ---------------- | ----------------------------------------------- |
+| `$schema` | URL to the JSON Schema for validation |
+| `version` | Schema version (currently `5.0.0`) |
+| `picture` | Profile photo configuration |
+| `basics` | Name, headline, contact info, custom fields |
+| `summary` | Professional summary or bio |
+| `sections` | Standard sections (experience, education, etc.) |
+| `customSections` | User-defined additional sections |
+| `metadata` | Template, layout, design, typography settings |
Each section within `sections` follows a consistent structure:
```json
{
- "title": "Experience",
- "columns": 1,
- "hidden": false,
- "items": [
- {
- "id": "uuid",
- "hidden": false,
- // ... section-specific fields
- }
- ]
+ "title": "Experience",
+ "columns": 1,
+ "hidden": false,
+ "items": [
+ {
+ "id": "uuid",
+ "hidden": false
+ // ... section-specific fields
+ }
+ ]
}
```
@@ -135,2333 +133,1931 @@ Want to integrate? The schema is open and freely available. You can fetch it pro
Below is the complete JSON Schema for Reactive Resume. You can also fetch the latest version directly from [https://rxresu.me/schema.json](https://rxresu.me/schema.json).
-
- Copy the schema from the live endpoint to ensure you always have the latest version.
-
+Copy the schema from the live endpoint to ensure you always have the latest version.
```json /schema.json lines expandable
{
- "$schema": "https://json-schema.org/draft/2020-12/schema",
- "type": "object",
- "properties": {
- "picture": {
- "type": "object",
- "properties": {
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the picture from the resume."
- },
- "url": {
- "type": "string",
- "description": "The URL to the picture to display on the resume. Must be a valid URL with a protocol (http:// or https://)."
- },
- "size": {
- "type": "number",
- "minimum": 32,
- "maximum": 512,
- "description": "The size of the picture to display on the resume, defined in points (pt)."
- },
- "rotation": {
- "type": "number",
- "minimum": 0,
- "maximum": 360,
- "description": "The rotation of the picture to display on the resume, defined in degrees (°)."
- },
- "aspectRatio": {
- "type": "number",
- "minimum": 0.5,
- "maximum": 2.5,
- "description": "The aspect ratio of the picture to display on the resume, defined as width / height (e.g. 1.5 for 1.5:1 or 0.5 for 1:2)."
- },
- "borderRadius": {
- "type": "number",
- "minimum": 0,
- "maximum": 100,
- "description": "The border radius of the picture to display on the resume, defined in points (pt)."
- },
- "borderColor": {
- "type": "string",
- "description": "The color of the border of the picture to display on the resume, defined as rgba(r, g, b, a)."
- },
- "borderWidth": {
- "type": "number",
- "minimum": 0,
- "description": "The width of the border of the picture to display on the resume, defined in points (pt)."
- },
- "shadowColor": {
- "type": "string",
- "description": "The color of the shadow of the picture to display on the resume, defined as rgba(r, g, b, a)."
- },
- "shadowWidth": {
- "type": "number",
- "minimum": 0,
- "description": "The width of the shadow of the picture to display on the resume, defined in points (pt)."
- }
- },
- "required": [
- "hidden",
- "url",
- "size",
- "rotation",
- "aspectRatio",
- "borderRadius",
- "borderColor",
- "borderWidth",
- "shadowColor",
- "shadowWidth"
- ],
- "additionalProperties": false,
- "description": "Configuration for photograph displayed on the resume"
- },
- "basics": {
- "type": "object",
- "properties": {
- "name": {
- "type": "string",
- "description": "The full name of the author of the resume."
- },
- "headline": {
- "type": "string",
- "description": "The headline of the author of the resume."
- },
- "email": {
- "anyOf": [
- {
- "type": "string",
- "format": "email",
- "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
- },
- {
- "type": "string",
- "const": ""
- }
- ],
- "description": "The email address of the author of the resume."
- },
- "phone": {
- "type": "string",
- "description": "The phone number of the author of the resume."
- },
- "location": {
- "type": "string",
- "description": "The location of the author of the resume."
- },
- "website": {
- "type": "object",
- "properties": {
- "url": {
- "type": "string",
- "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
- },
- "label": {
- "type": "string",
- "description": "The label to display for the URL. Leave blank to display the URL as-is."
- }
- },
- "required": [
- "url",
- "label"
- ],
- "additionalProperties": false,
- "description": "The website of the author of the resume."
- },
- "customFields": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "id": {
- "type": "string",
- "description": "The unique identifier for the custom field. Usually generated as a UUID."
- },
- "icon": {
- "type": "string",
- "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
- },
- "text": {
- "type": "string",
- "description": "The text to display for the custom field."
- },
- "link": {
- "default": "",
- "anyOf": [
- {
- "type": "string",
- "format": "uri"
- },
- {
- "type": "string",
- "const": ""
- }
- ],
- "description": "If the custom field should be a link, the URL to link to."
- }
- },
- "required": [
- "id",
- "icon",
- "text",
- "link"
- ],
- "additionalProperties": false
- },
- "description": "The custom fields to display on the resume."
- }
- },
- "required": [
- "name",
- "headline",
- "email",
- "phone",
- "location",
- "website",
- "customFields"
- ],
- "additionalProperties": false,
- "description": "Basic information about the author, such as name, email, phone, location, and website"
- },
- "summary": {
- "type": "object",
- "properties": {
- "title": {
- "type": "string",
- "description": "The title of the summary of the resume."
- },
- "columns": {
- "type": "number",
- "description": "The number of columns the summary should span across."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the summary from the resume."
- },
- "content": {
- "type": "string",
- "description": "The content of the summary of the resume. This should be a HTML-formatted string."
- }
- },
- "required": [
- "title",
- "columns",
- "hidden",
- "content"
- ],
- "additionalProperties": false,
- "description": "Summary section of the resume, useful for a short bio or introduction"
- },
- "sections": {
- "type": "object",
- "properties": {
- "profiles": {
- "type": "object",
- "properties": {
- "title": {
- "type": "string",
- "description": "The title of the section."
- },
- "columns": {
- "type": "number",
- "description": "The number of columns the section should span across."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the section from the resume."
- },
- "items": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "id": {
- "type": "string",
- "description": "The unique identifier for the item. Usually generated as a UUID."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the item from the resume."
- },
- "icon": {
- "type": "string",
- "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
- },
- "network": {
- "type": "string",
- "minLength": 1,
- "description": "The name of the network or platform."
- },
- "username": {
- "type": "string",
- "description": "The username of the author on the network or platform."
- },
- "website": {
- "type": "object",
- "properties": {
- "url": {
- "type": "string",
- "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
- },
- "label": {
- "type": "string",
- "description": "The label to display for the URL. Leave blank to display the URL as-is."
- }
- },
- "required": [
- "url",
- "label"
- ],
- "additionalProperties": false,
- "description": "The link to the profile of the author on the network or platform, if any."
- }
- },
- "required": [
- "id",
- "hidden",
- "icon",
- "network",
- "username",
- "website"
- ],
- "additionalProperties": false
- },
- "description": "The items to display in the profiles section."
- }
- },
- "required": [
- "title",
- "columns",
- "hidden",
- "items"
- ],
- "additionalProperties": false,
- "description": "The section to display the profiles of the author."
- },
- "experience": {
- "type": "object",
- "properties": {
- "title": {
- "type": "string",
- "description": "The title of the section."
- },
- "columns": {
- "type": "number",
- "description": "The number of columns the section should span across."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the section from the resume."
- },
- "items": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "id": {
- "type": "string",
- "description": "The unique identifier for the item. Usually generated as a UUID."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the item from the resume."
- },
- "company": {
- "type": "string",
- "minLength": 1,
- "description": "The name of the company or organization."
- },
- "position": {
- "type": "string",
- "description": "The position held at the company or organization."
- },
- "location": {
- "type": "string",
- "description": "The location of the company or organization."
- },
- "period": {
- "type": "string",
- "description": "The period of time the author was employed at the company or organization."
- },
- "website": {
- "type": "object",
- "properties": {
- "url": {
- "type": "string",
- "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
- },
- "label": {
- "type": "string",
- "description": "The label to display for the URL. Leave blank to display the URL as-is."
- }
- },
- "required": [
- "url",
- "label"
- ],
- "additionalProperties": false,
- "description": "The website of the company or organization, if any."
- },
- "description": {
- "type": "string",
- "description": "The description of the experience. This should be a HTML-formatted string."
- }
- },
- "required": [
- "id",
- "hidden",
- "company",
- "position",
- "location",
- "period",
- "website",
- "description"
- ],
- "additionalProperties": false
- },
- "description": "The items to display in the experience section."
- }
- },
- "required": [
- "title",
- "columns",
- "hidden",
- "items"
- ],
- "additionalProperties": false,
- "description": "The section to display the experience of the author."
- },
- "education": {
- "type": "object",
- "properties": {
- "title": {
- "type": "string",
- "description": "The title of the section."
- },
- "columns": {
- "type": "number",
- "description": "The number of columns the section should span across."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the section from the resume."
- },
- "items": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "id": {
- "type": "string",
- "description": "The unique identifier for the item. Usually generated as a UUID."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the item from the resume."
- },
- "school": {
- "type": "string",
- "minLength": 1,
- "description": "The name of the school or institution."
- },
- "degree": {
- "type": "string",
- "description": "The degree or qualification obtained."
- },
- "area": {
- "type": "string",
- "description": "The area of study or specialization."
- },
- "grade": {
- "type": "string",
- "description": "The grade or score achieved."
- },
- "location": {
- "type": "string",
- "description": "The location of the school or institution."
- },
- "period": {
- "type": "string",
- "description": "The period of time the education was obtained over."
- },
- "website": {
- "type": "object",
- "properties": {
- "url": {
- "type": "string",
- "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
- },
- "label": {
- "type": "string",
- "description": "The label to display for the URL. Leave blank to display the URL as-is."
- }
- },
- "required": [
- "url",
- "label"
- ],
- "additionalProperties": false,
- "description": "The website of the school or institution, if any."
- },
- "description": {
- "type": "string",
- "description": "The description of the education. This should be a HTML-formatted string."
- }
- },
- "required": [
- "id",
- "hidden",
- "school",
- "degree",
- "area",
- "grade",
- "location",
- "period",
- "website",
- "description"
- ],
- "additionalProperties": false
- },
- "description": "The items to display in the education section."
- }
- },
- "required": [
- "title",
- "columns",
- "hidden",
- "items"
- ],
- "additionalProperties": false,
- "description": "The section to display the education of the author."
- },
- "projects": {
- "type": "object",
- "properties": {
- "title": {
- "type": "string",
- "description": "The title of the section."
- },
- "columns": {
- "type": "number",
- "description": "The number of columns the section should span across."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the section from the resume."
- },
- "items": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "id": {
- "type": "string",
- "description": "The unique identifier for the item. Usually generated as a UUID."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the item from the resume."
- },
- "name": {
- "type": "string",
- "minLength": 1,
- "description": "The name of the project."
- },
- "period": {
- "type": "string",
- "description": "The period of time the project was worked on."
- },
- "website": {
- "type": "object",
- "properties": {
- "url": {
- "type": "string",
- "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
- },
- "label": {
- "type": "string",
- "description": "The label to display for the URL. Leave blank to display the URL as-is."
- }
- },
- "required": [
- "url",
- "label"
- ],
- "additionalProperties": false,
- "description": "The link to the project, if any."
- },
- "description": {
- "type": "string",
- "description": "The description of the project. This should be a HTML-formatted string."
- }
- },
- "required": [
- "id",
- "hidden",
- "name",
- "period",
- "website",
- "description"
- ],
- "additionalProperties": false
- },
- "description": "The items to display in the projects section."
- }
- },
- "required": [
- "title",
- "columns",
- "hidden",
- "items"
- ],
- "additionalProperties": false,
- "description": "The section to display the projects of the author."
- },
- "skills": {
- "type": "object",
- "properties": {
- "title": {
- "type": "string",
- "description": "The title of the section."
- },
- "columns": {
- "type": "number",
- "description": "The number of columns the section should span across."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the section from the resume."
- },
- "items": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "id": {
- "type": "string",
- "description": "The unique identifier for the item. Usually generated as a UUID."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the item from the resume."
- },
- "icon": {
- "type": "string",
- "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
- },
- "name": {
- "type": "string",
- "minLength": 1,
- "description": "The name of the skill."
- },
- "proficiency": {
- "type": "string",
- "description": "The proficiency level of the skill. Can be any text, such as 'Beginner', 'Intermediate', 'Advanced', etc."
- },
- "level": {
- "default": 0,
- "description": "The proficiency level of the skill, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden.",
- "type": "number",
- "minimum": 0,
- "maximum": 5
- },
- "keywords": {
- "default": [],
- "description": "The keywords associated with the skill, if any. These are displayed as tags below the name.",
- "type": "array",
- "items": {
- "type": "string"
- }
- }
- },
- "required": [
- "id",
- "hidden",
- "icon",
- "name",
- "proficiency",
- "level",
- "keywords"
- ],
- "additionalProperties": false
- },
- "description": "The items to display in the skills section."
- }
- },
- "required": [
- "title",
- "columns",
- "hidden",
- "items"
- ],
- "additionalProperties": false,
- "description": "The section to display the skills of the author."
- },
- "languages": {
- "type": "object",
- "properties": {
- "title": {
- "type": "string",
- "description": "The title of the section."
- },
- "columns": {
- "type": "number",
- "description": "The number of columns the section should span across."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the section from the resume."
- },
- "items": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "id": {
- "type": "string",
- "description": "The unique identifier for the item. Usually generated as a UUID."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the item from the resume."
- },
- "language": {
- "type": "string",
- "minLength": 1,
- "description": "The name of the language the author knows."
- },
- "fluency": {
- "type": "string",
- "description": "The fluency level of the language. Can be any text, such as 'Native', 'Fluent', 'Conversational', etc. or can also be a CEFR level (A1, A2, B1, B2, C1, C2)."
- },
- "level": {
- "default": 0,
- "description": "The proficiency level of the language, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden.",
- "type": "number",
- "minimum": 0,
- "maximum": 5
- }
- },
- "required": [
- "id",
- "hidden",
- "language",
- "fluency",
- "level"
- ],
- "additionalProperties": false
- },
- "description": "The items to display in the languages section."
- }
- },
- "required": [
- "title",
- "columns",
- "hidden",
- "items"
- ],
- "additionalProperties": false,
- "description": "The section to display the languages of the author."
- },
- "interests": {
- "type": "object",
- "properties": {
- "title": {
- "type": "string",
- "description": "The title of the section."
- },
- "columns": {
- "type": "number",
- "description": "The number of columns the section should span across."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the section from the resume."
- },
- "items": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "id": {
- "type": "string",
- "description": "The unique identifier for the item. Usually generated as a UUID."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the item from the resume."
- },
- "icon": {
- "type": "string",
- "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
- },
- "name": {
- "type": "string",
- "minLength": 1,
- "description": "The name of the interest/hobby."
- },
- "keywords": {
- "default": [],
- "description": "The keywords associated with the interest/hobby, if any. These are displayed as tags below the name.",
- "type": "array",
- "items": {
- "type": "string"
- }
- }
- },
- "required": [
- "id",
- "hidden",
- "icon",
- "name",
- "keywords"
- ],
- "additionalProperties": false
- },
- "description": "The items to display in the interests section."
- }
- },
- "required": [
- "title",
- "columns",
- "hidden",
- "items"
- ],
- "additionalProperties": false,
- "description": "The section to display the interests of the author."
- },
- "awards": {
- "type": "object",
- "properties": {
- "title": {
- "type": "string",
- "description": "The title of the section."
- },
- "columns": {
- "type": "number",
- "description": "The number of columns the section should span across."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the section from the resume."
- },
- "items": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "id": {
- "type": "string",
- "description": "The unique identifier for the item. Usually generated as a UUID."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the item from the resume."
- },
- "title": {
- "type": "string",
- "minLength": 1,
- "description": "The title of the award."
- },
- "awarder": {
- "type": "string",
- "description": "The awarder of the award."
- },
- "date": {
- "type": "string",
- "description": "The date when the award was received."
- },
- "website": {
- "type": "object",
- "properties": {
- "url": {
- "type": "string",
- "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
- },
- "label": {
- "type": "string",
- "description": "The label to display for the URL. Leave blank to display the URL as-is."
- }
- },
- "required": [
- "url",
- "label"
- ],
- "additionalProperties": false,
- "description": "The website of the award, if any."
- },
- "description": {
- "type": "string",
- "description": "The description of the award. This should be a HTML-formatted string."
- }
- },
- "required": [
- "id",
- "hidden",
- "title",
- "awarder",
- "date",
- "website",
- "description"
- ],
- "additionalProperties": false
- },
- "description": "The items to display in the awards section."
- }
- },
- "required": [
- "title",
- "columns",
- "hidden",
- "items"
- ],
- "additionalProperties": false,
- "description": "The section to display the awards of the author."
- },
- "certifications": {
- "type": "object",
- "properties": {
- "title": {
- "type": "string",
- "description": "The title of the section."
- },
- "columns": {
- "type": "number",
- "description": "The number of columns the section should span across."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the section from the resume."
- },
- "items": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "id": {
- "type": "string",
- "description": "The unique identifier for the item. Usually generated as a UUID."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the item from the resume."
- },
- "title": {
- "type": "string",
- "minLength": 1,
- "description": "The title of the certification."
- },
- "issuer": {
- "type": "string",
- "description": "The issuer of the certification."
- },
- "date": {
- "type": "string",
- "description": "The date when the certification was received."
- },
- "website": {
- "type": "object",
- "properties": {
- "url": {
- "type": "string",
- "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
- },
- "label": {
- "type": "string",
- "description": "The label to display for the URL. Leave blank to display the URL as-is."
- }
- },
- "required": [
- "url",
- "label"
- ],
- "additionalProperties": false,
- "description": "The website of the certification, if any."
- },
- "description": {
- "type": "string",
- "description": "The description of the certification. This should be a HTML-formatted string."
- }
- },
- "required": [
- "id",
- "hidden",
- "title",
- "issuer",
- "date",
- "website",
- "description"
- ],
- "additionalProperties": false
- },
- "description": "The items to display in the certifications section."
- }
- },
- "required": [
- "title",
- "columns",
- "hidden",
- "items"
- ],
- "additionalProperties": false,
- "description": "The section to display the certifications of the author."
- },
- "publications": {
- "type": "object",
- "properties": {
- "title": {
- "type": "string",
- "description": "The title of the section."
- },
- "columns": {
- "type": "number",
- "description": "The number of columns the section should span across."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the section from the resume."
- },
- "items": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "id": {
- "type": "string",
- "description": "The unique identifier for the item. Usually generated as a UUID."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the item from the resume."
- },
- "title": {
- "type": "string",
- "minLength": 1,
- "description": "The title of the publication."
- },
- "publisher": {
- "type": "string",
- "description": "The publisher of the publication."
- },
- "date": {
- "type": "string",
- "description": "The date when the publication was published."
- },
- "website": {
- "type": "object",
- "properties": {
- "url": {
- "type": "string",
- "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
- },
- "label": {
- "type": "string",
- "description": "The label to display for the URL. Leave blank to display the URL as-is."
- }
- },
- "required": [
- "url",
- "label"
- ],
- "additionalProperties": false,
- "description": "The link to the publication, if any."
- },
- "description": {
- "type": "string",
- "description": "The description of the publication. This should be a HTML-formatted string."
- }
- },
- "required": [
- "id",
- "hidden",
- "title",
- "publisher",
- "date",
- "website",
- "description"
- ],
- "additionalProperties": false
- },
- "description": "The items to display in the publications section."
- }
- },
- "required": [
- "title",
- "columns",
- "hidden",
- "items"
- ],
- "additionalProperties": false,
- "description": "The section to display the publications of the author."
- },
- "volunteer": {
- "type": "object",
- "properties": {
- "title": {
- "type": "string",
- "description": "The title of the section."
- },
- "columns": {
- "type": "number",
- "description": "The number of columns the section should span across."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the section from the resume."
- },
- "items": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "id": {
- "type": "string",
- "description": "The unique identifier for the item. Usually generated as a UUID."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the item from the resume."
- },
- "organization": {
- "type": "string",
- "minLength": 1,
- "description": "The name of the organization or company."
- },
- "location": {
- "type": "string",
- "description": "The location of the organization or company."
- },
- "period": {
- "type": "string",
- "description": "The period of time the author was volunteered at the organization or company."
- },
- "website": {
- "type": "object",
- "properties": {
- "url": {
- "type": "string",
- "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
- },
- "label": {
- "type": "string",
- "description": "The label to display for the URL. Leave blank to display the URL as-is."
- }
- },
- "required": [
- "url",
- "label"
- ],
- "additionalProperties": false,
- "description": "The link to the organization or company, if any."
- },
- "description": {
- "type": "string",
- "description": "The description of the volunteer experience. This should be a HTML-formatted string."
- }
- },
- "required": [
- "id",
- "hidden",
- "organization",
- "location",
- "period",
- "website",
- "description"
- ],
- "additionalProperties": false
- },
- "description": "The items to display in the volunteer section."
- }
- },
- "required": [
- "title",
- "columns",
- "hidden",
- "items"
- ],
- "additionalProperties": false,
- "description": "The section to display the volunteer experience of the author."
- },
- "references": {
- "type": "object",
- "properties": {
- "title": {
- "type": "string",
- "description": "The title of the section."
- },
- "columns": {
- "type": "number",
- "description": "The number of columns the section should span across."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the section from the resume."
- },
- "items": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "id": {
- "type": "string",
- "description": "The unique identifier for the item. Usually generated as a UUID."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the item from the resume."
- },
- "name": {
- "type": "string",
- "minLength": 1,
- "description": "The name of the reference, or a note such as 'Available upon request'."
- },
- "position": {
- "type": "string",
- "description": "The position or job title of the reference."
- },
- "website": {
- "type": "object",
- "properties": {
- "url": {
- "type": "string",
- "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
- },
- "label": {
- "type": "string",
- "description": "The label to display for the URL. Leave blank to display the URL as-is."
- }
- },
- "required": [
- "url",
- "label"
- ],
- "additionalProperties": false,
- "description": "The website or LinkedIn profile of the reference, if any."
- },
- "phone": {
- "type": "string",
- "description": "The phone number of the reference."
- },
- "description": {
- "type": "string",
- "description": "The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string."
- }
- },
- "required": [
- "id",
- "hidden",
- "name",
- "position",
- "website",
- "phone",
- "description"
- ],
- "additionalProperties": false
- },
- "description": "The items to display in the references section."
- }
- },
- "required": [
- "title",
- "columns",
- "hidden",
- "items"
- ],
- "additionalProperties": false,
- "description": "The section to display the references of the author."
- }
- },
- "required": [
- "profiles",
- "experience",
- "education",
- "projects",
- "skills",
- "languages",
- "interests",
- "awards",
- "certifications",
- "publications",
- "volunteer",
- "references"
- ],
- "additionalProperties": false,
- "description": "Various sections of the resume, such as experience, education, projects, etc."
- },
- "customSections": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "title": {
- "type": "string",
- "description": "The title of the section."
- },
- "columns": {
- "type": "number",
- "description": "The number of columns the section should span across."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the section from the resume."
- },
- "id": {
- "type": "string",
- "description": "The unique identifier for the custom section. Usually generated as a UUID."
- },
- "type": {
- "type": "string",
- "enum": [
- "profiles",
- "experience",
- "education",
- "projects",
- "skills",
- "languages",
- "interests",
- "awards",
- "certifications",
- "publications",
- "volunteer",
- "references"
- ],
- "description": "The type of items this custom section contains. Determines which item schema and form fields to use."
- },
- "items": {
- "type": "array",
- "items": {
- "anyOf": [
- {
- "type": "object",
- "properties": {
- "id": {
- "type": "string",
- "description": "The unique identifier for the item. Usually generated as a UUID."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the item from the resume."
- },
- "icon": {
- "type": "string",
- "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
- },
- "network": {
- "type": "string",
- "minLength": 1,
- "description": "The name of the network or platform."
- },
- "username": {
- "type": "string",
- "description": "The username of the author on the network or platform."
- },
- "website": {
- "type": "object",
- "properties": {
- "url": {
- "type": "string",
- "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
- },
- "label": {
- "type": "string",
- "description": "The label to display for the URL. Leave blank to display the URL as-is."
- }
- },
- "required": [
- "url",
- "label"
- ],
- "additionalProperties": false,
- "description": "The link to the profile of the author on the network or platform, if any."
- }
- },
- "required": [
- "id",
- "hidden",
- "icon",
- "network",
- "username",
- "website"
- ],
- "additionalProperties": false
- },
- {
- "type": "object",
- "properties": {
- "id": {
- "type": "string",
- "description": "The unique identifier for the item. Usually generated as a UUID."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the item from the resume."
- },
- "company": {
- "type": "string",
- "minLength": 1,
- "description": "The name of the company or organization."
- },
- "position": {
- "type": "string",
- "description": "The position held at the company or organization."
- },
- "location": {
- "type": "string",
- "description": "The location of the company or organization."
- },
- "period": {
- "type": "string",
- "description": "The period of time the author was employed at the company or organization."
- },
- "website": {
- "type": "object",
- "properties": {
- "url": {
- "type": "string",
- "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
- },
- "label": {
- "type": "string",
- "description": "The label to display for the URL. Leave blank to display the URL as-is."
- }
- },
- "required": [
- "url",
- "label"
- ],
- "additionalProperties": false,
- "description": "The website of the company or organization, if any."
- },
- "description": {
- "type": "string",
- "description": "The description of the experience. This should be a HTML-formatted string."
- }
- },
- "required": [
- "id",
- "hidden",
- "company",
- "position",
- "location",
- "period",
- "website",
- "description"
- ],
- "additionalProperties": false
- },
- {
- "type": "object",
- "properties": {
- "id": {
- "type": "string",
- "description": "The unique identifier for the item. Usually generated as a UUID."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the item from the resume."
- },
- "school": {
- "type": "string",
- "minLength": 1,
- "description": "The name of the school or institution."
- },
- "degree": {
- "type": "string",
- "description": "The degree or qualification obtained."
- },
- "area": {
- "type": "string",
- "description": "The area of study or specialization."
- },
- "grade": {
- "type": "string",
- "description": "The grade or score achieved."
- },
- "location": {
- "type": "string",
- "description": "The location of the school or institution."
- },
- "period": {
- "type": "string",
- "description": "The period of time the education was obtained over."
- },
- "website": {
- "type": "object",
- "properties": {
- "url": {
- "type": "string",
- "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
- },
- "label": {
- "type": "string",
- "description": "The label to display for the URL. Leave blank to display the URL as-is."
- }
- },
- "required": [
- "url",
- "label"
- ],
- "additionalProperties": false,
- "description": "The website of the school or institution, if any."
- },
- "description": {
- "type": "string",
- "description": "The description of the education. This should be a HTML-formatted string."
- }
- },
- "required": [
- "id",
- "hidden",
- "school",
- "degree",
- "area",
- "grade",
- "location",
- "period",
- "website",
- "description"
- ],
- "additionalProperties": false
- },
- {
- "type": "object",
- "properties": {
- "id": {
- "type": "string",
- "description": "The unique identifier for the item. Usually generated as a UUID."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the item from the resume."
- },
- "name": {
- "type": "string",
- "minLength": 1,
- "description": "The name of the project."
- },
- "period": {
- "type": "string",
- "description": "The period of time the project was worked on."
- },
- "website": {
- "type": "object",
- "properties": {
- "url": {
- "type": "string",
- "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
- },
- "label": {
- "type": "string",
- "description": "The label to display for the URL. Leave blank to display the URL as-is."
- }
- },
- "required": [
- "url",
- "label"
- ],
- "additionalProperties": false,
- "description": "The link to the project, if any."
- },
- "description": {
- "type": "string",
- "description": "The description of the project. This should be a HTML-formatted string."
- }
- },
- "required": [
- "id",
- "hidden",
- "name",
- "period",
- "website",
- "description"
- ],
- "additionalProperties": false
- },
- {
- "type": "object",
- "properties": {
- "id": {
- "type": "string",
- "description": "The unique identifier for the item. Usually generated as a UUID."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the item from the resume."
- },
- "icon": {
- "type": "string",
- "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
- },
- "name": {
- "type": "string",
- "minLength": 1,
- "description": "The name of the skill."
- },
- "proficiency": {
- "type": "string",
- "description": "The proficiency level of the skill. Can be any text, such as 'Beginner', 'Intermediate', 'Advanced', etc."
- },
- "level": {
- "default": 0,
- "description": "The proficiency level of the skill, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden.",
- "type": "number",
- "minimum": 0,
- "maximum": 5
- },
- "keywords": {
- "default": [],
- "description": "The keywords associated with the skill, if any. These are displayed as tags below the name.",
- "type": "array",
- "items": {
- "type": "string"
- }
- }
- },
- "required": [
- "id",
- "hidden",
- "icon",
- "name",
- "proficiency",
- "level",
- "keywords"
- ],
- "additionalProperties": false
- },
- {
- "type": "object",
- "properties": {
- "id": {
- "type": "string",
- "description": "The unique identifier for the item. Usually generated as a UUID."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the item from the resume."
- },
- "language": {
- "type": "string",
- "minLength": 1,
- "description": "The name of the language the author knows."
- },
- "fluency": {
- "type": "string",
- "description": "The fluency level of the language. Can be any text, such as 'Native', 'Fluent', 'Conversational', etc. or can also be a CEFR level (A1, A2, B1, B2, C1, C2)."
- },
- "level": {
- "default": 0,
- "description": "The proficiency level of the language, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden.",
- "type": "number",
- "minimum": 0,
- "maximum": 5
- }
- },
- "required": [
- "id",
- "hidden",
- "language",
- "fluency",
- "level"
- ],
- "additionalProperties": false
- },
- {
- "type": "object",
- "properties": {
- "id": {
- "type": "string",
- "description": "The unique identifier for the item. Usually generated as a UUID."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the item from the resume."
- },
- "icon": {
- "type": "string",
- "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
- },
- "name": {
- "type": "string",
- "minLength": 1,
- "description": "The name of the interest/hobby."
- },
- "keywords": {
- "default": [],
- "description": "The keywords associated with the interest/hobby, if any. These are displayed as tags below the name.",
- "type": "array",
- "items": {
- "type": "string"
- }
- }
- },
- "required": [
- "id",
- "hidden",
- "icon",
- "name",
- "keywords"
- ],
- "additionalProperties": false
- },
- {
- "type": "object",
- "properties": {
- "id": {
- "type": "string",
- "description": "The unique identifier for the item. Usually generated as a UUID."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the item from the resume."
- },
- "title": {
- "type": "string",
- "minLength": 1,
- "description": "The title of the award."
- },
- "awarder": {
- "type": "string",
- "description": "The awarder of the award."
- },
- "date": {
- "type": "string",
- "description": "The date when the award was received."
- },
- "website": {
- "type": "object",
- "properties": {
- "url": {
- "type": "string",
- "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
- },
- "label": {
- "type": "string",
- "description": "The label to display for the URL. Leave blank to display the URL as-is."
- }
- },
- "required": [
- "url",
- "label"
- ],
- "additionalProperties": false,
- "description": "The website of the award, if any."
- },
- "description": {
- "type": "string",
- "description": "The description of the award. This should be a HTML-formatted string."
- }
- },
- "required": [
- "id",
- "hidden",
- "title",
- "awarder",
- "date",
- "website",
- "description"
- ],
- "additionalProperties": false
- },
- {
- "type": "object",
- "properties": {
- "id": {
- "type": "string",
- "description": "The unique identifier for the item. Usually generated as a UUID."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the item from the resume."
- },
- "title": {
- "type": "string",
- "minLength": 1,
- "description": "The title of the certification."
- },
- "issuer": {
- "type": "string",
- "description": "The issuer of the certification."
- },
- "date": {
- "type": "string",
- "description": "The date when the certification was received."
- },
- "website": {
- "type": "object",
- "properties": {
- "url": {
- "type": "string",
- "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
- },
- "label": {
- "type": "string",
- "description": "The label to display for the URL. Leave blank to display the URL as-is."
- }
- },
- "required": [
- "url",
- "label"
- ],
- "additionalProperties": false,
- "description": "The website of the certification, if any."
- },
- "description": {
- "type": "string",
- "description": "The description of the certification. This should be a HTML-formatted string."
- }
- },
- "required": [
- "id",
- "hidden",
- "title",
- "issuer",
- "date",
- "website",
- "description"
- ],
- "additionalProperties": false
- },
- {
- "type": "object",
- "properties": {
- "id": {
- "type": "string",
- "description": "The unique identifier for the item. Usually generated as a UUID."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the item from the resume."
- },
- "title": {
- "type": "string",
- "minLength": 1,
- "description": "The title of the publication."
- },
- "publisher": {
- "type": "string",
- "description": "The publisher of the publication."
- },
- "date": {
- "type": "string",
- "description": "The date when the publication was published."
- },
- "website": {
- "type": "object",
- "properties": {
- "url": {
- "type": "string",
- "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
- },
- "label": {
- "type": "string",
- "description": "The label to display for the URL. Leave blank to display the URL as-is."
- }
- },
- "required": [
- "url",
- "label"
- ],
- "additionalProperties": false,
- "description": "The link to the publication, if any."
- },
- "description": {
- "type": "string",
- "description": "The description of the publication. This should be a HTML-formatted string."
- }
- },
- "required": [
- "id",
- "hidden",
- "title",
- "publisher",
- "date",
- "website",
- "description"
- ],
- "additionalProperties": false
- },
- {
- "type": "object",
- "properties": {
- "id": {
- "type": "string",
- "description": "The unique identifier for the item. Usually generated as a UUID."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the item from the resume."
- },
- "organization": {
- "type": "string",
- "minLength": 1,
- "description": "The name of the organization or company."
- },
- "location": {
- "type": "string",
- "description": "The location of the organization or company."
- },
- "period": {
- "type": "string",
- "description": "The period of time the author was volunteered at the organization or company."
- },
- "website": {
- "type": "object",
- "properties": {
- "url": {
- "type": "string",
- "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
- },
- "label": {
- "type": "string",
- "description": "The label to display for the URL. Leave blank to display the URL as-is."
- }
- },
- "required": [
- "url",
- "label"
- ],
- "additionalProperties": false,
- "description": "The link to the organization or company, if any."
- },
- "description": {
- "type": "string",
- "description": "The description of the volunteer experience. This should be a HTML-formatted string."
- }
- },
- "required": [
- "id",
- "hidden",
- "organization",
- "location",
- "period",
- "website",
- "description"
- ],
- "additionalProperties": false
- },
- {
- "type": "object",
- "properties": {
- "id": {
- "type": "string",
- "description": "The unique identifier for the item. Usually generated as a UUID."
- },
- "hidden": {
- "type": "boolean",
- "description": "Whether to hide the item from the resume."
- },
- "name": {
- "type": "string",
- "minLength": 1,
- "description": "The name of the reference, or a note such as 'Available upon request'."
- },
- "position": {
- "type": "string",
- "description": "The position or job title of the reference."
- },
- "website": {
- "type": "object",
- "properties": {
- "url": {
- "type": "string",
- "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
- },
- "label": {
- "type": "string",
- "description": "The label to display for the URL. Leave blank to display the URL as-is."
- }
- },
- "required": [
- "url",
- "label"
- ],
- "additionalProperties": false,
- "description": "The website or LinkedIn profile of the reference, if any."
- },
- "phone": {
- "type": "string",
- "description": "The phone number of the reference."
- },
- "description": {
- "type": "string",
- "description": "The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string."
- }
- },
- "required": [
- "id",
- "hidden",
- "name",
- "position",
- "website",
- "phone",
- "description"
- ],
- "additionalProperties": false
- }
- ]
- },
- "description": "The items to display in the custom section. Items follow the schema of the section type."
- }
- },
- "required": [
- "title",
- "columns",
- "hidden",
- "id",
- "type",
- "items"
- ],
- "additionalProperties": false
- },
- "description": "Custom sections of the resume, such as a custom section for notes, etc."
- },
- "metadata": {
- "type": "object",
- "properties": {
- "template": {
- "default": "onyx",
- "description": "The template to use for the resume. Determines the overall design and appearance of the resume.",
- "type": "string",
- "enum": [
- "azurill",
- "bronzor",
- "chikorita",
- "ditto",
- "ditgar",
- "gengar",
- "glalie",
- "kakuna",
- "lapras",
- "leafish",
- "onyx",
- "pikachu",
- "rhyhorn"
- ]
- },
- "layout": {
- "type": "object",
- "properties": {
- "sidebarWidth": {
- "default": 35,
- "description": "The width of the sidebar column, defined as a percentage of the page width.",
- "type": "number",
- "minimum": 10,
- "maximum": 50
- },
- "pages": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "fullWidth": {
- "type": "boolean",
- "description": "Whether the layout of the page should be full width. If true, the main column will span the entire width of the page. This means that there should be no items in the sidebar column."
- },
- "main": {
- "type": "array",
- "items": {
- "type": "string"
- },
- "description": "The items to display in the main column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)."
- },
- "sidebar": {
- "type": "array",
- "items": {
- "type": "string"
- },
- "description": "The items to display in the sidebar column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)."
- }
- },
- "required": [
- "fullWidth",
- "main",
- "sidebar"
- ],
- "additionalProperties": false
- },
- "description": "The pages to display in the layout."
- }
- },
- "required": [
- "sidebarWidth",
- "pages"
- ],
- "additionalProperties": false,
- "description": "The layout of the resume. Determines the structure and arrangement of the sections on the resume."
- },
- "css": {
- "type": "object",
- "properties": {
- "enabled": {
- "type": "boolean",
- "description": "Whether to enable custom CSS for the resume."
- },
- "value": {
- "type": "string",
- "description": "The custom CSS to apply to the resume. This should be a valid CSS string."
- }
- },
- "required": [
- "enabled",
- "value"
- ],
- "additionalProperties": false,
- "description": "Custom CSS to apply to the resume. Can be used to override the default styles of the template."
- },
- "page": {
- "type": "object",
- "properties": {
- "gapX": {
- "type": "number",
- "minimum": 0,
- "description": "The horizontal gap between the sections of the page, defined in points (pt)."
- },
- "gapY": {
- "type": "number",
- "minimum": 0,
- "description": "The vertical gap between the sections of the page, defined in points (pt)."
- },
- "marginX": {
- "type": "number",
- "minimum": 0,
- "description": "The horizontal margin of the page, defined in points (pt)."
- },
- "marginY": {
- "type": "number",
- "minimum": 0,
- "description": "The vertical margin of the page, defined in points (pt)."
- },
- "format": {
- "type": "string",
- "enum": [
- "a4",
- "letter",
- "free-form"
- ],
- "description": "The format of the page. Can be 'a4', 'letter', or 'free-form'."
- },
- "locale": {
- "default": "en-US",
- "type": "string",
- "description": "The locale of the page. Used for displaying pre-translated section headings, if not overridden."
- },
- "hideIcons": {
- "default": false,
- "type": "boolean",
- "description": "Whether to hide the icons of the sections."
- }
- },
- "required": [
- "gapX",
- "gapY",
- "marginX",
- "marginY",
- "format",
- "locale",
- "hideIcons"
- ],
- "additionalProperties": false,
- "description": "The page settings of the resume. Determines the margins, format, and locale of the resume."
- },
- "design": {
- "type": "object",
- "properties": {
- "level": {
- "type": "object",
- "properties": {
- "icon": {
- "type": "string",
- "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
- },
- "type": {
- "type": "string",
- "enum": [
- "hidden",
- "circle",
- "square",
- "rectangle",
- "rectangle-full",
- "progress-bar",
- "icon"
- ],
- "description": "The type of the level design. 'hidden' will hide the level design, 'circle' will display a circle, 'square' will display a square, 'rectangle' will display a rectangle, 'rectangle-full' will display a full rectangle, 'progress-bar' will display a progress bar, and 'icon' will display an icon. If 'icon' is selected, the icon to display should be specified in the 'icon' field."
- }
- },
- "required": [
- "icon",
- "type"
- ],
- "additionalProperties": false
- },
- "colors": {
- "type": "object",
- "properties": {
- "primary": {
- "type": "string",
- "description": "The primary color of the design, defined as rgba(r, g, b, a)."
- },
- "text": {
- "type": "string",
- "description": "The text color of the design, defined as rgba(r, g, b, a). Usually set to black: rgba(0, 0, 0, 1)."
- },
- "background": {
- "type": "string",
- "description": "The background color of the design, defined as rgba(r, g, b, a). Usually set to white: rgba(255, 255, 255, 1)."
- }
- },
- "required": [
- "primary",
- "text",
- "background"
- ],
- "additionalProperties": false
- }
- },
- "required": [
- "level",
- "colors"
- ],
- "additionalProperties": false,
- "description": "The design settings of the resume. Determines the colors, level designs, and typography of the resume."
- },
- "typography": {
- "type": "object",
- "properties": {
- "body": {
- "type": "object",
- "properties": {
- "fontFamily": {
- "type": "string",
- "description": "The family of the font to use. Must be a font that is available on Google Fonts."
- },
- "fontWeights": {
- "default": [
- "400"
- ],
- "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font.",
- "type": "array",
- "items": {
- "type": "string",
- "enum": [
- "100",
- "200",
- "300",
- "400",
- "500",
- "600",
- "700",
- "800",
- "900"
- ]
- }
- },
- "fontSize": {
- "default": 11,
- "description": "The size of the font to use, defined in points (pt).",
- "type": "number",
- "minimum": 6,
- "maximum": 24
- },
- "lineHeight": {
- "default": 1.5,
- "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x).",
- "type": "number",
- "minimum": 0.5,
- "maximum": 4
- }
- },
- "required": [
- "fontFamily",
- "fontWeights",
- "fontSize",
- "lineHeight"
- ],
- "additionalProperties": false,
- "description": "The typography for the body of the resume."
- },
- "heading": {
- "type": "object",
- "properties": {
- "fontFamily": {
- "type": "string",
- "description": "The family of the font to use. Must be a font that is available on Google Fonts."
- },
- "fontWeights": {
- "default": [
- "400"
- ],
- "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font.",
- "type": "array",
- "items": {
- "type": "string",
- "enum": [
- "100",
- "200",
- "300",
- "400",
- "500",
- "600",
- "700",
- "800",
- "900"
- ]
- }
- },
- "fontSize": {
- "default": 11,
- "description": "The size of the font to use, defined in points (pt).",
- "type": "number",
- "minimum": 6,
- "maximum": 24
- },
- "lineHeight": {
- "default": 1.5,
- "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x).",
- "type": "number",
- "minimum": 0.5,
- "maximum": 4
- }
- },
- "required": [
- "fontFamily",
- "fontWeights",
- "fontSize",
- "lineHeight"
- ],
- "additionalProperties": false,
- "description": "The typography for the headings of the resume."
- }
- },
- "required": [
- "body",
- "heading"
- ],
- "additionalProperties": false,
- "description": "The typography settings of the resume. Determines the fonts and sizes of the body and headings of the resume."
- },
- "notes": {
- "type": "string",
- "description": "Personal notes for the resume. Can be used to add any additional information or instructions for the resume. These notes are not displayed on the resume, they are only visible to the author of the resume when editing the resume. This should be a HTML-formatted string."
- }
- },
- "required": [
- "template",
- "layout",
- "css",
- "page",
- "design",
- "typography",
- "notes"
- ],
- "additionalProperties": false,
- "description": "Metadata for the resume, such as template, layout, typography, etc. This section describes the overall design and appearance of the resume."
- }
- },
- "required": [
- "picture",
- "basics",
- "summary",
- "sections",
- "customSections",
- "metadata"
- ],
- "additionalProperties": false
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "type": "object",
+ "properties": {
+ "picture": {
+ "type": "object",
+ "properties": {
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the picture from the resume."
+ },
+ "url": {
+ "type": "string",
+ "description": "The URL to the picture to display on the resume. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "size": {
+ "type": "number",
+ "minimum": 32,
+ "maximum": 512,
+ "description": "The size of the picture to display on the resume, defined in points (pt)."
+ },
+ "rotation": {
+ "type": "number",
+ "minimum": 0,
+ "maximum": 360,
+ "description": "The rotation of the picture to display on the resume, defined in degrees (°)."
+ },
+ "aspectRatio": {
+ "type": "number",
+ "minimum": 0.5,
+ "maximum": 2.5,
+ "description": "The aspect ratio of the picture to display on the resume, defined as width / height (e.g. 1.5 for 1.5:1 or 0.5 for 1:2)."
+ },
+ "borderRadius": {
+ "type": "number",
+ "minimum": 0,
+ "maximum": 100,
+ "description": "The border radius of the picture to display on the resume, defined in points (pt)."
+ },
+ "borderColor": {
+ "type": "string",
+ "description": "The color of the border of the picture to display on the resume, defined as rgba(r, g, b, a)."
+ },
+ "borderWidth": {
+ "type": "number",
+ "minimum": 0,
+ "description": "The width of the border of the picture to display on the resume, defined in points (pt)."
+ },
+ "shadowColor": {
+ "type": "string",
+ "description": "The color of the shadow of the picture to display on the resume, defined as rgba(r, g, b, a)."
+ },
+ "shadowWidth": {
+ "type": "number",
+ "minimum": 0,
+ "description": "The width of the shadow of the picture to display on the resume, defined in points (pt)."
+ }
+ },
+ "required": [
+ "hidden",
+ "url",
+ "size",
+ "rotation",
+ "aspectRatio",
+ "borderRadius",
+ "borderColor",
+ "borderWidth",
+ "shadowColor",
+ "shadowWidth"
+ ],
+ "additionalProperties": false,
+ "description": "Configuration for photograph displayed on the resume"
+ },
+ "basics": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The full name of the author of the resume."
+ },
+ "headline": {
+ "type": "string",
+ "description": "The headline of the author of the resume."
+ },
+ "email": {
+ "anyOf": [
+ {
+ "type": "string",
+ "format": "email",
+ "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
+ },
+ {
+ "type": "string",
+ "const": ""
+ }
+ ],
+ "description": "The email address of the author of the resume."
+ },
+ "phone": {
+ "type": "string",
+ "description": "The phone number of the author of the resume."
+ },
+ "location": {
+ "type": "string",
+ "description": "The location of the author of the resume."
+ },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "additionalProperties": false,
+ "description": "The website of the author of the resume."
+ },
+ "customFields": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the custom field. Usually generated as a UUID."
+ },
+ "icon": {
+ "type": "string",
+ "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
+ },
+ "text": {
+ "type": "string",
+ "description": "The text to display for the custom field."
+ },
+ "link": {
+ "default": "",
+ "anyOf": [
+ {
+ "type": "string",
+ "format": "uri"
+ },
+ {
+ "type": "string",
+ "const": ""
+ }
+ ],
+ "description": "If the custom field should be a link, the URL to link to."
+ }
+ },
+ "required": ["id", "icon", "text", "link"],
+ "additionalProperties": false
+ },
+ "description": "The custom fields to display on the resume."
+ }
+ },
+ "required": ["name", "headline", "email", "phone", "location", "website", "customFields"],
+ "additionalProperties": false,
+ "description": "Basic information about the author, such as name, email, phone, location, and website"
+ },
+ "summary": {
+ "type": "object",
+ "properties": {
+ "title": {
+ "type": "string",
+ "description": "The title of the summary of the resume."
+ },
+ "columns": {
+ "type": "number",
+ "description": "The number of columns the summary should span across."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the summary from the resume."
+ },
+ "content": {
+ "type": "string",
+ "description": "The content of the summary of the resume. This should be a HTML-formatted string."
+ }
+ },
+ "required": ["title", "columns", "hidden", "content"],
+ "additionalProperties": false,
+ "description": "Summary section of the resume, useful for a short bio or introduction"
+ },
+ "sections": {
+ "type": "object",
+ "properties": {
+ "profiles": {
+ "type": "object",
+ "properties": {
+ "title": {
+ "type": "string",
+ "description": "The title of the section."
+ },
+ "columns": {
+ "type": "number",
+ "description": "The number of columns the section should span across."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the section from the resume."
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the item from the resume."
+ },
+ "icon": {
+ "type": "string",
+ "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
+ },
+ "network": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the network or platform."
+ },
+ "username": {
+ "type": "string",
+ "description": "The username of the author on the network or platform."
+ },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "additionalProperties": false,
+ "description": "The link to the profile of the author on the network or platform, if any."
+ }
+ },
+ "required": ["id", "hidden", "icon", "network", "username", "website"],
+ "additionalProperties": false
+ },
+ "description": "The items to display in the profiles section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "additionalProperties": false,
+ "description": "The section to display the profiles of the author."
+ },
+ "experience": {
+ "type": "object",
+ "properties": {
+ "title": {
+ "type": "string",
+ "description": "The title of the section."
+ },
+ "columns": {
+ "type": "number",
+ "description": "The number of columns the section should span across."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the section from the resume."
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the item from the resume."
+ },
+ "company": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the company or organization."
+ },
+ "position": {
+ "type": "string",
+ "description": "The position held at the company or organization."
+ },
+ "location": {
+ "type": "string",
+ "description": "The location of the company or organization."
+ },
+ "period": {
+ "type": "string",
+ "description": "The period of time the author was employed at the company or organization."
+ },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "additionalProperties": false,
+ "description": "The website of the company or organization, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the experience. This should be a HTML-formatted string."
+ }
+ },
+ "required": ["id", "hidden", "company", "position", "location", "period", "website", "description"],
+ "additionalProperties": false
+ },
+ "description": "The items to display in the experience section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "additionalProperties": false,
+ "description": "The section to display the experience of the author."
+ },
+ "education": {
+ "type": "object",
+ "properties": {
+ "title": {
+ "type": "string",
+ "description": "The title of the section."
+ },
+ "columns": {
+ "type": "number",
+ "description": "The number of columns the section should span across."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the section from the resume."
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the item from the resume."
+ },
+ "school": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the school or institution."
+ },
+ "degree": {
+ "type": "string",
+ "description": "The degree or qualification obtained."
+ },
+ "area": {
+ "type": "string",
+ "description": "The area of study or specialization."
+ },
+ "grade": {
+ "type": "string",
+ "description": "The grade or score achieved."
+ },
+ "location": {
+ "type": "string",
+ "description": "The location of the school or institution."
+ },
+ "period": {
+ "type": "string",
+ "description": "The period of time the education was obtained over."
+ },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "additionalProperties": false,
+ "description": "The website of the school or institution, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the education. This should be a HTML-formatted string."
+ }
+ },
+ "required": [
+ "id",
+ "hidden",
+ "school",
+ "degree",
+ "area",
+ "grade",
+ "location",
+ "period",
+ "website",
+ "description"
+ ],
+ "additionalProperties": false
+ },
+ "description": "The items to display in the education section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "additionalProperties": false,
+ "description": "The section to display the education of the author."
+ },
+ "projects": {
+ "type": "object",
+ "properties": {
+ "title": {
+ "type": "string",
+ "description": "The title of the section."
+ },
+ "columns": {
+ "type": "number",
+ "description": "The number of columns the section should span across."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the section from the resume."
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the item from the resume."
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the project."
+ },
+ "period": {
+ "type": "string",
+ "description": "The period of time the project was worked on."
+ },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "additionalProperties": false,
+ "description": "The link to the project, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the project. This should be a HTML-formatted string."
+ }
+ },
+ "required": ["id", "hidden", "name", "period", "website", "description"],
+ "additionalProperties": false
+ },
+ "description": "The items to display in the projects section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "additionalProperties": false,
+ "description": "The section to display the projects of the author."
+ },
+ "skills": {
+ "type": "object",
+ "properties": {
+ "title": {
+ "type": "string",
+ "description": "The title of the section."
+ },
+ "columns": {
+ "type": "number",
+ "description": "The number of columns the section should span across."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the section from the resume."
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the item from the resume."
+ },
+ "icon": {
+ "type": "string",
+ "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the skill."
+ },
+ "proficiency": {
+ "type": "string",
+ "description": "The proficiency level of the skill. Can be any text, such as 'Beginner', 'Intermediate', 'Advanced', etc."
+ },
+ "level": {
+ "default": 0,
+ "description": "The proficiency level of the skill, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden.",
+ "type": "number",
+ "minimum": 0,
+ "maximum": 5
+ },
+ "keywords": {
+ "default": [],
+ "description": "The keywords associated with the skill, if any. These are displayed as tags below the name.",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ },
+ "required": ["id", "hidden", "icon", "name", "proficiency", "level", "keywords"],
+ "additionalProperties": false
+ },
+ "description": "The items to display in the skills section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "additionalProperties": false,
+ "description": "The section to display the skills of the author."
+ },
+ "languages": {
+ "type": "object",
+ "properties": {
+ "title": {
+ "type": "string",
+ "description": "The title of the section."
+ },
+ "columns": {
+ "type": "number",
+ "description": "The number of columns the section should span across."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the section from the resume."
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the item from the resume."
+ },
+ "language": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the language the author knows."
+ },
+ "fluency": {
+ "type": "string",
+ "description": "The fluency level of the language. Can be any text, such as 'Native', 'Fluent', 'Conversational', etc. or can also be a CEFR level (A1, A2, B1, B2, C1, C2)."
+ },
+ "level": {
+ "default": 0,
+ "description": "The proficiency level of the language, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden.",
+ "type": "number",
+ "minimum": 0,
+ "maximum": 5
+ }
+ },
+ "required": ["id", "hidden", "language", "fluency", "level"],
+ "additionalProperties": false
+ },
+ "description": "The items to display in the languages section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "additionalProperties": false,
+ "description": "The section to display the languages of the author."
+ },
+ "interests": {
+ "type": "object",
+ "properties": {
+ "title": {
+ "type": "string",
+ "description": "The title of the section."
+ },
+ "columns": {
+ "type": "number",
+ "description": "The number of columns the section should span across."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the section from the resume."
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the item from the resume."
+ },
+ "icon": {
+ "type": "string",
+ "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the interest/hobby."
+ },
+ "keywords": {
+ "default": [],
+ "description": "The keywords associated with the interest/hobby, if any. These are displayed as tags below the name.",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ },
+ "required": ["id", "hidden", "icon", "name", "keywords"],
+ "additionalProperties": false
+ },
+ "description": "The items to display in the interests section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "additionalProperties": false,
+ "description": "The section to display the interests of the author."
+ },
+ "awards": {
+ "type": "object",
+ "properties": {
+ "title": {
+ "type": "string",
+ "description": "The title of the section."
+ },
+ "columns": {
+ "type": "number",
+ "description": "The number of columns the section should span across."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the section from the resume."
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the item from the resume."
+ },
+ "title": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The title of the award."
+ },
+ "awarder": {
+ "type": "string",
+ "description": "The awarder of the award."
+ },
+ "date": {
+ "type": "string",
+ "description": "The date when the award was received."
+ },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "additionalProperties": false,
+ "description": "The website of the award, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the award. This should be a HTML-formatted string."
+ }
+ },
+ "required": ["id", "hidden", "title", "awarder", "date", "website", "description"],
+ "additionalProperties": false
+ },
+ "description": "The items to display in the awards section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "additionalProperties": false,
+ "description": "The section to display the awards of the author."
+ },
+ "certifications": {
+ "type": "object",
+ "properties": {
+ "title": {
+ "type": "string",
+ "description": "The title of the section."
+ },
+ "columns": {
+ "type": "number",
+ "description": "The number of columns the section should span across."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the section from the resume."
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the item from the resume."
+ },
+ "title": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The title of the certification."
+ },
+ "issuer": {
+ "type": "string",
+ "description": "The issuer of the certification."
+ },
+ "date": {
+ "type": "string",
+ "description": "The date when the certification was received."
+ },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "additionalProperties": false,
+ "description": "The website of the certification, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the certification. This should be a HTML-formatted string."
+ }
+ },
+ "required": ["id", "hidden", "title", "issuer", "date", "website", "description"],
+ "additionalProperties": false
+ },
+ "description": "The items to display in the certifications section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "additionalProperties": false,
+ "description": "The section to display the certifications of the author."
+ },
+ "publications": {
+ "type": "object",
+ "properties": {
+ "title": {
+ "type": "string",
+ "description": "The title of the section."
+ },
+ "columns": {
+ "type": "number",
+ "description": "The number of columns the section should span across."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the section from the resume."
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the item from the resume."
+ },
+ "title": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The title of the publication."
+ },
+ "publisher": {
+ "type": "string",
+ "description": "The publisher of the publication."
+ },
+ "date": {
+ "type": "string",
+ "description": "The date when the publication was published."
+ },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "additionalProperties": false,
+ "description": "The link to the publication, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the publication. This should be a HTML-formatted string."
+ }
+ },
+ "required": ["id", "hidden", "title", "publisher", "date", "website", "description"],
+ "additionalProperties": false
+ },
+ "description": "The items to display in the publications section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "additionalProperties": false,
+ "description": "The section to display the publications of the author."
+ },
+ "volunteer": {
+ "type": "object",
+ "properties": {
+ "title": {
+ "type": "string",
+ "description": "The title of the section."
+ },
+ "columns": {
+ "type": "number",
+ "description": "The number of columns the section should span across."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the section from the resume."
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the item from the resume."
+ },
+ "organization": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the organization or company."
+ },
+ "location": {
+ "type": "string",
+ "description": "The location of the organization or company."
+ },
+ "period": {
+ "type": "string",
+ "description": "The period of time the author was volunteered at the organization or company."
+ },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "additionalProperties": false,
+ "description": "The link to the organization or company, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the volunteer experience. This should be a HTML-formatted string."
+ }
+ },
+ "required": ["id", "hidden", "organization", "location", "period", "website", "description"],
+ "additionalProperties": false
+ },
+ "description": "The items to display in the volunteer section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "additionalProperties": false,
+ "description": "The section to display the volunteer experience of the author."
+ },
+ "references": {
+ "type": "object",
+ "properties": {
+ "title": {
+ "type": "string",
+ "description": "The title of the section."
+ },
+ "columns": {
+ "type": "number",
+ "description": "The number of columns the section should span across."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the section from the resume."
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the item from the resume."
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the reference, or a note such as 'Available upon request'."
+ },
+ "position": {
+ "type": "string",
+ "description": "The position or job title of the reference."
+ },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "additionalProperties": false,
+ "description": "The website or LinkedIn profile of the reference, if any."
+ },
+ "phone": {
+ "type": "string",
+ "description": "The phone number of the reference."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string."
+ }
+ },
+ "required": ["id", "hidden", "name", "position", "website", "phone", "description"],
+ "additionalProperties": false
+ },
+ "description": "The items to display in the references section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "additionalProperties": false,
+ "description": "The section to display the references of the author."
+ }
+ },
+ "required": [
+ "profiles",
+ "experience",
+ "education",
+ "projects",
+ "skills",
+ "languages",
+ "interests",
+ "awards",
+ "certifications",
+ "publications",
+ "volunteer",
+ "references"
+ ],
+ "additionalProperties": false,
+ "description": "Various sections of the resume, such as experience, education, projects, etc."
+ },
+ "customSections": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "title": {
+ "type": "string",
+ "description": "The title of the section."
+ },
+ "columns": {
+ "type": "number",
+ "description": "The number of columns the section should span across."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the section from the resume."
+ },
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the custom section. Usually generated as a UUID."
+ },
+ "type": {
+ "type": "string",
+ "enum": [
+ "profiles",
+ "experience",
+ "education",
+ "projects",
+ "skills",
+ "languages",
+ "interests",
+ "awards",
+ "certifications",
+ "publications",
+ "volunteer",
+ "references"
+ ],
+ "description": "The type of items this custom section contains. Determines which item schema and form fields to use."
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "anyOf": [
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the item from the resume."
+ },
+ "icon": {
+ "type": "string",
+ "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
+ },
+ "network": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the network or platform."
+ },
+ "username": {
+ "type": "string",
+ "description": "The username of the author on the network or platform."
+ },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "additionalProperties": false,
+ "description": "The link to the profile of the author on the network or platform, if any."
+ }
+ },
+ "required": ["id", "hidden", "icon", "network", "username", "website"],
+ "additionalProperties": false
+ },
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the item from the resume."
+ },
+ "company": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the company or organization."
+ },
+ "position": {
+ "type": "string",
+ "description": "The position held at the company or organization."
+ },
+ "location": {
+ "type": "string",
+ "description": "The location of the company or organization."
+ },
+ "period": {
+ "type": "string",
+ "description": "The period of time the author was employed at the company or organization."
+ },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "additionalProperties": false,
+ "description": "The website of the company or organization, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the experience. This should be a HTML-formatted string."
+ }
+ },
+ "required": ["id", "hidden", "company", "position", "location", "period", "website", "description"],
+ "additionalProperties": false
+ },
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the item from the resume."
+ },
+ "school": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the school or institution."
+ },
+ "degree": {
+ "type": "string",
+ "description": "The degree or qualification obtained."
+ },
+ "area": {
+ "type": "string",
+ "description": "The area of study or specialization."
+ },
+ "grade": {
+ "type": "string",
+ "description": "The grade or score achieved."
+ },
+ "location": {
+ "type": "string",
+ "description": "The location of the school or institution."
+ },
+ "period": {
+ "type": "string",
+ "description": "The period of time the education was obtained over."
+ },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "additionalProperties": false,
+ "description": "The website of the school or institution, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the education. This should be a HTML-formatted string."
+ }
+ },
+ "required": [
+ "id",
+ "hidden",
+ "school",
+ "degree",
+ "area",
+ "grade",
+ "location",
+ "period",
+ "website",
+ "description"
+ ],
+ "additionalProperties": false
+ },
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the item from the resume."
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the project."
+ },
+ "period": {
+ "type": "string",
+ "description": "The period of time the project was worked on."
+ },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "additionalProperties": false,
+ "description": "The link to the project, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the project. This should be a HTML-formatted string."
+ }
+ },
+ "required": ["id", "hidden", "name", "period", "website", "description"],
+ "additionalProperties": false
+ },
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the item from the resume."
+ },
+ "icon": {
+ "type": "string",
+ "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the skill."
+ },
+ "proficiency": {
+ "type": "string",
+ "description": "The proficiency level of the skill. Can be any text, such as 'Beginner', 'Intermediate', 'Advanced', etc."
+ },
+ "level": {
+ "default": 0,
+ "description": "The proficiency level of the skill, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden.",
+ "type": "number",
+ "minimum": 0,
+ "maximum": 5
+ },
+ "keywords": {
+ "default": [],
+ "description": "The keywords associated with the skill, if any. These are displayed as tags below the name.",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ },
+ "required": ["id", "hidden", "icon", "name", "proficiency", "level", "keywords"],
+ "additionalProperties": false
+ },
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the item from the resume."
+ },
+ "language": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the language the author knows."
+ },
+ "fluency": {
+ "type": "string",
+ "description": "The fluency level of the language. Can be any text, such as 'Native', 'Fluent', 'Conversational', etc. or can also be a CEFR level (A1, A2, B1, B2, C1, C2)."
+ },
+ "level": {
+ "default": 0,
+ "description": "The proficiency level of the language, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden.",
+ "type": "number",
+ "minimum": 0,
+ "maximum": 5
+ }
+ },
+ "required": ["id", "hidden", "language", "fluency", "level"],
+ "additionalProperties": false
+ },
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the item from the resume."
+ },
+ "icon": {
+ "type": "string",
+ "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the interest/hobby."
+ },
+ "keywords": {
+ "default": [],
+ "description": "The keywords associated with the interest/hobby, if any. These are displayed as tags below the name.",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ },
+ "required": ["id", "hidden", "icon", "name", "keywords"],
+ "additionalProperties": false
+ },
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the item from the resume."
+ },
+ "title": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The title of the award."
+ },
+ "awarder": {
+ "type": "string",
+ "description": "The awarder of the award."
+ },
+ "date": {
+ "type": "string",
+ "description": "The date when the award was received."
+ },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "additionalProperties": false,
+ "description": "The website of the award, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the award. This should be a HTML-formatted string."
+ }
+ },
+ "required": ["id", "hidden", "title", "awarder", "date", "website", "description"],
+ "additionalProperties": false
+ },
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the item from the resume."
+ },
+ "title": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The title of the certification."
+ },
+ "issuer": {
+ "type": "string",
+ "description": "The issuer of the certification."
+ },
+ "date": {
+ "type": "string",
+ "description": "The date when the certification was received."
+ },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "additionalProperties": false,
+ "description": "The website of the certification, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the certification. This should be a HTML-formatted string."
+ }
+ },
+ "required": ["id", "hidden", "title", "issuer", "date", "website", "description"],
+ "additionalProperties": false
+ },
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the item from the resume."
+ },
+ "title": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The title of the publication."
+ },
+ "publisher": {
+ "type": "string",
+ "description": "The publisher of the publication."
+ },
+ "date": {
+ "type": "string",
+ "description": "The date when the publication was published."
+ },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "additionalProperties": false,
+ "description": "The link to the publication, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the publication. This should be a HTML-formatted string."
+ }
+ },
+ "required": ["id", "hidden", "title", "publisher", "date", "website", "description"],
+ "additionalProperties": false
+ },
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the item from the resume."
+ },
+ "organization": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the organization or company."
+ },
+ "location": {
+ "type": "string",
+ "description": "The location of the organization or company."
+ },
+ "period": {
+ "type": "string",
+ "description": "The period of time the author was volunteered at the organization or company."
+ },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "additionalProperties": false,
+ "description": "The link to the organization or company, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the volunteer experience. This should be a HTML-formatted string."
+ }
+ },
+ "required": ["id", "hidden", "organization", "location", "period", "website", "description"],
+ "additionalProperties": false
+ },
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Whether to hide the item from the resume."
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the reference, or a note such as 'Available upon request'."
+ },
+ "position": {
+ "type": "string",
+ "description": "The position or job title of the reference."
+ },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "additionalProperties": false,
+ "description": "The website or LinkedIn profile of the reference, if any."
+ },
+ "phone": {
+ "type": "string",
+ "description": "The phone number of the reference."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string."
+ }
+ },
+ "required": ["id", "hidden", "name", "position", "website", "phone", "description"],
+ "additionalProperties": false
+ }
+ ]
+ },
+ "description": "The items to display in the custom section. Items follow the schema of the section type."
+ }
+ },
+ "required": ["title", "columns", "hidden", "id", "type", "items"],
+ "additionalProperties": false
+ },
+ "description": "Custom sections of the resume, such as a custom section for notes, etc."
+ },
+ "metadata": {
+ "type": "object",
+ "properties": {
+ "template": {
+ "default": "onyx",
+ "description": "The template to use for the resume. Determines the overall design and appearance of the resume.",
+ "type": "string",
+ "enum": [
+ "azurill",
+ "bronzor",
+ "chikorita",
+ "ditto",
+ "ditgar",
+ "gengar",
+ "glalie",
+ "kakuna",
+ "lapras",
+ "leafish",
+ "onyx",
+ "pikachu",
+ "rhyhorn"
+ ]
+ },
+ "layout": {
+ "type": "object",
+ "properties": {
+ "sidebarWidth": {
+ "default": 35,
+ "description": "The width of the sidebar column, defined as a percentage of the page width.",
+ "type": "number",
+ "minimum": 10,
+ "maximum": 50
+ },
+ "pages": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "fullWidth": {
+ "type": "boolean",
+ "description": "Whether the layout of the page should be full width. If true, the main column will span the entire width of the page. This means that there should be no items in the sidebar column."
+ },
+ "main": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "The items to display in the main column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)."
+ },
+ "sidebar": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "The items to display in the sidebar column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)."
+ }
+ },
+ "required": ["fullWidth", "main", "sidebar"],
+ "additionalProperties": false
+ },
+ "description": "The pages to display in the layout."
+ }
+ },
+ "required": ["sidebarWidth", "pages"],
+ "additionalProperties": false,
+ "description": "The layout of the resume. Determines the structure and arrangement of the sections on the resume."
+ },
+ "css": {
+ "type": "object",
+ "properties": {
+ "enabled": {
+ "type": "boolean",
+ "description": "Whether to enable custom CSS for the resume."
+ },
+ "value": {
+ "type": "string",
+ "description": "The custom CSS to apply to the resume. This should be a valid CSS string."
+ }
+ },
+ "required": ["enabled", "value"],
+ "additionalProperties": false,
+ "description": "Custom CSS to apply to the resume. Can be used to override the default styles of the template."
+ },
+ "page": {
+ "type": "object",
+ "properties": {
+ "gapX": {
+ "type": "number",
+ "minimum": 0,
+ "description": "The horizontal gap between the sections of the page, defined in points (pt)."
+ },
+ "gapY": {
+ "type": "number",
+ "minimum": 0,
+ "description": "The vertical gap between the sections of the page, defined in points (pt)."
+ },
+ "marginX": {
+ "type": "number",
+ "minimum": 0,
+ "description": "The horizontal margin of the page, defined in points (pt)."
+ },
+ "marginY": {
+ "type": "number",
+ "minimum": 0,
+ "description": "The vertical margin of the page, defined in points (pt)."
+ },
+ "format": {
+ "type": "string",
+ "enum": ["a4", "letter", "free-form"],
+ "description": "The format of the page. Can be 'a4', 'letter', or 'free-form'."
+ },
+ "locale": {
+ "default": "en-US",
+ "type": "string",
+ "description": "The locale of the page. Used for displaying pre-translated section headings, if not overridden."
+ },
+ "hideIcons": {
+ "default": false,
+ "type": "boolean",
+ "description": "Whether to hide the icons of the sections."
+ }
+ },
+ "required": ["gapX", "gapY", "marginX", "marginY", "format", "locale", "hideIcons"],
+ "additionalProperties": false,
+ "description": "The page settings of the resume. Determines the margins, format, and locale of the resume."
+ },
+ "design": {
+ "type": "object",
+ "properties": {
+ "level": {
+ "type": "object",
+ "properties": {
+ "icon": {
+ "type": "string",
+ "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
+ },
+ "type": {
+ "type": "string",
+ "enum": ["hidden", "circle", "square", "rectangle", "rectangle-full", "progress-bar", "icon"],
+ "description": "The type of the level design. 'hidden' will hide the level design, 'circle' will display a circle, 'square' will display a square, 'rectangle' will display a rectangle, 'rectangle-full' will display a full rectangle, 'progress-bar' will display a progress bar, and 'icon' will display an icon. If 'icon' is selected, the icon to display should be specified in the 'icon' field."
+ }
+ },
+ "required": ["icon", "type"],
+ "additionalProperties": false
+ },
+ "colors": {
+ "type": "object",
+ "properties": {
+ "primary": {
+ "type": "string",
+ "description": "The primary color of the design, defined as rgba(r, g, b, a)."
+ },
+ "text": {
+ "type": "string",
+ "description": "The text color of the design, defined as rgba(r, g, b, a). Usually set to black: rgba(0, 0, 0, 1)."
+ },
+ "background": {
+ "type": "string",
+ "description": "The background color of the design, defined as rgba(r, g, b, a). Usually set to white: rgba(255, 255, 255, 1)."
+ }
+ },
+ "required": ["primary", "text", "background"],
+ "additionalProperties": false
+ }
+ },
+ "required": ["level", "colors"],
+ "additionalProperties": false,
+ "description": "The design settings of the resume. Determines the colors, level designs, and typography of the resume."
+ },
+ "typography": {
+ "type": "object",
+ "properties": {
+ "body": {
+ "type": "object",
+ "properties": {
+ "fontFamily": {
+ "type": "string",
+ "description": "The family of the font to use. Must be a font that is available on Google Fonts."
+ },
+ "fontWeights": {
+ "default": ["400"],
+ "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font.",
+ "type": "array",
+ "items": {
+ "type": "string",
+ "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"]
+ }
+ },
+ "fontSize": {
+ "default": 11,
+ "description": "The size of the font to use, defined in points (pt).",
+ "type": "number",
+ "minimum": 6,
+ "maximum": 24
+ },
+ "lineHeight": {
+ "default": 1.5,
+ "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x).",
+ "type": "number",
+ "minimum": 0.5,
+ "maximum": 4
+ }
+ },
+ "required": ["fontFamily", "fontWeights", "fontSize", "lineHeight"],
+ "additionalProperties": false,
+ "description": "The typography for the body of the resume."
+ },
+ "heading": {
+ "type": "object",
+ "properties": {
+ "fontFamily": {
+ "type": "string",
+ "description": "The family of the font to use. Must be a font that is available on Google Fonts."
+ },
+ "fontWeights": {
+ "default": ["400"],
+ "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font.",
+ "type": "array",
+ "items": {
+ "type": "string",
+ "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"]
+ }
+ },
+ "fontSize": {
+ "default": 11,
+ "description": "The size of the font to use, defined in points (pt).",
+ "type": "number",
+ "minimum": 6,
+ "maximum": 24
+ },
+ "lineHeight": {
+ "default": 1.5,
+ "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x).",
+ "type": "number",
+ "minimum": 0.5,
+ "maximum": 4
+ }
+ },
+ "required": ["fontFamily", "fontWeights", "fontSize", "lineHeight"],
+ "additionalProperties": false,
+ "description": "The typography for the headings of the resume."
+ }
+ },
+ "required": ["body", "heading"],
+ "additionalProperties": false,
+ "description": "The typography settings of the resume. Determines the fonts and sizes of the body and headings of the resume."
+ },
+ "notes": {
+ "type": "string",
+ "description": "Personal notes for the resume. Can be used to add any additional information or instructions for the resume. These notes are not displayed on the resume, they are only visible to the author of the resume when editing the resume. This should be a HTML-formatted string."
+ }
+ },
+ "required": ["template", "layout", "css", "page", "design", "typography", "notes"],
+ "additionalProperties": false,
+ "description": "Metadata for the resume, such as template, layout, typography, etc. This section describes the overall design and appearance of the resume."
+ }
+ },
+ "required": ["picture", "basics", "summary", "sections", "customSections", "metadata"],
+ "additionalProperties": false
}
```
diff --git a/docs/guides/linking-social-accounts.mdx b/docs/guides/linking-social-accounts.mdx
index 9f7ff8237..77f5480f7 100644
--- a/docs/guides/linking-social-accounts.mdx
+++ b/docs/guides/linking-social-accounts.mdx
@@ -8,13 +8,15 @@ description: "Learn how to connect or disconnect social sign-in providers like G
Head over to [https://rxresu.me](https://rxresu.me) and sign in with your account credentials.
-
- Once you're in the dashboard, look for the sidebar navigation on the left. Under the Settings section, click on the Authentication link.
-
+
+ Once you're in the dashboard, look for the sidebar navigation on the left. Under the Settings section,
+ click on the Authentication link.
+
-
- On the Authentication page, you may see sections for one or more providers (for example, Google or GitHub), depending on what is enabled on your instance.
-
+
+ On the Authentication page, you may see sections for one or more providers (for example, Google or{" "}
+ GitHub), depending on what is enabled on your instance.
+
Click the Connect button for the provider you want to link. You'll be redirected to the provider to authorize access, then returned to the Authentication settings page.
@@ -22,6 +24,7 @@ description: "Learn how to connect or disconnect social sign-in providers like G
After a successful link, the button will change to Disconnect.
+
@@ -30,6 +33,6 @@ description: "Learn how to connect or disconnect social sign-in providers like G
Before disconnecting, make sure you still have another way to sign in (for example, a password or another linked provider) so you don't get locked out.
+
-
diff --git a/docs/guides/moving-items-between-sections.mdx b/docs/guides/moving-items-between-sections.mdx
index 8f2f760ac..444acb7e8 100644
--- a/docs/guides/moving-items-between-sections.mdx
+++ b/docs/guides/moving-items-between-sections.mdx
@@ -20,6 +20,7 @@ If you have a long work history or extensive project list, you may want to split
Make sure you have at least one item in a section (e.g., an experience entry, project, or skill) before proceeding.
+
@@ -28,6 +29,7 @@ If you have a long work history or extensive project list, you may want to split
+
@@ -36,11 +38,13 @@ If you have a long work history or extensive project list, you may want to split
+
-
- In the dropdown menu, hover over or click the Move to option. This will open a submenu showing all available destinations.
-
+
+ In the dropdown menu, hover over or click the Move to option. This will open a submenu showing all
+ available destinations.
+
The submenu displays available destinations organized by:
@@ -54,10 +58,11 @@ If you have a long work history or extensive project list, you may want to split
-
+
If a custom section of the same type doesn't exist on your target page, Reactive Resume will **automatically create one** for you. This makes it easy to split sections across pages without manual setup.
+
@@ -69,6 +74,7 @@ If you have a long work history or extensive project list, you may want to split
+
@@ -91,6 +97,7 @@ A common use case is when your work history is too long to fit on a single page.
If no Experience section exists on page 2, a new custom section will be created automatically with the same type, so your formatting stays consistent.
+
@@ -101,21 +108,25 @@ A common use case is when your work history is too long to fit on a single page.
- The section headings and styling remain consistent
{/* TODO: Add screenshot showing a resume with Experience split across page 1 and page 2 */}
+
## Tips for organizing multi-page resumes
- **Keep it logical**: Maintain chronological order within each page. Move complete job entries rather than splitting a single role across pages.
+ **Keep it logical**: Maintain chronological order within each page. Move complete job entries rather than splitting a
+ single role across pages.
- **Use custom section names**: After moving items to a new custom section, you can rename it (e.g., "Earlier Experience" or "Additional Projects") to provide context for recruiters.
+ **Use custom section names**: After moving items to a new custom section, you can rename it (e.g., "Earlier
+ Experience" or "Additional Projects") to provide context for recruiters.
- Moving items reorganizes your content but doesn't affect the data itself. You can always move items back or to different sections as needed.
+ Moving items reorganizes your content but doesn't affect the data itself. You can always move items back or to
+ different sections as needed.
## Troubleshooting
diff --git a/docs/guides/selecting-page-format.mdx b/docs/guides/selecting-page-format.mdx
index e717146d9..2dc92efc5 100644
--- a/docs/guides/selecting-page-format.mdx
+++ b/docs/guides/selecting-page-format.mdx
@@ -11,10 +11,10 @@ Reactive Resume offers three page format options: **A4**, **Letter**, and **Free
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) |
+| 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.
@@ -22,10 +22,10 @@ Choose A4 if you're applying to jobs internationally or in regions that use the
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) |
+| Property | Value |
+| -------- | -------------- |
+| Width | 216mm (816px) |
+| Height | 279mm (1056px) |
Choose Letter if you're applying to jobs in North America.
@@ -33,13 +33,14 @@ Choose Letter if you're applying to jobs in North America.
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 |
+| Property | Value |
+| -------- | ------------- |
+| Width | 210mm (794px) |
+| Height | Unlimited |
- With Free-Form, there are no page breaks. Your entire resume renders as one seamless document, regardless of how much content you have.
+ With Free-Form, there are no page breaks. Your entire resume renders as one seamless document, regardless of how much
+ content you have.
## Why Free-Form exists
@@ -56,7 +57,8 @@ When your resume is processed digitally:
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.
- 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.
+ 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.
## How to change your page format
@@ -66,13 +68,13 @@ Since physical page constraints no longer matter for most use cases, Free-Form l
Navigate to your Dashboard and click on the resume you want to edit.
-
- In the resume builder, look for the right sidebar on the right side of the screen.
-
+
+ In the resume builder, look for the right sidebar on the right side of the screen.
+
-
- In the right sidebar, find and click on the **Page** section to expand it.
-
+
+ In the right sidebar, find and click on the **Page** section to expand it.
+
Find the **Format** dropdown and select your preferred option: A4, Letter, or Free-Form.
@@ -80,6 +82,7 @@ Since physical page constraints no longer matter for most use cases, Free-Form l
+
@@ -91,18 +94,19 @@ Since physical page constraints no longer matter for most use cases, Free-Form l
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 |
+| 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 |
- 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.
+ 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.
## Example PDFs
@@ -110,12 +114,24 @@ Use this quick reference to decide which format fits your needs:
Download these sample resumes to see how different formats affect the final PDF output:
-
- A sample resume in A4 format showing traditional page breaks and constraints.
-
-
- A sample resume in Free-Form format showing a continuous single-page layout.
-
+
+ A sample resume in A4 format showing traditional page breaks and constraints.
+
+
+ A sample resume in Free-Form format showing a continuous single-page layout.
+
## Frequently asked questions
@@ -125,17 +141,20 @@ Download these sample resumes to see how different formats affect the final PDF
Yes. ATS systems parse the text content of your PDF, not the page dimensions. Free-Form resumes are fully compatible with applicant tracking systems.
-
- 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.
-
+
+ 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.
+
-
- 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.
-
+
+ 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.
+
-
- 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.
-
+
+ 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.
+
LinkedIn doesn't display uploaded resumes in their original format—it extracts the content. Free-Form works perfectly for LinkedIn uploads.
diff --git a/docs/guides/setting-up-passkeys.mdx b/docs/guides/setting-up-passkeys.mdx
index df2b727a2..9288305be 100644
--- a/docs/guides/setting-up-passkeys.mdx
+++ b/docs/guides/setting-up-passkeys.mdx
@@ -6,7 +6,8 @@ description: "Learn how to register passkeys (WebAuthn) to sign in securely usin
Passkeys Temporarily Disabled: 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 GitHub issue #7463.
+For more details and technical updates, see the GitHub issue #7463.
+
---
@@ -16,17 +17,19 @@ description: "Learn how to register passkeys (WebAuthn) to sign in securely usin
Head over to [https://rxresu.me](https://rxresu.me) and sign in with your account credentials.
-
- Once you're in the dashboard, look for the sidebar navigation on the left. Under the Settings section, click on the Authentication link.
-
+
+ Once you're in the dashboard, look for the sidebar navigation on the left. Under the Settings section,
+ click on the Authentication link.
+
-
- In the Passkeys section, click on the Register New Device button.
-
+
+ In the Passkeys section, click on the Register New Device button.
+
-
- You'll be prompted to enter a name for the passkey. Use something descriptive so you can recognize it later (for example, “MacBook Touch ID” or “iPhone Face ID”).
-
+
+ You'll be prompted to enter a name for the passkey. Use something descriptive so you can recognize it later (for
+ example, “MacBook Touch ID” or “iPhone Face ID”).
+
Your browser or device will show a passkey prompt (WebAuthn). Follow the on-screen instructions to register your passkey using your biometric (Face ID / Touch ID / fingerprint) or device PIN.
@@ -34,6 +37,7 @@ description: "Learn how to register passkeys (WebAuthn) to sign in securely usin
Passkeys are tied to your device (or password manager) and are a more secure alternative to passwords.
+
@@ -42,6 +46,6 @@ description: "Learn how to register passkeys (WebAuthn) to sign in securely usin
Deleting a passkey cannot be undone. After deletion, you won't be able to sign in using that passkey anymore.
+
-
diff --git a/docs/guides/setting-up-two-factor-authentication.mdx b/docs/guides/setting-up-two-factor-authentication.mdx
index ac15ab32c..640a30ec1 100644
--- a/docs/guides/setting-up-two-factor-authentication.mdx
+++ b/docs/guides/setting-up-two-factor-authentication.mdx
@@ -10,6 +10,7 @@ description: "Learn how to enable two-factor authentication (2FA) to add an extr
Two-factor authentication requires a password to be set on your account. If you signed up using a social provider (Google or GitHub), you'll need to set a password first from the Authentication settings page.
+
@@ -18,15 +19,18 @@ description: "Learn how to enable two-factor authentication (2FA) to add an extr
If you haven't created an account yet, follow the guide on [Creating an Account](/guides/creating-an-account).
+
-
- Once you're in the dashboard, look for the sidebar navigation on the left. Under the Settings section, click on the Authentication link.
-
+
+ Once you're in the dashboard, look for the sidebar navigation on the left. Under the Settings section,
+ click on the Authentication link.
+
-
- On the Authentication page, find the Two-Factor Authentication section and click on the Enable 2FA button.
-
+
+ On the Authentication page, find the Two-Factor Authentication section and click on the{" "}
+ Enable 2FA button.
+
You will be asked to re-enter your password to authenticate that it's really you. Enter your password and click continue.
@@ -34,6 +38,7 @@ description: "Learn how to enable two-factor authentication (2FA) to add an extr
This password verification step ensures that only authorized users can enable two-factor authentication on your account.
+
@@ -51,6 +56,7 @@ description: "Learn how to enable two-factor authentication (2FA) to add an extr
You can also copy the secret key above the QR code and paste it into your authenticator app if you prefer manual entry.
+
@@ -59,6 +65,7 @@ description: "Learn how to enable two-factor authentication (2FA) to add an extr
Make sure to copy and store these backup codes in a safe place. If you lose your authenticator device and don't have backup codes, you may lose access to your account.
+
@@ -67,5 +74,6 @@ description: "Learn how to enable two-factor authentication (2FA) to add an extr
If you've lost access to your authenticator device, you can use one of your backup codes to sign in. Remember that each backup code can only be used once.
+
diff --git a/docs/guides/sharing-your-resume-publicly.mdx b/docs/guides/sharing-your-resume-publicly.mdx
index 253e79e5c..3a3c6c3d8 100644
--- a/docs/guides/sharing-your-resume-publicly.mdx
+++ b/docs/guides/sharing-your-resume-publicly.mdx
@@ -8,18 +8,18 @@ Reactive Resume lets you share your resume via a **public URL** that anyone can
## Key benefits of public sharing
-
- Viewers always see the latest version of your resume. No need to send new files when you make updates.
-
-
- See how many times your resume has been viewed and downloaded.
-
-
- Optionally require a password so only people you trust can access your resume.
-
-
- A simple URL that works anywhere—email signatures, LinkedIn, portfolios, job applications.
-
+
+ Viewers always see the latest version of your resume. No need to send new files when you make updates.
+
+
+ See how many times your resume has been viewed and downloaded.
+
+
+ Optionally require a password so only people you trust can access your resume.
+
+
+ A simple URL that works anywhere—email signatures, LinkedIn, portfolios, job applications.
+
## How to enable public sharing
@@ -29,13 +29,11 @@ Reactive Resume lets you share your resume via a **public URL** that anyone can
Navigate to your resume in the resume builder.
-
- In the **right sidebar**, select **Sharing**.
-
+In the **right sidebar**, select **Sharing**.
-
- Turn on the **Allow Public Access** switch. Once enabled, your resume will be accessible via its public URL.
-
+
+ Turn on the **Allow Public Access** switch. Once enabled, your resume will be accessible via its public URL.
+
Your public URL is displayed below the toggle. It follows this format:
@@ -45,11 +43,13 @@ Reactive Resume lets you share your resume via a **public URL** that anyone can
```
Click the **copy** button to copy the URL to your clipboard.
+
- The `{slug}` is the unique, URL-safe name you assign to your resume when you create it. If you want to change your slug, go to the dashboard, right-click your resume card, and choose "Update" to edit its details.
+ The `{slug}` is the unique, URL-safe name you assign to your resume when you create it. If you want to change your
+ slug, go to the dashboard, right-click your resume card, and choose "Update" to edit its details.
## How the public URL works
@@ -62,7 +62,8 @@ When someone visits your public resume URL:
4. **Views are tracked** - Each visit is counted in your resume statistics (see below)
- Changes you make in the builder are reflected immediately on the public URL. There's no separate "publish" step—your public resume is always in sync.
+ Changes you make in the builder are reflected immediately on the public URL. There's no separate "publish" step—your
+ public resume is always in sync.
## Tracking views and downloads
@@ -75,15 +76,16 @@ In the resume builder, open the **right sidebar** and select **Statistics**.
You'll see:
-| Metric | Description |
-|--------|-------------|
-| **Views** | Number of times your public resume page was visited |
-| **Last viewed** | The date when your resume was last viewed |
-| **Downloads** | Number of times your resume was downloaded (via the public page) |
-| **Last downloaded** | The date when your resume was last downloaded |
+| Metric | Description |
+| ------------------- | ---------------------------------------------------------------- |
+| **Views** | Number of times your public resume page was visited |
+| **Last viewed** | The date when your resume was last viewed |
+| **Downloads** | Number of times your resume was downloaded (via the public page) |
+| **Last downloaded** | The date when your resume was last downloaded |
- Statistics are only tracked when your resume is **public**. If you turn off public access, tracking pauses but your existing stats are preserved.
+ Statistics are only tracked when your resume is **public**. If you turn off public access, tracking pauses but your
+ existing stats are preserved.
### What counts as a view?
@@ -95,7 +97,8 @@ A view is counted each time someone loads your public resume page. This includes
- Search engine visits (if your resume is indexed)
- Only **you** can see your resume's statistics. Visitors to your public URL cannot see how many views or downloads your resume has.
+ Only **you** can see your resume's statistics. Visitors to your public URL cannot see how many views or downloads your
+ resume has.
## Password protecting your resume
@@ -115,13 +118,11 @@ When password protection is enabled:
First, make sure **Allow Public Access** is turned on in the **Sharing** section.
-
- Below the public URL, click the **Set Password** button.
-
+Below the public URL, click the **Set Password** button.
-
- Type a password (6-64 characters) and confirm. This password will be required to view your resume.
-
+
+ Type a password (6-64 characters) and confirm. This password will be required to view your resume.
+
Share the password with your intended audience through a secure channel (e.g., direct message, email).
@@ -129,7 +130,7 @@ When password protection is enabled:
- Choose a password you're comfortable sharing. Anyone with the password can view and download your resume.
+ Choose a password you're comfortable sharing. Anyone with the password can view and download your resume.
### How to remove password protection
@@ -149,21 +150,21 @@ Your resume will become accessible to anyone with the public URL.
Add your public resume URL to your LinkedIn profile's **Featured** section or **Contact Info**. Recruiters can view your detailed resume directly.
-
- Include your resume link in your email signature. Anyone you correspond with can quickly access your resume.
-
+
+ Include your resume link in your email signature. Anyone you correspond with can quickly access your resume.
+
-
- Embed or link to your resume from your personal website. The link always shows your latest resume.
-
+
+ Embed or link to your resume from your personal website. The link always shows your latest resume.
+
-
- Some applications ask for a link to your resume. Your public URL is a professional alternative to file uploads.
-
+
+ Some applications ask for a link to your resume. Your public URL is a professional alternative to file uploads.
+
-
- Share your resume URL via QR code or NFC. Update your resume before the event and everyone gets the latest version.
-
+
+ Share your resume URL via QR code or NFC. Update your resume before the event and everyone gets the latest version.
+
Use password protection to share your resume only with specific recruiters while keeping it hidden from your current employer.
@@ -185,7 +186,8 @@ When public access is disabled:
- Password protection settings are preserved
- If you only want to temporarily hide your resume, consider using password protection instead of disabling public access entirely. This way, your URL remains active for people who have the password.
+ If you only want to temporarily hide your resume, consider using password protection instead of disabling public
+ access entirely. This way, your URL remains active for people who have the password.
## Frequently asked questions
@@ -195,17 +197,19 @@ When public access is disabled:
Yes! The URL is based on your **username** and the resume's **slug**. You can change the slug in the **Update Resume** dialog. To open it, right-click on your resume card in the dashboard and select "Update". The username is set in your account settings.
-
- Public resumes may be indexed by search engines. If you want to prevent indexing, use password protection or keep your resume private.
-
+
+ Public resumes may be indexed by search engines. If you want to prevent indexing, use password protection or keep your
+ resume private.
+
-
- Views are only counted when someone visits your public resume URL while not logged in. Visits made while you're logged in to your account are not included in the view statistics.
-
+
+ Views are only counted when someone visits your public resume URL while not logged in. Visits made while you're logged
+ in to your account are not included in the view statistics.
+
-
- No, Reactive Resume only tracks view and download counts, not the identity of visitors. This protects visitor privacy.
-
+
+ No, Reactive Resume only tracks view and download counts, not the identity of visitors. This protects visitor privacy.
+
Your public URL will change to reflect the new username. Old URLs will stop working immediately. You might need to update any links you've shared.
diff --git a/docs/guides/updating-your-profile.mdx b/docs/guides/updating-your-profile.mdx
index 80dd4eea8..8b9f67c2f 100644
--- a/docs/guides/updating-your-profile.mdx
+++ b/docs/guides/updating-your-profile.mdx
@@ -10,11 +10,13 @@ description: "Learn how to update your profile information including your name,
If you haven't created an account yet, follow the guide on [Creating an Account](/guides/creating-an-account).
+
-
- Once you're in the dashboard, look for the sidebar navigation on the left. Under the Settings section, click on the Profile link.
-
+
+ Once you're in the dashboard, look for the sidebar navigation on the left. Under the Settings section,
+ click on the Profile link.
+
On the Profile page, you can update the following information:
@@ -26,6 +28,7 @@ description: "Learn how to update your profile information including your name,
If you update your email address, you will receive a verification link on your current email address. The change will only be accepted after you click on the verification link.
+
@@ -34,5 +37,6 @@ description: "Learn how to update your profile information including your name,
Make sure to check your spam folder if you don't see the verification email in your inbox. The verification link is required to complete the email address change.
+
diff --git a/docs/guides/using-ai.mdx b/docs/guides/using-ai.mdx
index 41dee43b5..b3135e22b 100644
--- a/docs/guides/using-ai.mdx
+++ b/docs/guides/using-ai.mdx
@@ -8,9 +8,10 @@ description: "Learn how to configure an AI provider and API key for AI-assisted
Head over to [https://rxresu.me](https://rxresu.me) and sign in with your account credentials.
-
- Once you're in the dashboard, look for the sidebar navigation on the left. Under the Settings section, click on Artificial Intelligence.
-
+
+ Once you're in the dashboard, look for the sidebar navigation on the left. Under the Settings section,
+ click on Artificial Intelligence.
+
When you configure your AI provider settings on this page, all information (such as the API key and model) is stored locally in your browser and **not** in our database.
@@ -20,6 +21,7 @@ description: "Learn how to configure an AI provider and API key for AI-assisted
However, whenever you use an AI-assisted feature (such as text generation), these credentials need to be sent to the server **temporarily and only for the duration of your request**. This is required to connect with the AI provider on your behalf. See below for a deeper explanation.
+
@@ -29,11 +31,13 @@ description: "Learn how to configure an AI provider and API key for AI-assisted
- **Google Gemini**
- **Anthropic Claude**
- **Ollama** (coming soon)
+
-
- In the Model field, enter the model you want to use (for example, gpt-5.2, claude-4.5-opus, or gemini-3-flash).
-
+
+ In the Model field, enter the model you want to use (for example, gpt-5.2,{" "}
+ claude-4.5-opus, or gemini-3-flash).
+
In the API Key field, paste your provider API key.
@@ -41,6 +45,7 @@ description: "Learn how to configure an AI provider and API key for AI-assisted
Treat this key like a password. Anyone with it can use your provider account and may incur costs.
+
@@ -49,12 +54,13 @@ description: "Learn how to configure an AI provider and API key for AI-assisted
If the status shows Disabled, double-check that all three fields are filled in.
+
## Why AI requests are sent through our server
-Most AI providers (like OpenAI, Gemini, or Anthropic) block requests that come directly from the browser due to something called **CORS** ("Cross-Origin Resource Sharing"), a security mechanism built into web browsers.
+Most AI providers (like OpenAI, Gemini, or Anthropic) block requests that come directly from the browser due to something called **CORS** ("Cross-Origin Resource Sharing"), a security mechanism built into web browsers.
**What is CORS?**
CORS is a browser feature that prevents web pages from making requests to a different domain than the one that served the web page, unless that domain explicitly allows it. Most AI providers do not allow browser-based requests from random websites, primarily for security and abuse prevention.
@@ -62,11 +68,10 @@ CORS is a browser feature that prevents web pages from making requests to a diff
Because of this restriction, the app has to **forward your AI requests (along with your credentials) through our server**, which then contacts the AI provider and returns the response to your browser. **Your credentials are not stored on our server; they are only used to make the request you initiated, and are not logged or retained.**
- If you're curious or would like to audit how your AI requests are forwarded, you can review the code yourself.
- See the relevant implementation in src/integrations/orpc/router/ai.ts within the codebase.
+ If you're curious or would like to audit how your AI requests are forwarded, you can review the code yourself. See the
+ relevant implementation in src/integrations/orpc/router/ai.ts within the codebase.
-
---
### Do you know a workaround?
diff --git a/docs/guides/using-custom-css.mdx b/docs/guides/using-custom-css.mdx
index 67af29ac4..0830437b1 100644
--- a/docs/guides/using-custom-css.mdx
+++ b/docs/guides/using-custom-css.mdx
@@ -9,10 +9,13 @@ The **Custom CSS** panel lets you write your own CSS rules to change how your re
- **Live updates**: your changes are applied immediately as you type.
- **Auto-save**: there is no “Save” button—your CSS is saved automatically when it changes.
-- **Scoped styling (mostly)**: to avoid affecting the rest of the app, your CSS is *usually* scoped to the resume preview.
+- **Scoped styling (mostly)**: to avoid affecting the rest of the app, your CSS is _usually_ scoped to the resume preview.
-
+
## Where to find it
@@ -45,14 +48,14 @@ Example:
```css
/* Normal rules: rely on auto-scoping */
.page a {
- text-decoration: none;
+ text-decoration: none;
}
/* At-rules: manually scope selectors */
@media print {
- .resume-preview-container .page a {
- text-decoration: none;
- }
+ .resume-preview-container .page a {
+ text-decoration: none;
+ }
}
```
@@ -61,7 +64,9 @@ Example:
Keep each rule's selector on **one line**, for example:
```css
-.page-section-experience .section-item-title { font-weight: 700; }
+.page-section-experience .section-item-title {
+ font-weight: 700;
+}
```
Avoid splitting a selector across multiple lines unless you've confirmed it behaves as expected.
@@ -94,7 +99,7 @@ To apply styles only on one template, prefix with the template wrapper:
```css
.template-azurill .page-header {
- gap: 12pt;
+ gap: 12pt;
}
```
@@ -104,7 +109,7 @@ Each page has a class like `.page-0`, `.page-1`, etc.
```css
.page-0 .page-header {
- margin-bottom: 6pt;
+ margin-bottom: 6pt;
}
```
@@ -114,8 +119,8 @@ Custom sections include a dynamic class: `.page-section-`.
```css
.page-section-custom {
- border-top: 1pt solid color-mix(in srgb, var(--page-text-color) 15%, transparent);
- padding-top: 6pt;
+ border-top: 1pt solid color-mix(in srgb, var(--page-text-color) 15%, transparent);
+ padding-top: 6pt;
}
```
@@ -137,9 +142,9 @@ Example (tighten spacing without changing your layout settings):
```css
.page {
- --page-margin-x: 10pt;
- --page-margin-y: 10pt;
- --page-gap-y: 6pt;
+ --page-margin-x: 10pt;
+ --page-margin-y: 10pt;
+ --page-gap-y: 6pt;
}
```
@@ -149,10 +154,10 @@ Example (tighten spacing without changing your layout settings):
```css
.page-section > h6 {
- margin-bottom: 2pt;
- font-weight: 800;
- letter-spacing: 0.02em;
- text-transform: uppercase;
+ margin-bottom: 2pt;
+ font-weight: 800;
+ letter-spacing: 0.02em;
+ text-transform: uppercase;
}
```
@@ -160,12 +165,12 @@ Example (tighten spacing without changing your layout settings):
```css
.page-section {
- padding-bottom: 8pt;
- border-bottom: 1pt solid color-mix(in srgb, var(--page-text-color) 12%, transparent);
+ padding-bottom: 8pt;
+ border-bottom: 1pt solid color-mix(in srgb, var(--page-text-color) 12%, transparent);
}
.page-section:last-child {
- border-bottom: none;
+ border-bottom: none;
}
```
@@ -173,8 +178,8 @@ Example (tighten spacing without changing your layout settings):
```css
.section-item-metadata {
- font-size: calc(var(--page-body-font-size) * 0.9pt);
- opacity: 0.75;
+ font-size: calc(var(--page-body-font-size) * 0.9pt);
+ opacity: 0.75;
}
```
@@ -182,16 +187,16 @@ Example (tighten spacing without changing your layout settings):
```css
.section-item-description {
- opacity: 0.95;
+ opacity: 0.95;
}
.section-item-description ul {
- margin-left: 12pt;
- padding-left: 0;
+ margin-left: 12pt;
+ padding-left: 0;
}
.section-item-description li {
- margin: 2pt 0;
+ margin: 2pt 0;
}
```
@@ -199,11 +204,11 @@ Example (tighten spacing without changing your layout settings):
```css
.section-item-website a {
- display: inline-block;
- padding: 3pt 9pt;
- border-radius: 999pt;
- border: 2pt solid color-mix(in srgb, var(--page-primary-color) 45%, transparent);
- text-decoration: none;
+ display: inline-block;
+ padding: 3pt 9pt;
+ border-radius: 999pt;
+ border: 2pt solid color-mix(in srgb, var(--page-primary-color) 45%, transparent);
+ text-decoration: none;
}
```
@@ -211,7 +216,7 @@ Example (tighten spacing without changing your layout settings):
```css
.page-picture {
- display: none;
+ display: none;
}
```
@@ -219,12 +224,12 @@ Example (tighten spacing without changing your layout settings):
```css
.page-header {
- gap: 6pt;
- padding-bottom: 6pt;
+ gap: 6pt;
+ padding-bottom: 6pt;
}
.basics-headline {
- opacity: 0.8;
+ opacity: 0.8;
}
```
@@ -232,16 +237,16 @@ Example (tighten spacing without changing your layout settings):
```css
.skills-item-name {
- font-weight: 700;
+ font-weight: 700;
}
.skills-item-proficiency {
- margin-top: 1pt;
+ margin-top: 1pt;
}
.skills-item-keywords {
- display: block;
- opacity: 0.85;
+ display: block;
+ opacity: 0.85;
}
```
@@ -249,9 +254,9 @@ Example (tighten spacing without changing your layout settings):
```css
.page-section-profiles {
- background: color-mix(in srgb, var(--page-primary-color) 10%, transparent);
- border-radius: 8pt;
- padding: 8pt 12pt;
+ background: color-mix(in srgb, var(--page-primary-color) 10%, transparent);
+ border-radius: 8pt;
+ padding: 8pt 12pt;
}
```
@@ -266,4 +271,3 @@ Example (tighten spacing without changing your layout settings):
### My `@media` rules affect the whole app / don't apply
At-rules aren't auto-scoped. Prefix selectors inside them with `.resume-preview-container` as shown above.
-
diff --git a/docs/guides/using-private-notes.mdx b/docs/guides/using-private-notes.mdx
index f2074c6e7..a3defe64a 100644
--- a/docs/guides/using-private-notes.mdx
+++ b/docs/guides/using-private-notes.mdx
@@ -10,7 +10,7 @@ The **Private Notes** section is a dedicated space for storing personal informat
Think of it as a built-in notebook attached to each resume where you can jot down anything relevant to your job search.
- Private Notes are stored securely with your resume data and are only visible to you when editing your resume.
+ Private Notes are stored securely with your resume data and are only visible to you when editing your resume.
## Where to find it
@@ -18,7 +18,10 @@ Think of it as a built-in notebook attached to each resume where you can jot dow
In the resume builder, open the **right sidebar** and select **Notes** from the available sections.
-
+
## Use cases
@@ -39,7 +42,8 @@ Keep a record of where you've sent this particular resume:
Paste links to the original job postings so you can quickly reference them when preparing for interviews or following up.
- Job postings often get taken down after a position is filled. Consider copying key requirements or responsibilities into your notes as a backup.
+ Job postings often get taken down after a position is filled. Consider copying key requirements or responsibilities
+ into your notes as a backup.
### Interview preparation
@@ -74,9 +78,9 @@ Track your follow-up schedule:
In the resume builder, click on **Notes** in the right sidebar to expand the section.
-
- Use the rich text editor to add your notes. You can format text with bold, italics, bullet points, and more.
-
+
+ Use the rich text editor to add your notes. You can format text with bold, italics, bullet points, and more.
+
Your notes are saved automatically as you type—there's no need to click a save button.
@@ -86,10 +90,8 @@ Track your follow-up schedule:
## Privacy guarantee
- Private Notes are for your eyes only. They will **never** appear on:
- - Your exported PDF or other file formats
- - Your publicly shared resume link
- - Any printed version of your resume
+ Private Notes are for your eyes only. They will **never** appear on: - Your exported PDF or other file formats - Your
+ publicly shared resume link - Any printed version of your resume
This makes it safe to store sensitive information like salary expectations, candid thoughts about opportunities, or personal reminders that you wouldn't want an employer to see.
diff --git a/docs/guides/using-the-api.mdx b/docs/guides/using-the-api.mdx
index 95d688876..81717264e 100644
--- a/docs/guides/using-the-api.mdx
+++ b/docs/guides/using-the-api.mdx
@@ -8,9 +8,10 @@ description: "Learn how to create API keys and authenticate requests to the Reac
Head over to [https://rxresu.me](https://rxresu.me) and sign in with your account credentials.
-
- Once you're in the dashboard, look for the sidebar navigation on the left. Under the Settings section, click on the API Keys link.
-
+
+ Once you're in the dashboard, look for the sidebar navigation on the left. Under the Settings section,
+ click on the API Keys link.
+
On the API Keys page, click API Documentation to view the API reference.
@@ -18,6 +19,7 @@ description: "Learn how to create API keys and authenticate requests to the Reac
You can also open it directly here: [API Reference](https://docs.rxresu.me/api-reference).
+
@@ -25,6 +27,7 @@ description: "Learn how to create API keys and authenticate requests to the Reac
- **Name**: A label to help you identify what you use this key for
- **Expires in**: How long the key should remain valid
+
@@ -33,6 +36,7 @@ description: "Learn how to create API keys and authenticate requests to the Reac
For security reasons, your API key is only displayed once. If you lose it, you must create a new one.
+
@@ -46,6 +50,7 @@ description: "Learn how to create API keys and authenticate requests to the Reac
curl "https://rxresu.me/api/openapi/resumes" \
-H "x-api-key: YOUR_API_KEY"
```
+
@@ -54,6 +59,6 @@ description: "Learn how to create API keys and authenticate requests to the Reac
Deleted API keys stop working immediately, and the action cannot be undone.
+
-
diff --git a/docs/guides/using-the-mcp-server.mdx b/docs/guides/using-the-mcp-server.mdx
index bd2fc6525..1f158d67f 100644
--- a/docs/guides/using-the-mcp-server.mdx
+++ b/docs/guides/using-the-mcp-server.mdx
@@ -16,6 +16,7 @@ The [Model Context Protocol (MCP)](https://modelcontextprotocol.io) is a standar
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).
+
@@ -29,14 +30,14 @@ If your client supports the `url` field (e.g. **Cursor**), use this — no extra
```json
{
- "mcpServers": {
- "reactive-resume": {
- "url": "https://rxresu.me/mcp",
- "headers": {
- "x-api-key": "your-api-key"
- }
- }
- }
+ "mcpServers": {
+ "reactive-resume": {
+ "url": "https://rxresu.me/mcp",
+ "headers": {
+ "x-api-key": "your-api-key"
+ }
+ }
+ }
}
```
@@ -46,31 +47,24 @@ If your client only supports `command` / `args` (e.g. **Claude Desktop**), use [
```json
{
- "mcpServers": {
- "reactive-resume": {
- "command": "npx",
- "args": [
- "mcp-remote",
- "https://rxresu.me/mcp",
- "--header",
- "x-api-key:your-api-key"
- ]
- }
- }
+ "mcpServers": {
+ "reactive-resume": {
+ "command": "npx",
+ "args": ["mcp-remote", "https://rxresu.me/mcp", "--header", "x-api-key:your-api-key"]
+ }
+ }
}
```
-
- Replace `your-api-key` with the API key you created in the prerequisites step.
-
+Replace `your-api-key` with the API key you created in the prerequisites step.
### 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 |
+| 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
@@ -78,10 +72,10 @@ If you're running a self-hosted Reactive Resume instance, replace `https://rxres
```json
{
- "url": "https://resume.example.com/mcp",
- "headers": {
- "x-api-key": "your-api-key"
- }
+ "url": "https://resume.example.com/mcp",
+ "headers": {
+ "x-api-key": "your-api-key"
+ }
}
```
@@ -89,37 +83,37 @@ If you're running a self-hosted Reactive Resume instance, replace `https://rxres
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 |
+| 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 |
+| 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 |
+| 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
@@ -166,15 +160,16 @@ Once your MCP client is connected, you can use natural language to interact with
- "Tailor my resume for this job description: ..." (uses `tailor_resume`)
- 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.
+ 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.
## 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 |
+| 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` |
diff --git a/docs/guides/using-the-patch-api.mdx b/docs/guides/using-the-patch-api.mdx
index c22b5cf02..071f82e6a 100644
--- a/docs/guides/using-the-patch-api.mdx
+++ b/docs/guides/using-the-patch-api.mdx
@@ -9,15 +9,16 @@ This is based on the [JSON Patch (RFC 6902)](https://datatracker.ietf.org/doc/ht
## When to Use PATCH vs PUT
-| Use case | Method |
-| --- | --- |
+| 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** |
+| Add or remove an item in a section | **PATCH** |
+| Change template, colors, or fonts | **PATCH** |
+| Replace the entire resume data at once | **PUT** |
- 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.
+ 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.
## Authentication
@@ -25,7 +26,7 @@ This is based on the [JSON Patch (RFC 6902)](https://datatracker.ietf.org/doc/ht
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.
- If you're self-hosting, replace `https://rxresu.me` with your instance URL. The API is served under `/api/openapi`.
+ If you're self-hosting, replace `https://rxresu.me` with your instance URL. The API is served under `/api/openapi`.
## Endpoint
@@ -40,20 +41,18 @@ The resume ID is taken from the URL path, so the request body only requires the
```json
{
- "operations": [
- { "op": "replace", "path": "/basics/name", "value": "Jane Doe" }
- ]
+ "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 |
+| 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
@@ -102,8 +101,8 @@ curl -X PATCH "https://rxresu.me/api/openapi/resume/YOUR_RESUME_ID" \
```
- 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.
+ 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.
### Remove an Item from a Section
@@ -172,15 +171,16 @@ curl -X PATCH "https://rxresu.me/api/openapi/resume/YOUR_RESUME_ID" \
## 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. |
+| 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. |
- All operations in a single request are applied atomically. If any operation fails (including a `test`), none of the operations are applied.
+ All operations in a single request are applied atomically. If any operation fails (including a `test`), none of the
+ operations are applied.
## Tips
diff --git a/docs/legal/license.mdx b/docs/legal/license.mdx
index 4baa35ae3..4132f6e63 100644
--- a/docs/legal/license.mdx
+++ b/docs/legal/license.mdx
@@ -36,4 +36,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-
diff --git a/docs/legal/privacy-policy.mdx b/docs/legal/privacy-policy.mdx
index f9be5836f..bf2fe97c6 100644
--- a/docs/legal/privacy-policy.mdx
+++ b/docs/legal/privacy-policy.mdx
@@ -17,7 +17,9 @@ Reactive Resume is open-source and can be operated in different ways (for exampl
If you are self-hosting, **you** are the Service Operator and responsible for compliance (including configuring email delivery, storage, and logging appropriately).
- **Note for self-hosted deployments:** If you are using the official hosted service at rxresu.me, the project's published support contact is hello@amruthpillai.com. Replace the placeholders above with the correct operator details for your deployment if you are self-hosting.
+ **Note for self-hosted deployments:** If you are using the official hosted service at rxresu.me, the
+ project's published support contact is hello@amruthpillai.com. Replace the placeholders above with the
+ correct operator details for your deployment if you are self-hosting.
---
@@ -37,6 +39,7 @@ Reactive Resume is a resume builder that lets you:
## Information We Collect
### Account information
+
When you create an account or sign in, the Service stores:
- **Identity and profile**: name, email address, username/display username, optional profile image
@@ -45,6 +48,7 @@ When you create an account or sign in, the Service stores:
If you use social sign-in (e.g., Google, GitHub, or a custom OAuth provider), the Service stores identifiers and tokens needed to link and maintain that login.
### Authentication and security data
+
To keep your account secure and keep you signed in, the Service stores:
- **Session data**: session token, session expiry, and (if provided) IP address and user agent
@@ -55,6 +59,7 @@ To keep your account secure and keep you signed in, the Service stores:
The Service Operator may also send **transactional emails** (for example, password reset or email verification). Depending on deployment, these emails may be delivered via an email provider or (in development/testing) the links may be logged to server output.
### Resume content
+
When you create or import a resume, the Service stores the resume data you provide, which may include personal data such as:
- Contact details, location, summary
@@ -64,12 +69,14 @@ When you create or import a resume, the Service stores the resume data you provi
Resumes may also have metadata such as tags, a slug, visibility (public/private), and an optional resume password (if you lock a resume).
### Public resume access and statistics
+
If you publish a resume, other users may access it via its public link. The Service may also maintain simple statistics such as:
- View count and download count
- Last viewed/downloaded timestamps
### Uploaded files (e.g., profile pictures)
+
If you upload files, the Service stores them either:
- On the **local filesystem** of the server (default: under a `data/` directory), or
@@ -78,12 +85,14 @@ If you upload files, the Service stores them either:
Depending on configuration, uploaded files may be publicly accessible (for example, some S3 configurations may default to public read access for uploaded objects). The Service Operator is responsible for selecting appropriate access controls for uploads.
### API keys created in the Service
+
If the Service Operator enables API key functionality, the Service can store:
- API key metadata and rate limit counters
- The API key value itself (as stored by the Service)
### Local-only preferences and settings
+
Some settings are stored on your device:
- **Cookies**: UI preferences such as `theme` and `locale`
@@ -121,6 +130,7 @@ The Service does not include built-in behavioral advertising or third-party anal
We share information only as needed to provide the Service:
### PDF generation and screenshots (Printer)
+
When you export to PDF or request a screenshot, the Service sends a request to a configured printer endpoint (a headless Chromium browser) to render a resume URL.
Depending on your deployment, the printer may be:
@@ -129,12 +139,15 @@ Depending on your deployment, the printer may be:
- Operated by a third party (in which case the third party will process the resume content for rendering)
### Storage providers (optional)
+
If configured, uploaded files may be stored in an S3-compatible provider. In that case, the storage provider processes and stores file data on behalf of the Service Operator.
### OAuth providers (optional)
+
If you sign in via OAuth (Google/GitHub/custom), those providers receive authentication requests and return profile information (such as email/name) to the Service, as permitted by your provider settings.
### AI providers (optional, user-supplied)
+
If you enable AI features and provide your own API key, prompts and generated content may be sent to your selected AI provider (OpenAI, Google, Anthropic), according to your use of those features and the provider's policies.
---
@@ -196,4 +209,3 @@ For privacy requests or questions, contact:
- **Service Operator**: Amruth Pillai
- **Email**: hello@amruthpillai.com
-
diff --git a/docs/legal/terms-of-service.mdx b/docs/legal/terms-of-service.mdx
index 8520ba01b..c5415b219 100644
--- a/docs/legal/terms-of-service.mdx
+++ b/docs/legal/terms-of-service.mdx
@@ -15,7 +15,9 @@ These Terms of Service (the “Terms”) govern your access to and use of **Reac
If you do not agree, do not use the Service.
- Replace the placeholders above with the correct operator details for your deployment. If you are using the official hosted service at rxresu.me, the project's published support contact is hello@amruthpillai.com.
+ Replace the placeholders above with the correct operator details for your deployment. If you are using the official
+ hosted service at rxresu.me, the project's published support contact is{" "}
+ hello@amruthpillai.com.
---
@@ -45,15 +47,19 @@ The Service Operator may suspend or terminate accounts to protect the Service, o
## Your Content
### What you provide
+
“Content” includes the resume data you enter or import (text, links, personal details, etc.) and any files you upload (e.g., profile pictures).
### Ownership
+
You retain ownership of your Content.
### License to operate the Service
+
You grant the Service Operator a limited license to host, store, process, and display your Content **only as necessary** to provide the Service (for example, to render your resume in the builder, generate PDFs, serve uploaded images, or show a public resume page you publish).
### Public resumes and sharing
+
If you set a resume as **public** or share a link, you understand that anyone with access to that link may view (and potentially copy) the content you make available. You are responsible for what you choose to publish.
If you password-protect a resume, you are responsible for controlling distribution of that password.
@@ -183,4 +189,3 @@ Questions about these Terms can be sent to:
- **Service Operator**: Amruth Pillai
- **Email**: hello@amruthpillai.com
-
diff --git a/docs/self-hosting/docker.mdx b/docs/self-hosting/docker.mdx
index acdb513ee..46146b298 100644
--- a/docs/self-hosting/docker.mdx
+++ b/docs/self-hosting/docker.mdx
@@ -8,18 +8,14 @@ description: "A comprehensive guide to self-host Reactive Resume with Docker (Po
Reactive Resume can be self-hosted using Docker in a matter of minutes, and this guide will walk you through the process. Here are some of the services you'll need to get started:
-
- Stores accounts, resumes, and application data.
-
-
- Generates PDFs and screenshots using a headless Chromium browser.
-
-
- SMTP for verification emails, password reset, etc. If not configured, emails are logged to the server console.
-
-
- Use S3-compatible storage, or local persistent storage via /app/data.
-
+ Stores accounts, resumes, and application data.
+ Generates PDFs and screenshots using a headless Chromium browser.
+
+ SMTP for verification emails, password reset, etc. If not configured, emails are logged to the server console.
+
+
+ Use S3-compatible storage, or local persistent storage via /app/data.
+
You can pull the latest app image from:
@@ -30,15 +26,9 @@ You can pull the latest app image from:
## Minimum requirements
-
- Docker Engine + Docker Compose plugin (or Docker Desktop).
-
-
- 2 vCPU / 2 GB RAM minimum (4 GB recommended if Postgres + Printer run on the same host).
-
-
- Enough for Postgres + uploads (start with 10-20 GB and scale as needed).
-
+ Docker Engine + Docker Compose plugin (or Docker Desktop).
+ 2 vCPU / 2 GB RAM minimum (4 GB recommended if Postgres + Printer run on the same host).
+ Enough for Postgres + uploads (start with 10-20 GB and scale as needed).
## Quickstart using Docker Compose
@@ -122,6 +112,7 @@ FLAG_DEBUG_PRINTER="false"
FLAG_DISABLE_SIGNUPS="false"
FLAG_DISABLE_EMAIL_AUTH="false"
```
+
@@ -143,6 +134,7 @@ FLAG_DISABLE_EMAIL_AUTH="false"
certutil -generateSRS 32 | findstr /r /v "^$" | findstr /v ":" | findstr /v " " | findstr /v "-" | findstr /v "certutil"
```
+
@@ -229,6 +221,7 @@ volumes:
Prefer pulling from Docker Hub? Keep amruthpillai/reactive-resume:latest. Prefer GHCR? Swap it to ghcr.io/amruthpillai/reactive-resume:latest.
+
@@ -249,34 +242,52 @@ docker compose logs -f reactive-resume
Reactive Resume should now be available at your `APP_URL` (for the example above: `http://localhost:3000`).
+
## How startup works (database migrations)
- On every start, the server automatically runs database migrations before serving traffic. If migrations fail (usually due to a DB connection issue), the container will exit with an error.
+ On every start, the server automatically runs database migrations before serving traffic. If migrations fail
+ (usually due to a DB connection issue), the container will exit with an error.
## Environment variables
-
-
-
APP_URL
-
DATABASE_URL
-
PRINTER_ENDPOINT
-
AUTH_SECRET
-
-
-
-
-
SMTP (SMTP_*)
-
Social auth (GOOGLE_*, GITHUB_*)
-
S3 storage (S3_*)
-
Feature flags (FLAG_*)
-
-
+
+
+
+ APP_URL
+
+
+ DATABASE_URL
+
+
+ PRINTER_ENDPOINT
+
+
+ AUTH_SECRET
+
+
+
+
+
+
+ SMTP (SMTP_*)
+
+
+ Social auth (GOOGLE_*, GITHUB_*)
+
+
+ S3 storage (S3_*)
+
+
+ Feature flags (FLAG_*)
+
+
+
@@ -302,12 +313,13 @@ docker compose logs -f reactive-resume
Set `PRINTER_ENDPOINT` to `http://chrome:9222` (in Docker Compose) or `http://localhost:9222` (if running externally). This provides the same PDF/screenshot generation with a smaller image footprint.
+
-
- - **`DATABASE_URL`**: Postgres connection string in the format `postgresql://USER:PASSWORD@HOST:PORT/DATABASE`.
- - In Docker Compose, set `HOST` to the Postgres service name (e.g. `postgres`), not `localhost`.
-
+
+ - **`DATABASE_URL`**: Postgres connection string in the format `postgresql://USER:PASSWORD@HOST:PORT/DATABASE`. - In
+ Docker Compose, set `HOST` to the Postgres service name (e.g. `postgres`), not `localhost`.
+
**`AUTH_SECRET`**: Secret used to secure authentication. Changing it invalidates existing sessions.
@@ -330,10 +342,11 @@ openssl rand -hex 32
- **`OAUTH_PROVIDER_NAME`**: Display name in the UI
- **`OAUTH_CLIENT_ID`** / **`OAUTH_CLIENT_SECRET`**: Required for any custom OAuth provider
- **`OAUTH_SCOPES`**: Space-separated scopes (defaults to `openid profile email`)
-
+
Configure endpoints using **one** of these methods:
- **Option A — OIDC Discovery (preferred)**: Set `OAUTH_DISCOVERY_URL` to your provider's `.well-known/openid-configuration` URL
- **Option B — Manual URLs**: Set all three: `OAUTH_AUTHORIZATION_URL`, `OAUTH_TOKEN_URL`, and `OAUTH_USER_INFO_URL`
+
@@ -344,20 +357,20 @@ openssl rand -hex 32
- **`SMTP_USER`** / **`SMTP_PASS`**: SMTP credentials.
- **`SMTP_FROM`**: Default from address (for example, `Reactive Resume `).
- **`SMTP_SECURE`**: `"true"` or `"false"` (string). Match your provider settings.
+
-
- - **Default (local)**: If all `S3_*` values are empty, uploads are stored under `/app/data`. Mount it to persistent storage (for example `./data:/app/data`) or uploads can be lost on container recreation.
- - **S3/S3-compatible**: Configure these to store uploads in an S3-compatible service (SeaweedFS, MinIO, AWS S3, etc.):
- - **`S3_ACCESS_KEY_ID`**
- - **`S3_SECRET_ACCESS_KEY`**
- - **`S3_REGION`**
- - **`S3_ENDPOINT`** (for S3-compatible providers; may be blank for AWS depending on your setup)
- - **`S3_BUCKET`**
- - **`S3_FORCE_PATH_STYLE`**: Controls how the bucket is addressed in URLs. Defaults to `"false"`.
- - Set to `"true"` for **path-style** URLs (`https://s3-server.com/bucket`). Common with **MinIO**, **SeaweedFS**, and other self-hosted S3-compatible services.
- - Set to `"false"` for **virtual-hosted-style** URLs (`https://bucket.s3-server.com`). Common with **AWS S3**, **Cloudflare R2**, and most cloud providers.
-
+
+ - **Default (local)**: If all `S3_*` values are empty, uploads are stored under `/app/data`. Mount it to persistent
+ storage (for example `./data:/app/data`) or uploads can be lost on container recreation. - **S3/S3-compatible**:
+ Configure these to store uploads in an S3-compatible service (SeaweedFS, MinIO, AWS S3, etc.): -
+ **`S3_ACCESS_KEY_ID`** - **`S3_SECRET_ACCESS_KEY`** - **`S3_REGION`** - **`S3_ENDPOINT`** (for S3-compatible
+ providers; may be blank for AWS depending on your setup) - **`S3_BUCKET`** - **`S3_FORCE_PATH_STYLE`**: Controls how
+ the bucket is addressed in URLs. Defaults to `"false"`. - Set to `"true"` for **path-style** URLs
+ (`https://s3-server.com/bucket`). Common with **MinIO**, **SeaweedFS**, and other self-hosted S3-compatible services.
+ - Set to `"false"` for **virtual-hosted-style** URLs (`https://bucket.s3-server.com`). Common with **AWS S3**,
+ **Cloudflare R2**, and most cloud providers.
+
- **`FLAG_DEBUG_PRINTER`**: Bypasses the printer-only access restriction (useful when debugging `/printer/{resumeId}`). Recommended: keep `"false"` in production.
@@ -371,17 +384,19 @@ openssl rand -hex 32
To update your Reactive Resume installation to the latest available version, follow these steps:
-1. **Pull the latest images** for all services defined in your Docker Compose file.
+1. **Pull the latest images** for all services defined in your Docker Compose file.
+
```bash
docker compose pull
```
-2. **Restart the containers** to run the new images.
+2. **Restart the containers** to run the new images.
+
```bash
docker compose up -d
```
-3. **(Optional) Remove old, unused Docker images** to free up disk space.
+3. **(Optional) Remove old, unused Docker images** to free up disk space.
```bash
docker image prune -f
```
@@ -430,7 +445,8 @@ Most reverse proxies (such as **Traefik**, **Caddy**, or **nginx** with upstream
This is particularly useful in high-availability setups where you have multiple instances of Reactive Resume. If one instance becomes unhealthy (for example, it loses its database connection), the reverse proxy will stop routing traffic to it until it recovers.
- If you're using **Traefik**, it automatically respects Docker health checks when using the Docker provider. Unhealthy containers are excluded from routing without any additional configuration.
+ If you're using **Traefik**, it automatically respects Docker health checks when using the Docker provider. Unhealthy
+ containers are excluded from routing without any additional configuration.
### Manually checking health
@@ -459,27 +475,27 @@ A healthy response returns HTTP 200. Any other response (or a connection failure
```
-
- - **Common cause**: `APP_URL` doesn't match the URL you're actually using (especially behind a reverse proxy).
- - **Fix**: set `APP_URL` to the public URL (preferably HTTPS) and restart the container.
-
+
+ - **Common cause**: `APP_URL` doesn't match the URL you're actually using (especially behind a reverse proxy). -
+ **Fix**: set `APP_URL` to the public URL (preferably HTTPS) and restart the container.
+
-
- - **Common cause**: Reactive Resume can't reach the printer or the printer can't reach your app.
- - **Checks**:
- - `PRINTER_ENDPOINT` should usually be `http://printer:3000` in Compose.
- - If you use `PRINTER_APP_URL="http://host.docker.internal:3000"`, ensure `extra_hosts: host-gateway` is present for the printer service.
-
+
+ - **Common cause**: Reactive Resume can't reach the printer or the printer can't reach your app. - **Checks**: -
+ `PRINTER_ENDPOINT` should usually be `http://printer:3000` in Compose. - If you use
+ `PRINTER_APP_URL="http://host.docker.internal:3000"`, ensure `extra_hosts: host-gateway` is present for the printer
+ service.
+
-
- - **Cause**: you didn't mount persistent storage for `/app/data` (when not using S3).
- - **Fix**: add a volume mount like `./data:/app/data` and redeploy.
-
+
+ - **Cause**: you didn't mount persistent storage for `/app/data` (when not using S3). - **Fix**: add a volume mount
+ like `./data:/app/data` and redeploy.
+
-
- - **Expected behavior**: if SMTP vars are empty, the app logs emails to the console instead.
- - **Fix**: configure SMTP and verify your provider's TLS/port settings.
-
+
+ - **Expected behavior**: if SMTP vars are empty, the app logs emails to the console instead. - **Fix**: configure SMTP
+ and verify your provider's TLS/port settings.
+
- **Common cause**: The S3 client is using virtual-hosted-style addressing (prepending the bucket name to the endpoint), but your S3-compatible storage expects path-style addressing.
diff --git a/docs/self-hosting/examples.mdx b/docs/self-hosting/examples.mdx
index bb67ba2c2..4ce43d3c3 100644
--- a/docs/self-hosting/examples.mdx
+++ b/docs/self-hosting/examples.mdx
@@ -10,7 +10,9 @@ Every self-hosted setup is unique. You might be running on a single VPS, a Kuber
These examples go beyond the basic setup in the [Self-Hosting with Docker](/self-hosting/docker) guide, showing production-ready configurations with reverse proxies, SSL termination, and other common patterns.
- **Help others by sharing your setup!** If you have a working configuration that isn't covered here, I'd love to include it. Simply [open a pull request](https://github.com/amruthpillai/reactive-resume) with your example added to this page. Your contribution helps the community and makes self-hosting easier for everyone.
+ **Help others by sharing your setup!** If you have a working configuration that isn't covered here, I'd love to
+ include it. Simply [open a pull request](https://github.com/amruthpillai/reactive-resume) with your example added to
+ this page. Your contribution helps the community and makes self-hosting easier for everyone.
---
@@ -20,7 +22,8 @@ These examples go beyond the basic setup in the [Self-Hosting with Docker](/self
This example uses [Traefik](https://traefik.io/) as a reverse proxy with automatic SSL certificate management via Let's Encrypt. Only the Reactive Resume app is exposed through Traefik—Postgres and the printer remain on an internal network.
- Traefik automatically discovers services via Docker labels and handles SSL certificates, making it ideal for setups where you want minimal configuration.
+ Traefik automatically discovers services via Docker labels and handles SSL certificates, making it ideal for setups
+ where you want minimal configuration.
```yaml compose-traefik.yml lines expandable
@@ -291,7 +294,8 @@ http {
```
- For automatic SSL certificates with nginx, consider using [certbot](https://certbot.eff.org/) with the `--nginx` plugin, or a companion container like [nginx-proxy-acme](https://github.com/nginx-proxy/acme-companion).
+ For automatic SSL certificates with nginx, consider using [certbot](https://certbot.eff.org/) with the `--nginx`
+ plugin, or a companion container like [nginx-proxy-acme](https://github.com/nginx-proxy/acme-companion).
---
@@ -301,7 +305,8 @@ http {
This example demonstrates a production-grade Docker Swarm deployment with multiple replicas, health checks, rolling updates, and Traefik integration. It includes SeaweedFS for S3-compatible storage and a PostgreSQL database with custom configuration.
- Docker Swarm is great for multi-node deployments where you need high availability and easy scaling. The app service is configured with 2 replicas and rolling update strategy.
+ Docker Swarm is great for multi-node deployments where you need high availability and easy scaling. The app service is
+ configured with 2 replicas and rolling update strategy.
```yaml compose-swarm.yml lines expandable
@@ -472,7 +477,8 @@ docker stack rm reactive_resume
```
- This example assumes you have an external Traefik network already set up. Adjust the `traefik_network` reference and labels based on your Traefik configuration.
+ This example assumes you have an external Traefik network already set up. Adjust the `traefik_network` reference and
+ labels based on your Traefik configuration.
---
@@ -493,4 +499,4 @@ To contribute, [open a pull request](https://github.com/amruthpillai/reactive-re
1. A brief description of when/why someone would use this setup
2. The complete Docker Compose (or equivalent) configuration
3. Any additional configuration files (nginx.conf, etc.)
-4. Required environment variables
\ No newline at end of file
+4. Required environment variables
diff --git a/docs/self-hosting/migration.mdx b/docs/self-hosting/migration.mdx
index eb918d2e4..92f4ffc7e 100644
--- a/docs/self-hosting/migration.mdx
+++ b/docs/self-hosting/migration.mdx
@@ -8,7 +8,8 @@ description: "A step-by-step guide to migrate your Reactive Resume instance from
This guide walks you through migrating your Reactive Resume installation from **v4 to v5**. The migration process involves setting up a new v5 instance alongside your existing v4 instance, then transferring your users and resumes to the new system.
- **Keep your v4 instance running** until you have successfully migrated all data to v5 and verified everything works correctly. This ensures you have a fallback in case anything goes wrong during the migration.
+ **Keep your v4 instance running** until you have successfully migrated all data to v5 and verified everything works
+ correctly. This ensures you have a fallback in case anything goes wrong during the migration.
## Prerequisites
@@ -16,18 +17,15 @@ This guide walks you through migrating your Reactive Resume installation from **
Before starting the migration, ensure you have:
-
- Your existing Reactive Resume v4 instance should be running and accessible.
-
-
- A fresh Reactive Resume v5 instance set up and running. Follow the [Self-Hosting with Docker](/self-hosting/docker) guide if you haven't done this yet.
-
-
- Access to both your v4 PostgreSQL database (source) and v5 PostgreSQL database (target).
-
-
- A recent backup of your v4 database. Always backup before any migration.
-
+ Your existing Reactive Resume v4 instance should be running and accessible.
+
+ A fresh Reactive Resume v5 instance set up and running. Follow the [Self-Hosting with Docker](/self-hosting/docker)
+ guide if you haven't done this yet.
+
+
+ Access to both your v4 PostgreSQL database (source) and v5 PostgreSQL database (target).
+
+ A recent backup of your v4 database. Always backup before any migration.
## Choosing a Migration Method
@@ -35,16 +33,14 @@ Before starting the migration, ensure you have:
The best migration approach depends on the size of your instance:
-
- **Best for**: Small instances with a handful of resumes.
-
- Uses the built-in Import Dialog to manually convert resumes one at a time.
-
-
- **Best for**: Large instances with many users and resumes.
-
- Uses migration scripts to batch-process all users and resumes automatically.
-
+
+ **Best for**: Small instances with a handful of resumes. Uses the built-in Import Dialog to manually convert resumes
+ one at a time.
+
+
+ **Best for**: Large instances with many users and resumes. Uses migration scripts to batch-process all users and
+ resumes automatically.
+
## Manual Migration (Small Instances)
@@ -56,15 +52,11 @@ If you have only a few resumes to migrate, the simplest approach is to use the *
In your v4 instance, go to each resume and export it as JSON. This creates a portable file containing all your resume data.
-
- In your new v5 instance:
- 1. Log in or create a new account
- 2. Click **Create Resume** or use the **Import** option
- 3. Select the **Reactive Resume v4** format
- 4. Upload your exported JSON file
-
- The import process automatically converts the v4 format to v5.
-
+
+ In your new v5 instance: 1. Log in or create a new account 2. Click **Create Resume** or use the **Import** option 3.
+ Select the **Reactive Resume v4** format 4. Upload your exported JSON file The import process automatically converts
+ the v4 format to v5.
+
Review the imported resume to ensure all data transferred correctly. Repeat for each resume you need to migrate.
@@ -72,7 +64,8 @@ If you have only a few resumes to migrate, the simplest approach is to use the *
- The Import Dialog handles the schema conversion automatically, so you don't need to worry about format differences between v4 and v5.
+ The Import Dialog handles the schema conversion automatically, so you don't need to worry about format differences
+ between v4 and v5.
## Automated Migration (Large Instances)
@@ -84,27 +77,17 @@ For instances with many users and resumes, use the migration scripts to automate
To run the migration scripts, you need the following installed on your host machine:
-
- **tsx** - TypeScript execution environment. Install globally with:
- ```bash
- npm install -g tsx
- ```
-
-
- **dotenvx** (or any tool to load `.env` files). Install globally with:
- ```bash
- npm install -g @dotenvx/dotenvx
- ```
- Alternatively, you can use `dotenv`, `direnv`, or export the variables manually.
-
-
- Clone the Reactive Resume repository to access the migration scripts:
- ```bash
- git clone https://github.com/amruthpillai/reactive-resume.git
- cd reactive-resume
- pnpm install
- ```
-
+
+ **tsx** - TypeScript execution environment. Install globally with: ```bash npm install -g tsx ```
+
+
+ **dotenvx** (or any tool to load `.env` files). Install globally with: ```bash npm install -g @dotenvx/dotenvx ```
+ Alternatively, you can use `dotenv`, `direnv`, or export the variables manually.
+
+
+ Clone the Reactive Resume repository to access the migration scripts: ```bash git clone
+ https://github.com/amruthpillai/reactive-resume.git cd reactive-resume pnpm install ```
+
### Environment Setup
@@ -120,7 +103,8 @@ PRODUCTION_DATABASE_URL="postgresql://user:password@localhost:5432/reactive_resu
```
- Double-check your connection strings! `DATABASE_URL` should point to your **new v5 database** and `PRODUCTION_DATABASE_URL` should point to your **old v4 database**. Mixing these up could cause data loss.
+ Double-check your connection strings! `DATABASE_URL` should point to your **new v5 database** and
+ `PRODUCTION_DATABASE_URL` should point to your **old v4 database**. Mixing these up could cause data loss.
### Step 1: Migrate Users
@@ -140,7 +124,8 @@ dotenvx run -- tsx scripts/migration/user.ts
- Creates a mapping file (`scripts/migration/user-id-map.json`) that links old user IDs to new ones
- The script saves progress automatically. If interrupted (Ctrl+C), you can run it again and it will resume from where it left off.
+ The script saves progress automatically. If interrupted (Ctrl+C), you can run it again and it will resume from where
+ it left off.
**Expected output:**
@@ -179,9 +164,7 @@ dotenvx run -- tsx scripts/migration/resume.ts
- Migrates resume statistics (views, downloads)
- Preserves visibility settings (public/private) and lock status
-
- Like the user script, the resume migration also saves progress and can be resumed if interrupted.
-
+Like the user script, the resume migration also saves progress and can be resumed if interrupted.
**Expected output:**
@@ -213,7 +196,8 @@ Both migration scripts support graceful shutdown and resume:
- **Resume migration**: Run the script again to continue from where you left off
- If you need to restart the migration from scratch, delete the progress files and the user ID mapping file before running the scripts again.
+ If you need to restart the migration from scratch, delete the progress files and the user ID mapping file before
+ running the scripts again.
## Post-Migration Steps
@@ -225,15 +209,14 @@ After completing the migration:
Log into your v5 instance and spot-check several user accounts and resumes to ensure data transferred correctly.
-
- - Create a test resume and export it as PDF
- - Verify social logins work (if configured)
- - Check that two-factor authentication works for migrated users
-
+
+ - Create a test resume and export it as PDF - Verify social logins work (if configured) - Check that two-factor
+ authentication works for migrated users
+
-
- Once verified, update your DNS records or reverse proxy to point to the new v5 instance.
-
+
+ Once verified, update your DNS records or reverse proxy to point to the new v5 instance.
+
After confirming everything works and allowing a grace period, you can safely shut down your v4 instance.
@@ -247,17 +230,20 @@ After completing the migration:
Users who signed up with email/password can continue using their existing passwords. No password reset is required after migration.
-
- User profile pictures (avatars) are stored as references in the database. If you were using S3 storage, ensure your v5 instance has access to the same bucket, or users may need to re-upload their avatars.
-
+
+ User profile pictures (avatars) are stored as references in the database. If you were using S3 storage, ensure your v5
+ instance has access to the same bucket, or users may need to re-upload their avatars.
+
-
- Similar to profile pictures, any images embedded in resumes need to be accessible from your v5 instance. Consider migrating your storage bucket or updating references as needed.
-
+
+ Similar to profile pictures, any images embedded in resumes need to be accessible from your v5 instance. Consider
+ migrating your storage bucket or updating references as needed.
+
-
- If you're using custom OAuth providers, ensure the same providers are configured in v5 with matching client IDs. Users authenticate with the same provider ID, so mismatched configurations will cause login failures.
-
+
+ If you're using custom OAuth providers, ensure the same providers are configured in v5 with matching client IDs. Users
+ authenticate with the same provider ID, so mismatched configurations will cause login failures.
+
The v5 schema has some changes from v4:
@@ -276,25 +262,20 @@ After completing the migration:
Ensure your `.env` file contains both `DATABASE_URL` and `PRODUCTION_DATABASE_URL`, and that you're using a tool like `dotenvx` to load them before running the script.
-
- Users are skipped if:
- - Their email already exists in the v5 database
- - Their username already exists in the v5 database
- - They were already migrated in a previous run
-
- Check the console output for skip reasons.
-
+
+ Users are skipped if: - Their email already exists in the v5 database - Their username already exists in the v5
+ database - They were already migrated in a previous run Check the console output for skip reasons.
+
-
- Resumes are skipped if:
- - The associated user wasn't migrated (user ID not in mapping file)
- - A resume with the same slug already exists for that user
- - They were already migrated in a previous run
-
+
+ Resumes are skipped if: - The associated user wasn't migrated (user ID not in mapping file) - A resume with the same
+ slug already exists for that user - They were already migrated in a previous run
+
-
- If a resume can't be parsed from v4 format, it will be created with default empty data. Check the console output for warnings about specific resumes, and consider manually importing those using the Import Dialog.
-
+
+ If a resume can't be parsed from v4 format, it will be created with default empty data. Check the console output for
+ warnings about specific resumes, and consider manually importing those using the Import Dialog.
+
The scripts process data in batches to avoid overwhelming the database. For very large instances:
diff --git a/docs/self-hosting/sso.mdx b/docs/self-hosting/sso.mdx
index cfdab4a07..b1e77f5da 100644
--- a/docs/self-hosting/sso.mdx
+++ b/docs/self-hosting/sso.mdx
@@ -12,7 +12,8 @@ Reactive Resume supports custom OAuth providers, allowing you to integrate with
- Integrate with existing LDAP/Active Directory infrastructure
- Custom OAuth is designed for **self-hosted instances**. If you're using the hosted version at [rxresu.me](https://rxresu.me), you can use the built-in Google and GitHub sign-in options.
+ Custom OAuth is designed for **self-hosted instances**. If you're using the hosted version at
+ [rxresu.me](https://rxresu.me), you can use the built-in Google and GitHub sign-in options.
## Environment Variables
@@ -21,9 +22,9 @@ To enable a custom OAuth provider, you need to configure the following environme
### Required Variables
-| Variable | Description |
-|----------|-------------|
-| `OAUTH_CLIENT_ID` | The client ID provided by your OAuth provider |
+| Variable | Description |
+| --------------------- | ------------------------------------------------- |
+| `OAUTH_CLIENT_ID` | The client ID provided by your OAuth provider |
| `OAUTH_CLIENT_SECRET` | The client secret provided by your OAuth provider |
### Endpoint Configuration
@@ -44,6 +45,7 @@ You must configure endpoints using **one** of these two methods:
- Authentik: `https://auth.example.com/application/o/reactive-resume/.well-known/openid-configuration`
- Keycloak: `https://keycloak.example.com/realms/myrealm/.well-known/openid-configuration`
- Authelia: `https://auth.example.com/.well-known/openid-configuration`
+
@@ -54,15 +56,16 @@ You must configure endpoints using **one** of these two methods:
| `OAUTH_AUTHORIZATION_URL` | The URL where users are redirected to authorize |
| `OAUTH_TOKEN_URL` | The URL to exchange authorization codes for tokens |
| `OAUTH_USER_INFO_URL` | The URL to fetch user profile information |
+
### Optional Variables
-| Variable | Description | Default |
-|----------|-------------|---------|
-| `OAUTH_PROVIDER_NAME` | Display name shown on the sign-in button | `Custom OAuth` |
-| `OAUTH_SCOPES` | Space-separated list of OAuth scopes | `openid profile email` |
+| Variable | Description | Default |
+| --------------------- | ---------------------------------------- | ---------------------- |
+| `OAUTH_PROVIDER_NAME` | Display name shown on the sign-in button | `Custom OAuth` |
+| `OAUTH_SCOPES` | Space-separated list of OAuth scopes | `openid profile email` |
## Callback URL
@@ -79,7 +82,8 @@ https://resume.example.com/api/auth/oauth2/callback/custom
```
- Make sure the callback URL exactly matches what you configure in your OAuth provider. A mismatch will cause authentication to fail.
+ Make sure the callback URL exactly matches what you configure in your OAuth provider. A mismatch will cause
+ authentication to fail.
## Profile Mapping
@@ -87,14 +91,14 @@ https://resume.example.com/api/auth/oauth2/callback/custom
Reactive Resume automatically maps user profile data from the OAuth provider. The following fields are used:
| Reactive Resume Field | OAuth Profile Fields (in order of preference) |
-|-----------------------|-----------------------------------------------|
-| **Email** (required) | `email` |
-| **Name** | `name` → `preferred_username` → email prefix |
-| **Username** | `preferred_username` → email prefix |
-| **Avatar** | `image` → `picture` → `avatar_url` |
+| --------------------- | --------------------------------------------- |
+| **Email** (required) | `email` |
+| **Name** | `name` → `preferred_username` → email prefix |
+| **Username** | `preferred_username` → email prefix |
+| **Avatar** | `image` → `picture` → `avatar_url` |
- The OAuth provider **must** return an email address. If no email is provided, authentication will fail with an error.
+ The OAuth provider **must** return an email address. If no email is provided, authentication will fail with an error.
## Provider-Specific Setup
@@ -109,6 +113,7 @@ Reactive Resume automatically maps user profile data from the OAuth provider. Th
- **Authorization flow**: Use your preferred authorization flow
- **Client type**: Confidential
- **Redirect URIs**: `https://resume.example.com/api/auth/oauth2/callback/custom`
+
@@ -117,11 +122,10 @@ Reactive Resume automatically maps user profile data from the OAuth provider. Th
- **Name**: Reactive Resume
- **Slug**: `reactive-resume`
- **Provider**: Select the provider you just created
+
-
- From the provider settings, copy the **Client ID** and **Client Secret**.
-
+From the provider settings, copy the **Client ID** and **Client Secret**.
```bash .env
@@ -145,9 +149,9 @@ identity_providers:
clients:
- client_id: reactive-resume
client_name: Reactive Resume
- client_secret: 'your-hashed-secret' # Use authelia hash-password to generate
+ client_secret: "your-hashed-secret" # Use authelia hash-password to generate
public: false
- authorization_policy: two_factor # or one_factor
+ authorization_policy: two_factor # or one_factor
redirect_uris:
- https://resume.example.com/api/auth/oauth2/callback/custom
scopes:
@@ -160,6 +164,7 @@ identity_providers:
Generate the hashed secret using: `authelia crypto hash generate pbkdf2 --variant sha512`
+
@@ -173,6 +178,7 @@ OAUTH_DISCOVERY_URL="https://auth.example.com/.well-known/openid-configuration"
Use the **plain text** secret in Reactive Resume's environment, not the hashed version used in Authelia's configuration.
+
@@ -187,17 +193,17 @@ OAUTH_DISCOVERY_URL="https://auth.example.com/.well-known/openid-configuration"
3. Set **Client ID** (e.g., `reactive-resume`)
4. Set **Client authentication** to **On**
5. Enable **Standard flow**
+
In the client settings, add the redirect URI:
- **Valid redirect URIs**: `https://resume.example.com/api/auth/oauth2/callback/custom`
+
-
- Go to the **Credentials** tab and copy the **Client secret**.
-
+Go to the **Credentials** tab and copy the **Client secret**.
```bash .env
@@ -266,17 +272,11 @@ OAUTH_DISCOVERY_URL="https://auth.company.com/application/o/reactive-resume/.wel
- The user has an email address set in the identity provider
-
- The callback URL configured in your OAuth provider must exactly match:
- ```
- {APP_URL}/api/auth/oauth2/callback/custom
- ```
- Common issues:
- - Trailing slash mismatch
- - HTTP vs HTTPS mismatch
- - Port number differences
- - Path case sensitivity
-
+
+ The callback URL configured in your OAuth provider must exactly match: ```
+ {APP_URL}/api/auth/oauth2/callback/custom ``` Common issues: - Trailing slash mismatch - HTTP vs HTTPS mismatch - Port
+ number differences - Path case sensitivity
+
The custom OAuth option only appears if both `OAUTH_CLIENT_ID` and `OAUTH_CLIENT_SECRET` are set, **and** either:
@@ -284,14 +284,14 @@ OAUTH_DISCOVERY_URL="https://auth.company.com/application/o/reactive-resume/.wel
- All three manual URLs are set (`OAUTH_AUTHORIZATION_URL`, `OAUTH_TOKEN_URL`, `OAUTH_USER_INFO_URL`)
Double-check your environment variables and restart the container.
+
-
- If running behind a reverse proxy:
- - Ensure `APP_URL` matches your public URL
- - Verify the proxy passes the correct headers (`X-Forwarded-Proto`, `X-Forwarded-Host`)
- - Check that your OAuth provider allows the redirect URI from your domain
-
+
+ If running behind a reverse proxy: - Ensure `APP_URL` matches your public URL - Verify the proxy passes the correct
+ headers (`X-Forwarded-Proto`, `X-Forwarded-Host`) - Check that your OAuth provider allows the redirect URI from your
+ domain
+
The profile mapping depends on your provider returning standard claims:
@@ -300,22 +300,24 @@ OAUTH_DISCOVERY_URL="https://auth.company.com/application/o/reactive-resume/.wel
- `picture`, `image`, or `avatar_url` for avatar
Check your provider's documentation to ensure these claims are included in the ID token or userinfo response.
+
## Security Considerations
-
- Always use HTTPS for both your Reactive Resume instance and OAuth provider in production. OAuth tokens should never be transmitted over unencrypted connections.
-
-
- Never commit `OAUTH_CLIENT_SECRET` to version control. Use environment variables or a secrets manager.
-
-
- Configure your OAuth provider to only allow the exact redirect URI. Avoid wildcards in redirect URI configurations.
-
-
- Only request the scopes you need. The default (`openid profile email`) is sufficient for Reactive Resume.
-
+
+ Always use HTTPS for both your Reactive Resume instance and OAuth provider in production. OAuth tokens should never
+ be transmitted over unencrypted connections.
+
+
+ Never commit `OAUTH_CLIENT_SECRET` to version control. Use environment variables or a secrets manager.
+
+
+ Configure your OAuth provider to only allow the exact redirect URI. Avoid wildcards in redirect URI configurations.
+
+
+ Only request the scopes you need. The default (`openid profile email`) is sufficient for Reactive Resume.
+
diff --git a/docs/spec.json b/docs/spec.json
index 1e614db8a..1838aafc5 100644
--- a/docs/spec.json
+++ b/docs/spec.json
@@ -1 +1,3097 @@
-{"info":{"title":"Reactive Resume","version":"5.0.9","description":"Reactive Resume API","license":{"name":"MIT","url":"https://github.com/amruthpillai/reactive-resume/blob/main/LICENSE"},"contact":{"name":"Amruth Pillai","email":"hello@amruthpillai.com","url":"https://amruthpillai.com"}},"servers":[{"url":"http://localhost:3000/api/openapi"}],"externalDocs":{"url":"https://docs.rxresu.me","description":"Reactive Resume Documentation"},"components":{"securitySchemes":{"apiKey":{"type":"apiKey","name":"x-api-key","in":"header","description":"The API key to authenticate requests."}},"schemas":{"ResumeData":{"type":"object","properties":{"picture":{"type":"object","properties":{"hidden":{"type":"boolean","description":"Whether to hide the picture from the resume."},"url":{"type":"string","description":"The URL to the picture to display on the resume. Must be a valid URL with a protocol (http:// or https://)."},"size":{"type":"number","minimum":32,"maximum":512,"description":"The size of the picture to display on the resume, defined in points (pt)."},"rotation":{"type":"number","minimum":0,"maximum":360,"description":"The rotation of the picture to display on the resume, defined in degrees (°)."},"aspectRatio":{"type":"number","minimum":0.5,"maximum":2.5,"description":"The aspect ratio of the picture to display on the resume, defined as width / height (e.g. 1.5 for 1.5:1 or 0.5 for 1:2)."},"borderRadius":{"type":"number","minimum":0,"maximum":100,"description":"The border radius of the picture to display on the resume, defined in points (pt)."},"borderColor":{"type":"string","description":"The color of the border of the picture to display on the resume, defined as rgba(r, g, b, a)."},"borderWidth":{"type":"number","minimum":0,"description":"The width of the border of the picture to display on the resume, defined in points (pt)."},"shadowColor":{"type":"string","description":"The color of the shadow of the picture to display on the resume, defined as rgba(r, g, b, a)."},"shadowWidth":{"type":"number","minimum":0,"description":"The width of the shadow of the picture to display on the resume, defined in points (pt)."}},"required":["hidden","url","size","rotation","aspectRatio","borderRadius","borderColor","borderWidth","shadowColor","shadowWidth"],"description":"Configuration for photograph displayed on the resume"},"basics":{"type":"object","properties":{"name":{"type":"string","description":"The full name of the author of the resume."},"headline":{"type":"string","description":"The headline of the author of the resume."},"email":{"type":"string","description":"The email address of the author of the resume."},"phone":{"type":"string","description":"The phone number of the author of the resume."},"location":{"type":"string","description":"The location of the author of the resume."},"website":{"type":"object","properties":{"url":{"type":"string","description":"The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."},"label":{"type":"string","description":"The label to display for the URL. Leave blank to display the URL as-is."}},"required":["url","label"],"description":"The website of the author of the resume."},"customFields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the custom field. Usually generated as a UUID."},"icon":{"type":"string","description":"The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."},"text":{"type":"string","description":"The text to display for the custom field."},"link":{"type":"string","description":"If the custom field should be a link, the URL to link to."}},"required":["id","icon","text","link"]},"description":"The custom fields to display on the resume."}},"required":["name","headline","email","phone","location","website","customFields"],"description":"Basic information about the author, such as name, email, phone, location, and website"},"summary":{"type":"object","properties":{"title":{"type":"string","description":"The title of the summary of the resume."},"columns":{"type":"number","description":"The number of columns the summary should span across."},"hidden":{"type":"boolean","description":"Whether to hide the summary from the resume."},"content":{"type":"string","description":"The content of the summary of the resume. This should be a HTML-formatted string."}},"required":["title","columns","hidden","content"],"description":"Summary section of the resume, useful for a short bio or introduction"},"sections":{"type":"object","properties":{"profiles":{"type":"object","properties":{"title":{"type":"string","description":"The title of the section."},"columns":{"type":"number","description":"The number of columns the section should span across."},"hidden":{"type":"boolean","description":"Whether to hide the section from the resume."},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"options":{"type":"object","properties":{"showLinkInTitle":{"type":"boolean","description":"If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."}},"required":["showLinkInTitle"],"description":"Display options for this item."},"icon":{"type":"string","description":"The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."},"network":{"type":"string","minLength":1,"description":"The name of the network or platform."},"username":{"type":"string","description":"The username of the author on the network or platform."},"website":{"type":"object","properties":{"url":{"type":"string","description":"The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."},"label":{"type":"string","description":"The label to display for the URL. Leave blank to display the URL as-is."}},"required":["url","label"],"description":"The link to the profile of the author on the network or platform, if any."}},"required":["id","hidden","icon","network","username","website"]},"description":"The items to display in the profiles section."}},"required":["title","columns","hidden","items"],"description":"The section to display the profiles of the author."},"experience":{"type":"object","properties":{"title":{"type":"string","description":"The title of the section."},"columns":{"type":"number","description":"The number of columns the section should span across."},"hidden":{"type":"boolean","description":"Whether to hide the section from the resume."},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"options":{"type":"object","properties":{"showLinkInTitle":{"type":"boolean","description":"If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."}},"required":["showLinkInTitle"],"description":"Display options for this item."},"company":{"type":"string","minLength":1,"description":"The name of the company or organization."},"position":{"type":"string","description":"The position held at the company or organization."},"location":{"type":"string","description":"The location of the company or organization."},"period":{"type":"string","description":"The period of time the author was employed at the company or organization."},"website":{"type":"object","properties":{"url":{"type":"string","description":"The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."},"label":{"type":"string","description":"The label to display for the URL. Leave blank to display the URL as-is."}},"required":["url","label"],"description":"The website of the company or organization, if any."},"description":{"type":"string","description":"The description of the experience. This should be a HTML-formatted string."}},"required":["id","hidden","company","position","location","period","website","description"]},"description":"The items to display in the experience section."}},"required":["title","columns","hidden","items"],"description":"The section to display the experience of the author."},"education":{"type":"object","properties":{"title":{"type":"string","description":"The title of the section."},"columns":{"type":"number","description":"The number of columns the section should span across."},"hidden":{"type":"boolean","description":"Whether to hide the section from the resume."},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"options":{"type":"object","properties":{"showLinkInTitle":{"type":"boolean","description":"If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."}},"required":["showLinkInTitle"],"description":"Display options for this item."},"school":{"type":"string","minLength":1,"description":"The name of the school or institution."},"degree":{"type":"string","description":"The degree or qualification obtained."},"area":{"type":"string","description":"The area of study or specialization."},"grade":{"type":"string","description":"The grade or score achieved."},"location":{"type":"string","description":"The location of the school or institution."},"period":{"type":"string","description":"The period of time the education was obtained over."},"website":{"type":"object","properties":{"url":{"type":"string","description":"The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."},"label":{"type":"string","description":"The label to display for the URL. Leave blank to display the URL as-is."}},"required":["url","label"],"description":"The website of the school or institution, if any."},"description":{"type":"string","description":"The description of the education. This should be a HTML-formatted string."}},"required":["id","hidden","school","degree","area","grade","location","period","website","description"]},"description":"The items to display in the education section."}},"required":["title","columns","hidden","items"],"description":"The section to display the education of the author."},"projects":{"type":"object","properties":{"title":{"type":"string","description":"The title of the section."},"columns":{"type":"number","description":"The number of columns the section should span across."},"hidden":{"type":"boolean","description":"Whether to hide the section from the resume."},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"options":{"type":"object","properties":{"showLinkInTitle":{"type":"boolean","description":"If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."}},"required":["showLinkInTitle"],"description":"Display options for this item."},"name":{"type":"string","minLength":1,"description":"The name of the project."},"period":{"type":"string","description":"The period of time the project was worked on."},"website":{"type":"object","properties":{"url":{"type":"string","description":"The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."},"label":{"type":"string","description":"The label to display for the URL. Leave blank to display the URL as-is."}},"required":["url","label"],"description":"The link to the project, if any."},"description":{"type":"string","description":"The description of the project. This should be a HTML-formatted string."}},"required":["id","hidden","name","period","website","description"]},"description":"The items to display in the projects section."}},"required":["title","columns","hidden","items"],"description":"The section to display the projects of the author."},"skills":{"type":"object","properties":{"title":{"type":"string","description":"The title of the section."},"columns":{"type":"number","description":"The number of columns the section should span across."},"hidden":{"type":"boolean","description":"Whether to hide the section from the resume."},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"options":{"type":"object","properties":{"showLinkInTitle":{"type":"boolean","description":"If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."}},"required":["showLinkInTitle"],"description":"Display options for this item."},"icon":{"type":"string","description":"The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."},"name":{"type":"string","minLength":1,"description":"The name of the skill."},"proficiency":{"type":"string","description":"The proficiency level of the skill. Can be any text, such as 'Beginner', 'Intermediate', 'Advanced', etc."},"level":{"type":"number","minimum":0,"maximum":5,"description":"The proficiency level of the skill, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden."},"keywords":{"type":"array","items":{"type":"string"},"description":"The keywords associated with the skill, if any. These are displayed as tags below the name."}},"required":["id","hidden","icon","name","proficiency","level","keywords"]},"description":"The items to display in the skills section."}},"required":["title","columns","hidden","items"],"description":"The section to display the skills of the author."},"languages":{"type":"object","properties":{"title":{"type":"string","description":"The title of the section."},"columns":{"type":"number","description":"The number of columns the section should span across."},"hidden":{"type":"boolean","description":"Whether to hide the section from the resume."},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"options":{"type":"object","properties":{"showLinkInTitle":{"type":"boolean","description":"If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."}},"required":["showLinkInTitle"],"description":"Display options for this item."},"language":{"type":"string","minLength":1,"description":"The name of the language the author knows."},"fluency":{"type":"string","description":"The fluency level of the language. Can be any text, such as 'Native', 'Fluent', 'Conversational', etc. or can also be a CEFR level (A1, A2, B1, B2, C1, C2)."},"level":{"type":"number","minimum":0,"maximum":5,"description":"The proficiency level of the language, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden."}},"required":["id","hidden","language","fluency","level"]},"description":"The items to display in the languages section."}},"required":["title","columns","hidden","items"],"description":"The section to display the languages of the author."},"interests":{"type":"object","properties":{"title":{"type":"string","description":"The title of the section."},"columns":{"type":"number","description":"The number of columns the section should span across."},"hidden":{"type":"boolean","description":"Whether to hide the section from the resume."},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"options":{"type":"object","properties":{"showLinkInTitle":{"type":"boolean","description":"If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."}},"required":["showLinkInTitle"],"description":"Display options for this item."},"icon":{"type":"string","description":"The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."},"name":{"type":"string","minLength":1,"description":"The name of the interest/hobby."},"keywords":{"type":"array","items":{"type":"string"},"description":"The keywords associated with the interest/hobby, if any. These are displayed as tags below the name."}},"required":["id","hidden","icon","name","keywords"]},"description":"The items to display in the interests section."}},"required":["title","columns","hidden","items"],"description":"The section to display the interests of the author."},"awards":{"type":"object","properties":{"title":{"type":"string","description":"The title of the section."},"columns":{"type":"number","description":"The number of columns the section should span across."},"hidden":{"type":"boolean","description":"Whether to hide the section from the resume."},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"options":{"type":"object","properties":{"showLinkInTitle":{"type":"boolean","description":"If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."}},"required":["showLinkInTitle"],"description":"Display options for this item."},"title":{"type":"string","minLength":1,"description":"The title of the award."},"awarder":{"type":"string","description":"The awarder of the award."},"date":{"type":"string","description":"The date when the award was received."},"website":{"type":"object","properties":{"url":{"type":"string","description":"The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."},"label":{"type":"string","description":"The label to display for the URL. Leave blank to display the URL as-is."}},"required":["url","label"],"description":"The website of the award, if any."},"description":{"type":"string","description":"The description of the award. This should be a HTML-formatted string."}},"required":["id","hidden","title","awarder","date","website","description"]},"description":"The items to display in the awards section."}},"required":["title","columns","hidden","items"],"description":"The section to display the awards of the author."},"certifications":{"type":"object","properties":{"title":{"type":"string","description":"The title of the section."},"columns":{"type":"number","description":"The number of columns the section should span across."},"hidden":{"type":"boolean","description":"Whether to hide the section from the resume."},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"options":{"type":"object","properties":{"showLinkInTitle":{"type":"boolean","description":"If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."}},"required":["showLinkInTitle"],"description":"Display options for this item."},"title":{"type":"string","minLength":1,"description":"The title of the certification."},"issuer":{"type":"string","description":"The issuer of the certification."},"date":{"type":"string","description":"The date when the certification was received."},"website":{"type":"object","properties":{"url":{"type":"string","description":"The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."},"label":{"type":"string","description":"The label to display for the URL. Leave blank to display the URL as-is."}},"required":["url","label"],"description":"The website of the certification, if any."},"description":{"type":"string","description":"The description of the certification. This should be a HTML-formatted string."}},"required":["id","hidden","title","issuer","date","website","description"]},"description":"The items to display in the certifications section."}},"required":["title","columns","hidden","items"],"description":"The section to display the certifications of the author."},"publications":{"type":"object","properties":{"title":{"type":"string","description":"The title of the section."},"columns":{"type":"number","description":"The number of columns the section should span across."},"hidden":{"type":"boolean","description":"Whether to hide the section from the resume."},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"options":{"type":"object","properties":{"showLinkInTitle":{"type":"boolean","description":"If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."}},"required":["showLinkInTitle"],"description":"Display options for this item."},"title":{"type":"string","minLength":1,"description":"The title of the publication."},"publisher":{"type":"string","description":"The publisher of the publication."},"date":{"type":"string","description":"The date when the publication was published."},"website":{"type":"object","properties":{"url":{"type":"string","description":"The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."},"label":{"type":"string","description":"The label to display for the URL. Leave blank to display the URL as-is."}},"required":["url","label"],"description":"The link to the publication, if any."},"description":{"type":"string","description":"The description of the publication. This should be a HTML-formatted string."}},"required":["id","hidden","title","publisher","date","website","description"]},"description":"The items to display in the publications section."}},"required":["title","columns","hidden","items"],"description":"The section to display the publications of the author."},"volunteer":{"type":"object","properties":{"title":{"type":"string","description":"The title of the section."},"columns":{"type":"number","description":"The number of columns the section should span across."},"hidden":{"type":"boolean","description":"Whether to hide the section from the resume."},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"options":{"type":"object","properties":{"showLinkInTitle":{"type":"boolean","description":"If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."}},"required":["showLinkInTitle"],"description":"Display options for this item."},"organization":{"type":"string","minLength":1,"description":"The name of the organization or company."},"location":{"type":"string","description":"The location of the organization or company."},"period":{"type":"string","description":"The period of time the author was volunteered at the organization or company."},"website":{"type":"object","properties":{"url":{"type":"string","description":"The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."},"label":{"type":"string","description":"The label to display for the URL. Leave blank to display the URL as-is."}},"required":["url","label"],"description":"The link to the organization or company, if any."},"description":{"type":"string","description":"The description of the volunteer experience. This should be a HTML-formatted string."}},"required":["id","hidden","organization","location","period","website","description"]},"description":"The items to display in the volunteer section."}},"required":["title","columns","hidden","items"],"description":"The section to display the volunteer experience of the author."},"references":{"type":"object","properties":{"title":{"type":"string","description":"The title of the section."},"columns":{"type":"number","description":"The number of columns the section should span across."},"hidden":{"type":"boolean","description":"Whether to hide the section from the resume."},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"options":{"type":"object","properties":{"showLinkInTitle":{"type":"boolean","description":"If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."}},"required":["showLinkInTitle"],"description":"Display options for this item."},"name":{"type":"string","minLength":1,"description":"The name of the reference, or a note such as 'Available upon request'."},"position":{"type":"string","description":"The position or job title of the reference."},"website":{"type":"object","properties":{"url":{"type":"string","description":"The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."},"label":{"type":"string","description":"The label to display for the URL. Leave blank to display the URL as-is."}},"required":["url","label"],"description":"The website or LinkedIn profile of the reference, if any."},"phone":{"type":"string","description":"The phone number of the reference."},"description":{"type":"string","description":"The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string."}},"required":["id","hidden","name","position","website","phone","description"]},"description":"The items to display in the references section."}},"required":["title","columns","hidden","items"],"description":"The section to display the references of the author."}},"required":["profiles","experience","education","projects","skills","languages","interests","awards","certifications","publications","volunteer","references"],"description":"Various sections of the resume, such as experience, education, projects, etc."},"customSections":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string","description":"The title of the section."},"columns":{"type":"number","description":"The number of columns the section should span across."},"hidden":{"type":"boolean","description":"Whether to hide the section from the resume."},"id":{"type":"string","description":"The unique identifier for the custom section. Usually generated as a UUID."},"type":{"enum":["summary","profiles","experience","education","projects","skills","languages","interests","awards","certifications","publications","volunteer","references","cover-letter"],"description":"The type of items this custom section contains. Determines which item schema and form fields to use."},"items":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"options":{"type":"object","properties":{"showLinkInTitle":{"type":"boolean","description":"If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."}},"required":["showLinkInTitle"],"description":"Display options for this item."},"recipient":{"type":"string","description":"The recipient's address block as HTML (name, title, company, address, email)."},"content":{"type":"string","description":"The cover letter body as HTML (salutation, paragraphs, closing, signature)."}},"required":["id","hidden","recipient","content"]},{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"options":{"type":"object","properties":{"showLinkInTitle":{"type":"boolean","description":"If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."}},"required":["showLinkInTitle"],"description":"Display options for this item."},"content":{"type":"string","description":"The rich text content of the summary item. This should be a HTML-formatted string."}},"required":["id","hidden","content"]},{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"options":{"type":"object","properties":{"showLinkInTitle":{"type":"boolean","description":"If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."}},"required":["showLinkInTitle"],"description":"Display options for this item."},"icon":{"type":"string","description":"The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."},"network":{"type":"string","minLength":1,"description":"The name of the network or platform."},"username":{"type":"string","description":"The username of the author on the network or platform."},"website":{"type":"object","properties":{"url":{"type":"string","description":"The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."},"label":{"type":"string","description":"The label to display for the URL. Leave blank to display the URL as-is."}},"required":["url","label"],"description":"The link to the profile of the author on the network or platform, if any."}},"required":["id","hidden","icon","network","username","website"]},{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"options":{"type":"object","properties":{"showLinkInTitle":{"type":"boolean","description":"If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."}},"required":["showLinkInTitle"],"description":"Display options for this item."},"company":{"type":"string","minLength":1,"description":"The name of the company or organization."},"position":{"type":"string","description":"The position held at the company or organization."},"location":{"type":"string","description":"The location of the company or organization."},"period":{"type":"string","description":"The period of time the author was employed at the company or organization."},"website":{"type":"object","properties":{"url":{"type":"string","description":"The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."},"label":{"type":"string","description":"The label to display for the URL. Leave blank to display the URL as-is."}},"required":["url","label"],"description":"The website of the company or organization, if any."},"description":{"type":"string","description":"The description of the experience. This should be a HTML-formatted string."}},"required":["id","hidden","company","position","location","period","website","description"]},{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"options":{"type":"object","properties":{"showLinkInTitle":{"type":"boolean","description":"If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."}},"required":["showLinkInTitle"],"description":"Display options for this item."},"school":{"type":"string","minLength":1,"description":"The name of the school or institution."},"degree":{"type":"string","description":"The degree or qualification obtained."},"area":{"type":"string","description":"The area of study or specialization."},"grade":{"type":"string","description":"The grade or score achieved."},"location":{"type":"string","description":"The location of the school or institution."},"period":{"type":"string","description":"The period of time the education was obtained over."},"website":{"type":"object","properties":{"url":{"type":"string","description":"The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."},"label":{"type":"string","description":"The label to display for the URL. Leave blank to display the URL as-is."}},"required":["url","label"],"description":"The website of the school or institution, if any."},"description":{"type":"string","description":"The description of the education. This should be a HTML-formatted string."}},"required":["id","hidden","school","degree","area","grade","location","period","website","description"]},{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"options":{"type":"object","properties":{"showLinkInTitle":{"type":"boolean","description":"If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."}},"required":["showLinkInTitle"],"description":"Display options for this item."},"name":{"type":"string","minLength":1,"description":"The name of the project."},"period":{"type":"string","description":"The period of time the project was worked on."},"website":{"type":"object","properties":{"url":{"type":"string","description":"The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."},"label":{"type":"string","description":"The label to display for the URL. Leave blank to display the URL as-is."}},"required":["url","label"],"description":"The link to the project, if any."},"description":{"type":"string","description":"The description of the project. This should be a HTML-formatted string."}},"required":["id","hidden","name","period","website","description"]},{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"options":{"type":"object","properties":{"showLinkInTitle":{"type":"boolean","description":"If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."}},"required":["showLinkInTitle"],"description":"Display options for this item."},"icon":{"type":"string","description":"The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."},"name":{"type":"string","minLength":1,"description":"The name of the skill."},"proficiency":{"type":"string","description":"The proficiency level of the skill. Can be any text, such as 'Beginner', 'Intermediate', 'Advanced', etc."},"level":{"type":"number","minimum":0,"maximum":5,"description":"The proficiency level of the skill, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden."},"keywords":{"type":"array","items":{"type":"string"},"description":"The keywords associated with the skill, if any. These are displayed as tags below the name."}},"required":["id","hidden","icon","name","proficiency","level","keywords"]},{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"options":{"type":"object","properties":{"showLinkInTitle":{"type":"boolean","description":"If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."}},"required":["showLinkInTitle"],"description":"Display options for this item."},"language":{"type":"string","minLength":1,"description":"The name of the language the author knows."},"fluency":{"type":"string","description":"The fluency level of the language. Can be any text, such as 'Native', 'Fluent', 'Conversational', etc. or can also be a CEFR level (A1, A2, B1, B2, C1, C2)."},"level":{"type":"number","minimum":0,"maximum":5,"description":"The proficiency level of the language, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden."}},"required":["id","hidden","language","fluency","level"]},{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"options":{"type":"object","properties":{"showLinkInTitle":{"type":"boolean","description":"If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."}},"required":["showLinkInTitle"],"description":"Display options for this item."},"icon":{"type":"string","description":"The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."},"name":{"type":"string","minLength":1,"description":"The name of the interest/hobby."},"keywords":{"type":"array","items":{"type":"string"},"description":"The keywords associated with the interest/hobby, if any. These are displayed as tags below the name."}},"required":["id","hidden","icon","name","keywords"]},{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"options":{"type":"object","properties":{"showLinkInTitle":{"type":"boolean","description":"If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."}},"required":["showLinkInTitle"],"description":"Display options for this item."},"title":{"type":"string","minLength":1,"description":"The title of the award."},"awarder":{"type":"string","description":"The awarder of the award."},"date":{"type":"string","description":"The date when the award was received."},"website":{"type":"object","properties":{"url":{"type":"string","description":"The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."},"label":{"type":"string","description":"The label to display for the URL. Leave blank to display the URL as-is."}},"required":["url","label"],"description":"The website of the award, if any."},"description":{"type":"string","description":"The description of the award. This should be a HTML-formatted string."}},"required":["id","hidden","title","awarder","date","website","description"]},{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"options":{"type":"object","properties":{"showLinkInTitle":{"type":"boolean","description":"If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."}},"required":["showLinkInTitle"],"description":"Display options for this item."},"title":{"type":"string","minLength":1,"description":"The title of the certification."},"issuer":{"type":"string","description":"The issuer of the certification."},"date":{"type":"string","description":"The date when the certification was received."},"website":{"type":"object","properties":{"url":{"type":"string","description":"The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."},"label":{"type":"string","description":"The label to display for the URL. Leave blank to display the URL as-is."}},"required":["url","label"],"description":"The website of the certification, if any."},"description":{"type":"string","description":"The description of the certification. This should be a HTML-formatted string."}},"required":["id","hidden","title","issuer","date","website","description"]},{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"options":{"type":"object","properties":{"showLinkInTitle":{"type":"boolean","description":"If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."}},"required":["showLinkInTitle"],"description":"Display options for this item."},"title":{"type":"string","minLength":1,"description":"The title of the publication."},"publisher":{"type":"string","description":"The publisher of the publication."},"date":{"type":"string","description":"The date when the publication was published."},"website":{"type":"object","properties":{"url":{"type":"string","description":"The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."},"label":{"type":"string","description":"The label to display for the URL. Leave blank to display the URL as-is."}},"required":["url","label"],"description":"The link to the publication, if any."},"description":{"type":"string","description":"The description of the publication. This should be a HTML-formatted string."}},"required":["id","hidden","title","publisher","date","website","description"]},{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"options":{"type":"object","properties":{"showLinkInTitle":{"type":"boolean","description":"If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."}},"required":["showLinkInTitle"],"description":"Display options for this item."},"organization":{"type":"string","minLength":1,"description":"The name of the organization or company."},"location":{"type":"string","description":"The location of the organization or company."},"period":{"type":"string","description":"The period of time the author was volunteered at the organization or company."},"website":{"type":"object","properties":{"url":{"type":"string","description":"The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."},"label":{"type":"string","description":"The label to display for the URL. Leave blank to display the URL as-is."}},"required":["url","label"],"description":"The link to the organization or company, if any."},"description":{"type":"string","description":"The description of the volunteer experience. This should be a HTML-formatted string."}},"required":["id","hidden","organization","location","period","website","description"]},{"type":"object","properties":{"id":{"type":"string","description":"The unique identifier for the item. Usually generated as a UUID."},"hidden":{"type":"boolean","description":"Whether to hide the item from the resume."},"options":{"type":"object","properties":{"showLinkInTitle":{"type":"boolean","description":"If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."}},"required":["showLinkInTitle"],"description":"Display options for this item."},"name":{"type":"string","minLength":1,"description":"The name of the reference, or a note such as 'Available upon request'."},"position":{"type":"string","description":"The position or job title of the reference."},"website":{"type":"object","properties":{"url":{"type":"string","description":"The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."},"label":{"type":"string","description":"The label to display for the URL. Leave blank to display the URL as-is."}},"required":["url","label"],"description":"The website or LinkedIn profile of the reference, if any."},"phone":{"type":"string","description":"The phone number of the reference."},"description":{"type":"string","description":"The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string."}},"required":["id","hidden","name","position","website","phone","description"]}]},"description":"The items to display in the custom section. Items follow the schema of the section type."}},"required":["title","columns","hidden","id","type","items"]},"description":"Custom sections of the resume, such as a custom section for notes, etc."},"metadata":{"type":"object","properties":{"template":{"enum":["azurill","bronzor","chikorita","ditgar","ditto","gengar","glalie","kakuna","lapras","leafish","onyx","pikachu","rhyhorn"],"description":"The template to use for the resume. Determines the overall design and appearance of the resume."},"layout":{"type":"object","properties":{"sidebarWidth":{"type":"number","minimum":10,"maximum":50,"description":"The width of the sidebar column, defined as a percentage of the page width."},"pages":{"type":"array","items":{"type":"object","properties":{"fullWidth":{"type":"boolean","description":"Whether the layout of the page should be full width. If true, the main column will span the entire width of the page. This means that there should be no items in the sidebar column."},"main":{"type":"array","items":{"type":"string"},"description":"The items to display in the main column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)."},"sidebar":{"type":"array","items":{"type":"string"},"description":"The items to display in the sidebar column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)."}},"required":["fullWidth","main","sidebar"]},"description":"The pages to display in the layout."}},"required":["sidebarWidth","pages"],"description":"The layout of the resume. Determines the structure and arrangement of the sections on the resume."},"css":{"type":"object","properties":{"enabled":{"type":"boolean","description":"Whether to enable custom CSS for the resume."},"value":{"type":"string","description":"The custom CSS to apply to the resume. This should be a valid CSS string."}},"required":["enabled","value"],"description":"Custom CSS to apply to the resume. Can be used to override the default styles of the template."},"page":{"type":"object","properties":{"gapX":{"type":"number","minimum":0,"description":"The horizontal gap between the sections of the page, defined in points (pt)."},"gapY":{"type":"number","minimum":0,"description":"The vertical gap between the sections of the page, defined in points (pt)."},"marginX":{"type":"number","minimum":0,"description":"The horizontal margin of the page, defined in points (pt)."},"marginY":{"type":"number","minimum":0,"description":"The vertical margin of the page, defined in points (pt)."},"format":{"enum":["a4","letter","free-form"],"description":"The format of the page. Can be 'a4', 'letter' or 'free-form'."},"locale":{"type":"string","description":"The locale of the page. Used for displaying pre-translated section headings, if not overridden."},"hideIcons":{"type":"boolean","description":"Whether to hide the icons of the sections."}},"required":["gapX","gapY","marginX","marginY","format","locale","hideIcons"],"description":"The page settings of the resume. Determines the margins, format, and locale of the resume."},"design":{"type":"object","properties":{"level":{"type":"object","properties":{"icon":{"type":"string","description":"The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."},"type":{"enum":["hidden","circle","square","rectangle","rectangle-full","progress-bar","icon"],"description":"The type of the level design. 'hidden' will hide the level design, 'circle' will display a circle, 'square' will display a square, 'rectangle' will display a rectangle, 'rectangle-full' will display a full rectangle, 'progress-bar' will display a progress bar, and 'icon' will display an icon. If 'icon' is selected, the icon to display should be specified in the 'icon' field."}},"required":["icon","type"]},"colors":{"type":"object","properties":{"primary":{"type":"string","description":"The primary color of the design, defined as rgba(r, g, b, a)."},"text":{"type":"string","description":"The text color of the design, defined as rgba(r, g, b, a). Usually set to black: rgba(0, 0, 0, 1)."},"background":{"type":"string","description":"The background color of the design, defined as rgba(r, g, b, a). Usually set to white: rgba(255, 255, 255, 1)."}},"required":["primary","text","background"]}},"required":["level","colors"],"description":"The design settings of the resume. Determines the colors, level designs, and typography of the resume."},"typography":{"type":"object","properties":{"body":{"type":"object","properties":{"fontFamily":{"type":"string","description":"The family of the font to use. Must be a font that is available on Google Fonts."},"fontWeights":{"type":"array","items":{"enum":["100","200","300","400","500","600","700","800","900"]},"description":"The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font."},"fontSize":{"type":"number","minimum":6,"maximum":24,"description":"The size of the font to use, defined in points (pt)."},"lineHeight":{"type":"number","minimum":0.5,"maximum":4,"description":"The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)."}},"required":["fontFamily","fontWeights","fontSize","lineHeight"],"description":"The typography for the body of the resume."},"heading":{"type":"object","properties":{"fontFamily":{"type":"string","description":"The family of the font to use. Must be a font that is available on Google Fonts."},"fontWeights":{"type":"array","items":{"enum":["100","200","300","400","500","600","700","800","900"]},"description":"The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font."},"fontSize":{"type":"number","minimum":6,"maximum":24,"description":"The size of the font to use, defined in points (pt)."},"lineHeight":{"type":"number","minimum":0.5,"maximum":4,"description":"The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)."}},"required":["fontFamily","fontWeights","fontSize","lineHeight"],"description":"The typography for the headings of the resume."}},"required":["body","heading"],"description":"The typography settings of the resume. Determines the fonts and sizes of the body and headings of the resume."},"notes":{"type":"string","description":"Personal notes for the resume. Can be used to add any additional information or instructions for the resume. These notes are not displayed on the resume, they are only visible to the author of the resume when editing the resume. This should be a HTML-formatted string."}},"required":["template","layout","css","page","design","typography","notes"],"description":"Metadata for the resume, such as template, layout, typography, etc. This section describes the overall design and appearance of the resume."}},"required":["picture","basics","summary","sections","customSections","metadata"]}}},"security":[{"apiKey":[]}],"openapi":"3.1.1","paths":{"/ai/test-connection":{"post":{"operationId":"testAiConnection","summary":"Test AI provider connection","description":"Validates the connection to an AI provider by sending a simple test prompt. Requires the provider type, model name, API key, and an optional base URL. Supported providers: OpenAI, Anthropic, Google Gemini, Ollama, and Vercel AI Gateway. Requires authentication.","tags":["AI"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"provider":{"enum":["ollama","openai","gemini","anthropic","vercel-ai-gateway"]},"model":{"type":"string"},"apiKey":{"type":"string"},"baseURL":{"type":"string"}},"required":["provider","model","apiKey","baseURL"]}}}},"responses":{"200":{"description":"The AI provider connection was successful.","content":{"application/json":{"schema":{"anyOf":[{},{"not":{}}]}}}},"502":{"description":"502","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"defined":{"const":true},"code":{"const":"BAD_GATEWAY"},"status":{"const":502},"message":{"type":"string","default":"The AI provider returned an error or is unreachable."},"data":{}},"required":["defined","code","status","message"]},{"type":"object","properties":{"defined":{"const":false},"code":{"type":"string"},"status":{"type":"number"},"message":{"type":"string"},"data":{}},"required":["defined","code","status","message"]}]}}}}}}},"/ai/parse-pdf":{"post":{"operationId":"parseResumePdf","summary":"Parse a PDF file into resume data","description":"Extracts structured resume data from a PDF file using the specified AI provider. The file should be sent as a base64-encoded string along with AI provider credentials. Returns a complete ResumeData object. Requires authentication.","tags":["AI"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"provider":{"enum":["ollama","openai","gemini","anthropic","vercel-ai-gateway"]},"model":{"type":"string"},"apiKey":{"type":"string"},"baseURL":{"type":"string"},"file":{"type":"object","properties":{"name":{"type":"string"},"data":{"type":"string"}},"required":["name","data"]}},"required":["provider","model","apiKey","baseURL","file"]}}}},"responses":{"200":{"description":"The PDF was successfully parsed into structured resume data.","content":{"application/json":{"schema":{"anyOf":[{},{"not":{}}]}}}},"502":{"description":"502","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"defined":{"const":true},"code":{"const":"BAD_GATEWAY"},"status":{"const":502},"message":{"type":"string","default":"The AI provider returned an error or is unreachable."},"data":{}},"required":["defined","code","status","message"]},{"type":"object","properties":{"defined":{"const":false},"code":{"type":"string"},"status":{"type":"number"},"message":{"type":"string"},"data":{}},"required":["defined","code","status","message"]}]}}}}}}},"/ai/parse-docx":{"post":{"operationId":"parseResumeDocx","summary":"Parse a DOCX file into resume data","description":"Extracts structured resume data from a DOCX or DOC file using the specified AI provider. The file should be sent as a base64-encoded string along with AI provider credentials and the document's media type. Returns a complete ResumeData object. Requires authentication.","tags":["AI"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"provider":{"enum":["ollama","openai","gemini","anthropic","vercel-ai-gateway"]},"model":{"type":"string"},"apiKey":{"type":"string"},"baseURL":{"type":"string"},"file":{"type":"object","properties":{"name":{"type":"string"},"data":{"type":"string"}},"required":["name","data"]},"mediaType":{"enum":["application/msword","application/vnd.openxmlformats-officedocument.wordprocessingml.document"]}},"required":["provider","model","apiKey","baseURL","file","mediaType"]}}}},"responses":{"200":{"description":"The DOCX was successfully parsed into structured resume data.","content":{"application/json":{"schema":{"anyOf":[{},{"not":{}}]}}}},"502":{"description":"502","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"defined":{"const":true},"code":{"const":"BAD_GATEWAY"},"status":{"const":502},"message":{"type":"string","default":"The AI provider returned an error or is unreachable."},"data":{}},"required":["defined","code","status","message"]},{"type":"object","properties":{"defined":{"const":false},"code":{"type":"string"},"status":{"type":"number"},"message":{"type":"string"},"data":{}},"required":["defined","code","status","message"]}]}}}}}}},"/ai/chat":{"post":{"operationId":"aiChat","summary":"Chat with AI to modify resume","description":"Streams a chat response from the configured AI provider. The LLM can call the patch_resume tool to generate JSON Patch operations that modify the resume. Requires authentication and AI provider credentials.","tags":["AI"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"anyOf":[{},{"not":{}}]}}}}}}},"/auth/providers":{"get":{"operationId":"listAuthProviders","summary":"List authentication providers","description":"Returns a list of all authentication providers enabled on this Reactive Resume instance, along with their display names. Possible providers include password-based credentials, Google, GitHub, and custom OAuth. No authentication required.","tags":["Authentication"],"responses":{"200":{"description":"A map of enabled authentication provider identifiers to their display names.","content":{"application/json":{"schema":{"anyOf":[{},{"not":{}}]}}}}}}},"/auth/account":{"delete":{"operationId":"deleteAccount","summary":"Delete user account","description":"Permanently deletes the authenticated user's account, including all resumes, uploaded files (profile pictures, screenshots, PDFs), and associated data. This action is irreversible. Requires authentication.","tags":["Authentication"],"responses":{"200":{"description":"The user account and all associated data have been successfully deleted.","content":{"application/json":{"schema":{"anyOf":[{},{"not":{}}]}}}}}}},"/flags":{"get":{"operationId":"getFeatureFlags","summary":"Get feature flags","description":"Returns the current feature flags for this Reactive Resume instance. Feature flags control instance-wide settings such as whether new user signups or email-based authentication are disabled. No authentication required.","tags":["Feature Flags"],"responses":{"200":{"description":"The current feature flags for this instance.","content":{"application/json":{"schema":{"type":"object","properties":{"disableSignups":{"type":"boolean","description":"Whether new user signups are disabled on this instance."},"disableEmailAuth":{"type":"boolean","description":"Whether email-based authentication is disabled on this instance."}},"required":["disableSignups","disableEmailAuth"]}}}}}}},"/resumes/tags":{"get":{"operationId":"listResumeTags","summary":"List all resume tags","description":"Returns a sorted list of all unique tags across the authenticated user's resumes. Useful for populating tag filters in the dashboard. Requires authentication.","tags":["Resumes"],"responses":{"200":{"description":"A sorted array of unique tag strings.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}}}}},"/resumes/{id}/statistics":{"get":{"operationId":"getResumeStatistics","summary":"Get resume statistics","description":"Returns view and download statistics for the specified resume, including total counts and the timestamps of the last view and download. Requires authentication.","tags":["Resume Statistics"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"The unique identifier of the resume."}}],"responses":{"200":{"description":"The resume's view and download statistics.","content":{"application/json":{"schema":{"type":"object","properties":{"isPublic":{"type":"boolean","description":"Whether the resume is currently public."},"views":{"type":"number","description":"Total number of times the resume has been viewed."},"downloads":{"type":"number","description":"Total number of times the resume has been downloaded."},"lastViewedAt":{"anyOf":[{"type":"string","format":"date-time","x-native-type":"date"},{"type":"null"}],"description":"Timestamp of the last view, or null if never viewed."},"lastDownloadedAt":{"anyOf":[{"type":"string","format":"date-time","x-native-type":"date"},{"type":"null"}],"description":"Timestamp of the last download, or null if never downloaded."}},"required":["isPublic","views","downloads","lastViewedAt","lastDownloadedAt"]}}}}}}},"/resumes":{"get":{"operationId":"listResumes","summary":"List all resumes","description":"Returns a list of all resumes belonging to the authenticated user. Results can be filtered by tags and sorted by last updated date, creation date, or name. Resume data is not included in the response for performance; use the get endpoint to fetch full resume data. Requires authentication.","tags":["Resumes"],"parameters":[{"name":"tags","in":"query","schema":{"type":"array","items":{"type":"string"},"default":[]},"style":"deepObject","explode":true,"allowEmptyValue":true,"allowReserved":true},{"name":"sort","in":"query","schema":{"enum":["lastUpdatedAt","createdAt","name"],"default":"lastUpdatedAt"},"style":"deepObject","explode":true,"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"A list of resumes with their metadata (without full resume data).","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The ID of the resume."},"name":{"type":"string","minLength":1,"maxLength":64,"description":"The name of the resume."},"slug":{"type":"string","minLength":1,"maxLength":64,"description":"The slug of the resume."},"tags":{"type":"array","items":{"type":"string"},"description":"The tags of the resume."},"isPublic":{"type":"boolean","description":"Whether the resume is public."},"isLocked":{"type":"boolean","description":"Whether the resume is locked."},"createdAt":{"type":"string","format":"date-time","x-native-type":"date","description":"The date and time the resume was created."},"updatedAt":{"type":"string","format":"date-time","x-native-type":"date","description":"The date and time the resume was last updated."}},"required":["id","name","slug","tags","isPublic","isLocked","createdAt","updatedAt"]}}}}}}},"post":{"operationId":"createResume","summary":"Create a new resume","description":"Creates a new resume with the given name, slug, and tags. Optionally initializes the resume with sample data by setting withSampleData to true. The slug must be unique across the user's resumes. Returns the ID of the newly created resume. Requires authentication.","tags":["Resumes"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":64,"description":"The name of the resume."},"slug":{"type":"string","minLength":1,"maxLength":64,"description":"The slug of the resume."},"tags":{"type":"array","items":{"type":"string"},"description":"The tags of the resume."},"withSampleData":{"type":"boolean","default":false}},"required":["name","slug","tags"]}}}},"responses":{"200":{"description":"The ID of the newly created resume.","content":{"application/json":{"schema":{"type":"string","description":"The ID of the created resume."}}}},"400":{"description":"400","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"defined":{"const":true},"code":{"const":"RESUME_SLUG_ALREADY_EXISTS"},"status":{"const":400},"message":{"type":"string","default":"A resume with this slug already exists."},"data":{}},"required":["defined","code","status","message"]},{"type":"object","properties":{"defined":{"const":false},"code":{"type":"string"},"status":{"type":"number"},"message":{"type":"string"},"data":{}},"required":["defined","code","status","message"]}]}}}}}}},"/resumes/{id}":{"get":{"operationId":"getResume","summary":"Get resume by ID","description":"Returns a single resume with its full data, identified by its unique ID. Only resumes belonging to the authenticated user can be retrieved. Requires authentication.","tags":["Resumes"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"The ID of the resume."}}],"responses":{"200":{"description":"The resume with its full data.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"The ID of the resume."},"name":{"type":"string","minLength":1,"maxLength":64,"description":"The name of the resume."},"slug":{"type":"string","minLength":1,"maxLength":64,"description":"The slug of the resume."},"tags":{"type":"array","items":{"type":"string"},"description":"The tags of the resume."},"isPublic":{"type":"boolean","description":"Whether the resume is public."},"isLocked":{"type":"boolean","description":"Whether the resume is locked."},"data":{"$ref":"#/components/schemas/ResumeData"},"hasPassword":{"type":"boolean"}},"required":["id","name","slug","tags","isPublic","isLocked","data","hasPassword"]}}}}}},"put":{"operationId":"updateResume","summary":"Update a resume","description":"Updates one or more fields of a resume identified by its ID. All fields are optional; only provided fields will be updated. Locked resumes cannot be updated. Requires authentication.","tags":["Resumes"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":64,"description":"The name of the resume."},"slug":{"type":"string","minLength":1,"maxLength":64,"description":"The slug of the resume."},"tags":{"type":"array","items":{"type":"string"},"description":"The tags of the resume."},"data":{"$ref":"#/components/schemas/ResumeData"},"isPublic":{"type":"boolean","description":"Whether the resume is public."}},"required":[]}}}},"responses":{"200":{"description":"The updated resume with its full data.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"The ID of the resume."},"name":{"type":"string","minLength":1,"maxLength":64,"description":"The name of the resume."},"slug":{"type":"string","minLength":1,"maxLength":64,"description":"The slug of the resume."},"tags":{"type":"array","items":{"type":"string"},"description":"The tags of the resume."},"isPublic":{"type":"boolean","description":"Whether the resume is public."},"isLocked":{"type":"boolean","description":"Whether the resume is locked."},"data":{"$ref":"#/components/schemas/ResumeData"}},"required":["id","name","slug","tags","isPublic","isLocked","data"]}}}},"400":{"description":"400","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"defined":{"const":true},"code":{"const":"RESUME_SLUG_ALREADY_EXISTS"},"status":{"const":400},"message":{"type":"string","default":"A resume with this slug already exists."},"data":{}},"required":["defined","code","status","message"]},{"type":"object","properties":{"defined":{"const":false},"code":{"type":"string"},"status":{"type":"number"},"message":{"type":"string"},"data":{}},"required":["defined","code","status","message"]}]}}}}}},"patch":{"operationId":"patchResume","summary":"Patch resume data","description":"Applies JSON Patch (RFC 6902) operations to partially update a resume's data. This allows small, targeted changes (e.g. updating a single field) without sending the entire resume object. Locked resumes cannot be patched. Requires authentication.","tags":["Resumes"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"The ID of the resume to patch."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"operations":{"type":"array","minItems":1,"items":{"anyOf":[{"type":"object","properties":{"op":{"const":"add"},"path":{"type":"string"},"value":{}},"required":["op","path"]},{"type":"object","properties":{"op":{"const":"remove"},"path":{"type":"string"}},"required":["op","path"]},{"type":"object","properties":{"op":{"const":"replace"},"path":{"type":"string"},"value":{}},"required":["op","path"]},{"type":"object","properties":{"op":{"const":"move"},"path":{"type":"string"},"from":{"type":"string"}},"required":["op","path","from"]},{"type":"object","properties":{"op":{"const":"copy"},"path":{"type":"string"},"from":{"type":"string"}},"required":["op","path","from"]},{"type":"object","properties":{"op":{"const":"test"},"path":{"type":"string"},"value":{}},"required":["op","path"]}]},"description":"An array of JSON Patch (RFC 6902) operations to apply to the resume data."}},"required":["operations"]}}}},"responses":{"200":{"description":"The patched resume with its full data.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"The ID of the resume."},"name":{"type":"string","minLength":1,"maxLength":64,"description":"The name of the resume."},"slug":{"type":"string","minLength":1,"maxLength":64,"description":"The slug of the resume."},"tags":{"type":"array","items":{"type":"string"},"description":"The tags of the resume."},"isPublic":{"type":"boolean","description":"Whether the resume is public."},"isLocked":{"type":"boolean","description":"Whether the resume is locked."},"data":{"$ref":"#/components/schemas/ResumeData"},"hasPassword":{"type":"boolean"}},"required":["id","name","slug","tags","isPublic","isLocked","data","hasPassword"]}}}},"400":{"description":"400","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"defined":{"const":true},"code":{"const":"INVALID_PATCH_OPERATIONS"},"status":{"const":400},"message":{"type":"string","default":"The patch operations are invalid or produced an invalid resume."},"data":{}},"required":["defined","code","status","message"]},{"type":"object","properties":{"defined":{"const":false},"code":{"type":"string"},"status":{"type":"number"},"message":{"type":"string"},"data":{}},"required":["defined","code","status","message"]}]}}}}}},"delete":{"operationId":"deleteResume","summary":"Delete a resume","description":"Permanently deletes a resume and its associated files (screenshots, PDFs) from storage. Locked resumes cannot be deleted; unlock the resume first. Requires authentication.","tags":["Resumes"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"The ID of the resume."}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"required":[]}}}},"responses":{"200":{"description":"The resume and its associated files were deleted successfully.","content":{"application/json":{"schema":{"anyOf":[{"not":{}},{"not":{}}]}}}}}}},"/resumes/{username}/{slug}":{"get":{"operationId":"getResumeBySlug","summary":"Get public resume by username and slug","description":"Returns a publicly shared resume identified by the owner's username and the resume's slug. If the resume is password-protected and the viewer has not yet verified the password, a 401 error with code NEED_PASSWORD is returned. No authentication required for public resumes; if authenticated as the owner, private resumes are also accessible.","tags":["Resume Sharing"],"parameters":[{"name":"username","in":"path","required":true,"schema":{"type":"string"}},{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The public resume with its full data.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"The ID of the resume."},"name":{"type":"string","minLength":1,"maxLength":64,"description":"The name of the resume."},"slug":{"type":"string","minLength":1,"maxLength":64,"description":"The slug of the resume."},"tags":{"type":"array","items":{"type":"string"},"description":"The tags of the resume."},"isPublic":{"type":"boolean","description":"Whether the resume is public."},"isLocked":{"type":"boolean","description":"Whether the resume is locked."},"data":{"$ref":"#/components/schemas/ResumeData"}},"required":["id","name","slug","tags","isPublic","isLocked","data"]}}}}}}},"/resumes/import":{"post":{"operationId":"importResume","summary":"Import a resume","description":"Creates a new resume from an existing ResumeData object (e.g. from a previously exported JSON file). A random name and slug are generated automatically. Returns the ID of the imported resume. Requires authentication.","tags":["Resumes"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ResumeData"}},"required":["data"]}}}},"responses":{"200":{"description":"The ID of the imported resume.","content":{"application/json":{"schema":{"type":"string","description":"The ID of the imported resume."}}}},"400":{"description":"400","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"defined":{"const":true},"code":{"const":"RESUME_SLUG_ALREADY_EXISTS"},"status":{"const":400},"message":{"type":"string","default":"A resume with this slug already exists."},"data":{}},"required":["defined","code","status","message"]},{"type":"object","properties":{"defined":{"const":false},"code":{"type":"string"},"status":{"type":"number"},"message":{"type":"string"},"data":{}},"required":["defined","code","status","message"]}]}}}}}}},"/resumes/{id}/lock":{"post":{"operationId":"setResumeLocked","summary":"Set resume lock status","description":"Toggles the locked status of a resume. When locked, a resume cannot be updated, patched, or deleted. Useful for protecting finalized resumes from accidental edits. Requires authentication.","tags":["Resumes"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"The ID of the resume."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"isLocked":{"type":"boolean","description":"Whether the resume is locked."}},"required":["isLocked"]}}}},"responses":{"200":{"description":"The resume lock status was updated successfully.","content":{"application/json":{"schema":{"anyOf":[{"not":{}},{"not":{}}]}}}}}}},"/resumes/{id}/password":{"put":{"operationId":"setResumePassword","summary":"Set resume password","description":"Sets or updates a password on a resume. When a password is set, viewers of the public resume must enter the password before the resume data is revealed. The password must be between 6 and 64 characters. Requires authentication.","tags":["Resume Sharing"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"The ID of the resume."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"password":{"type":"string","minLength":6,"maxLength":64}},"required":["password"]}}}},"responses":{"200":{"description":"The resume password was set successfully.","content":{"application/json":{"schema":{"anyOf":[{"not":{}},{"not":{}}]}}}}}},"delete":{"operationId":"removeResumePassword","summary":"Remove resume password","description":"Removes password protection from a resume. After removal, the resume (if public) can be viewed without entering a password. Requires authentication.","tags":["Resume Sharing"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"The ID of the resume."}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"required":[]}}}},"responses":{"200":{"description":"The resume password was removed successfully.","content":{"application/json":{"schema":{"anyOf":[{"not":{}},{"not":{}}]}}}}}}},"/resumes/{username}/{slug}/password/verify":{"post":{"operationId":"verifyResumePassword","summary":"Verify resume password","description":"Verifies a password for a password-protected public resume. On success, the viewer is granted access to view the resume data for the duration of their session. No authentication required.","tags":["Resume Sharing"],"parameters":[{"name":"username","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"The username of the resume owner."}},{"name":"slug","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"The slug of the resume."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"password":{"type":"string","minLength":1,"description":"The password to verify."}},"required":["password"]}}}},"responses":{"200":{"description":"The password was verified successfully and access has been granted.","content":{"application/json":{"schema":{"type":"boolean"}}}}}}},"/resumes/{id}/duplicate":{"post":{"operationId":"duplicateResume","summary":"Duplicate a resume","description":"Creates a copy of an existing resume with the same data. Optionally override the name, slug, and tags for the duplicate. If not provided, the original resume's name, slug, and tags are used. Returns the ID of the duplicated resume. Requires authentication.","tags":["Resumes"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"The ID of the resume."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":64,"description":"The name of the resume."},"slug":{"type":"string","minLength":1,"maxLength":64,"description":"The slug of the resume."},"tags":{"type":"array","items":{"type":"string"},"description":"The tags of the resume."}},"required":["name","slug","tags"]}}}},"responses":{"200":{"description":"The ID of the duplicated resume.","content":{"application/json":{"schema":{"type":"string","description":"The ID of the duplicated resume."}}}}}}},"/resumes/{id}/pdf":{"get":{"operationId":"exportResumePdf","summary":"Export resume as PDF","description":"Generates a PDF from the specified resume and uploads it to storage. Returns a URL to download the generated PDF file. If the request is made by an unauthenticated user (e.g. via a public share link), the resume's download count is incremented. Authentication is optional.","tags":["Resume Export"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"The unique identifier of the resume to export."}}],"responses":{"200":{"description":"The PDF was generated successfully. Returns a URL to download the file.","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","description":"The URL to download the generated PDF file."}},"required":["url"]}}}}}}},"/resumes/{id}/screenshot":{"get":{"operationId":"getResumeScreenshot","summary":"Get resume screenshot","description":"Returns a URL to a screenshot image of the first page of the specified resume. Screenshots are cached for up to 6 hours and regenerated automatically when the resume is updated. Returns null if the screenshot cannot be generated. Requires authentication.","tags":["Resume Export"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"The unique identifier of the resume."}}],"responses":{"200":{"description":"The screenshot URL, or null if the screenshot could not be generated.","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The URL to the screenshot image, or null."}},"required":["url"]}}}}}}},"/statistics/users":{"get":{"operationId":"getUserCount","summary":"Get total number of users","description":"Returns the total number of registered users on this Reactive Resume instance. The count is cached for up to 6 hours for performance. No authentication required.","tags":["Platform Statistics"],"responses":{"200":{"description":"The total number of registered users.","content":{"application/json":{"schema":{"type":"number","description":"The total number of registered users."}}}}}}},"/statistics/resumes":{"get":{"operationId":"getResumeCount","summary":"Get total number of resumes","description":"Returns the total number of resumes created on this Reactive Resume instance. The count is cached for up to 6 hours for performance. No authentication required.","tags":["Platform Statistics"],"responses":{"200":{"description":"The total number of resumes created.","content":{"application/json":{"schema":{"type":"number","description":"The total number of resumes created."}}}}}}},"/statistics/github/stars":{"get":{"operationId":"getGitHubStarCount","summary":"Get GitHub star count","description":"Returns the number of GitHub stars for the Reactive Resume repository. The count is cached for up to 6 hours and falls back to a last-known value if the GitHub API is unavailable. No authentication required.","tags":["Platform Statistics"],"responses":{"200":{"description":"The number of GitHub stars for the Reactive Resume repository.","content":{"application/json":{"schema":{"type":"number","description":"The number of GitHub stars."}}}}}}}}}
\ No newline at end of file
+{
+ "info": {
+ "title": "Reactive Resume",
+ "version": "5.0.9",
+ "description": "Reactive Resume API",
+ "license": { "name": "MIT", "url": "https://github.com/amruthpillai/reactive-resume/blob/main/LICENSE" },
+ "contact": { "name": "Amruth Pillai", "email": "hello@amruthpillai.com", "url": "https://amruthpillai.com" }
+ },
+ "servers": [{ "url": "http://localhost:3000/api/openapi" }],
+ "externalDocs": { "url": "https://docs.rxresu.me", "description": "Reactive Resume Documentation" },
+ "components": {
+ "securitySchemes": {
+ "apiKey": {
+ "type": "apiKey",
+ "name": "x-api-key",
+ "in": "header",
+ "description": "The API key to authenticate requests."
+ }
+ },
+ "schemas": {
+ "ResumeData": {
+ "type": "object",
+ "properties": {
+ "picture": {
+ "type": "object",
+ "properties": {
+ "hidden": { "type": "boolean", "description": "Whether to hide the picture from the resume." },
+ "url": {
+ "type": "string",
+ "description": "The URL to the picture to display on the resume. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "size": {
+ "type": "number",
+ "minimum": 32,
+ "maximum": 512,
+ "description": "The size of the picture to display on the resume, defined in points (pt)."
+ },
+ "rotation": {
+ "type": "number",
+ "minimum": 0,
+ "maximum": 360,
+ "description": "The rotation of the picture to display on the resume, defined in degrees (°)."
+ },
+ "aspectRatio": {
+ "type": "number",
+ "minimum": 0.5,
+ "maximum": 2.5,
+ "description": "The aspect ratio of the picture to display on the resume, defined as width / height (e.g. 1.5 for 1.5:1 or 0.5 for 1:2)."
+ },
+ "borderRadius": {
+ "type": "number",
+ "minimum": 0,
+ "maximum": 100,
+ "description": "The border radius of the picture to display on the resume, defined in points (pt)."
+ },
+ "borderColor": {
+ "type": "string",
+ "description": "The color of the border of the picture to display on the resume, defined as rgba(r, g, b, a)."
+ },
+ "borderWidth": {
+ "type": "number",
+ "minimum": 0,
+ "description": "The width of the border of the picture to display on the resume, defined in points (pt)."
+ },
+ "shadowColor": {
+ "type": "string",
+ "description": "The color of the shadow of the picture to display on the resume, defined as rgba(r, g, b, a)."
+ },
+ "shadowWidth": {
+ "type": "number",
+ "minimum": 0,
+ "description": "The width of the shadow of the picture to display on the resume, defined in points (pt)."
+ }
+ },
+ "required": [
+ "hidden",
+ "url",
+ "size",
+ "rotation",
+ "aspectRatio",
+ "borderRadius",
+ "borderColor",
+ "borderWidth",
+ "shadowColor",
+ "shadowWidth"
+ ],
+ "description": "Configuration for photograph displayed on the resume"
+ },
+ "basics": {
+ "type": "object",
+ "properties": {
+ "name": { "type": "string", "description": "The full name of the author of the resume." },
+ "headline": { "type": "string", "description": "The headline of the author of the resume." },
+ "email": { "type": "string", "description": "The email address of the author of the resume." },
+ "phone": { "type": "string", "description": "The phone number of the author of the resume." },
+ "location": { "type": "string", "description": "The location of the author of the resume." },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "description": "The website of the author of the resume."
+ },
+ "customFields": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the custom field. Usually generated as a UUID."
+ },
+ "icon": {
+ "type": "string",
+ "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
+ },
+ "text": { "type": "string", "description": "The text to display for the custom field." },
+ "link": {
+ "type": "string",
+ "description": "If the custom field should be a link, the URL to link to."
+ }
+ },
+ "required": ["id", "icon", "text", "link"]
+ },
+ "description": "The custom fields to display on the resume."
+ }
+ },
+ "required": ["name", "headline", "email", "phone", "location", "website", "customFields"],
+ "description": "Basic information about the author, such as name, email, phone, location, and website"
+ },
+ "summary": {
+ "type": "object",
+ "properties": {
+ "title": { "type": "string", "description": "The title of the summary of the resume." },
+ "columns": { "type": "number", "description": "The number of columns the summary should span across." },
+ "hidden": { "type": "boolean", "description": "Whether to hide the summary from the resume." },
+ "content": {
+ "type": "string",
+ "description": "The content of the summary of the resume. This should be a HTML-formatted string."
+ }
+ },
+ "required": ["title", "columns", "hidden", "content"],
+ "description": "Summary section of the resume, useful for a short bio or introduction"
+ },
+ "sections": {
+ "type": "object",
+ "properties": {
+ "profiles": {
+ "type": "object",
+ "properties": {
+ "title": { "type": "string", "description": "The title of the section." },
+ "columns": {
+ "type": "number",
+ "description": "The number of columns the section should span across."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "options": {
+ "type": "object",
+ "properties": {
+ "showLinkInTitle": {
+ "type": "boolean",
+ "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."
+ }
+ },
+ "required": ["showLinkInTitle"],
+ "description": "Display options for this item."
+ },
+ "icon": {
+ "type": "string",
+ "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
+ },
+ "network": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the network or platform."
+ },
+ "username": {
+ "type": "string",
+ "description": "The username of the author on the network or platform."
+ },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "description": "The link to the profile of the author on the network or platform, if any."
+ }
+ },
+ "required": ["id", "hidden", "icon", "network", "username", "website"]
+ },
+ "description": "The items to display in the profiles section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "description": "The section to display the profiles of the author."
+ },
+ "experience": {
+ "type": "object",
+ "properties": {
+ "title": { "type": "string", "description": "The title of the section." },
+ "columns": {
+ "type": "number",
+ "description": "The number of columns the section should span across."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "options": {
+ "type": "object",
+ "properties": {
+ "showLinkInTitle": {
+ "type": "boolean",
+ "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."
+ }
+ },
+ "required": ["showLinkInTitle"],
+ "description": "Display options for this item."
+ },
+ "company": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the company or organization."
+ },
+ "position": {
+ "type": "string",
+ "description": "The position held at the company or organization."
+ },
+ "location": { "type": "string", "description": "The location of the company or organization." },
+ "period": {
+ "type": "string",
+ "description": "The period of time the author was employed at the company or organization."
+ },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "description": "The website of the company or organization, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the experience. This should be a HTML-formatted string."
+ }
+ },
+ "required": [
+ "id",
+ "hidden",
+ "company",
+ "position",
+ "location",
+ "period",
+ "website",
+ "description"
+ ]
+ },
+ "description": "The items to display in the experience section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "description": "The section to display the experience of the author."
+ },
+ "education": {
+ "type": "object",
+ "properties": {
+ "title": { "type": "string", "description": "The title of the section." },
+ "columns": {
+ "type": "number",
+ "description": "The number of columns the section should span across."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "options": {
+ "type": "object",
+ "properties": {
+ "showLinkInTitle": {
+ "type": "boolean",
+ "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."
+ }
+ },
+ "required": ["showLinkInTitle"],
+ "description": "Display options for this item."
+ },
+ "school": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the school or institution."
+ },
+ "degree": { "type": "string", "description": "The degree or qualification obtained." },
+ "area": { "type": "string", "description": "The area of study or specialization." },
+ "grade": { "type": "string", "description": "The grade or score achieved." },
+ "location": { "type": "string", "description": "The location of the school or institution." },
+ "period": {
+ "type": "string",
+ "description": "The period of time the education was obtained over."
+ },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "description": "The website of the school or institution, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the education. This should be a HTML-formatted string."
+ }
+ },
+ "required": [
+ "id",
+ "hidden",
+ "school",
+ "degree",
+ "area",
+ "grade",
+ "location",
+ "period",
+ "website",
+ "description"
+ ]
+ },
+ "description": "The items to display in the education section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "description": "The section to display the education of the author."
+ },
+ "projects": {
+ "type": "object",
+ "properties": {
+ "title": { "type": "string", "description": "The title of the section." },
+ "columns": {
+ "type": "number",
+ "description": "The number of columns the section should span across."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "options": {
+ "type": "object",
+ "properties": {
+ "showLinkInTitle": {
+ "type": "boolean",
+ "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."
+ }
+ },
+ "required": ["showLinkInTitle"],
+ "description": "Display options for this item."
+ },
+ "name": { "type": "string", "minLength": 1, "description": "The name of the project." },
+ "period": { "type": "string", "description": "The period of time the project was worked on." },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "description": "The link to the project, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the project. This should be a HTML-formatted string."
+ }
+ },
+ "required": ["id", "hidden", "name", "period", "website", "description"]
+ },
+ "description": "The items to display in the projects section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "description": "The section to display the projects of the author."
+ },
+ "skills": {
+ "type": "object",
+ "properties": {
+ "title": { "type": "string", "description": "The title of the section." },
+ "columns": {
+ "type": "number",
+ "description": "The number of columns the section should span across."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "options": {
+ "type": "object",
+ "properties": {
+ "showLinkInTitle": {
+ "type": "boolean",
+ "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."
+ }
+ },
+ "required": ["showLinkInTitle"],
+ "description": "Display options for this item."
+ },
+ "icon": {
+ "type": "string",
+ "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
+ },
+ "name": { "type": "string", "minLength": 1, "description": "The name of the skill." },
+ "proficiency": {
+ "type": "string",
+ "description": "The proficiency level of the skill. Can be any text, such as 'Beginner', 'Intermediate', 'Advanced', etc."
+ },
+ "level": {
+ "type": "number",
+ "minimum": 0,
+ "maximum": 5,
+ "description": "The proficiency level of the skill, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden."
+ },
+ "keywords": {
+ "type": "array",
+ "items": { "type": "string" },
+ "description": "The keywords associated with the skill, if any. These are displayed as tags below the name."
+ }
+ },
+ "required": ["id", "hidden", "icon", "name", "proficiency", "level", "keywords"]
+ },
+ "description": "The items to display in the skills section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "description": "The section to display the skills of the author."
+ },
+ "languages": {
+ "type": "object",
+ "properties": {
+ "title": { "type": "string", "description": "The title of the section." },
+ "columns": {
+ "type": "number",
+ "description": "The number of columns the section should span across."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "options": {
+ "type": "object",
+ "properties": {
+ "showLinkInTitle": {
+ "type": "boolean",
+ "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."
+ }
+ },
+ "required": ["showLinkInTitle"],
+ "description": "Display options for this item."
+ },
+ "language": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the language the author knows."
+ },
+ "fluency": {
+ "type": "string",
+ "description": "The fluency level of the language. Can be any text, such as 'Native', 'Fluent', 'Conversational', etc. or can also be a CEFR level (A1, A2, B1, B2, C1, C2)."
+ },
+ "level": {
+ "type": "number",
+ "minimum": 0,
+ "maximum": 5,
+ "description": "The proficiency level of the language, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden."
+ }
+ },
+ "required": ["id", "hidden", "language", "fluency", "level"]
+ },
+ "description": "The items to display in the languages section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "description": "The section to display the languages of the author."
+ },
+ "interests": {
+ "type": "object",
+ "properties": {
+ "title": { "type": "string", "description": "The title of the section." },
+ "columns": {
+ "type": "number",
+ "description": "The number of columns the section should span across."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "options": {
+ "type": "object",
+ "properties": {
+ "showLinkInTitle": {
+ "type": "boolean",
+ "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."
+ }
+ },
+ "required": ["showLinkInTitle"],
+ "description": "Display options for this item."
+ },
+ "icon": {
+ "type": "string",
+ "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
+ },
+ "name": { "type": "string", "minLength": 1, "description": "The name of the interest/hobby." },
+ "keywords": {
+ "type": "array",
+ "items": { "type": "string" },
+ "description": "The keywords associated with the interest/hobby, if any. These are displayed as tags below the name."
+ }
+ },
+ "required": ["id", "hidden", "icon", "name", "keywords"]
+ },
+ "description": "The items to display in the interests section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "description": "The section to display the interests of the author."
+ },
+ "awards": {
+ "type": "object",
+ "properties": {
+ "title": { "type": "string", "description": "The title of the section." },
+ "columns": {
+ "type": "number",
+ "description": "The number of columns the section should span across."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "options": {
+ "type": "object",
+ "properties": {
+ "showLinkInTitle": {
+ "type": "boolean",
+ "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."
+ }
+ },
+ "required": ["showLinkInTitle"],
+ "description": "Display options for this item."
+ },
+ "title": { "type": "string", "minLength": 1, "description": "The title of the award." },
+ "awarder": { "type": "string", "description": "The awarder of the award." },
+ "date": { "type": "string", "description": "The date when the award was received." },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "description": "The website of the award, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the award. This should be a HTML-formatted string."
+ }
+ },
+ "required": ["id", "hidden", "title", "awarder", "date", "website", "description"]
+ },
+ "description": "The items to display in the awards section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "description": "The section to display the awards of the author."
+ },
+ "certifications": {
+ "type": "object",
+ "properties": {
+ "title": { "type": "string", "description": "The title of the section." },
+ "columns": {
+ "type": "number",
+ "description": "The number of columns the section should span across."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "options": {
+ "type": "object",
+ "properties": {
+ "showLinkInTitle": {
+ "type": "boolean",
+ "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."
+ }
+ },
+ "required": ["showLinkInTitle"],
+ "description": "Display options for this item."
+ },
+ "title": { "type": "string", "minLength": 1, "description": "The title of the certification." },
+ "issuer": { "type": "string", "description": "The issuer of the certification." },
+ "date": { "type": "string", "description": "The date when the certification was received." },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "description": "The website of the certification, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the certification. This should be a HTML-formatted string."
+ }
+ },
+ "required": ["id", "hidden", "title", "issuer", "date", "website", "description"]
+ },
+ "description": "The items to display in the certifications section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "description": "The section to display the certifications of the author."
+ },
+ "publications": {
+ "type": "object",
+ "properties": {
+ "title": { "type": "string", "description": "The title of the section." },
+ "columns": {
+ "type": "number",
+ "description": "The number of columns the section should span across."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "options": {
+ "type": "object",
+ "properties": {
+ "showLinkInTitle": {
+ "type": "boolean",
+ "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."
+ }
+ },
+ "required": ["showLinkInTitle"],
+ "description": "Display options for this item."
+ },
+ "title": { "type": "string", "minLength": 1, "description": "The title of the publication." },
+ "publisher": { "type": "string", "description": "The publisher of the publication." },
+ "date": { "type": "string", "description": "The date when the publication was published." },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "description": "The link to the publication, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the publication. This should be a HTML-formatted string."
+ }
+ },
+ "required": ["id", "hidden", "title", "publisher", "date", "website", "description"]
+ },
+ "description": "The items to display in the publications section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "description": "The section to display the publications of the author."
+ },
+ "volunteer": {
+ "type": "object",
+ "properties": {
+ "title": { "type": "string", "description": "The title of the section." },
+ "columns": {
+ "type": "number",
+ "description": "The number of columns the section should span across."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "options": {
+ "type": "object",
+ "properties": {
+ "showLinkInTitle": {
+ "type": "boolean",
+ "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."
+ }
+ },
+ "required": ["showLinkInTitle"],
+ "description": "Display options for this item."
+ },
+ "organization": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the organization or company."
+ },
+ "location": { "type": "string", "description": "The location of the organization or company." },
+ "period": {
+ "type": "string",
+ "description": "The period of time the author was volunteered at the organization or company."
+ },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "description": "The link to the organization or company, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the volunteer experience. This should be a HTML-formatted string."
+ }
+ },
+ "required": ["id", "hidden", "organization", "location", "period", "website", "description"]
+ },
+ "description": "The items to display in the volunteer section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "description": "The section to display the volunteer experience of the author."
+ },
+ "references": {
+ "type": "object",
+ "properties": {
+ "title": { "type": "string", "description": "The title of the section." },
+ "columns": {
+ "type": "number",
+ "description": "The number of columns the section should span across."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "options": {
+ "type": "object",
+ "properties": {
+ "showLinkInTitle": {
+ "type": "boolean",
+ "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."
+ }
+ },
+ "required": ["showLinkInTitle"],
+ "description": "Display options for this item."
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the reference, or a note such as 'Available upon request'."
+ },
+ "position": { "type": "string", "description": "The position or job title of the reference." },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "description": "The website or LinkedIn profile of the reference, if any."
+ },
+ "phone": { "type": "string", "description": "The phone number of the reference." },
+ "description": {
+ "type": "string",
+ "description": "The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string."
+ }
+ },
+ "required": ["id", "hidden", "name", "position", "website", "phone", "description"]
+ },
+ "description": "The items to display in the references section."
+ }
+ },
+ "required": ["title", "columns", "hidden", "items"],
+ "description": "The section to display the references of the author."
+ }
+ },
+ "required": [
+ "profiles",
+ "experience",
+ "education",
+ "projects",
+ "skills",
+ "languages",
+ "interests",
+ "awards",
+ "certifications",
+ "publications",
+ "volunteer",
+ "references"
+ ],
+ "description": "Various sections of the resume, such as experience, education, projects, etc."
+ },
+ "customSections": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "title": { "type": "string", "description": "The title of the section." },
+ "columns": { "type": "number", "description": "The number of columns the section should span across." },
+ "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." },
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the custom section. Usually generated as a UUID."
+ },
+ "type": {
+ "enum": [
+ "summary",
+ "profiles",
+ "experience",
+ "education",
+ "projects",
+ "skills",
+ "languages",
+ "interests",
+ "awards",
+ "certifications",
+ "publications",
+ "volunteer",
+ "references",
+ "cover-letter"
+ ],
+ "description": "The type of items this custom section contains. Determines which item schema and form fields to use."
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "anyOf": [
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "options": {
+ "type": "object",
+ "properties": {
+ "showLinkInTitle": {
+ "type": "boolean",
+ "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."
+ }
+ },
+ "required": ["showLinkInTitle"],
+ "description": "Display options for this item."
+ },
+ "recipient": {
+ "type": "string",
+ "description": "The recipient's address block as HTML (name, title, company, address, email)."
+ },
+ "content": {
+ "type": "string",
+ "description": "The cover letter body as HTML (salutation, paragraphs, closing, signature)."
+ }
+ },
+ "required": ["id", "hidden", "recipient", "content"]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "options": {
+ "type": "object",
+ "properties": {
+ "showLinkInTitle": {
+ "type": "boolean",
+ "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."
+ }
+ },
+ "required": ["showLinkInTitle"],
+ "description": "Display options for this item."
+ },
+ "content": {
+ "type": "string",
+ "description": "The rich text content of the summary item. This should be a HTML-formatted string."
+ }
+ },
+ "required": ["id", "hidden", "content"]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "options": {
+ "type": "object",
+ "properties": {
+ "showLinkInTitle": {
+ "type": "boolean",
+ "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."
+ }
+ },
+ "required": ["showLinkInTitle"],
+ "description": "Display options for this item."
+ },
+ "icon": {
+ "type": "string",
+ "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
+ },
+ "network": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the network or platform."
+ },
+ "username": {
+ "type": "string",
+ "description": "The username of the author on the network or platform."
+ },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "description": "The link to the profile of the author on the network or platform, if any."
+ }
+ },
+ "required": ["id", "hidden", "icon", "network", "username", "website"]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "options": {
+ "type": "object",
+ "properties": {
+ "showLinkInTitle": {
+ "type": "boolean",
+ "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."
+ }
+ },
+ "required": ["showLinkInTitle"],
+ "description": "Display options for this item."
+ },
+ "company": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the company or organization."
+ },
+ "position": {
+ "type": "string",
+ "description": "The position held at the company or organization."
+ },
+ "location": {
+ "type": "string",
+ "description": "The location of the company or organization."
+ },
+ "period": {
+ "type": "string",
+ "description": "The period of time the author was employed at the company or organization."
+ },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "description": "The website of the company or organization, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the experience. This should be a HTML-formatted string."
+ }
+ },
+ "required": [
+ "id",
+ "hidden",
+ "company",
+ "position",
+ "location",
+ "period",
+ "website",
+ "description"
+ ]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "options": {
+ "type": "object",
+ "properties": {
+ "showLinkInTitle": {
+ "type": "boolean",
+ "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."
+ }
+ },
+ "required": ["showLinkInTitle"],
+ "description": "Display options for this item."
+ },
+ "school": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the school or institution."
+ },
+ "degree": { "type": "string", "description": "The degree or qualification obtained." },
+ "area": { "type": "string", "description": "The area of study or specialization." },
+ "grade": { "type": "string", "description": "The grade or score achieved." },
+ "location": { "type": "string", "description": "The location of the school or institution." },
+ "period": {
+ "type": "string",
+ "description": "The period of time the education was obtained over."
+ },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "description": "The website of the school or institution, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the education. This should be a HTML-formatted string."
+ }
+ },
+ "required": [
+ "id",
+ "hidden",
+ "school",
+ "degree",
+ "area",
+ "grade",
+ "location",
+ "period",
+ "website",
+ "description"
+ ]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "options": {
+ "type": "object",
+ "properties": {
+ "showLinkInTitle": {
+ "type": "boolean",
+ "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."
+ }
+ },
+ "required": ["showLinkInTitle"],
+ "description": "Display options for this item."
+ },
+ "name": { "type": "string", "minLength": 1, "description": "The name of the project." },
+ "period": {
+ "type": "string",
+ "description": "The period of time the project was worked on."
+ },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "description": "The link to the project, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the project. This should be a HTML-formatted string."
+ }
+ },
+ "required": ["id", "hidden", "name", "period", "website", "description"]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "options": {
+ "type": "object",
+ "properties": {
+ "showLinkInTitle": {
+ "type": "boolean",
+ "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."
+ }
+ },
+ "required": ["showLinkInTitle"],
+ "description": "Display options for this item."
+ },
+ "icon": {
+ "type": "string",
+ "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
+ },
+ "name": { "type": "string", "minLength": 1, "description": "The name of the skill." },
+ "proficiency": {
+ "type": "string",
+ "description": "The proficiency level of the skill. Can be any text, such as 'Beginner', 'Intermediate', 'Advanced', etc."
+ },
+ "level": {
+ "type": "number",
+ "minimum": 0,
+ "maximum": 5,
+ "description": "The proficiency level of the skill, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden."
+ },
+ "keywords": {
+ "type": "array",
+ "items": { "type": "string" },
+ "description": "The keywords associated with the skill, if any. These are displayed as tags below the name."
+ }
+ },
+ "required": ["id", "hidden", "icon", "name", "proficiency", "level", "keywords"]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "options": {
+ "type": "object",
+ "properties": {
+ "showLinkInTitle": {
+ "type": "boolean",
+ "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."
+ }
+ },
+ "required": ["showLinkInTitle"],
+ "description": "Display options for this item."
+ },
+ "language": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the language the author knows."
+ },
+ "fluency": {
+ "type": "string",
+ "description": "The fluency level of the language. Can be any text, such as 'Native', 'Fluent', 'Conversational', etc. or can also be a CEFR level (A1, A2, B1, B2, C1, C2)."
+ },
+ "level": {
+ "type": "number",
+ "minimum": 0,
+ "maximum": 5,
+ "description": "The proficiency level of the language, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden."
+ }
+ },
+ "required": ["id", "hidden", "language", "fluency", "level"]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "options": {
+ "type": "object",
+ "properties": {
+ "showLinkInTitle": {
+ "type": "boolean",
+ "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."
+ }
+ },
+ "required": ["showLinkInTitle"],
+ "description": "Display options for this item."
+ },
+ "icon": {
+ "type": "string",
+ "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the interest/hobby."
+ },
+ "keywords": {
+ "type": "array",
+ "items": { "type": "string" },
+ "description": "The keywords associated with the interest/hobby, if any. These are displayed as tags below the name."
+ }
+ },
+ "required": ["id", "hidden", "icon", "name", "keywords"]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "options": {
+ "type": "object",
+ "properties": {
+ "showLinkInTitle": {
+ "type": "boolean",
+ "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."
+ }
+ },
+ "required": ["showLinkInTitle"],
+ "description": "Display options for this item."
+ },
+ "title": { "type": "string", "minLength": 1, "description": "The title of the award." },
+ "awarder": { "type": "string", "description": "The awarder of the award." },
+ "date": { "type": "string", "description": "The date when the award was received." },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "description": "The website of the award, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the award. This should be a HTML-formatted string."
+ }
+ },
+ "required": ["id", "hidden", "title", "awarder", "date", "website", "description"]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "options": {
+ "type": "object",
+ "properties": {
+ "showLinkInTitle": {
+ "type": "boolean",
+ "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."
+ }
+ },
+ "required": ["showLinkInTitle"],
+ "description": "Display options for this item."
+ },
+ "title": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The title of the certification."
+ },
+ "issuer": { "type": "string", "description": "The issuer of the certification." },
+ "date": { "type": "string", "description": "The date when the certification was received." },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "description": "The website of the certification, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the certification. This should be a HTML-formatted string."
+ }
+ },
+ "required": ["id", "hidden", "title", "issuer", "date", "website", "description"]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "options": {
+ "type": "object",
+ "properties": {
+ "showLinkInTitle": {
+ "type": "boolean",
+ "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."
+ }
+ },
+ "required": ["showLinkInTitle"],
+ "description": "Display options for this item."
+ },
+ "title": { "type": "string", "minLength": 1, "description": "The title of the publication." },
+ "publisher": { "type": "string", "description": "The publisher of the publication." },
+ "date": { "type": "string", "description": "The date when the publication was published." },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "description": "The link to the publication, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the publication. This should be a HTML-formatted string."
+ }
+ },
+ "required": ["id", "hidden", "title", "publisher", "date", "website", "description"]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "options": {
+ "type": "object",
+ "properties": {
+ "showLinkInTitle": {
+ "type": "boolean",
+ "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."
+ }
+ },
+ "required": ["showLinkInTitle"],
+ "description": "Display options for this item."
+ },
+ "organization": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the organization or company."
+ },
+ "location": {
+ "type": "string",
+ "description": "The location of the organization or company."
+ },
+ "period": {
+ "type": "string",
+ "description": "The period of time the author was volunteered at the organization or company."
+ },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "description": "The link to the organization or company, if any."
+ },
+ "description": {
+ "type": "string",
+ "description": "The description of the volunteer experience. This should be a HTML-formatted string."
+ }
+ },
+ "required": ["id", "hidden", "organization", "location", "period", "website", "description"]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the item. Usually generated as a UUID."
+ },
+ "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." },
+ "options": {
+ "type": "object",
+ "properties": {
+ "showLinkInTitle": {
+ "type": "boolean",
+ "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom."
+ }
+ },
+ "required": ["showLinkInTitle"],
+ "description": "Display options for this item."
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the reference, or a note such as 'Available upon request'."
+ },
+ "position": {
+ "type": "string",
+ "description": "The position or job title of the reference."
+ },
+ "website": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label to display for the URL. Leave blank to display the URL as-is."
+ }
+ },
+ "required": ["url", "label"],
+ "description": "The website or LinkedIn profile of the reference, if any."
+ },
+ "phone": { "type": "string", "description": "The phone number of the reference." },
+ "description": {
+ "type": "string",
+ "description": "The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string."
+ }
+ },
+ "required": ["id", "hidden", "name", "position", "website", "phone", "description"]
+ }
+ ]
+ },
+ "description": "The items to display in the custom section. Items follow the schema of the section type."
+ }
+ },
+ "required": ["title", "columns", "hidden", "id", "type", "items"]
+ },
+ "description": "Custom sections of the resume, such as a custom section for notes, etc."
+ },
+ "metadata": {
+ "type": "object",
+ "properties": {
+ "template": {
+ "enum": [
+ "azurill",
+ "bronzor",
+ "chikorita",
+ "ditgar",
+ "ditto",
+ "gengar",
+ "glalie",
+ "kakuna",
+ "lapras",
+ "leafish",
+ "onyx",
+ "pikachu",
+ "rhyhorn"
+ ],
+ "description": "The template to use for the resume. Determines the overall design and appearance of the resume."
+ },
+ "layout": {
+ "type": "object",
+ "properties": {
+ "sidebarWidth": {
+ "type": "number",
+ "minimum": 10,
+ "maximum": 50,
+ "description": "The width of the sidebar column, defined as a percentage of the page width."
+ },
+ "pages": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "fullWidth": {
+ "type": "boolean",
+ "description": "Whether the layout of the page should be full width. If true, the main column will span the entire width of the page. This means that there should be no items in the sidebar column."
+ },
+ "main": {
+ "type": "array",
+ "items": { "type": "string" },
+ "description": "The items to display in the main column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)."
+ },
+ "sidebar": {
+ "type": "array",
+ "items": { "type": "string" },
+ "description": "The items to display in the sidebar column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)."
+ }
+ },
+ "required": ["fullWidth", "main", "sidebar"]
+ },
+ "description": "The pages to display in the layout."
+ }
+ },
+ "required": ["sidebarWidth", "pages"],
+ "description": "The layout of the resume. Determines the structure and arrangement of the sections on the resume."
+ },
+ "css": {
+ "type": "object",
+ "properties": {
+ "enabled": { "type": "boolean", "description": "Whether to enable custom CSS for the resume." },
+ "value": {
+ "type": "string",
+ "description": "The custom CSS to apply to the resume. This should be a valid CSS string."
+ }
+ },
+ "required": ["enabled", "value"],
+ "description": "Custom CSS to apply to the resume. Can be used to override the default styles of the template."
+ },
+ "page": {
+ "type": "object",
+ "properties": {
+ "gapX": {
+ "type": "number",
+ "minimum": 0,
+ "description": "The horizontal gap between the sections of the page, defined in points (pt)."
+ },
+ "gapY": {
+ "type": "number",
+ "minimum": 0,
+ "description": "The vertical gap between the sections of the page, defined in points (pt)."
+ },
+ "marginX": {
+ "type": "number",
+ "minimum": 0,
+ "description": "The horizontal margin of the page, defined in points (pt)."
+ },
+ "marginY": {
+ "type": "number",
+ "minimum": 0,
+ "description": "The vertical margin of the page, defined in points (pt)."
+ },
+ "format": {
+ "enum": ["a4", "letter", "free-form"],
+ "description": "The format of the page. Can be 'a4', 'letter' or 'free-form'."
+ },
+ "locale": {
+ "type": "string",
+ "description": "The locale of the page. Used for displaying pre-translated section headings, if not overridden."
+ },
+ "hideIcons": { "type": "boolean", "description": "Whether to hide the icons of the sections." }
+ },
+ "required": ["gapX", "gapY", "marginX", "marginY", "format", "locale", "hideIcons"],
+ "description": "The page settings of the resume. Determines the margins, format, and locale of the resume."
+ },
+ "design": {
+ "type": "object",
+ "properties": {
+ "level": {
+ "type": "object",
+ "properties": {
+ "icon": {
+ "type": "string",
+ "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
+ },
+ "type": {
+ "enum": ["hidden", "circle", "square", "rectangle", "rectangle-full", "progress-bar", "icon"],
+ "description": "The type of the level design. 'hidden' will hide the level design, 'circle' will display a circle, 'square' will display a square, 'rectangle' will display a rectangle, 'rectangle-full' will display a full rectangle, 'progress-bar' will display a progress bar, and 'icon' will display an icon. If 'icon' is selected, the icon to display should be specified in the 'icon' field."
+ }
+ },
+ "required": ["icon", "type"]
+ },
+ "colors": {
+ "type": "object",
+ "properties": {
+ "primary": {
+ "type": "string",
+ "description": "The primary color of the design, defined as rgba(r, g, b, a)."
+ },
+ "text": {
+ "type": "string",
+ "description": "The text color of the design, defined as rgba(r, g, b, a). Usually set to black: rgba(0, 0, 0, 1)."
+ },
+ "background": {
+ "type": "string",
+ "description": "The background color of the design, defined as rgba(r, g, b, a). Usually set to white: rgba(255, 255, 255, 1)."
+ }
+ },
+ "required": ["primary", "text", "background"]
+ }
+ },
+ "required": ["level", "colors"],
+ "description": "The design settings of the resume. Determines the colors, level designs, and typography of the resume."
+ },
+ "typography": {
+ "type": "object",
+ "properties": {
+ "body": {
+ "type": "object",
+ "properties": {
+ "fontFamily": {
+ "type": "string",
+ "description": "The family of the font to use. Must be a font that is available on Google Fonts."
+ },
+ "fontWeights": {
+ "type": "array",
+ "items": { "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] },
+ "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font."
+ },
+ "fontSize": {
+ "type": "number",
+ "minimum": 6,
+ "maximum": 24,
+ "description": "The size of the font to use, defined in points (pt)."
+ },
+ "lineHeight": {
+ "type": "number",
+ "minimum": 0.5,
+ "maximum": 4,
+ "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)."
+ }
+ },
+ "required": ["fontFamily", "fontWeights", "fontSize", "lineHeight"],
+ "description": "The typography for the body of the resume."
+ },
+ "heading": {
+ "type": "object",
+ "properties": {
+ "fontFamily": {
+ "type": "string",
+ "description": "The family of the font to use. Must be a font that is available on Google Fonts."
+ },
+ "fontWeights": {
+ "type": "array",
+ "items": { "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] },
+ "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font."
+ },
+ "fontSize": {
+ "type": "number",
+ "minimum": 6,
+ "maximum": 24,
+ "description": "The size of the font to use, defined in points (pt)."
+ },
+ "lineHeight": {
+ "type": "number",
+ "minimum": 0.5,
+ "maximum": 4,
+ "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)."
+ }
+ },
+ "required": ["fontFamily", "fontWeights", "fontSize", "lineHeight"],
+ "description": "The typography for the headings of the resume."
+ }
+ },
+ "required": ["body", "heading"],
+ "description": "The typography settings of the resume. Determines the fonts and sizes of the body and headings of the resume."
+ },
+ "notes": {
+ "type": "string",
+ "description": "Personal notes for the resume. Can be used to add any additional information or instructions for the resume. These notes are not displayed on the resume, they are only visible to the author of the resume when editing the resume. This should be a HTML-formatted string."
+ }
+ },
+ "required": ["template", "layout", "css", "page", "design", "typography", "notes"],
+ "description": "Metadata for the resume, such as template, layout, typography, etc. This section describes the overall design and appearance of the resume."
+ }
+ },
+ "required": ["picture", "basics", "summary", "sections", "customSections", "metadata"]
+ }
+ }
+ },
+ "security": [{ "apiKey": [] }],
+ "openapi": "3.1.1",
+ "paths": {
+ "/ai/test-connection": {
+ "post": {
+ "operationId": "testAiConnection",
+ "summary": "Test AI provider connection",
+ "description": "Validates the connection to an AI provider by sending a simple test prompt. Requires the provider type, model name, API key, and an optional base URL. Supported providers: OpenAI, Anthropic, Google Gemini, Ollama, and Vercel AI Gateway. Requires authentication.",
+ "tags": ["AI"],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "provider": { "enum": ["ollama", "openai", "gemini", "anthropic", "vercel-ai-gateway"] },
+ "model": { "type": "string" },
+ "apiKey": { "type": "string" },
+ "baseURL": { "type": "string" }
+ },
+ "required": ["provider", "model", "apiKey", "baseURL"]
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "The AI provider connection was successful.",
+ "content": { "application/json": { "schema": { "anyOf": [{}, { "not": {} }] } } }
+ },
+ "502": {
+ "description": "502",
+ "content": {
+ "application/json": {
+ "schema": {
+ "oneOf": [
+ {
+ "type": "object",
+ "properties": {
+ "defined": { "const": true },
+ "code": { "const": "BAD_GATEWAY" },
+ "status": { "const": 502 },
+ "message": {
+ "type": "string",
+ "default": "The AI provider returned an error or is unreachable."
+ },
+ "data": {}
+ },
+ "required": ["defined", "code", "status", "message"]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "defined": { "const": false },
+ "code": { "type": "string" },
+ "status": { "type": "number" },
+ "message": { "type": "string" },
+ "data": {}
+ },
+ "required": ["defined", "code", "status", "message"]
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/ai/parse-pdf": {
+ "post": {
+ "operationId": "parseResumePdf",
+ "summary": "Parse a PDF file into resume data",
+ "description": "Extracts structured resume data from a PDF file using the specified AI provider. The file should be sent as a base64-encoded string along with AI provider credentials. Returns a complete ResumeData object. Requires authentication.",
+ "tags": ["AI"],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "provider": { "enum": ["ollama", "openai", "gemini", "anthropic", "vercel-ai-gateway"] },
+ "model": { "type": "string" },
+ "apiKey": { "type": "string" },
+ "baseURL": { "type": "string" },
+ "file": {
+ "type": "object",
+ "properties": { "name": { "type": "string" }, "data": { "type": "string" } },
+ "required": ["name", "data"]
+ }
+ },
+ "required": ["provider", "model", "apiKey", "baseURL", "file"]
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "The PDF was successfully parsed into structured resume data.",
+ "content": { "application/json": { "schema": { "anyOf": [{}, { "not": {} }] } } }
+ },
+ "502": {
+ "description": "502",
+ "content": {
+ "application/json": {
+ "schema": {
+ "oneOf": [
+ {
+ "type": "object",
+ "properties": {
+ "defined": { "const": true },
+ "code": { "const": "BAD_GATEWAY" },
+ "status": { "const": 502 },
+ "message": {
+ "type": "string",
+ "default": "The AI provider returned an error or is unreachable."
+ },
+ "data": {}
+ },
+ "required": ["defined", "code", "status", "message"]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "defined": { "const": false },
+ "code": { "type": "string" },
+ "status": { "type": "number" },
+ "message": { "type": "string" },
+ "data": {}
+ },
+ "required": ["defined", "code", "status", "message"]
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/ai/parse-docx": {
+ "post": {
+ "operationId": "parseResumeDocx",
+ "summary": "Parse a DOCX file into resume data",
+ "description": "Extracts structured resume data from a DOCX or DOC file using the specified AI provider. The file should be sent as a base64-encoded string along with AI provider credentials and the document's media type. Returns a complete ResumeData object. Requires authentication.",
+ "tags": ["AI"],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "provider": { "enum": ["ollama", "openai", "gemini", "anthropic", "vercel-ai-gateway"] },
+ "model": { "type": "string" },
+ "apiKey": { "type": "string" },
+ "baseURL": { "type": "string" },
+ "file": {
+ "type": "object",
+ "properties": { "name": { "type": "string" }, "data": { "type": "string" } },
+ "required": ["name", "data"]
+ },
+ "mediaType": {
+ "enum": [
+ "application/msword",
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
+ ]
+ }
+ },
+ "required": ["provider", "model", "apiKey", "baseURL", "file", "mediaType"]
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "The DOCX was successfully parsed into structured resume data.",
+ "content": { "application/json": { "schema": { "anyOf": [{}, { "not": {} }] } } }
+ },
+ "502": {
+ "description": "502",
+ "content": {
+ "application/json": {
+ "schema": {
+ "oneOf": [
+ {
+ "type": "object",
+ "properties": {
+ "defined": { "const": true },
+ "code": { "const": "BAD_GATEWAY" },
+ "status": { "const": 502 },
+ "message": {
+ "type": "string",
+ "default": "The AI provider returned an error or is unreachable."
+ },
+ "data": {}
+ },
+ "required": ["defined", "code", "status", "message"]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "defined": { "const": false },
+ "code": { "type": "string" },
+ "status": { "type": "number" },
+ "message": { "type": "string" },
+ "data": {}
+ },
+ "required": ["defined", "code", "status", "message"]
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/ai/chat": {
+ "post": {
+ "operationId": "aiChat",
+ "summary": "Chat with AI to modify resume",
+ "description": "Streams a chat response from the configured AI provider. The LLM can call the patch_resume tool to generate JSON Patch operations that modify the resume. Requires authentication and AI provider credentials.",
+ "tags": ["AI"],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": { "application/json": { "schema": { "anyOf": [{}, { "not": {} }] } } }
+ }
+ }
+ }
+ },
+ "/auth/providers": {
+ "get": {
+ "operationId": "listAuthProviders",
+ "summary": "List authentication providers",
+ "description": "Returns a list of all authentication providers enabled on this Reactive Resume instance, along with their display names. Possible providers include password-based credentials, Google, GitHub, and custom OAuth. No authentication required.",
+ "tags": ["Authentication"],
+ "responses": {
+ "200": {
+ "description": "A map of enabled authentication provider identifiers to their display names.",
+ "content": { "application/json": { "schema": { "anyOf": [{}, { "not": {} }] } } }
+ }
+ }
+ }
+ },
+ "/auth/account": {
+ "delete": {
+ "operationId": "deleteAccount",
+ "summary": "Delete user account",
+ "description": "Permanently deletes the authenticated user's account, including all resumes, uploaded files (profile pictures, screenshots, PDFs), and associated data. This action is irreversible. Requires authentication.",
+ "tags": ["Authentication"],
+ "responses": {
+ "200": {
+ "description": "The user account and all associated data have been successfully deleted.",
+ "content": { "application/json": { "schema": { "anyOf": [{}, { "not": {} }] } } }
+ }
+ }
+ }
+ },
+ "/flags": {
+ "get": {
+ "operationId": "getFeatureFlags",
+ "summary": "Get feature flags",
+ "description": "Returns the current feature flags for this Reactive Resume instance. Feature flags control instance-wide settings such as whether new user signups or email-based authentication are disabled. No authentication required.",
+ "tags": ["Feature Flags"],
+ "responses": {
+ "200": {
+ "description": "The current feature flags for this instance.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "disableSignups": {
+ "type": "boolean",
+ "description": "Whether new user signups are disabled on this instance."
+ },
+ "disableEmailAuth": {
+ "type": "boolean",
+ "description": "Whether email-based authentication is disabled on this instance."
+ }
+ },
+ "required": ["disableSignups", "disableEmailAuth"]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/resumes/tags": {
+ "get": {
+ "operationId": "listResumeTags",
+ "summary": "List all resume tags",
+ "description": "Returns a sorted list of all unique tags across the authenticated user's resumes. Useful for populating tag filters in the dashboard. Requires authentication.",
+ "tags": ["Resumes"],
+ "responses": {
+ "200": {
+ "description": "A sorted array of unique tag strings.",
+ "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string" } } } }
+ }
+ }
+ }
+ },
+ "/resumes/{id}/statistics": {
+ "get": {
+ "operationId": "getResumeStatistics",
+ "summary": "Get resume statistics",
+ "description": "Returns view and download statistics for the specified resume, including total counts and the timestamps of the last view and download. Requires authentication.",
+ "tags": ["Resume Statistics"],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": { "type": "string", "description": "The unique identifier of the resume." }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "The resume's view and download statistics.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "isPublic": { "type": "boolean", "description": "Whether the resume is currently public." },
+ "views": { "type": "number", "description": "Total number of times the resume has been viewed." },
+ "downloads": {
+ "type": "number",
+ "description": "Total number of times the resume has been downloaded."
+ },
+ "lastViewedAt": {
+ "anyOf": [
+ { "type": "string", "format": "date-time", "x-native-type": "date" },
+ { "type": "null" }
+ ],
+ "description": "Timestamp of the last view, or null if never viewed."
+ },
+ "lastDownloadedAt": {
+ "anyOf": [
+ { "type": "string", "format": "date-time", "x-native-type": "date" },
+ { "type": "null" }
+ ],
+ "description": "Timestamp of the last download, or null if never downloaded."
+ }
+ },
+ "required": ["isPublic", "views", "downloads", "lastViewedAt", "lastDownloadedAt"]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/resumes": {
+ "get": {
+ "operationId": "listResumes",
+ "summary": "List all resumes",
+ "description": "Returns a list of all resumes belonging to the authenticated user. Results can be filtered by tags and sorted by last updated date, creation date, or name. Resume data is not included in the response for performance; use the get endpoint to fetch full resume data. Requires authentication.",
+ "tags": ["Resumes"],
+ "parameters": [
+ {
+ "name": "tags",
+ "in": "query",
+ "schema": { "type": "array", "items": { "type": "string" }, "default": [] },
+ "style": "deepObject",
+ "explode": true,
+ "allowEmptyValue": true,
+ "allowReserved": true
+ },
+ {
+ "name": "sort",
+ "in": "query",
+ "schema": { "enum": ["lastUpdatedAt", "createdAt", "name"], "default": "lastUpdatedAt" },
+ "style": "deepObject",
+ "explode": true,
+ "allowEmptyValue": true,
+ "allowReserved": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "A list of resumes with their metadata (without full resume data).",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": { "type": "string", "description": "The ID of the resume." },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 64,
+ "description": "The name of the resume."
+ },
+ "slug": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 64,
+ "description": "The slug of the resume."
+ },
+ "tags": {
+ "type": "array",
+ "items": { "type": "string" },
+ "description": "The tags of the resume."
+ },
+ "isPublic": { "type": "boolean", "description": "Whether the resume is public." },
+ "isLocked": { "type": "boolean", "description": "Whether the resume is locked." },
+ "createdAt": {
+ "type": "string",
+ "format": "date-time",
+ "x-native-type": "date",
+ "description": "The date and time the resume was created."
+ },
+ "updatedAt": {
+ "type": "string",
+ "format": "date-time",
+ "x-native-type": "date",
+ "description": "The date and time the resume was last updated."
+ }
+ },
+ "required": ["id", "name", "slug", "tags", "isPublic", "isLocked", "createdAt", "updatedAt"]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "post": {
+ "operationId": "createResume",
+ "summary": "Create a new resume",
+ "description": "Creates a new resume with the given name, slug, and tags. Optionally initializes the resume with sample data by setting withSampleData to true. The slug must be unique across the user's resumes. Returns the ID of the newly created resume. Requires authentication.",
+ "tags": ["Resumes"],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 64,
+ "description": "The name of the resume."
+ },
+ "slug": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 64,
+ "description": "The slug of the resume."
+ },
+ "tags": { "type": "array", "items": { "type": "string" }, "description": "The tags of the resume." },
+ "withSampleData": { "type": "boolean", "default": false }
+ },
+ "required": ["name", "slug", "tags"]
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "The ID of the newly created resume.",
+ "content": {
+ "application/json": { "schema": { "type": "string", "description": "The ID of the created resume." } }
+ }
+ },
+ "400": {
+ "description": "400",
+ "content": {
+ "application/json": {
+ "schema": {
+ "oneOf": [
+ {
+ "type": "object",
+ "properties": {
+ "defined": { "const": true },
+ "code": { "const": "RESUME_SLUG_ALREADY_EXISTS" },
+ "status": { "const": 400 },
+ "message": { "type": "string", "default": "A resume with this slug already exists." },
+ "data": {}
+ },
+ "required": ["defined", "code", "status", "message"]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "defined": { "const": false },
+ "code": { "type": "string" },
+ "status": { "type": "number" },
+ "message": { "type": "string" },
+ "data": {}
+ },
+ "required": ["defined", "code", "status", "message"]
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/resumes/{id}": {
+ "get": {
+ "operationId": "getResume",
+ "summary": "Get resume by ID",
+ "description": "Returns a single resume with its full data, identified by its unique ID. Only resumes belonging to the authenticated user can be retrieved. Requires authentication.",
+ "tags": ["Resumes"],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": { "type": "string", "description": "The ID of the resume." }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "The resume with its full data.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "id": { "type": "string", "description": "The ID of the resume." },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 64,
+ "description": "The name of the resume."
+ },
+ "slug": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 64,
+ "description": "The slug of the resume."
+ },
+ "tags": {
+ "type": "array",
+ "items": { "type": "string" },
+ "description": "The tags of the resume."
+ },
+ "isPublic": { "type": "boolean", "description": "Whether the resume is public." },
+ "isLocked": { "type": "boolean", "description": "Whether the resume is locked." },
+ "data": { "$ref": "#/components/schemas/ResumeData" },
+ "hasPassword": { "type": "boolean" }
+ },
+ "required": ["id", "name", "slug", "tags", "isPublic", "isLocked", "data", "hasPassword"]
+ }
+ }
+ }
+ }
+ }
+ },
+ "put": {
+ "operationId": "updateResume",
+ "summary": "Update a resume",
+ "description": "Updates one or more fields of a resume identified by its ID. All fields are optional; only provided fields will be updated. Locked resumes cannot be updated. Requires authentication.",
+ "tags": ["Resumes"],
+ "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 64,
+ "description": "The name of the resume."
+ },
+ "slug": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 64,
+ "description": "The slug of the resume."
+ },
+ "tags": { "type": "array", "items": { "type": "string" }, "description": "The tags of the resume." },
+ "data": { "$ref": "#/components/schemas/ResumeData" },
+ "isPublic": { "type": "boolean", "description": "Whether the resume is public." }
+ },
+ "required": []
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "The updated resume with its full data.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "id": { "type": "string", "description": "The ID of the resume." },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 64,
+ "description": "The name of the resume."
+ },
+ "slug": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 64,
+ "description": "The slug of the resume."
+ },
+ "tags": {
+ "type": "array",
+ "items": { "type": "string" },
+ "description": "The tags of the resume."
+ },
+ "isPublic": { "type": "boolean", "description": "Whether the resume is public." },
+ "isLocked": { "type": "boolean", "description": "Whether the resume is locked." },
+ "data": { "$ref": "#/components/schemas/ResumeData" }
+ },
+ "required": ["id", "name", "slug", "tags", "isPublic", "isLocked", "data"]
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "400",
+ "content": {
+ "application/json": {
+ "schema": {
+ "oneOf": [
+ {
+ "type": "object",
+ "properties": {
+ "defined": { "const": true },
+ "code": { "const": "RESUME_SLUG_ALREADY_EXISTS" },
+ "status": { "const": 400 },
+ "message": { "type": "string", "default": "A resume with this slug already exists." },
+ "data": {}
+ },
+ "required": ["defined", "code", "status", "message"]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "defined": { "const": false },
+ "code": { "type": "string" },
+ "status": { "type": "number" },
+ "message": { "type": "string" },
+ "data": {}
+ },
+ "required": ["defined", "code", "status", "message"]
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "patch": {
+ "operationId": "patchResume",
+ "summary": "Patch resume data",
+ "description": "Applies JSON Patch (RFC 6902) operations to partially update a resume's data. This allows small, targeted changes (e.g. updating a single field) without sending the entire resume object. Locked resumes cannot be patched. Requires authentication.",
+ "tags": ["Resumes"],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": { "type": "string", "description": "The ID of the resume to patch." }
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "operations": {
+ "type": "array",
+ "minItems": 1,
+ "items": {
+ "anyOf": [
+ {
+ "type": "object",
+ "properties": { "op": { "const": "add" }, "path": { "type": "string" }, "value": {} },
+ "required": ["op", "path"]
+ },
+ {
+ "type": "object",
+ "properties": { "op": { "const": "remove" }, "path": { "type": "string" } },
+ "required": ["op", "path"]
+ },
+ {
+ "type": "object",
+ "properties": { "op": { "const": "replace" }, "path": { "type": "string" }, "value": {} },
+ "required": ["op", "path"]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "op": { "const": "move" },
+ "path": { "type": "string" },
+ "from": { "type": "string" }
+ },
+ "required": ["op", "path", "from"]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "op": { "const": "copy" },
+ "path": { "type": "string" },
+ "from": { "type": "string" }
+ },
+ "required": ["op", "path", "from"]
+ },
+ {
+ "type": "object",
+ "properties": { "op": { "const": "test" }, "path": { "type": "string" }, "value": {} },
+ "required": ["op", "path"]
+ }
+ ]
+ },
+ "description": "An array of JSON Patch (RFC 6902) operations to apply to the resume data."
+ }
+ },
+ "required": ["operations"]
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "The patched resume with its full data.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "id": { "type": "string", "description": "The ID of the resume." },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 64,
+ "description": "The name of the resume."
+ },
+ "slug": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 64,
+ "description": "The slug of the resume."
+ },
+ "tags": {
+ "type": "array",
+ "items": { "type": "string" },
+ "description": "The tags of the resume."
+ },
+ "isPublic": { "type": "boolean", "description": "Whether the resume is public." },
+ "isLocked": { "type": "boolean", "description": "Whether the resume is locked." },
+ "data": { "$ref": "#/components/schemas/ResumeData" },
+ "hasPassword": { "type": "boolean" }
+ },
+ "required": ["id", "name", "slug", "tags", "isPublic", "isLocked", "data", "hasPassword"]
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "400",
+ "content": {
+ "application/json": {
+ "schema": {
+ "oneOf": [
+ {
+ "type": "object",
+ "properties": {
+ "defined": { "const": true },
+ "code": { "const": "INVALID_PATCH_OPERATIONS" },
+ "status": { "const": 400 },
+ "message": {
+ "type": "string",
+ "default": "The patch operations are invalid or produced an invalid resume."
+ },
+ "data": {}
+ },
+ "required": ["defined", "code", "status", "message"]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "defined": { "const": false },
+ "code": { "type": "string" },
+ "status": { "type": "number" },
+ "message": { "type": "string" },
+ "data": {}
+ },
+ "required": ["defined", "code", "status", "message"]
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "delete": {
+ "operationId": "deleteResume",
+ "summary": "Delete a resume",
+ "description": "Permanently deletes a resume and its associated files (screenshots, PDFs) from storage. Locked resumes cannot be deleted; unlock the resume first. Requires authentication.",
+ "tags": ["Resumes"],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": { "type": "string", "description": "The ID of the resume." }
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": { "application/json": { "schema": { "type": "object", "properties": {}, "required": [] } } }
+ },
+ "responses": {
+ "200": {
+ "description": "The resume and its associated files were deleted successfully.",
+ "content": { "application/json": { "schema": { "anyOf": [{ "not": {} }, { "not": {} }] } } }
+ }
+ }
+ }
+ },
+ "/resumes/{username}/{slug}": {
+ "get": {
+ "operationId": "getResumeBySlug",
+ "summary": "Get public resume by username and slug",
+ "description": "Returns a publicly shared resume identified by the owner's username and the resume's slug. If the resume is password-protected and the viewer has not yet verified the password, a 401 error with code NEED_PASSWORD is returned. No authentication required for public resumes; if authenticated as the owner, private resumes are also accessible.",
+ "tags": ["Resume Sharing"],
+ "parameters": [
+ { "name": "username", "in": "path", "required": true, "schema": { "type": "string" } },
+ { "name": "slug", "in": "path", "required": true, "schema": { "type": "string" } }
+ ],
+ "responses": {
+ "200": {
+ "description": "The public resume with its full data.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "id": { "type": "string", "description": "The ID of the resume." },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 64,
+ "description": "The name of the resume."
+ },
+ "slug": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 64,
+ "description": "The slug of the resume."
+ },
+ "tags": {
+ "type": "array",
+ "items": { "type": "string" },
+ "description": "The tags of the resume."
+ },
+ "isPublic": { "type": "boolean", "description": "Whether the resume is public." },
+ "isLocked": { "type": "boolean", "description": "Whether the resume is locked." },
+ "data": { "$ref": "#/components/schemas/ResumeData" }
+ },
+ "required": ["id", "name", "slug", "tags", "isPublic", "isLocked", "data"]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/resumes/import": {
+ "post": {
+ "operationId": "importResume",
+ "summary": "Import a resume",
+ "description": "Creates a new resume from an existing ResumeData object (e.g. from a previously exported JSON file). A random name and slug are generated automatically. Returns the ID of the imported resume. Requires authentication.",
+ "tags": ["Resumes"],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": { "data": { "$ref": "#/components/schemas/ResumeData" } },
+ "required": ["data"]
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "The ID of the imported resume.",
+ "content": {
+ "application/json": { "schema": { "type": "string", "description": "The ID of the imported resume." } }
+ }
+ },
+ "400": {
+ "description": "400",
+ "content": {
+ "application/json": {
+ "schema": {
+ "oneOf": [
+ {
+ "type": "object",
+ "properties": {
+ "defined": { "const": true },
+ "code": { "const": "RESUME_SLUG_ALREADY_EXISTS" },
+ "status": { "const": 400 },
+ "message": { "type": "string", "default": "A resume with this slug already exists." },
+ "data": {}
+ },
+ "required": ["defined", "code", "status", "message"]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "defined": { "const": false },
+ "code": { "type": "string" },
+ "status": { "type": "number" },
+ "message": { "type": "string" },
+ "data": {}
+ },
+ "required": ["defined", "code", "status", "message"]
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/resumes/{id}/lock": {
+ "post": {
+ "operationId": "setResumeLocked",
+ "summary": "Set resume lock status",
+ "description": "Toggles the locked status of a resume. When locked, a resume cannot be updated, patched, or deleted. Useful for protecting finalized resumes from accidental edits. Requires authentication.",
+ "tags": ["Resumes"],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": { "type": "string", "description": "The ID of the resume." }
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": { "isLocked": { "type": "boolean", "description": "Whether the resume is locked." } },
+ "required": ["isLocked"]
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "The resume lock status was updated successfully.",
+ "content": { "application/json": { "schema": { "anyOf": [{ "not": {} }, { "not": {} }] } } }
+ }
+ }
+ }
+ },
+ "/resumes/{id}/password": {
+ "put": {
+ "operationId": "setResumePassword",
+ "summary": "Set resume password",
+ "description": "Sets or updates a password on a resume. When a password is set, viewers of the public resume must enter the password before the resume data is revealed. The password must be between 6 and 64 characters. Requires authentication.",
+ "tags": ["Resume Sharing"],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": { "type": "string", "description": "The ID of the resume." }
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": { "password": { "type": "string", "minLength": 6, "maxLength": 64 } },
+ "required": ["password"]
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "The resume password was set successfully.",
+ "content": { "application/json": { "schema": { "anyOf": [{ "not": {} }, { "not": {} }] } } }
+ }
+ }
+ },
+ "delete": {
+ "operationId": "removeResumePassword",
+ "summary": "Remove resume password",
+ "description": "Removes password protection from a resume. After removal, the resume (if public) can be viewed without entering a password. Requires authentication.",
+ "tags": ["Resume Sharing"],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": { "type": "string", "description": "The ID of the resume." }
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": { "application/json": { "schema": { "type": "object", "properties": {}, "required": [] } } }
+ },
+ "responses": {
+ "200": {
+ "description": "The resume password was removed successfully.",
+ "content": { "application/json": { "schema": { "anyOf": [{ "not": {} }, { "not": {} }] } } }
+ }
+ }
+ }
+ },
+ "/resumes/{username}/{slug}/password/verify": {
+ "post": {
+ "operationId": "verifyResumePassword",
+ "summary": "Verify resume password",
+ "description": "Verifies a password for a password-protected public resume. On success, the viewer is granted access to view the resume data for the duration of their session. No authentication required.",
+ "tags": ["Resume Sharing"],
+ "parameters": [
+ {
+ "name": "username",
+ "in": "path",
+ "required": true,
+ "schema": { "type": "string", "minLength": 1, "description": "The username of the resume owner." }
+ },
+ {
+ "name": "slug",
+ "in": "path",
+ "required": true,
+ "schema": { "type": "string", "minLength": 1, "description": "The slug of the resume." }
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "password": { "type": "string", "minLength": 1, "description": "The password to verify." }
+ },
+ "required": ["password"]
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "The password was verified successfully and access has been granted.",
+ "content": { "application/json": { "schema": { "type": "boolean" } } }
+ }
+ }
+ }
+ },
+ "/resumes/{id}/duplicate": {
+ "post": {
+ "operationId": "duplicateResume",
+ "summary": "Duplicate a resume",
+ "description": "Creates a copy of an existing resume with the same data. Optionally override the name, slug, and tags for the duplicate. If not provided, the original resume's name, slug, and tags are used. Returns the ID of the duplicated resume. Requires authentication.",
+ "tags": ["Resumes"],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": { "type": "string", "description": "The ID of the resume." }
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 64,
+ "description": "The name of the resume."
+ },
+ "slug": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 64,
+ "description": "The slug of the resume."
+ },
+ "tags": { "type": "array", "items": { "type": "string" }, "description": "The tags of the resume." }
+ },
+ "required": ["name", "slug", "tags"]
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "The ID of the duplicated resume.",
+ "content": {
+ "application/json": { "schema": { "type": "string", "description": "The ID of the duplicated resume." } }
+ }
+ }
+ }
+ }
+ },
+ "/resumes/{id}/pdf": {
+ "get": {
+ "operationId": "exportResumePdf",
+ "summary": "Export resume as PDF",
+ "description": "Generates a PDF from the specified resume and uploads it to storage. Returns a URL to download the generated PDF file. If the request is made by an unauthenticated user (e.g. via a public share link), the resume's download count is incremented. Authentication is optional.",
+ "tags": ["Resume Export"],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": { "type": "string", "description": "The unique identifier of the resume to export." }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "The PDF was generated successfully. Returns a URL to download the file.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "url": { "type": "string", "description": "The URL to download the generated PDF file." }
+ },
+ "required": ["url"]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/resumes/{id}/screenshot": {
+ "get": {
+ "operationId": "getResumeScreenshot",
+ "summary": "Get resume screenshot",
+ "description": "Returns a URL to a screenshot image of the first page of the specified resume. Screenshots are cached for up to 6 hours and regenerated automatically when the resume is updated. Returns null if the screenshot cannot be generated. Requires authentication.",
+ "tags": ["Resume Export"],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": { "type": "string", "description": "The unique identifier of the resume." }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "The screenshot URL, or null if the screenshot could not be generated.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "anyOf": [{ "type": "string" }, { "type": "null" }],
+ "description": "The URL to the screenshot image, or null."
+ }
+ },
+ "required": ["url"]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/statistics/users": {
+ "get": {
+ "operationId": "getUserCount",
+ "summary": "Get total number of users",
+ "description": "Returns the total number of registered users on this Reactive Resume instance. The count is cached for up to 6 hours for performance. No authentication required.",
+ "tags": ["Platform Statistics"],
+ "responses": {
+ "200": {
+ "description": "The total number of registered users.",
+ "content": {
+ "application/json": {
+ "schema": { "type": "number", "description": "The total number of registered users." }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/statistics/resumes": {
+ "get": {
+ "operationId": "getResumeCount",
+ "summary": "Get total number of resumes",
+ "description": "Returns the total number of resumes created on this Reactive Resume instance. The count is cached for up to 6 hours for performance. No authentication required.",
+ "tags": ["Platform Statistics"],
+ "responses": {
+ "200": {
+ "description": "The total number of resumes created.",
+ "content": {
+ "application/json": {
+ "schema": { "type": "number", "description": "The total number of resumes created." }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/statistics/github/stars": {
+ "get": {
+ "operationId": "getGitHubStarCount",
+ "summary": "Get GitHub star count",
+ "description": "Returns the number of GitHub stars for the Reactive Resume repository. The count is cached for up to 6 hours and falls back to a last-known value if the GitHub API is unavailable. No authentication required.",
+ "tags": ["Platform Statistics"],
+ "responses": {
+ "200": {
+ "description": "The number of GitHub stars for the Reactive Resume repository.",
+ "content": {
+ "application/json": { "schema": { "type": "number", "description": "The number of GitHub stars." } }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
index 95bea76c9..6b925bf40 100644
--- a/package.json
+++ b/package.json
@@ -1,19 +1,18 @@
{
"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.12",
+ "description": "Reactive Resume is a free and open-source resume builder that simplifies the process of creating, updating, and sharing your resume.",
"license": "MIT",
- "type": "module",
- "packageManager": "pnpm@10.32.1+sha512.a706938f0e89ac1456b6563eab4edf1d1faf3368d1191fc5c59790e96dc918e4456ab2e67d613de1043d2e8c81f87303e6b40d4ffeca9df15ef1ad567348f2be",
- "repository": {
- "type": "git",
- "url": "https://github.com/amruthpillai/reactive-resume.git"
- },
"author": {
"name": "Amruth Pillai",
"email": "hello@amruthpillai.com",
"url": "https://amruthpillai.com"
},
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/amruthpillai/reactive-resume.git"
+ },
+ "type": "module",
"scripts": {
"build": "vite build",
"db:generate": "drizzle-kit generate",
@@ -23,9 +22,12 @@
"db:studio": "drizzle-kit studio",
"dev": "vite dev",
"docs:dev": "cd docs && npx mint dev",
+ "fmt": "oxfmt --check",
+ "fmt:fix": "oxfmt",
"knip": "knip",
"lingui:extract": "lingui extract --clean --overwrite",
- "lint": "biome check --write",
+ "lint": "oxlint --type-aware",
+ "lint:fix": "oxlint --type-aware --fix",
"preview": "vite preview",
"start": "node .output/server/index.mjs",
"typecheck": "tsgo --noEmit"
@@ -49,12 +51,12 @@
"@lingui/react": "^5.9.3",
"@modelcontextprotocol/sdk": "^1.27.1",
"@monaco-editor/react": "4.8.0-rc.3",
- "@orpc/client": "^1.13.7",
- "@orpc/json-schema": "^1.13.7",
- "@orpc/openapi": "^1.13.7",
- "@orpc/server": "^1.13.7",
- "@orpc/tanstack-query": "^1.13.7",
- "@orpc/zod": "^1.13.7",
+ "@orpc/client": "^1.13.8",
+ "@orpc/json-schema": "^1.13.8",
+ "@orpc/openapi": "^1.13.8",
+ "@orpc/server": "^1.13.8",
+ "@orpc/tanstack-query": "^1.13.8",
+ "@orpc/zod": "^1.13.8",
"@phosphor-icons/react": "^2.1.10",
"@phosphor-icons/web": "^2.1.2",
"@sindresorhus/slugify": "^3.0.0",
@@ -92,7 +94,7 @@
"jsonrepair": "^3.13.3",
"monaco-editor": "^0.55.1",
"motion": "^12.38.0",
- "nodemailer": "^8.0.2",
+ "nodemailer": "^8.0.3",
"pg": "^8.20.0",
"puppeteer-core": "^24.39.1",
"qrcode.react": "^4.2.0",
@@ -121,7 +123,6 @@
},
"devDependencies": {
"@babel/core": "^7.29.0",
- "@biomejs/biome": "^2.4.7",
"@lingui/babel-plugin-lingui-macro": "^5.9.3",
"@lingui/cli": "^5.9.3",
"@lingui/vite-plugin": "^5.9.3",
@@ -143,10 +144,14 @@
"node-addon-api": "^8.6.0",
"node-gyp": "^12.2.0",
"npm-check-updates": "^19.6.5",
+ "oxfmt": "^0.41.0",
+ "oxlint": "^1.56.0",
+ "oxlint-tsgolint": "^0.17.0",
"tsx": "^4.21.0",
"vite": "^8.0.0",
"vite-plugin-pwa": "^1.2.0"
},
+ "packageManager": "pnpm@10.32.1+sha512.a706938f0e89ac1456b6563eab4edf1d1faf3368d1191fc5c59790e96dc918e4456ab2e67d613de1043d2e8c81f87303e6b40d4ffeca9df15ef1ad567348f2be",
"pnpm": {
"overrides": {
"@isaacs/brace-expansion": "^5.0.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 252c3b0c7..4e3930b06 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -76,23 +76,23 @@ importers:
specifier: 4.8.0-rc.3
version: 4.8.0-rc.3(monaco-editor@0.55.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@orpc/client':
- specifier: ^1.13.7
- version: 1.13.7(@opentelemetry/api@1.9.0)
+ specifier: ^1.13.8
+ version: 1.13.8(@opentelemetry/api@1.9.0)
'@orpc/json-schema':
- specifier: ^1.13.7
- version: 1.13.7(@opentelemetry/api@1.9.0)(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0)
+ specifier: ^1.13.8
+ version: 1.13.8(@opentelemetry/api@1.9.0)(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0)
'@orpc/openapi':
- specifier: ^1.13.7
- version: 1.13.7(@opentelemetry/api@1.9.0)(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0)
+ specifier: ^1.13.8
+ version: 1.13.8(@opentelemetry/api@1.9.0)(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0)
'@orpc/server':
- specifier: ^1.13.7
- version: 1.13.7(@opentelemetry/api@1.9.0)(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0)
+ specifier: ^1.13.8
+ version: 1.13.8(@opentelemetry/api@1.9.0)(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0)
'@orpc/tanstack-query':
- specifier: ^1.13.7
- version: 1.13.7(@opentelemetry/api@1.9.0)(@orpc/client@1.13.7(@opentelemetry/api@1.9.0))(@tanstack/query-core@5.90.20)
+ specifier: ^1.13.8
+ version: 1.13.8(@opentelemetry/api@1.9.0)(@orpc/client@1.13.8(@opentelemetry/api@1.9.0))(@tanstack/query-core@5.90.20)
'@orpc/zod':
- specifier: ^1.13.7
- version: 1.13.7(@opentelemetry/api@1.9.0)(@orpc/contract@1.13.7(@opentelemetry/api@1.9.0))(@orpc/server@1.13.7(@opentelemetry/api@1.9.0)(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0))(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0)(zod@4.3.6)
+ specifier: ^1.13.8
+ version: 1.13.8(@opentelemetry/api@1.9.0)(@orpc/contract@1.13.8(@opentelemetry/api@1.9.0))(@orpc/server@1.13.8(@opentelemetry/api@1.9.0)(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0))(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0)(zod@4.3.6)
'@phosphor-icons/react':
specifier: ^2.1.10
version: 2.1.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
@@ -205,8 +205,8 @@ importers:
specifier: ^12.38.0
version: 12.38.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
nodemailer:
- specifier: ^8.0.2
- version: 8.0.2
+ specifier: ^8.0.3
+ version: 8.0.3
pg:
specifier: ^8.20.0
version: 8.20.0
@@ -286,9 +286,6 @@ importers:
'@babel/core':
specifier: ^7.29.0
version: 7.29.0
- '@biomejs/biome':
- specifier: ^2.4.7
- version: 2.4.7
'@lingui/babel-plugin-lingui-macro':
specifier: ^5.9.3
version: 5.9.3(babel-plugin-macros@3.1.0)(typescript@5.9.3)
@@ -352,6 +349,15 @@ importers:
npm-check-updates:
specifier: ^19.6.5
version: 19.6.5
+ oxfmt:
+ specifier: ^0.41.0
+ version: 0.41.0
+ oxlint:
+ specifier: ^1.56.0
+ version: 1.56.0(oxlint-tsgolint@0.17.0)
+ oxlint-tsgolint:
+ specifier: ^0.17.0
+ version: 0.17.0
tsx:
specifier: ^4.21.0
version: 4.21.0
@@ -1279,63 +1285,6 @@ packages:
'@better-fetch/fetch@1.1.21':
resolution: {integrity: sha512-/ImESw0sskqlVR94jB+5+Pxjf+xBwDZF/N5+y2/q4EqD7IARUTSpPfIo8uf39SYpCxyOCtbyYpUrZ3F/k0zT4A==}
- '@biomejs/biome@2.4.7':
- resolution: {integrity: sha512-vXrgcmNGZ4lpdwZSpMf1hWw1aWS6B+SyeSYKTLrNsiUsAdSRN0J4d/7mF3ogJFbIwFFSOL3wT92Zzxia/d5/ng==}
- engines: {node: '>=14.21.3'}
- hasBin: true
-
- '@biomejs/cli-darwin-arm64@2.4.7':
- resolution: {integrity: sha512-Oo0cF5mHzmvDmTXw8XSjhCia8K6YrZnk7aCS54+/HxyMdZMruMO3nfpDsrlar/EQWe41r1qrwKiCa2QDYHDzWA==}
- engines: {node: '>=14.21.3'}
- cpu: [arm64]
- os: [darwin]
-
- '@biomejs/cli-darwin-x64@2.4.7':
- resolution: {integrity: sha512-I+cOG3sd/7HdFtvDSnF9QQPrWguUH7zrkIMMykM3PtfWU9soTcS2yRb9Myq6MHmzbeCT08D1UmY+BaiMl5CcoQ==}
- engines: {node: '>=14.21.3'}
- cpu: [x64]
- os: [darwin]
-
- '@biomejs/cli-linux-arm64-musl@2.4.7':
- resolution: {integrity: sha512-I2NvM9KPb09jWml93O2/5WMfNR7Lee5Latag1JThDRMURVhPX74p9UDnyTw3Ae6cE1DgXfw7sqQgX7rkvpc0vw==}
- engines: {node: '>=14.21.3'}
- cpu: [arm64]
- os: [linux]
- libc: [musl]
-
- '@biomejs/cli-linux-arm64@2.4.7':
- resolution: {integrity: sha512-om6FugwmibzfP/6ALj5WRDVSND4H2G9X0nkI1HZpp2ySf9lW2j0X68oQSaHEnls6666oy4KDsc5RFjT4m0kV0w==}
- engines: {node: '>=14.21.3'}
- cpu: [arm64]
- os: [linux]
- libc: [glibc]
-
- '@biomejs/cli-linux-x64-musl@2.4.7':
- resolution: {integrity: sha512-00kx4YrBMU8374zd2wHuRV5wseh0rom5HqRND+vDldJPrWwQw+mzd/d8byI9hPx926CG+vWzq6AeiT7Yi5y59g==}
- engines: {node: '>=14.21.3'}
- cpu: [x64]
- os: [linux]
- libc: [musl]
-
- '@biomejs/cli-linux-x64@2.4.7':
- resolution: {integrity: sha512-bV8/uo2Tj+gumnk4sUdkerWyCPRabaZdv88IpbmDWARQQoA/Q0YaqPz1a+LSEDIL7OfrnPi9Hq1Llz4ZIGyIQQ==}
- engines: {node: '>=14.21.3'}
- cpu: [x64]
- os: [linux]
- libc: [glibc]
-
- '@biomejs/cli-win32-arm64@2.4.7':
- resolution: {integrity: sha512-hOUHBMlFCvDhu3WCq6vaBoG0dp0LkWxSEnEEsxxXvOa9TfT6ZBnbh72A/xBM7CBYB7WgwqboetzFEVDnMxelyw==}
- engines: {node: '>=14.21.3'}
- cpu: [arm64]
- os: [win32]
-
- '@biomejs/cli-win32-x64@2.4.7':
- resolution: {integrity: sha512-qEpGjSkPC3qX4ycbMUthXvi9CkRq7kZpkqMY1OyhmYlYLnANnooDQ7hDerM8+0NJ+DZKVnsIc07h30XOpt7LtQ==}
- engines: {node: '>=14.21.3'}
- cpu: [x64]
- os: [win32]
-
'@chevrotain/cst-dts-gen@10.5.0':
resolution: {integrity: sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw==}
@@ -2154,26 +2103,26 @@ packages:
resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==}
engines: {node: '>=8.0.0'}
- '@orpc/client@1.13.7':
- resolution: {integrity: sha512-qqmS28q0GOo9sfTstAc6cy0NoitYvdwZlgGBLXDgXsHNtSkkSm3nI5M1ohgWYpSL+lfP/jWriTbJJrBYPzyCiQ==}
+ '@orpc/client@1.13.8':
+ resolution: {integrity: sha512-7B8NDjBjP17Mrrgc/YeZl9b0YBu2Sk9/lKyVeG3755tyrAPLiezWuwQEaP9T45S2/+g8LTzFmV2R504Wn5R5MQ==}
- '@orpc/contract@1.13.7':
- resolution: {integrity: sha512-zRm+5tvn8DVM4DHJVxQEiuoM7mGdlgIQzRjTqnJMBRV0+rBNyZniokZCGwfJHKCjtBaAtJWieuJLQ+dFj3gbcw==}
+ '@orpc/contract@1.13.8':
+ resolution: {integrity: sha512-W8hjVYDnsHI63TgQUGB4bb+ldCqR5hdxL1o2b7ytkFEkXTft6HOrHHvv+ncmgK1c1XapD1ScsCj11zzxf5NUGQ==}
- '@orpc/interop@1.13.7':
- resolution: {integrity: sha512-mIO/g0el/yJ19Z73MW9au4SmPlZcuM1WSCXpZUBmU2wtJfY32bakUkC+V2J47xDvknMEAEpMHxwCiEhIjbzflg==}
+ '@orpc/interop@1.13.8':
+ resolution: {integrity: sha512-WFw227dJguO58uZfkk1bs/m6z4t5lgo5hc7m8+rQIRpt5CTKGEhdQW3/WkjGTYlBnCcaD0Eh48DrrcjtyBcOWA==}
- '@orpc/json-schema@1.13.7':
- resolution: {integrity: sha512-VELNkyne3RLz5l4tEs9FlNubbbfBmPJjHPCchYmeLGoeBH8bST08Hqj9mRzQ4TpplCY+L3tqTVl0s4QUVBB0qw==}
+ '@orpc/json-schema@1.13.8':
+ resolution: {integrity: sha512-XupAmUZdK1YrADuEvUv2zlrpuYIkP4T60iPWQO1kEWOXfCjQ5TqFAWVeWIB7nPnYiMGlBdVHFiud8L1Opy2FTw==}
- '@orpc/openapi-client@1.13.7':
- resolution: {integrity: sha512-0oWElGEtZ/EbfwOliMI6PccALpi8tp3aOyU746kJQRKTkSAwGohyRvfRA1R7BLbA3xwHTdNYB6ThaeYLvqiA0g==}
+ '@orpc/openapi-client@1.13.8':
+ resolution: {integrity: sha512-Cg7oDhbiO9bPpseRaFeWIhZFoA1bCF2pPxAJZj6/YtHkh+VSDI8W1xzbzoKNp2YHnhhJfgpIuVsHD42tX73+Mw==}
- '@orpc/openapi@1.13.7':
- resolution: {integrity: sha512-e7VCHxFZbTjR/+CKZM7TrwqSWDEppwiAOXyEN3uJxOalsrHhpxXrwTxvRc0+xo2HMgjVYlNO1F+mTeNh0YZKvA==}
+ '@orpc/openapi@1.13.8':
+ resolution: {integrity: sha512-fbbyJs9epMGeVIwkrxEuEJeGgqF7id4vCwCuXK2PlbDO7gaefG7FdlWdeUByyyavcbqezz0L8RLj4lAUNzWw1g==}
- '@orpc/server@1.13.7':
- resolution: {integrity: sha512-M0HimrQ0L/7OBwyVBZddUvKtx+du95QPv+wTxVRibU4P6TwgdmKJrLOwCOhGtMbieSQXHDBacscUWA6QDdUm3Q==}
+ '@orpc/server@1.13.8':
+ resolution: {integrity: sha512-hkZqlajENS1eMs5txxu2h3YxwPZqX7PNydYM3WusIhg5RKYhizRY1eMc1IHmE+ZB814+nnkN1dmtcu0XFGC09w==}
peerDependencies:
crossws: '>=0.3.4'
ws: '>=8.18.1'
@@ -2183,48 +2132,48 @@ packages:
ws:
optional: true
- '@orpc/shared@1.13.7':
- resolution: {integrity: sha512-yP0oDIC98sZHqSTmr4SUXJo4RNw9yias1GYVJTiVTXrRUEdniafkLrSkOrOHgrILP3w93sKiE69V3+/T0TNSOQ==}
+ '@orpc/shared@1.13.8':
+ resolution: {integrity: sha512-d7bZW2F8/ov6JFuGEMeh7XYZtW4+zgjxW5DKBv5tNkWmZEC5JJQz8l6Ym9ZRe2VyRzQgo5JarJGsVQlmqVVvhw==}
peerDependencies:
'@opentelemetry/api': '>=1.9.0'
peerDependenciesMeta:
'@opentelemetry/api':
optional: true
- '@orpc/standard-server-aws-lambda@1.13.7':
- resolution: {integrity: sha512-92WfdR2f3RvYarDW0JAGLzq68yOqNimD162/7DEddR8K1NlgWd+l6aAyCODnfXEj1tguS5/8xXbLUlXojXhhxw==}
+ '@orpc/standard-server-aws-lambda@1.13.8':
+ resolution: {integrity: sha512-w5C3fpjUli71LbgngGsqHWCR+1bWMudqZVwGrSaHWz7GxnjR2/HIxjt2VmeOsxzdjC/S5xyVuTd5pfH59UqpAw==}
- '@orpc/standard-server-fastify@1.13.7':
- resolution: {integrity: sha512-2I9yc+OUqf3J9hxys4MiR12eB5uhbEX+eFQ00VQROkMhWjjjj6Gp8eOVS+LlatrWDpD1Rn+VVqyi9yNpaFoviQ==}
+ '@orpc/standard-server-fastify@1.13.8':
+ resolution: {integrity: sha512-r5/rD0xodRZ79tXt89C6e5vCJWEolBw/NwjegptSPBdemeNY+eCZ3fiPCqVGMCGXSMTf50pakCkJ9vXOZppXQw==}
peerDependencies:
fastify: '>=5.6.1'
peerDependenciesMeta:
fastify:
optional: true
- '@orpc/standard-server-fetch@1.13.7':
- resolution: {integrity: sha512-Hj+41HAlao+JXuLffeLrPiADu8mhGqwVB34lf+JSLKGtZhxaX4n4MeZMYhFioExXC+/tanvSrbKKkJimfznIWQ==}
+ '@orpc/standard-server-fetch@1.13.8':
+ resolution: {integrity: sha512-g26Loo7GFTCF/S5QsM3Z6Xd9ZYs90K7jtRtEqbJh03YNrjecvZdpUKd/lTf/9kpJTBTQbhFxC9WCAJH4+8leFA==}
- '@orpc/standard-server-node@1.13.7':
- resolution: {integrity: sha512-ruRSytJhdDnab5rPdyBE6FYZBXdhdkFCEB9okekLUlIJ9BVhkOBe3JNO41iJlGf8JOVdBeO9lJTG/ShauofZQA==}
+ '@orpc/standard-server-node@1.13.8':
+ resolution: {integrity: sha512-gQAg2FUg6NIEJiP3UhtnmYV11yvMQO0K/VLY+PKyfOEm1HcbufdHWpHecGXleT8PjbpSSsVVQIRnk6lnmnJOxQ==}
- '@orpc/standard-server-peer@1.13.7':
- resolution: {integrity: sha512-mbjmkEVGtsWvGBBEieUuXdX+MAzllQZ0D9Z79kU4Ns9sVaBcvjCYSrL29/iXcYqVGTx23LS9PaYnIurAQejzSQ==}
+ '@orpc/standard-server-peer@1.13.8':
+ resolution: {integrity: sha512-ZyzWT6zZnLJkX15r04ecSDAJmkQ46PXTovORmK7RzOV47qIB7IryiRGR60U4WygBX0VDzZU8cgcXidZTx4v7oA==}
- '@orpc/standard-server@1.13.7':
- resolution: {integrity: sha512-5btxVTRAtgl9lmzg1XTCJYT8qd2QAAwcQ6XRvGXgLz56rSUCMf2vl3WeWPwlwiXXpueNvucPea/CaRGhJ9ZTeQ==}
+ '@orpc/standard-server@1.13.8':
+ resolution: {integrity: sha512-/v72eRSPFzWt6SoHDC04cjZfwdW94z3aib7dMBat32aK3eXwfRZmwPPmfVBQO/ZlJYlq+5rSdPoMKkSoirG/5Q==}
- '@orpc/tanstack-query@1.13.7':
- resolution: {integrity: sha512-MBxs86GBMjI5DXemTXn9W5jvgYEafdj33RpK5fXIrX+uEfwnJbmiWZCvFg7EOnDBUGLVnCWKLWow+tVspOFVmA==}
+ '@orpc/tanstack-query@1.13.8':
+ resolution: {integrity: sha512-ZUwwkAqoGPOCs8gBG7w6vVNxUOAJyTBVUuclmZoyTdbb5xgMVtUGCvyjiwaWOSoL4+N2urZBbvNdTbEMsuoqLQ==}
peerDependencies:
- '@orpc/client': 1.13.7
+ '@orpc/client': 1.13.8
'@tanstack/query-core': '>=5.80.2'
- '@orpc/zod@1.13.7':
- resolution: {integrity: sha512-m6nA1fISfBc7E9L0MslIfJvSL5ImEXQnBbD4RbOJGnejT1AFWwH2hqeBTJIs/AFPlOHWSJcVZYJW6N65iuUIcQ==}
+ '@orpc/zod@1.13.8':
+ resolution: {integrity: sha512-ClX08h4qyHHQf5vTdbmzano6b26ydluiNVQYVf+KW4cdJnZyOssv8agAyjooqIxxic4qynTA5oIIxnH9VHLNJg==}
peerDependencies:
- '@orpc/contract': 1.13.7
- '@orpc/server': 1.13.7
+ '@orpc/contract': 1.13.8
+ '@orpc/server': 1.13.8
zod: '>=3.25.0'
'@oxc-project/runtime@0.115.0':
@@ -2342,6 +2291,280 @@ packages:
cpu: [x64]
os: [win32]
+ '@oxfmt/binding-android-arm-eabi@0.41.0':
+ resolution: {integrity: sha512-REfrqeMKGkfMP+m/ScX4f5jJBSmVNYcpoDF8vP8f8eYPDuPGZmzp56NIUsYmx3h7f6NzC6cE3gqh8GDWrJHCKw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [android]
+
+ '@oxfmt/binding-android-arm64@0.41.0':
+ resolution: {integrity: sha512-s0b1dxNgb2KomspFV2LfogC2XtSJB42POXF4bMCLJyvQmAGos4ZtjGPfQreToQEaY0FQFjz3030ggI36rF1q5g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [android]
+
+ '@oxfmt/binding-darwin-arm64@0.41.0':
+ resolution: {integrity: sha512-EGXGualADbv/ZmamE7/2DbsrYmjoPlAmHEpTL4vapLF4EfVD6fr8/uQDFnPJkUBjiSWFJZtFNsGeN1B6V3owmA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@oxfmt/binding-darwin-x64@0.41.0':
+ resolution: {integrity: sha512-WxySJEvdQQYMmyvISH3qDpTvoS0ebnIP63IMxLLWowJyPp/AAH0hdWtlo+iGNK5y3eVfa5jZguwNaQkDKWpGSw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [darwin]
+
+ '@oxfmt/binding-freebsd-x64@0.41.0':
+ resolution: {integrity: sha512-Y2kzMkv3U3oyuYaR4wTfGjOTYTXiFC/hXmG0yVASKkbh02BJkvD98Ij8bIevr45hNZ0DmZEgqiXF+9buD4yMYQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@oxfmt/binding-linux-arm-gnueabihf@0.41.0':
+ resolution: {integrity: sha512-ptazDjdUyhket01IjPTT6ULS1KFuBfTUU97osTP96X5y/0oso+AgAaJzuH81oP0+XXyrWIHbRzozSAuQm4p48g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@oxfmt/binding-linux-arm-musleabihf@0.41.0':
+ resolution: {integrity: sha512-UkoL2OKxFD+56bPEBcdGn+4juTW4HRv/T6w1dIDLnvKKWr6DbarB/mtHXlADKlFiJubJz8pRkttOR7qjYR6lTA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@oxfmt/binding-linux-arm64-gnu@0.41.0':
+ resolution: {integrity: sha512-gofu0PuumSOHYczD8p62CPY4UF6ee+rSLZJdUXkpwxg6pILiwSDBIouPskjF/5nF3A7QZTz2O9KFNkNxxFN9tA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxfmt/binding-linux-arm64-musl@0.41.0':
+ resolution: {integrity: sha512-VfVZxL0+6RU86T8F8vKiDBa+iHsr8PAjQmKGBzSCAX70b6x+UOMFl+2dNihmKmUwqkCazCPfYjt6SuAPOeQJ3g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@oxfmt/binding-linux-ppc64-gnu@0.41.0':
+ resolution: {integrity: sha512-bwzokz2eGvdfJbc0i+zXMJ4BBjQPqg13jyWpEEZDOrBCQ91r8KeY2Mi2kUeuMTZNFXju+jcAbAbpyJxRGla0eg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxfmt/binding-linux-riscv64-gnu@0.41.0':
+ resolution: {integrity: sha512-POLM//PCH9uqDeNDwWL3b3DkMmI3oI2cU6hwc2lnztD1o7dzrQs3R9nq555BZ6wI7t2lyhT9CS+CRaz5X0XqLA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxfmt/binding-linux-riscv64-musl@0.41.0':
+ resolution: {integrity: sha512-NNK7PzhFqLUwx/G12Xtm6scGv7UITvyGdAR5Y+TlqsG+essnuRWR4jRNODWRjzLZod0T3SayRbnkSIWMBov33w==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [musl]
+
+ '@oxfmt/binding-linux-s390x-gnu@0.41.0':
+ resolution: {integrity: sha512-qVf/zDC5cN9eKe4qI/O/m445er1IRl6swsSl7jHkqmOSVfknwCe5JXitYjZca+V/cNJSU/xPlC5EFMabMMFDpw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxfmt/binding-linux-x64-gnu@0.41.0':
+ resolution: {integrity: sha512-ojxYWu7vUb6ysYqVCPHuAPVZHAI40gfZ0PDtZAMwVmh2f0V8ExpPIKoAKr7/8sNbAXJBBpZhs2coypIo2jJX4w==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxfmt/binding-linux-x64-musl@0.41.0':
+ resolution: {integrity: sha512-O2exZLBxoCMIv2vlvcbkdedazJPTdG0VSup+0QUCfYQtx751zCZNboX2ZUOiQ/gDTdhtXvSiot0h6GEGkOyalA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@oxfmt/binding-openharmony-arm64@0.41.0':
+ resolution: {integrity: sha512-N+31/VoL+z+NNBt8viy3I4NaIdPbiYeOnB884LKqvXldaE2dRztdPv3q5ipfZYv0RwFp7JfqS4I27K/DSHCakg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@oxfmt/binding-win32-arm64-msvc@0.41.0':
+ resolution: {integrity: sha512-Z7NAtu/RN8kjCQ1y5oDD0nTAeRswh3GJ93qwcW51srmidP7XPBmZbLlwERu1W5veCevQJtPS9xmkpcDTYsGIwQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [win32]
+
+ '@oxfmt/binding-win32-ia32-msvc@0.41.0':
+ resolution: {integrity: sha512-uNxxP3l4bJ6VyzIeRqCmBU2Q0SkCFgIhvx9/9dJ9V8t/v+jP1IBsuaLwCXGR8JPHtkj4tFp+RHtUmU2ZYAUpMA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [ia32]
+ os: [win32]
+
+ '@oxfmt/binding-win32-x64-msvc@0.41.0':
+ resolution: {integrity: sha512-49ZSpbZ1noozyPapE8SUOSm3IN0Ze4b5nkO+4+7fq6oEYQQJFhE0saj5k/Gg4oewVPdjn0L3ZFeWk2Vehjcw7A==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [win32]
+
+ '@oxlint-tsgolint/darwin-arm64@0.17.0':
+ resolution: {integrity: sha512-z3XwCDuOAKgk7bO4y5tyH8Zogwr51G56R0XGKC3tlAbrAq8DecoxAd3qhRZqWBMG2Gzl5bWU3Ghu7lrxuLPzYw==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@oxlint-tsgolint/darwin-x64@0.17.0':
+ resolution: {integrity: sha512-TZgVXy0MtI8nt0MYiceuZhHPwHcwlIZ/YwzFTAKrgdHiTvVzFbqHVdXi5wbZfT/o1nHGw9fbGWPlb6qKZ4uZ9Q==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@oxlint-tsgolint/linux-arm64@0.17.0':
+ resolution: {integrity: sha512-IDfhFl/Y8bjidCvAP6QAxVyBsl78TmfCHlfjtEv2XtJXgYmIwzv6muO18XMp74SZ2qAyD4y2n2dUedrmghGHeA==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@oxlint-tsgolint/linux-x64@0.17.0':
+ resolution: {integrity: sha512-Bgdgqx/m8EnfjmmlRLEeYy9Yhdt1GdFrMr5mTu/NyLRGkB1C9VLAikdxB7U9QambAGTAmjMbHNFDFk8Vx69Huw==}
+ cpu: [x64]
+ os: [linux]
+
+ '@oxlint-tsgolint/win32-arm64@0.17.0':
+ resolution: {integrity: sha512-dO6wyKMDqFWh1vwr+zNZS7/ovlfGgl4S3P1LDy4CKjP6V6NGtdmEwWkWax8j/I8RzGZdfXKnoUfb/qhVg5bx0w==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@oxlint-tsgolint/win32-x64@0.17.0':
+ resolution: {integrity: sha512-lPGYFp3yX2nh6hLTpIuMnJbZnt3Df42VkoA/fSkMYi2a/LXdDytQGpgZOrb5j47TICARd34RauKm0P3OA4Oxbw==}
+ cpu: [x64]
+ os: [win32]
+
+ '@oxlint/binding-android-arm-eabi@1.56.0':
+ resolution: {integrity: sha512-IyfYPthZyiSKwAv/dLjeO18SaK8MxLI9Yss2JrRDyweQAkuL3LhEy7pwIwI7uA3KQc1Vdn20kdmj3q0oUIQL6A==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [android]
+
+ '@oxlint/binding-android-arm64@1.56.0':
+ resolution: {integrity: sha512-Ga5zYrzH6vc/VFxhn6MmyUnYEfy9vRpwTIks99mY3j6Nz30yYpIkWryI0QKPCgvGUtDSXVLEaMum5nA+WrNOSg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [android]
+
+ '@oxlint/binding-darwin-arm64@1.56.0':
+ resolution: {integrity: sha512-ogmbdJysnw/D4bDcpf1sPLpFThZ48lYp4aKYm10Z/6Nh1SON6NtnNhTNOlhEY296tDFItsZUz+2tgcSYqh8Eyw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@oxlint/binding-darwin-x64@1.56.0':
+ resolution: {integrity: sha512-x8QE1h+RAtQ2g+3KPsP6Fk/tdz6zJQUv5c7fTrJxXV3GHOo+Ry5p/PsogU4U+iUZg0rj6hS+E4xi+mnwwlDCWQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [darwin]
+
+ '@oxlint/binding-freebsd-x64@1.56.0':
+ resolution: {integrity: sha512-6G+WMZvwJpMvY7my+/SHEjb7BTk/PFbePqLpmVmUJRIsJMy/UlyYqjpuh0RCgYYkPLcnXm1rUM04kbTk8yS1Yg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@oxlint/binding-linux-arm-gnueabihf@1.56.0':
+ resolution: {integrity: sha512-YYHBsk/sl7fYwQOok+6W5lBPeUEvisznV/HZD2IfZmF3Bns6cPC3Z0vCtSEOaAWTjYWN3jVsdu55jMxKlsdlhg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@oxlint/binding-linux-arm-musleabihf@1.56.0':
+ resolution: {integrity: sha512-+AZK8rOUr78y8WT6XkDb04IbMRqauNV+vgT6f8ZLOH8wnpQ9i7Nol0XLxAu+Cq7Sb+J9wC0j6Km5hG8rj47/yQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@oxlint/binding-linux-arm64-gnu@1.56.0':
+ resolution: {integrity: sha512-urse2SnugwJRojUkGSSeH2LPMaje5Q50yQtvtL9HFckiyeqXzoFwOAZqD5TR29R2lq7UHidfFDM9EGcchcbb8A==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxlint/binding-linux-arm64-musl@1.56.0':
+ resolution: {integrity: sha512-rkTZkBfJ4TYLjansjSzL6mgZOdN5IvUnSq3oNJSLwBcNvy3dlgQtpHPrRxrCEbbcp7oQ6If0tkNaqfOsphYZ9g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@oxlint/binding-linux-ppc64-gnu@1.56.0':
+ resolution: {integrity: sha512-uqL1kMH3u69/e1CH2EJhP3CP28jw2ExLsku4o8RVAZ7fySo9zOyI2fy9pVlTAp4voBLVgzndXi3SgtdyCTa2aA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxlint/binding-linux-riscv64-gnu@1.56.0':
+ resolution: {integrity: sha512-j0CcMBOgV6KsRaBdsebIeiy7hCjEvq2KdEsiULf2LZqAq0v1M1lWjelhCV57LxsqaIGChXFuFJ0RiFrSRHPhSg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxlint/binding-linux-riscv64-musl@1.56.0':
+ resolution: {integrity: sha512-7VDOiL8cDG3DQ/CY3yKjbV1c4YPvc4vH8qW09Vv+5ukq3l/Kcyr6XGCd5NvxUmxqDb2vjMpM+eW/4JrEEsUetA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [musl]
+
+ '@oxlint/binding-linux-s390x-gnu@1.56.0':
+ resolution: {integrity: sha512-JGRpX0M+ikD3WpwJ7vKcHKV6Kg0dT52BW2Eu2BupXotYeqGXBrbY+QPkAyKO6MNgKozyTNaRh3r7g+VWgyAQYQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxlint/binding-linux-x64-gnu@1.56.0':
+ resolution: {integrity: sha512-dNaICPvtmuxFP/VbqdofrLqdS3bM/AKJN3LMJD52si44ea7Be1cBk6NpfIahaysG9Uo+L98QKddU9CD5L8UHnQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxlint/binding-linux-x64-musl@1.56.0':
+ resolution: {integrity: sha512-pF1vOtM+GuXmbklM1hV8WMsn6tCNPvkUzklj/Ej98JhlanbmA2RB1BILgOpwSuCTRTIYx2MXssmEyQQ90QF5aA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@oxlint/binding-openharmony-arm64@1.56.0':
+ resolution: {integrity: sha512-bp8NQ4RE6fDIFLa4bdBiOA+TAvkNkg+rslR+AvvjlLTYXLy9/uKAYLQudaQouWihLD/hgkrXIKKzXi5IXOewwg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@oxlint/binding-win32-arm64-msvc@1.56.0':
+ resolution: {integrity: sha512-PxT4OJDfMOQBzo3OlzFb9gkoSD+n8qSBxyVq2wQSZIHFQYGEqIRTo9M0ZStvZm5fdhMqaVYpOnJvH2hUMEDk/g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [win32]
+
+ '@oxlint/binding-win32-ia32-msvc@1.56.0':
+ resolution: {integrity: sha512-PTRy6sIEPqy2x8PTP1baBNReN/BNEFmde0L+mYeHmjXE1Vlcc9+I5nsqENsB2yAm5wLkzPoTNCMY/7AnabT4/A==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [ia32]
+ os: [win32]
+
+ '@oxlint/binding-win32-x64-msvc@1.56.0':
+ resolution: {integrity: sha512-ZHa0clocjLmIDr+1LwoWtxRcoYniAvERotvwKUYKhH41NVfl0Y4LNbyQkwMZzwDvKklKGvGZ5+DAG58/Ik47tQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [win32]
+
'@phosphor-icons/react@2.1.10':
resolution: {integrity: sha512-vt8Tvq8GLjheAZZYa+YG/pW7HDbov8El/MANW8pOAz4eGxrwhnbfrQZq0Cp4q8zBEu8NIhHdnr+r8thnfRSNYA==}
engines: {node: '>=10'}
@@ -5932,8 +6155,8 @@ packages:
node-rsa@1.1.1:
resolution: {integrity: sha512-Jd4cvbJMryN21r5HgxQOpMEqv+ooke/korixNNK3mGqfGJmy0M77WDDzo/05969+OkMy3XW1UuZsSmW9KQm7Fw==}
- nodemailer@8.0.2:
- resolution: {integrity: sha512-zbj002pZAIkWQFxyAaqoxvn+zoIwRnS40hgjqTXudKOOJkiFFgBeNqjgD3/YCR12sZnrghWYBY+yP1ZucdDRpw==}
+ nodemailer@8.0.3:
+ resolution: {integrity: sha512-JQNBqvK+bj3NMhUFR3wmCl3SYcOeMotDiwDBvIoCuQdF0PvlIY0BH+FJ2CG7u4cXKPChplE78oowlH/Otsc4ZQ==}
engines: {node: '>=6.0.0'}
nopt@9.0.0:
@@ -6048,6 +6271,25 @@ packages:
oxc-resolver@11.19.1:
resolution: {integrity: sha512-qE/CIg/spwrTBFt5aKmwe3ifeDdLfA2NESN30E42X/lII5ClF8V7Wt6WIJhcGZjp0/Q+nQ+9vgxGk//xZNX2hg==}
+ oxfmt@0.41.0:
+ resolution: {integrity: sha512-sKLdJZdQ3bw6x9qKiT7+eID4MNEXlDHf5ZacfIircrq6Qwjk0L6t2/JQlZZrVHTXJawK3KaMuBoJnEJPcqCEdg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+
+ oxlint-tsgolint@0.17.0:
+ resolution: {integrity: sha512-TdrKhDZCgEYqONFo/j+KvGan7/k3tP5Ouz88wCqpOvJtI2QmcLfGsm1fcMvDnTik48Jj6z83IJBqlkmK9DnY1A==}
+ hasBin: true
+
+ oxlint@1.56.0:
+ resolution: {integrity: sha512-Q+5Mj5PVaH/R6/fhMMFzw4dT+KPB+kQW4kaL8FOIq7tfhlnEVp6+3lcWqFruuTNlUo9srZUW3qH7Id4pskeR6g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+ peerDependencies:
+ oxlint-tsgolint: '>=0.15.0'
+ peerDependenciesMeta:
+ oxlint-tsgolint:
+ optional: true
+
p-map@7.0.4:
resolution: {integrity: sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==}
engines: {node: '>=18'}
@@ -6975,6 +7217,10 @@ packages:
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
engines: {node: '>=12.0.0'}
+ tinypool@2.1.0:
+ resolution: {integrity: sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==}
+ engines: {node: ^20.0.0 || >=22.0.0}
+
tldts-core@6.1.86:
resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==}
@@ -9063,41 +9309,6 @@ snapshots:
'@better-fetch/fetch@1.1.21': {}
- '@biomejs/biome@2.4.7':
- optionalDependencies:
- '@biomejs/cli-darwin-arm64': 2.4.7
- '@biomejs/cli-darwin-x64': 2.4.7
- '@biomejs/cli-linux-arm64': 2.4.7
- '@biomejs/cli-linux-arm64-musl': 2.4.7
- '@biomejs/cli-linux-x64': 2.4.7
- '@biomejs/cli-linux-x64-musl': 2.4.7
- '@biomejs/cli-win32-arm64': 2.4.7
- '@biomejs/cli-win32-x64': 2.4.7
-
- '@biomejs/cli-darwin-arm64@2.4.7':
- optional: true
-
- '@biomejs/cli-darwin-x64@2.4.7':
- optional: true
-
- '@biomejs/cli-linux-arm64-musl@2.4.7':
- optional: true
-
- '@biomejs/cli-linux-arm64@2.4.7':
- optional: true
-
- '@biomejs/cli-linux-x64-musl@2.4.7':
- optional: true
-
- '@biomejs/cli-linux-x64@2.4.7':
- optional: true
-
- '@biomejs/cli-win32-arm64@2.4.7':
- optional: true
-
- '@biomejs/cli-win32-x64@2.4.7':
- optional: true
-
'@chevrotain/cst-dts-gen@10.5.0':
dependencies:
'@chevrotain/gast': 10.5.0
@@ -9807,33 +10018,33 @@ snapshots:
'@opentelemetry/api@1.9.0': {}
- '@orpc/client@1.13.7(@opentelemetry/api@1.9.0)':
+ '@orpc/client@1.13.8(@opentelemetry/api@1.9.0)':
dependencies:
- '@orpc/shared': 1.13.7(@opentelemetry/api@1.9.0)
- '@orpc/standard-server': 1.13.7(@opentelemetry/api@1.9.0)
- '@orpc/standard-server-fetch': 1.13.7(@opentelemetry/api@1.9.0)
- '@orpc/standard-server-peer': 1.13.7(@opentelemetry/api@1.9.0)
+ '@orpc/shared': 1.13.8(@opentelemetry/api@1.9.0)
+ '@orpc/standard-server': 1.13.8(@opentelemetry/api@1.9.0)
+ '@orpc/standard-server-fetch': 1.13.8(@opentelemetry/api@1.9.0)
+ '@orpc/standard-server-peer': 1.13.8(@opentelemetry/api@1.9.0)
transitivePeerDependencies:
- '@opentelemetry/api'
- '@orpc/contract@1.13.7(@opentelemetry/api@1.9.0)':
+ '@orpc/contract@1.13.8(@opentelemetry/api@1.9.0)':
dependencies:
- '@orpc/client': 1.13.7(@opentelemetry/api@1.9.0)
- '@orpc/shared': 1.13.7(@opentelemetry/api@1.9.0)
+ '@orpc/client': 1.13.8(@opentelemetry/api@1.9.0)
+ '@orpc/shared': 1.13.8(@opentelemetry/api@1.9.0)
'@standard-schema/spec': 1.1.0
openapi-types: 12.1.3
transitivePeerDependencies:
- '@opentelemetry/api'
- '@orpc/interop@1.13.7': {}
+ '@orpc/interop@1.13.8': {}
- '@orpc/json-schema@1.13.7(@opentelemetry/api@1.9.0)(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0)':
+ '@orpc/json-schema@1.13.8(@opentelemetry/api@1.9.0)(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0)':
dependencies:
- '@orpc/contract': 1.13.7(@opentelemetry/api@1.9.0)
- '@orpc/interop': 1.13.7
- '@orpc/openapi': 1.13.7(@opentelemetry/api@1.9.0)(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0)
- '@orpc/server': 1.13.7(@opentelemetry/api@1.9.0)(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0)
- '@orpc/shared': 1.13.7(@opentelemetry/api@1.9.0)
+ '@orpc/contract': 1.13.8(@opentelemetry/api@1.9.0)
+ '@orpc/interop': 1.13.8
+ '@orpc/openapi': 1.13.8(@opentelemetry/api@1.9.0)(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0)
+ '@orpc/server': 1.13.8(@opentelemetry/api@1.9.0)(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0)
+ '@orpc/shared': 1.13.8(@opentelemetry/api@1.9.0)
json-schema-typed: 8.0.2
transitivePeerDependencies:
- '@opentelemetry/api'
@@ -9841,24 +10052,24 @@ snapshots:
- fastify
- ws
- '@orpc/openapi-client@1.13.7(@opentelemetry/api@1.9.0)':
+ '@orpc/openapi-client@1.13.8(@opentelemetry/api@1.9.0)':
dependencies:
- '@orpc/client': 1.13.7(@opentelemetry/api@1.9.0)
- '@orpc/contract': 1.13.7(@opentelemetry/api@1.9.0)
- '@orpc/shared': 1.13.7(@opentelemetry/api@1.9.0)
- '@orpc/standard-server': 1.13.7(@opentelemetry/api@1.9.0)
+ '@orpc/client': 1.13.8(@opentelemetry/api@1.9.0)
+ '@orpc/contract': 1.13.8(@opentelemetry/api@1.9.0)
+ '@orpc/shared': 1.13.8(@opentelemetry/api@1.9.0)
+ '@orpc/standard-server': 1.13.8(@opentelemetry/api@1.9.0)
transitivePeerDependencies:
- '@opentelemetry/api'
- '@orpc/openapi@1.13.7(@opentelemetry/api@1.9.0)(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0)':
+ '@orpc/openapi@1.13.8(@opentelemetry/api@1.9.0)(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0)':
dependencies:
- '@orpc/client': 1.13.7(@opentelemetry/api@1.9.0)
- '@orpc/contract': 1.13.7(@opentelemetry/api@1.9.0)
- '@orpc/interop': 1.13.7
- '@orpc/openapi-client': 1.13.7(@opentelemetry/api@1.9.0)
- '@orpc/server': 1.13.7(@opentelemetry/api@1.9.0)(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0)
- '@orpc/shared': 1.13.7(@opentelemetry/api@1.9.0)
- '@orpc/standard-server': 1.13.7(@opentelemetry/api@1.9.0)
+ '@orpc/client': 1.13.8(@opentelemetry/api@1.9.0)
+ '@orpc/contract': 1.13.8(@opentelemetry/api@1.9.0)
+ '@orpc/interop': 1.13.8
+ '@orpc/openapi-client': 1.13.8(@opentelemetry/api@1.9.0)
+ '@orpc/server': 1.13.8(@opentelemetry/api@1.9.0)(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0)
+ '@orpc/shared': 1.13.8(@opentelemetry/api@1.9.0)
+ '@orpc/standard-server': 1.13.8(@opentelemetry/api@1.9.0)
json-schema-typed: 8.0.2
rou3: 0.7.12
transitivePeerDependencies:
@@ -9867,18 +10078,18 @@ snapshots:
- fastify
- ws
- '@orpc/server@1.13.7(@opentelemetry/api@1.9.0)(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0)':
+ '@orpc/server@1.13.8(@opentelemetry/api@1.9.0)(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0)':
dependencies:
- '@orpc/client': 1.13.7(@opentelemetry/api@1.9.0)
- '@orpc/contract': 1.13.7(@opentelemetry/api@1.9.0)
- '@orpc/interop': 1.13.7
- '@orpc/shared': 1.13.7(@opentelemetry/api@1.9.0)
- '@orpc/standard-server': 1.13.7(@opentelemetry/api@1.9.0)
- '@orpc/standard-server-aws-lambda': 1.13.7(@opentelemetry/api@1.9.0)
- '@orpc/standard-server-fastify': 1.13.7(@opentelemetry/api@1.9.0)
- '@orpc/standard-server-fetch': 1.13.7(@opentelemetry/api@1.9.0)
- '@orpc/standard-server-node': 1.13.7(@opentelemetry/api@1.9.0)
- '@orpc/standard-server-peer': 1.13.7(@opentelemetry/api@1.9.0)
+ '@orpc/client': 1.13.8(@opentelemetry/api@1.9.0)
+ '@orpc/contract': 1.13.8(@opentelemetry/api@1.9.0)
+ '@orpc/interop': 1.13.8
+ '@orpc/shared': 1.13.8(@opentelemetry/api@1.9.0)
+ '@orpc/standard-server': 1.13.8(@opentelemetry/api@1.9.0)
+ '@orpc/standard-server-aws-lambda': 1.13.8(@opentelemetry/api@1.9.0)
+ '@orpc/standard-server-fastify': 1.13.8(@opentelemetry/api@1.9.0)
+ '@orpc/standard-server-fetch': 1.13.8(@opentelemetry/api@1.9.0)
+ '@orpc/standard-server-node': 1.13.8(@opentelemetry/api@1.9.0)
+ '@orpc/standard-server-peer': 1.13.8(@opentelemetry/api@1.9.0)
cookie: 1.1.1
optionalDependencies:
crossws: 0.4.4(srvx@0.11.12)
@@ -9887,73 +10098,73 @@ snapshots:
- '@opentelemetry/api'
- fastify
- '@orpc/shared@1.13.7(@opentelemetry/api@1.9.0)':
+ '@orpc/shared@1.13.8(@opentelemetry/api@1.9.0)':
dependencies:
radash: 12.1.1
type-fest: 5.4.4
optionalDependencies:
'@opentelemetry/api': 1.9.0
- '@orpc/standard-server-aws-lambda@1.13.7(@opentelemetry/api@1.9.0)':
+ '@orpc/standard-server-aws-lambda@1.13.8(@opentelemetry/api@1.9.0)':
dependencies:
- '@orpc/shared': 1.13.7(@opentelemetry/api@1.9.0)
- '@orpc/standard-server': 1.13.7(@opentelemetry/api@1.9.0)
- '@orpc/standard-server-fetch': 1.13.7(@opentelemetry/api@1.9.0)
- '@orpc/standard-server-node': 1.13.7(@opentelemetry/api@1.9.0)
+ '@orpc/shared': 1.13.8(@opentelemetry/api@1.9.0)
+ '@orpc/standard-server': 1.13.8(@opentelemetry/api@1.9.0)
+ '@orpc/standard-server-fetch': 1.13.8(@opentelemetry/api@1.9.0)
+ '@orpc/standard-server-node': 1.13.8(@opentelemetry/api@1.9.0)
transitivePeerDependencies:
- '@opentelemetry/api'
- '@orpc/standard-server-fastify@1.13.7(@opentelemetry/api@1.9.0)':
+ '@orpc/standard-server-fastify@1.13.8(@opentelemetry/api@1.9.0)':
dependencies:
- '@orpc/shared': 1.13.7(@opentelemetry/api@1.9.0)
- '@orpc/standard-server': 1.13.7(@opentelemetry/api@1.9.0)
- '@orpc/standard-server-node': 1.13.7(@opentelemetry/api@1.9.0)
+ '@orpc/shared': 1.13.8(@opentelemetry/api@1.9.0)
+ '@orpc/standard-server': 1.13.8(@opentelemetry/api@1.9.0)
+ '@orpc/standard-server-node': 1.13.8(@opentelemetry/api@1.9.0)
transitivePeerDependencies:
- '@opentelemetry/api'
- '@orpc/standard-server-fetch@1.13.7(@opentelemetry/api@1.9.0)':
+ '@orpc/standard-server-fetch@1.13.8(@opentelemetry/api@1.9.0)':
dependencies:
- '@orpc/shared': 1.13.7(@opentelemetry/api@1.9.0)
- '@orpc/standard-server': 1.13.7(@opentelemetry/api@1.9.0)
+ '@orpc/shared': 1.13.8(@opentelemetry/api@1.9.0)
+ '@orpc/standard-server': 1.13.8(@opentelemetry/api@1.9.0)
transitivePeerDependencies:
- '@opentelemetry/api'
- '@orpc/standard-server-node@1.13.7(@opentelemetry/api@1.9.0)':
+ '@orpc/standard-server-node@1.13.8(@opentelemetry/api@1.9.0)':
dependencies:
- '@orpc/shared': 1.13.7(@opentelemetry/api@1.9.0)
- '@orpc/standard-server': 1.13.7(@opentelemetry/api@1.9.0)
- '@orpc/standard-server-fetch': 1.13.7(@opentelemetry/api@1.9.0)
+ '@orpc/shared': 1.13.8(@opentelemetry/api@1.9.0)
+ '@orpc/standard-server': 1.13.8(@opentelemetry/api@1.9.0)
+ '@orpc/standard-server-fetch': 1.13.8(@opentelemetry/api@1.9.0)
transitivePeerDependencies:
- '@opentelemetry/api'
- '@orpc/standard-server-peer@1.13.7(@opentelemetry/api@1.9.0)':
+ '@orpc/standard-server-peer@1.13.8(@opentelemetry/api@1.9.0)':
dependencies:
- '@orpc/shared': 1.13.7(@opentelemetry/api@1.9.0)
- '@orpc/standard-server': 1.13.7(@opentelemetry/api@1.9.0)
+ '@orpc/shared': 1.13.8(@opentelemetry/api@1.9.0)
+ '@orpc/standard-server': 1.13.8(@opentelemetry/api@1.9.0)
transitivePeerDependencies:
- '@opentelemetry/api'
- '@orpc/standard-server@1.13.7(@opentelemetry/api@1.9.0)':
+ '@orpc/standard-server@1.13.8(@opentelemetry/api@1.9.0)':
dependencies:
- '@orpc/shared': 1.13.7(@opentelemetry/api@1.9.0)
+ '@orpc/shared': 1.13.8(@opentelemetry/api@1.9.0)
transitivePeerDependencies:
- '@opentelemetry/api'
- '@orpc/tanstack-query@1.13.7(@opentelemetry/api@1.9.0)(@orpc/client@1.13.7(@opentelemetry/api@1.9.0))(@tanstack/query-core@5.90.20)':
+ '@orpc/tanstack-query@1.13.8(@opentelemetry/api@1.9.0)(@orpc/client@1.13.8(@opentelemetry/api@1.9.0))(@tanstack/query-core@5.90.20)':
dependencies:
- '@orpc/client': 1.13.7(@opentelemetry/api@1.9.0)
- '@orpc/shared': 1.13.7(@opentelemetry/api@1.9.0)
+ '@orpc/client': 1.13.8(@opentelemetry/api@1.9.0)
+ '@orpc/shared': 1.13.8(@opentelemetry/api@1.9.0)
'@tanstack/query-core': 5.90.20
transitivePeerDependencies:
- '@opentelemetry/api'
- '@orpc/zod@1.13.7(@opentelemetry/api@1.9.0)(@orpc/contract@1.13.7(@opentelemetry/api@1.9.0))(@orpc/server@1.13.7(@opentelemetry/api@1.9.0)(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0))(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0)(zod@4.3.6)':
+ '@orpc/zod@1.13.8(@opentelemetry/api@1.9.0)(@orpc/contract@1.13.8(@opentelemetry/api@1.9.0))(@orpc/server@1.13.8(@opentelemetry/api@1.9.0)(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0))(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0)(zod@4.3.6)':
dependencies:
- '@orpc/contract': 1.13.7(@opentelemetry/api@1.9.0)
- '@orpc/json-schema': 1.13.7(@opentelemetry/api@1.9.0)(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0)
- '@orpc/openapi': 1.13.7(@opentelemetry/api@1.9.0)(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0)
- '@orpc/server': 1.13.7(@opentelemetry/api@1.9.0)(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0)
- '@orpc/shared': 1.13.7(@opentelemetry/api@1.9.0)
+ '@orpc/contract': 1.13.8(@opentelemetry/api@1.9.0)
+ '@orpc/json-schema': 1.13.8(@opentelemetry/api@1.9.0)(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0)
+ '@orpc/openapi': 1.13.8(@opentelemetry/api@1.9.0)(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0)
+ '@orpc/server': 1.13.8(@opentelemetry/api@1.9.0)(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0)
+ '@orpc/shared': 1.13.8(@opentelemetry/api@1.9.0)
escape-string-regexp: 5.0.0
wildcard-match: 5.1.4
zod: 4.3.6
@@ -10029,6 +10240,138 @@ snapshots:
'@oxc-resolver/binding-win32-x64-msvc@11.19.1':
optional: true
+ '@oxfmt/binding-android-arm-eabi@0.41.0':
+ optional: true
+
+ '@oxfmt/binding-android-arm64@0.41.0':
+ optional: true
+
+ '@oxfmt/binding-darwin-arm64@0.41.0':
+ optional: true
+
+ '@oxfmt/binding-darwin-x64@0.41.0':
+ optional: true
+
+ '@oxfmt/binding-freebsd-x64@0.41.0':
+ optional: true
+
+ '@oxfmt/binding-linux-arm-gnueabihf@0.41.0':
+ optional: true
+
+ '@oxfmt/binding-linux-arm-musleabihf@0.41.0':
+ optional: true
+
+ '@oxfmt/binding-linux-arm64-gnu@0.41.0':
+ optional: true
+
+ '@oxfmt/binding-linux-arm64-musl@0.41.0':
+ optional: true
+
+ '@oxfmt/binding-linux-ppc64-gnu@0.41.0':
+ optional: true
+
+ '@oxfmt/binding-linux-riscv64-gnu@0.41.0':
+ optional: true
+
+ '@oxfmt/binding-linux-riscv64-musl@0.41.0':
+ optional: true
+
+ '@oxfmt/binding-linux-s390x-gnu@0.41.0':
+ optional: true
+
+ '@oxfmt/binding-linux-x64-gnu@0.41.0':
+ optional: true
+
+ '@oxfmt/binding-linux-x64-musl@0.41.0':
+ optional: true
+
+ '@oxfmt/binding-openharmony-arm64@0.41.0':
+ optional: true
+
+ '@oxfmt/binding-win32-arm64-msvc@0.41.0':
+ optional: true
+
+ '@oxfmt/binding-win32-ia32-msvc@0.41.0':
+ optional: true
+
+ '@oxfmt/binding-win32-x64-msvc@0.41.0':
+ optional: true
+
+ '@oxlint-tsgolint/darwin-arm64@0.17.0':
+ optional: true
+
+ '@oxlint-tsgolint/darwin-x64@0.17.0':
+ optional: true
+
+ '@oxlint-tsgolint/linux-arm64@0.17.0':
+ optional: true
+
+ '@oxlint-tsgolint/linux-x64@0.17.0':
+ optional: true
+
+ '@oxlint-tsgolint/win32-arm64@0.17.0':
+ optional: true
+
+ '@oxlint-tsgolint/win32-x64@0.17.0':
+ optional: true
+
+ '@oxlint/binding-android-arm-eabi@1.56.0':
+ optional: true
+
+ '@oxlint/binding-android-arm64@1.56.0':
+ optional: true
+
+ '@oxlint/binding-darwin-arm64@1.56.0':
+ optional: true
+
+ '@oxlint/binding-darwin-x64@1.56.0':
+ optional: true
+
+ '@oxlint/binding-freebsd-x64@1.56.0':
+ optional: true
+
+ '@oxlint/binding-linux-arm-gnueabihf@1.56.0':
+ optional: true
+
+ '@oxlint/binding-linux-arm-musleabihf@1.56.0':
+ optional: true
+
+ '@oxlint/binding-linux-arm64-gnu@1.56.0':
+ optional: true
+
+ '@oxlint/binding-linux-arm64-musl@1.56.0':
+ optional: true
+
+ '@oxlint/binding-linux-ppc64-gnu@1.56.0':
+ optional: true
+
+ '@oxlint/binding-linux-riscv64-gnu@1.56.0':
+ optional: true
+
+ '@oxlint/binding-linux-riscv64-musl@1.56.0':
+ optional: true
+
+ '@oxlint/binding-linux-s390x-gnu@1.56.0':
+ optional: true
+
+ '@oxlint/binding-linux-x64-gnu@1.56.0':
+ optional: true
+
+ '@oxlint/binding-linux-x64-musl@1.56.0':
+ optional: true
+
+ '@oxlint/binding-openharmony-arm64@1.56.0':
+ optional: true
+
+ '@oxlint/binding-win32-arm64-msvc@1.56.0':
+ optional: true
+
+ '@oxlint/binding-win32-ia32-msvc@1.56.0':
+ optional: true
+
+ '@oxlint/binding-win32-x64-msvc@1.56.0':
+ optional: true
+
'@phosphor-icons/react@2.1.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
dependencies:
react: 19.2.4
@@ -13682,7 +14025,7 @@ snapshots:
dependencies:
asn1: 0.2.6
- nodemailer@8.0.2: {}
+ nodemailer@8.0.3: {}
nopt@9.0.0:
dependencies:
@@ -13834,6 +14177,62 @@ snapshots:
'@oxc-resolver/binding-win32-ia32-msvc': 11.19.1
'@oxc-resolver/binding-win32-x64-msvc': 11.19.1
+ oxfmt@0.41.0:
+ dependencies:
+ tinypool: 2.1.0
+ optionalDependencies:
+ '@oxfmt/binding-android-arm-eabi': 0.41.0
+ '@oxfmt/binding-android-arm64': 0.41.0
+ '@oxfmt/binding-darwin-arm64': 0.41.0
+ '@oxfmt/binding-darwin-x64': 0.41.0
+ '@oxfmt/binding-freebsd-x64': 0.41.0
+ '@oxfmt/binding-linux-arm-gnueabihf': 0.41.0
+ '@oxfmt/binding-linux-arm-musleabihf': 0.41.0
+ '@oxfmt/binding-linux-arm64-gnu': 0.41.0
+ '@oxfmt/binding-linux-arm64-musl': 0.41.0
+ '@oxfmt/binding-linux-ppc64-gnu': 0.41.0
+ '@oxfmt/binding-linux-riscv64-gnu': 0.41.0
+ '@oxfmt/binding-linux-riscv64-musl': 0.41.0
+ '@oxfmt/binding-linux-s390x-gnu': 0.41.0
+ '@oxfmt/binding-linux-x64-gnu': 0.41.0
+ '@oxfmt/binding-linux-x64-musl': 0.41.0
+ '@oxfmt/binding-openharmony-arm64': 0.41.0
+ '@oxfmt/binding-win32-arm64-msvc': 0.41.0
+ '@oxfmt/binding-win32-ia32-msvc': 0.41.0
+ '@oxfmt/binding-win32-x64-msvc': 0.41.0
+
+ oxlint-tsgolint@0.17.0:
+ optionalDependencies:
+ '@oxlint-tsgolint/darwin-arm64': 0.17.0
+ '@oxlint-tsgolint/darwin-x64': 0.17.0
+ '@oxlint-tsgolint/linux-arm64': 0.17.0
+ '@oxlint-tsgolint/linux-x64': 0.17.0
+ '@oxlint-tsgolint/win32-arm64': 0.17.0
+ '@oxlint-tsgolint/win32-x64': 0.17.0
+
+ oxlint@1.56.0(oxlint-tsgolint@0.17.0):
+ optionalDependencies:
+ '@oxlint/binding-android-arm-eabi': 1.56.0
+ '@oxlint/binding-android-arm64': 1.56.0
+ '@oxlint/binding-darwin-arm64': 1.56.0
+ '@oxlint/binding-darwin-x64': 1.56.0
+ '@oxlint/binding-freebsd-x64': 1.56.0
+ '@oxlint/binding-linux-arm-gnueabihf': 1.56.0
+ '@oxlint/binding-linux-arm-musleabihf': 1.56.0
+ '@oxlint/binding-linux-arm64-gnu': 1.56.0
+ '@oxlint/binding-linux-arm64-musl': 1.56.0
+ '@oxlint/binding-linux-ppc64-gnu': 1.56.0
+ '@oxlint/binding-linux-riscv64-gnu': 1.56.0
+ '@oxlint/binding-linux-riscv64-musl': 1.56.0
+ '@oxlint/binding-linux-s390x-gnu': 1.56.0
+ '@oxlint/binding-linux-x64-gnu': 1.56.0
+ '@oxlint/binding-linux-x64-musl': 1.56.0
+ '@oxlint/binding-openharmony-arm64': 1.56.0
+ '@oxlint/binding-win32-arm64-msvc': 1.56.0
+ '@oxlint/binding-win32-ia32-msvc': 1.56.0
+ '@oxlint/binding-win32-x64-msvc': 1.56.0
+ oxlint-tsgolint: 0.17.0
+
p-map@7.0.4: {}
pac-proxy-agent@7.2.0:
@@ -14982,6 +15381,8 @@ snapshots:
fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
+ tinypool@2.1.0: {}
+
tldts-core@6.1.86: {}
tldts-core@7.0.25: {}
diff --git a/scripts/database/migrate.ts b/scripts/database/migrate.ts
index 6ef0df28d..f0d9049eb 100644
--- a/scripts/database/migrate.ts
+++ b/scripts/database/migrate.ts
@@ -1,6 +1,7 @@
import { drizzle } from "drizzle-orm/node-postgres";
import { migrate } from "drizzle-orm/node-postgres/migrator";
import { Pool } from "pg";
+
import { env } from "@/utils/env";
export async function migrateDatabase() {
diff --git a/scripts/database/reset.ts b/scripts/database/reset.ts
index f4ca6e0e5..4eb90ee14 100644
--- a/scripts/database/reset.ts
+++ b/scripts/database/reset.ts
@@ -1,6 +1,7 @@
import { sql } from "drizzle-orm";
import { drizzle } from "drizzle-orm/node-postgres";
import { Pool } from "pg";
+
import { env } from "@/utils/env";
export async function resetDatabase() {
diff --git a/scripts/fonts/generate.ts b/scripts/fonts/generate.ts
index bd3bd8769..e6a2253ba 100644
--- a/scripts/fonts/generate.ts
+++ b/scripts/fonts/generate.ts
@@ -9,6 +9,7 @@
*/
import { mkdir, readFile, writeFile } from "node:fs/promises";
+
import type { APIResponse, Variant, WebFont, Weight } from "./types";
const args = process.argv.slice(2);
diff --git a/scripts/migration/resume.ts b/scripts/migration/resume.ts
index 728acaab0..8dae96f65 100644
--- a/scripts/migration/resume.ts
+++ b/scripts/migration/resume.ts
@@ -1,7 +1,8 @@
-import fs from "node:fs/promises";
import { and, inArray, sql } from "drizzle-orm";
import { drizzle } from "drizzle-orm/node-postgres";
+import fs from "node:fs/promises";
import { Pool, type QueryResult } from "pg";
+
import { schema } from "@/integrations/drizzle";
import { ReactiveResumeV4JSONImporter } from "@/integrations/import/reactive-resume-v4-json";
import { defaultResumeData } from "@/schema/resume/data";
diff --git a/scripts/migration/user.ts b/scripts/migration/user.ts
index ebe31668f..ffc527fd1 100644
--- a/scripts/migration/user.ts
+++ b/scripts/migration/user.ts
@@ -1,7 +1,8 @@
-import fs from "node:fs/promises";
import { inArray, or, sql } from "drizzle-orm";
import { drizzle } from "drizzle-orm/node-postgres";
+import fs from "node:fs/promises";
import { Pool, type QueryResult } from "pg";
+
import { schema } from "@/integrations/drizzle";
import { generateId, toUsername } from "@/utils/string";
diff --git a/skills/resume-builder/SKILL.md b/skills/resume-builder/SKILL.md
index 8eb1a73e7..80b230be2 100644
--- a/skills/resume-builder/SKILL.md
+++ b/skills/resume-builder/SKILL.md
@@ -54,6 +54,7 @@ Ask about preferences:
Output must conform to the Reactive Resume schema. See [references/schema.md](references/schema.md) for the complete schema structure.
Key requirements:
+
- All item `id` fields must be valid UUIDs
- Description fields accept HTML-formatted strings
- Website fields require both `url` and `label` properties
@@ -75,6 +76,7 @@ Share these tips when helping users craft their resume content:
### Section Order Recommendations
For most professionals:
+
1. Summary (if experienced)
2. Experience
3. Education
@@ -83,6 +85,7 @@ For most professionals:
6. Certifications/Awards
For students/recent graduates:
+
1. Education
2. Projects
3. Skills
diff --git a/skills/resume-builder/references/schema.md b/skills/resume-builder/references/schema.md
index af4df8749..3a04ae003 100644
--- a/skills/resume-builder/references/schema.md
+++ b/skills/resume-builder/references/schema.md
@@ -21,16 +21,16 @@ This reference provides key structural information for generating valid resume d
```json
{
- "hidden": false,
- "url": "https://example.com/photo.jpg",
- "size": 80,
- "rotation": 0,
- "aspectRatio": 1,
- "borderRadius": 0,
- "borderColor": "rgba(0, 0, 0, 0.5)",
- "borderWidth": 0,
- "shadowColor": "rgba(0, 0, 0, 0.5)",
- "shadowWidth": 0
+ "hidden": false,
+ "url": "https://example.com/photo.jpg",
+ "size": 80,
+ "rotation": 0,
+ "aspectRatio": 1,
+ "borderRadius": 0,
+ "borderColor": "rgba(0, 0, 0, 0.5)",
+ "borderWidth": 0,
+ "shadowColor": "rgba(0, 0, 0, 0.5)",
+ "shadowWidth": 0
}
```
@@ -38,13 +38,13 @@ This reference provides key structural information for generating valid resume d
```json
{
- "name": "John Doe",
- "headline": "Senior Software Engineer",
- "email": "john@example.com",
- "phone": "+1 (555) 123-4567",
- "location": "San Francisco, CA",
- "website": { "url": "https://johndoe.com", "label": "Portfolio" },
- "customFields": []
+ "name": "John Doe",
+ "headline": "Senior Software Engineer",
+ "email": "john@example.com",
+ "phone": "+1 (555) 123-4567",
+ "location": "San Francisco, CA",
+ "website": { "url": "https://johndoe.com", "label": "Portfolio" },
+ "customFields": []
}
```
@@ -52,10 +52,10 @@ This reference provides key structural information for generating valid resume d
```json
{
- "title": "Summary",
- "columns": 1,
- "hidden": false,
- "content": "