3 Commits

Author SHA1 Message Date
hikari 23df8a3423 feat: replace formats text with download and print buttons
Node.js CI / CI (push) Failing after 23s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 52s
Adds four action buttons: download YAML, download JSON, print full
resume, and print condensed resume. The condensed print temporarily
hides certifications, projects, and publications sections before
opening the print dialog, restoring them on afterprint.
2026-04-20 12:32:42 -07:00
hikari 7b8cda989b fix: restore print media query functionality
Removes CDN global styles before printing via beforeprint/afterprint
events, preventing the witchy theme from bleeding into print output.
Fixes print layout: centred name and contact info, reset margins for
compact sub-position lists, and footer z-index for screen view.
2026-04-20 12:10:18 -07:00
hikari 8ed53738e0 chore: replace .npmrc with pnpm-workspace.yaml
Node.js CI / CI (push) Failing after 29s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 2m2s
2026-03-02 16:27:35 -08:00
6 changed files with 100 additions and 53 deletions
-25
View File
@@ -1,25 +0,0 @@
# Package Manager Configuration
# Force pnpm usage - breaks npm/yarn intentionally
node-linker=pnpm
# Security: Disable all lifecycle scripts
ignore-scripts=true
enable-pre-post-scripts=false
# Security: Require packages to be 10+ days old before installation
minimum-release-age=14400
# Security: Verify package integrity hashes
verify-store-integrity=true
# Security: Enforce strict trust policies
trust-policy=strict
# Security: Strict peer dependency resolution
strict-peer-dependencies=true
# Performance: Use symlinks for node_modules
symlink=true
# Lockfile: Ensure lockfile is not modified during install
frozen-lockfile=false
+1 -1
View File
@@ -21,6 +21,6 @@
"eslint": "9.24.0", "eslint": "9.24.0",
"tsx": "4.19.3", "tsx": "4.19.3",
"vitest": "3.1.1", "vitest": "3.1.1",
"yaml": "2.8.2" "yaml": "2.7.1"
} }
} }
+24 -24
View File
@@ -10,7 +10,7 @@ importers:
devDependencies: devDependencies:
'@nhcarrigan/eslint-config': '@nhcarrigan/eslint-config':
specifier: 5.2.0 specifier: 5.2.0
version: 5.2.0(@typescript-eslint/utils@8.29.1(eslint@9.24.0)(typescript@5.7.3))(eslint@9.24.0)(playwright@1.51.1)(react@19.1.0)(typescript@5.7.3)(vitest@3.1.1(@types/node@22.14.0)(tsx@4.19.3)(yaml@2.8.2)) version: 5.2.0(@typescript-eslint/utils@8.29.1(eslint@9.24.0)(typescript@5.7.3))(eslint@9.24.0)(playwright@1.51.1)(react@19.1.0)(typescript@5.7.3)(vitest@3.1.1(@types/node@22.14.0)(tsx@4.19.3)(yaml@2.7.1))
'@types/node': '@types/node':
specifier: 22.14.0 specifier: 22.14.0
version: 22.14.0 version: 22.14.0
@@ -25,10 +25,10 @@ importers:
version: 4.19.3 version: 4.19.3
vitest: vitest:
specifier: 3.1.1 specifier: 3.1.1
version: 3.1.1(@types/node@22.14.0)(tsx@4.19.3)(yaml@2.8.2) version: 3.1.1(@types/node@22.14.0)(tsx@4.19.3)(yaml@2.7.1)
yaml: yaml:
specifier: 2.8.2 specifier: 2.7.1
version: 2.8.2 version: 2.7.1
packages: packages:
@@ -2274,9 +2274,9 @@ packages:
resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==}
engines: {node: '>=12'} engines: {node: '>=12'}
yaml@2.8.2: yaml@2.7.1:
resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} resolution: {integrity: sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==}
engines: {node: '>= 14.6'} engines: {node: '>= 14'}
hasBin: true hasBin: true
yocto-queue@0.1.0: yocto-queue@0.1.0:
@@ -2674,7 +2674,7 @@ snapshots:
'@jridgewell/sourcemap-codec@1.5.0': {} '@jridgewell/sourcemap-codec@1.5.0': {}
'@nhcarrigan/eslint-config@5.2.0(@typescript-eslint/utils@8.29.1(eslint@9.24.0)(typescript@5.7.3))(eslint@9.24.0)(playwright@1.51.1)(react@19.1.0)(typescript@5.7.3)(vitest@3.1.1(@types/node@22.14.0)(tsx@4.19.3)(yaml@2.8.2))': '@nhcarrigan/eslint-config@5.2.0(@typescript-eslint/utils@8.29.1(eslint@9.24.0)(typescript@5.7.3))(eslint@9.24.0)(playwright@1.51.1)(react@19.1.0)(typescript@5.7.3)(vitest@3.1.1(@types/node@22.14.0)(tsx@4.19.3)(yaml@2.7.1))':
dependencies: dependencies:
'@eslint-community/eslint-plugin-eslint-comments': 4.4.1(eslint@9.24.0) '@eslint-community/eslint-plugin-eslint-comments': 4.4.1(eslint@9.24.0)
'@eslint/compat': 1.2.4(eslint@9.24.0) '@eslint/compat': 1.2.4(eslint@9.24.0)
@@ -2683,7 +2683,7 @@ snapshots:
'@stylistic/eslint-plugin': 2.12.1(eslint@9.24.0)(typescript@5.7.3) '@stylistic/eslint-plugin': 2.12.1(eslint@9.24.0)(typescript@5.7.3)
'@typescript-eslint/eslint-plugin': 8.19.0(@typescript-eslint/parser@8.19.0(eslint@9.24.0)(typescript@5.7.3))(eslint@9.24.0)(typescript@5.7.3) '@typescript-eslint/eslint-plugin': 8.19.0(@typescript-eslint/parser@8.19.0(eslint@9.24.0)(typescript@5.7.3))(eslint@9.24.0)(typescript@5.7.3)
'@typescript-eslint/parser': 8.19.0(eslint@9.24.0)(typescript@5.7.3) '@typescript-eslint/parser': 8.19.0(eslint@9.24.0)(typescript@5.7.3)
'@vitest/eslint-plugin': 1.1.24(@typescript-eslint/utils@8.29.1(eslint@9.24.0)(typescript@5.7.3))(eslint@9.24.0)(typescript@5.7.3)(vitest@3.1.1(@types/node@22.14.0)(tsx@4.19.3)(yaml@2.8.2)) '@vitest/eslint-plugin': 1.1.24(@typescript-eslint/utils@8.29.1(eslint@9.24.0)(typescript@5.7.3))(eslint@9.24.0)(typescript@5.7.3)(vitest@3.1.1(@types/node@22.14.0)(tsx@4.19.3)(yaml@2.7.1))
eslint: 9.24.0 eslint: 9.24.0
eslint-plugin-deprecation: 3.0.0(eslint@9.24.0)(typescript@5.7.3) eslint-plugin-deprecation: 3.0.0(eslint@9.24.0)(typescript@5.7.3)
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.19.0(eslint@9.24.0)(typescript@5.7.3))(eslint@9.24.0) eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.19.0(eslint@9.24.0)(typescript@5.7.3))(eslint@9.24.0)
@@ -2696,7 +2696,7 @@ snapshots:
playwright: 1.51.1 playwright: 1.51.1
react: 19.1.0 react: 19.1.0
typescript: 5.7.3 typescript: 5.7.3
vitest: 3.1.1(@types/node@22.14.0)(tsx@4.19.3)(yaml@2.8.2) vitest: 3.1.1(@types/node@22.14.0)(tsx@4.19.3)(yaml@2.7.1)
transitivePeerDependencies: transitivePeerDependencies:
- '@typescript-eslint/utils' - '@typescript-eslint/utils'
- eslint-import-resolver-typescript - eslint-import-resolver-typescript
@@ -2955,13 +2955,13 @@ snapshots:
'@typescript-eslint/types': 8.29.1 '@typescript-eslint/types': 8.29.1
eslint-visitor-keys: 4.2.0 eslint-visitor-keys: 4.2.0
'@vitest/eslint-plugin@1.1.24(@typescript-eslint/utils@8.29.1(eslint@9.24.0)(typescript@5.7.3))(eslint@9.24.0)(typescript@5.7.3)(vitest@3.1.1(@types/node@22.14.0)(tsx@4.19.3)(yaml@2.8.2))': '@vitest/eslint-plugin@1.1.24(@typescript-eslint/utils@8.29.1(eslint@9.24.0)(typescript@5.7.3))(eslint@9.24.0)(typescript@5.7.3)(vitest@3.1.1(@types/node@22.14.0)(tsx@4.19.3)(yaml@2.7.1))':
dependencies: dependencies:
'@typescript-eslint/utils': 8.29.1(eslint@9.24.0)(typescript@5.7.3) '@typescript-eslint/utils': 8.29.1(eslint@9.24.0)(typescript@5.7.3)
eslint: 9.24.0 eslint: 9.24.0
optionalDependencies: optionalDependencies:
typescript: 5.7.3 typescript: 5.7.3
vitest: 3.1.1(@types/node@22.14.0)(tsx@4.19.3)(yaml@2.8.2) vitest: 3.1.1(@types/node@22.14.0)(tsx@4.19.3)(yaml@2.7.1)
'@vitest/expect@3.1.1': '@vitest/expect@3.1.1':
dependencies: dependencies:
@@ -2970,13 +2970,13 @@ snapshots:
chai: 5.2.0 chai: 5.2.0
tinyrainbow: 2.0.0 tinyrainbow: 2.0.0
'@vitest/mocker@3.1.1(vite@6.2.5(@types/node@22.14.0)(tsx@4.19.3)(yaml@2.8.2))': '@vitest/mocker@3.1.1(vite@6.2.5(@types/node@22.14.0)(tsx@4.19.3)(yaml@2.7.1))':
dependencies: dependencies:
'@vitest/spy': 3.1.1 '@vitest/spy': 3.1.1
estree-walker: 3.0.3 estree-walker: 3.0.3
magic-string: 0.30.17 magic-string: 0.30.17
optionalDependencies: optionalDependencies:
vite: 6.2.5(@types/node@22.14.0)(tsx@4.19.3)(yaml@2.8.2) vite: 6.2.5(@types/node@22.14.0)(tsx@4.19.3)(yaml@2.7.1)
'@vitest/pretty-format@3.1.1': '@vitest/pretty-format@3.1.1':
dependencies: dependencies:
@@ -3222,7 +3222,7 @@ snapshots:
dependencies: dependencies:
'@cspell/cspell-types': 8.18.1 '@cspell/cspell-types': 8.18.1
comment-json: 4.2.5 comment-json: 4.2.5
yaml: 2.8.2 yaml: 2.7.1
cspell-dictionary@8.18.1: cspell-dictionary@8.18.1:
dependencies: dependencies:
@@ -4662,13 +4662,13 @@ snapshots:
spdx-correct: 3.2.0 spdx-correct: 3.2.0
spdx-expression-parse: 3.0.1 spdx-expression-parse: 3.0.1
vite-node@3.1.1(@types/node@22.14.0)(tsx@4.19.3)(yaml@2.8.2): vite-node@3.1.1(@types/node@22.14.0)(tsx@4.19.3)(yaml@2.7.1):
dependencies: dependencies:
cac: 6.7.14 cac: 6.7.14
debug: 4.4.0 debug: 4.4.0
es-module-lexer: 1.6.0 es-module-lexer: 1.6.0
pathe: 2.0.3 pathe: 2.0.3
vite: 6.2.5(@types/node@22.14.0)(tsx@4.19.3)(yaml@2.8.2) vite: 6.2.5(@types/node@22.14.0)(tsx@4.19.3)(yaml@2.7.1)
transitivePeerDependencies: transitivePeerDependencies:
- '@types/node' - '@types/node'
- jiti - jiti
@@ -4683,7 +4683,7 @@ snapshots:
- tsx - tsx
- yaml - yaml
vite@6.2.5(@types/node@22.14.0)(tsx@4.19.3)(yaml@2.8.2): vite@6.2.5(@types/node@22.14.0)(tsx@4.19.3)(yaml@2.7.1):
dependencies: dependencies:
esbuild: 0.25.2 esbuild: 0.25.2
postcss: 8.5.3 postcss: 8.5.3
@@ -4692,12 +4692,12 @@ snapshots:
'@types/node': 22.14.0 '@types/node': 22.14.0
fsevents: 2.3.3 fsevents: 2.3.3
tsx: 4.19.3 tsx: 4.19.3
yaml: 2.8.2 yaml: 2.7.1
vitest@3.1.1(@types/node@22.14.0)(tsx@4.19.3)(yaml@2.8.2): vitest@3.1.1(@types/node@22.14.0)(tsx@4.19.3)(yaml@2.7.1):
dependencies: dependencies:
'@vitest/expect': 3.1.1 '@vitest/expect': 3.1.1
'@vitest/mocker': 3.1.1(vite@6.2.5(@types/node@22.14.0)(tsx@4.19.3)(yaml@2.8.2)) '@vitest/mocker': 3.1.1(vite@6.2.5(@types/node@22.14.0)(tsx@4.19.3)(yaml@2.7.1))
'@vitest/pretty-format': 3.1.1 '@vitest/pretty-format': 3.1.1
'@vitest/runner': 3.1.1 '@vitest/runner': 3.1.1
'@vitest/snapshot': 3.1.1 '@vitest/snapshot': 3.1.1
@@ -4713,8 +4713,8 @@ snapshots:
tinyexec: 0.3.2 tinyexec: 0.3.2
tinypool: 1.0.2 tinypool: 1.0.2
tinyrainbow: 2.0.0 tinyrainbow: 2.0.0
vite: 6.2.5(@types/node@22.14.0)(tsx@4.19.3)(yaml@2.8.2) vite: 6.2.5(@types/node@22.14.0)(tsx@4.19.3)(yaml@2.7.1)
vite-node: 3.1.1(@types/node@22.14.0)(tsx@4.19.3)(yaml@2.8.2) vite-node: 3.1.1(@types/node@22.14.0)(tsx@4.19.3)(yaml@2.7.1)
why-is-node-running: 2.3.0 why-is-node-running: 2.3.0
optionalDependencies: optionalDependencies:
'@types/node': 22.14.0 '@types/node': 22.14.0
@@ -4790,6 +4790,6 @@ snapshots:
xdg-basedir@5.1.0: {} xdg-basedir@5.1.0: {}
yaml@2.8.2: {} yaml@2.7.1: {}
yocto-queue@0.1.0: {} yocto-queue@0.1.0: {}
+21
View File
@@ -0,0 +1,21 @@
# Security
# Do not execute any scripts of installed packages (project scripts still run)
ignoreDepScripts: true
# Do not automatically run pre/post scripts (e.g. preinstall, postbuild)
enablePrePostScripts: false
# Only allow packages published at least 10 days ago (reduces risk of compromised packages)
minimumReleaseAge: 14400
# Fail if a package's trust level has decreased compared to previous releases
trustPolicy: no-downgrade
# Ignore trust policy for packages published more than 1 year ago (predates provenance signing)
trustPolicyIgnoreAfter: 525960
# Fail if there are missing or invalid peer dependencies
strictPeerDependencies: true
# Prevent transitive dependencies from using exotic sources (git repos, direct tarball URLs)
blockExoticSubdeps: true
# Lockfile
# Allow the lockfile to be updated during install (set to true in CI for stricter reproducibility)
preferFrozenLockfile: false
+42 -3
View File
@@ -28,6 +28,42 @@ const htmlBeginning = `<!DOCTYPE html>
const htmlEnd = ` </main> const htmlEnd = ` </main>
</body> </body>
<script src="./dates.js"></script> <script src="./dates.js"></script>
<script>
var _cdnStyle = null;
var _condensedSections = [];
window.addEventListener("beforeprint", function() {
_cdnStyle = document.getElementById("nhcarrigan-global-styles");
if (_cdnStyle) { _cdnStyle.remove(); }
});
window.addEventListener("afterprint", function() {
if (_cdnStyle) { document.head.appendChild(_cdnStyle); _cdnStyle = null; }
_condensedSections.forEach(function(el) { el.style.display = ""; });
_condensedSections = [];
});
function downloadFile(url, filename) {
fetch(url)
.then(function(r) { return r.blob(); })
.then(function(blob) {
var a = document.createElement("a");
a.href = URL.createObjectURL(blob);
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(a.href);
});
}
function printCondensed() {
["certifications", "projects", "publications"].forEach(function(id) {
var el = document.getElementById(id);
if (el) { el.style.display = "none"; _condensedSections.push(el); }
});
window.print();
}
</script>
</html>`; </html>`;
const request = await fetch( const request = await fetch(
@@ -114,9 +150,12 @@ const heading = `<h1>${yaml.name}</h1>
<a href="#projects">Projects</a> | <a href="#projects">Projects</a> |
<a href="#publications">Publications</a> | <a href="#publications">Publications</a> |
</p> </p>
<p id="formats"> <div id="formats">
Get this resume in <a href="https://data.nhcarrigan.com/resume.yml">YAML</a> or <a href="https://data.nhcarrigan.com/resume.json">JSON</a> format. You can also <span style="text-decoration: underline" onclick="window.print();return false;">print</span> the resume (and optionally save as a PDF). <button onclick="downloadFile('https://data.nhcarrigan.com/resume.yml', 'resume.yml')">Download YAML</button>
</p> <button onclick="downloadFile('https://data.nhcarrigan.com/resume.json', 'resume.json')">Download JSON</button>
<button onclick="window.print()">Print Resume</button>
<button onclick="printCondensed()">Print Condensed Resume</button>
</div>
<p class="cta"> <p class="cta">
Interested in hiring me? Interested in hiring me?
<a href="https://testimonials.nhcarrigan.com" target="_blank" <a href="https://testimonials.nhcarrigan.com" target="_blank"
+12
View File
@@ -42,6 +42,9 @@ hr {
box-shadow: 4px 4px 4px rgba(0, 0, 0, 0.7); box-shadow: 4px 4px 4px rgba(0, 0, 0, 0.7);
margin-bottom: 10px; margin-bottom: 10px;
} }
footer {
z-index: 1;
}
} }
@media print { @media print {
:root { :root {
@@ -50,6 +53,15 @@ hr {
* { * {
color: black; color: black;
font-family: "Times New Roman", serif; font-family: "Times New Roman", serif;
margin: 0;
padding: 0;
}
h1 {
text-align: center;
font-size: 1.8rem;
}
.info {
text-align: center;
} }
video, video,
footer, footer,