Compare commits

...
27 Commits
Author SHA1 Message Date
Amruth Pillai ef6d054598 fixes #2612 2026-02-08 01:43:08 +01:00
Amruth Pillai 201d707aea fixes #2655 2026-02-08 01:39:58 +01:00
Amruth Pillai d8b0902ac9 fixes #2691 2026-02-08 01:39:24 +01:00
Amruth Pillai 01c75bd796 📦 v5.0.6 · Changelog: https://docs.rxresu.me/changelog 2026-02-08 01:18:41 +01:00
Amruth Pillai 0a609306a6 add error logging on server only 2026-02-08 01:16:38 +01:00
Amruth Pillai 5bdc6de862 update imports/exports 2026-02-08 00:28:28 +01:00
Amruth Pillai cc01fb9418 Feature: Implement Atomic Resume Patching API (#2692) 2026-02-08 00:16:11 +01:00
Arjun EarthpersonandAmruth Pillai 5e8e1349bc Return updated resume object from PUT /resume/{id} instead of void (#2688)
* implement PUT response

* Refactor resume router and service to use DTOs for input and output schemas

* define explicitly, the fields to be returned

---------

Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
2026-02-07 00:49:33 +01:00
Amruth Pillai ddbb71fb78 update dependencies 2026-02-06 23:07:37 +01:00
Amruth Pillai 3289453448 add .devcontainer 2026-02-06 19:15:56 +01:00
Amruth Pillai 611af8ee72 Add build badge and documentation link to README
Added a build badge for Ona and linked to documentation.
2026-02-06 19:05:40 +01:00
Amruth PillaiandCopilot 69b96a592d Sync Translations from Crowdin (#2686)
* New translations en-us.po (French)
[ci skip]

* Update locales/fr-FR.po

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-05 14:37:59 +01:00
Amruth Pillai 3151f6a9cc update dependencies 2026-02-03 22:55:22 +01:00
Amruth Pillai 726968288c fix lint errors 2026-02-02 17:37:15 +01:00
Arjun Earthperson ad086f337a update sample with cover letter and other existing fields (#2678) 2026-02-02 17:36:24 +01:00
Amruth Pillai 66d09820c3 rename service from "app" to "reactive_resume" 2026-02-02 13:27:14 +01:00
Amruth Pillai 5ec73e9234 fix no name returned in github 2026-02-02 13:03:49 +01:00
Amruth Pillai 17569d0658 add failure update to ai test status, when testing connection 2026-02-02 01:03:01 +01:00
Amruth Pillai 14ea464c0a add changelog, update README.md 2026-02-02 01:01:02 +01:00
Amruth Pillai da9a3c0b12 fixes #2663: validate AI output for test connection endpoint (#2674) 2026-02-02 00:43:31 +01:00
Abdul Alim f5a9ffb776 fix(layout): handle text overflow in sidebar/footer elements (#2672) 2026-02-01 22:17:30 +01:00
Amruth PillaiandCopilot 19181b79c8 Sync Translations from Crowdin (#2667)
* New translations en-us.po (Spanish)
[ci skip]

* New translations en-us.po (Italian)
[ci skip]

* Update locales/it-IT.po

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-31 17:10:26 +01:00
Amruth Pillai 1509678578 fix auth error: "name is required" (#2668) 2026-01-31 17:09:14 +01:00
Amruth Pillai c4d5dd4a3c New translations en-us.po (Italian) (#2665)
[ci skip]
2026-01-31 15:42:43 +01:00
Amruth Pillai ca734ca547 Sync Translations from Crowdin (#2664)
* New translations en-us.po (Romanian)
[ci skip]

* New translations en-us.po (French)
[ci skip]

* New translations en-us.po (Spanish)
[ci skip]

* New translations en-us.po (Afrikaans)
[ci skip]

* New translations en-us.po (Arabic)
[ci skip]

* New translations en-us.po (Bulgarian)
[ci skip]

* New translations en-us.po (Catalan)
[ci skip]

* New translations en-us.po (Czech)
[ci skip]

* New translations en-us.po (Danish)
[ci skip]

* New translations en-us.po (German)
[ci skip]

* New translations en-us.po (Greek)
[ci skip]

* New translations en-us.po (Finnish)
[ci skip]

* New translations en-us.po (Hebrew)
[ci skip]

* New translations en-us.po (Hungarian)
[ci skip]

* New translations en-us.po (Italian)
[ci skip]

* New translations en-us.po (Japanese)
[ci skip]

* New translations en-us.po (Korean)
[ci skip]

* New translations en-us.po (Lithuanian)
[ci skip]

* New translations en-us.po (Dutch)
[ci skip]

* New translations en-us.po (Norwegian)
[ci skip]

* New translations en-us.po (Polish)
[ci skip]

* New translations en-us.po (Portuguese)
[ci skip]

* New translations en-us.po (Russian)
[ci skip]

* New translations en-us.po (Slovak)
[ci skip]

* New translations en-us.po (Albanian)
[ci skip]

* New translations en-us.po (Serbian (Cyrillic))
[ci skip]

* New translations en-us.po (Swedish)
[ci skip]

* New translations en-us.po (Turkish)
[ci skip]

* New translations en-us.po (Ukrainian)
[ci skip]

* New translations en-us.po (Chinese Simplified)
[ci skip]

* New translations en-us.po (Chinese Traditional)
[ci skip]

* New translations en-us.po (Vietnamese)
[ci skip]

* New translations en-us.po (Portuguese, Brazilian)
[ci skip]

* New translations en-us.po (Indonesian)
[ci skip]

* New translations en-us.po (Persian)
[ci skip]

* New translations en-us.po (Khmer)
[ci skip]

* New translations en-us.po (Tamil)
[ci skip]

* New translations en-us.po (Bengali)
[ci skip]

* New translations en-us.po (Marathi)
[ci skip]

* New translations en-us.po (Thai)
[ci skip]

* New translations en-us.po (Latvian)
[ci skip]

* New translations en-us.po (Azerbaijani)
[ci skip]

* New translations en-us.po (Hindi)
[ci skip]

* New translations en-us.po (Malay)
[ci skip]

* New translations en-us.po (Telugu)
[ci skip]

* New translations en-us.po (Malayalam)
[ci skip]

* New translations en-us.po (Uzbek)
[ci skip]

* New translations en-us.po (Kannada)
[ci skip]

* New translations en-us.po (Odia)
[ci skip]

* New translations en-us.po (Amharic)
[ci skip]

* New translations en-us.po (Nepali)
[ci skip]

* New translations en-us.po (Slovenian)
[ci skip]
2026-01-31 14:14:16 +01:00
Amruth PillaiandCursor 8d347f5162 Feature: Implement Embedding Links in Titles of all Section Items (#2662)
* feat: add options.showLinkInTitle to baseItemSchema

Add itemOptionsSchema with showLinkInTitle boolean property to control
whether the website URL is rendered as a hyperlink on the title instead
of a separate link at the bottom. The field is optional for backwards
compatibility with existing resumes.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat: add hideLabelButton prop to URLInput

When hideLabelButton is true, the tag/label button is hidden from the
URL input. This is used when showLinkInTitle is enabled since the label
is not needed when the link is shown in the title.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat: create LinkedTitle component for title-as-link rendering

Add a reusable component that conditionally renders the title as a
hyperlink when showLinkInTitle is true and a website URL is provided.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat: add showLinkInTitle option to experience section

- Add Switch toggle in experience dialog for showLinkInTitle option
- Update URLInput to hide label button when showLinkInTitle is enabled
- Use LinkedTitle component in experience-item for conditional link rendering
- Hide bottom website link when showLinkInTitle is enabled

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat: add showLinkInTitle option to all section items

- Update education, projects, awards, certifications, publications,
  volunteer, references, and profiles dialogs with Switch toggle
- Add LinkedTitle component usage in all corresponding item components
- Conditionally hide bottom website link when showLinkInTitle is enabled
- Add hideLabelButton prop to URLInput when showLinkInTitle is enabled

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore: extract i18n strings for showLinkInTitle feature

Add "Show link in title" translation string to all locale catalogs.

Co-authored-by: Cursor <cursoragent@cursor.com>

* update dependencies, fix an issue with glalie template and non-clickable links, fix better-auth type error

* remove unused export

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-01-31 12:16:23 +01:00
Amruth Pillai 4d368ab6f6 Sync Translations from Crowdin (#2660)
* New translations en-us.po (Romanian)
[ci skip]

* New translations en-us.po (French)
[ci skip]

* New translations en-us.po (Spanish)
[ci skip]

* New translations en-us.po (Afrikaans)
[ci skip]

* New translations en-us.po (Arabic)
[ci skip]

* New translations en-us.po (Bulgarian)
[ci skip]

* New translations en-us.po (Catalan)
[ci skip]

* New translations en-us.po (Czech)
[ci skip]

* New translations en-us.po (Danish)
[ci skip]

* New translations en-us.po (German)
[ci skip]

* New translations en-us.po (Greek)
[ci skip]

* New translations en-us.po (Finnish)
[ci skip]

* New translations en-us.po (Hebrew)
[ci skip]

* New translations en-us.po (Hungarian)
[ci skip]

* New translations en-us.po (Italian)
[ci skip]

* New translations en-us.po (Japanese)
[ci skip]

* New translations en-us.po (Korean)
[ci skip]

* New translations en-us.po (Lithuanian)
[ci skip]

* New translations en-us.po (Dutch)
[ci skip]

* New translations en-us.po (Norwegian)
[ci skip]

* New translations en-us.po (Polish)
[ci skip]

* New translations en-us.po (Portuguese)
[ci skip]

* New translations en-us.po (Russian)
[ci skip]

* New translations en-us.po (Slovak)
[ci skip]

* New translations en-us.po (Albanian)
[ci skip]

* New translations en-us.po (Serbian (Cyrillic))
[ci skip]

* New translations en-us.po (Swedish)
[ci skip]

* New translations en-us.po (Turkish)
[ci skip]

* New translations en-us.po (Ukrainian)
[ci skip]

* New translations en-us.po (Chinese Simplified)
[ci skip]

* New translations en-us.po (Chinese Traditional)
[ci skip]

* New translations en-us.po (Vietnamese)
[ci skip]

* New translations en-us.po (Portuguese, Brazilian)
[ci skip]

* New translations en-us.po (Indonesian)
[ci skip]

* New translations en-us.po (Persian)
[ci skip]

* New translations en-us.po (Khmer)
[ci skip]

* New translations en-us.po (Tamil)
[ci skip]

* New translations en-us.po (Bengali)
[ci skip]

* New translations en-us.po (Marathi)
[ci skip]

* New translations en-us.po (Thai)
[ci skip]

* New translations en-us.po (Latvian)
[ci skip]

* New translations en-us.po (Azerbaijani)
[ci skip]

* New translations en-us.po (Hindi)
[ci skip]

* New translations en-us.po (Malay)
[ci skip]

* New translations en-us.po (Telugu)
[ci skip]

* New translations en-us.po (Malayalam)
[ci skip]

* New translations en-us.po (Uzbek)
[ci skip]

* New translations en-us.po (Kannada)
[ci skip]

* New translations en-us.po (Odia)
[ci skip]

* New translations en-us.po (Amharic)
[ci skip]

* New translations en-us.po (Nepali)
[ci skip]

* New translations en-us.po (Slovenian)
[ci skip]
2026-01-31 11:09:44 +01:00
118 changed files with 3910 additions and 2165 deletions
+10
View File
@@ -0,0 +1,10 @@
FROM mcr.microsoft.com/devcontainers/typescript-node:24
RUN corepack enable
RUN apt-get update && apt-get install -y --no-install-recommends \
git \
curl \
&& rm -rf /var/lib/apt/lists/*
EXPOSE 3000
+32
View File
@@ -0,0 +1,32 @@
{
"name": "Reactive Resume",
"service": "reactive_resume",
"dockerComposeFile": "docker-compose.yml",
"workspaceFolder": "/workspace",
"forwardPorts": [3000, 4000, 5432, 8333],
"portsAttributes": {
"3000": { "label": "Reactive Resume", "onAutoForward": "openBrowser" },
"4000": { "label": "Browserless (Printer)" },
"5432": { "label": "PostgreSQL" },
"8333": { "label": "SeaweedFS (S3)" }
},
"customizations": {
"vscode": {
"extensions": ["biomejs.biome", "bradlc.vscode-tailwindcss", "lokalise.i18n-ally"],
"settings": {
"biome.enabled": true,
"editor.codeActionsOnSave": {
"source.biome": "explicit",
"source.fixAll.biome": "explicit",
"source.organizeImports.biome": "explicit"
},
"editor.defaultFormatter": "biomejs.biome",
"typescript.tsdk": "node_modules/typescript/lib"
}
}
},
"postCreateCommand": "corepack enable && pnpm install"
}
+96
View File
@@ -0,0 +1,96 @@
services:
reactive_resume:
build:
context: ..
dockerfile: .devcontainer/Dockerfile
volumes:
- ..:/workspace:cached
command: sleep infinity
depends_on:
postgres:
condition: service_healthy
browserless:
condition: service_started
seaweedfs:
condition: service_healthy
seaweedfs_create_bucket:
condition: service_completed_successfully
environment:
TZ: Etc/UTC
APP_URL: http://localhost:3000
PRINTER_APP_URL: http://reactive_resume:3000
PRINTER_ENDPOINT: ws://browserless:3000?token=1234567890
DATABASE_URL: postgresql://postgres:postgres@postgres:5432/postgres
AUTH_SECRET: change-me-to-a-secure-secret-key-in-production
S3_ACCESS_KEY_ID: seaweedfs
S3_SECRET_ACCESS_KEY: seaweedfs
S3_REGION: us-east-1
S3_ENDPOINT: http://seaweedfs:8333
S3_BUCKET: reactive-resume
S3_FORCE_PATH_STYLE: "true"
postgres:
image: postgres:latest
restart: unless-stopped
environment:
POSTGRES_DB: postgres
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
volumes:
- postgres_data:/var/lib/postgresql
healthcheck:
test: ["CMD", "pg_isready", "-U", "postgres", "-d", "postgres"]
start_period: 10s
interval: 30s
timeout: 10s
retries: 3
browserless:
image: ghcr.io/browserless/chromium:latest
restart: unless-stopped
environment:
QUEUED: "10"
HEALTH: "true"
CONCURRENT: "5"
TOKEN: "1234567890"
healthcheck:
test:
["CMD", "curl", "-f", "http://localhost:3000/pressure?token=1234567890"]
interval: 10s
timeout: 5s
retries: 10
seaweedfs:
image: chrislusf/seaweedfs:latest
restart: unless-stopped
command: server -s3 -filer -dir=/data -ip=0.0.0.0
environment:
AWS_ACCESS_KEY_ID: seaweedfs
AWS_SECRET_ACCESS_KEY: seaweedfs
volumes:
- seaweedfs_data:/data
healthcheck:
test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://localhost:8888"]
start_period: 10s
interval: 30s
timeout: 10s
retries: 3
seaweedfs_create_bucket:
image: quay.io/minio/mc:latest
restart: on-failure
entrypoint: >
/bin/sh -c "
until mc alias set seaweedfs http://seaweedfs:8333 seaweedfs seaweedfs; do
echo 'Waiting for SeaweedFS...';
sleep 2;
done;
mc mb seaweedfs/reactive-resume --ignore-existing;
"
depends_on:
seaweedfs:
condition: service_healthy
volumes:
postgres_data:
seaweedfs_data:
+7 -17
View File
@@ -1,15 +1,3 @@
<div align="center" markdown="1">
<a href="https://go.warp.dev/Reactive-Resume">
<img alt="Warp Sponsorship" width="400" src="https://github.com/warpdotdev/brand-assets/blob/main/Github/Sponsor/Warp-Github-LG-03.png?raw=true" />
</a>
### [Warp, built for coding with multiple AI agents.](https://go.warp.dev/Reactive-Resume)
[Available for MacOS, Linux, & Windows](https://go.warp.dev/Reactive-Resume)
</div>
---
<div align="center">
<a href="https://rxresu.me">
<img src="public/opengraph/banner.jpg" alt="Reactive Resume" />
@@ -26,15 +14,15 @@
</p>
<p>
<img src="https://img.shields.io/github/stars/amruthpillai/Reactive-Resume?style=flat-square" alt="Stars" />
<img src="https://img.shields.io/github/package-json/v/amruthpillai/reactive-resume?style=flat-square" alt="Reactive Resume version">
<img src="https://img.shields.io/github/stars/amruthpillai/Reactive-Resume?style=flat-square" alt="GitHub Stars">
<img src="https://img.shields.io/github/license/amruthpillai/Reactive-Resume?style=flat-square" alt="License" />
<img src="https://img.shields.io/docker/pulls/amruthpillai/reactive-resume?style=flat-square" alt="Docker Pulls" />
<a href="https://discord.gg/EE8yFqW4"><img src="https://img.shields.io/discord/1173518977851473940?label=Discord&style=flat-square&logo=discord" alt="Discord" /></a>
<a href="https://discord.gg/EE8yFqW4"><img src="https://img.shields.io/discord/1173518977851473940?style=flat-square&label=discord" alt="Discord" /></a>
<a href="https://crowdin.com/project/reactive-resume"><img src="https://badges.crowdin.net/reactive-resume/localized.svg?style=flat-square" alt="Crowdin" /></a>
<a href="https://opencollective.com/reactive-resume"><img src="https://img.shields.io/opencollective/all/reactive-resume?style=flat-square" alt="Open Collective" /></a>
<a href="https://github.com/sponsors/AmruthPillai"><img src="https://img.shields.io/github/sponsors/AmruthPillai?style=flat-square&label=sponsors" alt="Sponsors" /></a>
<a href="https://opencollective.com/reactive-resume"><img src="https://img.shields.io/opencollective/backers/reactive-resume?style=flat-square&label=donations" alt="Donations" /></a>
</p>
<a href="https://www.producthunt.com/products/reactive-resume?embed=true&amp;utm_source=badge-featured&amp;utm_medium=badge&amp;utm_campaign=badge-reactive-resume-v5-2" target="_blank" rel="noopener"><img alt="Reactive Resume v5 - A free and open-source resume builder. | Product Hunt" width="250" height="54" src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=1065182&amp;theme=light&amp;t=1768850745585"></a>
</div>
---
@@ -157,6 +145,8 @@ docker compose up -d
open http://localhost:3000
```
[![Build with Ona](https://ona.com/build-with-ona.svg)](https://app.ona.com/#https://github.com/amruthpillai/reactive-resume)
For detailed setup instructions, environment configuration, and self-hosting guides, see the [documentation](https://docs.rxresu.me).
## Tech Stack
+1
View File
@@ -38,6 +38,7 @@
},
"correctness": {
"noUnknownTypeSelector": "off",
"useExhaustiveDependencies": "off",
"noUnusedImports": {
"fix": "safe",
"level": "error",
+1 -1
View File
@@ -77,7 +77,7 @@ services:
timeout: 10s
retries: 3
seaweedfs-create-bucket:
seaweedfs_create_bucket:
image: quay.io/minio/mc:latest
restart: on-failure
entrypoint: >
+4 -4
View File
@@ -56,7 +56,7 @@ services:
timeout: 10s
retries: 3
seaweedfs-create-bucket:
seaweedfs_create_bucket:
image: quay.io/minio/mc:latest
restart: on-failure
entrypoint: >
@@ -70,7 +70,7 @@ services:
seaweedfs:
condition: service_healthy
app:
reactive_resume:
image: amruthpillai/reactive-resume:latest
# image: ghcr.io/amruthpillai/reactive-resume:latest
environment:
@@ -78,7 +78,7 @@ services:
- TZ=Etc/UTC
- NODE_ENV=production
- APP_URL=http://localhost:3000
- PRINTER_APP_URL=http://app:3000
- PRINTER_APP_URL=http://reactive_resume:3000
# Printer
- PRINTER_ENDPOINT=ws://browserless:3000?token=1234567890
# - PRINTER_ENDPOINT=http://chrome:9222 # Or, if you're using chromedp/headless-shell
@@ -101,7 +101,7 @@ services:
condition: service_healthy
browserless:
condition: service_healthy
seaweedfs-create-bucket:
seaweedfs_create_bucket:
condition: service_completed_successfully
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
+22
View File
@@ -4,6 +4,28 @@ description: "List of all notable changes and updates to Reactive Resume"
rss: true
---
<Update label="v5.0.6" description="8th February 2026">
- Implement Atomic Resume Patching API for fine-grained resume updates, allowing partial, atomic updates to resumes via a new PATCH endpoint. [#2692](https://github.com/amruthpillai/reactive-resume/pull/2692)
- The API endpoint `PUT /resume/{id}` now returns the updated resume object instead of void. Also, resume routing/services now use explicit DTOs for input and output schemas. [#2688](https://github.com/amruthpillai/reactive-resume/pull/2688)
- Add error logging for API server errors (server-side only) to improve debugging and reliability.
- Refactor and clean up imports/exports for clarity and maintainability.
- Added `.devcontainer` configuration for improved contributor development environment.
- Update dependencies to the latest versions.
- Add build status badge and documentation link to README.
- Sync latest translations from Crowdin (notably: French, other languages).
- Other minor fixes and improvements.
</Update>
<Update label="v5.0.5" description="31st January 2026">
- Implement Cover Letter functionality in the resume builder, allowing the user to create cover letters as custom sections. [(link)](/guides/adding-a-cover-letter)
- Implement full-screen mode for the rich text editor in the resume builder, allowing the user to write in a more focused environment.
- Implement a new custom section type: `summary`, which allows the user to add another "summary" like section to the resume.
- Implement a `useFormBlocker` hook to prevent the user from closing a dialog while the form has unsaved changes.
- Fix an issue where keywords spacing was not consistent in the interests section. [#2631](https://github.com/amruthpillai/reactive-resume/pull/2631)
- Fix an issue where the AI connection test was not working correctly, also return appropriate error messages for AI provider issues.
</Update>
<Update label="v5.0.4" description="28th January 2026">
- Bring back Undo/Redo functionality in the resume builder for improved editing experience.
- Arrange the sidebar builder dynamically based on the section type in each template. [#2564](https://github.com/amruthpillai/reactive-resume/pull/2603)
+2 -2
View File
@@ -299,13 +299,13 @@ pnpm run typecheck
```bash
docker compose -f compose.dev.yml logs seaweedfs
docker compose -f compose.dev.yml logs seaweedfs-create-bucket
docker compose -f compose.dev.yml logs seaweedfs_create_bucket
```
If the bucket wasn't created, restart the bucket creation service:
```bash
docker compose -f compose.dev.yml restart seaweedfs-create-bucket
docker compose -f compose.dev.yml restart seaweedfs_create_bucket
```
</Accordion>
+6 -1
View File
@@ -69,7 +69,12 @@
},
{
"group": "Integrations",
"pages": ["guides/using-the-api", "guides/using-ai", "guides/json-resume-schema"]
"pages": [
"guides/using-the-api",
"guides/using-the-patch-api",
"guides/using-ai",
"guides/json-resume-schema"
]
},
{
"group": "Self-Hosting",
+191
View File
@@ -0,0 +1,191 @@
---
title: "Using the Patch API"
description: "Learn how to partially update your resume using JSON Patch (RFC 6902) operations"
---
The Patch API lets you make small, targeted changes to your resume without sending the entire data object. Instead of replacing the whole resume with a `PUT`, you send a list of **JSON Patch** operations that describe exactly what to change.
This is based on the [JSON Patch (RFC 6902)](https://datatracker.ietf.org/doc/html/rfc6902) standard.
## When to Use PATCH vs PUT
| Use case | Method |
| --- | --- |
| Update a single field (e.g., name, headline) | **PATCH** |
| Add or remove an item in a section | **PATCH** |
| Change template, colors, or fonts | **PATCH** |
| Replace the entire resume data at once | **PUT** |
<Info>
The PATCH endpoint only modifies the resume `data` (the JSONB column). To update top-level resume properties like `name`, `slug`, `tags`, or `isPublic`, use the existing `PUT /resume/{id}` endpoint.
</Info>
## Authentication
All requests require your API key in the `x-api-key` header. See [Using the API](/guides/using-the-api) for how to create one.
<Info>
If you're self-hosting, replace `https://rxresu.me` with your instance URL. The API is served under `/api/openapi`.
</Info>
## Endpoint
```
PATCH /api/openapi/resume/{id}
```
### Request Body
The resume ID is taken from the URL path, so the request body only requires the `operations` array:
```json
{
"operations": [
{ "op": "replace", "path": "/basics/name", "value": "Jane Doe" }
]
}
```
Each operation is an object with the following properties:
| Property | Required | Description |
| --- | --- | --- |
| `op` | Yes | The operation to perform: `add`, `remove`, `replace`, `move`, `copy`, or `test` |
| `path` | Yes | A JSON Pointer (RFC 6901) to the target location in the resume data |
| `value` | For `add`, `replace`, `test` | The value to use for the operation |
| `from` | For `move`, `copy` | A JSON Pointer to the source location |
## Examples
### Replace a Basic Field
Update the resume holder's name and headline:
```bash
curl -X PATCH "https://rxresu.me/api/openapi/resume/YOUR_RESUME_ID" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"operations": [
{ "op": "replace", "path": "/basics/name", "value": "Jane Doe" },
{ "op": "replace", "path": "/basics/headline", "value": "Senior Software Engineer" }
]
}'
```
### Add an Experience Entry
Append a new item to the experience section:
```bash
curl -X PATCH "https://rxresu.me/api/openapi/resume/YOUR_RESUME_ID" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"operations": [
{
"op": "add",
"path": "/sections/experience/items/-",
"value": {
"id": "a1b2c3d4-0000-0000-0000-000000000000",
"hidden": false,
"company": "Acme Corp",
"position": "Staff Engineer",
"location": "San Francisco, CA",
"period": "Jan 2024 - Present",
"website": { "url": "https://acme.example.com", "label": "Acme Corp" },
"description": "<p>Leading the platform team.</p>"
}
}
]
}'
```
<Tip>
The path `/sections/experience/items/-` uses the special `-` index, which means "append to the end of the array".
To insert at a specific position, use a numeric index like `/sections/experience/items/0` for the beginning.
</Tip>
### Remove an Item from a Section
Remove the second skill (index `1`) from the skills section:
```bash
curl -X PATCH "https://rxresu.me/api/openapi/resume/YOUR_RESUME_ID" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"operations": [
{ "op": "remove", "path": "/sections/skills/items/1" }
]
}'
```
### Update Metadata (Template, Colors, Fonts)
Switch the template and update the primary color:
```bash
curl -X PATCH "https://rxresu.me/api/openapi/resume/YOUR_RESUME_ID" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"operations": [
{ "op": "replace", "path": "/metadata/template", "value": "bronzor" },
{ "op": "replace", "path": "/metadata/design/colors/primary", "value": "rgba(37, 99, 235, 1)" }
]
}'
```
### Test-Then-Replace (Optimistic Concurrency)
The `test` operation checks that a value matches before proceeding. If the test fails, the entire patch is rejected. This is useful to avoid overwriting changes made by another client:
```bash
curl -X PATCH "https://rxresu.me/api/openapi/resume/YOUR_RESUME_ID" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"operations": [
{ "op": "test", "path": "/basics/name", "value": "Albert Einstein" },
{ "op": "replace", "path": "/basics/name", "value": "Jane Doe" }
]
}'
```
If `/basics/name` is not `"Albert Einstein"` at the time of the request, the entire patch will fail with a `400` error and no changes will be applied.
### Move an Item Within a Section
Move the first experience item to the third position:
```bash
curl -X PATCH "https://rxresu.me/api/openapi/resume/YOUR_RESUME_ID" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"operations": [
{ "op": "move", "from": "/sections/experience/items/0", "path": "/sections/experience/items/2" }
]
}'
```
## Error Handling
| Status | Error Code | Description |
| --- | --- | --- |
| `400` | `INVALID_PATCH_OPERATIONS` | The operations are structurally invalid, target a non-existent path, or produce resume data that fails schema validation. |
| `401` | `UNAUTHORIZED` | Missing or invalid API key. |
| `404` | `NOT_FOUND` | The resume does not exist or does not belong to the authenticated user. |
| `403` | `RESUME_LOCKED` | The resume is locked and cannot be modified. Unlock it first. |
<Warning>
All operations in a single request are applied atomically. If any operation fails (including a `test`), none of the operations are applied.
</Warning>
## Tips
- **Fetch first, then patch.** Use `GET /resume/{id}` to inspect the current structure before crafting your operations. This helps you target the correct paths and array indices.
- **Use `test` for safety.** When updating a value you expect to be a specific value, combine `test` + `replace` to avoid accidentally overwriting concurrent changes.
- **Batch related changes.** You can send multiple operations in a single request. They are applied in order, so later operations can depend on earlier ones.
- **The `-` index appends.** When adding items to arrays, use `-` as the index (e.g., `/sections/skills/items/-`) to append to the end.
+1 -1
View File
@@ -184,7 +184,7 @@ services:
timeout: 5s
retries: 10
app:
reactive-resume:
image: amruthpillai/reactive-resume:latest
# image: ghcr.io/amruthpillai/reactive-resume:latest
restart: unless-stopped
+13 -15
View File
@@ -92,18 +92,18 @@ services:
timeout: 10s
retries: 3
app:
reactive_resume:
image: amruthpillai/reactive-resume:latest
restart: unless-stopped
environment:
- APP_URL=https://resume.${DOMAIN}
- PRINTER_APP_URL=http://app:3000
- PRINTER_APP_URL=http://reactive_resume:3000
- DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/postgres
- PRINTER_ENDPOINT=http://printer:3000
- AUTH_SECRET=${AUTH_SECRET}
# Add other optional env vars as needed (SMTP, S3, OAuth, etc.)
volumes:
- app_data:/app/data
- reactive_resume_data:/app/data
networks:
- reactive_resume_network
depends_on:
@@ -130,7 +130,7 @@ networks:
volumes:
traefik_letsencrypt:
postgres_data:
app_data:
reactive_resume_data:
```
**Environment variables (`.env`):**
@@ -163,8 +163,6 @@ services:
- ./certs:/etc/nginx/certs:ro
networks:
- reactive_resume_network
depends_on:
- app
postgres:
image: postgres:latest
@@ -195,18 +193,18 @@ services:
networks:
- reactive_resume_network
app:
reactive_resume:
image: amruthpillai/reactive-resume:latest
restart: unless-stopped
environment:
- APP_URL=https://resume.${DOMAIN}
- PRINTER_APP_URL=http://app:3000
- PRINTER_APP_URL=http://reactive_resume:3000
- DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/postgres
- PRINTER_ENDPOINT=http://printer:3000
- AUTH_SECRET=${AUTH_SECRET}
# Add other optional env vars as needed (SMTP, S3, OAuth, etc.)
volumes:
- app_data:/app/data
- reactive_resume_data:/app/data
networks:
- reactive_resume_network
depends_on:
@@ -226,7 +224,7 @@ networks:
volumes:
postgres_data:
app_data:
reactive_resume_data:
```
**nginx configuration (`nginx.conf`):**
@@ -238,7 +236,7 @@ events {
http {
upstream reactive_resume {
server app:3000;
server reactive_resume:3000;
}
# Redirect HTTP to HTTPS
@@ -384,13 +382,13 @@ services:
mode: replicated
replicas: 1
app:
reactive_resume:
image: ghcr.io/amruthpillai/reactive-resume:latest
networks:
- traefik_network
- reactive_resume_network
volumes:
- reactive_resume_app_data:/app/data
- reactive_resume_data:/app/data
environment:
- APP_URL=$APP_URL
# If using browserless with token auth, include the token in the URL:
@@ -447,8 +445,8 @@ volumes:
name: reactive_resume_postgres_data
reactive_resume_seaweedfs_data:
name: reactive_resume_seaweedfs_data
reactive_resume_app_data:
name: reactive_resume_app_data
reactive_resume_data:
name: reactive_resume_data
```
**Deploy the stack:**
BIN
View File
Binary file not shown.
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: am\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:14\n"
"Last-Translator: \n"
"Language-Team: Amharic\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
@@ -2081,6 +2081,18 @@ msgstr "ማካፈል"
msgid "Show"
msgstr "አሳይ"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "የጎን አሞሌ"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: ar\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:18\n"
"PO-Revision-Date: 2026-01-31 12:13\n"
"Last-Translator: \n"
"Language-Team: Arabic\n"
"Plural-Forms: nplurals=6; plural=(n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5);\n"
@@ -2081,6 +2081,18 @@ msgstr "المشاركة"
msgid "Show"
msgstr "إظهار"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "الشريط الجانبي"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: az\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:14\n"
"Last-Translator: \n"
"Language-Team: Azerbaijani\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
@@ -2081,6 +2081,18 @@ msgstr "Paylaşma"
msgid "Show"
msgstr "Göstər"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "Yan panel"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: bg\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:18\n"
"PO-Revision-Date: 2026-01-31 12:13\n"
"Last-Translator: \n"
"Language-Team: Bulgarian\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
@@ -2081,6 +2081,18 @@ msgstr "Споделяне"
msgid "Show"
msgstr "Показване"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "Странична лента"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: bn\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:14\n"
"Last-Translator: \n"
"Language-Team: Bengali\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
@@ -2081,6 +2081,18 @@ msgstr "শেয়ারিং"
msgid "Show"
msgstr "দেখান"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "সাইডবার"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: ca\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:18\n"
"PO-Revision-Date: 2026-01-31 12:13\n"
"Last-Translator: \n"
"Language-Team: Catalan\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
@@ -2081,6 +2081,18 @@ msgstr "Compartició"
msgid "Show"
msgstr "Mostra"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "Barra lateral"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: cs\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:18\n"
"PO-Revision-Date: 2026-01-31 12:13\n"
"Last-Translator: \n"
"Language-Team: Czech\n"
"Plural-Forms: nplurals=4; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 3;\n"
@@ -2081,6 +2081,18 @@ msgstr "Sdílení"
msgid "Show"
msgstr "Zobrazit"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "Postranní panel"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: da\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:18\n"
"PO-Revision-Date: 2026-01-31 12:13\n"
"Last-Translator: \n"
"Language-Team: Danish\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
@@ -2081,6 +2081,18 @@ msgstr "Deling"
msgid "Show"
msgstr "Vis"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "Sidebjælke"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: de\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:18\n"
"PO-Revision-Date: 2026-01-31 12:13\n"
"Last-Translator: \n"
"Language-Team: German\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
@@ -2081,6 +2081,18 @@ msgstr "Teilen"
msgid "Show"
msgstr "Einblenden"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "Seitenleiste"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: el\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:18\n"
"PO-Revision-Date: 2026-01-31 12:13\n"
"Last-Translator: \n"
"Language-Team: Greek\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
@@ -2081,6 +2081,18 @@ msgstr "Κοινοποίηση"
msgid "Show"
msgstr "Εμφάνιση"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "Πλαϊνή μπάρα"
+12
View File
@@ -2076,6 +2076,18 @@ msgstr "Sharing"
msgid "Show"
msgstr "Show"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr "Show link in title"
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "Sidebar"
+26 -14
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: es\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:18\n"
"PO-Revision-Date: 2026-01-31 15:33\n"
"Last-Translator: \n"
"Language-Team: Spanish\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
@@ -299,7 +299,7 @@ msgstr "Árabe"
#: src/hooks/use-form-blocker.tsx
msgid "Are you sure you want to close this dialog?"
msgstr ""
msgstr "¿Estás seguro de que deseas cerrar este diálogo?"
#: src/routes/dashboard/settings/api-keys.tsx
msgid "Are you sure you want to delete this API key?"
@@ -577,7 +577,7 @@ msgstr "Conectar"
#: src/dialogs/resume/sections/cover-letter.tsx
#: src/dialogs/resume/sections/summary-item.tsx
msgid "Content"
msgstr ""
msgstr "Contenido"
#: src/dialogs/auth/enable-two-factor.tsx
#: src/dialogs/auth/enable-two-factor.tsx
@@ -621,7 +621,7 @@ msgstr "Copiar URL"
#: src/utils/resume/section.tsx
msgid "Cover Letter"
msgstr ""
msgstr "Carta de presentación"
#: src/dialogs/api-key/create.tsx
#: src/dialogs/resume/index.tsx
@@ -662,7 +662,7 @@ msgstr "Crear una nueva certificación"
#: src/dialogs/resume/sections/cover-letter.tsx
msgid "Create a new cover letter"
msgstr ""
msgstr "Crear una nueva carta de presentación"
#: src/dialogs/resume/sections/custom.tsx
msgid "Create a new custom section"
@@ -713,7 +713,7 @@ msgstr "Crear una nueva habilidad"
#: src/dialogs/resume/sections/summary-item.tsx
msgid "Create a new summary item"
msgstr ""
msgstr "Crear una nueva entrada del resumen"
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Create a new volunteer experience"
@@ -1035,7 +1035,7 @@ msgstr "Todo lo que necesitas para crear, personalizar y compartir currículums
#: src/components/input/rich-input.tsx
msgid "Exit Fullscreen"
msgstr ""
msgstr "Salir de pantalla completa"
#: src/utils/resume/section.tsx
msgid "Experience"
@@ -1147,7 +1147,7 @@ msgstr "Ancho completo"
#: src/components/input/rich-input.tsx
msgid "Fullscreen"
msgstr ""
msgstr "Pantalla completa"
#: src/dialogs/resume/index.tsx
msgid "Generate a random name"
@@ -1429,7 +1429,7 @@ msgstr "Obtén más información sobre cómo ajustar el contenido en una página
#: src/hooks/use-form-blocker.tsx
msgid "Leave"
msgstr ""
msgstr "Salir"
#: src/routes/builder/$resumeId/-sidebar/left/shared/section-menu.tsx
msgid "Leave empty to reset the title to the original."
@@ -1861,7 +1861,7 @@ msgstr "Reactive Resume es de código abierto, centrado en la privacidad y compl
#: src/dialogs/resume/sections/cover-letter.tsx
msgid "Recipient"
msgstr ""
msgstr "Destinatario"
#: src/components/level/combobox.tsx
msgid "Rectangle"
@@ -2081,6 +2081,18 @@ msgstr "Compartir"
msgid "Show"
msgstr "Mostrar"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr "Mostrar enlace en el título"
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "Barra lateral"
@@ -2220,7 +2232,7 @@ msgstr "Estadísticas"
#: src/hooks/use-form-blocker.tsx
msgid "Stay"
msgstr ""
msgstr "Continuar"
#: src/components/input/rich-input.tsx
msgid "Strike"
@@ -2545,7 +2557,7 @@ msgstr "Actualizar una certificación existente"
#: src/dialogs/resume/sections/cover-letter.tsx
msgid "Update an existing cover letter"
msgstr ""
msgstr "Actualizar la carta de presentación"
#: src/dialogs/resume/sections/custom.tsx
msgid "Update an existing custom section"
@@ -2589,7 +2601,7 @@ msgstr "Actualizar una habilidad existente"
#: src/dialogs/resume/sections/summary-item.tsx
msgid "Update an existing summary item"
msgstr ""
msgstr "Actualizar una entrada del perfil"
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Update an existing volunteer experience"
@@ -2754,7 +2766,7 @@ msgstr "Después de eliminarla, ya no podrás usar la clave de acceso \"{0}\" pa
#: src/hooks/use-form-blocker.tsx
msgid "You have unsaved changes that will be lost."
msgstr ""
msgstr "Tienes cambios sin guardar que se perderán."
#: src/routes/auth/forgot-password.tsx
#: src/routes/auth/register.tsx
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: fa\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:14\n"
"Last-Translator: \n"
"Language-Team: Persian\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
@@ -2081,6 +2081,18 @@ msgstr "اشتراک‌گذاری"
msgid "Show"
msgstr "نمایش"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "نوار کناری"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: fi\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:13\n"
"Last-Translator: \n"
"Language-Team: Finnish\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
@@ -2081,6 +2081,18 @@ msgstr "Jakaminen"
msgid "Show"
msgstr "Näytä"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "Sivupalkki"
+26 -14
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: fr\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:18\n"
"PO-Revision-Date: 2026-02-05 12:19\n"
"Last-Translator: \n"
"Language-Team: French\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
@@ -299,7 +299,7 @@ msgstr "Arabe"
#: src/hooks/use-form-blocker.tsx
msgid "Are you sure you want to close this dialog?"
msgstr ""
msgstr "Êtes-vous sûr de vouloir supprimer cet élément ?"
#: src/routes/dashboard/settings/api-keys.tsx
msgid "Are you sure you want to delete this API key?"
@@ -577,7 +577,7 @@ msgstr "Se connecter"
#: src/dialogs/resume/sections/cover-letter.tsx
#: src/dialogs/resume/sections/summary-item.tsx
msgid "Content"
msgstr ""
msgstr "Contenu"
#: src/dialogs/auth/enable-two-factor.tsx
#: src/dialogs/auth/enable-two-factor.tsx
@@ -621,7 +621,7 @@ msgstr "Copier l'URL"
#: src/utils/resume/section.tsx
msgid "Cover Letter"
msgstr ""
msgstr "Lettre de motivation"
#: src/dialogs/api-key/create.tsx
#: src/dialogs/resume/index.tsx
@@ -662,7 +662,7 @@ msgstr "Créer une nouvelle certification"
#: src/dialogs/resume/sections/cover-letter.tsx
msgid "Create a new cover letter"
msgstr ""
msgstr "Créez une nouvelle lettre de motivation"
#: src/dialogs/resume/sections/custom.tsx
msgid "Create a new custom section"
@@ -713,7 +713,7 @@ msgstr "Créer une nouvelle compétence"
#: src/dialogs/resume/sections/summary-item.tsx
msgid "Create a new summary item"
msgstr ""
msgstr "Créer un nouvel élément de synthèse"
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Create a new volunteer experience"
@@ -1035,7 +1035,7 @@ msgstr "Tout ce dont vous avez besoin pour créer, personnaliser et partager des
#: src/components/input/rich-input.tsx
msgid "Exit Fullscreen"
msgstr ""
msgstr "Quitter le mode plein écran"
#: src/utils/resume/section.tsx
msgid "Experience"
@@ -1147,7 +1147,7 @@ msgstr "Pleine largeur"
#: src/components/input/rich-input.tsx
msgid "Fullscreen"
msgstr ""
msgstr "Plein écran"
#: src/dialogs/resume/index.tsx
msgid "Generate a random name"
@@ -1429,7 +1429,7 @@ msgstr "En savoir plus sur la manière d'adapter le contenu d'une page"
#: src/hooks/use-form-blocker.tsx
msgid "Leave"
msgstr ""
msgstr "Quitter"
#: src/routes/builder/$resumeId/-sidebar/left/shared/section-menu.tsx
msgid "Leave empty to reset the title to the original."
@@ -1861,7 +1861,7 @@ msgstr "Reactive Resume est un logiciel libre, respectueux de la vie privée et
#: src/dialogs/resume/sections/cover-letter.tsx
msgid "Recipient"
msgstr ""
msgstr "Destinataire"
#: src/components/level/combobox.tsx
msgid "Rectangle"
@@ -2081,6 +2081,18 @@ msgstr "Partage"
msgid "Show"
msgstr "Afficher"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr "Afficher le lien dans le titre"
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "Encart"
@@ -2220,7 +2232,7 @@ msgstr "Statistiques"
#: src/hooks/use-form-blocker.tsx
msgid "Stay"
msgstr ""
msgstr "Rester"
#: src/components/input/rich-input.tsx
msgid "Strike"
@@ -2545,7 +2557,7 @@ msgstr "Modifier une certification existante"
#: src/dialogs/resume/sections/cover-letter.tsx
msgid "Update an existing cover letter"
msgstr ""
msgstr "Modifier une lettre de motivation existante"
#: src/dialogs/resume/sections/custom.tsx
msgid "Update an existing custom section"
@@ -2589,7 +2601,7 @@ msgstr "Modifier une compétence existante"
#: src/dialogs/resume/sections/summary-item.tsx
msgid "Update an existing summary item"
msgstr ""
msgstr "Mise à jour d'un élément de synthèse existant"
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Update an existing volunteer experience"
@@ -2754,7 +2766,7 @@ msgstr "Vous ne pouvez plus utiliser la clé d'accès \"{0}\" pour vous connecte
#: src/hooks/use-form-blocker.tsx
msgid "You have unsaved changes that will be lost."
msgstr ""
msgstr "Vous avez des modifications non enregistrées qui seront perdues."
#: src/routes/auth/forgot-password.tsx
#: src/routes/auth/register.tsx
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: he\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:13\n"
"Last-Translator: \n"
"Language-Team: Hebrew\n"
"Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3;\n"
@@ -2081,6 +2081,18 @@ msgstr "שיתוף"
msgid "Show"
msgstr "הצגה"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "סרגל צד"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: hi\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:14\n"
"Last-Translator: \n"
"Language-Team: Hindi\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
@@ -2081,6 +2081,18 @@ msgstr "साझा करना"
msgid "Show"
msgstr "दिखाएँ"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "साइडबार"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: hu\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:13\n"
"Last-Translator: \n"
"Language-Team: Hungarian\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
@@ -2081,6 +2081,18 @@ msgstr "Megosztás"
msgid "Show"
msgstr "Megjelenítés"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "Oldalsáv"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: id\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:13\n"
"Last-Translator: \n"
"Language-Team: Indonesian\n"
"Plural-Forms: nplurals=1; plural=0;\n"
@@ -2081,6 +2081,18 @@ msgstr "Berbagi"
msgid "Show"
msgstr "Tampilkan"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "Bilah Sisi"
+26 -14
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: it\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 15:33\n"
"Last-Translator: \n"
"Language-Team: Italian\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
@@ -299,7 +299,7 @@ msgstr "Arabo"
#: src/hooks/use-form-blocker.tsx
msgid "Are you sure you want to close this dialog?"
msgstr ""
msgstr "Sei sicuro di voler chiudere questa finestra di dialogo?"
#: src/routes/dashboard/settings/api-keys.tsx
msgid "Are you sure you want to delete this API key?"
@@ -577,7 +577,7 @@ msgstr "Connetti"
#: src/dialogs/resume/sections/cover-letter.tsx
#: src/dialogs/resume/sections/summary-item.tsx
msgid "Content"
msgstr ""
msgstr "Contenuto"
#: src/dialogs/auth/enable-two-factor.tsx
#: src/dialogs/auth/enable-two-factor.tsx
@@ -621,7 +621,7 @@ msgstr "Copia URL"
#: src/utils/resume/section.tsx
msgid "Cover Letter"
msgstr ""
msgstr "Lettera di presentazione"
#: src/dialogs/api-key/create.tsx
#: src/dialogs/resume/index.tsx
@@ -662,7 +662,7 @@ msgstr "Crea una nuova certificazione"
#: src/dialogs/resume/sections/cover-letter.tsx
msgid "Create a new cover letter"
msgstr ""
msgstr "Crea una nuova lettera di presentazione"
#: src/dialogs/resume/sections/custom.tsx
msgid "Create a new custom section"
@@ -713,7 +713,7 @@ msgstr "Crea una nuova competenza"
#: src/dialogs/resume/sections/summary-item.tsx
msgid "Create a new summary item"
msgstr ""
msgstr "Crea un nuovo elemento di riepilogo"
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Create a new volunteer experience"
@@ -1035,7 +1035,7 @@ msgstr "Tutto ciò di cui hai bisogno per creare, personalizzare e condividere c
#: src/components/input/rich-input.tsx
msgid "Exit Fullscreen"
msgstr ""
msgstr "Esci dalla modalità a schermo intero"
#: src/utils/resume/section.tsx
msgid "Experience"
@@ -1147,7 +1147,7 @@ msgstr "Larghezza completa"
#: src/components/input/rich-input.tsx
msgid "Fullscreen"
msgstr ""
msgstr "A schermo intero"
#: src/dialogs/resume/index.tsx
msgid "Generate a random name"
@@ -1429,7 +1429,7 @@ msgstr "Scopri di più su come adattare il contenuto a una pagina"
#: src/hooks/use-form-blocker.tsx
msgid "Leave"
msgstr ""
msgstr "Abbandona"
#: src/routes/builder/$resumeId/-sidebar/left/shared/section-menu.tsx
msgid "Leave empty to reset the title to the original."
@@ -1861,7 +1861,7 @@ msgstr "Reactive Resume è open source, incentrato sulla privacy e completamente
#: src/dialogs/resume/sections/cover-letter.tsx
msgid "Recipient"
msgstr ""
msgstr "Destinatario"
#: src/components/level/combobox.tsx
msgid "Rectangle"
@@ -2081,6 +2081,18 @@ msgstr "Condivisione"
msgid "Show"
msgstr "Mostra"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr "Mostra il link nel titolo"
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "Barra laterale"
@@ -2220,7 +2232,7 @@ msgstr "Statistiche"
#: src/hooks/use-form-blocker.tsx
msgid "Stay"
msgstr ""
msgstr "Rimani"
#: src/components/input/rich-input.tsx
msgid "Strike"
@@ -2545,7 +2557,7 @@ msgstr "Aggiorna una certificazione esistente"
#: src/dialogs/resume/sections/cover-letter.tsx
msgid "Update an existing cover letter"
msgstr ""
msgstr "Aggiorna una lettera di presentazione esistente"
#: src/dialogs/resume/sections/custom.tsx
msgid "Update an existing custom section"
@@ -2589,7 +2601,7 @@ msgstr "Aggiorna una competenza esistente"
#: src/dialogs/resume/sections/summary-item.tsx
msgid "Update an existing summary item"
msgstr ""
msgstr "Aggiorna una voce del profilo"
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Update an existing volunteer experience"
@@ -2754,7 +2766,7 @@ msgstr "Dopo l'eliminazione non potrai più usare la passkey \"{0}\" per acceder
#: src/hooks/use-form-blocker.tsx
msgid "You have unsaved changes that will be lost."
msgstr ""
msgstr "Hai modifiche non salvate che andranno perse."
#: src/routes/auth/forgot-password.tsx
#: src/routes/auth/register.tsx
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: ja\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:13\n"
"Last-Translator: \n"
"Language-Team: Japanese\n"
"Plural-Forms: nplurals=1; plural=0;\n"
@@ -2081,6 +2081,18 @@ msgstr "共有"
msgid "Show"
msgstr "表示"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "サイドバー"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: km\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:14\n"
"Last-Translator: \n"
"Language-Team: Khmer\n"
"Plural-Forms: nplurals=1; plural=0;\n"
@@ -2081,6 +2081,18 @@ msgstr "ការចែករំលែក"
msgid "Show"
msgstr "បង្ហាញ"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "របារ​បាត"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: kn\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:14\n"
"Last-Translator: \n"
"Language-Team: Kannada\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
@@ -2081,6 +2081,18 @@ msgstr "ಹಂಚಿಕೆ"
msgid "Show"
msgstr "ತೋರಿಸು"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "ಪಾರ್ಶ್ವಪಟ್ಟಿ"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: ko\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:13\n"
"Last-Translator: \n"
"Language-Team: Korean\n"
"Plural-Forms: nplurals=1; plural=0;\n"
@@ -2081,6 +2081,18 @@ msgstr "공유"
msgid "Show"
msgstr "표시"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "사이드바"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: lt\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:13\n"
"Last-Translator: \n"
"Language-Team: Lithuanian\n"
"Plural-Forms: nplurals=4; plural=(n%10==1 && (n%100>19 || n%100<11) ? 0 : (n%10>=2 && n%10<=9) && (n%100>19 || n%100<11) ? 1 : n%1!=0 ? 2: 3);\n"
@@ -2081,6 +2081,18 @@ msgstr "Dalijimasis"
msgid "Show"
msgstr "Rodyti"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "Šoninė juosta"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: lv\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:14\n"
"Last-Translator: \n"
"Language-Team: Latvian\n"
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2;\n"
@@ -2081,6 +2081,18 @@ msgstr "Kopīgošana"
msgid "Show"
msgstr "Rādīt"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "Sānu josla"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: ml\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:14\n"
"Last-Translator: \n"
"Language-Team: Malayalam\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
@@ -2081,6 +2081,18 @@ msgstr "ഷെയറിംഗ്"
msgid "Show"
msgstr "കാണിക്കുക"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "സൈഡ്ബാർ"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: mr\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:14\n"
"Last-Translator: \n"
"Language-Team: Marathi\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
@@ -2081,6 +2081,18 @@ msgstr "शेअर करणे"
msgid "Show"
msgstr "दाखवा"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "बाजूची पट्टी"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: ms\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:14\n"
"Last-Translator: \n"
"Language-Team: Malay\n"
"Plural-Forms: nplurals=1; plural=0;\n"
@@ -2081,6 +2081,18 @@ msgstr "Berkongsi"
msgid "Show"
msgstr "Paparkan"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "Panel Sisi"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: ne\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:14\n"
"Last-Translator: \n"
"Language-Team: Nepali\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
@@ -2081,6 +2081,18 @@ msgstr "सेयरिङ"
msgid "Show"
msgstr "देखाउनुहोस्"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "Sidebar"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: nl\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:13\n"
"Last-Translator: \n"
"Language-Team: Dutch\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
@@ -2081,6 +2081,18 @@ msgstr "Delen"
msgid "Show"
msgstr "Weergeven"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "Zijbalk"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: no\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:13\n"
"Last-Translator: \n"
"Language-Team: Norwegian\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
@@ -2081,6 +2081,18 @@ msgstr "Deling"
msgid "Show"
msgstr "Vis"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "Sidepanel"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: or\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:14\n"
"Last-Translator: \n"
"Language-Team: Odia\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
@@ -2081,6 +2081,18 @@ msgstr "ସେୟାର କରିବା"
msgid "Show"
msgstr "ଦେଖାନ୍ତୁ"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "ସାଇଡବାର୍"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: pl\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:13\n"
"Last-Translator: \n"
"Language-Team: Polish\n"
"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n"
@@ -2081,6 +2081,18 @@ msgstr "Udostępnianie"
msgid "Show"
msgstr "Pokaż"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "Pasek boczny"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: pt\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:13\n"
"Last-Translator: \n"
"Language-Team: Portuguese, Brazilian\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
@@ -2081,6 +2081,18 @@ msgstr "Compartilhamento"
msgid "Show"
msgstr "Mostrar"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "Barra lateral"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: pt\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:13\n"
"Last-Translator: \n"
"Language-Team: Portuguese\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
@@ -2081,6 +2081,18 @@ msgstr "Partilhar"
msgid "Show"
msgstr "Mostrar"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "Barra lateral"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: ro\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:18\n"
"PO-Revision-Date: 2026-01-31 12:13\n"
"Last-Translator: \n"
"Language-Team: Romanian\n"
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : (n==0 || (n%100>0 && n%100<20)) ? 1 : 2);\n"
@@ -2081,6 +2081,18 @@ msgstr "Partajarea"
msgid "Show"
msgstr "Arată"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "Bară laterală"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: ru\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:13\n"
"Last-Translator: \n"
"Language-Team: Russian\n"
"Plural-Forms: nplurals=4; plural=((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3));\n"
@@ -2081,6 +2081,18 @@ msgstr "Общий доступ"
msgid "Show"
msgstr "Показать"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "Боковая панель"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: sk\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:13\n"
"Last-Translator: \n"
"Language-Team: Slovak\n"
"Plural-Forms: nplurals=4; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 3;\n"
@@ -2081,6 +2081,18 @@ msgstr "Zdieľanie"
msgid "Show"
msgstr "Zobraziť"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "Bočný panel"
+42 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: sl\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:14\n"
"Last-Translator: \n"
"Language-Team: Slovenian\n"
"Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3;\n"
@@ -444,6 +444,8 @@ msgstr "Ali lahko izvozite svoj življenjepis v PDF?"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/cover-letter.tsx
#: src/dialogs/resume/sections/cover-letter.tsx
#: src/dialogs/resume/sections/custom.tsx
#: src/dialogs/resume/sections/custom.tsx
#: src/dialogs/resume/sections/education.tsx
@@ -572,6 +574,7 @@ msgstr "Potrdi"
msgid "Connect"
msgstr "Poveži"
#: src/dialogs/resume/sections/cover-letter.tsx
#: src/dialogs/resume/sections/summary-item.tsx
msgid "Content"
msgstr ""
@@ -616,10 +619,15 @@ msgstr "Kopirajte to skrivno kodo in jo uporabite v svojih aplikacijah za dostop
msgid "Copy URL"
msgstr "Kopiraj URL"
#: src/utils/resume/section.tsx
msgid "Cover Letter"
msgstr ""
#: src/dialogs/api-key/create.tsx
#: src/dialogs/resume/index.tsx
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/cover-letter.tsx
#: src/dialogs/resume/sections/custom.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
@@ -652,6 +660,10 @@ msgstr "Ustvari novo priznanje"
msgid "Create a new certification"
msgstr "Ustvari novo potrdilo"
#: src/dialogs/resume/sections/cover-letter.tsx
msgid "Create a new cover letter"
msgstr ""
#: src/dialogs/resume/sections/custom.tsx
msgid "Create a new custom section"
msgstr "Ustvari novo poljubno sekcijo"
@@ -1021,6 +1033,10 @@ msgstr "Vse, kar vnesete tukaj, je shranjeno lokalno v vašem brskalniku. Vaši
msgid "Everything you need to create, customize, and share professional resumes. Built with privacy in mind, powered by open source, and completely free forever."
msgstr "Vse, kar potrebujete za ustvarjanje, prilagajanje in deljenje strokovnih življenjepisov. Zgrajeno z mislijo na zasebnost, temelji na odprti kodi in je popolnoma brezplačno za vedno."
#: src/components/input/rich-input.tsx
msgid "Exit Fullscreen"
msgstr ""
#: src/utils/resume/section.tsx
msgid "Experience"
msgstr "Izkušnje"
@@ -1129,6 +1145,10 @@ msgstr "Francoščina"
msgid "Full Width"
msgstr "Polna širina"
#: src/components/input/rich-input.tsx
msgid "Fullscreen"
msgstr ""
#: src/dialogs/resume/index.tsx
msgid "Generate a random name"
msgstr "Ustvari naključno ime"
@@ -1839,6 +1859,10 @@ msgstr "Reactive Resume danes začenja na Product Huntu, obiščite nas in poka
msgid "Reactive Resume is open-source, privacy-focused, and completely free. Unlike other resume builders, it doesn't show ads, track your data, or limit your features behind a paywall."
msgstr "Reactive Resume je odprtokoden, osredotočen na zasebnost in popolnoma brezplačen. Za razliko od drugih graditeljev življenjepisov ne prikazuje oglasov, ne sledi vašim podatkom in ne omejuje funkcij z zidovi plačila."
#: src/dialogs/resume/sections/cover-letter.tsx
msgid "Recipient"
msgstr ""
#: src/components/level/combobox.tsx
msgid "Rectangle"
msgstr "Pravokotnik"
@@ -1946,6 +1970,7 @@ msgstr "ruščina"
#: src/dialogs/resume/index.tsx
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/cover-letter.tsx
#: src/dialogs/resume/sections/custom.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
@@ -2056,6 +2081,18 @@ msgstr "Deljenje"
msgid "Show"
msgstr "Prikaži"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "Stranska vrstica"
@@ -2518,6 +2555,10 @@ msgstr "Posodobi obstoječo nagrado"
msgid "Update an existing certification"
msgstr "Posodobi obstoječo certifikacijo"
#: src/dialogs/resume/sections/cover-letter.tsx
msgid "Update an existing cover letter"
msgstr ""
#: src/dialogs/resume/sections/custom.tsx
msgid "Update an existing custom section"
msgstr "Posodobi obstoječi razdelek po meri"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: sq\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:13\n"
"Last-Translator: \n"
"Language-Team: Albanian\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
@@ -2081,6 +2081,18 @@ msgstr "Shpërndarja"
msgid "Show"
msgstr "Shfaq"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "Shiriti anësor"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: sr\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:13\n"
"Last-Translator: \n"
"Language-Team: Serbian (Cyrillic)\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
@@ -2081,6 +2081,18 @@ msgstr "Дељење"
msgid "Show"
msgstr "Прикажи"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "Бочна трака"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: sv\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:13\n"
"Last-Translator: \n"
"Language-Team: Swedish\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
@@ -2081,6 +2081,18 @@ msgstr "Delning"
msgid "Show"
msgstr "Visa"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "Sidofält"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: ta\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:14\n"
"Last-Translator: \n"
"Language-Team: Tamil\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
@@ -2081,6 +2081,18 @@ msgstr "பகிர்வு"
msgid "Show"
msgstr "காட்டு"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "சைட்பார்"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: te\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:14\n"
"Last-Translator: \n"
"Language-Team: Telugu\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
@@ -2081,6 +2081,18 @@ msgstr "షేరింగ్"
msgid "Show"
msgstr "చూపించండి"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "<<<<<<< HEAD"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: th\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:14\n"
"Last-Translator: \n"
"Language-Team: Thai\n"
"Plural-Forms: nplurals=1; plural=0;\n"
@@ -2081,6 +2081,18 @@ msgstr "การแชร์"
msgid "Show"
msgstr "แสดง"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "<<<<<<< HEAD"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: tr\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:13\n"
"Last-Translator: \n"
"Language-Team: Turkish\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
@@ -2081,6 +2081,18 @@ msgstr "Paylaşım"
msgid "Show"
msgstr "Göster"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "<<<<<<< HEAD"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: uk\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:13\n"
"Last-Translator: \n"
"Language-Team: Ukrainian\n"
"Plural-Forms: nplurals=4; plural=((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3));\n"
@@ -2081,6 +2081,18 @@ msgstr "Поширити"
msgid "Show"
msgstr "Показати"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "<<<<<<< HEAD"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: uz\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:14\n"
"Last-Translator: \n"
"Language-Team: Uzbek\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
@@ -2081,6 +2081,18 @@ msgstr "Ulashish"
msgid "Show"
msgstr "Ko‘rsatish"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "<<<<<<< HEAD"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: vi\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:13\n"
"Last-Translator: \n"
"Language-Team: Vietnamese\n"
"Plural-Forms: nplurals=1; plural=0;\n"
@@ -2081,6 +2081,18 @@ msgstr "Chia sẻ"
msgid "Show"
msgstr "Hiện"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "<<<<<<< HEAD"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: zh\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:13\n"
"Last-Translator: \n"
"Language-Team: Chinese Simplified\n"
"Plural-Forms: nplurals=1; plural=0;\n"
@@ -2081,6 +2081,18 @@ msgstr "发布与分享"
msgid "Show"
msgstr "显示"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "侧边栏"
+13 -1
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: zh\n"
"Project-Id-Version: reactive-resume\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-01-31 01:19\n"
"PO-Revision-Date: 2026-01-31 12:13\n"
"Last-Translator: \n"
"Language-Team: Chinese Traditional\n"
"Plural-Forms: nplurals=1; plural=0;\n"
@@ -2081,6 +2081,18 @@ msgstr "分享"
msgid "Show"
msgstr "顯示"
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr "側邊欄"
+12
View File
@@ -2076,6 +2076,18 @@ msgstr ""
msgid "Show"
msgstr ""
#: src/dialogs/resume/sections/award.tsx
#: src/dialogs/resume/sections/certification.tsx
#: src/dialogs/resume/sections/education.tsx
#: src/dialogs/resume/sections/experience.tsx
#: src/dialogs/resume/sections/profile.tsx
#: src/dialogs/resume/sections/project.tsx
#: src/dialogs/resume/sections/publication.tsx
#: src/dialogs/resume/sections/reference.tsx
#: src/dialogs/resume/sections/volunteer.tsx
msgid "Show link in title"
msgstr ""
#: src/routes/builder/$resumeId/-sidebar/right/sections/layout/pages.tsx
msgid "Sidebar"
msgstr ""
+38 -36
View File
@@ -1,10 +1,10 @@
{
"name": "reactive-resume",
"description": "Reactive Resume is a free and open-source resume builder that simplifies the process of creating, updating, and sharing your resume.",
"version": "5.0.5",
"version": "5.0.6",
"license": "MIT",
"type": "module",
"packageManager": "pnpm@10.28.2+sha512.41872f037ad22f7348e3b1debbaf7e867cfd448f2726d9cf74c08f19507c31d2c8e7a11525b983febc2df640b5438dee6023ebb1f84ed43cc2d654d2bc326264",
"packageManager": "pnpm@10.29.1+sha512.48dae233635a645768a3028d19545cacc1688639eeb1f3734e42d6d6b971afbf22aa1ac9af52a173d9c3a20c15857cfa400f19994d79a2f626fcc73fccda9bbc",
"repository": {
"type": "git",
"url": "https://github.com/amruthpillai/reactive-resume.git"
@@ -31,12 +31,12 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@ai-sdk/anthropic": "^3.0.33",
"@ai-sdk/google": "^3.0.18",
"@ai-sdk/openai": "^3.0.23",
"@aws-sdk/client-s3": "^3.980.0",
"@better-auth/core": "1.5.0-beta.10",
"@better-auth/passkey": "1.5.0-beta.10",
"@ai-sdk/anthropic": "^3.0.38",
"@ai-sdk/google": "^3.0.22",
"@ai-sdk/openai": "^3.0.26",
"@aws-sdk/client-s3": "^3.985.0",
"@better-auth/core": "1.5.0-beta.13",
"@better-auth/passkey": "1.5.0-beta.13",
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
@@ -56,47 +56,49 @@
"@sindresorhus/slugify": "^3.0.0",
"@t3-oss/env-core": "^0.13.10",
"@tanstack/react-query": "5.90.20",
"@tanstack/react-router": "^1.157.17",
"@tanstack/react-router-ssr-query": "^1.157.17",
"@tanstack/react-start": "^1.157.17",
"@tanstack/zod-adapter": "^1.157.17",
"@tiptap/extension-highlight": "^3.18.0",
"@tiptap/extension-table": "^3.18.0",
"@tiptap/extension-text-align": "^3.18.0",
"@tiptap/pm": "^3.18.0",
"@tiptap/react": "^3.18.0",
"@tiptap/starter-kit": "^3.18.0",
"ai": "^6.0.63",
"ai-sdk-ollama": "^3.3.0",
"@tanstack/react-router": "^1.158.4",
"@tanstack/react-router-ssr-query": "^1.158.4",
"@tanstack/react-start": "^1.159.0",
"@tanstack/zod-adapter": "^1.158.4",
"@tiptap/extension-highlight": "^3.19.0",
"@tiptap/extension-table": "^3.19.0",
"@tiptap/extension-text-align": "^3.19.0",
"@tiptap/pm": "^3.19.0",
"@tiptap/react": "^3.19.0",
"@tiptap/starter-kit": "^3.19.0",
"ai": "^6.0.77",
"ai-sdk-ollama": "^3.5.0",
"bcrypt": "^6.0.0",
"better-auth": "1.5.0-beta.10",
"better-auth": "1.5.0-beta.13",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"dompurify": "^3.3.1",
"drizzle-orm": "^1.0.0-beta.12-a5629fb",
"drizzle-orm": "^1.0.0-beta.15-859cf75",
"drizzle-zod": "^0.8.3",
"es-toolkit": "^1.44.0",
"fast-deep-equal": "^3.1.3",
"fast-json-patch": "^3.1.1",
"fuse.js": "^7.1.0",
"immer": "^11.1.3",
"input-otp": "^1.4.2",
"js-cookie": "^3.0.5",
"monaco-editor": "^0.55.1",
"motion": "^12.29.2",
"nodemailer": "^7.0.13",
"motion": "^12.33.0",
"nodemailer": "^8.0.1",
"pg": "^8.18.0",
"puppeteer-core": "^24.36.1",
"puppeteer-core": "^24.37.2",
"qrcode.react": "^4.2.0",
"radix-ui": "^1.4.3",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-hook-form": "^7.71.1",
"react-hotkeys-hook": "^5.2.3",
"react-resizable-panels": "^4.5.6",
"react-window": "^2.2.5",
"react-hotkeys-hook": "^5.2.4",
"react-resizable-panels": "^4.6.2",
"react-window": "^2.2.6",
"react-zoom-pan-pinch": "^3.7.0",
"reflect-metadata": "^0.2.2",
"shadcn": "^3.8.0",
"shadcn": "^3.8.4",
"sharp": "^0.34.5",
"sonner": "^2.0.7",
"tailwind-merge": "^3.4.0",
@@ -110,25 +112,25 @@
"workbox-window": "^7.4.0",
"zod": "^4.3.6",
"zundo": "^2.3.0",
"zustand": "^5.0.10"
"zustand": "^5.0.11"
},
"devDependencies": {
"@biomejs/biome": "^2.3.13",
"@biomejs/biome": "^2.3.14",
"@lingui/babel-plugin-lingui-macro": "^5.9.0",
"@lingui/cli": "^5.9.0",
"@lingui/vite-plugin": "^5.9.0",
"@tailwindcss/vite": "^4.1.18",
"@types/bcrypt": "^6.0.0",
"@types/js-cookie": "^3.0.6",
"@types/node": "^25.1.0",
"@types/node": "^25.2.1",
"@types/nodemailer": "^7.0.9",
"@types/pg": "^8.16.0",
"@types/react": "^19.2.10",
"@types/react": "^19.2.13",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.2",
"@vitejs/plugin-react": "^5.1.3",
"babel-plugin-macros": "^3.1.0",
"drizzle-kit": "^1.0.0-beta.12-a5629fb",
"knip": "^5.82.1",
"drizzle-kit": "^1.0.0-beta.15-859cf75",
"knip": "^5.83.1",
"nitro": "npm:nitro-nightly@latest",
"node-addon-api": "^8.5.0",
"node-gyp": "^12.2.0",
+1743 -1641
View File
File diff suppressed because it is too large Load Diff
+21 -18
View File
@@ -24,9 +24,10 @@ function ensurePrefix(url: string) {
type Props = Omit<React.ComponentProps<"input">, "value" | "onChange"> & {
value: z.infer<typeof urlSchema>;
onChange: (value: z.infer<typeof urlSchema>) => void;
hideLabelButton?: boolean;
};
export function URLInput({ value, onChange, ...props }: Props) {
export function URLInput({ value, onChange, hideLabelButton, ...props }: Props) {
const handleUrlChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
onChange({
@@ -59,24 +60,26 @@ export function URLInput({ value, onChange, ...props }: Props) {
{...props}
/>
<InputGroupAddon align="inline-end">
<Popover>
<PopoverTrigger asChild>
<InputGroupButton size="icon-sm" title={t`Add a label to the URL`}>
<TagIcon />
</InputGroupButton>
</PopoverTrigger>
{!hideLabelButton && (
<InputGroupAddon align="inline-end">
<Popover>
<PopoverTrigger asChild>
<InputGroupButton size="icon-sm" title={t`Add a label to the URL`}>
<TagIcon />
</InputGroupButton>
</PopoverTrigger>
<PopoverContent className="pt-3">
<div className="grid gap-2" onClick={(e) => e.stopPropagation()}>
<Label htmlFor="url-label">
<Trans>Label</Trans>
</Label>
<Input id="url-label" name="url-label" value={value.label} onChange={handleLabelChange} />
</div>
</PopoverContent>
</Popover>
</InputGroupAddon>
<PopoverContent className="pt-3">
<div className="grid gap-2" onClick={(e) => e.stopPropagation()}>
<Label htmlFor="url-label">
<Trans>Label</Trans>
</Label>
<Input id="url-label" name="url-label" value={value.label} onChange={handleLabelChange} />
</div>
</PopoverContent>
</Popover>
</InputGroupAddon>
)}
</InputGroup>
);
}
+2
View File
@@ -29,6 +29,7 @@ export function LevelDisplay({ icon, type, level, className, ...props }: Props)
return (
<div
key={index}
data-active={isActive}
className={cn(
"h-2.5 flex-1 border border-(--page-primary-color) border-x-0 first:border-l last:border-r",
isActive && "bg-(--page-primary-color)",
@@ -50,6 +51,7 @@ export function LevelDisplay({ icon, type, level, className, ...props }: Props)
return (
<div
key={index}
data-active={isActive}
className={cn(
"size-2.5 border border-(--page-primary-color)",
isActive && "bg-(--page-primary-color)",
-6
View File
@@ -53,12 +53,6 @@
text-underline-offset: 0.15rem;
}
@media print {
a {
text-decoration: none;
}
}
strong {
font-weight: var(--page-body-font-weight-bold);
}
@@ -2,6 +2,7 @@ import { TiptapContent } from "@/components/input/rich-input";
import type { SectionItem } from "@/schema/resume/data";
import { stripHtml } from "@/utils/string";
import { cn } from "@/utils/style";
import { LinkedTitle } from "../linked-title";
import { PageLink } from "../page-link";
type AwardsItemProps = SectionItem<"awards"> & {
@@ -15,7 +16,12 @@ export function AwardsItem({ className, ...item }: AwardsItemProps) {
<div className="section-item-header awards-item-header">
{/* Row 1 */}
<div className="flex items-start justify-between gap-x-2">
<strong className="section-item-title awards-item-title">{item.title}</strong>
<LinkedTitle
title={item.title}
website={item.website}
showLinkInTitle={item.options?.showLinkInTitle}
className="section-item-title awards-item-title"
/>
<span className="section-item-metadata awards-item-date shrink-0 text-end">{item.date}</span>
</div>
@@ -31,9 +37,11 @@ export function AwardsItem({ className, ...item }: AwardsItemProps) {
</div>
{/* Website */}
<div className="section-item-website awards-item-website">
<PageLink {...item.website} label={item.website.label} />
</div>
{!item.options?.showLinkInTitle && (
<div className="section-item-website awards-item-website">
<PageLink {...item.website} label={item.website.label} />
</div>
)}
</div>
);
}
@@ -2,6 +2,7 @@ import { TiptapContent } from "@/components/input/rich-input";
import type { SectionItem } from "@/schema/resume/data";
import { stripHtml } from "@/utils/string";
import { cn } from "@/utils/style";
import { LinkedTitle } from "../linked-title";
import { PageLink } from "../page-link";
type CertificationsItemProps = SectionItem<"certifications"> & {
@@ -15,7 +16,12 @@ export function CertificationsItem({ className, ...item }: CertificationsItemPro
<div className="section-item-header certifications-item-header">
{/* Row 1 */}
<div className="flex items-start justify-between gap-x-2">
<strong className="section-item-title certifications-item-title">{item.title}</strong>
<LinkedTitle
title={item.title}
website={item.website}
showLinkInTitle={item.options?.showLinkInTitle}
className="section-item-title certifications-item-title"
/>
<span className="section-item-metadata certifications-item-date shrink-0 text-end">{item.date}</span>
</div>
@@ -36,9 +42,11 @@ export function CertificationsItem({ className, ...item }: CertificationsItemPro
</div>
{/* Website */}
<div className="section-item-website certifications-item-website">
<PageLink {...item.website} label={item.website.label} />
</div>
{!item.options?.showLinkInTitle && (
<div className="section-item-website certifications-item-website">
<PageLink {...item.website} label={item.website.label} />
</div>
)}
</div>
);
}
@@ -2,6 +2,7 @@ import { TiptapContent } from "@/components/input/rich-input";
import type { SectionItem } from "@/schema/resume/data";
import { stripHtml } from "@/utils/string";
import { cn } from "@/utils/style";
import { LinkedTitle } from "../linked-title";
import { PageLink } from "../page-link";
type EducationItemProps = SectionItem<"education"> & {
@@ -15,7 +16,12 @@ export function EducationItem({ className, ...item }: EducationItemProps) {
<div className="section-item-header education-item-header mb-2">
{/* Row 1 */}
<div className="flex items-start justify-between gap-x-2">
<strong className="section-item-title education-item-title">{item.school}</strong>
<LinkedTitle
title={item.school}
website={item.website}
showLinkInTitle={item.options?.showLinkInTitle}
className="section-item-title education-item-title"
/>
<span className="section-item-metadata education-item-degree-grade shrink-0 text-end">
{[item.degree, item.grade].filter(Boolean).join(" • ")}
</span>
@@ -38,9 +44,11 @@ export function EducationItem({ className, ...item }: EducationItemProps) {
</div>
{/* Website */}
<div className="section-item-website education-item-website">
<PageLink {...item.website} label={item.website.label} />
</div>
{!item.options?.showLinkInTitle && (
<div className="section-item-website education-item-website">
<PageLink {...item.website} label={item.website.label} />
</div>
)}
</div>
);
}
@@ -2,6 +2,7 @@ import { TiptapContent } from "@/components/input/rich-input";
import type { SectionItem } from "@/schema/resume/data";
import { stripHtml } from "@/utils/string";
import { cn } from "@/utils/style";
import { LinkedTitle } from "../linked-title";
import { PageLink } from "../page-link";
type ExperienceItemProps = SectionItem<"experience"> & {
@@ -15,7 +16,12 @@ export function ExperienceItem({ className, ...item }: ExperienceItemProps) {
<div className="section-item-header experience-item-header">
{/* Row 1 */}
<div className="flex items-start justify-between gap-x-2">
<strong className="section-item-title experience-item-title">{item.company}</strong>
<LinkedTitle
title={item.company}
website={item.website}
showLinkInTitle={item.options?.showLinkInTitle}
className="section-item-title experience-item-title"
/>
<span className="section-item-metadata experience-item-location shrink-0 text-end">{item.location}</span>
</div>
@@ -34,9 +40,11 @@ export function ExperienceItem({ className, ...item }: ExperienceItemProps) {
</div>
{/* Website */}
<div className="section-item-website experience-item-website">
<PageLink {...item.website} label={item.website.label} />
</div>
{!item.options?.showLinkInTitle && (
<div className="section-item-website experience-item-website">
<PageLink {...item.website} label={item.website.label} />
</div>
)}
</div>
);
}
@@ -1,5 +1,6 @@
import type { SectionItem } from "@/schema/resume/data";
import { cn } from "@/utils/style";
import { LinkedTitle } from "../linked-title";
import { PageIcon } from "../page-icon";
import { PageLink } from "../page-link";
@@ -13,15 +14,22 @@ export function ProfilesItem({ className, ...item }: ProfilesItemProps) {
{/* Header */}
<div className="section-item-header profiles-item-header flex items-center gap-x-1.5">
<PageIcon icon={item.icon} className="section-item-icon profiles-item-icon" />
<strong className="section-item-title profiles-item-network">{item.network}</strong>
<LinkedTitle
title={item.network}
website={item.website}
showLinkInTitle={item.options?.showLinkInTitle}
className="section-item-title profiles-item-network"
/>
</div>
{/* Website */}
<PageLink
{...item.website}
label={item.website.label || item.username}
className="section-item-website profiles-item-website"
/>
{!item.options?.showLinkInTitle && (
<PageLink
{...item.website}
label={item.website.label || item.username}
className="section-item-website profiles-item-website"
/>
)}
</div>
);
}
@@ -2,6 +2,7 @@ import { TiptapContent } from "@/components/input/rich-input";
import type { SectionItem } from "@/schema/resume/data";
import { stripHtml } from "@/utils/string";
import { cn } from "@/utils/style";
import { LinkedTitle } from "../linked-title";
import { PageLink } from "../page-link";
type ProjectsItemProps = SectionItem<"projects"> & {
@@ -15,7 +16,12 @@ export function ProjectsItem({ className, ...item }: ProjectsItemProps) {
<div className="section-item-header projects-item-header">
{/* Row 1 */}
<div className="flex items-start justify-between gap-x-2">
<strong className="section-item-title projects-item-title">{item.name}</strong>
<LinkedTitle
title={item.name}
website={item.website}
showLinkInTitle={item.options?.showLinkInTitle}
className="section-item-title projects-item-title"
/>
<span className="section-item-metadata projects-item-period shrink-0 text-end">{item.period}</span>
</div>
</div>
@@ -28,9 +34,11 @@ export function ProjectsItem({ className, ...item }: ProjectsItemProps) {
</div>
{/* Website */}
<div className="section-item-website projects-item-website">
<PageLink {...item.website} label={item.website.label} />
</div>
{!item.options?.showLinkInTitle && (
<div className="section-item-website projects-item-website">
<PageLink {...item.website} label={item.website.label} />
</div>
)}
</div>
);
}
@@ -2,6 +2,7 @@ import { TiptapContent } from "@/components/input/rich-input";
import type { SectionItem } from "@/schema/resume/data";
import { stripHtml } from "@/utils/string";
import { cn } from "@/utils/style";
import { LinkedTitle } from "../linked-title";
import { PageLink } from "../page-link";
type PublicationsItemProps = SectionItem<"publications"> & {
@@ -15,7 +16,12 @@ export function PublicationsItem({ className, ...item }: PublicationsItemProps)
<div className="section-item-header publications-item-header">
{/* Row 1 */}
<div className="flex items-start justify-between gap-x-2">
<strong className="section-item-title publications-item-title">{item.title}</strong>
<LinkedTitle
title={item.title}
website={item.website}
showLinkInTitle={item.options?.showLinkInTitle}
className="section-item-title publications-item-title"
/>
<span className="section-item-metadata publications-item-date shrink-0 text-end">{item.date}</span>
</div>
@@ -36,9 +42,11 @@ export function PublicationsItem({ className, ...item }: PublicationsItemProps)
</div>
{/* Website */}
<div className="section-item-website publications-item-website">
<PageLink {...item.website} label={item.website.label} />
</div>
{!item.options?.showLinkInTitle && (
<div className="section-item-website publications-item-website">
<PageLink {...item.website} label={item.website.label} />
</div>
)}
</div>
);
}
@@ -2,6 +2,7 @@ import { TiptapContent } from "@/components/input/rich-input";
import type { SectionItem } from "@/schema/resume/data";
import { stripHtml } from "@/utils/string";
import { cn } from "@/utils/style";
import { LinkedTitle } from "../linked-title";
import { PageLink } from "../page-link";
type ReferencesItemProps = SectionItem<"references"> & {
@@ -15,7 +16,12 @@ export function ReferencesItem({ className, ...item }: ReferencesItemProps) {
<div className="section-item-header references-item-header">
{/* Row 1 */}
<div className="flex items-start justify-between gap-x-2">
<strong className="section-item-title references-item-name">{item.name}</strong>
<LinkedTitle
title={item.name}
website={item.website}
showLinkInTitle={item.options?.showLinkInTitle}
className="section-item-title references-item-name"
/>
</div>
{/* Row 2 */}
@@ -37,11 +43,13 @@ export function ReferencesItem({ className, ...item }: ReferencesItemProps) {
<span className="section-item-metadata references-item-phone inline-block">{item.phone}</span>
{/* Row 2 */}
<PageLink
{...item.website}
label={item.website.label}
className="section-item-website references-item-website"
/>
{!item.options?.showLinkInTitle && (
<PageLink
{...item.website}
label={item.website.label}
className="section-item-website references-item-website"
/>
)}
</div>
</div>
);
@@ -2,6 +2,7 @@ import { TiptapContent } from "@/components/input/rich-input";
import type { SectionItem } from "@/schema/resume/data";
import { stripHtml } from "@/utils/string";
import { cn } from "@/utils/style";
import { LinkedTitle } from "../linked-title";
import { PageLink } from "../page-link";
type VolunteerItemProps = SectionItem<"volunteer"> & {
@@ -15,7 +16,12 @@ export function VolunteerItem({ className, ...item }: VolunteerItemProps) {
<div className="section-item-header volunteer-item-header">
{/* Row 1 */}
<div className="flex items-start justify-between gap-x-2">
<strong className="section-item-title volunteer-item-title">{item.organization}</strong>
<LinkedTitle
title={item.organization}
website={item.website}
showLinkInTitle={item.options?.showLinkInTitle}
className="section-item-title volunteer-item-title"
/>
<span className="section-item-metadata volunteer-item-period shrink-0 text-end">{item.period}</span>
</div>
@@ -33,9 +39,11 @@ export function VolunteerItem({ className, ...item }: VolunteerItemProps) {
</div>
{/* Website */}
<div className="section-item-website volunteer-item-website">
<PageLink {...item.website} label={item.website.label} />
</div>
{!item.options?.showLinkInTitle && (
<div className="section-item-website volunteer-item-website">
<PageLink {...item.website} label={item.website.label} />
</div>
)}
</div>
);
}
@@ -0,0 +1,20 @@
import { cn } from "@/utils/style";
type LinkedTitleProps = {
title: string;
website?: { url: string; label: string };
showLinkInTitle?: boolean;
className?: string;
};
export function LinkedTitle({ title, website, showLinkInTitle, className }: LinkedTitleProps) {
if (showLinkInTitle && website?.url) {
return (
<a href={website.url} target="_blank" rel="noopener" className={cn("inline-block", className)}>
<strong>{title}</strong>
</a>
);
}
return <strong className={className}>{title}</strong>;
}
@@ -19,8 +19,8 @@ const sectionClassName = cn(
"group-data-[layout=sidebar]:[&_.section-item_i]:text-(--page-background-color)!",
// Level Display in Sidebar Layout
"group-data-[layout=sidebar]:[&_.section-item-level>div]:bg-(--page-background-color)",
"group-data-[layout=sidebar]:[&_.section-item-level>div]:text-(--page-background-color)",
"group-data-[layout=sidebar]:[&_.section-item-level>div]:border-(--page-background-color)",
"group-data-[layout=sidebar]:[&_.section-item-level>div]:data-[active=true]:bg-(--page-background-color)",
// Section Item Header in Sidebar Layout
"group-data-[layout=sidebar]:[&_.section-item-header>div]:flex-col",
@@ -38,16 +38,16 @@ export function ChikoritaTemplate({ pageIndex, pageLayout }: TemplateProps) {
<div className="template-chikorita page-content">
{/* Sidebar Background */}
{!fullWidth && (
<div className="page-sidebar-background absolute inset-y-0 z-0 w-(--page-sidebar-width) shrink-0 bg-(--page-primary-color) ltr:end-0 rtl:start-0" />
<div className="page-sidebar-background pointer-events-none absolute inset-y-0 z-0 w-(--page-sidebar-width) shrink-0 bg-(--page-primary-color) ltr:end-0 rtl:start-0" />
)}
{isFirstPage && <Header />}
<div className="flex">
<main
data-layout="main"
className="group page-main z-10 flex-1 space-y-4 px-(--page-margin-x) pt-(--page-margin-y)"
>
{isFirstPage && <Header />}
{main.map((section) => {
const Component = getSectionComponent(section, { sectionClassName });
return <Component key={section} id={section} />;
@@ -75,10 +75,10 @@ function Header() {
return (
<div className="page-header relative flex">
<div className="flex flex-1 items-center ps-(--page-margin-x) pt-(--page-margin-y)">
<div className="flex flex-1 items-center gap-x-(--page-margin-x)">
<PagePicture />
<div className="page-basics space-y-2 px-(--page-margin-x)">
<div className="page-basics space-y-2">
<div>
<h2 className="basics-name">{basics.name}</h2>
<p className="basics-headline">{basics.headline}</p>
@@ -122,8 +122,6 @@ function Header() {
</div>
</div>
</div>
<div className="w-(--page-sidebar-width) shrink-0" />
</div>
);
}
+1 -1
View File
@@ -37,7 +37,7 @@ export function DitgarTemplate({ pageIndex, pageLayout }: TemplateProps) {
<div className="template-ditgar page-content">
{/* Sidebar Background */}
{(!fullWidth || isFirstPage) && (
<div className="page-sidebar-background absolute inset-y-0 z-0 w-(--page-sidebar-width) shrink-0 bg-(--page-primary-color)/20 ltr:start-0 rtl:end-0" />
<div className="page-sidebar-background pointer-events-none absolute inset-y-0 z-0 w-(--page-sidebar-width) shrink-0 bg-(--page-primary-color)/20 ltr:start-0 rtl:end-0" />
)}
<div className="flex">
+1 -1
View File
@@ -27,7 +27,7 @@ export function GengarTemplate({ pageIndex, pageLayout }: TemplateProps) {
<div className="template-gengar page-content">
{/* Sidebar Background */}
{(!fullWidth || isFirstPage) && (
<div className="page-sidebar-background absolute inset-y-0 z-0 w-(--page-sidebar-width) shrink-0 bg-(--page-primary-color)/20 ltr:start-0 rtl:end-0" />
<div className="page-sidebar-background pointer-events-none absolute inset-y-0 z-0 w-(--page-sidebar-width) shrink-0 bg-(--page-primary-color)/20 ltr:start-0 rtl:end-0" />
)}
<div className="flex">
+3 -3
View File
@@ -27,14 +27,14 @@ export function GlalieTemplate({ pageIndex, pageLayout }: TemplateProps) {
<div className="template-glalie page-content">
{/* Sidebar Background */}
{(!fullWidth || isFirstPage) && (
<div className="page-sidebar-background absolute inset-y-0 z-0 w-(--page-sidebar-width) shrink-0 bg-(--page-primary-color)/20 ltr:start-0 rtl:end-0" />
<div className="page-sidebar-background pointer-events-none absolute inset-y-0 z-0 w-(--page-sidebar-width) shrink-0 bg-(--page-primary-color)/20 ltr:start-0 rtl:end-0" />
)}
<div className="flex">
{(!fullWidth || isFirstPage) && (
<aside
data-layout="sidebar"
className="group page-sidebar flex w-(--page-sidebar-width) shrink-0 flex-col space-y-4 px-(--page-margin-x) pt-(--page-margin-y)"
className="group page-sidebar z-10 flex w-(--page-sidebar-width) shrink-0 flex-col space-y-4 px-(--page-margin-x) pt-(--page-margin-y)"
>
{isFirstPage && <Header />}
@@ -49,7 +49,7 @@ export function GlalieTemplate({ pageIndex, pageLayout }: TemplateProps) {
</aside>
)}
<main data-layout="main" className="group page-main">
<main data-layout="main" className="group page-main z-10">
<div className="space-y-4 px-(--page-margin-x) pt-(--page-margin-y)">
{main.map((section) => {
const Component = getSectionComponent(section, { sectionClassName });
+24 -1
View File
@@ -10,6 +10,7 @@ import { Button } from "@/components/ui/button";
import { DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import type { DialogProps } from "@/dialogs/store";
import { useDialogStore } from "@/dialogs/store";
import { useFormBlocker } from "@/hooks/use-form-blocker";
@@ -29,6 +30,7 @@ export function CreateAwardDialog({ data }: DialogProps<"resume.sections.awards.
defaultValues: {
id: generateId(),
hidden: data?.item?.hidden ?? false,
options: data?.item?.options ?? { showLinkInTitle: false },
title: data?.item?.title ?? "",
awarder: data?.item?.awarder ?? "",
date: data?.item?.date ?? "",
@@ -89,6 +91,7 @@ export function UpdateAwardDialog({ data }: DialogProps<"resume.sections.awards.
defaultValues: {
id: data.item.id,
hidden: data.item.hidden,
options: data.item.options ?? { showLinkInTitle: false },
title: data.item.title,
awarder: data.item.awarder,
date: data.item.date,
@@ -205,13 +208,33 @@ function AwardForm() {
<Trans>Website</Trans>
</FormLabel>
<FormControl>
<URLInput {...field} value={field.value} onChange={field.onChange} />
<URLInput
{...field}
value={field.value}
onChange={field.onChange}
hideLabelButton={form.watch("options.showLinkInTitle")}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="options.showLinkInTitle"
render={({ field }) => (
<FormItem className="flex items-center gap-x-2">
<FormControl>
<Switch checked={field.value} onCheckedChange={field.onChange} />
</FormControl>
<FormLabel className="!mt-0">
<Trans>Show link in title</Trans>
</FormLabel>
</FormItem>
)}
/>
<FormField
control={form.control}
name="description"
+24 -1
View File
@@ -10,6 +10,7 @@ import { Button } from "@/components/ui/button";
import { DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import type { DialogProps } from "@/dialogs/store";
import { useDialogStore } from "@/dialogs/store";
import { useFormBlocker } from "@/hooks/use-form-blocker";
@@ -29,6 +30,7 @@ export function CreateCertificationDialog({ data }: DialogProps<"resume.sections
defaultValues: {
id: generateId(),
hidden: data?.item?.hidden ?? false,
options: data?.item?.options ?? { showLinkInTitle: false },
title: data?.item?.title ?? "",
issuer: data?.item?.issuer ?? "",
date: data?.item?.date ?? "",
@@ -89,6 +91,7 @@ export function UpdateCertificationDialog({ data }: DialogProps<"resume.sections
defaultValues: {
id: data.item.id,
hidden: data.item.hidden,
options: data.item.options ?? { showLinkInTitle: false },
title: data.item.title,
issuer: data.item.issuer,
date: data.item.date,
@@ -205,13 +208,33 @@ function CertificationForm() {
<Trans>Website</Trans>
</FormLabel>
<FormControl>
<URLInput {...field} value={field.value} onChange={field.onChange} />
<URLInput
{...field}
value={field.value}
onChange={field.onChange}
hideLabelButton={form.watch("options.showLinkInTitle")}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="options.showLinkInTitle"
render={({ field }) => (
<FormItem className="flex items-center gap-x-2">
<FormControl>
<Switch checked={field.value} onCheckedChange={field.onChange} />
</FormControl>
<FormLabel className="!mt-0">
<Trans>Show link in title</Trans>
</FormLabel>
</FormItem>
)}
/>
<FormField
control={form.control}
name="description"
+24 -1
View File
@@ -10,6 +10,7 @@ import { Button } from "@/components/ui/button";
import { DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import type { DialogProps } from "@/dialogs/store";
import { useDialogStore } from "@/dialogs/store";
import { useFormBlocker } from "@/hooks/use-form-blocker";
@@ -29,6 +30,7 @@ export function CreateEducationDialog({ data }: DialogProps<"resume.sections.edu
defaultValues: {
id: generateId(),
hidden: data?.item?.hidden ?? false,
options: data?.item?.options ?? { showLinkInTitle: false },
school: data?.item?.school ?? "",
degree: data?.item?.degree ?? "",
area: data?.item?.area ?? "",
@@ -92,6 +94,7 @@ export function UpdateEducationDialog({ data }: DialogProps<"resume.sections.edu
defaultValues: {
id: data.item.id,
hidden: data.item.hidden,
options: data.item.options ?? { showLinkInTitle: false },
school: data.item.school,
degree: data.item.degree,
area: data.item.area,
@@ -259,13 +262,33 @@ function EducationForm() {
<Trans>Website</Trans>
</FormLabel>
<FormControl>
<URLInput {...field} value={field.value} onChange={field.onChange} />
<URLInput
{...field}
value={field.value}
onChange={field.onChange}
hideLabelButton={form.watch("options.showLinkInTitle")}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="options.showLinkInTitle"
render={({ field }) => (
<FormItem className="flex items-center gap-x-2 sm:col-span-full">
<FormControl>
<Switch checked={field.value} onCheckedChange={field.onChange} />
</FormControl>
<FormLabel className="!mt-0">
<Trans>Show link in title</Trans>
</FormLabel>
</FormItem>
)}
/>
<FormField
control={form.control}
name="description"
+24 -1
View File
@@ -10,6 +10,7 @@ import { Button } from "@/components/ui/button";
import { DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import type { DialogProps } from "@/dialogs/store";
import { useDialogStore } from "@/dialogs/store";
import { useFormBlocker } from "@/hooks/use-form-blocker";
@@ -29,6 +30,7 @@ export function CreateExperienceDialog({ data }: DialogProps<"resume.sections.ex
defaultValues: {
id: generateId(),
hidden: data?.item?.hidden ?? false,
options: data?.item?.options ?? { showLinkInTitle: false },
company: data?.item?.company ?? "",
position: data?.item?.position ?? "",
location: data?.item?.location ?? "",
@@ -90,6 +92,7 @@ export function UpdateExperienceDialog({ data }: DialogProps<"resume.sections.ex
defaultValues: {
id: data.item.id,
hidden: data.item.hidden,
options: data.item.options ?? { showLinkInTitle: false },
company: data.item.company,
position: data.item.position,
location: data.item.location,
@@ -223,13 +226,33 @@ function ExperienceForm() {
<Trans>Website</Trans>
</FormLabel>
<FormControl>
<URLInput {...field} value={field.value} onChange={field.onChange} />
<URLInput
{...field}
value={field.value}
onChange={field.onChange}
hideLabelButton={form.watch("options.showLinkInTitle")}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="options.showLinkInTitle"
render={({ field }) => (
<FormItem className="flex items-center gap-x-2 sm:col-span-full">
<FormControl>
<Switch checked={field.value} onCheckedChange={field.onChange} />
</FormControl>
<FormLabel className="!mt-0">
<Trans>Show link in title</Trans>
</FormLabel>
</FormItem>
)}
/>
<FormField
control={form.control}
name="description"
+24 -1
View File
@@ -12,6 +12,7 @@ import { DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTit
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { InputGroup, InputGroupAddon, InputGroupInput, InputGroupText } from "@/components/ui/input-group";
import { Switch } from "@/components/ui/switch";
import { type DialogProps, useDialogStore } from "@/dialogs/store";
import { useFormBlocker } from "@/hooks/use-form-blocker";
import { profileItemSchema } from "@/schema/resume/data";
@@ -31,6 +32,7 @@ export function CreateProfileDialog({ data }: DialogProps<"resume.sections.profi
defaultValues: {
id: generateId(),
hidden: data?.item?.hidden ?? false,
options: data?.item?.options ?? { showLinkInTitle: false },
icon: data?.item?.icon ?? "acorn",
network: data?.item?.network ?? "",
username: data?.item?.username ?? "",
@@ -90,6 +92,7 @@ export function UpdateProfileDialog({ data }: DialogProps<"resume.sections.profi
defaultValues: {
id: data.item.id,
hidden: data.item.hidden,
options: data.item.options ?? { showLinkInTitle: false },
icon: data.item.icon,
network: data.item.network,
username: data.item.username,
@@ -216,12 +219,32 @@ function ProfileForm() {
<Trans>Website</Trans>
</FormLabel>
<FormControl>
<URLInput {...field} value={field.value} onChange={field.onChange} />
<URLInput
{...field}
value={field.value}
onChange={field.onChange}
hideLabelButton={form.watch("options.showLinkInTitle")}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="options.showLinkInTitle"
render={({ field }) => (
<FormItem className="flex items-center gap-x-2 sm:col-span-full">
<FormControl>
<Switch checked={field.value} onCheckedChange={field.onChange} />
</FormControl>
<FormLabel className="!mt-0">
<Trans>Show link in title</Trans>
</FormLabel>
</FormItem>
)}
/>
</>
);
}
+24 -1
View File
@@ -10,6 +10,7 @@ import { Button } from "@/components/ui/button";
import { DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import type { DialogProps } from "@/dialogs/store";
import { useDialogStore } from "@/dialogs/store";
import { useFormBlocker } from "@/hooks/use-form-blocker";
@@ -29,6 +30,7 @@ export function CreateProjectDialog({ data }: DialogProps<"resume.sections.proje
defaultValues: {
id: generateId(),
hidden: data?.item?.hidden ?? false,
options: data?.item?.options ?? { showLinkInTitle: false },
name: data?.item?.name ?? "",
period: data?.item?.period ?? "",
website: data?.item?.website ?? { url: "", label: "" },
@@ -88,6 +90,7 @@ export function UpdateProjectDialog({ data }: DialogProps<"resume.sections.proje
defaultValues: {
id: data.item.id,
hidden: data.item.hidden,
options: data.item.options ?? { showLinkInTitle: false },
name: data.item.name,
period: data.item.period,
website: data.item.website,
@@ -187,13 +190,33 @@ function ProjectForm() {
<Trans>Website</Trans>
</FormLabel>
<FormControl>
<URLInput {...field} value={field.value} onChange={field.onChange} />
<URLInput
{...field}
value={field.value}
onChange={field.onChange}
hideLabelButton={form.watch("options.showLinkInTitle")}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="options.showLinkInTitle"
render={({ field }) => (
<FormItem className="flex items-center gap-x-2 sm:col-span-full">
<FormControl>
<Switch checked={field.value} onCheckedChange={field.onChange} />
</FormControl>
<FormLabel className="!mt-0">
<Trans>Show link in title</Trans>
</FormLabel>
</FormItem>
)}
/>
<FormField
control={form.control}
name="description"
+24 -1
View File
@@ -10,6 +10,7 @@ import { Button } from "@/components/ui/button";
import { DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import type { DialogProps } from "@/dialogs/store";
import { useDialogStore } from "@/dialogs/store";
import { useFormBlocker } from "@/hooks/use-form-blocker";
@@ -29,6 +30,7 @@ export function CreatePublicationDialog({ data }: DialogProps<"resume.sections.p
defaultValues: {
id: generateId(),
hidden: data?.item?.hidden ?? false,
options: data?.item?.options ?? { showLinkInTitle: false },
title: data?.item?.title ?? "",
publisher: data?.item?.publisher ?? "",
date: data?.item?.date ?? "",
@@ -89,6 +91,7 @@ export function UpdatePublicationDialog({ data }: DialogProps<"resume.sections.p
defaultValues: {
id: data.item.id,
hidden: data.item.hidden,
options: data.item.options ?? { showLinkInTitle: false },
title: data.item.title,
publisher: data.item.publisher,
date: data.item.date,
@@ -205,13 +208,33 @@ function PublicationForm() {
<Trans>Website</Trans>
</FormLabel>
<FormControl>
<URLInput {...field} value={field.value} onChange={field.onChange} />
<URLInput
{...field}
value={field.value}
onChange={field.onChange}
hideLabelButton={form.watch("options.showLinkInTitle")}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="options.showLinkInTitle"
render={({ field }) => (
<FormItem className="flex items-center gap-x-2">
<FormControl>
<Switch checked={field.value} onCheckedChange={field.onChange} />
</FormControl>
<FormLabel className="!mt-0">
<Trans>Show link in title</Trans>
</FormLabel>
</FormItem>
)}
/>
<FormField
control={form.control}
name="description"
+24 -1
View File
@@ -10,6 +10,7 @@ import { Button } from "@/components/ui/button";
import { DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import type { DialogProps } from "@/dialogs/store";
import { useDialogStore } from "@/dialogs/store";
import { useFormBlocker } from "@/hooks/use-form-blocker";
@@ -29,6 +30,7 @@ export function CreateReferenceDialog({ data }: DialogProps<"resume.sections.ref
defaultValues: {
id: generateId(),
hidden: data?.item?.hidden ?? false,
options: data?.item?.options ?? { showLinkInTitle: false },
name: data?.item?.name ?? "",
position: data?.item?.position ?? "",
website: data?.item?.website ?? { url: "", label: "" },
@@ -89,6 +91,7 @@ export function UpdateReferenceDialog({ data }: DialogProps<"resume.sections.ref
defaultValues: {
id: data.item.id,
hidden: data.item.hidden,
options: data.item.options ?? { showLinkInTitle: false },
name: data.item.name,
position: data.item.position,
website: data.item.website,
@@ -205,13 +208,33 @@ function ReferenceForm() {
<Trans>Website</Trans>
</FormLabel>
<FormControl>
<URLInput {...field} value={field.value} onChange={field.onChange} />
<URLInput
{...field}
value={field.value}
onChange={field.onChange}
hideLabelButton={form.watch("options.showLinkInTitle")}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="options.showLinkInTitle"
render={({ field }) => (
<FormItem className="flex items-center gap-x-2">
<FormControl>
<Switch checked={field.value} onCheckedChange={field.onChange} />
</FormControl>
<FormLabel className="!mt-0">
<Trans>Show link in title</Trans>
</FormLabel>
</FormItem>
)}
/>
<FormField
control={form.control}
name="description"
+24 -1
View File
@@ -10,6 +10,7 @@ import { Button } from "@/components/ui/button";
import { DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import type { DialogProps } from "@/dialogs/store";
import { useDialogStore } from "@/dialogs/store";
import { useFormBlocker } from "@/hooks/use-form-blocker";
@@ -29,6 +30,7 @@ export function CreateVolunteerDialog({ data }: DialogProps<"resume.sections.vol
defaultValues: {
id: generateId(),
hidden: data?.item?.hidden ?? false,
options: data?.item?.options ?? { showLinkInTitle: false },
organization: data?.item?.organization ?? "",
location: data?.item?.location ?? "",
period: data?.item?.period ?? "",
@@ -89,6 +91,7 @@ export function UpdateVolunteerDialog({ data }: DialogProps<"resume.sections.vol
defaultValues: {
id: data.item.id,
hidden: data.item.hidden,
options: data.item.options ?? { showLinkInTitle: false },
organization: data.item.organization,
location: data.item.location,
period: data.item.period,
@@ -205,13 +208,33 @@ function VolunteerForm() {
<Trans>Website</Trans>
</FormLabel>
<FormControl>
<URLInput {...field} value={field.value} onChange={field.onChange} />
<URLInput
{...field}
value={field.value}
onChange={field.onChange}
hideLabelButton={form.watch("options.showLinkInTitle")}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="options.showLinkInTitle"
render={({ field }) => (
<FormItem className="flex items-center gap-x-2">
<FormControl>
<Switch checked={field.value} onCheckedChange={field.onChange} />
</FormControl>
<FormLabel className="!mt-0">
<Trans>Show link in title</Trans>
</FormLabel>
</FormItem>
)}
/>
<FormField
control={form.control}
name="description"
+7 -3
View File
@@ -143,8 +143,10 @@ const getAuthConfig = () => {
// biome-ignore lint/style/noNonNullAssertion: enabled check ensures these are not null
clientSecret: env.GOOGLE_CLIENT_SECRET!,
mapProfileToUser: async (profile) => {
const name = profile.name ?? profile.email.split("@")[0];
return {
name: profile.name,
name,
email: profile.email,
image: profile.picture,
username: profile.email.split("@")[0],
@@ -162,8 +164,10 @@ const getAuthConfig = () => {
// biome-ignore lint/style/noNonNullAssertion: enabled check ensures these are not null
clientSecret: env.GITHUB_CLIENT_SECRET!,
mapProfileToUser: async (profile) => {
const name = profile.name ?? profile.login ?? String(profile.id);
const login = profile.login ?? String(profile.id);
const normalizedLogin = toUsername(login);
const [legacyAccount] = await db
.select({
accountId: schema.account.accountId,
@@ -185,7 +189,7 @@ const getAuthConfig = () => {
if (legacyAccount) {
return {
id: legacyAccount.accountId,
name: profile.name,
name,
email: legacyAccount.email,
image: profile.avatar_url,
username: legacyAccount.username,
@@ -195,7 +199,7 @@ const getAuthConfig = () => {
}
return {
name: profile.name,
name,
email: profile.email,
image: profile.avatar_url,
username: normalizedLogin,
+1 -1
View File
@@ -12,5 +12,5 @@ export const getSession = createIsomorphicFn()
})
.server(async (): Promise<AuthSession | null> => {
const result = await auth.api.getSession({ headers: getRequestHeaders() });
return result;
return result as AuthSession | null;
});
+6 -1
View File
@@ -1,7 +1,12 @@
import z from "zod";
import type { auth } from "./config";
export type AuthSession = typeof auth.$Infer.Session;
export type AuthSession = {
session: typeof auth.$Infer.Session.session;
user: typeof auth.$Infer.Session.user & {
name: string;
};
};
const authProviderSchema = z.enum(["credential", "google", "github", "custom"]);
+2 -13
View File
@@ -1,4 +1,4 @@
import { createORPCClient, onError } from "@orpc/client";
import { createORPCClient } from "@orpc/client";
import { RPCLink } from "@orpc/client/fetch";
import { BatchLinkPlugin } from "@orpc/client/plugins";
import { createRouterClient, type InferRouterInputs, type InferRouterOutputs, type RouterClient } from "@orpc/server";
@@ -11,11 +11,6 @@ import { getLocale } from "@/utils/locale";
export const getORPCClient = createIsomorphicFn()
.server((): RouterClient<typeof router> => {
return createRouterClient(router, {
interceptors: [
onError((error) => {
console.error(error);
}),
],
context: async () => {
const locale = await getLocale();
const reqHeaders = getRequestHeaders();
@@ -30,16 +25,10 @@ export const getORPCClient = createIsomorphicFn()
.client((): RouterClient<typeof router> => {
const link = new RPCLink({
url: `${window.location.origin}/api/rpc`,
plugins: [new BatchLinkPlugin({ groups: [{ condition: () => true, context: {} }] })],
fetch: (request, init) => {
return fetch(request, { ...init, credentials: "include" });
},
interceptors: [
onError((error) => {
if (error instanceof DOMException) return;
console.error(error);
}),
],
plugins: [new BatchLinkPlugin({ groups: [{ condition: () => true, context: {} }] })],
});
return createORPCClient(link);
+103
View File
@@ -0,0 +1,103 @@
import { createSelectSchema } from "drizzle-zod";
import z from "zod";
import { schema } from "@/integrations/drizzle";
import { resumeDataSchema } from "@/schema/resume/data";
import { jsonPatchOperationSchema } from "@/utils/resume/patch";
const resumeSchema = createSelectSchema(schema.resume, {
id: z.string().describe("The ID of the resume."),
name: z.string().min(1).max(64).describe("The name of the resume."),
slug: z.string().min(1).max(64).describe("The slug of the resume."),
tags: z.array(z.string()).describe("The tags of the resume."),
isPublic: z.boolean().describe("Whether the resume is public."),
isLocked: z.boolean().describe("Whether the resume is locked."),
password: z.string().min(6).max(64).nullable().describe("The password of the resume, if any."),
data: resumeDataSchema,
userId: z.string().describe("The ID of the user who owns the resume."),
createdAt: z.date().describe("The date and time the resume was created."),
updatedAt: z.date().describe("The date and time the resume was last updated."),
});
export const resumeDto = {
list: {
input: z
.object({
tags: z.array(z.string()).optional().default([]),
sort: z.enum(["lastUpdatedAt", "createdAt", "name"]).optional().default("lastUpdatedAt"),
})
.optional()
.default({ tags: [], sort: "lastUpdatedAt" }),
output: z.array(resumeSchema.omit({ data: true, password: true, userId: true })),
},
getById: {
input: resumeSchema.pick({ id: true }),
output: resumeSchema
.omit({ password: true, userId: true, createdAt: true, updatedAt: true })
.extend({ hasPassword: z.boolean() }),
},
getBySlug: {
input: z.object({ username: z.string(), slug: z.string() }),
output: resumeSchema.omit({ password: true, userId: true, createdAt: true, updatedAt: true }),
},
create: {
input: resumeSchema
.pick({ name: true, slug: true, tags: true })
.extend({ withSampleData: z.boolean().default(false) }),
output: z.string().describe("The ID of the created resume."),
},
import: {
input: resumeSchema.pick({ data: true }),
output: z.string().describe("The ID of the imported resume."),
},
update: {
input: resumeSchema
.pick({ name: true, slug: true, tags: true, data: true, isPublic: true })
.partial()
.extend({ id: z.string() }),
output: resumeSchema.omit({ password: true, userId: true, createdAt: true, updatedAt: true }),
},
setLocked: {
input: resumeSchema.pick({ id: true, isLocked: true }),
output: z.void(),
},
setPassword: {
input: resumeSchema.pick({ id: true }).extend({ password: z.string().min(6).max(64) }),
output: z.void(),
},
removePassword: {
input: resumeSchema.pick({ id: true }),
output: z.void(),
},
patch: {
input: z.object({
id: z.string().describe("The ID of the resume to patch."),
operations: z
.array(jsonPatchOperationSchema)
.min(1)
.describe("An array of JSON Patch (RFC 6902) operations to apply to the resume data."),
}),
output: resumeSchema
.omit({ password: true, userId: true, createdAt: true, updatedAt: true })
.extend({ hasPassword: z.boolean() }),
},
duplicate: {
input: resumeSchema.pick({ id: true, name: true, slug: true, tags: true }),
output: z.string().describe("The ID of the duplicated resume."),
},
delete: {
input: resumeSchema.pick({ id: true }),
output: z.void(),
},
};

Some files were not shown because too many files have changed in this diff Show More