1
.gitignore
vendored
@@ -9,3 +9,4 @@ vendor/
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
/_includes/notes_graph.json
|
||||
|
||||
10
Gemfile
Normal file
@@ -0,0 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
source "https://rubygems.org"
|
||||
|
||||
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
|
||||
|
||||
gem "jekyll", "~> 4.4"
|
||||
gem "jekyll-last-modified-at", git: "https://github.com/maximevaillancourt/jekyll-last-modified-at", branch: "add-support-for-files-in-git-submodules"
|
||||
gem "webrick", "~> 1.9"
|
||||
gem "nokogiri"
|
||||
226
Gemfile.lock
Normal file
@@ -0,0 +1,226 @@
|
||||
GIT
|
||||
remote: https://github.com/maximevaillancourt/jekyll-last-modified-at
|
||||
revision: b7f1164861ba589660af3dc096dcbdff75d67145
|
||||
branch: add-support-for-files-in-git-submodules
|
||||
specs:
|
||||
jekyll-last-modified-at (1.3.2)
|
||||
jekyll (>= 3.7, < 5.0)
|
||||
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
addressable (2.9.0)
|
||||
public_suffix (>= 2.0.2, < 8.0)
|
||||
base64 (0.3.0)
|
||||
bigdecimal (4.1.2)
|
||||
colorator (1.1.0)
|
||||
concurrent-ruby (1.3.8)
|
||||
csv (3.3.6)
|
||||
em-websocket (0.5.3)
|
||||
eventmachine (>= 0.12.9)
|
||||
http_parser.rb (~> 0)
|
||||
eventmachine (1.2.7)
|
||||
ffi (1.17.4-aarch64-linux-gnu)
|
||||
ffi (1.17.4-aarch64-linux-musl)
|
||||
ffi (1.17.4-arm-linux-gnu)
|
||||
ffi (1.17.4-arm-linux-musl)
|
||||
ffi (1.17.4-arm64-darwin)
|
||||
ffi (1.17.4-x86_64-darwin)
|
||||
ffi (1.17.4-x86_64-linux-gnu)
|
||||
ffi (1.17.4-x86_64-linux-musl)
|
||||
forwardable-extended (2.6.0)
|
||||
google-protobuf (4.35.1)
|
||||
bigdecimal
|
||||
rake (~> 13.3)
|
||||
google-protobuf (4.35.1-aarch64-linux-gnu)
|
||||
bigdecimal
|
||||
rake (~> 13.3)
|
||||
google-protobuf (4.35.1-aarch64-linux-musl)
|
||||
bigdecimal
|
||||
rake (~> 13.3)
|
||||
google-protobuf (4.35.1-arm64-darwin)
|
||||
bigdecimal
|
||||
rake (~> 13.3)
|
||||
google-protobuf (4.35.1-x86_64-darwin)
|
||||
bigdecimal
|
||||
rake (~> 13.3)
|
||||
google-protobuf (4.35.1-x86_64-linux-gnu)
|
||||
bigdecimal
|
||||
rake (~> 13.3)
|
||||
google-protobuf (4.35.1-x86_64-linux-musl)
|
||||
bigdecimal
|
||||
rake (~> 13.3)
|
||||
http_parser.rb (0.8.1)
|
||||
i18n (1.15.2)
|
||||
concurrent-ruby (~> 1.0)
|
||||
jekyll (4.4.1)
|
||||
addressable (~> 2.4)
|
||||
base64 (~> 0.2)
|
||||
colorator (~> 1.0)
|
||||
csv (~> 3.0)
|
||||
em-websocket (~> 0.5)
|
||||
i18n (~> 1.0)
|
||||
jekyll-sass-converter (>= 2.0, < 4.0)
|
||||
jekyll-watch (~> 2.0)
|
||||
json (~> 2.6)
|
||||
kramdown (~> 2.3, >= 2.3.1)
|
||||
kramdown-parser-gfm (~> 1.0)
|
||||
liquid (~> 4.0)
|
||||
mercenary (~> 0.3, >= 0.3.6)
|
||||
pathutil (~> 0.9)
|
||||
rouge (>= 3.0, < 5.0)
|
||||
safe_yaml (~> 1.0)
|
||||
terminal-table (>= 1.8, < 4.0)
|
||||
webrick (~> 1.7)
|
||||
jekyll-sass-converter (3.1.0)
|
||||
sass-embedded (~> 1.75)
|
||||
jekyll-watch (2.2.1)
|
||||
listen (~> 3.0)
|
||||
json (2.21.2)
|
||||
kramdown (2.5.2)
|
||||
rexml (>= 3.4.4)
|
||||
kramdown-parser-gfm (1.1.0)
|
||||
kramdown (~> 2.0)
|
||||
liquid (4.0.4)
|
||||
listen (3.10.0)
|
||||
logger
|
||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
||||
rb-inotify (~> 0.9, >= 0.9.10)
|
||||
logger (1.7.0)
|
||||
mercenary (0.4.0)
|
||||
nokogiri (1.19.4-aarch64-linux-gnu)
|
||||
racc (~> 1.4)
|
||||
nokogiri (1.19.4-aarch64-linux-musl)
|
||||
racc (~> 1.4)
|
||||
nokogiri (1.19.4-arm-linux-gnu)
|
||||
racc (~> 1.4)
|
||||
nokogiri (1.19.4-arm-linux-musl)
|
||||
racc (~> 1.4)
|
||||
nokogiri (1.19.4-arm64-darwin)
|
||||
racc (~> 1.4)
|
||||
nokogiri (1.19.4-x86_64-darwin)
|
||||
racc (~> 1.4)
|
||||
nokogiri (1.19.4-x86_64-linux-gnu)
|
||||
racc (~> 1.4)
|
||||
nokogiri (1.19.4-x86_64-linux-musl)
|
||||
racc (~> 1.4)
|
||||
pathutil (0.16.2)
|
||||
forwardable-extended (~> 2.6)
|
||||
public_suffix (7.0.5)
|
||||
racc (1.8.1)
|
||||
rake (13.4.2)
|
||||
rb-fsevent (0.11.2)
|
||||
rb-inotify (0.11.1)
|
||||
ffi (~> 1.0)
|
||||
rexml (3.4.4)
|
||||
rouge (4.7.0)
|
||||
safe_yaml (1.0.5)
|
||||
sass-embedded (1.102.0-aarch64-linux-gnu)
|
||||
google-protobuf (~> 4.31)
|
||||
sass-embedded (1.102.0-aarch64-linux-musl)
|
||||
google-protobuf (~> 4.31)
|
||||
sass-embedded (1.102.0-arm-linux-gnueabihf)
|
||||
google-protobuf (~> 4.31)
|
||||
sass-embedded (1.102.0-arm-linux-musleabihf)
|
||||
google-protobuf (~> 4.31)
|
||||
sass-embedded (1.102.0-arm64-darwin)
|
||||
google-protobuf (~> 4.31)
|
||||
sass-embedded (1.102.0-x86_64-darwin)
|
||||
google-protobuf (~> 4.31)
|
||||
sass-embedded (1.102.0-x86_64-linux-gnu)
|
||||
google-protobuf (~> 4.31)
|
||||
sass-embedded (1.102.0-x86_64-linux-musl)
|
||||
google-protobuf (~> 4.31)
|
||||
terminal-table (3.0.2)
|
||||
unicode-display_width (>= 1.1.1, < 3)
|
||||
unicode-display_width (2.6.0)
|
||||
webrick (1.9.2)
|
||||
|
||||
PLATFORMS
|
||||
aarch64-linux-gnu
|
||||
aarch64-linux-musl
|
||||
arm-linux-gnu
|
||||
arm-linux-gnueabihf
|
||||
arm-linux-musl
|
||||
arm-linux-musleabihf
|
||||
arm64-darwin
|
||||
x86_64-darwin
|
||||
x86_64-linux-gnu
|
||||
x86_64-linux-musl
|
||||
|
||||
DEPENDENCIES
|
||||
jekyll (~> 4.4)
|
||||
jekyll-last-modified-at!
|
||||
nokogiri
|
||||
webrick (~> 1.9)
|
||||
|
||||
CHECKSUMS
|
||||
addressable (2.9.0) sha256=7fdf6ac3660f7f4e867a0838be3f6cf722ace541dd97767fa42bc6cfa980c7af
|
||||
base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
|
||||
bigdecimal (4.1.2) sha256=53d217666027eab4280346fba98e7d5b66baaae1b9c3c1c0ffe89d48188a3fbd
|
||||
bundler (4.0.13) sha256=19f08be7f27022cf0b89f27da0b044ae075e8270a9ef44ad248a932614e1ca3b
|
||||
colorator (1.1.0) sha256=e2f85daf57af47d740db2a32191d1bdfb0f6503a0dfbc8327d0c9154d5ddfc38
|
||||
concurrent-ruby (1.3.8) sha256=b2f1be836e968ccc78ccfce277ea79c72a88633f22306782c16ff23fb415d1e1
|
||||
csv (3.3.6) sha256=aba61e7e507a66f03d45cb1f3c4b6359861c3504038b422962875dce099e4456
|
||||
em-websocket (0.5.3) sha256=f56a92bde4e6cb879256d58ee31f124181f68f8887bd14d53d5d9a292758c6a8
|
||||
eventmachine (1.2.7) sha256=994016e42aa041477ba9cff45cbe50de2047f25dd418eba003e84f0d16560972
|
||||
ffi (1.17.4-aarch64-linux-gnu) sha256=b208f06f91ffd8f5e1193da3cae3d2ccfc27fc36fba577baf698d26d91c080df
|
||||
ffi (1.17.4-aarch64-linux-musl) sha256=9286b7a615f2676245283aef0a0a3b475ae3aae2bb5448baace630bb77b91f39
|
||||
ffi (1.17.4-arm-linux-gnu) sha256=d6dbddf7cb77bf955411af5f187a65b8cd378cb003c15c05697f5feee1cb1564
|
||||
ffi (1.17.4-arm-linux-musl) sha256=9d4838ded0465bef6e2426935f6bcc93134b6616785a84ffd2a3d82bc3cf6f95
|
||||
ffi (1.17.4-arm64-darwin) sha256=19071aaf1419251b0a46852abf960e77330a3b334d13a4ab51d58b31a937001b
|
||||
ffi (1.17.4-x86_64-darwin) sha256=aa70390523cf3235096cf64962b709b4cfbd5c082a2cb2ae714eb0fe2ccda496
|
||||
ffi (1.17.4-x86_64-linux-gnu) sha256=9d3db14c2eae074b382fa9c083fe95aec6e0a1451da249eab096c34002bc752d
|
||||
ffi (1.17.4-x86_64-linux-musl) sha256=3fdf9888483de005f8ef8d1cf2d3b20d86626af206cbf780f6a6a12439a9c49e
|
||||
forwardable-extended (2.6.0) sha256=1bec948c469bbddfadeb3bd90eb8c85f6e627a412a3e852acfd7eaedbac3ec97
|
||||
google-protobuf (4.35.1) sha256=a3a6471331d918f58dfa4d014a8f6286f0af2cf4840216bde52fcf2ea3fe3726
|
||||
google-protobuf (4.35.1-aarch64-linux-gnu) sha256=50ca44d0eeff3f8475e630a1accdd974256f3510694d574e2c9d6119ea8bc9e1
|
||||
google-protobuf (4.35.1-aarch64-linux-musl) sha256=d5c65cef6bd6498a9e5ed5f88cf6cf7e341c10b0a005e32137d5d1a2b6e8c18a
|
||||
google-protobuf (4.35.1-arm64-darwin) sha256=d9c957df04fa89c749fa9a72a7b383eb4296efc9b2303dc6fd6fbe39c698ad6b
|
||||
google-protobuf (4.35.1-x86_64-darwin) sha256=66b62b4df00931018a692806df66393efa960d6d2b7da69735187249f950d3ee
|
||||
google-protobuf (4.35.1-x86_64-linux-gnu) sha256=c786439087512a3fbd199e9897d265b855f951d4027e218ea55e858d45969edd
|
||||
google-protobuf (4.35.1-x86_64-linux-musl) sha256=91890eb0002934a339fdb7d77a147c46b7474b6799db27872b747b905837f744
|
||||
http_parser.rb (0.8.1) sha256=9ae8df145b39aa5398b2f90090d651c67bd8e2ebfe4507c966579f641e11097a
|
||||
i18n (1.15.2) sha256=00f9eb62412fe593b2a65a97daa75300d37abb8f7202ec748e94b6d46a9dd1b5
|
||||
jekyll (4.4.1) sha256=4c1144d857a5b2b80d45b8cf5138289579a9f8136aadfa6dd684b31fe2bc18c1
|
||||
jekyll-last-modified-at (1.3.2)
|
||||
jekyll-sass-converter (3.1.0) sha256=83925d84f1d134410c11d0c6643b0093e82e3a3cf127e90757a85294a3862443
|
||||
jekyll-watch (2.2.1) sha256=bc44ed43f5e0a552836245a54dbff3ea7421ecc2856707e8a1ee203a8387a7e1
|
||||
json (2.21.2) sha256=1f1d3b7cf2b3ba1a69beca0bb6db13d5438b80bff3cd54cdaaa620b9b07c1c6a
|
||||
kramdown (2.5.2) sha256=1ba542204c66b6f9111ff00dcc26075b95b220b07f2905d8261740c82f7f02fa
|
||||
kramdown-parser-gfm (1.1.0) sha256=fb39745516427d2988543bf01fc4cf0ab1149476382393e0e9c48592f6581729
|
||||
liquid (4.0.4) sha256=4fcfebb1a045e47918388dbb7a0925e7c3893e58d2bd6c3b3c73ec17a2d8fdb3
|
||||
listen (3.10.0) sha256=c6e182db62143aeccc2e1960033bebe7445309c7272061979bb098d03760c9d2
|
||||
logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
|
||||
mercenary (0.4.0) sha256=b25a1e4a59adca88665e08e24acf0af30da5b5d859f7d8f38fba52c28f405138
|
||||
nokogiri (1.19.4-aarch64-linux-gnu) sha256=1269fb644a6de405057a53dd5c762b1209b43ca7424f839454d3dbc677c31a8f
|
||||
nokogiri (1.19.4-aarch64-linux-musl) sha256=35c65b9ce72b3bb03207bdbe7067915019dc18c1b9b59139684bd6690fdd01af
|
||||
nokogiri (1.19.4-arm-linux-gnu) sha256=a301313e38bb065d68239e79734bcd6f56fb6efaacebde29e9abf2a4735340ca
|
||||
nokogiri (1.19.4-arm-linux-musl) sha256=588923c101bcfa78869734d247d25b598674323e7f22474fc468f6e5647311eb
|
||||
nokogiri (1.19.4-arm64-darwin) sha256=a46db9853286e6597b36ebc6953817d15acf3a299583eb3f89fdc6f91dd63527
|
||||
nokogiri (1.19.4-x86_64-darwin) sha256=7fd17057d3e1f00e9954a74b3cd76595d3d4a5ef233b7ed9599047c204f70551
|
||||
nokogiri (1.19.4-x86_64-linux-gnu) sha256=379fae440b28915e3f19d752ce2dcf8465ed2b2fbefd2a7ca0dd497bc981a06a
|
||||
nokogiri (1.19.4-x86_64-linux-musl) sha256=17dfb7c1fa194ae02fbf7c51a7afc8d278045ab3fdacfd86f91d02d7b274470b
|
||||
pathutil (0.16.2) sha256=e43b74365631cab4f6d5e4228f812927efc9cb2c71e62976edcb252ee948d589
|
||||
public_suffix (7.0.5) sha256=1a8bb08f1bbea19228d3bed6e5ed908d1cb4f7c2726d18bd9cadf60bc676f623
|
||||
racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f
|
||||
rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701
|
||||
rb-fsevent (0.11.2) sha256=43900b972e7301d6570f64b850a5aa67833ee7d87b458ee92805d56b7318aefe
|
||||
rb-inotify (0.11.1) sha256=a0a700441239b0ff18eb65e3866236cd78613d6b9f78fea1f9ac47a85e47be6e
|
||||
rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142
|
||||
rouge (4.7.0) sha256=dba5896715c0325c362e895460a6d350803dbf6427454f49a47500f3193ea739
|
||||
safe_yaml (1.0.5) sha256=a6ac2d64b7eb027bdeeca1851fe7e7af0d668e133e8a88066a0c6f7087d9f848
|
||||
sass-embedded (1.102.0-aarch64-linux-gnu) sha256=087f377fa4135a8f67f5f422781b59815e99b8fd3aec335935c50a47c2a205fd
|
||||
sass-embedded (1.102.0-aarch64-linux-musl) sha256=12bb4091972499241c35304ca11ca2c314764badf1b9933a8d437ba62432b538
|
||||
sass-embedded (1.102.0-arm-linux-gnueabihf) sha256=566b1188e4f923366f494a5d95e4e5e03864ae55bae5ea52beb2c30b1b9e2915
|
||||
sass-embedded (1.102.0-arm-linux-musleabihf) sha256=1ca396610bf4f4aa8f849b67a9f2720f67d9f3b42651072eafee786aece314cf
|
||||
sass-embedded (1.102.0-arm64-darwin) sha256=8e18b2dd94b232c2dd73984c579f77bbc1a066028f4675c8989ac34fa40d14dc
|
||||
sass-embedded (1.102.0-x86_64-darwin) sha256=3e78c6ac5be0ba4236d075bd044a49d0f515d54799f7d6f253c71d325ffedac0
|
||||
sass-embedded (1.102.0-x86_64-linux-gnu) sha256=9815b7604123a9a44a65d44d5e58e7af1c7005174b745cda83bad4c9e3760ab3
|
||||
sass-embedded (1.102.0-x86_64-linux-musl) sha256=9e3c15eb5d83784adf3b946d4fc5d8a32aed25566a2fee939f25dd1e1d217ddf
|
||||
terminal-table (3.0.2) sha256=f951b6af5f3e00203fb290a669e0a85c5dd5b051b3b023392ccfd67ba5abae91
|
||||
unicode-display_width (2.6.0) sha256=12279874bba6d5e4d2728cef814b19197dbb10d7a7837a869bab65da943b7f5a
|
||||
webrick (1.9.2) sha256=beb4a15fc474defed24a3bda4ffd88a490d517c9e4e6118c3edce59e45864131
|
||||
|
||||
BUNDLED WITH
|
||||
4.0.13
|
||||
13
README.md
@@ -1,3 +1,16 @@
|
||||
# brendan-ch.github.io
|
||||
|
||||
This is my personal webpage built using Jekyll and GitHub Pages.
|
||||
|
||||
It is based on the [digital-garden-jekyll-template](https://github.com/maximevaillancourt/digital-garden-jekyll-template) by Maxime
|
||||
Vaillancourt. The MIT license of the project:
|
||||
|
||||
```
|
||||
Copyright (c) 2020 Maxime Vaillancourt.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
```
|
||||
|
||||
20
_config.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
exclude: [ '_includes/notes_graph.json' ]
|
||||
|
||||
plugins:
|
||||
- jekyll-last-modified-at
|
||||
|
||||
open_external_links_in_new_tab: true
|
||||
use_html_extension: false
|
||||
permalink: pretty
|
||||
relative_permalinks: false
|
||||
|
||||
collections:
|
||||
notes:
|
||||
output: true
|
||||
permalink: /:slug/
|
||||
|
||||
defaults:
|
||||
- scope:
|
||||
path: "_notes/**/*.md"
|
||||
values:
|
||||
layout: "note"
|
||||
@@ -1,20 +1,9 @@
|
||||
<nav class="navbar">
|
||||
<a href="/" class="nav-logo">
|
||||
<img src="/assets/web-app-manifest-512x512.png" alt="Brendan Chen home" class="nav-logo-icon">
|
||||
<a href="/" class="nav-title">
|
||||
<p>
|
||||
<b>
|
||||
Brendan Chen
|
||||
</b>
|
||||
</p>
|
||||
</a>
|
||||
<input type="checkbox" id="hamburger-toggle" class="hamburger-toggle">
|
||||
<label for="hamburger-toggle" class="hamburger">
|
||||
<span class="hamburger-line"></span>
|
||||
<span class="hamburger-line"></span>
|
||||
<span class="hamburger-line"></span>
|
||||
</label>
|
||||
<label for="hamburger-toggle" class="menu-overlay"></label>
|
||||
<ul class="nav-menu">
|
||||
<li class="nav-item">
|
||||
<a href="/" class="nav-link">Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/projects" class="nav-link">Projects</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
@@ -4,14 +4,15 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<script defer src="https://sassy-fulmar.pikapod.net/script.js" data-website-id="479f6086-eaaf-46c8-a365-78a53a066682"></script>
|
||||
<script defer src="https://sassy-fulmar.pikapod.net/script.js"
|
||||
data-website-id="479f6086-eaaf-46c8-a365-78a53a066682"></script>
|
||||
|
||||
<link rel="icon" type="image/png" href="/assets/favicon-96x96.png" sizes="96x96" />
|
||||
<link rel="icon" type="image/svg+xml" href="/assets/favicon.svg" />
|
||||
<link rel="shortcut icon" href="/assets/favicon.ico" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/assets/apple-touch-icon.png" />
|
||||
<meta name="apple-mobile-web-app-title" content="Brendan Chen" />
|
||||
<link rel="manifest" href="/assets/site.webmanifest" />
|
||||
<link rel="icon" type="image/png" href="/assets/favicon-96x96.png" sizes="96x96"/>
|
||||
<link rel="icon" type="image/svg+xml" href="/assets/favicon.svg"/>
|
||||
<link rel="shortcut icon" href="/assets/favicon.ico"/>
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/assets/apple-touch-icon.png"/>
|
||||
<meta name="apple-mobile-web-app-title" content="Brendan Chen"/>
|
||||
<link rel="manifest" href="/assets/site.webmanifest"/>
|
||||
<link rel="stylesheet" href="/assets/css/global.css">
|
||||
|
||||
<meta name="theme-color" content="#f4f4f4">
|
||||
@@ -19,16 +20,19 @@
|
||||
<title>{{ page.title }}</title>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
{% include navigation.html %}
|
||||
</header>
|
||||
<main>
|
||||
{{ content }}
|
||||
</main>
|
||||
<footer>
|
||||
copyright 2025 Brendan Chen |
|
||||
<header>
|
||||
{% include navigation.html %}
|
||||
</header>
|
||||
<main>
|
||||
{{ content }}
|
||||
</main>
|
||||
<footer>
|
||||
<p>
|
||||
<!-- see https://stackoverflow.com/questions/12464656/get-todays-date-in-jekyll-with-liquid-markup -->
|
||||
copyright {{ site.time | date: '%Y' }} Brendan Chen |
|
||||
<a target="_blank" href="https://github.com/brendan-ch">GitHub</a> |
|
||||
<a target="_blank" href="https://linkedin.com/in/brendan-ch">LinkedIn</a> | e: me [at] bchen.dev
|
||||
</footer>
|
||||
</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
26
_layouts/note.html
Normal file
@@ -0,0 +1,26 @@
|
||||
---
|
||||
layout: default
|
||||
---
|
||||
|
||||
<h1>{{ page.title }}</h1>
|
||||
|
||||
{{ content }}
|
||||
|
||||
<br />
|
||||
|
||||
<hr class="backlink-divider">
|
||||
|
||||
<div class="backlink-box">
|
||||
{% if page.backlinks.size > 0 %}
|
||||
<h3>Notes mentioning this note</h3>
|
||||
<div>
|
||||
{% for backlink in page.backlinks %}
|
||||
<div class="backlinks">
|
||||
<a class="internal-link" href="{{ site.baseurl }}{{ backlink.url }}{%- if site.use_html_extension -%}.html{%- endif -%}">{{ backlink.title }}</a><br>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<br />
|
||||
6
_layouts/page_moved.html
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
layout: default
|
||||
---
|
||||
|
||||
<h1>Page moved</h1>
|
||||
<p>This page has been moved. {{ content }}</p>
|
||||
26
_notes/cmes-mobile.md
Normal file
@@ -0,0 +1,26 @@
|
||||
---
|
||||
title: CMES Mobile App
|
||||
---
|
||||
|
||||

|
||||
|
||||
**App development project for volunteer organization Techies Without Borders.**
|
||||
I helped implement the CMES mobile app, facilitating the download, viewing and
|
||||
playback of medical education content in resource-constrained countries.
|
||||
|
||||
In the early stages, I built the download screen and business logic, the PDF
|
||||
viewer integration, and background audio playback using native code. Later, I
|
||||
built the native web view to integrate with
|
||||
the [CMES-Pi](https://cmesworld.org/cmes_product-Pi.html) solution, simplifying
|
||||
the business logic while adding support for future edge platforms.
|
||||
|
||||
The app's front-end uses Capacitor and Angular, with several Capacitor plugins
|
||||
in Swift + Java to bundle native functionality.
|
||||
|
||||
This app is actively used in medical facilities in Uganda and Nepal, to name a
|
||||
few.
|
||||
|
||||
- [App Store](https://apps.apple.com/us/app/cmes/id1276335909)
|
||||
- [Google Play](https://play.google.com/store/apps/details?id=org.cmesworld.www.cmes_piandroid_app)
|
||||
- [Techies Without Borders](https://techieswithoutborders.us/)
|
||||
- [CMES Project](https://cmesworld.org/)
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
title: Don't Wake Sandie
|
||||
layout: project
|
||||
---
|
||||
|
||||
<iframe style="width: 100%; height: 400px; border-radius: 1px; pointer-events: auto; background-color: white;" src="https://www.linkedin.com/embed/feed/update/urn:li:activity:7274629268624551936?compact=true" frameborder="0" sandbox="allow-scripts allow-popups allow-top-navigation-by-user-activation allow-forms allow-same-origin allow-storage-access-by-user-activation allow-popups-to-escape-sandbox" allowfullscreen=""></iframe>
|
||||
@@ -17,5 +16,5 @@ UI development, artwork integration, and version control. There were so many
|
||||
moving parts, and I really aimed to get as much input for the gameplay design
|
||||
as I could from the team.
|
||||
|
||||
[itch.io (play the game!)](https://bchendev.itch.io/dont-wake-sandie){: .primary-action }
|
||||
{: .horizontal-wrapper }
|
||||
[Play the game on itch.io!](https://bchendev.itch.io/dont-wake-sandie)
|
||||
|
||||
24
_notes/index.md
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
title: "Brendan Chen"
|
||||
layout: default
|
||||
isIndexPage: 1
|
||||
permalink: /
|
||||
---
|
||||
|
||||
I'm Brendan, a recently graduated Software Engineering student who attended
|
||||
Chapman University. I'm currently an associate software engineer at Cargill. I'm
|
||||
also the creator of [OpenAppLock](https://github.com/brendan-ch/OpenAppLock), an
|
||||
open-source time blocker for iOS,
|
||||
and [Interchange](https://interchange.bchen.dev), a free shuttle app for Chapman
|
||||
students.
|
||||
|
||||
I believe that no matter which path an app's development takes, it should be
|
||||
grounded in real experiences.
|
||||
|
||||
Feel free to [say hi](mailto:brendan@bchen.dev)!
|
||||
|
||||
- [[Projects]]
|
||||
- [View my resume](/assets/resume.pdf)
|
||||
|
||||

|
||||
|
||||
15
_notes/interchange.md
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
title: Interchange
|
||||
---
|
||||
|
||||

|
||||
|
||||
**An iOS app for college transit, currently available for Chapman University.**
|
||||
View routes, shuttle locations, and estimated arrival times in a sleek,
|
||||
easy-to-use interface. I built this app using SwiftUI, Node.js and Apollo
|
||||
GraphQL, with Redis as a primary storage mechanism.
|
||||
|
||||
- [Download on the App Store](https://apps.apple.com/us/app/interchange-college-commute/id6739968742)
|
||||
- [Join the TestFlight](https://testflight.apple.com/join/HbuHfUgW)
|
||||
- [Website](https://interchange.bchen.dev/)
|
||||
|
||||
26
_notes/power-to-the-people.md
Normal file
@@ -0,0 +1,26 @@
|
||||
---
|
||||
title: Power to the People
|
||||
---
|
||||
|
||||

|
||||
|
||||
**An iPhone app designed to make traffic stops safer for marginalized
|
||||
communities.** Record interactions with police, assign and text emergency
|
||||
contacts, and be informed of your rights. I worked in a student team of 4,
|
||||
including [David Sohn](https://www.linkedin.com/in/sohn-david/), [Jacob Zamore](https://www.linkedin.com/in/jacob-zamore/),
|
||||
and [Patrick Smith](https://www.linkedin.com/in/patrick-smith-ld/), to prototype
|
||||
and build a proof of concept in 36 hours. The app was selected for the *
|
||||
*President's Pick award** at [CruzHacks 2024](https://cruzhacks.com/), the
|
||||
premier hackathon of UC Santa Cruz.
|
||||
|
||||
- [GitHub](https://github.com/brendan-ch/PowerToThePeople)
|
||||
- [Devpost](https://devpost.com/software/power-to-the-people-lbhxyd)
|
||||
|
||||
My contribution was the iOS app prototype, of which a demo is shown below. I
|
||||
built the primary user flow of recording a police interaction, simultaneously
|
||||
using the front and back iPhone cameras to capture all angles. Activating the
|
||||
recording also displays the "Know Your Rights" screen, displaying basic dos and
|
||||
don'ts. Finally, I built a way to view previous recordings and share the files
|
||||
using the iOS share sheet.
|
||||
|
||||
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/Un8JWXkpcW4?si=CfQ0qOuUjSpas0ny&start=89" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
|
||||
@@ -1,42 +1,44 @@
|
||||
---
|
||||
title: Projects
|
||||
layout: default
|
||||
---
|
||||
|
||||
# Projects
|
||||
|
||||
## Games
|
||||
|
||||
- [Resonance](/projects/resonance)
|
||||
- [[Resonance]]
|
||||
- **IEEE GameSig Finalist**, 2026
|
||||
- A cyberpunk-themed game show shooter.
|
||||
- [Don't Wake Sandie](/projects/dont-wake-sandie)
|
||||
- [[Don't Wake Sandie]]
|
||||
- A game about not waking up a beloved hungover corgi
|
||||
|
||||
## Personal
|
||||
|
||||
- [Interchange](/projects/interchange)
|
||||
- [[Interchange]]
|
||||
- An iOS app for college transit, now on the App Store!
|
||||
- [Timestamp](/projects/timestamp)
|
||||
- [[Timestamp]]
|
||||
- An unintrusive, open source Pomodoro timer for macOS.
|
||||
- [Sourcebook](/projects/sourcebook)
|
||||
- [[Sourcebook]]
|
||||
- A lightweight content management system (CMS) for class websites.
|
||||
|
||||
## Hackathons
|
||||
|
||||
- [teech.ai](/projects/teech-ai-cal-hacks-12)
|
||||
- [[teech.ai]]
|
||||
- Cal Hacks 12.0
|
||||
- A tutoring app to make education more accessible, built with SwiftUI and Gemini Vision API.
|
||||
- [Power to the People](/projects/power-to-the-people)
|
||||
- A tutoring app to make education more accessible, built with SwiftUI and
|
||||
Gemini Vision API.
|
||||
- [[Power to the People]]
|
||||
- **President's Pick**, CruzHacks 2024
|
||||
- An iOS app designed to make traffic stops safer for marginalized communities.
|
||||
- An iOS app designed to make traffic stops safer for marginalized
|
||||
communities.
|
||||
|
||||
## Work
|
||||
|
||||
- [The Farmer's Dog Trivia Game](/projects/the-farmers-dog-trivia)
|
||||
- A Windows kiosk application designed to connect customers with the Farmer's Dog experience.
|
||||
- [[The Farmer's Dog Trivia Game]]
|
||||
- A Windows kiosk application designed to connect customers with the
|
||||
Farmer's Dog experience.
|
||||
|
||||
## Community
|
||||
|
||||
- [CMES Mobile App](/projects/cmes-mobile/)
|
||||
- One of my earliest projects. A cross-platform mobile app to deliver educational medical content to end users, part of [Techies Without Borders](https://techieswithoutborders.us/).
|
||||
- [[CMES Mobile App]]
|
||||
- One of my earliest projects. A cross-platform mobile app to deliver
|
||||
educational medical content to end users, part
|
||||
of [Techies Without Borders](https://techieswithoutborders.us/).
|
||||
40
_notes/resonance.md
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
title: Resonance
|
||||
---
|
||||
|
||||

|
||||
|
||||
Resonance is a cyberpunk-themed game show shooter. Enter the arena, blast your
|
||||
opponents, and become the star of the show. I worked with a student team of 12
|
||||
to create this game over multiple months. The game was selected as an **IEEE
|
||||
GameSig finalist**.
|
||||
|
||||
*Steam link coming soon!*
|
||||
|
||||
My core role was to implement the game's networking logic using PurrNet, an
|
||||
open-source library for Unity.
|
||||
|
||||
My first project was to implement networking for **Arena**, a free-for-all PVP
|
||||
game mode, where the player with the most eliminations wins. I learned how to
|
||||
isolate game logic on the server and synchronize state on each client. The
|
||||
networked API for Arena is used by every aspect of the game, including the
|
||||
scoreboard UI and spotlight mechanic. Following a pivot in the game’s core
|
||||
direction, I successfully handed this system to another engineer, who
|
||||
implemented a rating system for style points.
|
||||
|
||||
Another project was the **build system**. Having a server dependency and Steam
|
||||
integration meant that the environment differed from development to production.
|
||||
We found it painful to prepare a build for each class-mandated playtest. With
|
||||
the help of Claude, I created a two-step system: an editor script to build and
|
||||
configure dependencies in one step, and a nightly GitHub Actions pipeline.
|
||||
Besides simplifying the build process, this created another positive side effect
|
||||
by ensuring we fixed compiler issues proactively.
|
||||
|
||||
I'm currently working on a client-side prediction model to match other FPS games
|
||||
on the market.
|
||||
|
||||
Even with our team size, the most important thing I learned was how to manage
|
||||
scope and scale. Each one of us pushed ourselves to the limits of our knowledge,
|
||||
going from bare theoretical understanding to practical working implementations.
|
||||
The goal was never to get things right on the first try; it was to learn and
|
||||
realize new ideas in multiple passes.
|
||||
@@ -1,9 +1,8 @@
|
||||
---
|
||||
title: Sourcebook
|
||||
layout: project
|
||||
---
|
||||
|
||||

|
||||

|
||||
|
||||
**A lightweight content management system (CMS) designed to build class
|
||||
websites.** I designed it to be a self-hosted alternative for popular apps like
|
||||
@@ -13,5 +12,5 @@ I designed an entity-relationship diagram, MySQL database schema, and HTML/CSS
|
||||
high-fidelity prototype to support the project and show to professors. I then
|
||||
built a lightweight full-stack application using Python, Flask, and MySQL.
|
||||
|
||||
[GitHub](https://github.com/brendan-ch/sourcebook){: .primary-action }
|
||||
{: .horizontal-wrapper }
|
||||
[GitHub](https://github.com/brendan-ch/sourcebook)
|
||||
|
||||
24
_notes/tad-pool.md
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
title: Tad-Pool
|
||||
---
|
||||
|
||||

|
||||
|
||||
**A mobile app designed to make carpooling easier for college students.** Find
|
||||
carpools based on time and location, and get rewarded for helping to reduce CO2
|
||||
emissions. I worked
|
||||
with [Jazmine Stanek](https://www.linkedin.com/in/vjazminestanek/)
|
||||
and [Isabelle Woo](https://www.linkedin.com/in/isabellewoo/) to create the UI/UX
|
||||
design using Figma and FigJam, in a period of 24 hours for
|
||||
[FullyBeyond 2024](https://fullybeyond.acmcsuf.com/).
|
||||
|
||||
The project reached the finalist judging stage.
|
||||
|
||||
- [High-fidelity design](https://www.figma.
|
||||
com/design/nTKCcwftKrg27wlP5mmy9e/Tad-Pool-Design?node-id=13-1837&node-type=canvas&t=YKuVvM6RAPs4hTIo-11)
|
||||
- [Low-fidelity design](https://www.figma.
|
||||
com/design/nTKCcwftKrg27wlP5mmy9e/Tad-Pool-Design?node-id=0-1&node-type=canvas&t=YKuVvM6RAPs4hTIo-11)
|
||||
- [FigJam board](https://www.figma.
|
||||
com/board/Dct7GuhKFYq8maw0KoAVBV/Product-Brainstorm-FullyBeyond?node-id=0-1&node-type=canvas&t=g7RKLLo5IoU84ndk-11)
|
||||
- [Devpost](https://devpost.com/software/tad-pool)
|
||||
|
||||
29
_notes/teech-ai-cal-hacks-12.md
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
title: teech.ai
|
||||
---
|
||||
|
||||
<iframe width="100%" height="480" src="https://www.youtube-nocookie.com/embed/luVIdLB9id0?si=1sTE9Z3MeyWntRmd" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
|
||||
|
||||
**A proof-of-concept tutoring app for students learning math.** Using the phone
|
||||
camera, the app sees and understands math problems, guiding students through
|
||||
problems with hints and step-by-step questions. We added text-to-speech (TTS) to
|
||||
give our app a voice, further engaging students and making it feel like a more
|
||||
personal experience.
|
||||
|
||||
[Tri Vu](https://www.linkedin.com/in/tri-q-vu/) built a web interface prototype
|
||||
and Python-based backend utilizing the Gemini Vision API and Fish Audio for TTS.
|
||||
|
||||
I built a native proof-of-concept using SwiftUI, AVFoundation, and other native
|
||||
frameworks. In the app, I built a chat and camera interface, integrating with
|
||||
Tri's backend by sending image and prompt data. Swiping to the right reveals the
|
||||
user's chat history, which is also continuously synced with the backend.
|
||||
Design-wise, I decided to use this gesture-based approach in order to bring the
|
||||
camera interface front and center.
|
||||
|
||||
The app was built in 48 hours for **Cal Hacks 12.0**, the world's largest
|
||||
collegiate hackathon.
|
||||
|
||||
- [GitHub](https://github.com/brendan-ch/teecher-ios)
|
||||
- [Devpost](https://devpost.com/software/teech-ai)
|
||||
- [Cal Hacks](https://www.calhacks.io)
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
---
|
||||
title: The Farmer's Dog Trivia Game
|
||||
layout: project
|
||||
---
|
||||
|
||||

|
||||

|
||||
|
||||
**A Windows kiosk application designed to connect customers with the Farmer's
|
||||
Dog experience.** Play the interactive trivia game, score points, and win swag!
|
||||
I worked with [Walter Cheng](https://www.linkedin.com/in/walter-j-cheng/) to
|
||||
plan out dependencies and requirements, set up the codebase, and implement the
|
||||
screens for the app using React Native for Windows.
|
||||
screens for the app using React Native for Windows.
|
||||
|
||||
"I’ve worked with Brendan for many years, and he is the first person I turn to
|
||||
for web/app development. He’s knowledgeable and efficient, and his task
|
||||
management skills make him an ideal project member." — Walter Cheng, project overseer
|
||||
management skills make him an ideal project member." — Walter Cheng, project
|
||||
overseer
|
||||
|
||||
[The Farmer’s Dog](https://thefarmersdog.com/){: .primary-action }
|
||||
{: .horizontal-wrapper }
|
||||
[The Farmer’s Dog](https://thefarmersdog.com/)
|
||||
11
_notes/timestamp.md
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
title: Timestamp
|
||||
---
|
||||
|
||||

|
||||
|
||||
A simple, unintrusive Pomodoro timer for macOS which lives in the menu bar. I
|
||||
built it with SwiftUI and made it open source.
|
||||
|
||||
[Download the app](https://github.com/brendan-ch/timestamp/releases)
|
||||
[View the code](https://github.com/brendan-ch/timestamp)
|
||||
111
_plugins/bidirectional_links_generator.rb
Normal file
@@ -0,0 +1,111 @@
|
||||
# frozen_string_literal: true
|
||||
class BidirectionalLinksGenerator < Jekyll::Generator
|
||||
def generate(site)
|
||||
graph_nodes = []
|
||||
graph_edges = []
|
||||
|
||||
all_notes = site.collections['notes'].docs
|
||||
all_pages = site.pages
|
||||
|
||||
all_docs = all_notes + all_pages
|
||||
|
||||
link_extension = !!site.config["use_html_extension"] ? '.html' : ''
|
||||
|
||||
# Convert all Wiki/Roam-style double-bracket link syntax to plain HTML
|
||||
# anchor tag elements (<a>) with "internal-link" CSS class
|
||||
all_docs.each do |current_note|
|
||||
all_docs.each do |note_potentially_linked_to|
|
||||
note_title_regexp_pattern = Regexp.escape(
|
||||
File.basename(
|
||||
note_potentially_linked_to.basename,
|
||||
File.extname(note_potentially_linked_to.basename)
|
||||
)
|
||||
).gsub('\_', '[ _]').gsub('\-', '[ -]').capitalize
|
||||
|
||||
title_from_data = note_potentially_linked_to.data['title']
|
||||
if title_from_data
|
||||
title_from_data = Regexp.escape(title_from_data)
|
||||
end
|
||||
|
||||
new_href = "#{site.baseurl}#{note_potentially_linked_to.url}#{link_extension}"
|
||||
anchor_tag = "<a class='internal-link' href='#{new_href}'>\\1</a>"
|
||||
|
||||
# Replace double-bracketed links with label using note title
|
||||
# [[A note about cats|this is a link to the note about cats]]
|
||||
current_note.content.gsub!(
|
||||
/\[\[#{note_title_regexp_pattern}\|(.+?)(?=\])\]\]/i,
|
||||
anchor_tag
|
||||
)
|
||||
|
||||
# Replace double-bracketed links with label using note filename
|
||||
# [[cats|this is a link to the note about cats]]
|
||||
current_note.content.gsub!(
|
||||
/\[\[#{title_from_data}\|(.+?)(?=\])\]\]/i,
|
||||
anchor_tag
|
||||
)
|
||||
|
||||
# Replace double-bracketed links using note title
|
||||
# [[a note about cats]]
|
||||
current_note.content.gsub!(
|
||||
/\[\[(#{title_from_data})\]\]/i,
|
||||
anchor_tag
|
||||
)
|
||||
|
||||
# Replace double-bracketed links using note filename
|
||||
# [[cats]]
|
||||
current_note.content.gsub!(
|
||||
/\[\[(#{note_title_regexp_pattern})\]\]/i,
|
||||
anchor_tag
|
||||
)
|
||||
end
|
||||
|
||||
# At this point, all remaining double-bracket-wrapped words are
|
||||
# pointing to non-existing pages, so let's turn them into disabled
|
||||
# links by greying them out and changing the cursor
|
||||
current_note.content = current_note.content.gsub(
|
||||
/\[\[([^\]]+)\]\]/i, # match on the remaining double-bracket links
|
||||
<<~HTML.delete("\n") # replace with this HTML (\\1 is what was inside the brackets)
|
||||
<span title='There is no note that matches this link.' class='invalid-link'>
|
||||
<span class='invalid-link-brackets'>[[</span>
|
||||
\\1
|
||||
<span class='invalid-link-brackets'>]]</span></span>
|
||||
HTML
|
||||
)
|
||||
end
|
||||
|
||||
# Identify note backlinks and add them to each note
|
||||
all_notes.each do |current_note|
|
||||
# Nodes: Jekyll
|
||||
notes_linking_to_current_note = all_notes.filter do |e|
|
||||
e.url != current_note.url && e.content.include?(current_note.url)
|
||||
end
|
||||
|
||||
# Nodes: Graph
|
||||
graph_nodes << {
|
||||
id: note_id_from_note(current_note),
|
||||
path: "#{site.baseurl}#{current_note.url}#{link_extension}",
|
||||
label: current_note.data['title'],
|
||||
} unless current_note.path.include?('_notes/index.html')
|
||||
|
||||
# Edges: Jekyll
|
||||
current_note.data['backlinks'] = notes_linking_to_current_note
|
||||
|
||||
# Edges: Graph
|
||||
notes_linking_to_current_note.each do |n|
|
||||
graph_edges << {
|
||||
source: note_id_from_note(n),
|
||||
target: note_id_from_note(current_note),
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
File.write('_includes/notes_graph.json', JSON.dump({
|
||||
edges: graph_edges,
|
||||
nodes: graph_nodes,
|
||||
}))
|
||||
end
|
||||
|
||||
def note_id_from_note(note)
|
||||
note.data['title'].bytes.join
|
||||
end
|
||||
end
|
||||
18
_plugins/empty_front_matter_note_injector.rb
Normal file
@@ -0,0 +1,18 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
EMPTY_FRONT_MATTER = <<~JEKYLL
|
||||
---
|
||||
---
|
||||
|
||||
JEKYLL
|
||||
|
||||
# Inject empty front matter in notes that don't have any
|
||||
Jekyll::Hooks.register :site, :after_init do |site|
|
||||
Dir.glob(site.collections['notes'].relative_directory + '/**/*.md').each do |filename|
|
||||
raw_note_content = File.read(filename)
|
||||
unless raw_note_content.start_with?('---')
|
||||
raw_note_content.prepend(EMPTY_FRONT_MATTER)
|
||||
File.write(filename, raw_note_content)
|
||||
end
|
||||
end
|
||||
end
|
||||
18
_plugins/last_modified_at_generator.rb
Normal file
@@ -0,0 +1,18 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'fileutils'
|
||||
require 'pathname'
|
||||
require 'jekyll-last-modified-at'
|
||||
|
||||
module Recents
|
||||
# Generate change information for all markdown pages
|
||||
class Generator < Jekyll::Generator
|
||||
def generate(site)
|
||||
items = site.collections['notes'].docs
|
||||
items.each do |page|
|
||||
timestamp = Jekyll::LastModifiedAt::Determinator.new(site.source, page.path, '%FT%T%:z').to_s
|
||||
page.data['last_modified_at_timestamp'] = timestamp
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
18
_plugins/markdown-highlighter.rb
Normal file
@@ -0,0 +1,18 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Turns ==something== in Markdown to <mark>something</mark> in output HTML
|
||||
|
||||
Jekyll::Hooks.register [:notes], :pre_render do |doc|
|
||||
replace(doc)
|
||||
end
|
||||
|
||||
Jekyll::Hooks.register [:pages], :pre_render do |doc|
|
||||
# jekyll considers anything at the root as a page,
|
||||
# we only want to consider actual pages
|
||||
next unless doc.path.start_with?('_pages/')
|
||||
replace(doc)
|
||||
end
|
||||
|
||||
def replace(doc)
|
||||
doc.content.gsub!(/==+([^ ](.*?)?[^ .=])==+/, "<mark>\\1</mark>")
|
||||
end
|
||||
28
_plugins/open_external_links_in_new_tab.rb
Normal file
@@ -0,0 +1,28 @@
|
||||
# If the configuration sets `open_external_links_in_new_tab` to a truthy value,
|
||||
# add 'target=_blank' to anchor tags that don't have `internal-link` class
|
||||
|
||||
# frozen_string_literal: true
|
||||
require 'nokogiri'
|
||||
|
||||
Jekyll::Hooks.register [:notes], :post_convert do |doc|
|
||||
convert_links(doc)
|
||||
end
|
||||
|
||||
Jekyll::Hooks.register [:pages], :post_convert do |doc|
|
||||
# jekyll considers anything at the root as a page,
|
||||
# we only want to consider actual pages
|
||||
next unless doc.path.start_with?('_pages/')
|
||||
convert_links(doc)
|
||||
end
|
||||
|
||||
def convert_links(doc)
|
||||
open_external_links_in_new_tab = !!doc.site.config["open_external_links_in_new_tab"]
|
||||
|
||||
if open_external_links_in_new_tab
|
||||
parsed_doc = Nokogiri::HTML::DocumentFragment.parse(doc.content)
|
||||
parsed_doc.css("a:not(.internal-link):not(.footnote):not(.reversefootnote)").each do |link|
|
||||
link.set_attribute('target', '_blank')
|
||||
end
|
||||
doc.content = parsed_doc.inner_html
|
||||
end
|
||||
end
|
||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
@@ -1,3 +1,18 @@
|
||||
:root {
|
||||
--background-color: #f4f4f4;
|
||||
--color-background-1: #f4f4f4;
|
||||
--color-background-2: #e4e4e4;
|
||||
--color-primary-1: #000000;
|
||||
--color-accent: #008891;
|
||||
--color-accent-hover: #539397;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--color-background-1: #171616;
|
||||
--color-background-2: #222222;
|
||||
--color-primary-1: #eeeeee;
|
||||
--color-accent: #00B7C3;
|
||||
--color-accent-hover: #70c1c6;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
/* Custom classes for styling elements on a page, such as links */
|
||||
|
||||
.horizontal-wrapper {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 8px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.primary-action {
|
||||
background: black;
|
||||
padding: 0.8em;
|
||||
color: white;
|
||||
text-decoration-line: unset;
|
||||
border-radius: 4px;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
border-color: black;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.secondary-action {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: black;
|
||||
padding: 0.8em;
|
||||
text-decoration-line: unset;
|
||||
border-radius: 4px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.horizontal-wrapper {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
@import url('fonts.css');
|
||||
@import url('navigation.css');
|
||||
@import url('colors.css');
|
||||
@import url('content.css');
|
||||
@import url('note.css');
|
||||
|
||||
html {
|
||||
background-color: var(--background-color);
|
||||
background-color: var(--color-background-1);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'FormaDJRText', sans-serif;
|
||||
color: black;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Inter, Ubuntu, sans-serif;
|
||||
color: var(--color-primary-1);
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -35,12 +35,14 @@ footer {
|
||||
width: 100%;
|
||||
|
||||
padding-bottom: 32px;
|
||||
padding-top: 64px;
|
||||
color: dimgray;
|
||||
}
|
||||
|
||||
a {
|
||||
color: unset;
|
||||
color: var(--color-accent);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--color-accent-hover);
|
||||
}
|
||||
|
||||
/* Improve readability for smaller text, especially in long paragraphs */
|
||||
@@ -48,8 +50,8 @@ p,
|
||||
a,
|
||||
ul,
|
||||
ol {
|
||||
letter-spacing: 0.2px;
|
||||
line-height: 24px;
|
||||
line-height: 1.6em;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
h1,
|
||||
@@ -58,23 +60,23 @@ h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
margin-top: 2em;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 0.5em;
|
||||
font-family: 'FormaDJRDisplay', sans-serif;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Inter, Ubuntu, sans-serif;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3em;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
h3 {
|
||||
h2 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
@@ -10,16 +10,13 @@
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
.nav-logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.nav-title {
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
color: unset;
|
||||
}
|
||||
|
||||
.nav-logo-icon {
|
||||
height: 48px;
|
||||
width: 48px;
|
||||
.nav-title:hover {
|
||||
color: unset;
|
||||
}
|
||||
|
||||
.nav-menu {
|
||||
@@ -29,134 +26,3 @@
|
||||
padding: 0;
|
||||
gap: 32px;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.nav-link:hover {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* Hamburger Menu */
|
||||
.hamburger-toggle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.hamburger {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
cursor: pointer;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: black;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.hamburger-line {
|
||||
width: 20px;
|
||||
height: 2px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.menu-overlay {
|
||||
position: fixed;
|
||||
top: 80px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: calc(100% - 80px);
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 999;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Mobile Navigation */
|
||||
@media screen and (max-width: 768px) {
|
||||
.hamburger {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.nav-menu {
|
||||
position: fixed;
|
||||
left: -100%;
|
||||
top: 80px;
|
||||
flex-direction: column;
|
||||
background-color: var(--background-color);
|
||||
width: 100%;
|
||||
z-index: 1000;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.hamburger-toggle:checked ~ .nav-menu {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.hamburger-toggle:checked ~ .menu-overlay {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.nav-menu .nav-link {
|
||||
padding: 16px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.project-header {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
margin-top: 96px;
|
||||
}
|
||||
|
||||
.project-header h1 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.project-header .breadcrumb-wrapper {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 100%;
|
||||
margin-bottom: 0.5rem;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.project-header .breadcrumb-wrapper a {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.project-header .breadcrumb-wrapper p {
|
||||
margin: 0;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.project-footer {
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
|
||||
height: 64px;
|
||||
border-top: 1px solid black;
|
||||
border-bottom: 1px solid black;
|
||||
|
||||
padding-top: 16px;
|
||||
padding-bottom: 16px;
|
||||
margin-top: 96px;
|
||||
}
|
||||
|
||||
.project-footer div {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
12
assets/css/note.css
Normal file
@@ -0,0 +1,12 @@
|
||||
.backlink-divider {
|
||||
color: var(--color-background-2);
|
||||
}
|
||||
|
||||
.backlink-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.backlinks {
|
||||
|
||||
}
|
||||
|
Before Width: | Height: | Size: 461 KiB After Width: | Height: | Size: 461 KiB |
|
Before Width: | Height: | Size: 843 KiB After Width: | Height: | Size: 843 KiB |
|
Before Width: | Height: | Size: 2.8 MiB After Width: | Height: | Size: 2.8 MiB |
|
Before Width: | Height: | Size: 182 KiB After Width: | Height: | Size: 182 KiB |
|
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 1.9 MiB |
|
Before Width: | Height: | Size: 760 KiB After Width: | Height: | Size: 760 KiB |
|
Before Width: | Height: | Size: 941 KiB After Width: | Height: | Size: 941 KiB |
|
Before Width: | Height: | Size: 9.4 MiB After Width: | Height: | Size: 9.4 MiB |
21
index.md
@@ -1,21 +0,0 @@
|
||||
---
|
||||
title: "Brendan Chen"
|
||||
layout: default
|
||||
isIndexPage: 1
|
||||
---
|
||||
|
||||
# Brendan Chen
|
||||
|
||||
I'm Brendan, a recently graduated Software Engineering student who attended Chapman University. I'm currently an associate software engineer at Cargill. I'm also the creator of [OpenAppLock](https://github.com/brendan-ch/OpenAppLock), an open-source time blocker for iOS, and [Interchange](https://interchange.bchen.dev), a free shuttle app for Chapman students.
|
||||
|
||||
I believe that no matter which path an app's development takes, it should be grounded in real experiences.
|
||||
|
||||
Feel free to [say hi](mailto:brendan@bchen.dev)!
|
||||
|
||||
<div class="horizontal-wrapper">
|
||||
<a class="primary-action" href="/projects">View projects</a>
|
||||
<a class="secondary-action" href="/assets/resume.pdf" target="_blank">View resume</a>
|
||||
</div>
|
||||
|
||||

|
||||
|
||||
23
notes.html
@@ -1,23 +0,0 @@
|
||||
---
|
||||
title: "Notes"
|
||||
layout: default
|
||||
---
|
||||
|
||||
<h1>Notes</h1>
|
||||
|
||||
<p>I've moved publishing to my [Substack](https://bchendev.substack.com?utm_source=bchen.dev). For reference, old notes remain accessible here.</p>
|
||||
|
||||
<p></p>
|
||||
|
||||
{% assign posts_by_month = site.posts | group_by_exp: "post", "post.date | date: '%B %Y'" %}
|
||||
{% for month in posts_by_month %}
|
||||
<h2>{{ month.name }}</h2>
|
||||
<ul>
|
||||
{% for post in month.items %}
|
||||
<li>
|
||||
<time>{{ post.date | date: "%Y.%m.%d" }}</time>
|
||||
<a href="{{ post.url }}">{{ post.title }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endfor %}
|
||||
5
projects/cmes-mobile.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: Page moved
|
||||
layout: page_moved
|
||||
---
|
||||
[[CMES Mobile App]]
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
title: CMES Mobile App
|
||||
layout: project
|
||||
---
|
||||
|
||||

|
||||
|
||||
**App development project for volunteer organization Techies Without Borders.** I helped implement the CMES mobile app, facilitating the download, viewing and playback of medical education content in resource-constrained countries.
|
||||
|
||||
In the early stages, I built the download screen and business logic, the PDF viewer integration, and background audio playback using native code. Later, I built the native web view to integrate with the [CMES-Pi](https://cmesworld.org/cmes_product-Pi.html) solution, simplifying the business logic while adding support for future edge platforms.
|
||||
|
||||
The app's front-end uses Capacitor and Angular, with several Capacitor plugins in Swift + Java to bundle native functionality.
|
||||
|
||||
This app is actively used in medical facilities in Uganda and Nepal, to name a few.
|
||||
|
||||
[App Store](https://apps.apple.com/us/app/cmes/id1276335909){: .primary-action }
|
||||
[Google Play](https://play.google.com/store/apps/details?id=org.cmesworld.www.cmes_piandroid_app){: .primary-action }
|
||||
[Techies Without Borders](https://techieswithoutborders.us/){: .secondary-action }
|
||||
[CMES Project](https://cmesworld.org/){: .secondary-action }
|
||||
{: .horizontal-wrapper }
|
||||
5
projects/dont-wake-sandie.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: Page moved
|
||||
layout: page_moved
|
||||
---
|
||||
[[Don't Wake Sandie]]
|
||||
5
projects/interchange.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: Page moved
|
||||
layout: page_moved
|
||||
---
|
||||
[[Interchange]]
|
||||
@@ -1,16 +0,0 @@
|
||||
---
|
||||
title: Interchange
|
||||
layout: project
|
||||
---
|
||||
|
||||

|
||||
|
||||
**An iOS app for college transit, currently available for Chapman University.**
|
||||
View routes, shuttle locations, and estimated arrival times in a sleek,
|
||||
easy-to-use interface. I built this app using SwiftUI, Node.js and Apollo
|
||||
GraphQL, with Redis as a primary storage mechanism.
|
||||
|
||||
[Download on the App Store](https://apps.apple.com/us/app/interchange-college-commute/id6739968742){: .primary-action }
|
||||
[Join the TestFlight](https://testflight.apple.com/join/HbuHfUgW){: .secondary-action }
|
||||
[Website](https://interchange.bchen.dev/){: .secondary-action }
|
||||
{: .horizontal-wrapper }
|
||||
5
projects/power-to-the-people.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: Page moved
|
||||
layout: page_moved
|
||||
---
|
||||
[[Power to the People]]
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
title: Power to the People
|
||||
layout: project
|
||||
---
|
||||
|
||||

|
||||
|
||||
**An iPhone app designed to make traffic stops safer for marginalized communities.** Record interactions with police, assign and text emergency contacts, and be informed of your rights. I worked in a student team of 4, including [David Sohn](https://www.linkedin.com/in/sohn-david/), [Jacob Zamore](https://www.linkedin.com/in/jacob-zamore/), and [Patrick Smith](https://www.linkedin.com/in/patrick-smith-ld/), to prototype and build a proof of concept in 36 hours. The app was selected for the **President's Pick award** at [CruzHacks 2024](https://cruzhacks.com/), the premier hackathon of UC Santa Cruz.
|
||||
|
||||
|
||||
[GitHub](https://github.com/brendan-ch/PowerToThePeople){: .primary-action }
|
||||
[Devpost](https://devpost.com/software/power-to-the-people-lbhxyd){: .secondary-action }
|
||||
{: .horizontal-wrapper }
|
||||
|
||||
My contribution was the iOS app prototype, of which a demo is shown below. I built the primary user flow of recording a police interaction, simultaneously using the front and back iPhone cameras to capture all angles. Activating the recording also displays the "Know Your Rights" screen, displaying basic dos and don'ts. Finally, I built a way to view previous recordings and share the files using the iOS share sheet.
|
||||
|
||||
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/Un8JWXkpcW4?si=CfQ0qOuUjSpas0ny&start=89" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
|
||||
5
projects/resonance.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: Page moved
|
||||
layout: page_moved
|
||||
---
|
||||
[[Resonance]]
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
title: Resonance
|
||||
layout: project
|
||||
---
|
||||
|
||||

|
||||
|
||||
Resonance is a cyberpunk-themed game show shooter. Enter the arena, blast your opponents, and become the star of the show. I worked with a student team of 12 to create this game over multiple months. The game was selected as an **IEEE GameSig finalist**.
|
||||
|
||||
*Steam link coming soon!*
|
||||
|
||||
My core role was to implement the game's networking logic using PurrNet, an open-source library for Unity.
|
||||
|
||||
My first project was to implement networking for **Arena**, a free-for-all PVP game mode, where the player with the most eliminations wins. I learned how to isolate game logic on the server and synchronize state on each client. The networked API for Arena is used by every aspect of the game, including the scoreboard UI and spotlight mechanic. Following a pivot in the game’s core direction, I successfully handed this system to another engineer, who implemented a rating system for style points.
|
||||
|
||||
Another project was the **build system**. Having a server dependency and Steam integration meant that the environment differed from development to production. We found it painful to prepare a build for each class-mandated playtest. With the help of Claude, I created a two-step system: an editor script to build and configure dependencies in one step, and a nightly GitHub Actions pipeline. Besides simplifying the build process, this created another positive side effect by ensuring we fixed compiler issues proactively.
|
||||
|
||||
I'm currently working on a client-side prediction model to match other FPS games on the market.
|
||||
|
||||
Even with our team size, the most important thing I learned was how to manage scope and scale. Each one of us pushed ourselves to the limits of our knowledge, going from bare theoretical understanding to practical working implementations. The goal was never to get things right on the first try; it was to learn and realize new ideas in multiple passes.
|
||||
5
projects/sourcebook.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: Page moved
|
||||
layout: page_moved
|
||||
---
|
||||
[[Sourcebook]]
|
||||
5
projects/tad-pool.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: Page moved
|
||||
layout: page_moved
|
||||
---
|
||||
[[Tad-Pool]]
|
||||
@@ -1,21 +0,0 @@
|
||||
---
|
||||
title: Tad-Pool
|
||||
layout: project
|
||||
---
|
||||
|
||||

|
||||
|
||||
**A mobile app designed to make carpooling easier for college students.** Find
|
||||
carpools based on time and location, and get rewarded for helping to reduce CO2
|
||||
emissions. I worked with [Jazmine Stanek](https://www.linkedin.com/in/vjazminestanek/)
|
||||
and [Isabelle Woo](https://www.linkedin.com/in/isabellewoo/) to create the UI/UX
|
||||
design using Figma and FigJam, in a period of 24 hours for
|
||||
[FullyBeyond 2024](https://fullybeyond.acmcsuf.com/).
|
||||
|
||||
The project reached the finalist judging stage.
|
||||
|
||||
[High-fidelity design](https://www.figma.com/design/nTKCcwftKrg27wlP5mmy9e/Tad-Pool-Design?node-id=13-1837&node-type=canvas&t=YKuVvM6RAPs4hTIo-11){: .primary-action }
|
||||
[Low-fidelity design](https://www.figma.com/design/nTKCcwftKrg27wlP5mmy9e/Tad-Pool-Design?node-id=0-1&node-type=canvas&t=YKuVvM6RAPs4hTIo-11){: .secondary-action }
|
||||
[FigJam board](https://www.figma.com/board/Dct7GuhKFYq8maw0KoAVBV/Product-Brainstorm-FullyBeyond?node-id=0-1&node-type=canvas&t=g7RKLLo5IoU84ndk-11){: .secondary-action }
|
||||
[Devpost](https://devpost.com/software/tad-pool){: .secondary-action }
|
||||
{: .horizontal-wrapper }
|
||||
5
projects/teech-ai-cal-hacks-12.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: Page moved
|
||||
layout: page_moved
|
||||
---
|
||||
[[teech.ai]]
|
||||
@@ -1,19 +0,0 @@
|
||||
---
|
||||
title: teech.ai
|
||||
layout: project
|
||||
---
|
||||
|
||||
<iframe width="100%" height="480" src="https://www.youtube-nocookie.com/embed/luVIdLB9id0?si=1sTE9Z3MeyWntRmd" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
|
||||
|
||||
**A proof-of-concept tutoring app for students learning math.** Using the phone camera, the app sees and understands math problems, guiding students through problems with hints and step-by-step questions. We added text-to-speech (TTS) to give our app a voice, further engaging students and making it feel like a more personal experience.
|
||||
|
||||
[Tri Vu](https://www.linkedin.com/in/tri-q-vu/) built a web interface prototype and Python-based backend utilizing the Gemini Vision API and Fish Audio for TTS.
|
||||
|
||||
I built a native proof-of-concept using SwiftUI, AVFoundation, and other native frameworks. In the app, I built a chat and camera interface, integrating with Tri's backend by sending image and prompt data. Swiping to the right reveals the user's chat history, which is also continuously synced with the backend. Design-wise, I decided to use this gesture-based approach in order to bring the camera interface front and center.
|
||||
|
||||
The app was built in 48 hours for **Cal Hacks 12.0**, the world's largest collegiate hackathon.
|
||||
|
||||
[GitHub](https://github.com/brendan-ch/teecher-ios){: .primary-action }
|
||||
[Devpost](https://devpost.com/software/teech-ai){: .secondary-action }
|
||||
[Cal Hacks](https://www.calhacks.io){: .secondary-action }
|
||||
{: .horizontal-wrapper }
|
||||
5
projects/the-farmers-dog-trivia.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: Page moved
|
||||
layout: page_moved
|
||||
---
|
||||
[[The Farmer's Dog Trivia Game]]
|
||||
6
projects/timestamp.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
title: Timestamp
|
||||
layout: page_moved
|
||||
---
|
||||
[[Timestamp]]
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
---
|
||||
title: Timestamp
|
||||
layout: project
|
||||
---
|
||||
|
||||

|
||||
|
||||
A simple, unintrusive Pomodoro timer for macOS which lives in the menu bar. I built it with SwiftUI and made it open source.
|
||||
|
||||
[Download the app](https://github.com/brendan-ch/timestamp/releases){: .primary-action }
|
||||
[View the code](https://github.com/brendan-ch/timestamp){: .secondary-action }
|
||||
{: .horizontal-wrapper }
|
||||