From b1db1bb31fd4707b43a4df45e10faa459a42e8a0 Mon Sep 17 00:00:00 2001 From: Sina Atalay <79940989+sinaatalay@users.noreply.github.com> Date: Thu, 19 Feb 2026 14:53:15 +0300 Subject: [PATCH] Remove llms.txt and add more sample generators --- docs/llms.txt | 955 --------------------- scripts/update_llms_txt/template.j2.md | 360 -------- scripts/update_llms_txt/update_llms_txt.py | 508 ----------- src/rendercv/schema/sample_generator.py | 240 +++++- tests/schema/test_sample_generator.py | 93 ++ 5 files changed, 316 insertions(+), 1840 deletions(-) delete mode 100644 docs/llms.txt delete mode 100644 scripts/update_llms_txt/template.j2.md delete mode 100644 scripts/update_llms_txt/update_llms_txt.py diff --git a/docs/llms.txt b/docs/llms.txt deleted file mode 100644 index 22d615e8..00000000 --- a/docs/llms.txt +++ /dev/null @@ -1,955 +0,0 @@ -# RenderCV YAML Input Reference - -RenderCV generates professional CVs/resumes from YAML input files. Write your CV as YAML, run `rendercv render your_cv.yaml`, and get a PDF with perfect typography. - -## YAML Structure Overview - -A RenderCV YAML file has four top-level fields. Only `cv` is required. - -```yaml -cv: # Required. Your CV content (personal info and sections). - ... -design: # Optional. Visual styling (theme, colors, fonts, spacing). - ... -locale: # Optional. Language and date formatting. - ... -settings: # Optional. Output paths, generation flags, bold keywords. - ... -``` - -## `cv` Field - -### Personal Information (Header) - -All header fields are optional. RenderCV adapts to whatever you provide. - -```yaml -cv: - name: John Doe - headline: Machine Learning Engineer - location: San Francisco, CA - email: john@example.com - phone: "+14155551234" - website: https://johndoe.dev - photo: photo.jpg - social_networks: - - network: LinkedIn - username: johndoe - - network: GitHub - username: johndoe - custom_connections: - - placeholder: Book a call - url: https://cal.com/johndoe - fontawesome_icon: calendar-days -``` - -**Header fields:** - -- `name`: string. Examples: "John Doe", "Jane Smith". -- `headline`: string. Examples: "Software Engineer", "Data Scientist", "Product Manager". -- `location`: string. Examples: "New York, NY", "London, UK", "Istanbul, T\u00fcrkiye". -- `email`: string. You can provide multiple emails as a list. Examples: "john.doe@example.com", ["john.doe.1@example.com", "john.doe.2@example.com"]. -- `photo`: file path. Photo file path, relative to the YAML file. Examples: "photo.jpg", "images/profile.png". -- `phone`: string. Your phone number with country code in international format (e.g., +1 for USA, +44 for UK). The display format in the output is controlled by `design.header.connections.phone_number_format`. You can provide multiple numbers as a list. Examples: "+1-234-567-8900", ["+1-234-567-8900", "+44 20 1234 5678"]. -- `website`: string. You can provide multiple URLs as a list. Examples: "https://johndoe.com", ["https://johndoe.com", "https://www.janesmith.dev"]. -- `social_networks`: list of SocialNetwork. -- `custom_connections`: list of CustomConnection. Additional header connections you define yourself. Each item has a `placeholder` (the displayed text), an optional `url`, and the Font Awesome icon name to render (from https://fontawesome.com/search). Examples: [{"fontawesome_icon": "calendar-days", "placeholder": "Book a call", "url": "https://cal.com/johndoe"}]. - -**Available social networks:** LinkedIn, GitHub, GitLab, IMDB, Instagram, ORCID, Mastodon, StackOverflow, ResearchGate, YouTube, Google Scholar, Telegram, WhatsApp, Leetcode, X, Bluesky, Reddit. - -**`email`, `phone`, and `website` accept a single value or a list** for multiple entries. - -### Sections - -The `sections` field holds the main content of your CV. It is a dictionary where keys are section titles (displayed as headings) and values are lists of entries. - -```yaml -cv: - sections: - experience: # Section title (can be anything) - - company: Acme Corp - position: Senior Engineer - start_date: 2020-01 - end_date: present - highlights: - - Led migration to microservices architecture - skills: - - label: Languages - details: Python, Go, Rust -``` - -- Section titles are arbitrary strings. Use any name you want. -- Each section must contain only one type of entry. You cannot mix different entry types in the same section. -- Entry types are auto-detected based on the fields you provide. - -### Entry Types - -RenderCV provides 9 entry types: - -#### TextEntry - -Plain text without structure. Just write a string. - -```yaml -sections: - summary: - - Software engineer with 10 years of experience in distributed systems. - - See the [documentation](https://docs.rendercv.com) for more details. -``` - -#### OneLineEntry - -For compact key-value pairs, ideal for skills or technical proficiencies. - -| Field | Required | Type | Description | -| ----- | -------- | ---- | ----------- | -| `label` | Yes | string | | -| `details` | Yes | string | | - -```yaml -- label: Languages - details: Python, C++, CUDA, Rust, Julia -``` - -#### NormalEntry - -A flexible entry for projects, awards, certifications, or anything else. - -| Field | Required | Type | Description | -| ----- | -------- | ---- | ----------- | -| `name` | Yes | string | | -| `date` | No | date (YYYY-MM-DD, YYYY-MM, YYYY, or custom text) | The date of this event in YYYY-MM-DD, YYYY-MM, or YYYY format, or any custom text like 'Fall 2023'. Use this for single-day or imprecise dates. For date ranges, use `start_date` and `end_date` instead | -| `start_date` | No | date (YYYY-MM-DD, YYYY-MM, YYYY, or custom text) | The start date in YYYY-MM-DD, YYYY-MM, or YYYY format | -| `end_date` | No | date (YYYY-MM-DD, YYYY-MM, YYYY, or custom text) or "present" | The end date in YYYY-MM-DD, YYYY-MM, or YYYY format. Use "present" for ongoing events, or omit it to indicate the event is ongoing | -| `location` | No | string | | -| `summary` | No | string | | -| `highlights` | No | list of strings | Bullet points for key achievements, responsibilities, or contributions | - -```yaml -- name: "[FlashInfer](https://github.com/)" - start_date: 2023-01 - end_date: present - summary: Open-source library for high-performance LLM inference - highlights: - - Achieved 2.8x speedup over baseline attention implementations - - 8,500+ GitHub stars, 200+ contributors -``` - -#### ExperienceEntry - -For work history and professional roles. - -| Field | Required | Type | Description | -| ----- | -------- | ---- | ----------- | -| `company` | Yes | string | | -| `position` | Yes | string | | -| `date` | No | date (YYYY-MM-DD, YYYY-MM, YYYY, or custom text) | The date of this event in YYYY-MM-DD, YYYY-MM, or YYYY format, or any custom text like 'Fall 2023'. Use this for single-day or imprecise dates. For date ranges, use `start_date` and `end_date` instead | -| `start_date` | No | date (YYYY-MM-DD, YYYY-MM, YYYY, or custom text) | The start date in YYYY-MM-DD, YYYY-MM, or YYYY format | -| `end_date` | No | date (YYYY-MM-DD, YYYY-MM, YYYY, or custom text) or "present" | The end date in YYYY-MM-DD, YYYY-MM, or YYYY format. Use "present" for ongoing events, or omit it to indicate the event is ongoing | -| `location` | No | string | | -| `summary` | No | string | | -| `highlights` | No | list of strings | Bullet points for key achievements, responsibilities, or contributions | - -```yaml -- company: Nexus AI - position: Co-Founder & CTO - start_date: 2023-06 - end_date: present - location: San Francisco, CA - summary: Building foundation model infrastructure - highlights: - - Raised $18M Series A led by Sequoia Capital - - Scaled engineering team from 3 to 28 -``` - -#### EducationEntry - -For academic credentials. - -| Field | Required | Type | Description | -| ----- | -------- | ---- | ----------- | -| `institution` | Yes | string | | -| `area` | Yes | string | Field of study or major | -| `degree` | No | string | | -| `date` | No | date (YYYY-MM-DD, YYYY-MM, YYYY, or custom text) | The date of this event in YYYY-MM-DD, YYYY-MM, or YYYY format, or any custom text like 'Fall 2023'. Use this for single-day or imprecise dates. For date ranges, use `start_date` and `end_date` instead | -| `start_date` | No | date (YYYY-MM-DD, YYYY-MM, YYYY, or custom text) | The start date in YYYY-MM-DD, YYYY-MM, or YYYY format | -| `end_date` | No | date (YYYY-MM-DD, YYYY-MM, YYYY, or custom text) or "present" | The end date in YYYY-MM-DD, YYYY-MM, or YYYY format. Use "present" for ongoing events, or omit it to indicate the event is ongoing | -| `location` | No | string | | -| `summary` | No | string | | -| `highlights` | No | list of strings | Bullet points for key achievements, responsibilities, or contributions | - -```yaml -- institution: Princeton University - area: Computer Science - degree: PhD - start_date: 2018-09 - end_date: 2023-05 - location: Princeton, NJ - highlights: - - "Thesis: Efficient Neural Architecture Search" - - NSF Graduate Research Fellowship -``` - -#### PublicationEntry - -For papers, articles, and other publications. - -| Field | Required | Type | Description | -| ----- | -------- | ---- | ----------- | -| `title` | Yes | string | | -| `authors` | Yes | list of strings | You can bold your name with **double asterisks** | -| `summary` | No | string | | -| `doi` | No | string | The DOI (Digital Object Identifier). If provided, it will be used as the link instead of the URL | -| `url` | No | URL | A URL link to the publication. Ignored if DOI is provided | -| `journal` | No | string | The journal, conference, or venue where it was published | -| `date` | No | date (YYYY-MM-DD, YYYY-MM, YYYY, or custom text) | The date of this event in YYYY-MM-DD, YYYY-MM, or YYYY format, or any custom text like 'Fall 2023'. Use this for single-day or imprecise dates. For date ranges, use `start_date` and `end_date` instead | - -```yaml -- title: "Sparse Mixture-of-Experts at Scale" - authors: - - "*John Doe*" - - Sarah Williams - doi: 10.1234/neurips.2023.1234 - journal: NeurIPS 2023 - date: 2023-07 -``` - -#### BulletEntry - -A single bullet point. Use for simple lists. - -| Field | Required | Type | Description | -| ----- | -------- | ---- | ----------- | -| `bullet` | Yes | string | | - -```yaml -- bullet: MIT Technology Review 35 Under 35 Innovators (2024) -``` - -#### NumberedEntry - -An automatically numbered entry. - -| Field | Required | Type | Description | -| ----- | -------- | ---- | ----------- | -| `number` | Yes | string | | - -```yaml -- number: "Adaptive Quantization for Neural Network Inference (US Patent 11,234,567)" -``` - -#### ReversedNumberedEntry - -A numbered entry that counts down (useful for publication lists where recent items come first). - -| Field | Required | Type | Description | -| ----- | -------- | ---- | ----------- | -| `reversed_number` | Yes | string | Reverse-numbered list item. Numbering goes in reverse (5, 4, 3, 2, 1), making recent items have higher numbers | - -```yaml -- reversed_number: "Scaling Laws for Efficient Inference — Stanford HAI Symposium (2024)" -``` - - -### Date Formats - -Fields `date`, `start_date`, and `end_date` accept: -- Full date: `2024-01-15` (YYYY-MM-DD) -- Month precision: `2024-01` (YYYY-MM) -- Year only: `2024` (YYYY) -- Custom text: `"Fall 2023"`, `"Summer 2020"` (for `date` field only) -- Ongoing: `present` (for `end_date` only) - -### Text Formatting - -All text fields support basic Markdown: -- `**text**` renders as bold -- `*text*` renders as italic -- `[text](url)` renders as a hyperlink -- `` `code` `` renders as inline code - -All text fields also support Typst syntax: -- `$$f(x) = x^2$$` renders as math -- `#emph[text]` and other Typst commands work directly - -### Arbitrary Keys - -You can add custom fields to any entry. By default they are ignored, but you can reference them in `design.templates` as UPPERCASE placeholders. - -```yaml -experience: - - company: Startup Inc - position: Founder - start_date: 2020-01 - end_date: present - revenue: $5M ARR # Custom field, available as REVENUE in templates -``` - -## `design` Field - -### Available Themes - -Available themes: classic, engineeringclassic, engineeringresumes, moderncv, sb2nov. - -All themes share the same design options but have different default values. If you specify a setting explicitly, it overrides the theme's default. - -```yaml -design: - theme: classic # Choose a theme, then override any option below -``` - -### Design Options - -Below are all available design options. All are optional; themes provide defaults. - -#### `design.page` - -- `size`: one of: "a4", "a5", "us-letter", "us-executive". The page size. Use 'a4' (international standard) or 'us-letter' (US standard). Default: `us-letter`. -- `top_margin`: dimension (e.g., 0.7in, 1.2em, 10pt). It can be specified with units (cm, in, pt, mm, ex, em). For example, `0.1cm`. Default: `0.7in`. -- `bottom_margin`: dimension (e.g., 0.7in, 1.2em, 10pt). It can be specified with units (cm, in, pt, mm, ex, em). For example, `0.1cm`. Default: `0.7in`. -- `left_margin`: dimension (e.g., 0.7in, 1.2em, 10pt). It can be specified with units (cm, in, pt, mm, ex, em). For example, `0.1cm`. Default: `0.7in`. -- `right_margin`: dimension (e.g., 0.7in, 1.2em, 10pt). It can be specified with units (cm, in, pt, mm, ex, em). For example, `0.1cm`. Default: `0.7in`. -- `show_footer`: boolean. Show the footer at the bottom of pages. Default: `True`. -- `show_top_note`: boolean. Show the top note at the top of the first page. Default: `True`. -#### `design.colors` - -- `body`: string. The color can be specified either with their name (https://www.w3.org/TR/SVG11/types.html#ColorKeywords), hexadecimal value, RGB value, or HSL value. Default: `rgb(0, 0, 0)`. -- `name`: string. The color can be specified either with their name (https://www.w3.org/TR/SVG11/types.html#ColorKeywords), hexadecimal value, RGB value, or HSL value. Default: `rgb(0, 79, 144)`. -- `headline`: string. The color can be specified either with their name (https://www.w3.org/TR/SVG11/types.html#ColorKeywords), hexadecimal value, RGB value, or HSL value. Default: `rgb(0, 79, 144)`. -- `connections`: string. The color can be specified either with their name (https://www.w3.org/TR/SVG11/types.html#ColorKeywords), hexadecimal value, RGB value, or HSL value. Default: `rgb(0, 79, 144)`. -- `section_titles`: string. The color can be specified either with their name (https://www.w3.org/TR/SVG11/types.html#ColorKeywords), hexadecimal value, RGB value, or HSL value. Default: `rgb(0, 79, 144)`. -- `links`: string. The color can be specified either with their name (https://www.w3.org/TR/SVG11/types.html#ColorKeywords), hexadecimal value, RGB value, or HSL value. Default: `rgb(0, 79, 144)`. -- `footer`: string. The color can be specified either with their name (https://www.w3.org/TR/SVG11/types.html#ColorKeywords), hexadecimal value, RGB value, or HSL value. Default: `rgb(128, 128, 128)`. -- `top_note`: string. The color can be specified either with their name (https://www.w3.org/TR/SVG11/types.html#ColorKeywords), hexadecimal value, RGB value, or HSL value. Default: `rgb(128, 128, 128)`. -#### `design.typography` - -- `line_spacing`: dimension (e.g., 0.7in, 1.2em, 10pt). Space between lines of text. Larger values create more vertical space. Default: `0.6em`. -- `alignment`: string. Text alignment. Options: 'left', 'justified' (spreads text across full width), 'justified-with-no-hyphenation' (justified without word breaks). Default: `justified`. -- `date_and_location_column_alignment`: one of: "left", "center", "right". Alignment for dates and locations in entries. Options: 'left', 'center', 'right'. Default: `right`. -- `font_family`: object or one of: "Aptos", "Arial", "Arial Rounded MT", "Arial Unicode MS", "Comic Sans MS", "Courier New", "DejaVu Sans Mono", "Didot", "EB Garamond", "Fontin", "Garamond", "Gentium Book Plus", "Georgia", "Gill Sans", "Helvetica", "Impact", "Inter", "Lato", "Libertinus Serif", "Lucida Sans Unicode", "Mukta", "New Computer Modern", "Noto Sans", "Open Sans", "Open Sauce Sans", "Poppins", "Raleway", "Roboto", "Source Sans 3", "Tahoma", "Times New Roman", "Trebuchet MS", "Ubuntu", "Verdana", "XCharter". The font family. You can provide a single font name as a string (applies to all elements), or a dictionary with keys 'body', 'name', 'headline', 'connections', and 'section_titles' to customize each element. Any system font can be used. -##### `design.typography.font_size` - -- `body`: dimension (e.g., 0.7in, 1.2em, 10pt). The font size for body text. Default: `10pt`. -- `name`: dimension (e.g., 0.7in, 1.2em, 10pt). The font size for the name. Default: `30pt`. -- `headline`: dimension (e.g., 0.7in, 1.2em, 10pt). The font size for the headline. Default: `10pt`. -- `connections`: dimension (e.g., 0.7in, 1.2em, 10pt). The font size for connections. Default: `10pt`. -- `section_titles`: dimension (e.g., 0.7in, 1.2em, 10pt). The font size for section titles. Default: `1.4em`. -##### `design.typography.small_caps` - -- `name`: boolean. Whether to use small caps for the name. Default: `False`. -- `headline`: boolean. Whether to use small caps for the headline. Default: `False`. -- `connections`: boolean. Whether to use small caps for connections. Default: `False`. -- `section_titles`: boolean. Whether to use small caps for section titles. Default: `False`. -##### `design.typography.bold` - -- `name`: boolean. Whether to make the name bold. Default: `True`. -- `headline`: boolean. Whether to make the headline bold. Default: `False`. -- `connections`: boolean. Whether to make connections bold. Default: `False`. -- `section_titles`: boolean. Whether to make section titles bold. Default: `True`. -#### `design.links` - -- `underline`: boolean. Underline hyperlinks. Default: `False`. -- `show_external_link_icon`: boolean. Show an external link icon next to URLs. Default: `False`. -#### `design.header` - -- `alignment`: one of: "left", "center", "right". Header alignment. Options: 'left', 'center', 'right'. Default: `center`. -- `photo_width`: dimension (e.g., 0.7in, 1.2em, 10pt). Photo width. It can be specified with units (cm, in, pt, mm, ex, em). For example, `0.1cm`. Default: `3.5cm`. -- `photo_position`: string. Photo position (left or right). Default: `left`. -- `photo_space_left`: dimension (e.g., 0.7in, 1.2em, 10pt). Space to the left of the photo. It can be specified with units (cm, in, pt, mm, ex, em). For example, `0.1cm`. Default: `0.4cm`. -- `photo_space_right`: dimension (e.g., 0.7in, 1.2em, 10pt). Space to the right of the photo. It can be specified with units (cm, in, pt, mm, ex, em). For example, `0.1cm`. Default: `0.4cm`. -- `space_below_name`: dimension (e.g., 0.7in, 1.2em, 10pt). Space below your name. It can be specified with units (cm, in, pt, mm, ex, em). For example, `0.1cm`. Default: `0.7cm`. -- `space_below_headline`: dimension (e.g., 0.7in, 1.2em, 10pt). Space below the headline. It can be specified with units (cm, in, pt, mm, ex, em). For example, `0.1cm`. Default: `0.7cm`. -- `space_below_connections`: dimension (e.g., 0.7in, 1.2em, 10pt). Space below contact information. It can be specified with units (cm, in, pt, mm, ex, em). For example, `0.1cm`. Default: `0.7cm`. -##### `design.header.connections` - -- `phone_number_format`: one of: "national", "international", "E164". Phone number format. Default: `national`. -- `hyperlink`: boolean. Make contact information clickable in the PDF. Default: `True`. -- `show_icons`: boolean. Show icons next to contact information. Default: `True`. -- `display_urls_instead_of_usernames`: boolean. Display full URLs instead of labels. Default: `False`. -- `separator`: string. Character(s) to separate contact items (e.g., '|' or '•'). Leave empty for no separator. Default: ``. -- `space_between_connections`: dimension (e.g., 0.7in, 1.2em, 10pt). Horizontal space between contact items. It can be specified with units (cm, in, pt, mm, ex, em). For example, `0.1cm`. Default: `0.5cm`. -#### `design.section_titles` - -- `type`: one of: "with_partial_line", "with_full_line", "without_line", "moderncv". Section title visual style. Use 'with_partial_line' for a line next to the title, 'with_full_line' for a line across the page, 'without_line' for no line, or 'moderncv' for the ModernCV style. Default: `with_partial_line`. -- `line_thickness`: dimension (e.g., 0.7in, 1.2em, 10pt). It can be specified with units (cm, in, pt, mm, ex, em). For example, `0.1cm`. Default: `0.5pt`. -- `space_above`: dimension (e.g., 0.7in, 1.2em, 10pt). It can be specified with units (cm, in, pt, mm, ex, em). For example, `0.1cm`. Default: `0.5cm`. -- `space_below`: dimension (e.g., 0.7in, 1.2em, 10pt). It can be specified with units (cm, in, pt, mm, ex, em). For example, `0.1cm`. Default: `0.3cm`. -#### `design.sections` - -- `allow_page_break`: boolean. Allow page breaks within sections. If false, sections that don't fit will start on a new page. Default: `True`. -- `space_between_regular_entries`: dimension (e.g., 0.7in, 1.2em, 10pt). Vertical space between entries. It can be specified with units (cm, in, pt, mm, ex, em). For example, `0.1cm`. Default: `1.2em`. -- `space_between_text_based_entries`: dimension (e.g., 0.7in, 1.2em, 10pt). Vertical space between text-based entries. It can be specified with units (cm, in, pt, mm, ex, em). For example, `0.1cm`. Default: `0.3em`. -- `show_time_spans_in`: list of strings. Section titles where time spans (e.g., '2 years 3 months') should be displayed. Default: `['experience']`. -#### `design.entries` - -- `date_and_location_width`: dimension (e.g., 0.7in, 1.2em, 10pt). Width of the date/location column. It can be specified with units (cm, in, pt, mm, ex, em). For example, `0.1cm`. Default: `4.15cm`. -- `side_space`: dimension (e.g., 0.7in, 1.2em, 10pt). Left and right margins. It can be specified with units (cm, in, pt, mm, ex, em). For example, `0.1cm`. Default: `0.2cm`. -- `space_between_columns`: dimension (e.g., 0.7in, 1.2em, 10pt). Space between main content and date/location columns. It can be specified with units (cm, in, pt, mm, ex, em). For example, `0.1cm`. Default: `0.1cm`. -- `allow_page_break`: boolean. Allow page breaks within entries. If false, entries that don't fit will move to a new page. Default: `False`. -- `short_second_row`: boolean. Shorten the second row to align with the date/location column. Default: `True`. -##### `design.entries.summary` - -- `space_above`: dimension (e.g., 0.7in, 1.2em, 10pt). Space above summary text. It can be specified with units (cm, in, pt, mm, ex, em). For example, `0.1cm`. Default: `0cm`. -- `space_left`: dimension (e.g., 0.7in, 1.2em, 10pt). Left margin for summary text. It can be specified with units (cm, in, pt, mm, ex, em). For example, `0.1cm`. Default: `0cm`. -##### `design.entries.highlights` - -- `bullet`: one of: "●", "•", "◦", "-", "◆", "★", "■", "—", "○". Bullet character for highlights. Default: `•`. -- `nested_bullet`: one of: "●", "•", "◦", "-", "◆", "★", "■", "—", "○". Bullet character for nested highlights. Default: `•`. -- `space_left`: dimension (e.g., 0.7in, 1.2em, 10pt). Left indentation. It can be specified with units (cm, in, pt, mm, ex, em). For example, `0.1cm`. Default: `0.15cm`. -- `space_above`: dimension (e.g., 0.7in, 1.2em, 10pt). Space above highlights. It can be specified with units (cm, in, pt, mm, ex, em). For example, `0.1cm`. Default: `0cm`. -- `space_between_items`: dimension (e.g., 0.7in, 1.2em, 10pt). Space between highlight items. It can be specified with units (cm, in, pt, mm, ex, em). For example, `0.1cm`. Default: `0cm`. -- `space_between_bullet_and_text`: dimension (e.g., 0.7in, 1.2em, 10pt). Space between bullet and text. It can be specified with units (cm, in, pt, mm, ex, em). For example, `0.1cm`. Default: `0.5em`. -#### `design.templates` - -- `footer`: string. Template for the footer. Default: `*NAME -- PAGE_NUMBER/TOTAL_PAGES*`. -- `top_note`: string. Template for the top note. Default: `*LAST_UPDATED CURRENT_DATE*`. -- `single_date`: string. Template for single dates. Default: `MONTH_ABBREVIATION YEAR`. -- `date_range`: string. Template for date ranges. Default: `START_DATE – END_DATE`. -- `time_span`: string. Template for time spans (duration calculations). Default: `HOW_MANY_YEARS YEARS HOW_MANY_MONTHS MONTHS`. -##### `design.templates.one_line_entry` - -- `main_column`: string. Template for one-line entries. Default: `**LABEL:** DETAILS`. -##### `design.templates.education_entry` - -- `main_column`: string. Template for education entry main column. Default: `**INSTITUTION**, AREA -SUMMARY -HIGHLIGHTS`. -- `degree_column`: string. Optional degree column template. If provided, displays degree in separate column. If `null`, no degree column is shown. Default: `**DEGREE**`. -- `date_and_location_column`: string. Template for education entry date/location column. Default: `LOCATION -DATE`. -##### `design.templates.normal_entry` - -- `main_column`: string. Template for normal entry main column. Default: `**NAME** -SUMMARY -HIGHLIGHTS`. -- `date_and_location_column`: string. Template for normal entry date/location column. Default: `LOCATION -DATE`. -##### `design.templates.experience_entry` - -- `main_column`: string. Template for experience entry main column. Default: `**COMPANY**, POSITION -SUMMARY -HIGHLIGHTS`. -- `date_and_location_column`: string. Template for experience entry date/location column. Default: `LOCATION -DATE`. -##### `design.templates.publication_entry` - -- `main_column`: string. Template for publication entry main column. Default: `**TITLE** -SUMMARY -AUTHORS -URL (JOURNAL)`. -- `date_and_location_column`: string. Template for publication entry date column. Default: `DATE`. - -### Design Example - -```yaml -design: - theme: classic - page: - size: us-letter - top_margin: 0.7in - bottom_margin: 0.7in - left_margin: 0.7in - right_margin: 0.7in - show_footer: true - show_top_note: true - colors: - body: rgb(0, 0, 0) - name: rgb(0, 79, 144) - connections: rgb(0, 79, 144) - section_titles: rgb(0, 79, 144) - links: rgb(0, 79, 144) - typography: - line_spacing: 0.6em - alignment: justified - font_family: Source Sans 3 # Shorthand: applies to all elements - font_size: - body: 10pt - name: 30pt - links: - underline: false - show_external_link_icon: false - header: - alignment: center - connections: - phone_number_format: national - show_icons: true - section_titles: - type: with_partial_line - sections: - show_time_spans_in: - - experience - entries: - date_and_location_width: 4.15cm - highlights: - bullet: "\u2022" - templates: - footer: "*NAME -- PAGE_NUMBER/TOTAL_PAGES*" - top_note: "*LAST_UPDATED CURRENT_DATE*" - single_date: MONTH_ABBREVIATION YEAR - date_range: "START_DATE \u2013 END_DATE" - time_span: HOW_MANY_YEARS YEARS HOW_MANY_MONTHS MONTHS - one_line_entry: - main_column: "**LABEL:** DETAILS" - education_entry: - main_column: "**INSTITUTION**, AREA\nSUMMARY\nHIGHLIGHTS" - degree_column: "**DEGREE**" - date_and_location_column: "LOCATION\nDATE" - normal_entry: - main_column: "**NAME**\nSUMMARY\nHIGHLIGHTS" - date_and_location_column: "LOCATION\nDATE" - experience_entry: - main_column: "**COMPANY**, POSITION\nSUMMARY\nHIGHLIGHTS" - date_and_location_column: "LOCATION\nDATE" - publication_entry: - main_column: "**TITLE**\nSUMMARY\nAUTHORS\nURL (JOURNAL)" - date_and_location_column: DATE -``` - -#### Template Placeholders - -Templates use UPPERCASE placeholders that map to entry fields: -- **All entries:** DATE, LOCATION, SUMMARY, HIGHLIGHTS -- **ExperienceEntry:** COMPANY, POSITION -- **EducationEntry:** INSTITUTION, AREA, DEGREE -- **PublicationEntry:** TITLE, AUTHORS, URL, DOI, JOURNAL -- **NormalEntry:** NAME -- **OneLineEntry:** LABEL, DETAILS -- **Page-level:** NAME, PAGE_NUMBER, TOTAL_PAGES, LAST_UPDATED, CURRENT_DATE -- **Date templates:** MONTH_ABBREVIATION, MONTH_NAME, YEAR, START_DATE, END_DATE, HOW_MANY_YEARS, HOW_MANY_MONTHS, YEARS, MONTHS - -You can also add arbitrary keys to entries and use them as UPPERCASE placeholders. - -## `locale` Field - -### Available Languages - -Available languages: danish, dutch, english, french, german, hindi, indonesian, italian, japanese, korean, mandarin_chinese, norwegian_bokmål, norwegian_nynorsk, portuguese, russian, spanish, turkish. - -```yaml -locale: - language: german # Use a built-in locale - present: jetzt # Override individual fields -``` - -### Customizable Fields - -- `last_updated`: string. Translation of "Last updated in". Default: `Last updated in`. -- `month`: string. Translation of "month" (singular). Default: `month`. -- `months`: string. Translation of "months" (plural). Default: `months`. -- `year`: string. Translation of "year" (singular). Default: `year`. -- `years`: string. Translation of "years" (plural). Default: `years`. -- `present`: string. Translation of "present" for ongoing dates. Default: `present`. -- `phrases`: object. Locale-specific phrases used in entry templates as placeholders. -- `month_abbreviations`: list of strings. Month abbreviations (Jan-Dec). Default: `['Jan', 'Feb', 'Mar', 'Apr', 'May', 'June', 'July', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec']`. -- `month_names`: list of strings. Full month names (January-December). Default: `['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']`. - -## `settings` Field - -- `current_date`: date (YYYY-MM-DD). The date to use as "current date" for filenames, the "last updated" label, and time span calculations. Defaults to the actual current date. -- `render_command`: object. Settings for the `render` command. These correspond to command-line arguments. CLI arguments take precedence over these settings. -- `bold_keywords`: list of strings. Keywords to automatically bold in the output. Default: `[]`. - -### `settings.render_command` - -- `output_folder`: file path. Base output folder for all generated files. Referenced as `OUTPUT_FOLDER` in output path defaults. Default: `rendercv_output`. -- `design`: file path. Path to a YAML file containing the `design` field. -- `locale`: file path. Path to a YAML file containing the `locale` field. -- `typst_path`: file path. Output path for the Typst file, relative to the input YAML file. Default: `OUTPUT_FOLDER/NAME_IN_SNAKE_CASE_CV.typ`. -- `pdf_path`: file path. Output path for the PDF file, relative to the input YAML file. Default: `OUTPUT_FOLDER/NAME_IN_SNAKE_CASE_CV.pdf`. -- `markdown_path`: file path. Output path for the Markdown file, relative to the input YAML file. Default: `OUTPUT_FOLDER/NAME_IN_SNAKE_CASE_CV.md`. -- `html_path`: file path. Output path for the HTML file, relative to the input YAML file. Default: `OUTPUT_FOLDER/NAME_IN_SNAKE_CASE_CV.html`. -- `png_path`: file path. Output path for PNG files, relative to the input YAML file. Default: `OUTPUT_FOLDER/NAME_IN_SNAKE_CASE_CV.png`. -- `dont_generate_markdown`: boolean. Skip Markdown generation. This also disables HTML generation. Default: `False`. -- `dont_generate_html`: boolean. Skip HTML generation. Default: `False`. -- `dont_generate_typst`: boolean. Skip Typst generation. This also disables PDF and PNG generation. Default: `False`. -- `dont_generate_pdf`: boolean. Skip PDF generation. Default: `False`. -- `dont_generate_png`: boolean. Skip PNG generation. Default: `False`. - -### Path Placeholders - -Output path fields (`typst_path`, `pdf_path`, etc.) support these placeholders: - -- `OUTPUT_FOLDER`: The output folder path (e.g., rendercv_output) -- `MONTH_NAME`: Full name of the month (e.g., January) -- `MONTH_ABBREVIATION`: Abbreviation of the month (e.g., Jan) -- `MONTH`: Month as a number (e.g., 1) -- `MONTH_IN_TWO_DIGITS`: Month as a number in two digits (e.g., 01) -- `DAY`: Day of the month (e.g., 5) -- `DAY_IN_TWO_DIGITS`: Day of the month in two digits (e.g., 05) -- `YEAR`: Year as a number (e.g., 2024) -- `YEAR_IN_TWO_DIGITS`: Year as a number in two digits (e.g., 24) -- `NAME`: The name of the CV owner (e.g., John Doe) -- `NAME_IN_SNAKE_CASE`: The name of the CV owner in snake case (e.g., John_Doe) -- `NAME_IN_LOWER_SNAKE_CASE`: The name of the CV owner in lower snake case (e.g., john_doe) -- `NAME_IN_UPPER_SNAKE_CASE`: The name of the CV owner in upper snake case (e.g., JOHN_DOE) -- `NAME_IN_KEBAB_CASE`: The name of the CV owner in kebab case (e.g., John-Doe) -- `NAME_IN_LOWER_KEBAB_CASE`: The name of the CV owner in lower kebab case (e.g., john-doe) -- `NAME_IN_UPPER_KEBAB_CASE`: The name of the CV owner in upper kebab case (e.g., JOHN-DOE) - -## Complete Example - -```yaml -cv: - name: John Doe - headline: - location: San Francisco, CA - email: john.doe@email.com - photo: - phone: - website: https://rendercv.com/ - social_networks: - - network: LinkedIn - username: rendercv - - network: GitHub - username: rendercv - custom_connections: - sections: - Welcome to RenderCV: - - RenderCV reads a CV written in a YAML file, and generates a PDF with professional typography. - - See the [documentation](https://docs.rendercv.com) for more details. - education: - - institution: Princeton University - area: Computer Science - degree: PhD - date: - start_date: 2018-09 - end_date: 2023-05 - location: Princeton, NJ - summary: - highlights: - - 'Thesis: Efficient Neural Architecture Search for Resource-Constrained Deployment' - - 'Advisor: Prof. Sanjeev Arora' - - NSF Graduate Research Fellowship, Siebel Scholar (Class of 2022) - - institution: Boğaziçi University - area: Computer Engineering - degree: BS - date: - start_date: 2014-09 - end_date: 2018-06 - location: Istanbul, Türkiye - summary: - highlights: - - 'GPA: 3.97/4.00, Valedictorian' - - Fulbright Scholarship recipient for graduate studies - experience: - - company: Nexus AI - position: Co-Founder & CTO - date: - start_date: 2023-06 - end_date: present - location: San Francisco, CA - summary: - highlights: - - Built foundation model infrastructure serving 2M+ monthly API requests with 99.97% uptime - - Raised $18M Series A led by Sequoia Capital, with participation from a16z and Founders Fund - - Scaled engineering team from 3 to 28 across ML research, platform, and applied AI divisions - - Developed proprietary inference optimization reducing latency by 73% compared to baseline - - company: NVIDIA Research - position: Research Intern - date: - start_date: 2022-05 - end_date: 2022-08 - location: Santa Clara, CA - summary: - highlights: - - Designed sparse attention mechanism reducing transformer memory footprint by 4.2x - - Co-authored paper accepted at NeurIPS 2022 (spotlight presentation, top 5% of submissions) - - company: Google DeepMind - position: Research Intern - date: - start_date: 2021-05 - end_date: 2021-08 - location: London, UK - summary: - highlights: - - Developed reinforcement learning algorithms for multi-agent coordination - - Published research at top-tier venues with significant academic impact - - ICML 2022 main conference paper, cited 340+ times within two years - - NeurIPS 2022 workshop paper on emergent communication protocols - - Invited journal extension in JMLR (2023) - - company: Apple ML Research - position: Research Intern - date: - start_date: 2020-05 - end_date: 2020-08 - location: Cupertino, CA - summary: - highlights: - - Created on-device neural network compression pipeline deployed across 50M+ devices - - Filed 2 patents on efficient model quantization techniques for edge inference - - company: Microsoft Research - position: Research Intern - date: - start_date: 2019-05 - end_date: 2019-08 - location: Redmond, WA - summary: - highlights: - - Implemented novel self-supervised learning framework for low-resource language modeling - - Research integrated into Azure Cognitive Services, reducing training data requirements by 60% - projects: - - name: '[FlashInfer](https://github.com/)' - date: - start_date: 2023-01 - end_date: present - location: - summary: Open-source library for high-performance LLM inference kernels - highlights: - - Achieved 2.8x speedup over baseline attention implementations on A100 GPUs - - Adopted by 3 major AI labs, 8,500+ GitHub stars, 200+ contributors - - name: '[NeuralPrune](https://github.com/)' - date: '2021' - start_date: - end_date: - location: - summary: Automated neural network pruning toolkit with differentiable masks - highlights: - - Reduced model size by 90% with less than 1% accuracy degradation on ImageNet - - Featured in PyTorch ecosystem tools, 4,200+ GitHub stars - publications: - - title: 'Sparse Mixture-of-Experts at Scale: Efficient Routing for Trillion-Parameter Models' - authors: - - '*John Doe*' - - Sarah Williams - - David Park - summary: - doi: 10.1234/neurips.2023.1234 - url: - journal: NeurIPS 2023 - date: 2023-07 - - title: Neural Architecture Search via Differentiable Pruning - authors: - - James Liu - - '*John Doe*' - summary: - doi: 10.1234/neurips.2022.5678 - url: - journal: NeurIPS 2022, Spotlight - date: 2022-12 - - title: Multi-Agent Reinforcement Learning with Emergent Communication - authors: - - Maria Garcia - - '*John Doe*' - - Tom Anderson - summary: - doi: 10.1234/icml.2022.9012 - url: - journal: ICML 2022 - date: 2022-07 - - title: On-Device Model Compression via Learned Quantization - authors: - - '*John Doe*' - - Kevin Wu - summary: - doi: 10.1234/iclr.2021.3456 - url: - journal: ICLR 2021, Best Paper Award - date: 2021-05 - selected_honors: - - bullet: MIT Technology Review 35 Under 35 Innovators (2024) - - bullet: Forbes 30 Under 30 in Enterprise Technology (2024) - - bullet: ACM Doctoral Dissertation Award Honorable Mention (2023) - - bullet: Google PhD Fellowship in Machine Learning (2020 – 2023) - - bullet: Fulbright Scholarship for Graduate Studies (2018) - skills: - - label: Languages - details: Python, C++, CUDA, Rust, Julia - - label: ML Frameworks - details: PyTorch, JAX, TensorFlow, Triton, ONNX - - label: Infrastructure - details: Kubernetes, Ray, distributed training, AWS, GCP - - label: Research Areas - details: Neural architecture search, model compression, efficient inference, multi-agent RL - patents: - - number: Adaptive Quantization for Neural Network Inference on Edge Devices (US Patent 11,234,567) - - number: Dynamic Sparsity Patterns for Efficient Transformer Attention (US Patent 11,345,678) - - number: Hardware-Aware Neural Architecture Search Method (US Patent 11,456,789) - invited_talks: - - reversed_number: Scaling Laws for Efficient Inference — Stanford HAI Symposium (2024) - - reversed_number: Building AI Infrastructure for the Next Decade — TechCrunch Disrupt (2024) - - reversed_number: 'From Research to Production: Lessons in ML Systems — NeurIPS Workshop (2023)' - - reversed_number: "Efficient Deep Learning: A Practitioner's Perspective — Google Tech Talk (2022)" - any_section_title: - - You can use any section title you want. - - 'You can choose any entry type for the section: `TextEntry`, `ExperienceEntry`, `EducationEntry`, `PublicationEntry`, `BulletEntry`, `NumberedEntry`, or `ReversedNumberedEntry`.' - - Markdown syntax is supported everywhere. - - The `design` field in YAML gives you control over almost any aspect of your CV design. - - See the [documentation](https://docs.rendercv.com) for more details. -design: - theme: engineeringclassic - page: - size: us-letter - top_margin: 0.7in - bottom_margin: 0.7in - left_margin: 0.7in - right_margin: 0.7in - show_footer: true - show_top_note: true - colors: - body: rgb(0, 0, 0) - name: rgb(0, 79, 144) - headline: rgb(0, 79, 144) - connections: rgb(0, 79, 144) - section_titles: rgb(0, 79, 144) - links: rgb(0, 79, 144) - footer: rgb(128, 128, 128) - top_note: rgb(128, 128, 128) - typography: - line_spacing: 0.6em - alignment: justified - date_and_location_column_alignment: right - font_family: - body: Raleway - name: Raleway - headline: Raleway - connections: Raleway - section_titles: Raleway - font_size: - body: 10pt - name: 30pt - headline: 10pt - connections: 10pt - section_titles: 1.4em - small_caps: - name: false - headline: false - connections: false - section_titles: false - bold: - name: false - headline: false - connections: false - section_titles: false - links: - underline: false - show_external_link_icon: false - header: - alignment: left - photo_width: 3.5cm - photo_position: left - photo_space_left: 0.4cm - photo_space_right: 0.4cm - space_below_name: 0.7cm - space_below_headline: 0.7cm - space_below_connections: 0.7cm - connections: - phone_number_format: national - hyperlink: true - show_icons: true - display_urls_instead_of_usernames: false - separator: '' - space_between_connections: 0.5cm - section_titles: - type: with_full_line - line_thickness: 0.5pt - space_above: 0.5cm - space_below: 0.3cm - sections: - allow_page_break: true - space_between_regular_entries: 1.2em - space_between_text_based_entries: 0.3em - show_time_spans_in: [] - entries: - date_and_location_width: 4.15cm - side_space: 0.2cm - space_between_columns: 0.1cm - allow_page_break: false - short_second_row: false - summary: - space_above: 0.12cm - space_left: 0cm - highlights: - bullet: • - nested_bullet: • - space_left: 0cm - space_above: 0.12cm - space_between_items: 0.12cm - space_between_bullet_and_text: 0.5em - templates: - footer: '*NAME -- PAGE_NUMBER/TOTAL_PAGES*' - top_note: '*LAST_UPDATED CURRENT_DATE*' - single_date: MONTH_ABBREVIATION YEAR - date_range: START_DATE – END_DATE - time_span: HOW_MANY_YEARS YEARS HOW_MANY_MONTHS MONTHS - one_line_entry: - main_column: '**LABEL:** DETAILS' - education_entry: - main_column: |- - **INSTITUTION**, DEGREE_WITH_AREA -- LOCATION - SUMMARY - HIGHLIGHTS - degree_column: - date_and_location_column: DATE - normal_entry: - main_column: |- - **NAME** -- **LOCATION** - SUMMARY - HIGHLIGHTS - date_and_location_column: DATE - experience_entry: - main_column: |- - **POSITION**, COMPANY -- LOCATION - SUMMARY - HIGHLIGHTS - date_and_location_column: DATE - publication_entry: - main_column: |- - **TITLE** - SUMMARY - AUTHORS - URL (JOURNAL) - date_and_location_column: DATE -locale: - language: english - last_updated: Last updated in - month: month - months: months - year: year - years: years - present: present - phrases: - degree_with_area: DEGREE in AREA - month_abbreviations: - - Jan - - Feb - - Mar - - Apr - - May - - June - - July - - Aug - - Sept - - Oct - - Nov - - Dec - month_names: - - January - - February - - March - - April - - May - - June - - July - - August - - September - - October - - November - - December -settings: - current_date: '2026-02-16' - render_command: - output_folder: rendercv_output - design: - locale: - typst_path: OUTPUT_FOLDER/NAME_IN_SNAKE_CASE_CV.typ - pdf_path: OUTPUT_FOLDER/NAME_IN_SNAKE_CASE_CV.pdf - markdown_path: OUTPUT_FOLDER/NAME_IN_SNAKE_CASE_CV.md - html_path: OUTPUT_FOLDER/NAME_IN_SNAKE_CASE_CV.html - png_path: OUTPUT_FOLDER/NAME_IN_SNAKE_CASE_CV.png - dont_generate_markdown: false - dont_generate_html: false - dont_generate_typst: false - dont_generate_pdf: false - dont_generate_png: false - bold_keywords: [] -``` \ No newline at end of file diff --git a/scripts/update_llms_txt/template.j2.md b/scripts/update_llms_txt/template.j2.md deleted file mode 100644 index de312a16..00000000 --- a/scripts/update_llms_txt/template.j2.md +++ /dev/null @@ -1,360 +0,0 @@ -# RenderCV YAML Input Reference - -RenderCV generates professional CVs/resumes from YAML input files. Write your CV as YAML, run `rendercv render your_cv.yaml`, and get a PDF with perfect typography. - -## YAML Structure Overview - -A RenderCV YAML file has four top-level fields. Only `cv` is required. - -```yaml -cv: # Required. Your CV content (personal info and sections). - ... -design: # Optional. Visual styling (theme, colors, fonts, spacing). - ... -locale: # Optional. Language and date formatting. - ... -settings: # Optional. Output paths, generation flags, bold keywords. - ... -``` - -## `cv` Field - -### Personal Information (Header) - -All header fields are optional. RenderCV adapts to whatever you provide. - -```yaml -cv: - name: John Doe - headline: Machine Learning Engineer - location: San Francisco, CA - email: john@example.com - phone: "+14155551234" - website: https://johndoe.dev - photo: photo.jpg - social_networks: - - network: LinkedIn - username: johndoe - - network: GitHub - username: johndoe - custom_connections: - - placeholder: Book a call - url: https://cal.com/johndoe - fontawesome_icon: calendar-days -``` - -**Header fields:** - -{% for field in cv_fields %} -- `{{ field.name }}`{% if field.required %} (required){% endif %}: {{ field.type }}.{% if field.description %} {{ field.description }}.{% endif %}{% if field.examples %} Examples: {{ field.examples | map('tojson') | join(', ') }}.{% endif %}{{ "" }} -{% endfor %} - -**Available social networks:** {{ social_networks | join(', ') }}. - -**`email`, `phone`, and `website` accept a single value or a list** for multiple entries. - -### Sections - -The `sections` field holds the main content of your CV. It is a dictionary where keys are section titles (displayed as headings) and values are lists of entries. - -```yaml -cv: - sections: - experience: # Section title (can be anything) - - company: Acme Corp - position: Senior Engineer - start_date: 2020-01 - end_date: present - highlights: - - Led migration to microservices architecture - skills: - - label: Languages - details: Python, Go, Rust -``` - -- Section titles are arbitrary strings. Use any name you want. -- Each section must contain only one type of entry. You cannot mix different entry types in the same section. -- Entry types are auto-detected based on the fields you provide. - -### Entry Types - -RenderCV provides {{ entry_types | length }} entry types: - -{% for entry in entry_types %} -#### {{ entry.name }} - -{{ entry.description }} -{% if entry.fields %} - -| Field | Required | Type | Description | -| ----- | -------- | ---- | ----------- | -{% for field in entry.fields %} -| `{{ field.name }}` | {{ "Yes" if field.required else "No" }} | {{ field.type }} | {{ field.description or "" }} | -{% endfor %} -{% endif %} - -{% if entry.name == "EducationEntry" %} -```yaml -- institution: Princeton University - area: Computer Science - degree: PhD - start_date: 2018-09 - end_date: 2023-05 - location: Princeton, NJ - highlights: - - "Thesis: Efficient Neural Architecture Search" - - NSF Graduate Research Fellowship -``` -{% elif entry.name == "ExperienceEntry" %} -```yaml -- company: Nexus AI - position: Co-Founder & CTO - start_date: 2023-06 - end_date: present - location: San Francisco, CA - summary: Building foundation model infrastructure - highlights: - - Raised $18M Series A led by Sequoia Capital - - Scaled engineering team from 3 to 28 -``` -{% elif entry.name == "PublicationEntry" %} -```yaml -- title: "Sparse Mixture-of-Experts at Scale" - authors: - - "*John Doe*" - - Sarah Williams - doi: 10.1234/neurips.2023.1234 - journal: NeurIPS 2023 - date: 2023-07 -``` -{% elif entry.name == "NormalEntry" %} -```yaml -- name: "[FlashInfer](https://github.com/)" - start_date: 2023-01 - end_date: present - summary: Open-source library for high-performance LLM inference - highlights: - - Achieved 2.8x speedup over baseline attention implementations - - 8,500+ GitHub stars, 200+ contributors -``` -{% elif entry.name == "OneLineEntry" %} -```yaml -- label: Languages - details: Python, C++, CUDA, Rust, Julia -``` -{% elif entry.name == "BulletEntry" %} -```yaml -- bullet: MIT Technology Review 35 Under 35 Innovators (2024) -``` -{% elif entry.name == "NumberedEntry" %} -```yaml -- number: "Adaptive Quantization for Neural Network Inference (US Patent 11,234,567)" -``` -{% elif entry.name == "ReversedNumberedEntry" %} -```yaml -- reversed_number: "Scaling Laws for Efficient Inference — Stanford HAI Symposium (2024)" -``` -{% elif entry.name == "TextEntry" %} -```yaml -sections: - summary: - - Software engineer with 10 years of experience in distributed systems. - - See the [documentation](https://docs.rendercv.com) for more details. -``` -{% endif %} - -{% endfor %} - -### Date Formats - -Fields `date`, `start_date`, and `end_date` accept: -- Full date: `2024-01-15` (YYYY-MM-DD) -- Month precision: `2024-01` (YYYY-MM) -- Year only: `2024` (YYYY) -- Custom text: `"Fall 2023"`, `"Summer 2020"` (for `date` field only) -- Ongoing: `present` (for `end_date` only) - -### Text Formatting - -All text fields support basic Markdown: -- `**text**` renders as bold -- `*text*` renders as italic -- `[text](url)` renders as a hyperlink -- `` `code` `` renders as inline code - -All text fields also support Typst syntax: -- `$$f(x) = x^2$$` renders as math -- `#emph[text]` and other Typst commands work directly - -### Arbitrary Keys - -You can add custom fields to any entry. By default they are ignored, but you can reference them in `design.templates` as UPPERCASE placeholders. - -```yaml -experience: - - company: Startup Inc - position: Founder - start_date: 2020-01 - end_date: present - revenue: $5M ARR # Custom field, available as REVENUE in templates -``` - -## `design` Field - -### Available Themes - -Available themes: {{ themes | join(', ') }}. - -All themes share the same design options but have different default values. If you specify a setting explicitly, it overrides the theme's default. - -```yaml -design: - theme: classic # Choose a theme, then override any option below -``` - -### Design Options - -Below are all available design options. All are optional; themes provide defaults. - -{% for section in design_sections %} -#### `design.{{ section.name }}` - -{% for field in section.fields %} -- `{{ field.name }}`: {{ field.type }}.{% if field.description %} {{ field.description }}.{% endif %}{% if field.default is not none %} Default: `{{ field.default }}`.{% endif %} - -{% endfor %} -{% for sub in section.sub_sections %} -##### `design.{{ section.name }}.{{ sub.name }}` - -{% for field in sub.fields %} -- `{{ field.name }}`: {{ field.type }}.{% if field.description %} {{ field.description }}.{% endif %}{% if field.default is not none %} Default: `{{ field.default }}`.{% endif %} - -{% endfor %} -{% endfor %} -{% endfor %} - -### Design Example - -```yaml -design: - theme: classic - page: - size: us-letter - top_margin: 0.7in - bottom_margin: 0.7in - left_margin: 0.7in - right_margin: 0.7in - show_footer: true - show_top_note: true - colors: - body: rgb(0, 0, 0) - name: rgb(0, 79, 144) - connections: rgb(0, 79, 144) - section_titles: rgb(0, 79, 144) - links: rgb(0, 79, 144) - typography: - line_spacing: 0.6em - alignment: justified - font_family: Source Sans 3 # Shorthand: applies to all elements - font_size: - body: 10pt - name: 30pt - links: - underline: false - show_external_link_icon: false - header: - alignment: center - connections: - phone_number_format: national - show_icons: true - section_titles: - type: with_partial_line - sections: - show_time_spans_in: - - experience - entries: - date_and_location_width: 4.15cm - highlights: - bullet: "\u2022" - templates: - footer: "*NAME -- PAGE_NUMBER/TOTAL_PAGES*" - top_note: "*LAST_UPDATED CURRENT_DATE*" - single_date: MONTH_ABBREVIATION YEAR - date_range: "START_DATE \u2013 END_DATE" - time_span: HOW_MANY_YEARS YEARS HOW_MANY_MONTHS MONTHS - one_line_entry: - main_column: "**LABEL:** DETAILS" - education_entry: - main_column: "**INSTITUTION**, AREA\nSUMMARY\nHIGHLIGHTS" - degree_column: "**DEGREE**" - date_and_location_column: "LOCATION\nDATE" - normal_entry: - main_column: "**NAME**\nSUMMARY\nHIGHLIGHTS" - date_and_location_column: "LOCATION\nDATE" - experience_entry: - main_column: "**COMPANY**, POSITION\nSUMMARY\nHIGHLIGHTS" - date_and_location_column: "LOCATION\nDATE" - publication_entry: - main_column: "**TITLE**\nSUMMARY\nAUTHORS\nURL (JOURNAL)" - date_and_location_column: DATE -``` - -#### Template Placeholders - -Templates use UPPERCASE placeholders that map to entry fields: -- **All entries:** DATE, LOCATION, SUMMARY, HIGHLIGHTS -- **ExperienceEntry:** COMPANY, POSITION -- **EducationEntry:** INSTITUTION, AREA, DEGREE -- **PublicationEntry:** TITLE, AUTHORS, URL, DOI, JOURNAL -- **NormalEntry:** NAME -- **OneLineEntry:** LABEL, DETAILS -- **Page-level:** NAME, PAGE_NUMBER, TOTAL_PAGES, LAST_UPDATED, CURRENT_DATE -- **Date templates:** MONTH_ABBREVIATION, MONTH_NAME, YEAR, START_DATE, END_DATE, HOW_MANY_YEARS, HOW_MANY_MONTHS, YEARS, MONTHS - -You can also add arbitrary keys to entries and use them as UPPERCASE placeholders. - -## `locale` Field - -### Available Languages - -Available languages: {{ locales | join(', ') }}. - -```yaml -locale: - language: german # Use a built-in locale - present: jetzt # Override individual fields -``` - -### Customizable Fields - -{% for field in locale_fields %} -- `{{ field.name }}`: {{ field.type }}.{% if field.description %} {{ field.description }}.{% endif %}{% if field.default is not none %} Default: `{{ field.default }}`.{% endif %} - -{% endfor %} - -## `settings` Field - -{% for field in settings_fields %} -- `{{ field.name }}`: {{ field.type }}.{% if field.description %} {{ field.description }}.{% endif %}{% if field.default is not none %} Default: `{{ field.default }}`.{% endif %} - -{% endfor %} - -### `settings.render_command` - -{% for field in render_command_fields %} -- `{{ field.name }}`: {{ field.type }}.{% if field.description %} {{ field.description }}.{% endif %}{% if field.default is not none %} Default: `{{ field.default }}`.{% endif %} - -{% endfor %} - -### Path Placeholders - -Output path fields (`typst_path`, `pdf_path`, etc.) support these placeholders: - -{% for p in path_placeholders %} -- `{{ p.name }}`: {{ p.description }} -{% endfor %} - -## Complete Example - -```yaml -{{ complete_example }}``` diff --git a/scripts/update_llms_txt/update_llms_txt.py b/scripts/update_llms_txt/update_llms_txt.py deleted file mode 100644 index 9d4a6e65..00000000 --- a/scripts/update_llms_txt/update_llms_txt.py +++ /dev/null @@ -1,508 +0,0 @@ -"""Generate docs/llms.txt from schema.json using a Jinja2 template.""" - -import json -import pathlib -import re - -import jinja2 - -repository_root = pathlib.Path(__file__).parent.parent.parent -schema_path = repository_root / "schema.json" -example_path = repository_root / "examples" / "John_Doe_EngineeringclassicTheme_CV.yaml" -template_dir = pathlib.Path(__file__).parent -output_path = repository_root / "docs" / "llms.txt" - - -def resolve_ref(ref: str, defs: dict) -> dict: - """Resolve a JSON Schema $ref string to its definition. - - Why: $ref strings like "#/$defs/PageSize" need to be looked up in the - schema's $defs dictionary. - - Args: - ref: The $ref string (e.g., "#/$defs/PageSize"). - defs: The $defs dictionary from the schema. - - Returns: - The resolved definition dictionary. - """ - key = ref.removeprefix("#/$defs/") - return defs[key] - - -def simplify_type(prop_schema: dict, defs: dict) -> str: - """Convert a JSON Schema property definition to a human-readable type string. - - Why: JSON Schema types are verbose and nested. LLMs need simple type - descriptions like "string", "boolean", "list of strings", etc. - - Args: - prop_schema: The property schema fragment. - defs: The $defs dictionary for resolving references. - - Returns: - A simplified type string. - """ - if "anyOf" in prop_schema: - # Filter out null types - non_null = [ - branch for branch in prop_schema["anyOf"] if branch != {"type": "null"} - ] - if len(non_null) == 1: - return simplify_type(non_null[0], defs) - # Multiple non-null types (e.g., ArbitraryDate = string | integer) - return " or ".join(simplify_type(branch, defs) for branch in non_null) - - if "$ref" in prop_schema: - resolved = resolve_ref(prop_schema["$ref"], defs) - ref_key = prop_schema["$ref"].removeprefix("#/$defs/") - - if "enum" in resolved: - return "one of: " + ", ".join(f'"{v}"' for v in resolved["enum"]) - - if ref_key == "TypstDimension": - return "dimension (e.g., 0.7in, 1.2em, 10pt)" - - if ref_key in ("ArbitraryDate", "ExactDate"): - return "date (YYYY-MM-DD, YYYY-MM, YYYY, or custom text)" - - if ref_key in ( - "ExistingPathRelativeToInput", - "PlannedPathRelativeToInput", - ): - return "file path" - - # If it has properties, it's a nested object - if "properties" in resolved: - return "object" - - return simplify_type(resolved, defs) - - if "const" in prop_schema: - return f'"{prop_schema["const"]}"' - - schema_type = prop_schema.get("type", "string") - - if schema_type == "array": - items = prop_schema.get("items", {}) - if "$ref" in items: - resolved = resolve_ref(items["$ref"], defs) - item_title = resolved.get("title", "object") - return f"list of {item_title}" - item_type = items.get("type", "string") - return f"list of {item_type}s" - - if prop_schema.get("format") == "date": - return "date (YYYY-MM-DD)" - - if prop_schema.get("format") == "uri": - return "URL" - - return schema_type - - -def clean_description(description: str | None) -> str | None: - """Clean up a schema description for use in llms.txt. - - Why: Schema descriptions contain verbose content like "The default value - is ..." and "Available placeholders:" lists that are redundant when shown - alongside defaults and in a separate placeholders section. - - Args: - description: The raw description string from the schema. - - Returns: - A cleaned description string, or None if empty. - """ - if not description: - return None - - # Remove "The default value is ..." wherever it appears - description = re.sub( - r"\s*The default value is `.+?`\.?", - "", - description, - ) - - # Remove "Available placeholders:" sections and everything after - description = re.sub( - r"\s*Available placeholders:.*", - "", - description, - flags=re.DOTALL, - ) - - # Remove "The following placeholders can be used:" sections - description = re.sub( - r"\s*The following placeholders can be used:.*", - "", - description, - flags=re.DOTALL, - ) - - # Collapse multiple whitespace/newlines into single space - description = re.sub(r"\s+", " ", description).strip().rstrip(".") - - return description if description else None - - -def extract_fields(definition: dict, defs: dict) -> list[dict]: - """Extract field information from a JSON Schema definition. - - Why: Entry types, CV model, and design sub-models all have properties - that need to be documented with their names, types, and metadata. - - Args: - definition: A JSON Schema definition with "properties". - defs: The $defs dictionary for resolving references. - - Returns: - A list of field info dicts with keys: name, required, type, - description, default, examples. - """ - properties = definition.get("properties", {}) - required_fields = definition.get("required", []) - fields = [] - - for prop_name, prop_schema in properties.items(): - field_type = simplify_type(prop_schema, defs) - description = clean_description(prop_schema.get("description")) - - default = prop_schema.get("default") - examples = prop_schema.get("examples") - - fields.append( - { - "name": prop_name, - "required": prop_name in required_fields, - "type": field_type, - "description": description, - "default": default, - "examples": examples, - } - ) - - return fields - - -def extract_entry_types(defs: dict) -> list[dict]: - """Extract all entry types from the ListOfEntries definition. - - Why: Entry types are the core building blocks for CV sections. The - ListOfEntries.anyOf array lists all valid entry types. - - Args: - defs: The $defs dictionary from the schema. - - Returns: - A list of entry type dicts with keys: name, fields. - """ - entry_types = [] - - # Human-readable descriptions for each entry type (stable, rarely changes) - descriptions = { - "TextEntry": "Plain text without structure. Just write a string.", - "EducationEntry": "For academic credentials.", - "ExperienceEntry": "For work history and professional roles.", - "PublicationEntry": "For papers, articles, and other publications.", - "NormalEntry": ( - "A flexible entry for projects, awards, certifications, or anything else." - ), - "OneLineEntry": ( - "For compact key-value pairs, ideal for skills or technical proficiencies." - ), - "BulletEntry": "A single bullet point. Use for simple lists.", - "NumberedEntry": "An automatically numbered entry.", - "ReversedNumberedEntry": ( - "A numbered entry that counts down (useful for publication" - " lists where recent items come first)." - ), - } - - for entry_variant in defs["ListOfEntries"]["anyOf"]: - items = entry_variant.get("items", {}) - - if items.get("type") == "string": - # TextEntry — plain strings - entry_types.append( - { - "name": "TextEntry", - "description": descriptions["TextEntry"], - "fields": [], - } - ) - continue - - if "$ref" not in items: - continue - - ref_key = items["$ref"].removeprefix("#/$defs/") - definition = defs[ref_key] - name = definition.get("title", ref_key.split("__")[-1]) - - fields = extract_fields(definition, defs) - - entry_types.append( - { - "name": name, - "description": descriptions.get(name, ""), - "fields": fields, - } - ) - - return entry_types - - -def extract_cv_fields(defs: dict) -> list[dict]: - """Extract CV header fields from the Cv definition. - - Why: The CV model has personal information fields (name, email, etc.) - that need to be documented. - - Args: - defs: The $defs dictionary from the schema. - - Returns: - A list of field info dicts (excluding 'sections' which is documented - separately). - """ - cv_def = defs["Cv"] - fields = extract_fields(cv_def, defs) - return [f for f in fields if f["name"] != "sections"] - - -def extract_design_section( - section_name: str, - section_schema: dict, - defs: dict, -) -> dict: - """Extract a design sub-section's fields, handling nested $ref objects. - - Why: Design sub-models (page, colors, typography, etc.) may contain - nested objects (e.g., typography.font_family, header.connections) that - need to be extracted as sub-sections. - - Args: - section_name: The name of the design sub-section. - section_schema: The schema for this sub-section (may be a $ref). - defs: The $defs dictionary from the schema. - - Returns: - A dict with keys: name, fields, sub_sections. - """ - # Resolve $ref if present - if "$ref" in section_schema: - definition = resolve_ref(section_schema["$ref"], defs) - else: - definition = section_schema - - if "properties" not in definition: - return {"name": section_name, "fields": [], "sub_sections": []} - - properties = definition.get("properties", {}) - required_fields = definition.get("required", []) - fields = [] - sub_sections = [] - - for prop_name, prop_schema in properties.items(): - # Check if this property references a nested object (not an enum) - ref_key = None - resolved = None - - if "$ref" in prop_schema: - ref_key = prop_schema["$ref"].removeprefix("#/$defs/") - resolved = defs.get(ref_key, {}) - elif "anyOf" in prop_schema: - non_null = [b for b in prop_schema["anyOf"] if b != {"type": "null"}] - if len(non_null) == 1 and "$ref" in non_null[0]: - ref_key = non_null[0]["$ref"].removeprefix("#/$defs/") - resolved = defs.get(ref_key, {}) - - # If it resolves to an object with properties (not an enum), treat - # as a sub-section - if resolved and "properties" in resolved and "enum" not in resolved: - sub_fields = extract_fields(resolved, defs) - sub_sections.append( - { - "name": prop_name, - "fields": sub_fields, - } - ) - else: - field_type = simplify_type(prop_schema, defs) - description = clean_description(prop_schema.get("description")) - default = prop_schema.get("default") - - fields.append( - { - "name": prop_name, - "required": prop_name in required_fields, - "type": field_type, - "description": description, - "default": default, - } - ) - - return { - "name": section_name, - "fields": fields, - "sub_sections": sub_sections, - } - - -def extract_design_sections(defs: dict) -> list[dict]: - """Extract all design sub-sections from the ClassicTheme definition. - - Why: ClassicTheme is the canonical theme. Its sub-sections (page, colors, - typography, etc.) define all available design options. - - Args: - defs: The $defs dictionary from the schema. - - Returns: - A list of design section dicts. - """ - classic = defs["ClassicTheme"] - properties = classic.get("properties", {}) - sections = [] - - for prop_name, prop_schema in properties.items(): - if prop_name == "theme": - continue # Skip the theme discriminator field - section = extract_design_section(prop_name, prop_schema, defs) - sections.append(section) - - return sections - - -def extract_path_placeholders(defs: dict) -> list[dict]: - """Extract path placeholders from the RenderCommand definition. - - Why: Output path fields support placeholders like NAME, YEAR, etc. These - are documented in the description of path fields. - - Args: - defs: The $defs dictionary from the schema. - - Returns: - A list of placeholder dicts with keys: name, description. - """ - render_command = defs["RenderCommand"] - # Get the typst_path description which lists all placeholders - typst_desc = render_command["properties"]["typst_path"].get("description", "") - - placeholders = [] - for line in typst_desc.split("\n"): - stripped = line.strip() - if stripped.startswith("- ") and ":" in stripped: - parts = stripped[2:].split(":", 1) - placeholders.append( - { - "name": parts[0].strip(), - "description": parts[1].strip(), - } - ) - - return placeholders - - -def uncomment_yaml(text: str) -> str: - """Uncomment commented-out YAML lines (e.g., ' # key: value' → ' key: value'). - - Why: The example YAML files have optional sections commented out. For llms.txt, - we want to show all options as active YAML so LLMs can see the full structure. - - Args: - text: The YAML text with commented lines. - - Returns: - The YAML text with comment markers removed. - """ - lines = text.split("\n") - result = [] - for line in lines: - # Match lines like " # key: value" — indented comment that is YAML content - uncommented = re.sub(r"^(\s*)# ", r"\1", line) - result.append(uncommented) - return "\n".join(result) - - -def main() -> None: - """Generate docs/llms.txt from schema.json and the Jinja2 template.""" - schema = json.loads(schema_path.read_text()) - defs = schema["$defs"] - - # Extract all data from schema - entry_types = extract_entry_types(defs) - cv_fields = extract_cv_fields(defs) - themes = list(defs["BuiltInDesign"]["discriminator"]["mapping"].keys()) - social_networks = defs["SocialNetworkName"]["enum"] - locales = list(defs["Locale"]["discriminator"]["mapping"].keys()) - - # Locale customizable fields from EnglishLocale (excluding "language") - locale_fields = [ - f - for f in extract_fields(defs["EnglishLocale"], defs) - if f["name"] != "language" - ] - - enums = { - "page_sizes": defs["PageSize"]["enum"], - "alignments": defs["Alignment"]["enum"], - "bullets": defs["Bullet"]["enum"], - "section_title_types": defs["SectionTitleType"]["enum"], - "phone_number_formats": defs["PhoneNumberFormatType"]["enum"], - } - - design_sections = extract_design_sections(defs) - - # Settings fields - settings_fields = extract_fields(defs["Settings"], defs) - - # RenderCommand fields - render_command_fields = extract_fields(defs["RenderCommand"], defs) - - path_placeholders = extract_path_placeholders(defs) - - # Read the example YAML (generated by scripts/update_examples.py) - complete_example = example_path.read_text() - # Strip the yaml-language-server schema comment (first line) - if complete_example.startswith("# yaml-language-server"): - complete_example = complete_example.split("\n", 1)[1] - # Uncomment all commented-out sections so LLMs see the full structure - complete_example = uncomment_yaml(complete_example) - - # Build context - context = { - "entry_types": entry_types, - "cv_fields": cv_fields, - "themes": themes, - "social_networks": social_networks, - "locales": locales, - "locale_fields": locale_fields, - "enums": enums, - "design_sections": design_sections, - "settings_fields": settings_fields, - "render_command_fields": render_command_fields, - "path_placeholders": path_placeholders, - "complete_example": complete_example, - } - - # Render template - env = jinja2.Environment( - loader=jinja2.FileSystemLoader(template_dir), - trim_blocks=True, - lstrip_blocks=True, - ) - template = env.get_template("template.j2.md") - output = template.render(**context) - - # Clean up excessive blank lines (more than 2 consecutive) - output = re.sub(r"\n{4,}", "\n\n\n", output) - - output_path.write_text(output) - print("llms.txt generated successfully.") # NOQA: T201 - - -if __name__ == "__main__": - main() diff --git a/src/rendercv/schema/sample_generator.py b/src/rendercv/schema/sample_generator.py index b09126b9..1bbeb2ff 100644 --- a/src/rendercv/schema/sample_generator.py +++ b/src/rendercv/schema/sample_generator.py @@ -144,23 +144,7 @@ def create_sample_yaml_input_file( name=name, theme=theme, locale=locale ) - # Instead of getting the dictionary with data_model.model_dump() directly, we - # convert it to JSON and then to a dictionary. Because the YAML library we are - # using sometimes has problems with the dictionary returned by model_dump(). - - # We exclude "cv.sections" because the data model automatically generates them. - # The user's "cv.sections" input is actually "cv.sections_input" in the data - # model. It is shown as "cv.sections" in the YAML file because an alias is being - # used. If"cv.sections" were not excluded, the automatically generated - # "cv.sections" would overwrite the "cv.sections_input". "cv.sections" are - # automatically generated from "cv.sections_input" to make the templating - # process easier. "cv.sections_input" exists for the convenience of the user. - # Also, we don't want to show the cv.photo field in the Web app. - data_model_as_json = data_model.model_dump_json( - exclude_none=False, - by_alias=True, - ) - data_model_as_dictionary = json.loads(data_model_as_json) + data_model_as_dictionary = rendercv_model_to_dictionary(data_model) yaml_string = dictionary_to_yaml(data_model_as_dictionary) @@ -207,3 +191,225 @@ def create_sample_yaml_input_file( file_path.write_text(yaml_string, encoding="utf-8") return yaml_string + + +def rendercv_model_to_dictionary(data_model: RenderCVModel) -> dict: + """Convert a RenderCVModel to a plain dictionary via JSON serialization. + + Why: + The YAML library sometimes has problems with the dictionary returned by + Pydantic's model_dump(). Converting through JSON first produces clean + Python dicts that serialize reliably. + + Args: + data_model: Validated RenderCV model. + + Returns: + Plain dictionary representation of the model. + """ + data_model_as_json = data_model.model_dump_json( + exclude_none=False, + by_alias=True, + ) + return json.loads(data_model_as_json) + + +@overload +def create_sample_yaml_file( + *, + dictionary: dict, + file_path: None, +) -> str: ... +@overload +def create_sample_yaml_file( + *, + dictionary: dict, + file_path: pathlib.Path, +) -> None: ... +def create_sample_yaml_file( + *, + dictionary: dict, + file_path: pathlib.Path | None = None, +) -> str | None: + """Convert a dictionary to formatted YAML and optionally write to file. + + Why: + Multiple sample file generators share the same conversion and + file-writing logic. Centralizing it avoids duplication across + create_sample_cv_file, create_sample_design_file, etc. + + Args: + dictionary: Data structure to convert to YAML. + file_path: Optional path to write file. + + Returns: + YAML string if file_path is None, otherwise None after writing file. + """ + yaml_string = dictionary_to_yaml(dictionary) + + # Process for nested bullets: + yaml_string = re.sub(r"(? str: ... +@overload +def create_sample_cv_file( + *, + file_path: pathlib.Path, + name: str = "John Doe", +) -> None: ... +def create_sample_cv_file( + *, + file_path: pathlib.Path | None = None, + name: str = "John Doe", +) -> str | None: + """Generate a sample YAML file containing only the CV section. + + Why: + Standalone CV files let users focus on content separately from + design, locale, and settings configuration. + + Args: + file_path: Optional path to write file. + name: Person's full name. + + Returns: + YAML string if file_path is None, otherwise None after writing file. + """ + data_model = create_sample_rendercv_pydantic_model(name=name) + dictionary = rendercv_model_to_dictionary(data_model) + return create_sample_yaml_file( + dictionary={"cv": dictionary["cv"]}, file_path=file_path + ) + + +@overload +def create_sample_design_file( + *, + file_path: None, + theme: str = "classic", +) -> str: ... +@overload +def create_sample_design_file( + *, + file_path: pathlib.Path, + theme: str = "classic", +) -> None: ... +def create_sample_design_file( + *, + file_path: pathlib.Path | None = None, + theme: str = "classic", +) -> str | None: + """Generate a sample YAML file containing only the design section. + + Why: + Standalone design files let users explore all theme options + without mixing in CV content or locale settings. + + Args: + file_path: Optional path to write file. + theme: Design theme identifier. + + Returns: + YAML string if file_path is None, otherwise None after writing file. + """ + if theme not in available_themes: + message = ( + f"The theme {theme} is not available. The available themes are:" + f" {available_themes}" + ) + raise RenderCVUserError(message) + + data_model = create_sample_rendercv_pydantic_model(theme=theme) + dictionary = rendercv_model_to_dictionary(data_model) + return create_sample_yaml_file( + dictionary={"design": dictionary["design"]}, file_path=file_path + ) + + +@overload +def create_sample_locale_file( + *, + file_path: None, + locale: str = "english", +) -> str: ... +@overload +def create_sample_locale_file( + *, + file_path: pathlib.Path, + locale: str = "english", +) -> None: ... +def create_sample_locale_file( + *, + file_path: pathlib.Path | None = None, + locale: str = "english", +) -> str | None: + """Generate a sample YAML file containing only the locale section. + + Why: + Standalone locale files let users customize language and date + formatting independently from CV content and design. + + Args: + file_path: Optional path to write file. + locale: Language/date format identifier. + + Returns: + YAML string if file_path is None, otherwise None after writing file. + """ + if locale not in available_locales: + message = ( + f"The locale {locale} is not available. The available locales are:" + f" {available_locales}. \n\nBut you can continue with `English`, and then" + " write your own `locale` field in the input file." + ) + raise RenderCVUserError(message) + + data_model = create_sample_rendercv_pydantic_model(locale=locale) + dictionary = rendercv_model_to_dictionary(data_model) + return create_sample_yaml_file( + dictionary={"locale": dictionary["locale"]}, file_path=file_path + ) + + +@overload +def create_sample_settings_file( + *, + file_path: None, +) -> str: ... +@overload +def create_sample_settings_file( + *, + file_path: pathlib.Path, +) -> None: ... +def create_sample_settings_file( + *, + file_path: pathlib.Path | None = None, +) -> str | None: + """Generate a sample YAML file containing only the settings section. + + Why: + Standalone settings files let users configure render options + independently from CV content, design, and locale. + + Args: + file_path: Optional path to write file. + + Returns: + YAML string if file_path is None, otherwise None after writing file. + """ + data_model = create_sample_rendercv_pydantic_model() + dictionary = rendercv_model_to_dictionary(data_model) + return create_sample_yaml_file( + dictionary={"settings": dictionary["settings"]}, file_path=file_path + ) diff --git a/tests/schema/test_sample_generator.py b/tests/schema/test_sample_generator.py index 38e3315a..045fa980 100644 --- a/tests/schema/test_sample_generator.py +++ b/tests/schema/test_sample_generator.py @@ -6,7 +6,12 @@ from rendercv.schema.models.design.built_in_design import available_themes from rendercv.schema.models.locale.locale import available_locales from rendercv.schema.models.rendercv_model import RenderCVModel from rendercv.schema.sample_generator import ( + create_sample_cv_file, + create_sample_design_file, + create_sample_locale_file, create_sample_rendercv_pydantic_model, + create_sample_settings_file, + create_sample_yaml_file, create_sample_yaml_input_file, dictionary_to_yaml, ) @@ -72,6 +77,94 @@ class TestCreateSampleYamlInputFile: create_sample_yaml_input_file(file_path=None, **{key: "invalid"}) +class TestCreateSampleYamlFile: + def test_writes_file_and_returns_matching_content(self, tmp_path): + dictionary = {"key": "value", "list": [1, 2, 3]} + file_path = tmp_path / "test.yaml" + result = create_sample_yaml_file(dictionary=dictionary, file_path=file_path) + + assert file_path.exists() + assert result == file_path.read_text(encoding="utf-8") + + def test_returns_string_without_file(self): + dictionary = {"key": "value"} + result = create_sample_yaml_file(dictionary=dictionary, file_path=None) + + yaml_object = ruamel.yaml.YAML() + assert yaml_object.load(result) == dictionary + + +class TestCreateSampleCvFile: + def test_creates_valid_yaml_with_only_cv_key(self, tmp_path): + file_path = tmp_path / "cv.yaml" + result = create_sample_cv_file(file_path=file_path, name="Jane Smith") + + assert file_path.exists() + assert result == file_path.read_text(encoding="utf-8") + + yaml_object = ruamel.yaml.YAML() + data = yaml_object.load(result) + assert list(data.keys()) == ["cv"] + assert data["cv"]["name"] == "Jane Smith" + + +class TestCreateSampleDesignFile: + @pytest.mark.parametrize( + "theme", + available_themes, + ) + def test_creates_valid_yaml_for_all_themes(self, tmp_path, theme): + file_path = tmp_path / "design.yaml" + result = create_sample_design_file(file_path=file_path, theme=theme) + + assert file_path.exists() + assert result == file_path.read_text(encoding="utf-8") + + yaml_object = ruamel.yaml.YAML() + data = yaml_object.load(result) + assert list(data.keys()) == ["design"] + assert data["design"]["theme"] == theme + + def test_rejects_invalid_theme(self): + with pytest.raises(RenderCVUserError): + create_sample_design_file(file_path=None, theme="invalid") + + +class TestCreateSampleLocaleFile: + @pytest.mark.parametrize( + "locale", + available_locales, + ) + def test_creates_valid_yaml_for_all_locales(self, tmp_path, locale): + file_path = tmp_path / "locale.yaml" + result = create_sample_locale_file(file_path=file_path, locale=locale) + + assert file_path.exists() + assert result == file_path.read_text(encoding="utf-8") + + yaml_object = ruamel.yaml.YAML() + data = yaml_object.load(result) + assert list(data.keys()) == ["locale"] + assert data["locale"]["language"] == locale + + def test_rejects_invalid_locale(self): + with pytest.raises(RenderCVUserError): + create_sample_locale_file(file_path=None, locale="invalid") + + +class TestCreateSampleSettingsFile: + def test_creates_valid_yaml_with_only_settings_key(self, tmp_path): + file_path = tmp_path / "settings.yaml" + result = create_sample_settings_file(file_path=file_path) + + assert file_path.exists() + assert result == file_path.read_text(encoding="utf-8") + + yaml_object = ruamel.yaml.YAML() + data = yaml_object.load(result) + assert list(data.keys()) == ["settings"] + + def test_dictionary_to_yaml(): input_dictionary = { "test_list": [