Skip to content

Commit ea25ae6

Browse files
committed
feat: Improved build and watch scripts, added print button
1 parent 8449bb6 commit ea25ae6

18 files changed

Lines changed: 186 additions & 346 deletions

.github/workflows/build.yaml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: build-pdfs
1+
name: build-page
22
on:
33
push:
44
tags:
@@ -29,9 +29,18 @@ jobs:
2929
cp -r fonts _site/
3030
3131
- name: Upload Site Artifacts
32-
uses: actions/upload-pages-atrifact@v3
32+
id: deployment
33+
uses: actions/upload-pages-artifact@v3
3334
with:
3435
path: _site
3536

37+
deploy:
38+
environment:
39+
name: github-pages
40+
rl: ${{ steps.deployment.outputs.page_url }}
41+
runs-on: ubuntu-latest
42+
needs: build
43+
steps:
3644
- name: Deploy to GitHub Pages
45+
id: deployment
3746
uses: actions/deploy-pages@v4

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](http://keepachangelog.com/)
66
and this project adheres to [Semantic Versioning](http://semver.org/).
77

8+
## v5.1.0 - 2025-03-07
9+
* More work on the build and watch script
10+
* A little more styling
11+
* Added a print/download button
12+
* Working on deploy action
13+
814
## v5.0.0 - 2025-03-06
915
* Converted to just building an HTML page (will figure out pdf later)
1016
* Added github action to deploy page

pyproject.toml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,21 @@
11
[project]
22
name = "cv"
3-
version = "5.0.0"
3+
version = "5.1.0"
44
description = "Tools for building career documents."
55
readme = "README.md"
66
requires-python = ">=3.13"
77
dependencies = [
88
"auto-name-enum>=2.0.0",
99
"httpx>=0.28.1",
1010
"inflection>=0.5.1",
11-
"livereload>=2.7.1",
1211
"loguru>=0.7.3",
1312
"markdown>=3.7",
1413
"openai>=1.65.1",
15-
"playwright>=1.50.0",
1614
"py-buzz>=5.0.2",
1715
"pydantic-settings>=2.8.1",
1816
"snick>=1.4.1",
1917
"typer>=0.13.0",
20-
"weasyprint==64.1",
18+
"watchfiles>=1.0.4",
2119
]
2220

2321
[tool.uv]
Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
from logging import debug
12
from pathlib import Path
23
from typing import Annotated
34
from xml.dom.minidom import Document, Element, parseString, Node
5+
from xml.dom.xmlbuilder import DOMBuilder
46

57
import typer
68
from buzz import require_condition
@@ -24,15 +26,18 @@ def get_html_path() -> Path:
2426
return Path("index.html")
2527

2628

27-
def build_page(color: ColorScheme) -> Path:
28-
logger.debug(f"Building PDF using scheme {color}")
29+
def build_page(color: ColorScheme, debug: bool = False) -> Path:
30+
logger.debug(f"Building page using scheme {color}")
2931
md_path = Path("README.md")
3032

3133
html_content = markdown(md_path.read_text())
3234
html_content = fill_html(html_content, color)
3335
html_content = inject_divs(html_content)
3436
html_content = inject_photo(html_content)
3537
html_content = tag_emojis(html_content)
38+
html_content = add_download_button(html_content)
39+
if debug:
40+
html_content = inject_live_script(html_content)
3641

3742
html_path = get_html_path()
3843
logger.debug(f"Writing HTML file to {html_path}")
@@ -59,10 +64,13 @@ def _pretty_html(dom: Document) -> str:
5964
return "\n".join([l for l in dom.toprettyxml(indent=" ").split("\n") if l.strip()])
6065

6166

62-
def find_element(parent: Document | Element, tag_name: str, class_name: str) -> Node:
67+
def find_element(parent: Document | Element, tag_name: str, class_name: str | None = None) -> Node:
6368
elements = []
6469
for node in parent.getElementsByTagName(tag_name):
65-
if node.getAttribute("class") == class_name:
70+
if class_name:
71+
if node.getAttribute("class") == class_name:
72+
elements.append(node)
73+
else:
6674
elements.append(node)
6775
require_condition(
6876
len(elements) == 1,
@@ -77,10 +85,10 @@ def fill_html(html: str, color: ColorScheme) -> str:
7785
html = f"""
7886
<html>
7987
<head>
80-
<meta charset="UTF-8" />
88+
<meta charset="utf-8" />
8189
<title>Tucker Beck Resumé</title>
82-
<link rel="stylesheet" href="css/styles.css" />
83-
<link rel="stylesheet" href="css/{color}.css" />
90+
<link rel="stylesheet" type="text/css" href="static/css/styles.css" />
91+
<link rel="stylesheet" type="text/css" href="static/css/{color}.css" />
8492
</head>
8593
<body>
8694
{html}
@@ -94,13 +102,13 @@ def fill_html(html: str, color: ColorScheme) -> str:
94102
def inject_photo(html: str) -> str:
95103
logger.debug("Injecting photo into HTML")
96104
dom = parseString(html)
97-
header_div = find_element(dom, "div", "header")
105+
header_div = find_element(dom, "div", class_name="header")
98106

99107
header_photo_div = dom.createElement("div")
100108
header_photo_div.setAttribute("class", "header-photo")
101109
header_div.insertBefore(header_photo_div, header_div.firstChild)
102110
header_photo_img = dom.createElement("img")
103-
header_photo_img.setAttribute("src", "images/me.png")
111+
header_photo_img.setAttribute("src", "static/images/me.png")
104112
header_photo_img.setAttribute("alt", "Tucker Beck Photo")
105113
header_photo_div.appendChild(header_photo_img)
106114

@@ -170,7 +178,7 @@ def tag_emojis(html: str) -> str:
170178

171179
dom = parseString(html)
172180

173-
contact_list_div = find_element(dom, "div", "contact_list")
181+
contact_list_div = find_element(dom, "div", class_name="contact_list")
174182
li_elements = contact_list_div.getElementsByTagName("li")
175183
for li in li_elements:
176184
text_node = li.firstChild
@@ -186,3 +194,40 @@ def tag_emojis(html: str) -> str:
186194
_move_nodes_in_place(contact_div.nextSibling, li.childNodes[-1], contact_div)
187195

188196
return _pretty_html(dom)
197+
198+
199+
def add_download_button(html: str) -> str:
200+
logger.debug("Adding download/print button")
201+
202+
dom = parseString(html)
203+
204+
sidebar_div = find_element(dom, "div", class_name="sidebar")
205+
206+
text = dom.createTextNode("Print (Download PDF)")
207+
208+
span = dom.createElement("span")
209+
span.setAttribute("id", "button-span")
210+
span.appendChild(text)
211+
212+
button = dom.createElement("button")
213+
button.setAttribute("id", "download-button")
214+
button.setAttribute("onClick", "window.print()")
215+
button.setAttribute("class", "no-print")
216+
button.appendChild(span)
217+
sidebar_div.appendChild(button)
218+
219+
return _pretty_html(dom)
220+
221+
222+
def inject_live_script(html: str) -> str:
223+
logger.debug("Adding live reload script")
224+
225+
dom = parseString(html)
226+
227+
head = find_element(dom, "head")
228+
meta = dom.createElement("meta")
229+
meta.setAttribute("http-equiv", "refresh")
230+
meta.setAttribute("content", "3")
231+
head.appendChild(meta)
232+
233+
return _pretty_html(dom)

src/app/logging.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ def init_logs(verbose=False):
1414
logger.debug("Logging initialized")
1515

1616

17-
def hijack_weasyprint():
18-
logging.basicConfig(handlers=[InterceptHandler()], level=0)
17+
#logging.basicConfig(handlers=[InterceptHandler()], level=0)
1918

2019

2120
class InterceptHandler(logging.Handler):

src/app/main.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from app.version import show_version
44
from app.logging import init_logs
5-
from app.resume import cli as resume_cli
5+
from app.build import cli as build_cli
66
from app.watch import cli as watch_cli
77

88

@@ -26,7 +26,7 @@ def main(
2626
init_logs(verbose=verbose)
2727

2828

29-
cli.add_typer(resume_cli, name="resume")
29+
cli.add_typer(build_cli, name="build")
3030
cli.add_typer(watch_cli, name="watch")
3131

3232

src/app/watch.py

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
1-
import os
1+
import subprocess
22
from pathlib import Path
33
from typing import Annotated
44

55
import typer
6-
from livereload import Server
76
from loguru import logger
8-
from weasyprint import html
7+
from watchfiles import run_process
98

109
from app.constants import ColorScheme
11-
from app.resume import build_page, __file__ as resume_module_file, get_html_path
10+
from app.build import build, build_page, __file__ as build_module_file, get_html_path
1211

1312

1413
cli = typer.Typer()
@@ -18,20 +17,17 @@
1817
def page(
1918
color: Annotated[ColorScheme, typer.Option(help="Render with color scheme.")] = ColorScheme.light,
2019
):
21-
def _build():
22-
build_page(color)
23-
2420
html_path = get_html_path()
25-
_build()
26-
server = Server()
27-
logger.debug(f"Adding README.md to watchers")
21+
static_path = Path("static")
2822
readme_path = Path("README.md")
29-
server.watch(readme_path, func=_build)
30-
etc_path = Path("etc/css")
31-
for css_path in etc_path.glob("*.css"):
32-
logger.debug(f"Adding {css_path} to watchers")
33-
server.watch(str(css_path), func=_build)
34-
logger.debug(f"Adding {resume_module_file} to watchers")
35-
server.watch(resume_module_file, func=_build)
36-
37-
server.serve(default_filename=str(html_path), open_url_delay=0.25, live_css=False)
23+
24+
watch_paths = [build_module_file, html_path, static_path, readme_path]
25+
26+
logger.debug("Triggering initial build")
27+
build_page(color, debug=True)
28+
29+
logger.debug("Opening browser to page")
30+
subprocess.run(["explorer.exe", str(html_path)])
31+
32+
logger.debug(f"Watching for changes in {watch_paths}")
33+
run_process(*watch_paths, target=build_page, args=(color,), kwargs=dict(debug=True))
File renamed without changes.
File renamed without changes.

css/light.css renamed to static/css/light.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@
5656
--sidebar-text-color: var(--light-gray-6);
5757
--sidebar-link-color: var(--light-red-4);
5858
--sidebar-background-color: var(--light-gray-1);
59+
--sidebar-button-color: var(--light-gray-6);
60+
--sidebar-button-hover-color: var(--light-gray-7);
61+
--sidebar-button-clicked-color: var(--light-blue-5);
62+
--sidebar-button-text-color: var(--light-red-2);
5963

6064
--main-title-color: var(--light-blue-5);
6165
--main-heading-color: var(--light-red-6);

0 commit comments

Comments
 (0)