440 Commits

Author SHA1 Message Date
Sina Atalay
1d4b87bc42 Fix date validation test to work across Python 3.12-3.14
Python 3.14 changed ValueError messages for invalid dates.
Use broader match patterns that work across all versions.
2026-03-25 16:45:27 +03:00
Sina Atalay
b643d456e7 Fix flaky Hypothesis test for case-sensitive keyword matching
Some Unicode uppercase letters (e.g., mathematical bold 𝐀) have no
distinct lowercase form, so keyword.lower() == keyword. Filter these
out since the test requires a genuine case difference.
2026-03-25 16:38:48 +03:00
Sina Atalay
6956f39835 Address code review feedback on Hypothesis and classic_theme changes
- Update hypothesis to latest version (>=6.151.9)
- Remove pythonpath pytest config (was only needed for tests.strategies)
- Consolidate classic_theme.py into single file with all design models
- Move Hypothesis strategies from strategies.py into their test files
- Add noqa: ARG001 to unused yaml_field_override CLI parameter
- Fix lint and type errors across the codebase
2026-03-25 16:28:44 +03:00
Sina Atalay
2df9d2262b Fix placeholder removal eating provided placeholders with overlapping names
The regex in remove_not_provided_placeholders used a bare alternation
without word boundaries, so removing placeholder "AA" would also
destroy "AAA" in the same template. Fix: add \b word boundaries and
sort longest-first.

