commit 4779c0142b2db1a9a89d6448710d017da3e3b15e Author: Youwen Wu Date: Tue Dec 31 19:17:37 2024 -0800 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fcfc4a1 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +result* diff --git a/cv.pdf b/cv.pdf new file mode 100644 index 0000000..d7ffc83 Binary files /dev/null and b/cv.pdf differ diff --git a/cv.typ b/cv.typ new file mode 100644 index 0000000..306ed64 --- /dev/null +++ b/cv.typ @@ -0,0 +1,120 @@ +#import "./template.typ": * + +#show: moderner-cv.with( + name: "Youwen Wu", + lang: "en", + subtitle: [Curriculum Vitæ], + social: ( + // predefined socials: phone, email, github, linkedin, x, bluesky + email: "youwen@ucsb.edu", + github: "youwen5", + linkedin: "Youwen Wu", + // custom socials: (icon, link, body) + // any fontawesome icon can be used: https://fontawesome.com/search + website: ("link", "https://youwen.dev", "youwen.dev"), + ), +) + += Education + +#cv-entry( + date: [2024 -- 2028], + title: [B.S. Mathematics], + employer: [University of California, Santa Barbara], +)[4.0/4.0] + +#cv-entry( + date: [2024 -- 2028], + title: [B.S. Computer Science], + employer: [University of California, Santa Barbara], +)[4.0/4.0] + += Experience + +#cv-entry( + date: [4/2022 -- 7/2023], + employer: [UCSB Robotics Lab], + title: [Systems programmer], + [#linebreak()#text( + 10pt, + [ + Designed reproducible and purely functional hermetic build systems. + Creating 3D simulacrums of laboratory experiments that are reproduced in + real life, using C++, React, and Three.js. + ], + )], +) + +#cv-entry( + date: [09/2022 -- 06/2024], + employer: [FIRST Robotics Team 1280], + title: [Artificial Intelligence Lead], + [#linebreak()#text( + 10pt, + [ + Worked on autonomous decision making and path planning algorithms. + Replaced the venerable _BozoAuto_ autonomous subroutine with the + _DeepBozo_ autonomous suite. Designed a novel robot control dashboard + with 3D visualization using Rust, Tauri, and Svelte. + ], + )], +) + += Skills + +#cv-line[ + Languages +][ + Haskell, Rust, TypeScript, Bash, Python, Nu, Nix, C++. +] + +#cv-double-item[ + Development +][ + Tauri, React, Svelte, Linux. +][Deployment][ + Nix and NixOS, Docker and NixOS based service deployment and system administration. +] + +#cv-line[Other][ + Git and Mercurial Version Control. +] + += Papers + +== Preprints + +#cv-paper-item( + name: "The DeepBozo Report", + date: [1/2024], + journal: [ + #link("https://www.eexiv.org/document/view/deepbozo-report")[eeXiv:10j4yyvp4so9.] + ], +) + +#cv-paper-item( + name: "eeXiv Whitepaper", + date: [3/2024], + journal: [ + #link("https://www.eexiv.org/document/view/eexiv-white-paper")[eeXiv:gc3lso8s9tyb] + ], +) + +#cv-paper-item( + name: "The LiDAR Whitepaper", + date: [2/2024], + journal: [ + #link("https://www.eexiv.org/document/view/lidar-whitepaper")[eeXiv:my8nruhlzjsq] + ], +) + += Research Projects + +#cv-line( + [5/2024], + [Split Cycle: A New Condorcet Consistent Voting Method Independent of Clones and Immune to Spoilers], +) + += Languages + +#cv-double-item[English][Native][Chinese][Fluent] diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..7bc4151 --- /dev/null +++ b/flake.lock @@ -0,0 +1,116 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "font-awesome": { + "flake": false, + "locked": { + "lastModified": 1734384716, + "narHash": "sha256-MaJG96kYj8ukJVyqOTDpkHH/eWr/ZlbVKk9AvJM7ub4=", + "owner": "FortAwesome", + "repo": "Font-Awesome", + "rev": "af620534bfc3c2d4cbefcfeec29603bbe7809e64", + "type": "github" + }, + "original": { + "owner": "FortAwesome", + "repo": "Font-Awesome", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1735471104, + "narHash": "sha256-0q9NGQySwDQc7RhAV2ukfnu7Gxa5/ybJ2ANT8DQrQrs=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "88195a94f390381c6afcdaa933c2f6ff93959cb4", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "font-awesome": "font-awesome", + "nixpkgs": "nixpkgs", + "typix": "typix", + "typst-packages": "typst-packages" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "typix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1735477665, + "narHash": "sha256-Te7CauUV4NbcKurJpARZ+lwZvN5oqcmzeyVjLX59SXg=", + "owner": "loqusion", + "repo": "typix", + "rev": "fae09f6c69a9e441bfed863a7ba9867f296e8fed", + "type": "github" + }, + "original": { + "owner": "loqusion", + "repo": "typix", + "type": "github" + } + }, + "typst-packages": { + "flake": false, + "locked": { + "lastModified": 1734773545, + "narHash": "sha256-xpjaqcgLLvTyxDaFDWrHxAlvJCXFisELZNrpdHYGV9o=", + "owner": "typst", + "repo": "packages", + "rev": "b6c3c7cfffda6661dc8eb5de38602fcec5aa72d3", + "type": "github" + }, + "original": { + "owner": "typst", + "repo": "packages", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..bcfc7f9 --- /dev/null +++ b/flake.nix @@ -0,0 +1,127 @@ +{ + description = "A Typst project"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + + typix = { + url = "github:loqusion/typix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + flake-utils.url = "github:numtide/flake-utils"; + + font-awesome = { + url = "github:FortAwesome/Font-Awesome"; + flake = false; + }; + + typst-packages = { + url = "github:typst/packages"; + flake = false; + }; + }; + + outputs = + inputs@{ + nixpkgs, + typix, + flake-utils, + self, + ... + }: + flake-utils.lib.eachDefaultSystem ( + system: + let + pkgs = nixpkgs.legacyPackages.${system}; + inherit (pkgs) lib; + + typixLib = typix.lib.${system}; + + src = typixLib.cleanTypstSource ./.; + commonArgs = { + typstSource = "cv.typ"; + + fontPaths = [ + # Add paths to fonts here + # "${pkgs.roboto}/share/fonts/truetype" + "${inputs.font-awesome}/otfs" + ]; + + virtualPaths = [ + # Add paths that must be locally accessible to typst here + { + dest = "icons"; + src = "${inputs.font-awesome}/svgs/regular"; + } + ]; + + XDG_CACHE_HOME = typstPackagesCache; + + SOURCE_DATE_EPOCH = builtins.toString self.lastModified; + }; + + typstPackagesSrc = "${inputs.typst-packages}/packages"; + + typstPackagesCache = pkgs.stdenvNoCC.mkDerivation { + name = "typst-packages-cache"; + src = typstPackagesSrc; + dontBuild = true; + installPhase = '' + mkdir -p "$out/typst/packages" + cp -LR --reflink=auto --no-preserve=mode -t "$out/typst/packages" "$src"/* + ''; + }; + + # Compile a Typst project, *without* copying the result + # to the current directory + build-drv = typixLib.buildTypstProject ( + commonArgs + // { + inherit src; + } + ); + + # Compile a Typst project, and then copy the result + # to the current directory + build-script = typixLib.buildTypstProjectLocal ( + commonArgs + // { + inherit src; + } + ); + + # Watch a project and recompile on changes + watch-script = typixLib.watchTypstProject commonArgs; + in + { + checks = { + inherit build-drv build-script watch-script; + }; + + packages.default = build-drv; + + apps = rec { + default = watch; + build = flake-utils.lib.mkApp { + drv = build-script; + }; + watch = flake-utils.lib.mkApp { + drv = watch-script; + }; + }; + + devShells.default = typixLib.devShell { + inherit (commonArgs) fontPaths virtualPaths; + packages = [ + # WARNING: Don't run `typst-build` directly, instead use `nix run .#build` + # See https://github.com/loqusion/typix/issues/2 + # build-script + watch-script + # More packages can be added here, like typstfmt + # pkgs.typstfmt + ]; + }; + } + ); +} diff --git a/template.typ b/template.typ new file mode 100644 index 0000000..d92f908 --- /dev/null +++ b/template.typ @@ -0,0 +1,220 @@ +#import "@preview/fontawesome:0.5.0": * + +#let _cv-line(left, right, ..args) = { + set block(below: 0pt) + table( + columns: (1fr, 5fr), + stroke: none, + ..args.named(), + left, + right, + ) +} +#let moderncv-blue = rgb("#3973AF") + +#let _header( + title: [], + subtitle: [], + socials: (:), +) = { + let titleStack = stack( + dir: ttb, + spacing: 4.2em, + text(size: 30pt, title), + text(size: 16pt, subtitle), + ) + + let social(icon, link_prefix, username) = [ + #stack( + dir: ltr, + spacing: 0.5em, + fa-icon(icon), + link(link_prefix + username)[#username], + ) + ] + + let custom-social(icon, dest, body) = [ + #stack( + dir: ltr, + spacing: 0.6em, + fa-icon(icon), + link(dest, body), + ) + ] + + let socialsDict = ( + // key: (faIcon, linkPrefix) + phone: ("phone", "tel:"), + email: ("envelope", "mailto:"), + github: ("github", "https://github.com/"), + linkedin: ("linkedin", "https://linkedin.com/in/"), + x: ("x-twitter", "https://twitter.com/"), + bluesky: ("bluesky", "https://bsky.app/profile/"), + ) + + let socialsList = () + for entry in socials { + assert(type(entry) == array, message: "Invalid social entry type.") + assert(entry.len() == 2, message: "Invalid social entry length.") + let (key, value) = entry + if type(value) == str { + if key not in socialsDict { + panic("Unknown social key: " + key) + } + let (icon, linkPrefix) = socialsDict.at(key) + socialsList.push(social(icon, linkPrefix, value)) + } else if type(value) == array { + assert(value.len() == 3, message: "Invalid social entry: " + key) + let (icon, dest, body) = value + socialsList.push(custom-social(icon, dest, body)) + } else { + panic("Invalid social entry: " + entry) + } + } + + let socialStack = stack( + dir: ttb, + spacing: 0.5em, + ..socialsList, + ) + + stack( + dir: ltr, + titleStack, + align( + right + top, + socialStack, + ), + ) +} + +#let moderner-cv( + name: [], + subtitle: [CV], + social: (:), + color: moderncv-blue, + lang: "en", + font: "New Computer Modern", + show-footer: true, + body, +) = [ + #set page( + paper: "a4", + margin: ( + top: 10mm, + bottom: 15mm, + left: 15mm, + right: 15mm, + ), + ) + #set text( + font: font, + lang: lang, + ) + + #show heading: it => { + set text(weight: "regular") + set text(color) + set block(above: 0pt) + _cv-line( + [], + [#it.body], + ) + } + #show heading.where(level: 1): it => { + set text(weight: "regular") + set text(color) + _cv-line( + align: horizon, + [#box(fill: color, width: 28mm, height: 0.25em)], + [#it.body], + ) + } + + #show heading.where(level: 2): it => { + set text(weight: "regular", size: 13pt) + pad(left: 6.9em, [#it.body]) + } + + #_header(title: name, subtitle: subtitle, socials: social) + + #body + + #if show-footer [ + #v(1fr, weak: false) + #name\ + #datetime.today().display("[month repr:long] [day], [year]") + ] +] + +#let cv-line(left-side, right-side) = { + _cv-line( + align(right, left-side), + par(right-side, justify: true), + ) +} + +#let cv-entry( + date: [], + title: [], + employer: [], + ..description, +) = { + let elements = ( + strong(title), + emph(employer), + ..description.pos(), + ) + cv-line( + date, + elements.join(", "), + ) +} + +#let cv-language(name: [], level: [], comment: []) = { + _cv-line( + align(right, name), + stack(dir: ltr, level, align(right, emph(comment))), + ) +} + +#let cv-paper-item(name: [], date: [], journal: []) = { + show link: it => [ + #set text(red) + #it + ] + _cv-line( + align(right, date), + stack( + dir: ttb, + spacing: 1em, + [*#name*], + journal, + ), + ) +} + +#let cv-double-item(left-1, right-1, left-2, right-2) = { + set block(below: 0pt) + table( + columns: (1fr, 2fr, 1fr, 2fr), + stroke: none, + align(right, left-1), right-1, align(right, left-2), right-2, + ) +} + +#let cv-list-item(item) = { + _cv-line( + [], + list(item), + ) +} + +#let cv-list-double-item(item1, item2) = { + set block(below: 0pt) + table( + columns: (1fr, 2.5fr, 2.5fr), + stroke: none, + [], list(item1), list(item2), + ) +}