Also add Hypothesis tests for:
- remove_not_provided_placeholders: provided keys survive removal
- remove_connectors_of_missing_placeholders: connectors removed/preserved
- validate_arbitrary_date: pass-through for valid dates and custom text
- Mastodon URL: domain and username appear in generated URL
2026-03-25 16:28:44 +03:00
Sina Atalay
141ea9fe92 Fix bold_keywords not matching keywords with non-word boundaries (#706)
The \b word boundary anchor requires a word character (\w) on one side
and a non-word character on the other. Keywords ending with ":" or
starting with "+" have no word boundary at those positions, so \b
fails to match.

Fix: only add \b on the side of a keyword where the character is a
word character. For "Tech stack:", \b is added before "T" but not
after ":". For "C++", \b is added before "C" but not after "+".

Added a Hypothesis property test that catches this entire class of
bugs: any keyword surrounded by spaces in the input text must appear
bolded in the output.
2026-03-25 16:28:44 +03:00
Sina Atalay
005d1d835f Remove useless Hypothesis tests that fuzz trivial operations
Removed tests that were running 100 random inputs through string
operations that can never fail regardless of input:
- process_highlights: fuzzing "- " + string prepend
- process_authors: fuzzing ", ".join()
- PublicationEntry DOI URL: fuzzing f"https://doi.org/{doi}"

These tests add execution time without any chance of finding bugs.
2026-03-25 16:28:20 +03:00
Sina Atalay
1704a4be57 Add Hypothesis test for PublicationEntry DOI URL generation
Test that random valid DOI strings always produce the correct
https://doi.org/ URL. Removed the useless test_doi_clears_url that
was just re-verifying a static validator with random irrelevant input.
2026-03-25 16:28:20 +03:00
Sina Atalay
aa4c47e3a1 Move Hypothesis TypstDimension tests to test_typst_dimension.py
They were in the wrong file (test_classic_theme.py). Now they live
alongside the existing parametrized dimension tests.
2026-03-25 16:28:20 +03:00
Sina Atalay
9bf120f07b Add Hypothesis test for bold_keywords deduplication order preservation 2026-03-25 16:28:20 +03:00
Sina Atalay
34d4055aef Add Hypothesis tests for dictionary_key_to_proper_section_title
- snake_case keys always replace underscores with spaces
- keys with spaces returned unchanged
- keys with uppercase letters returned unchanged
2026-03-25 16:28:20 +03:00
Sina Atalay
3728cd5e33 Add Hypothesis tests for BaseEntryWithComplexFields date adjustment logic
- date_only_clears_start_and_end: providing date nullifies start/end
- start_only_implies_present: start without end defaults to "present"
- end_only_becomes_date: end without start treated as single date
2026-03-25 16:28:20 +03:00
Sina Atalay
427053339b Add Hypothesis tests for process_highlights, process_authors, clean_trailing_parts
- process_highlights: bullet count matches input length, output starts with "- "
- process_authors: comma count is n-1, all authors appear in output
- clean_trailing_parts: allowed trailing chars preserved, never crashes
2026-03-25 16:28:20 +03:00
Sina Atalay
0f7f6f4f97 Follow test naming conventions for Hypothesis tests
Merge property-based tests into existing test classes per the project
testing rules: multiple tests for the same function belong in a
TestFunctionName class.

- TestEscapeTypstCharactersProperties merged into TestEscapeTypstCharacters
- TestMarkdownToTypstProperties merged into TestMarkdownToTypst
- TestBuildDatePlaceholdersProperties merged into TestBuildDatePlaceholders
- TestComputeTimeSpanStringProperties merged into TestComputeTimeSpanString
- TestGetDateObjectProperties renamed to TestGetDateObject
- TestApplyOverridesProperties merged into TestApplyOverridesToDictionary
- TestUpdateValueByLocationProperties merged into TestUpdateValueByLocation
- TestResolveOutputFolderPlaceholderProperties merged into TestResolveOutputFolderPlaceholder
- TestBuildNameVariantsProperties renamed to TestBuildNameVariants
- TestTypstDimensionProperties renamed to TestTypstDimension
- TestSocialNetworkUsernameProperties merged into TestSocialNetwork
2026-03-25 16:28:20 +03:00
Sina Atalay
5e95eee87d Distribute Hypothesis tests into their respective unit test files
Move all 55 property-based tests from tests/test_hypothesis.py into
the existing test files for each module they test:

- test_string_processor.py: keyword bolding, placeholder, URL, pattern
- test_markdown_parser.py: Typst escaping, markdown-to-typst
- test_date.py: date parsing, placeholders, time spans
- test_override_dictionary.py: immutability, path traversal
- test_path_resolver.py: name variants, OUTPUT_FOLDER resolution
- test_classic_theme.py: Typst dimension validation
- test_social_network.py: username format validation

Reusable Hypothesis strategies live in tests/strategies.py. Added
pythonpath=["."] to pyproject.toml so tests can import the strategies
module.
2026-03-25 16:28:20 +03:00
Sina Atalay
7034a5e70b Fix 3 bugs found by Hypothesis property-based testing
1. clean_url: use rstrip("/") instead of single-slash strip. URLs
   with multiple trailing slashes (e.g., "example.com//") now clean
   fully. Also restores idempotency (clean(clean(x)) == clean(x)).

2. get_date_object: use Date(year, 1, 1) instead of
   Date.fromisoformat(f"{date}-01-01"). The fromisoformat call
   crashed on years < 1000 because it requires 4-digit year strings.

3. build_date_placeholders: use f"{year % 100:02d}" for
   YEAR_IN_TWO_DIGITS instead of str(year)[-2:]. The slice produced
   1-char output for single-digit years (e.g., year 9 gave "9"
   instead of "09").

Test ranges widened back to full datetime.date range now that the
source code handles all values correctly.
2026-03-25 16:28:20 +03:00
Sina Atalay
5fb507d388 Add Hypothesis property-based testing suite (55 tests)
Property-based tests verify invariants across random inputs, catching
edge cases that hand-picked parametrized tests miss. Covers 6 modules:

- string_processor: keyword bolding, placeholder substitution, URL cleaning
- markdown_parser: Typst escaping robustness, formatting preservation
- date: date parsing, placeholder generation, time span arithmetic
- override_dictionary: immutability, path traversal, error conditions
- path_resolver: name variant generation, OUTPUT_FOLDER resolution
- Pydantic validators: Typst dimensions, social network usernames

Bugs surfaced during development:
- clean_url only strips one trailing slash (double slash passes through)
- get_date_object crashes on year < 10 (single digit isoformat)
- YEAR_IN_TWO_DIGITS is 1 char for years < 10
2026-03-25 16:28:20 +03:00
Sina Atalay
43b255eff7 Split classic_theme.py into focused modules
Extract three modules from classic_theme.py (845 lines) to improve
navigability:

- templates.py: entry template option models and Templates class
- typography.py: Typography, FontFamily config, FontSize, SmallCaps, Bold
- header.py: Header, Connections, Links, PhoneNumberFormatType

classic_theme.py retains Page, Colors, Sections, Entries, and
ClassicTheme. All imports use absolute paths so the custom theme
generator (which copies classic_theme.py as text) continues to work.

Template entry option classes renamed from e.g. OneLineEntry to
OneLineEntryTemplate to avoid name collision with CV entry models.
2026-03-25 16:28:20 +03:00
Sina Atalay
db6ad98654 Fix test failures from code quality changes
- Revert entry_with_date.py to let ValueError propagate naturally
  (Pydantic produces better messages like "The month must be between
  1 and 12." than our generic wrapper)
- Update create_theme tests to expect typer.Exit (now has error handler)
- Update pdf_png test to use real temp file instead of patching read_text
- Fix watcher test mock to handle double join() call pattern
- Regenerate JSON schema after union type order change
2026-03-25 16:28:20 +03:00
Sina Atalay
ec884975d4 Fix lint issues from previous changes
- Use Path.open() instead of open() in pdf_png.py (PTH123)
- Chain KeyError with 'from e' in pdf_png.py (B904)
- Fix comment placement in section.py to avoid false unused-ignore
- Update watcher test to mock observer.join() instead of time.sleep()
2026-03-25 16:28:20 +03:00
Sina Atalay
1ffb4c4890 Add flag_emoji property to locale model
Add a cached_property that maps each language to its primary country's
flag emoji using regional indicator symbols, with tests and updated
developer guide.
2026-03-23 03:20:53 +03:00
Sina Atalay
9387166637 Fix offline wheel test Unicode crash on Windows
The rich progress panel outputs ✓ (U+2713), which Windows cp1252
console encoding cannot encode. Set PYTHONIOENCODING=utf-8 in the
subprocess environment.
2026-03-21 15:15:16 +03:00
Sina Atalay
56c30dc605 Show stdout/stderr in offline wheel test failures for better diagnostics 2026-03-21 15:11:28 +03:00
Sina Atalay
617dcf779f Fix offline wheel test failing on Windows due to hardcoded Unix venv path 2026-03-21 14:54:44 +03:00
Sina Atalay
dd5ac9115a Fix test_raises_error_when_version_missing_from_typst_toml match pattern 2026-03-21 03:56:52 +03:00
Sina Atalay
c917f3a05b Add typst-fontawesome as bundled Typst package for offline icon support
Bundle the typst-fontawesome package as a git submodule so that Font
Awesome icons are available during Typst compilation without network
access. Refactor get_package_path to install multiple bundled packages
into the temporary cache. Add an offline wheel installation test that
renders a PDF with a poisoned HTTP proxy to verify no network calls.
2026-03-21 03:54:59 +03:00
Sikai Zhang
3b6f20ff95 Fix custom fonts folder not found when using relative input path (#692) 2026-03-21 02:40:50 +03:00
Sina Atalay
da94718587 Update dev tooling and dependencies to latest versions
Bump black (26.3.1), ruff (0.15.7), ty (0.0.24), prek (0.3.6), typer
(0.24.1), and codespell (v2.4.2). Add ty:ignore comments and type
annotations to satisfy stricter checks in ty 0.0.24. Make skill zip
generation reproducible with a fixed timestamp.
2026-03-21 01:20:17 +03:00
Sina Atalay
70d9efcb66 Fix #595: Auto-create intermediate dicts for CLI overrides on missing keys
When using CLI overrides like `--design.theme "moderncv"` with a separate
design file, `update_value_by_location` crashed with `KeyError: 'design'`
because intermediate dictionary keys were not auto-created during traversal.
Now missing intermediate keys are initialized as empty dicts, allowing
overrides to work regardless of whether the target section exists in the
main YAML.
2026-03-21 01:03:32 +03:00
Sina Atalay
d9864d9495 Revert "Fix #689: Allow arbitrary font names in JSON schema while keeping enum suggestions"
This reverts commit 9ebcebd3ccc6abee3c71c07c884488614044b958.
2026-03-21 01:03:32 +03:00
Sina Atalay
295ae6185b Fix #653: Allow SUMMARY placeholder to be used inline in templates
process_summary() unconditionally wrapped summary text in Markdown admonition
syntax (!!! summary), which only works as a block-level construct on its own
line. When users placed SUMMARY inline in a template (e.g., "**COMPANY**,
SUMMARY"), the admonition syntax broke rendering.

Now render_entry_templates() checks if SUMMARY appears on its own line in
the template. If standalone, admonition wrapping is applied for the special
#summary[] Typst formatting. If inline, the raw summary text is used directly.
2026-03-21 01:03:32 +03:00
Sina Atalay
d5c1e724fa Fix #690: Load design/locale overlay files from YAML settings.render_command
When users specified design or locale overlay file paths in their YAML via
settings.render_command.design or settings.render_command.locale, these were
parsed but never actually loaded as overlays. Only CLI flags (--design,
--locale) triggered overlay loading.

Now cli_command_render resolves these paths from the YAML (via the existing
collect_input_file_paths helper) before building arguments, so overlay files
referenced in settings are loaded the same way as CLI-provided ones.
2026-03-21 01:03:32 +03:00
Sina Atalay
847e4f9ea3 Fix #689: Allow arbitrary font names in JSON schema while keeping enum suggestions
The FontFamily JSON schema used a strict enum that rejected custom and system
fonts like Georgia. Changed from SkipJsonSchema[str] to plain str in the union
type, so the generated schema uses anyOf with both the known font enum (for
editor autocomplete) and a generic string type (for arbitrary fonts).
2026-03-21 01:03:32 +03:00
Sina Atalay
21e3cd7f33 Fix #673: Make copied template files writable for immutable distros
On NixOS and other immutable distributions, package files in the store are
read-only. shutil.copytree preserves source permissions, so copied template
files remain read-only. This causes PermissionError when create-theme tries
to write __init__.py into the copied directory.

Added make_tree_writable() that adds user-write permission to all files and
directories after copying. Called automatically by copy_templates().
2026-03-21 01:03:32 +03:00
Sina Atalay
e80ecfc559 Fix #685: Process markdown lines independently to prevent cross-line emphasis interference
When multi-line template fields (like main_column) contained markdown emphasis
markers on adjacent lines, the Python markdown library treated single-newline-
separated lines as one paragraph, causing markers to interact across lines and
produce garbled Typst output with mismatched brackets.

The fix processes each line of input independently in markdown_to_typst() while
preserving multi-line admonition blocks. This prevents emphasis markers on one
line from interfering with markers on adjacent lines.
2026-03-21 01:03:32 +03:00
Noah Croghan
fb9b0e9295 Fix EducationEntry location placement in markdown reference file (#691)
* Fix EducationEntry location placement

* Fix EducationEntry location placement in markdown reference file

---------

Co-authored-by: simonafield <simon.techkid@outlook.com>
2026-03-21 00:26:30 +03:00
Sina Atalay
9bc8c2862c Add Ink, Opal, and Ember themes with examples, docs, and test data
Ink: elegant academic theme with EB Garamond and purple palette.
Opal: modern minimalist with Lato and teal accents.
Ember: editorial theme with mixed Gentium Book Plus/Ubuntu fonts,
crimson accents, diamond bullets, and justified-with-no-hyphenation.

Updates README theme grid from 2x3 to 3x3 to showcase all 9 themes.
2026-03-20 21:25:38 +03:00
Sina Atalay
9b83f07ac9 Move distributable skill to rendercv/rendercv-skill submodule
The skill file lived in the main repo, which meant `npx skills add`
had to clone the entire RenderCV codebase and exposed internal dev
skills. A dedicated lightweight repo solves both problems.

- Create rendercv/rendercv-skill repo as a read-only distribution channel
- Add it as a submodule at .claude/skills/rendercv-skill/
- Update generation script and tests to write to the submodule path
- Add submodules: true to test workflow checkout steps
- Update docs, README, and install commands to use rendercv/rendercv-skill
- Add --recursive clone instruction to developer guide
- Delete the old skills/ directory at repo root
2026-03-20 20:41:53 +03:00
Sina Atalay
021c9fbde3 Add RenderCV skill with eval framework and auto-generated docs/llms.txt
- Add SKILL.md for AI agents: YAML schema reference, entry type field
  tables, design samples per theme, CLI reference, and important patterns
  (YAML quoting, phone validation, bullet characters)
- Add Jinja2 template and generate.py that auto-generates SKILL.md and
  docs/llms.txt from live Pydantic models and sample generators
- Add promptfoo eval suite (15 tests across 4 files): cv_generation,
  pdf_parsing, design, and cli_workflow
- Add deterministic grader that validates LLM output through RenderCV's
  own pydantic pipeline (not jsonschema)
2026-03-20 20:15:09 +03:00
Sina Atalay
bc87090b96 Add tests for uncovered edge cases in pdf_png and sample_generator 2026-03-20 18:30:02 +03:00
Sina Atalay
009eec5dfb Bundle rendercv-typst inside the Python package to decouple from Typst Universe
Move `rendercv-typst/` into `src/rendercv/renderer/rendercv_typst/` so that
`lib.typ` and `typst.toml` ship with every pip install. This eliminates the
runtime dependency on Typst Universe, allowing releases without waiting for
external PR reviews. The wheel excludes non-essential files (examples, template,
README, etc.) via `[tool.uv.build-backend]` wheel-exclude.
2026-03-20 18:18:04 +03:00
Sina Atalay
5927cf0d00 Update Harvard theme 2026-03-20 16:52:35 +03:00
Sina Atalay
4e07fa2380 Add centered section title types and bump rendercv-typst to 0.3.0
Add four new section title styles: `centered_without_line`,
`centered_with_partial_line` (baseline), `centered_with_centered_partial_line`
(middle-aligned), and `centered_with_full_line`. These allow section headings
to be centered with various line decorations, complementing the existing
left-aligned options.
2026-03-20 05:20:10 +03:00
Sina Atalay
9b7830a0e1 Create new theme: harvard 2026-03-19 23:03:18 +03:00
Sina Atalay
0c45adf776 Fix formatting and linting issues 2026-03-19 22:28:33 +03:00
Sina Atalay
7138cee196 Fix orphaned connector words when placeholders are removed from templates
When a placeholder like DEGREE was missing from an education entry, the
locale phrase "DEGREE in AREA" left the connector word "in" behind,
producing output like "Princeton University, in Computer Science". This
affected all locales with connector words (en, em, di, at, etc.).

The fix adds a general-purpose `remove_connectors_of_missing_placeholders`
step inside `remove_not_provided_placeholders` that strips bare connector
words from separators between placeholders when either side is missing.
This works for any template, not just locale phrases — for example,
"JOB_TITLE at COMPANY" now correctly drops "at" when COMPANY is absent.
2026-03-19 22:17:00 +03:00
Sina Atalay
4c617170e8 Clean up characters around empty string inputs 2026-03-04 04:21:34 +03:00
Sina Atalay
da2e657416 Increase test coverage 2026-03-03 16:39:23 +03:00
Sina Atalay
9e773d64d0 Only catch full words in settings.bold_keywords 2026-03-03 01:02:57 +03:00
Sina Atalay
eb3cc3406c Remove ex unit from TypstDimension 2026-03-02 19:08:26 +03:00
Sina Atalay
1bd0277edb Allow empty sections 2026-03-02 18:36:52 +03:00