Compare commits

...
4 Commits
Author SHA1 Message Date
haopengzhan 17b3e2d7d8 ci: add Gitea Actions workflow for container build and registry publish 2026-09-16 23:11:22 -07:00
haopengzhan 95a7dcb93b feat(admin): add ephemeral staging DB, read-only posts overview, and dynamic search
- Implement ephemeral staging mode (APP_ENV=staging) to isolate test/dev DB writes in /tmp
- Add read-only posts management dashboard under /guanli/posts with live links and comment counters
- Add hot re-indexing endpoint (/guanli/reindex) to refresh Markdown without restarting server
- Replace static recommended search config with dynamic frequency-based trending searches
- Remove early scaffold mockup file (dropbox/zh.md) and clean legacy mock test queries
- Add native Makefile with dev, dev-persistent, test, lint, and docker targets
- Update test suite and documentation for development workflows
2026-09-16 23:09:14 -07:00
haopengzhan f52b3fe9e4 feat(migration): complete WordPress migration and UX refinements
- Migrate historical posts, localized assets, and 103 comments
- Colocate media into posts/<slug>/images/ and remove wp-content
- Add native bilingual support for ai-can-change post (English default, /zh variant)
- Support clean taxonomy routing (/category/:category and /tag/:tag)
- Dynamically populate categories and tags in sitemap.xml
- Fix header scroll padding/margin offsets for anchor and reference links
- Constrain image and media widths to prevent content overflow
- Redesign homepage recent posts to 4 compact editorial cards
- Restore valid binary favicon.ico and high-res icon assets
- Remove placeholder content and sample test comments
2026-09-16 23:09:09 -07:00
haopengzhan 8c0b04db0e feat: initial release of pengzhan.dev personal editorial blog platform
- Editorial web platform built with Node.js, Express, EJS, and Vanilla CSS
- Markdown content engine with YAML frontmatter, bilingual variants, and hot-reload watcher
- Unified SQLite with FTS5 search indexing, query logging, and schema filters
- Centered editorial post layout with margin-docked left TOC and mobile bottom sheet
- WordPress-grade comment system with moderation dashboard and bulk operations under /guanli
- Complete SEO suite (JSON-LD structured data, RSS/Atom feeds, XML sitemap, robots.txt)
- CI/CD workflows for linting, security audits (Gitleaks), automated tests, and Docker releases
2026-09-16 23:08:53 -07:00
195 changed files with 12205 additions and 3984 deletions
-3
View File
@@ -1,3 +0,0 @@
---
BUNDLE_PATH: "_vendor/bundle"
BUNDLE_DISABLE_SHARED_GEMS: "true"
+5 -5
View File
@@ -1,7 +1,7 @@
.git
node_modules/
npm-debug.log
.git/
.gitignore
_site
.sass-cache
.agent/
sample-content/
.DS_Store
_vendor
*.log
+36
View File
@@ -0,0 +1,36 @@
name: Build and Push Docker Image
on:
push:
branches: [ "main" ]
jobs:
build:
runs-on: ubuntu-latest
env:
DOCKER_HOST: unix:///var/run/docker.sock
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Log in to Gitea Registry
run: echo "${{ secrets.PUSH_TOKEN }}" | docker login git.pengzhan.dev -u "${{ github.actor }}" --password-stdin
- name: Build and push Docker image
run: |
REPO_LOWER=$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]')
IMAGE_NAME="git.pengzhan.dev/$REPO_LOWER"
REPO_URL="${{ github.server_url }}/${{ github.repository }}"
echo "Building image: $IMAGE_NAME with source link: $REPO_URL"
docker build \
--network host \
--label "org.opencontainers.image.source=$REPO_URL" \
-t $IMAGE_NAME:latest \
-t $IMAGE_NAME:${{ github.sha }} .
docker push $IMAGE_NAME:latest
docker push $IMAGE_NAME:${{ github.sha }}
+44
View File
@@ -0,0 +1,44 @@
name: CI Quality & Security Pipeline
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
security-scan:
name: Gitleaks Secret Scan
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Run Gitleaks
uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
test-and-lint:
name: Lint & Automated Tests
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js 22
uses: actions/setup-node@v4
with:
node-version: 22
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run Code Linter
run: npm run lint
- name: Run Automated Test Suites
run: npm test
+52
View File
@@ -0,0 +1,52 @@
name: Docker Build & Release
on:
push:
branches: [ main ]
tags: [ 'v*.*.*' ]
jobs:
build-and-push:
name: Build & Push Container Image
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up QEMU (Multi-platform emulation)
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry (ghcr.io)
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract Docker metadata (tags, labels)
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}
type=sha,format=short,prefix=sha-
type=semver,pattern={{version}}
- name: Build and Push Docker Image
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
+21 -3
View File
@@ -1,4 +1,22 @@
_site
.sass-cache
# Dependencies
node_modules/
# OS and Environment
.DS_Store
_vendor
.env
.agent/
# Logs
*.log
npm-debug.log*
# SQLite Database & WAL / Journal files (runtime persistent state)
*.db
*.db-journal
*.db-wal
*.db-shm
**/data/*.db*
# Raw WordPress XML Exports / Backups
*.WordPress.*.xml
*.wxr.xml
-11
View File
@@ -1,11 +0,0 @@
language: ruby
rvm:
- 2.5.0
script: bundle exec jekyll build
install: bundle install
notifications:
email: false
-1
View File
@@ -1 +0,0 @@
resume-template.joelglovier.com
+24 -6
View File
@@ -1,11 +1,29 @@
FROM ruby:2.7
# Multi-stage lightweight build for pengzhan.dev skeleton
FROM node:22-alpine AS dependencies
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
WORKDIR /home/app
FROM node:22-alpine AS runner
WORKDIR /app
COPY Gemfile* ./
ENV NODE_ENV=production
ENV PORT=3000
RUN bundle install
# Copy node_modules from dependencies stage
COPY --from=dependencies /app/node_modules ./node_modules
COPY package*.json ./
COPY . .
# Copy skeleton web framework code
COPY server/ ./server/
COPY views/ ./views/
COPY public/ ./public/
CMD [ "bundle", "exec", "jekyll", "serve" ]
# Persistent Volume mount point
VOLUME /app/content
EXPOSE 3000
USER node
CMD ["node", "server/index.js"]
-8
View File
@@ -1,8 +0,0 @@
source "https://rubygems.org"
gem 'github-pages'
# this gem provides regeneration support improvements on Windows
gem 'wdm', '>= 0.1.0' if Gem.win_platform?
gem "webrick"
gem "rexml", "~> 3.2"
-281
View File
@@ -1,281 +0,0 @@
GEM
remote: https://rubygems.org/
specs:
activesupport (6.0.6.1)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
tzinfo (~> 1.1)
zeitwerk (~> 2.2, >= 2.2.2)
addressable (2.8.1)
public_suffix (>= 2.0.2, < 6.0)
coffee-script (2.4.1)
coffee-script-source
execjs
coffee-script-source (1.11.1)
colorator (1.1.0)
commonmarker (0.17.13)
ruby-enum (~> 0.5)
concurrent-ruby (1.2.0)
dnsruby (1.61.3)
addressable (~> 2.5)
em-websocket (0.5.1)
eventmachine (>= 0.12.9)
http_parser.rb (~> 0.6.0)
ethon (0.15.0)
ffi (>= 1.15.0)
eventmachine (1.2.7)
eventmachine (1.2.7-x64-mingw32)
execjs (2.8.1)
faraday (1.10.2)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
faraday-excon (~> 1.1)
faraday-httpclient (~> 1.0)
faraday-multipart (~> 1.0)
faraday-net_http (~> 1.0)
faraday-net_http_persistent (~> 1.0)
faraday-patron (~> 1.0)
faraday-rack (~> 1.0)
faraday-retry (~> 1.0)
ruby2_keywords (>= 0.0.4)
faraday-em_http (1.0.0)
faraday-em_synchrony (1.0.0)
faraday-excon (1.1.0)
faraday-httpclient (1.0.1)
faraday-multipart (1.0.4)
multipart-post (~> 2)
faraday-net_http (1.0.1)
faraday-net_http_persistent (1.2.0)
faraday-patron (1.0.0)
faraday-rack (1.0.0)
faraday-retry (1.0.3)
ffi (1.15.5)
ffi (1.15.5-x64-mingw32)
forwardable-extended (2.6.0)
gemoji (3.0.1)
github-pages (205)
github-pages-health-check (= 1.16.1)
jekyll (= 3.8.7)
jekyll-avatar (= 0.7.0)
jekyll-coffeescript (= 1.1.1)
jekyll-commonmark-ghpages (= 0.1.6)
jekyll-default-layout (= 0.1.4)
jekyll-feed (= 0.13.0)
jekyll-gist (= 1.5.0)
jekyll-github-metadata (= 2.13.0)
jekyll-mentions (= 1.5.1)
jekyll-optional-front-matter (= 0.3.2)
jekyll-paginate (= 1.1.0)
jekyll-readme-index (= 0.3.0)
jekyll-redirect-from (= 0.15.0)
jekyll-relative-links (= 0.6.1)
jekyll-remote-theme (= 0.4.1)
jekyll-sass-converter (= 1.5.2)
jekyll-seo-tag (= 2.6.1)
jekyll-sitemap (= 1.4.0)
jekyll-swiss (= 1.0.0)
jekyll-theme-architect (= 0.1.1)
jekyll-theme-cayman (= 0.1.1)
jekyll-theme-dinky (= 0.1.1)
jekyll-theme-hacker (= 0.1.1)
jekyll-theme-leap-day (= 0.1.1)
jekyll-theme-merlot (= 0.1.1)
jekyll-theme-midnight (= 0.1.1)
jekyll-theme-minimal (= 0.1.1)
jekyll-theme-modernist (= 0.1.1)
jekyll-theme-primer (= 0.5.4)
jekyll-theme-slate (= 0.1.1)
jekyll-theme-tactile (= 0.1.1)
jekyll-theme-time-machine (= 0.1.1)
jekyll-titles-from-headings (= 0.5.3)
jemoji (= 0.11.1)
kramdown (= 1.17.0)
liquid (= 4.0.3)
mercenary (~> 0.3)
minima (= 2.5.1)
nokogiri (>= 1.10.4, < 2.0)
rouge (= 3.13.0)
terminal-table (~> 1.4)
github-pages-health-check (1.16.1)
addressable (~> 2.3)
dnsruby (~> 1.60)
octokit (~> 4.0)
public_suffix (~> 3.0)
typhoeus (~> 1.3)
html-pipeline (2.14.3)
activesupport (>= 2)
nokogiri (>= 1.4)
http_parser.rb (0.6.0)
i18n (0.9.5)
concurrent-ruby (~> 1.0)
jekyll (3.8.7)
addressable (~> 2.4)
colorator (~> 1.0)
em-websocket (~> 0.5)
i18n (~> 0.7)
jekyll-sass-converter (~> 1.0)
jekyll-watch (~> 2.0)
kramdown (~> 1.14)
liquid (~> 4.0)
mercenary (~> 0.3.3)
pathutil (~> 0.9)
rouge (>= 1.7, < 4)
safe_yaml (~> 1.0)
jekyll-avatar (0.7.0)
jekyll (>= 3.0, < 5.0)
jekyll-coffeescript (1.1.1)
coffee-script (~> 2.2)
coffee-script-source (~> 1.11.1)
jekyll-commonmark (1.3.1)
commonmarker (~> 0.14)
jekyll (>= 3.7, < 5.0)
jekyll-commonmark-ghpages (0.1.6)
commonmarker (~> 0.17.6)
jekyll-commonmark (~> 1.2)
rouge (>= 2.0, < 4.0)
jekyll-default-layout (0.1.4)
jekyll (~> 3.0)
jekyll-feed (0.13.0)
jekyll (>= 3.7, < 5.0)
jekyll-gist (1.5.0)
octokit (~> 4.2)
jekyll-github-metadata (2.13.0)
jekyll (>= 3.4, < 5.0)
octokit (~> 4.0, != 4.4.0)
jekyll-mentions (1.5.1)
html-pipeline (~> 2.3)
jekyll (>= 3.7, < 5.0)
jekyll-optional-front-matter (0.3.2)
jekyll (>= 3.0, < 5.0)
jekyll-paginate (1.1.0)
jekyll-readme-index (0.3.0)
jekyll (>= 3.0, < 5.0)
jekyll-redirect-from (0.15.0)
jekyll (>= 3.3, < 5.0)
jekyll-relative-links (0.6.1)
jekyll (>= 3.3, < 5.0)
jekyll-remote-theme (0.4.1)
addressable (~> 2.0)
jekyll (>= 3.5, < 5.0)
rubyzip (>= 1.3.0)
jekyll-sass-converter (1.5.2)
sass (~> 3.4)
jekyll-seo-tag (2.6.1)
jekyll (>= 3.3, < 5.0)
jekyll-sitemap (1.4.0)
jekyll (>= 3.7, < 5.0)
jekyll-swiss (1.0.0)
jekyll-theme-architect (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-cayman (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-dinky (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-hacker (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-leap-day (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-merlot (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-midnight (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-minimal (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-modernist (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-primer (0.5.4)
jekyll (> 3.5, < 5.0)
jekyll-github-metadata (~> 2.9)
jekyll-seo-tag (~> 2.0)
jekyll-theme-slate (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-tactile (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-time-machine (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-titles-from-headings (0.5.3)
jekyll (>= 3.3, < 5.0)
jekyll-watch (2.2.1)
listen (~> 3.0)
jemoji (0.11.1)
gemoji (~> 3.0)
html-pipeline (~> 2.2)
jekyll (>= 3.0, < 5.0)
kramdown (1.17.0)
liquid (4.0.3)
listen (3.7.1)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
mercenary (0.3.6)
mini_portile2 (2.8.1)
minima (2.5.1)
jekyll (>= 3.5, < 5.0)
jekyll-feed (~> 0.9)
jekyll-seo-tag (~> 2.1)
minitest (5.17.0)
multipart-post (2.1.1)
nokogiri (1.14.1)
mini_portile2 (~> 2.8.0)
racc (~> 1.4)
nokogiri (1.14.1-x64-mingw32)
racc (~> 1.4)
octokit (4.22.0)
faraday (>= 0.9)
sawyer (~> 0.8.0, >= 0.5.3)
pathutil (0.16.2)
forwardable-extended (~> 2.6)
public_suffix (3.1.1)
racc (1.6.2)
rb-fsevent (0.10.4)
rb-inotify (0.10.1)
ffi (~> 1.0)
rexml (3.2.6)
rouge (3.13.0)
ruby-enum (0.8.0)
i18n
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
safe_yaml (1.0.5)
sass (3.7.4)
sass-listen (~> 4.0.0)
sass-listen (4.0.0)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
sawyer (0.8.2)
addressable (>= 2.3.5)
faraday (> 0.8, < 2.0)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
thread_safe (0.3.6)
typhoeus (1.4.0)
ethon (>= 0.9.0)
tzinfo (1.2.11)
thread_safe (~> 0.1)
unicode-display_width (1.7.0)
webrick (1.8.1)
zeitwerk (2.6.6)
PLATFORMS
ruby
x64-mingw32
DEPENDENCIES
github-pages
rexml (~> 3.2)
webrick
BUNDLED WITH
2.1.4
-22
View File
@@ -1,22 +0,0 @@
The MIT License (MIT)
Copyright (c) 2015 Joel Glovier
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.
+52
View File
@@ -0,0 +1,52 @@
.PHONY: help dev dev-persistent test lint clean docker-build docker-run
# Default target
all: help
help:
@echo "========================================================================"
@echo " pengzhan.dev — Engineering Makefile"
@echo "========================================================================"
@echo "Available commands:"
@echo " make dev Start local server in Ephemeral Staging Mode"
@echo " (Safe by default: site.db is NEVER mutated)"
@echo " make dev-persistent Start local server in Persistent DB Mode"
@echo " (Changes to comments & logs write to site.db)"
@echo " make test Run complete automated test suite (in isolated temp DB)"
@echo " make lint Run comprehensive project linter (JS, EJS, CSS)"
@echo " make clean Remove temporary staging DBs and build caches"
@echo " make docker-build Build multi-stage production Docker image"
@echo " make docker-run Run production Docker container on port 3000"
@echo "========================================================================"
dev:
@echo "Starting dev server in Safe Ephemeral Staging Mode..."
@echo "-> Production sample-content/data/site.db is protected from writes."
APP_ENV=staging node --watch --watch-path=server --watch-path=views --watch-path=public server/index.js
dev-persistent:
@echo "Starting dev server in Persistent DB Mode..."
@echo "-> Writes will be recorded to sample-content/data/site.db."
node --watch --watch-path=server --watch-path=views --watch-path=public server/index.js
test:
@echo "Running automated test suite..."
NODE_ENV=test node test/test.js
lint:
@echo "Running project linter..."
node scripts/lint.js
clean:
@echo "Cleaning temporary test and staging artifacts..."
@rm -f /tmp/site.staging-*.db /tmp/site.staging-*.db-*
@rm -f sample-content/data/*.db-journal sample-content/data/*.db-wal sample-content/data/*.db-shm
@echo "Clean complete."
docker-build:
@echo "Building Docker image pengzhan.dev:latest..."
docker build -t pengzhan.dev:latest .
docker-run:
@echo "Running Docker container on http://localhost:3000..."
docker run --rm -p 3000:3000 -v $(PWD)/sample-content:/app/content pengzhan.dev:latest
+230 -36
View File
@@ -1,66 +1,260 @@
# Resume template
# pengzhan.dev — Dynamic Web Framework & Personal Site Skeleton
*A simple Jekyll + GitHub Pages powered resume template.*
A custom, ultra-lightweight dynamic web framework designed to replicate and replace WordPress for **pengzhan.dev**, optimized for containerized deployment on Kubernetes.
![img](images/screenshot.png)
---
## Docs
## 1. Architectural Model: Skeleton vs. Persistent Volume
### Running locally
The architecture separates the **web runtime container (the "Skeleton")** from your **content and extensions (the "Persistent Volume")**:
To test locally, run the following in your terminal:
```plaintext
Kubernetes Pod
┌───────────────────────────────────────────────┐
│ Docker Container (The "Skeleton") │
│ - Node.js 22 + Express │
│ - SSR EJS Templates │
│ - Markdown-it + PrismJS (Okaidia) │
│ - Structured Schema Search Engine │
│ - Dynamic Plugin Loader │
│ - Hot-reload Watcher (Chokidar) │
└───────────────────────┬───────────────────────┘
│ mounts /app/content
▼
┌───────────────────────────────────────────────┐
│ Persistent Volume (Your Content & Data) │
│ - posts/ (Blog articles & bilinguals) │
│ - pages/ (Standalone timeless pages) │
│ - plugins/ (Custom executable plugins) │
│ - static/ (Fonts, PDFs, Images) │
│ - css/ (custom.css overrides) │
│ - data/ (site.db SQLite) │
│ - config.yaml (Site metadata & settings) │
└───────────────────────────────────────────────┘
```
1. Clone repo locally
1. `bundle install`
2. `bundle exec jekyll serve`
3. Open your browser to `localhost:4000`
- **Zero Container Restarts**: Editing markdown files, uploading PDFs/images, adding new pages, or changing `config.yaml` immediately hot-reloads in memory via `chokidar`.
- **Tiny Footprint**: Multi-stage Docker image weighs ~25MB (no React/Vue hydration overhead, no headless browser, no heavy DB daemon).
### Running locally with Docker
---
To test locally with docker, run the following in your terminal after installing docker into your system:
## 2. Directory Structure on Persistent Volume (`/app/content`)
1. `docker image build -t resume-template .`
2. `docker run --rm --name resume-template -v "$PWD":/home/app --network host resume-template`
```plaintext
content/
├── config.yaml # Site title, tagline, social links, author bio
├── posts/ # Blog articles
│ ├── using-gemini-cli-and-mcp-server-to-organize-my-dropbox/
│ │ ├── index.md # Default post (English or Chinese)
│ │ ├── zh.md # Chinese translation variant
│ │ └── images/ # Post assets
│ │ └── cover.png
│ └── kubelet-source-code-deep-dive/
│ └── index.md
├── pages/ # Standalone timeless pages
│ ├── home.md # Homepage / About-me (mapped to /)
│ ├── quick-links.md # Quick links page (mapped to /quick-links)
│ └── resume.md # Custom standalone page (mapped to /resume)
├── plugins/ # Executable plugins & extensions
│ ├── google-analytics/
│ │ ├── plugin.json
│ │ └── index.js
│ └── reading-time/
│ ├── plugin.json
│ └── index.js
├── static/ # Static files served directly at root
│ ├── fonts/ # XiaoheSimplifySerif-VF, Maple Mono NF CN
│ ├── documents/ # CV.pdf, research papers
│ └── images/ # Favicon, profile avatars
├── css/
│ └── custom.css # User CSS loaded after base styles
└── data/
└── site.db # SQLite database (auto-created by server)
```
### Customizing
---
First you'll want to fork the repo to your own account. Then clone it locally and customize, or use the GitHub web editor to customize.
## 3. Posts vs. Pages
#### Options/configuration
| Feature | `posts/` | `pages/` |
| --- | --- | --- |
| **Role** | Time-stamped, categorized blog articles. | Timeless, standalone site destinations (e.g. Home, Quick Links). |
| **Routing** | Dedicated blog router: `/<slug>` and `/<slug>/<lang>`. | Direct top-level route based on filename or permalink (`/quick-links`, `/resume`). |
| **Taxonomy** | Dynamically feeds `category`, `tags: [...]`, and author metadata. | No tags or dates required; does not pollute blog streams. |
| **Stream & RSS** | Appears in `/blogs` stream, pagination, and `/feed` (RSS 2.0). | **Excluded** from blog streams, pagination, and RSS feeds. |
| **Bilingual** | Sibling translation files (`index.md` + `zh.md` / `en.md`) with in-page switcher. | Self-contained markdown or HTML with custom layout control. |
Most of the basic customization will take place in the `/_config.yml` file. Here is a list of customizations available via `/_config.yml`:
---
[...write these out...]
## 4. 100% Dynamic Taxonomy (Tags & Categories)
#### Editing content
- **Zero Static Configuration**: Tags are **never** declared statically in `config.yaml`.
- Whenever a post specifies `tags: [k8s, cloud, "AI Era"]`, the content engine dynamically discovers, normalizes, and aggregates it with count statistics.
- The `/search` page automatically derives its default discovery pills from the top active tags and categories.
Most of the content configuration will take place in the `/_layouts/resume.html` file. Simply edit the markup there accordingly
---
### Publishing to GitHub Pages for free
## 5. Structured Schema Search Engine
[GitHub Pages](https://pages.github.com/) will host this for free with your GitHub account. Just make sure you're using a `gh-pages` branch, and the site will automatically be available at `yourusername.github.io/resume-template` (you can rename the repo to resume for your own use if you want it to be available at `yourusername.github.io/resume`). You can also add a CNAME if you want it to be available at a custom domain...
The built-in search engine supports a full query DSL combining metadata filters, exact phrases, and free-text keywords:
### Configuring with your own domain name
```text
tag:k8s "container image" category:tech cgroup
```
To setup your GH Pages site with a custom domain, [follow the instructions](https://help.github.com/articles/setting-up-a-custom-domain-with-github-pages/) on the GitHub Help site for that topic.
### Search Syntax Capabilities
### Themes
- **Schema Filters**:
- `tag:<name>` or `tags:<name>` (e.g. `tag:k8s`)
- `category:<name>` or `cat:<name>` (e.g. `category:tech`)
- `author:<name>`
- `lang:<en|zh>`
- **Exact Phrases**: Quoted strings (`"container image"`) requiring exact substring match.
- **Keywords**: Bare words searched across title, tags, excerpt, and full markdown body.
- **Ranking & Highlighting**: Returns relevance-scored results with contextual snippets wrapped in `<mark>...</mark>`.
- **Deduplication**: Bilingual versions of the same post are unified so you never get duplicate cards for the same post.
Right now resume-template only has one theme. More are coming :soon: though. :heart:
---
## Roadmap
## 6. Plugin & Extension Architecture (`plugins/`)
A feature roadmap is [available here](https://github.com/jglovier/resume-template/projects/1). If you features suggestions, please [open a new issue](https://github.com/jglovier/resume-template/issues/new).
Plugins are self-contained folders placed in `/app/content/plugins/<plugin-name>/`:
## Contributing
```javascript
// plugins/my-plugin/index.js
module.exports = {
// 1. Initialize Express middleware or routes
onInit({ app, config, pluginConfig }) {},
If you spot a bug, or want to improve the code, or even make the dummy content better, you can do the following:
// 2. Extend markdown-it renderer (e.g. custom fences, math, diagrams)
onMarkdownInit({ markdownIt, pluginConfig }) {},
1. [Open an issue](https://github.com/jglovier/resume-template/issues/new) describing the bug or feature idea
2. Fork the project, make changes, and submit a pull request
// 3. Transform or augment post metadata
onPostLoad({ post, pluginConfig }) {},
## License
// 4. Inject into HTML head or body
injectHead({ req, pageType, post }) {
return `<script src="..."></script>`;
},
The code and styles are licensed under the MIT license. [See project license.](LICENSE) Obviously you should not use the content of this demo repo in your own resume. :wink:
injectPostEnd({ post }) {
return `<div>Custom author bio or reaction buttons</div>`;
},
Disclaimer: Use of Lisa M. Simpson image and name used under [Fair Use](https://en.wikipedia.org/wiki/Fair_use) for educational purposes. Project license does not apply to use of this material.
// 5. Lifecycle event: new comment posted
async onCommentCreated({ comment, post }) {}
};
```
---
## 7. Bilingual Post Authoring
In `/app/content/posts/<post-slug>/`:
### Default Language (`index.md`)
```yaml
---
title: "Using Gemini-CLI and MCP server to organize my dropbox"
date: 2025-08-31
author: "haopengzhan"
category: "Tech"
tags: ["AI Era", "Dropbox", "Gemini", "k8s"]
excerpt: "How I automated Dropbox organization with MCP."
lang: en
---
Post content in English...
```
### Translation Variant (`zh.md`)
```yaml
---
title: "使用 Gemini-CLI 与 MCP 服务整理 Dropbox"
lang: zh
---
Post content in Chinese...
```
### Routing
- `https://pengzhan.dev/<post-slug>` &rarr; Serves default version (`index.md`).
- `https://pengzhan.dev/<post-slug>/zh` &rarr; Serves Chinese version (`zh.md`).
- Both are linked via the in-page language switcher pill (`🌐 EN | 中文`).
- Only **one** entry appears in `/blogs` and `/search`.
---
## 8. Development & Makefile Workflows
The platform includes a native `Makefile` designed to protect your production data while developing and testing:
```bash
# 1. Install dependencies
npm install
# 2. Run local development in Safe Ephemeral Staging Mode (Default)
make dev
# -> Copies site.db to a temp file in /tmp so your real database is NEVER mutated.
# -> Discards all test queries and mock comments automatically on exit.
# 3. Run development in Persistent Mode (only when you intentionally want to save changes to site.db)
make dev-persistent
# 4. Run automated test suite (in isolated temp DB)
make test
# 5. Run project linter
make lint
# 6. Clean temporary staging artifacts
make clean
```
### Database Isolation Architecture
- **Safe by Default**: Running `npm run dev` or `make dev` automatically runs with `APP_ENV=staging`.
- **Ephemeral Sandbox**: In staging or test mode (`NODE_ENV=test`), `server/services/db.js` mirrors the real SQLite database into a temporary sandbox in `os.tmpdir()`. You get 100% full application functionality, but all writes are discarded on exit, keeping `sample-content/data/site.db` 100% pristine.
### Docker Build
```bash
docker build -t pengzhan-web:latest .
```
### Run Container with Mounted Persistent Volume
```bash
docker run -d \
-p 3000:3000 \
-v /path/to/my-content:/app/content \
--name pengzhan-site \
pengzhan-web:latest
```
### Kubernetes Deployment Example
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: pengzhan-web
spec:
replicas: 1
template:
spec:
containers:
- name: web
image: pengzhan-web:latest
ports:
- containerPort: 3000
volumeMounts:
- name: content
mountPath: /app/content
volumes:
- name: content
persistentVolumeClaim:
claimName: pengzhan-content-pvc
```
-1848
View File
File diff suppressed because one or more lines are too long
-17
View File
@@ -1,17 +0,0 @@
<svg class="icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 28 28" enable-background="new 0 0 28 28" xml:space="preserve">
<path id="Dribbble" fill="#D1CECC" d="M13.76,14.4c0.37-0.15,0.78-0.26,1.16-0.43c-0.18-0.34-0.35-0.73-0.5-1.04
c-0.06-0.11-0.1-0.25-0.2-0.33c-2.49,0.72-5.25,1.17-8.48,1.16c-0.06,2.6,0.88,4.41,2.12,5.79C9.3,17.33,11.22,15.46,13.76,14.4z
M15.45,15.38c-2.87,1.04-5.13,2.68-6.53,5.19c0.95,0.74,2.22,1.38,3.72,1.65c1.65,0.29,3.38-0.01,4.6-0.55
c-0.37-2.12-0.97-4.14-1.65-5.92C15.57,15.62,15.57,15.46,15.45,15.38z M10.47,6.52c-2.3,1.15-3.92,2.99-4.58,5.79
c2.87-0.04,5.42-0.39,7.66-1.04C12.58,9.64,11.6,8.01,10.47,6.52z M14.01,0C6.27,0,0,6.28,0,14.01s6.27,14.01,14.01,14.01
c7.05,0,12.89-5.21,13.86-12c0.1-0.46,0.14-0.94,0.13-1.43c0.01-0.2,0.01-0.39,0.01-0.58C28.02,6.28,21.75,0,14.01,0z M21.17,20.6
c-0.77,0.85-1.69,1.53-2.78,2.07c-1.09,0.55-2.33,0.97-3.8,1.04c-3.1,0.15-5.52-1.04-7.14-2.53c-1.66-1.53-2.92-3.59-3.14-6.56
c-0.11-1.58,0.2-2.97,0.67-4.15c0.48-1.23,1.06-2.14,1.84-3.01c1.11-1.2,2.61-2.26,4.5-2.78c0.68-0.19,1.39-0.31,2.1-0.36
c3.17-0.2,5.51,1.07,7.16,2.56c1.65,1.48,2.94,3.62,3.11,6.58C23.89,16.57,22.7,18.94,21.17,20.6z M17.05,14.98
c0.64,1.86,1.23,3.76,1.59,5.9c0.46-0.26,0.86-0.61,1.24-0.99c1.14-1.15,1.99-2.63,2.33-4.55C20.79,14.91,18.79,14.7,17.05,14.98z
M15.2,10.8c1.73-0.7,3.28-1.6,4.3-3.01c-1.36-1.14-3.32-2.18-5.82-2.05c-0.6,0.03-1.05,0.08-1.59,0.2
c0.93,1.37,1.93,2.79,2.73,4.22C14.94,10.38,15.02,10.63,15.2,10.8z M15.81,12.06c0.2,0.38,0.39,0.81,0.56,1.19
c0.06,0.14,0.08,0.31,0.2,0.41c1.93-0.22,4.06-0.06,5.74,0.28c-0.06-2.27-0.82-3.85-1.87-5.14C19.24,10.23,17.65,11.27,15.81,12.06z
"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

-7
View File
@@ -1,7 +0,0 @@
<svg class="icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 28 28" enable-background="new 0 0 28 28" xml:space="preserve">
<path id="Facebook" fill="#D1CECC" d="M14,0C6.27,0,0,6.27,0,14s6.27,14,14,14s14-6.27,14-14S21.73,0,14,0z M20.99,20.22
c0,0.43-0.35,0.77-0.77,0.77h-3.56v-5.42h1.82l0.27-2.11h-2.09v-1.35c0-0.61,0.17-1.03,1.05-1.03l1.12,0V9.2
c-0.19-0.03-0.86-0.08-1.63-0.08c-1.61,0-2.71,0.98-2.71,2.79v1.56h-1.82v2.11h1.82v5.42h-6.7c-0.43,0-0.77-0.35-0.77-0.77V7.78
c0-0.43,0.35-0.77,0.77-0.77h12.44c0.43,0,0.77,0.35,0.77,0.77V20.22z"/>
</svg>

Before

Width:  |  Height:  |  Size: 618 B

-10
View File
@@ -1,10 +0,0 @@
<svg class="icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 28 28" enable-background="new 0 0 28 28" xml:space="preserve">
<path id="GitHub" fill-rule="evenodd" clip-rule="evenodd" fill="#D1CECC" d="M14.01,0C6.27,0-0.01,6.28-0.01,14.02
c0,6.19,4.02,11.45,9.59,13.3c0.7,0.13,0.96-0.3,0.96-0.68c0-0.33-0.01-1.21-0.02-2.38c-3.9,0.85-4.72-1.88-4.72-1.88
c-0.64-1.62-1.56-2.05-1.56-2.05c-1.27-0.87,0.1-0.85,0.1-0.85c1.41,0.1,2.15,1.44,2.15,1.44c1.25,2.14,3.28,1.52,4.08,1.16
c0.13-0.91,0.49-1.52,0.89-1.87c-3.11-0.35-6.38-1.56-6.38-6.93c0-1.53,0.55-2.78,1.44-3.76C6.37,9.17,5.89,7.74,6.65,5.81
c0,0,1.18-0.38,3.85,1.44c1.12-0.31,2.32-0.47,3.51-0.47c1.19,0.01,2.39,0.16,3.51,0.47c2.68-1.81,3.85-1.44,3.85-1.44
c0.76,1.93,0.28,3.35,0.14,3.71c0.9,0.98,1.44,2.23,1.44,3.76c0,5.38-3.28,6.57-6.4,6.92c0.5,0.43,0.95,1.29,0.95,2.6
c0,1.87-0.02,3.39-0.02,3.84c0,0.37,0.25,0.81,0.96,0.67c5.56-1.86,9.58-7.11,9.58-13.3C28.03,6.28,21.75,0,14.01,0z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1014 B

-12
View File
@@ -1,12 +0,0 @@
<svg class="icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 28 28" enable-background="new 0 0 28 28" xml:space="preserve">
<path id="Instagram" fill="#D1CECC" d="M9.89,7.35h0.49v3.05H9.89V7.35z M7.35,8.26v2.15h0.49V7.44C7.55,7.58,7.35,7.89,7.35,8.26z
M18.22,10.3h1.36c0.51,0,0.92-0.41,0.92-0.92V8.38c0-0.51-0.41-0.92-0.92-0.92h-1.36c-0.51,0-0.92,0.41-0.92,0.92v0.99
C17.31,9.87,17.72,10.3,18.22,10.3z M8.2,7.35h0.49v3.05H8.2V7.35z M9.05,7.35h0.49v3.05H9.05V7.35z M14-0.01
C6.26-0.01-0.01,6.27-0.01,14S6.26,28.01,14,28.01S28.01,21.73,28.01,14S21.74-0.01,14-0.01z M6.63,7.93c0-0.71,0.58-1.3,1.3-1.3
h12.14c0.71,0,1.3,0.59,1.3,1.3v3.05h-4.56c-0.64-0.85-1.66-1.39-2.81-1.39s-2.16,0.55-2.81,1.39H6.63V7.93z M17.09,13.1
c0,1.7-1.39,3.09-3.09,3.09s-3.09-1.39-3.09-3.09s1.39-3.09,3.09-3.09C15.7,10.01,17.09,11.39,17.09,13.1z M21.37,20.07
c0,0.71-0.59,1.3-1.3,1.3H7.93c-0.71,0-1.3-0.59-1.3-1.3V11.5h4.24c-0.25,0.48-0.39,1.02-0.39,1.6c0,1.94,1.58,3.52,3.52,3.52
s3.52-1.58,3.52-3.52c0-0.57-0.14-1.12-0.39-1.6h4.24L21.37,20.07L21.37,20.07z M14,10.58c1.39,0,2.52,1.13,2.52,2.52
s-1.13,2.52-2.52,2.52s-2.52-1.13-2.52-2.52S12.61,10.58,14,10.58z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

-9
View File
@@ -1,9 +0,0 @@
<svg class="icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 28 28" enable-background="new 0 0 28 28" xml:space="preserve">
<path id="LinkedIn" fill="#D1CECC" d="M18.82,15.09v3.61h-2.09v-3.37c0-0.85-0.3-1.42-1.06-1.42c-0.58,0-0.92,0.39-1.07,0.77
c-0.06,0.13-0.07,0.32-0.07,0.51v3.52h-2.09c0,0,0.03-5.71,0-6.3h2.09v0.89c0,0.01-0.01,0.01-0.01,0.02h0.01V13.3
c0.28-0.43,0.77-1.04,1.89-1.04C17.79,12.25,18.82,13.16,18.82,15.09z M9.18,18.7h2.09v-6.3H9.18V18.7z M10.24,9.36
c-0.72,0-1.19,0.47-1.19,1.09c0,0.61,0.45,1.09,1.16,1.09h0.01c0.73,0,1.18-0.48,1.18-1.09C11.39,9.84,10.95,9.36,10.24,9.36z
M28,14c0,7.73-6.27,14-14,14S0,21.73,0,14S6.27,0,14,0S28,6.27,28,14z M20.93,8.02c0-0.55-0.46-1-1.02-1H8.09
c-0.57,0-1.02,0.45-1.02,1v11.96c0,0.55,0.46,1,1.02,1h11.82c0.57,0,1.02-0.45,1.02-1V8.02z"/>
</svg>

Before

Width:  |  Height:  |  Size: 867 B

-14
View File
@@ -1,14 +0,0 @@
<svg class="icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 28 28" enable-background="new 0 0 28 28" xml:space="preserve">
<path id="PDF" fill="#D1CECC" d="M12.28,15.27c0.46-0.94,0.81-1.83,1.05-2.66c0.52,1.29,0.97,2.09,1.35,2.41
c0.38,0.32,0.74,0.53,1.09,0.62c-0.75,0.11-1.45,0.24-2.1,0.38c-0.65,0.14-1.35,0.38-2.1,0.71L12.28,15.27z M28,14
c0,7.73-6.27,14-14,14S0,21.73,0,14S6.27,0,14,0S28,6.27,28,14z M20.94,9.37L17.47,5.9H7.06v16.2h13.89V9.37z M19.35,17.4
c-0.39-0.02-0.86-0.11-1.42-0.27s-1.1-0.36-1.62-0.61c-0.57,0.06-1.14,0.14-1.71,0.25c-0.57,0.11-1.13,0.23-1.67,0.36
c-0.07,0.02-0.24,0.08-0.5,0.17s-0.57,0.21-0.93,0.35c-0.84,1.41-1.52,2.32-2.03,2.74c-0.51,0.42-0.92,0.6-1.22,0.55h11.54v-3.58
c-0.07,0-0.14,0.01-0.21,0.02C19.51,17.39,19.44,17.4,19.35,17.4z M10.17,16.84c0.12-0.22,0.47-0.93,1.05-2.14
c0.58-1.21,1.01-2.24,1.28-3.08c-0.2-0.72-0.33-1.42-0.39-2.09s-0.02-1.23,0.1-1.69c0.07-0.25,0.17-0.44,0.28-0.55
s0.23-0.19,0.35-0.23H8.21v10.85c0.4-0.33,0.78-0.58,1.16-0.75C9.74,16.98,10.01,16.87,10.17,16.84z M13.39,7.06
c0.27,0,0.5,0.1,0.7,0.31c0.2,0.2,0.28,0.54,0.24,1.01c0.01,0.01,0.01,0.3,0,0.87c-0.01,0.57-0.09,1.33-0.24,2.28
c0.52,1.37,1.02,2.27,1.52,2.69c0.49,0.42,0.87,0.67,1.14,0.74c0.52-0.06,0.99-0.09,1.43-0.09c0.31,0,0.61,0.04,0.88,0.13
c0.27,0.08,0.52,0.19,0.73,0.33V9.95l-2.89-2.89H13.39z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

-7
View File
@@ -1,7 +0,0 @@
<svg class="icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 28 28" enable-background="new 0 0 28 28" xml:space="preserve">
<path id="Print" fill="#D1CECC" d="M9.5,7.5h9V9h-9V7.5z M9.5,13.5h9v7h-9V13.5z M10.5,15.51h7v-1h-7V15.51z M10.5,17.49h7v-1h-7
V17.49z M10.5,19.49h7v-1h-7V19.49z M28,14c0,7.73-6.27,14-14,14S0,21.73,0,14S6.27,0,14,0S28,6.27,28,14z M22,11c0-1.1-0.9-2-2-2
h-0.5V6.5h-11V9H8c-1.1,0-2,0.9-2,2v4c0,1.1,0.9,2,2,2h0.5v4.5h11V17H20c1.1,0,2-0.9,2-2V11z M20.02,10.45c-0.28,0-0.5,0.22-0.5,0.5
s0.22,0.5,0.5,0.5s0.5-0.22,0.5-0.5S20.29,10.45,20.02,10.45z"/>
</svg>

Before

Width:  |  Height:  |  Size: 643 B

-10
View File
@@ -1,10 +0,0 @@
<svg class="icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 28 28" enable-background="new 0 0 28 28" xml:space="preserve">
<path id="Twitter" fill="#D1CECC" d="M14,0C6.27,0,0,6.27,0,14s6.27,14,14,14s14-6.27,14-14S21.73,0,14,0z M20.69,10.57
c0.01,0.15,0.01,0.3,0.01,0.45c0,4.56-3.47,9.82-9.82,9.82c-1.95,0-3.76-0.57-5.29-1.55c0.27,0.03,0.54,0.05,0.82,0.05
c1.62,0,3.11-0.55,4.29-1.48c-1.51-0.03-2.79-1.03-3.23-2.4c0.21,0.04,0.43,0.06,0.65,0.06c0.31,0,0.62-0.04,0.91-0.12
c-1.58-0.32-2.77-1.71-2.77-3.39c0-0.01,0-0.03,0-0.04c0.47,0.26,1,0.41,1.56,0.43c-0.93-0.62-1.54-1.68-1.54-2.87
c0-0.63,0.17-1.23,0.47-1.74c1.7,2.09,4.25,3.46,7.12,3.61c-0.06-0.25-0.09-0.52-0.09-0.79c0-1.91,1.55-3.45,3.45-3.45
c0.99,0,1.89,0.42,2.52,1.09c0.79-0.15,1.53-0.44,2.19-0.84c-0.26,0.81-0.81,1.48-1.52,1.91c0.7-0.08,1.36-0.27,1.98-0.54
C21.95,9.47,21.37,10.08,20.69,10.57z"/>
</svg>

Before

Width:  |  Height:  |  Size: 934 B

-40
View File
@@ -1,40 +0,0 @@
<svg class="icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 28 28" enable-background="new 0 0 28 28" xml:space="preserve">
<path id="Website" fill="#D1CECC" d="M14.83,7.6c0.03,0.08,0.03,0.14,0,0.19s-0.08,0.07-0.17,0.07c-0.12,0-0.26-0.01-0.4-0.04
s-0.2-0.06-0.18-0.1c0.15-0.15,0.16-0.26,0.03-0.33c-0.13-0.07-0.27-0.11-0.42-0.11s-0.25-0.04-0.3-0.09s-0.03-0.09,0.05-0.09
s0.22,0,0.4,0.01c0.19,0.01,0.31,0.04,0.37,0.08s0.17,0.1,0.33,0.19S14.8,7.52,14.83,7.6z M15.9,13.35c-0.11-0.03-0.2-0.03-0.26,0
s-0.13,0.05-0.2,0.05c-0.08-0.01-0.25-0.09-0.5-0.24s-0.5-0.22-0.77-0.22c-0.11,0.01-0.17,0.04-0.19,0.08s-0.01,0.07,0.01,0.08
c0.02,0.01,0.12,0.03,0.29,0.05s0.4,0.11,0.67,0.26c0.28,0.15,0.48,0.21,0.61,0.19s0.22-0.06,0.28-0.1
c0.07-0.01,0.12-0.03,0.16-0.06S16.01,13.38,15.9,13.35z M13.62,6.85c0.17-0.03,0.3-0.13,0.38-0.3c0.03-0.14-0.04-0.2-0.21-0.18
s-0.29,0.07-0.37,0.13c-0.01,0.03-0.02,0.06-0.03,0.09s-0.02,0.06-0.03,0.07l-0.08,0.1c-0.03,0.03-0.03,0.05-0.02,0.06
C13.32,6.88,13.44,6.89,13.62,6.85z M16.12,7.08c-0.02-0.16-0.11-0.3-0.26-0.41s-0.32-0.19-0.5-0.22c-0.18-0.03-0.38-0.07-0.61-0.12
s-0.4-0.04-0.53,0.04c-0.21,0.18-0.25,0.31-0.12,0.39c0.07,0.03,0.2,0.05,0.38,0.06s0.44,0.15,0.76,0.41
c0.11,0.08,0.29,0.21,0.55,0.37s0.44,0.23,0.55,0.19c0.06-0.06,0.04-0.15-0.05-0.28S16.14,7.24,16.12,7.08z M28,14
c0,7.73-6.27,14-14,14S0,21.73,0,14S6.27,0,14,0S28,6.27,28,14z M21.95,14c-0.06-2.25-0.83-4.12-2.33-5.62
c-1.5-1.5-3.37-2.27-5.62-2.33c-2.25,0.06-4.12,0.83-5.62,2.33c-1.5,1.5-2.27,3.37-2.33,5.62c0.06,2.25,0.83,4.12,2.33,5.62
c1.5,1.5,3.37,2.27,5.62,2.33c0.54,0,1.06-0.05,1.57-0.14c-0.11-0.07-0.17-0.24-0.18-0.51s0.04-0.52,0.16-0.76
c0.15-0.26,0.3-0.61,0.46-1.04c0.15-0.43,0.08-0.77-0.23-1c-0.32-0.18-0.51-0.34-0.57-0.49s-0.19-0.32-0.37-0.54
s-0.28-0.36-0.28-0.44v-0.21c-0.01-0.21,0.05-0.43,0.19-0.66l0.25-0.39c0.01-0.04,0.02-0.1,0.02-0.19v-0.19
c-0.01-0.04-0.07-0.09-0.17-0.16s-0.17-0.1-0.22-0.1s-0.09,0.01-0.11,0.05s-0.07,0.06-0.13,0.06s-0.17-0.04-0.33-0.13
c-0.16-0.09-0.27-0.16-0.32-0.21s-0.1-0.11-0.14-0.19s-0.09-0.15-0.16-0.22s-0.11-0.1-0.16-0.09s-0.12,0-0.23-0.03l-1.45-0.56
c-0.26-0.11-0.43-0.23-0.5-0.37c-0.07-0.14-0.1-0.26-0.1-0.37s-0.06-0.23-0.17-0.37l-0.29-0.37c-0.08-0.12-0.14-0.24-0.17-0.34
s-0.05-0.15-0.06-0.13c-0.01,0.07,0.02,0.23,0.1,0.48s0.12,0.39,0.1,0.43c-0.03,0.01-0.07-0.02-0.12-0.11s-0.12-0.19-0.19-0.3
c-0.06-0.07-0.07-0.15-0.04-0.25c0.03-0.1-0.07-0.38-0.29-0.85c-0.22-0.47-0.26-0.85-0.11-1.15s0.24-0.57,0.3-0.82
c0.03-0.21,0.09-0.25,0.19-0.13c0.1,0.12,0.1,0.11,0.02-0.01c-0.08-0.18-0.11-0.4-0.08-0.65c0.03-0.26,0.01-0.46-0.06-0.61
c-0.11-0.1-0.3-0.08-0.58,0.05S9.12,8.14,9.1,8.16c0.03-0.12,0.18-0.28,0.46-0.46l0.87-0.58c0.26-0.17,0.49-0.22,0.69-0.17
s0.41,0.12,0.62,0.21s0.34,0.12,0.37,0.1s0.01-0.06-0.06-0.13s-0.07-0.13,0.01-0.16S12.26,6.96,12.43,7
c0.17,0.03,0.31,0.11,0.42,0.24s0.3,0.18,0.54,0.17s0.32,0.01,0.25,0.06s-0.11,0.12-0.11,0.19c0.04,0.06,0.02,0.1-0.05,0.13
s-0.2,0.1-0.36,0.21s-0.18,0.19-0.05,0.26c0.13,0.07,0.36,0.21,0.67,0.41c0.28,0.19,0.41,0.19,0.41-0.01s-0.02-0.4-0.06-0.61
c-0.01-0.14,0.05-0.19,0.2-0.14s0.22,0.07,0.24,0.08c0.17,0.11,0.27,0.15,0.32,0.11s0.13-0.04,0.26-0.01
c0.17,0.07,0.37,0.19,0.6,0.37l0.41,0.33c-0.43,0.25-0.6,0.41-0.51,0.49s0.2,0.14,0.32,0.2c0.06,0.06,0.02,0.12-0.1,0.21
s-0.2,0.12-0.23,0.12c-0.08-0.08-0.15-0.1-0.2-0.05s-0.1,0.09-0.16,0.13c-0.06,0.04-0.07,0.09-0.05,0.14s0.04,0.09,0.05,0.1
c-0.3,0.07-0.47,0.22-0.49,0.44s-0.02,0.39-0.01,0.49c0,0.08-0.07,0.18-0.21,0.3s-0.24,0.23-0.31,0.34c-0.03,0.14,0,0.29,0.08,0.47
c0.08,0.17,0.08,0.27,0,0.3c-0.08-0.01-0.22-0.12-0.41-0.32c-0.19-0.2-0.55-0.24-1.08-0.13c-0.19,0.04-0.39,0.16-0.6,0.36
s-0.23,0.49-0.06,0.88c0.21,0.32,0.43,0.38,0.68,0.19c0.25-0.19,0.43-0.3,0.56-0.31c0.1,0.08,0.12,0.2,0.06,0.35
c-0.06,0.15-0.08,0.24-0.08,0.27c0.06-0.01,0.17,0.02,0.34,0.09s0.28,0.27,0.32,0.58c0.04,0.31,0.19,0.49,0.46,0.55
s0.45,0.08,0.57,0.07s0.28-0.09,0.48-0.24s0.32-0.24,0.37-0.27s0.17-0.06,0.35-0.1s0.47,0.03,0.84,0.23s0.67,0.31,0.88,0.35
s0.38,0.11,0.5,0.2s0.18,0.2,0.18,0.33s0.05,0.23,0.14,0.3c0.15,0.04,0.39,0.07,0.72,0.07s0.57,0.1,0.72,0.28
c0.07,0.28-0.08,0.73-0.46,1.38c-0.37,0.64-0.68,1.05-0.93,1.23c-0.18,0.12-0.34,0.29-0.48,0.49s-0.3,0.38-0.49,0.55
s-0.41,0.34-0.66,0.52s-0.51,0.34-0.78,0.5c-0.21,0.15-0.36,0.32-0.46,0.51s-0.19,0.32-0.27,0.4c1.81-0.43,3.28-1.34,4.43-2.75
C21.31,17.6,21.9,15.93,21.95,14z"/>
</svg>

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.
-66
View File
@@ -1,66 +0,0 @@
# Site settings
title: "Pengzhan.dev"
description: "Pengzhan Hao's personal biography"
# Build settings
markdown: kramdown
sass:
sass_dir: _sass
style: compressed
# Resume settings
resume_avatar: "true"
resume_name: "Pengzhan Hao"
resume_title: "Software Engineer"
# used for the contact button in _layouts/resume.html
resume_contact_email: "[email protected]"
# the next two items are used for schema itemprops in _layouts/resume.html
#resume_contact_telephone: "555-7334"
#resume_contact_address: "742 Evergreen Terrace, Springfield"
#resume_header_contact_info: "742 Evergreen Terrace, Springfield • 555-7334 • [email protected]"
#display_header_contact_info: "false"
resume_header_intro: "<p>Software engineer on Cloud, research focus on Edge computing and Distributed systems</p>"
# use "yes" to display the email contact button,
# "no" to display an "I'm not looking for work" message,
# or remove the resume_looking_for_work option entirely
# to leave blank
resume_looking_for_work: "no"
# Decide which sections to use
# comment out to hide
resume_section_experience: true
resume_section_education: true
resume_section_publications: true
resume_section_projects: true
resume_section_skills: true
resume_section_recognition: true
resume_section_links: true
# resume_section_associations: true
# resume_section_interests: true
# Resume social links
# uncomment the options you wish to display, and add your own URL
resume_social_links:
resume_github_url: "https://github.com/CoderSherlock"
#resume_twitter_url: "http://twitter.com/jglovier"
#resume_dribbble_url: "https://dribbble.com/jag"
# resume_facebook_url: "insert Facebook URL here"
resume_linkedin_url: "https://www.linkedin.com/in/pengzhanhao/"
resume_instagram_url: "https://www.instagram.com/pengzhan.hao/"
resume_website_url: "http://pengzhan.dev"
#resume_medium_url: "https://medium.com/@jglovier"
resume_print_social_links: true
# Design settings
resume_theme: default
# Note: when editing locally, remember to restart
# your Jekyll server when you edit this file. Changes
# to _config.yml are only recognized when the server starts
exclude: ['README.md', 'LICENSE', 'Dockerfile', 'CNAME', 'Gemfile', 'Gemfile.lock', 'deploy.sh']
-15
View File
@@ -1,15 +0,0 @@
# Associations
# comment out the urls if you do not wish to display them
- organization: PETA
role: Volunteer
year: 2001 &mdash; 2010
url: https://www.peta.org/
summary: Organized fund drives and participated in fundraising events for the benefit of PETA's local Springfield operations.
- organization: Yale Amnesty International
role: Communications coordinator
year: 2002 &mdash; 2004
url: http://campuspress.yale.edu/amnesty/
summary: During my junion and senior years at Yale, I was actively involved in Yale Amnesty International where I helped facilitate our student organization communications through website maintenance, writing for our campus publication, and graphic design of related materials.
-14
View File
@@ -1,14 +0,0 @@
# Degree
- degree: Doctorate of Science in Computer Science
uni: Binghamton university, the State University of New York
year: 2017 &mdash;
- degree: Master of Science in Computer Science
uni: Binghamton university, the State University of New York
year: 2015 &mdash; 2017
summary: GPA 4.0/4.0
- degree: Bachelor of Engineering in Software Engineering
uni: Beihang University
year: 2011 &mdash; 2015
summary: GPA 3.25/4.0
-34
View File
@@ -1,34 +0,0 @@
# Jobs
# Google
- company: Google
position: Software Engineer
duration: Jun, 2022 &mdash;
summary: Keep GKE nodes working
# Graduate position
- company: Binghamton University, Mobile and Operating System Lab
position: Research Assistant, advised by Prof. Yifan Zhang
duration: Oct, 2016 &mdash;
summary: Edge-based distributed deep learning
<ul class="resume-item-list">
<li>Implemented a system for time-sensitive, edge-based distributed neural network training scenarios with state-of-the-art performanc</li>
<li>Applied empirical experiments to show potential opportunities in Edge-based/Mobile-based distributed neural network training.</li>
<li>Investigated heterogeneous-aware training tasks scheduling problem. </li>
</ul>
Edge computing
<ul class="resume-item-list">
<li>Envision edge computing's work-flow in end device performance(latency, traffic) improving.</li>
<li>Applied empirical experiments on different edge computing work-flow and propose/implement related solutions. </li>
<li>Design and implement an edge-based multi-tier system for decreasing bandwidth usage from endpoint mobile device. </li>
</ul>
# Undergrad
- company: Advance computer application technology engineering research center
position: Research Assistant, advised by Prof. Juhua Pu
duration: Oct, 2013 &mdash; Sep, 2014
summary: Real-time routing
<ul class="resume-item-list">
<li>Studied on real-time routing problem based vehicle location data </li>
<li>Optimized current routing algorithm with predicted traffic information </li>
</ul>
-4
View File
@@ -1,4 +0,0 @@
# Interests
- description: Human rights and world affairs
- description: Music (baritone saxiphonist)
- description: Linguistics
-3
View File
@@ -1,3 +0,0 @@
# links
- url: "https://blog.pengzhan.dev/"
description: My personal blog
-32
View File
@@ -1,32 +0,0 @@
# Projects
# uncomment the urls if you wish to display them, and add your own URL
- project: EDDL - Edge distributed deep learning framework
role: Leader
duration: 2018 &mdash; Present
# url: "#"
description: A lite-weight c++ written framework(based on Dlib) for distributed neural network training on embedded devices and
mobile phones. With supporting of our implemented runtime profilers, training devices can interconnect in an optimized
network topology under various network environments.
In our last publication, we showed our framework can perform distributed training for diverse neural networks on
resource-limited devices. The EDDL framework also showed well scalability in large amounts of devices.
We are currently working on network optimization on per-batch communication time and overall convergence time.
- project: Edge Courier
role: Leader
duration: 2016 &mdash; 2017
# url: "#"
description: A proxy-based protocol for incremental file synchronization and cooperative online document editing. This protocol
supports file hosting services including Google Drive, Dropbox, and OneDrive as well as document collaboration services
such as G Suite and Microsoft 365.
Edge courier can significantly decrease bandwidth consumption for collaborative editing web apps, and also showed
advancement in cloud file storage services.
- project: RTRouting assistant
role: Undergraduate-level student researcher
duration: 2013 &mdash; 2014
# url: "#"
description: A real time routing prototype which can guide vehicle to alternative routes with high-volume traffic prediction. The
predicted traffic alert is generated by a pre-trained model learned from history monitored traffics, real time event
warnings and a abnormal traffic prediction model. This project belongs to a urban computing project studied together
by Advance computer application technology engineering research center(Beihang University) and Microsoft Research
Asia(MSRA).
-29
View File
@@ -1,29 +0,0 @@
# Publications
# uncomment the urls if you wish to display them, and add your own URL
- name: "EDDL: A Distributed Deep Learning System for Resource-limited Edge Computing Environment"
url: "https://blog.pengzhan.dev/static/2021-12/eddl-sec21.pdf"
description: "Pengzhan Hao, and Yifan Zhang The Sixth ACM/IEEE Symposium on Edge Computing (ACM/IEEE SEC)
San Jose, CA, USA, 2021."
- name: A Case for Web Service Bandwidth Reduction on Mobile Devices with Edge-hosted Personal Services
description: "Yongshu Bai, Pengzhan Hao, and Yifan Zhang The 37th IEEE International ConferenDDL - Edge distributed deep learning frameworkce on Computer Communications (IEEE INFOCOM)
Honolulu, HI, USA, 2018."
url: "https://blog.pengzhan.dev/static/2021-10/eta-infocom18.pdf"
- name: "EdgeCourier: An Edge-hosted Personal Service for Low-bandwidth Document Synchronization in Mobile Cloud Storage Services"
description: "Pengzhan Hao, Yongshu Bai, Xin Zhang, and Yifan Zhang
The 2nd ACM/IEEE Symposium on Edge Computing (ACM/IEEE SEC)
San Jose, CA, USA, 2017."
url: "https://blog.pengzhan.dev/static/2018-02/a7-hao.pdf"
- name: "Poster: Securing Device Inputs for Smartphones Using Hypervisor Based Approach"
description: "Xin Zhang, Yongshu Bai, Pengzhan Hao, and Yifan Zhang
The 15th ACM International Conference on Mobile Systems, Applications, and Services (ACM MobiSys)
Niagara Falls, NY, USA, 2017."
url: "https://blog.pengzhan.dev/static/2017-07/p169-zhang.pdf"
- name: "Poster: EPS - Edge-hosted Personal Services for Mobile Users"
description: "Pengzhan Hao, Yongshu Bai, Xin Zhang, and Yifan Zhang
The 15th ACM International Conference on Mobile Systems, Applications, and Services (ACM MobiSys)
Niagara Falls, NY, USA, 2017."
url: "https://blog.pengzhan.dev/static/2017-07/p163-hao.pdf"
-8
View File
@@ -1,8 +0,0 @@
# Recognitions
- award: ACM/IEEE SEC student travel grant
organization: IEEE
year: 2017, 2021
- award: ACM MobiSys 2017 student travel grant
organization: ACM
year: 2017
-4
View File
@@ -1,4 +0,0 @@
# Skills
# Organizational leadership
- skill: Professional skills
description: "C/C++, Python, Golang, Java, Bash, SQL, Docker, Kubernetes, Xen hypervisor, RESTful, Django, OAuth, ProtoBuf"
-27
View File
@@ -1,27 +0,0 @@
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% if page.title %}{{ page.title }}{% else %}{{ site.title }}{% endif %}</title>
<!-- Google font typography settings - defined in _config.yml -->
{% if site.resume_theme == 'default' %}
<link href='https://fonts.googleapis.com/css?family=Lora:400,700|Open+Sans:400,300,800,700' rel='stylesheet' type='text/css'>
{% endif %}
<meta name="description" content="{% if page.excerpt %}{{ page.excerpt | strip_html | strip_newlines | truncate: 160 }}{% else %}{{ site.description }}{% endif %}">
<link rel="stylesheet" href="css/main.css">
<link rel="canonical" href="{{ page.url | replace:'index.html','' | prepend: site.baseurl | prepend: site.url }}">
<link rel="icon" type="image/x-icon" href="favicon.ico" />
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-MNP4QN1T9T"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-MNP4QN1T9T');
</script>
</head>
-48
View File
@@ -1,48 +0,0 @@
<!-- and guess where these are defined? Yup, you guessed it: the _config.yml file -->
{% if site.resume_social_links %}
<ul class="icon-links">
<!-- GitHub link -->
{% if site.resume_social_links.resume_github_url %}
<li class="icon-link-item"><a href="{{ site.resume_social_links.resume_github_url }}" class="icon-link" itemprop="sameAs">{% include icons/icon-github.html %}</a></li>
{% endif %}
<!-- Twitter link -->
{% if site.resume_social_links.resume_twitter_url %}
<li class="icon-link-item"><a href="{{ site.resume_social_links.resume_twitter_url }}" class="icon-link" itemprop="sameAs">{% include icons/icon-twitter.html %}</a></li>
{% endif %}
<!-- Medium link -->
{% if site.resume_social_links.resume_medium_url %}
<li class="icon-link-item">
<a href="{{ site.resume_social_links.resume_medium_url }}" class="icon-link" itemprop="url">{% include icons/icon-medium.html %}</a>
</li>
{% endif %}
<!-- Dribbble link -->
{% if site.resume_social_links.resume_dribbble_url %}
<li class="icon-link-item"><a href="{{ site.resume_social_links.resume_dribbble_url }}" class="icon-link" itemprop="sameAs">{% include icons/icon-dribbble.html %}</a></li>
{% endif %}
<!-- Facebook link -->
{% if site.resume_social_links.resume_facebook_url %}
<li class="icon-link-item"><a href="{{ site.resume_social_links.resume_facebook_url }}" class="icon-link" itemprop="sameAs">{% include icons/icon-facebook.html %}</a></li>
{% endif %}
<!-- LinkedIn link -->
{% if site.resume_social_links.resume_linkedin_url %}
<li class="icon-link-item"><a href="{{ site.resume_social_links.resume_linkedin_url }}" class="icon-link" itemprop="sameAs">{% include icons/icon-linkedin.html %}</a></li>
{% endif %}
<!-- Instagram link -->
{% if site.resume_social_links.resume_instagram_url %}
<li class="icon-link-item"><a href="{{ site.resume_social_links.resume_instagram_url }}" class="icon-link" itemprop="sameAs">{% include icons/icon-instagram.html %}</a></li>
{% endif %}
<!-- Website link -->
{% if site.resume_social_links.resume_website_url %}
<li class="icon-link-item"><a href="{{ site.resume_social_links.resume_website_url }}" class="icon-link" itemprop="url">{% include icons/icon-website.html %}</a></li>
{% endif %}
</ul>
{% endif %}
-17
View File
@@ -1,17 +0,0 @@
<svg class="icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 28 28" enable-background="new 0 0 28 28" xml:space="preserve" width="28">
<path id="Dribbble" fill="#D1CECC" d="M13.76,14.4c0.37-0.15,0.78-0.26,1.16-0.43c-0.18-0.34-0.35-0.73-0.5-1.04
c-0.06-0.11-0.1-0.25-0.2-0.33c-2.49,0.72-5.25,1.17-8.48,1.16c-0.06,2.6,0.88,4.41,2.12,5.79C9.3,17.33,11.22,15.46,13.76,14.4z
M15.45,15.38c-2.87,1.04-5.13,2.68-6.53,5.19c0.95,0.74,2.22,1.38,3.72,1.65c1.65,0.29,3.38-0.01,4.6-0.55
c-0.37-2.12-0.97-4.14-1.65-5.92C15.57,15.62,15.57,15.46,15.45,15.38z M10.47,6.52c-2.3,1.15-3.92,2.99-4.58,5.79
c2.87-0.04,5.42-0.39,7.66-1.04C12.58,9.64,11.6,8.01,10.47,6.52z M14.01,0C6.27,0,0,6.28,0,14.01s6.27,14.01,14.01,14.01
c7.05,0,12.89-5.21,13.86-12c0.1-0.46,0.14-0.94,0.13-1.43c0.01-0.2,0.01-0.39,0.01-0.58C28.02,6.28,21.75,0,14.01,0z M21.17,20.6
c-0.77,0.85-1.69,1.53-2.78,2.07c-1.09,0.55-2.33,0.97-3.8,1.04c-3.1,0.15-5.52-1.04-7.14-2.53c-1.66-1.53-2.92-3.59-3.14-6.56
c-0.11-1.58,0.2-2.97,0.67-4.15c0.48-1.23,1.06-2.14,1.84-3.01c1.11-1.2,2.61-2.26,4.5-2.78c0.68-0.19,1.39-0.31,2.1-0.36
c3.17-0.2,5.51,1.07,7.16,2.56c1.65,1.48,2.94,3.62,3.11,6.58C23.89,16.57,22.7,18.94,21.17,20.6z M17.05,14.98
c0.64,1.86,1.23,3.76,1.59,5.9c0.46-0.26,0.86-0.61,1.24-0.99c1.14-1.15,1.99-2.63,2.33-4.55C20.79,14.91,18.79,14.7,17.05,14.98z
M15.2,10.8c1.73-0.7,3.28-1.6,4.3-3.01c-1.36-1.14-3.32-2.18-5.82-2.05c-0.6,0.03-1.05,0.08-1.59,0.2
c0.93,1.37,1.93,2.79,2.73,4.22C14.94,10.38,15.02,10.63,15.2,10.8z M15.81,12.06c0.2,0.38,0.39,0.81,0.56,1.19
c0.06,0.14,0.08,0.31,0.2,0.41c1.93-0.22,4.06-0.06,5.74,0.28c-0.06-2.27-0.82-3.85-1.87-5.14C19.24,10.23,17.65,11.27,15.81,12.06z
"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

-7
View File
@@ -1,7 +0,0 @@
<svg class="icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 28 28" enable-background="new 0 0 28 28" xml:space="preserve" width="28">
<path id="Facebook" fill="#D1CECC" d="M14,0C6.27,0,0,6.27,0,14s6.27,14,14,14s14-6.27,14-14S21.73,0,14,0z M20.99,20.22
c0,0.43-0.35,0.77-0.77,0.77h-3.56v-5.42h1.82l0.27-2.11h-2.09v-1.35c0-0.61,0.17-1.03,1.05-1.03l1.12,0V9.2
c-0.19-0.03-0.86-0.08-1.63-0.08c-1.61,0-2.71,0.98-2.71,2.79v1.56h-1.82v2.11h1.82v5.42h-6.7c-0.43,0-0.77-0.35-0.77-0.77V7.78
c0-0.43,0.35-0.77,0.77-0.77h12.44c0.43,0,0.77,0.35,0.77,0.77V20.22z"/>
</svg>

Before

Width:  |  Height:  |  Size: 633 B

-10
View File
@@ -1,10 +0,0 @@
<svg class="icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 28 28" enable-background="new 0 0 28 28" xml:space="preserve" width="28">
<path id="GitHub" fill-rule="evenodd" clip-rule="evenodd" fill="#D1CECC" d="M14.01,0C6.27,0-0.01,6.28-0.01,14.02
c0,6.19,4.02,11.45,9.59,13.3c0.7,0.13,0.96-0.3,0.96-0.68c0-0.33-0.01-1.21-0.02-2.38c-3.9,0.85-4.72-1.88-4.72-1.88
c-0.64-1.62-1.56-2.05-1.56-2.05c-1.27-0.87,0.1-0.85,0.1-0.85c1.41,0.1,2.15,1.44,2.15,1.44c1.25,2.14,3.28,1.52,4.08,1.16
c0.13-0.91,0.49-1.52,0.89-1.87c-3.11-0.35-6.38-1.56-6.38-6.93c0-1.53,0.55-2.78,1.44-3.76C6.37,9.17,5.89,7.74,6.65,5.81
c0,0,1.18-0.38,3.85,1.44c1.12-0.31,2.32-0.47,3.51-0.47c1.19,0.01,2.39,0.16,3.51,0.47c2.68-1.81,3.85-1.44,3.85-1.44
c0.76,1.93,0.28,3.35,0.14,3.71c0.9,0.98,1.44,2.23,1.44,3.76c0,5.38-3.28,6.57-6.4,6.92c0.5,0.43,0.95,1.29,0.95,2.6
c0,1.87-0.02,3.39-0.02,3.84c0,0.37,0.25,0.81,0.96,0.67c5.56-1.86,9.58-7.11,9.58-13.3C28.03,6.28,21.75,0,14.01,0z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

-12
View File
@@ -1,12 +0,0 @@
<svg class="icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 28 28" enable-background="new 0 0 28 28" xml:space="preserve" width="28">
<path id="Instagram" fill="#D1CECC" d="M9.89,7.35h0.49v3.05H9.89V7.35z M7.35,8.26v2.15h0.49V7.44C7.55,7.58,7.35,7.89,7.35,8.26z
M18.22,10.3h1.36c0.51,0,0.92-0.41,0.92-0.92V8.38c0-0.51-0.41-0.92-0.92-0.92h-1.36c-0.51,0-0.92,0.41-0.92,0.92v0.99
C17.31,9.87,17.72,10.3,18.22,10.3z M8.2,7.35h0.49v3.05H8.2V7.35z M9.05,7.35h0.49v3.05H9.05V7.35z M14-0.01
C6.26-0.01-0.01,6.27-0.01,14S6.26,28.01,14,28.01S28.01,21.73,28.01,14S21.74-0.01,14-0.01z M6.63,7.93c0-0.71,0.58-1.3,1.3-1.3
h12.14c0.71,0,1.3,0.59,1.3,1.3v3.05h-4.56c-0.64-0.85-1.66-1.39-2.81-1.39s-2.16,0.55-2.81,1.39H6.63V7.93z M17.09,13.1
c0,1.7-1.39,3.09-3.09,3.09s-3.09-1.39-3.09-3.09s1.39-3.09,3.09-3.09C15.7,10.01,17.09,11.39,17.09,13.1z M21.37,20.07
c0,0.71-0.59,1.3-1.3,1.3H7.93c-0.71,0-1.3-0.59-1.3-1.3V11.5h4.24c-0.25,0.48-0.39,1.02-0.39,1.6c0,1.94,1.58,3.52,3.52,3.52
s3.52-1.58,3.52-3.52c0-0.57-0.14-1.12-0.39-1.6h4.24L21.37,20.07L21.37,20.07z M14,10.58c1.39,0,2.52,1.13,2.52,2.52
s-1.13,2.52-2.52,2.52s-2.52-1.13-2.52-2.52S12.61,10.58,14,10.58z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

-9
View File
@@ -1,9 +0,0 @@
<svg class="icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 28 28" enable-background="new 0 0 28 28" xml:space="preserve" width="28">
<path id="LinkedIn" fill="#D1CECC" d="M18.82,15.09v3.61h-2.09v-3.37c0-0.85-0.3-1.42-1.06-1.42c-0.58,0-0.92,0.39-1.07,0.77
c-0.06,0.13-0.07,0.32-0.07,0.51v3.52h-2.09c0,0,0.03-5.71,0-6.3h2.09v0.89c0,0.01-0.01,0.01-0.01,0.02h0.01V13.3
c0.28-0.43,0.77-1.04,1.89-1.04C17.79,12.25,18.82,13.16,18.82,15.09z M9.18,18.7h2.09v-6.3H9.18V18.7z M10.24,9.36
c-0.72,0-1.19,0.47-1.19,1.09c0,0.61,0.45,1.09,1.16,1.09h0.01c0.73,0,1.18-0.48,1.18-1.09C11.39,9.84,10.95,9.36,10.24,9.36z
M28,14c0,7.73-6.27,14-14,14S0,21.73,0,14S6.27,0,14,0S28,6.27,28,14z M20.93,8.02c0-0.55-0.46-1-1.02-1H8.09
c-0.57,0-1.02,0.45-1.02,1v11.96c0,0.55,0.46,1,1.02,1h11.82c0.57,0,1.02-0.45,1.02-1V8.02z"/>
</svg>

Before

Width:  |  Height:  |  Size: 884 B

-6
View File
@@ -1,6 +0,0 @@
<svg class="icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 28 28" enable-background="new 0 0 28 28" xml:space="preserve" width="28">
<path id="Medium" fill="#D1CECC" d="M14,0C6.3,0,0,6.3,0,14s6.3,14,14,14s14-6.3,14-14S21.7,0,14,0z M11.2,19.5H7v-0.2l1.5-1.8
c0.2-0.2,0.2-0.4,0.2-0.6v-6.2c0-0.2,0-0.3-0.2-0.5L7.2,8.7V8.4h4.1l3.1,6.9l2.8-6.9H21v0.2l-1.1,1.1c-0.1,0.1-0.1,0.2-0.1,0.3v7.9
c0,0.1,0,0.2,0.1,0.3l1.1,1.1v0.2h-5.5v-0.2l1.1-1.1c0.1-0.1,0.1-0.1,0.1-0.3v-6.4l-3.1,8h-0.4l-3.7-8v5.4c0,0.2,0,0.5,0.2,0.6
l1.5,1.8C11.2,19.3,11.2,19.5,11.2,19.5z"/>
</svg>

Before

Width:  |  Height:  |  Size: 627 B

-14
View File
@@ -1,14 +0,0 @@
<svg class="icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 28 28" enable-background="new 0 0 28 28" xml:space="preserve" width="28">
<path id="PDF" fill="#D1CECC" d="M12.28,15.27c0.46-0.94,0.81-1.83,1.05-2.66c0.52,1.29,0.97,2.09,1.35,2.41
c0.38,0.32,0.74,0.53,1.09,0.62c-0.75,0.11-1.45,0.24-2.1,0.38c-0.65,0.14-1.35,0.38-2.1,0.71L12.28,15.27z M28,14
c0,7.73-6.27,14-14,14S0,21.73,0,14S6.27,0,14,0S28,6.27,28,14z M20.94,9.37L17.47,5.9H7.06v16.2h13.89V9.37z M19.35,17.4
c-0.39-0.02-0.86-0.11-1.42-0.27s-1.1-0.36-1.62-0.61c-0.57,0.06-1.14,0.14-1.71,0.25c-0.57,0.11-1.13,0.23-1.67,0.36
c-0.07,0.02-0.24,0.08-0.5,0.17s-0.57,0.21-0.93,0.35c-0.84,1.41-1.52,2.32-2.03,2.74c-0.51,0.42-0.92,0.6-1.22,0.55h11.54v-3.58
c-0.07,0-0.14,0.01-0.21,0.02C19.51,17.39,19.44,17.4,19.35,17.4z M10.17,16.84c0.12-0.22,0.47-0.93,1.05-2.14
c0.58-1.21,1.01-2.24,1.28-3.08c-0.2-0.72-0.33-1.42-0.39-2.09s-0.02-1.23,0.1-1.69c0.07-0.25,0.17-0.44,0.28-0.55
s0.23-0.19,0.35-0.23H8.21v10.85c0.4-0.33,0.78-0.58,1.16-0.75C9.74,16.98,10.01,16.87,10.17,16.84z M13.39,7.06
c0.27,0,0.5,0.1,0.7,0.31c0.2,0.2,0.28,0.54,0.24,1.01c0.01,0.01,0.01,0.3,0,0.87c-0.01,0.57-0.09,1.33-0.24,2.28
c0.52,1.37,1.02,2.27,1.52,2.69c0.49,0.42,0.87,0.67,1.14,0.74c0.52-0.06,0.99-0.09,1.43-0.09c0.31,0,0.61,0.04,0.88,0.13
c0.27,0.08,0.52,0.19,0.73,0.33V9.95l-2.89-2.89H13.39z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

-7
View File
@@ -1,7 +0,0 @@
<svg class="icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 28 28" enable-background="new 0 0 28 28" xml:space="preserve" width="28">
<path id="Print" fill="#D1CECC" d="M9.5,7.5h9V9h-9V7.5z M9.5,13.5h9v7h-9V13.5z M10.5,15.51h7v-1h-7V15.51z M10.5,17.49h7v-1h-7
V17.49z M10.5,19.49h7v-1h-7V19.49z M28,14c0,7.73-6.27,14-14,14S0,21.73,0,14S6.27,0,14,0S28,6.27,28,14z M22,11c0-1.1-0.9-2-2-2
h-0.5V6.5h-11V9H8c-1.1,0-2,0.9-2,2v4c0,1.1,0.9,2,2,2h0.5v4.5h11V17H20c1.1,0,2-0.9,2-2V11z M20.02,10.45c-0.28,0-0.5,0.22-0.5,0.5
s0.22,0.5,0.5,0.5s0.5-0.22,0.5-0.5S20.29,10.45,20.02,10.45z"/>
</svg>

Before

Width:  |  Height:  |  Size: 658 B

-10
View File
@@ -1,10 +0,0 @@
<svg class="icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 28 28" enable-background="new 0 0 28 28" xml:space="preserve" width="28">
<path id="Twitter" fill="#D1CECC" d="M14,0C6.27,0,0,6.27,0,14s6.27,14,14,14s14-6.27,14-14S21.73,0,14,0z M20.69,10.57
c0.01,0.15,0.01,0.3,0.01,0.45c0,4.56-3.47,9.82-9.82,9.82c-1.95,0-3.76-0.57-5.29-1.55c0.27,0.03,0.54,0.05,0.82,0.05
c1.62,0,3.11-0.55,4.29-1.48c-1.51-0.03-2.79-1.03-3.23-2.4c0.21,0.04,0.43,0.06,0.65,0.06c0.31,0,0.62-0.04,0.91-0.12
c-1.58-0.32-2.77-1.71-2.77-3.39c0-0.01,0-0.03,0-0.04c0.47,0.26,1,0.41,1.56,0.43c-0.93-0.62-1.54-1.68-1.54-2.87
c0-0.63,0.17-1.23,0.47-1.74c1.7,2.09,4.25,3.46,7.12,3.61c-0.06-0.25-0.09-0.52-0.09-0.79c0-1.91,1.55-3.45,3.45-3.45
c0.99,0,1.89,0.42,2.52,1.09c0.79-0.15,1.53-0.44,2.19-0.84c-0.26,0.81-0.81,1.48-1.52,1.91c0.7-0.08,1.36-0.27,1.98-0.54
C21.95,9.47,21.37,10.08,20.69,10.57z"/>
</svg>

Before

Width:  |  Height:  |  Size: 952 B

-40
View File
@@ -1,40 +0,0 @@
<svg class="icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 28 28" enable-background="new 0 0 28 28" xml:space="preserve" width="28">
<path id="Website" fill="#D1CECC" d="M14.83,7.6c0.03,0.08,0.03,0.14,0,0.19s-0.08,0.07-0.17,0.07c-0.12,0-0.26-0.01-0.4-0.04
s-0.2-0.06-0.18-0.1c0.15-0.15,0.16-0.26,0.03-0.33c-0.13-0.07-0.27-0.11-0.42-0.11s-0.25-0.04-0.3-0.09s-0.03-0.09,0.05-0.09
s0.22,0,0.4,0.01c0.19,0.01,0.31,0.04,0.37,0.08s0.17,0.1,0.33,0.19S14.8,7.52,14.83,7.6z M15.9,13.35c-0.11-0.03-0.2-0.03-0.26,0
s-0.13,0.05-0.2,0.05c-0.08-0.01-0.25-0.09-0.5-0.24s-0.5-0.22-0.77-0.22c-0.11,0.01-0.17,0.04-0.19,0.08s-0.01,0.07,0.01,0.08
c0.02,0.01,0.12,0.03,0.29,0.05s0.4,0.11,0.67,0.26c0.28,0.15,0.48,0.21,0.61,0.19s0.22-0.06,0.28-0.1
c0.07-0.01,0.12-0.03,0.16-0.06S16.01,13.38,15.9,13.35z M13.62,6.85c0.17-0.03,0.3-0.13,0.38-0.3c0.03-0.14-0.04-0.2-0.21-0.18
s-0.29,0.07-0.37,0.13c-0.01,0.03-0.02,0.06-0.03,0.09s-0.02,0.06-0.03,0.07l-0.08,0.1c-0.03,0.03-0.03,0.05-0.02,0.06
C13.32,6.88,13.44,6.89,13.62,6.85z M16.12,7.08c-0.02-0.16-0.11-0.3-0.26-0.41s-0.32-0.19-0.5-0.22c-0.18-0.03-0.38-0.07-0.61-0.12
s-0.4-0.04-0.53,0.04c-0.21,0.18-0.25,0.31-0.12,0.39c0.07,0.03,0.2,0.05,0.38,0.06s0.44,0.15,0.76,0.41
c0.11,0.08,0.29,0.21,0.55,0.37s0.44,0.23,0.55,0.19c0.06-0.06,0.04-0.15-0.05-0.28S16.14,7.24,16.12,7.08z M28,14
c0,7.73-6.27,14-14,14S0,21.73,0,14S6.27,0,14,0S28,6.27,28,14z M21.95,14c-0.06-2.25-0.83-4.12-2.33-5.62
c-1.5-1.5-3.37-2.27-5.62-2.33c-2.25,0.06-4.12,0.83-5.62,2.33c-1.5,1.5-2.27,3.37-2.33,5.62c0.06,2.25,0.83,4.12,2.33,5.62
c1.5,1.5,3.37,2.27,5.62,2.33c0.54,0,1.06-0.05,1.57-0.14c-0.11-0.07-0.17-0.24-0.18-0.51s0.04-0.52,0.16-0.76
c0.15-0.26,0.3-0.61,0.46-1.04c0.15-0.43,0.08-0.77-0.23-1c-0.32-0.18-0.51-0.34-0.57-0.49s-0.19-0.32-0.37-0.54
s-0.28-0.36-0.28-0.44v-0.21c-0.01-0.21,0.05-0.43,0.19-0.66l0.25-0.39c0.01-0.04,0.02-0.1,0.02-0.19v-0.19
c-0.01-0.04-0.07-0.09-0.17-0.16s-0.17-0.1-0.22-0.1s-0.09,0.01-0.11,0.05s-0.07,0.06-0.13,0.06s-0.17-0.04-0.33-0.13
c-0.16-0.09-0.27-0.16-0.32-0.21s-0.1-0.11-0.14-0.19s-0.09-0.15-0.16-0.22s-0.11-0.1-0.16-0.09s-0.12,0-0.23-0.03l-1.45-0.56
c-0.26-0.11-0.43-0.23-0.5-0.37c-0.07-0.14-0.1-0.26-0.1-0.37s-0.06-0.23-0.17-0.37l-0.29-0.37c-0.08-0.12-0.14-0.24-0.17-0.34
s-0.05-0.15-0.06-0.13c-0.01,0.07,0.02,0.23,0.1,0.48s0.12,0.39,0.1,0.43c-0.03,0.01-0.07-0.02-0.12-0.11s-0.12-0.19-0.19-0.3
c-0.06-0.07-0.07-0.15-0.04-0.25c0.03-0.1-0.07-0.38-0.29-0.85c-0.22-0.47-0.26-0.85-0.11-1.15s0.24-0.57,0.3-0.82
c0.03-0.21,0.09-0.25,0.19-0.13c0.1,0.12,0.1,0.11,0.02-0.01c-0.08-0.18-0.11-0.4-0.08-0.65c0.03-0.26,0.01-0.46-0.06-0.61
c-0.11-0.1-0.3-0.08-0.58,0.05S9.12,8.14,9.1,8.16c0.03-0.12,0.18-0.28,0.46-0.46l0.87-0.58c0.26-0.17,0.49-0.22,0.69-0.17
s0.41,0.12,0.62,0.21s0.34,0.12,0.37,0.1s0.01-0.06-0.06-0.13s-0.07-0.13,0.01-0.16S12.26,6.96,12.43,7
c0.17,0.03,0.31,0.11,0.42,0.24s0.3,0.18,0.54,0.17s0.32,0.01,0.25,0.06s-0.11,0.12-0.11,0.19c0.04,0.06,0.02,0.1-0.05,0.13
s-0.2,0.1-0.36,0.21s-0.18,0.19-0.05,0.26c0.13,0.07,0.36,0.21,0.67,0.41c0.28,0.19,0.41,0.19,0.41-0.01s-0.02-0.4-0.06-0.61
c-0.01-0.14,0.05-0.19,0.2-0.14s0.22,0.07,0.24,0.08c0.17,0.11,0.27,0.15,0.32,0.11s0.13-0.04,0.26-0.01
c0.17,0.07,0.37,0.19,0.6,0.37l0.41,0.33c-0.43,0.25-0.6,0.41-0.51,0.49s0.2,0.14,0.32,0.2c0.06,0.06,0.02,0.12-0.1,0.21
s-0.2,0.12-0.23,0.12c-0.08-0.08-0.15-0.1-0.2-0.05s-0.1,0.09-0.16,0.13c-0.06,0.04-0.07,0.09-0.05,0.14s0.04,0.09,0.05,0.1
c-0.3,0.07-0.47,0.22-0.49,0.44s-0.02,0.39-0.01,0.49c0,0.08-0.07,0.18-0.21,0.3s-0.24,0.23-0.31,0.34c-0.03,0.14,0,0.29,0.08,0.47
c0.08,0.17,0.08,0.27,0,0.3c-0.08-0.01-0.22-0.12-0.41-0.32c-0.19-0.2-0.55-0.24-1.08-0.13c-0.19,0.04-0.39,0.16-0.6,0.36
s-0.23,0.49-0.06,0.88c0.21,0.32,0.43,0.38,0.68,0.19c0.25-0.19,0.43-0.3,0.56-0.31c0.1,0.08,0.12,0.2,0.06,0.35
c-0.06,0.15-0.08,0.24-0.08,0.27c0.06-0.01,0.17,0.02,0.34,0.09s0.28,0.27,0.32,0.58c0.04,0.31,0.19,0.49,0.46,0.55
s0.45,0.08,0.57,0.07s0.28-0.09,0.48-0.24s0.32-0.24,0.37-0.27s0.17-0.06,0.35-0.1s0.47,0.03,0.84,0.23s0.67,0.31,0.88,0.35
s0.38,0.11,0.5,0.2s0.18,0.2,0.18,0.33s0.05,0.23,0.14,0.3c0.15,0.04,0.39,0.07,0.72,0.07s0.57,0.1,0.72,0.28
c0.07,0.28-0.08,0.73-0.46,1.38c-0.37,0.64-0.68,1.05-0.93,1.23c-0.18,0.12-0.34,0.29-0.48,0.49s-0.3,0.38-0.49,0.55
s-0.41,0.34-0.66,0.52s-0.51,0.34-0.78,0.5c-0.21,0.15-0.36,0.32-0.46,0.51s-0.19,0.32-0.27,0.4c1.81-0.43,3.28-1.34,4.43-2.75
C21.31,17.6,21.9,15.93,21.95,14z"/>
</svg>

Before

Width:  |  Height:  |  Size: 4.4 KiB

-41
View File
@@ -1,41 +0,0 @@
<!-- and guess where these are defined? Yup, you guessed it: the _config.yml file -->
{% if site.resume_print_social_links %}
<ul>
<!-- GitHub link -->
{% if site.resume_social_links.resume_github_url %}
<li><strong>Github</strong>: {{ site.resume_social_links.resume_github_url }}</li>
{% endif %}
<!-- Twitter link -->
{% if site.resume_social_links.resume_twitter_url %}
<li><strong>Twitter</strong>: {{ site.resume_social_links.resume_twitter_url }}</li>
{% endif %}
<!-- Dribbble link -->
{% if site.resume_social_links.resume_dribbble_url %}
<li><strong>Dribbble</strong>: {{ site.resume_social_links.resume_dribbble_url }}</li>
{% endif %}
<!-- Facebook link -->
{% if site.resume_social_links.resume_facebook_url %}
<li><strong>Facebook</strong>: {{ site.resume_social_links.resume_facebook_url }}</li>
{% endif %}
<!-- LinkedIn link -->
{% if site.resume_social_links.resume_linkedin_url %}
<li><strong>LinkedIn</strong>: {{ site.resume_social_links.resume_linkedin_url }}</li>
{% endif %}
<!-- Instagram link -->
{% if site.resume_social_links.resume_instagram_url %}
<li><strong>Instagram</strong>: {{ site.resume_social_links.resume_instagram_url }}</li>
{% endif %}
<!-- Website link -->
{% if site.resume_social_links.resume_website_url %}
<li><strong>Website</strong>: {{ site.resume_social_links.resume_website_url }}</li>
{% endif %}
</ul>
{% endif %}
-254
View File
@@ -1,254 +0,0 @@
<!DOCTYPE html>
<html>
{% include head.html %}
<body class="theme-{% if site.resume_theme %}{{ site.resume_theme }}{% endif %}">
<div class="wrapper" itemscope itemtype="http://schema.org/Person">
<meta itemprop="telephone" content="{{ site.resume_contact_telephone }}"/>
<meta itemprop="address" content="{{ site.resume_contact_address }}"/>
<header class="page-header">
<!-- You can turn off the avatar in _config.yml by setting to false -->
{% if site.resume_avatar == 'true' %}
<img src="images/avatar.jpg" alt="my photo" class="avatar no-print" itemprop="image">
{% endif %}
<!-- Your name is defined in the _config.yml file -->
<h1 class="header-name" itemprop="name">{{ site.resume_name }}</h1>
<!-- To display contact info here, change `display_header_contact_info` value in _config.yml to true -->
{% if site.display_header_contact_info == 'true' %}
<div class="header-contact-info">
<p>{{ site.resume_header_contact_info }}</p>
</div>
{% endif %}
<div class="title-bar no-print">
<!-- Your title is also defined in the _config.yml file -->
<h2 class="header-title" itemprop="jobTitle">{{ site.resume_title }}</h2>
<!-- This is the markup for the icon links; moved out to an include because it's very verbose, and you shouldn't ever need to edit the markup (unless you want to re-order the icons); if you want to customize which links appear, define them in the _config.yml file -->
{% include icon-links.html %}
</div>
<div class="executive-summary" itemprop="description">
{{ site.resume_header_intro }}
</div>
</header>
{% if site.resume_section_experience %}
<!-- begin Experience -->
<section class="content-section">
<header class="section-header">
<h2>Experience</h2>
</header>
{% for job in site.data.experience %}
<div class="resume-item" itemscope itemprop="worksFor" itemtype="http://schema.org/Organization">
<h3 class="resume-item-title" itemprop="name">{{ job.company }}</h3>
<h4 class="resume-item-details" itemprop="description">{{ job.position }} &bull; {{ job.duration }}</h4>
<p class="resume-item-copy">{{ job.summary }}</p>
</div><!-- end of resume-item -->
{% endfor %}
</section>
<!-- end Experience -->
{% endif %}
{% if site.resume_section_publications %}
<!-- begin Publications -->
<section class="content-section">
<header class="section-header">
<h2>Publications</h2>
</header>
{% for publication in site.data.publications %}
<div class="resume-item" itemscope itemtype="http://schema.org/CreativeWork">
<meta itemprop="creator" content="{{ site.resume_name }}" itemtype="http://schema.org/Person" />
<h3 class="resume-item-copy" itemprop="name">{% if publication.url %}<a href="{{ publication.url }}" itemprop="url">{{ publication.name }}</a>{% else %}{{ publication.name }}{% endif %}</h3>
<p class="resume-item-copy">{{ publication.description }}</p>
</div>
{% endfor %}
</section>
<!-- end Publications -->
{% endif %}
{% if site.resume_section_education %}
<!-- begin Education -->
<section class="content-section">
<header class="section-header">
<h2>Education</h2>
</header>
{% for education in site.data.education %}
<div class="resume-item" itemscope itemprop="alumniOf" itemtype="http://schema.org/CollegeOrUniversity">
<h3 class="resume-item-title" itemprop="name">{{ education.uni }}</h3>
<h4 class="resume-item-details group" itemprop="description">{{ education.degree }} &bull; {{ education.year }}</h4>
<h5 class="resume-item-details award-title" itemprop="description">{{ education.award }}</h5>
<p class="resume-item-copy" itemprop="description">
<ul class="resume-item-list">
{% for award in education.awards %}
<li>{{ award.award }}</li>
{% endfor %}
</ul>
<p class="resume-item-copy">{{ education.summary }}</p>
</div>
{% endfor %}
</section>
<!-- end Education -->
{% endif %}
{% if site.resume_section_projects %}
<!-- begin Projects -->
<section class="content-section">
<header class="section-header">
<h2>Projects</h2>
</header>
{% for project in site.data.projects %}
<div class="resume-item" itemscope itemtype="http://schema.org/CreativeWork">
<meta itemprop="creator" content="{{ site.resume_name }}" itemtype="http://schema.org/Person" />
<h3 class="resume-item-title" itemprop="name">{% if project.url %}<a href="{{ project.url }}" itemprop="url">{{ project.project }}</a>{% else %}{{ project.project }}{% endif %}</h3>
<h4 class="resume-item-details" itemprop="description">{{ project.role }} &bull; {{ project.duration }}</h4>
<p class="resume-item-copy">{{ project.description }}</p>
</div>
{% endfor %}
</section>
<!-- end Projects -->
{% endif %}
{% if site.resume_section_skills %}
<!-- begin Skills -->
<section class="content-section">
<header class="section-header">
<h2>Skills</h2>
</header>
{% for skill in site.data.skills %}
<div class="resume-item">
<h4 class="resume-item-details">{{ skill.skill }}</h4>
<p class="resume-item-copy">{{ skill.description }}</p>
</div>
{% endfor %}
</section>
<!-- end Skills -->
{% endif %}
{% if site.resume_section_recognition %}
<!-- begin Recognition -->
<section class="content-section">
<header class="section-header">
<h2>Recognition</h2>
</header>
{% for recognition in site.data.recognitions %}
<div class="resume-item">
<h3 class="resume-item-title" itemprop="award">{{ recognition.award }}</h3>
<h4 class="resume-item-details">{{ recognition.organization }} &bull; {{ recognition.year }}</h4>
<p class="resume-item-copy">{{ recognition.summary }}</p>
</div>
{% endfor %}
</section>
<!-- end Recognition -->
{% endif %}
{% if site.resume_section_associations %}
<!-- begin Associations -->
<section class="content-section">
<header class="section-header">
<h2>Associations</h2>
</header>
{% for association in site.data.associations %}
<div class="resume-item" itemscope itemprop="memberOf" itemtype="http://schema.org/Organization">
<h3 class="resume-item-title" itemprop="name">{% if association.url %}<a href="{{ association.url }}">{{ association.organization }}</a>{% else %}{{ association.organization }}{% endif %}</h3>
<h4 class="resume-item-details" itemprop="description">{{ association.role }} &bull; {{ association.year }}</h4>
<p class="resume-item-copy">{{ association.summary }}</p>
</div>
{% endfor %}
</section>
<!-- end Associations -->
{% endif %}
{% if site.resume_section_interests %}
<!-- begin Interests -->
<section class="content-section">
<header class="section-header">
<h2>Outside Interests</h2>
</header>
<div class="resume-item">
<ul class="resume-item-list">
{% for interest in site.data.interests %}
<li>{{ interest.description }}</li>
{% endfor %}
</ul>
</div>
</section>
<!-- end Interests -->
{% endif %}
{% if site.resume_section_links %}
<!-- begin Links -->
<section class="content-section">
<header class="section-header">
<h2>Additional Links</h2>
</header>
<div class="resume-item">
<ul class="resume-item-list">
{% for link in site.data.links %}
<li><a href={{ link.url }} itemprop="url">{{ link.description }}</a></li>
{% endfor %}
</ul>
</div>
</section>
<!-- end Links -->
{% endif %}
{% if site.resume_print_social_links %}
<!-- begin Print Social Links -->
<section class="content-section print-only">
<header class="section-header">
<h2>Social Links</h2>
</header>
<div class="resume-item">
{% include print-social-links.html %}
</div>
</section>
<!-- end Print Social Links -->
{% endif %}
<footer class="page-footer">
<p class="footer-line">Theme credit to <a href="http://twitter.com/jglovier">@jglovier</a>. Fork me on <a href="https://github.com/jglovier/resume-template">GitHub</a>.</p>
<p class="footer-line">Pengzhan.dev - 2023</p>
</footer>
</div>
</body>
</html>
-46
View File
@@ -1,46 +0,0 @@
// Base styles
// ---------------------------------------/
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
html {
background: $white;
}
body{
@include serif;
color: $text_color;
font-size: 16px;
line-height: 1.5;
}
.wrapper:before,
.wrapper:after { content:""; display:table; }
.wrapper:after { clear: both; }
.wrapper {
max-width: 728px;
padding: 0 24px;
margin: 0 auto;
zoom: 1;
}
// clearfix (now called group,
// via http://css-tricks.com/snippets/css/clear-fix/)
.group:before,
.group:after { content:""; display:table; }
.group:after { clear:both; }
.group { zoom:1; /* For IE 6/7 (trigger hasLayout) */ }
::-moz-selection {
background: rgba(51,51,51,.8);
color: #fff;
text-shadow: none;
}
::selection {
background: rgba(51,51,51,.8);
color: #fff;
text-shadow: none;
}
-74
View File
@@ -1,74 +0,0 @@
// Layout styles
// ---------------------------------------/
// Layout from Primer (https://github.com/primer/primer/blob/master/scss/_layout.scss)
// Fixed-width, centered column for site content.
.container {
width: $container-width;
margin-right: auto;
margin-left: auto;
@include clearfix;
}
// Grid system
//
// Create rows with `.columns` to clear the floated columns and outdent the
// padding on `.column`s with negative margin for alignment.
.columns {
margin-right: -$grid-gutter;
margin-left: -$grid-gutter;
@include clearfix;
}
// Base class for every column (requires a column width from below)
.column {
float: left;
padding-right: $grid-gutter;
padding-left: $grid-gutter;
}
// Column widths
.one-third {
width: 33.333333%;
}
.two-thirds {
width: 66.666667%;
}
.one-fourth {
width: 25%;
}
.one-half {
width: 50%;
}
.three-fourths {
width: 75%;
}
.one-fifth {
width: 20%;
}
.four-fifths {
width: 80%;
}
// Single column hack
.single-column {
padding-right: $grid-gutter;
padding-left: $grid-gutter;
}
// Equal width columns via table sorcery.
.table-column {
display: table-cell;
width: 1%;
padding-right: $grid-gutter;
padding-left: $grid-gutter;
vertical-align: top;
}
-103
View File
@@ -1,103 +0,0 @@
// Mixins
// ---------------------------------------/
@mixin border-radius($radius) {
-webkit-border-radius: $radius;
-moz-border-radius: $radius;
border-radius: $radius;
}
@mixin transition($value) {
-webkit-transition: $value;
-moz-transition: $value;
transition: $value;
}
// Clearfix
//
// Clears floats via mixin (avoid using as a class).
@mixin clearfix {
&:before {
display: table;
content: "";
}
&:after {
display: table;
clear: both;
content: "";
}
}
// media querie tools
@mixin media_max($screen_width) {
@media (max-width: $screen_width) { @content; }
}
@mixin media_min($screen_width) {
@media (min-width: $screen_width) { @content; }
}
@mixin media_larger_than_mobile {
@media (min-width: 600px) { @content; }
}
@mixin media_mobile {
@media (max-width: 600px) { @content; }
}
// type utilities
@mixin sans {
font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
}
@mixin sans_light {
@include sans;
font-weight: 300;
}
@mixin sans_regular {
@include sans;
font-weight: 400;
}
@mixin sans_bold {
@include sans;
font-weight: 700;
}
@mixin sans_extrabold {
@include sans;
font-weight: 800;
}
@mixin serif {
font-family: "Lora", "Minion Pro", Palatino, Georgia, serif;
}
@mixin serif_regular {
@include serif;
font-weight: 400;
}
@mixin serif_bold {
@include serif;
font-weight: 700;
}
// layout
@mixin section_border {
border-top: 4px solid #c7c7c7;
border-bottom: 2px solid #c7c7c7;
padding: .2rem 0 .4rem;
}
@mixin section_border_thin {
border-top: 1px solid #c7c7c7;
border-bottom: 1px solid #c7c7c7;
padding: .2rem 0 .2rem;
}
-426
View File
@@ -1,426 +0,0 @@
// Normalize styles
// ---------------------------------------/
/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */
/**
* 1. Set default font family to sans-serif.
* 2. Prevent iOS and IE text size adjust after device orientation change,
* without disabling user zoom.
*/
html {
font-family: sans-serif; /* 1 */
text-size-adjust: 100%; /* 2 */
}
/**
* Remove default margin.
*/
body {
margin: 0;
}
/* HTML5 display definitions
========================================================================== */
/**
* Correct `block` display not defined for any HTML5 element in IE 8/9.
* Correct `block` display not defined for `details` or `summary` in IE 10/11
* and Firefox.
* Correct `block` display not defined for `main` in IE 11.
*/
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
main,
menu,
nav,
section,
summary {
display: block;
}
/**
* 1. Correct `inline-block` display not defined in IE 8/9.
* 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
*/
audio,
canvas,
progress,
video {
display: inline-block; /* 1 */
vertical-align: baseline; /* 2 */
}
/**
* Prevent modern browsers from displaying `audio` without controls.
* Remove excess height in iOS 5 devices.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/**
* Address `[hidden]` styling not present in IE 8/9/10.
* Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22.
*/
[hidden],
template {
display: none;
}
/* Links
========================================================================== */
/**
* Remove the gray background color from active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* Improve readability of focused elements when they are also in an
* active/hover state.
*/
a:active,
a:hover {
outline: 0;
}
/* Text-level semantics
========================================================================== */
/**
* Address styling not present in IE 8/9/10/11, Safari, and Chrome.
*/
abbr[title] {
border-bottom: 1px dotted;
}
/**
* Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
*/
b,
strong {
font-weight: bold;
}
/**
* Address styling not present in Safari and Chrome.
*/
dfn {
font-style: italic;
}
/**
* Address variable `h1` font-size and margin within `section` and `article`
* contexts in Firefox 4+, Safari, and Chrome.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/**
* Address styling not present in IE 8/9.
*/
mark {
background: #ff0;
color: #000;
}
/**
* Address inconsistent and variable font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` affecting `line-height` in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
/* Embedded content
========================================================================== */
/**
* Remove border when inside `a` element in IE 8/9/10.
*/
img {
border: 0;
}
/**
* Correct overflow not hidden in IE 9/10/11.
*/
svg:not(:root) {
overflow: hidden;
}
/* Grouping content
========================================================================== */
/**
* Address margin not present in IE 8/9 and Safari.
*/
figure {
margin: 1em 40px;
}
/**
* Address differences between Firefox and other browsers.
*/
hr {
box-sizing: content-box;
height: 0;
}
/**
* Contain overflow in all browsers.
*/
pre {
overflow: auto;
}
/**
* Address odd `em`-unit font size rendering in all browsers.
*/
code,
kbd,
pre,
samp {
font-family: monospace, monospace;
font-size: 1em;
}
/* Forms
========================================================================== */
/**
* Known limitation: by default, Chrome and Safari on OS X allow very limited
* styling of `select`, unless a `border` property is set.
*/
/**
* 1. Correct color not being inherited.
* Known issue: affects color of disabled elements.
* 2. Correct font properties not being inherited.
* 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
*/
button,
input,
optgroup,
select,
textarea {
color: inherit; /* 1 */
font: inherit; /* 2 */
margin: 0; /* 3 */
}
/**
* Address `overflow` set to `hidden` in IE 8/9/10/11.
*/
button {
overflow: visible;
}
/**
* Address inconsistent `text-transform` inheritance for `button` and `select`.
* All other form control elements do not inherit `text-transform` values.
* Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
* Correct `select` style inheritance in Firefox.
*/
button,
select {
text-transform: none;
}
/**
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
* and `video` controls.
* 2. Correct inability to style clickable `input` types in iOS.
* 3. Improve usability and consistency of cursor style between image-type
* `input` and others.
*/
button,
html input[type="button"], /* 1 */
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button; /* 2 */
cursor: pointer; /* 3 */
}
/**
* Re-set default cursor for disabled elements.
*/
button[disabled],
html input[disabled] {
cursor: default;
}
/**
* Remove inner padding and border in Firefox 4+.
*/
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
/**
* Address Firefox 4+ setting `line-height` on `input` using `!important` in
* the UA stylesheet.
*/
input {
line-height: normal;
}
/**
* It's recommended that you don't attempt to style these elements.
* Firefox's implementation doesn't respect box-sizing, padding, or width.
*
* 1. Address box sizing set to `content-box` in IE 8/9/10.
* 2. Remove excess padding in IE 8/9/10.
*/
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Fix the cursor style for Chrome's increment/decrement buttons. For certain
* `font-size` values of the `input`, it causes the cursor style of the
* decrement button to change from `default` to `text`.
*/
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Address `appearance` set to `searchfield` in Safari and Chrome.
* 2. Address `box-sizing` set to `border-box` in Safari and Chrome.
*/
input[type="search"] {
-webkit-appearance: textfield; /* 1 */
box-sizing: content-box; /* 2 */
}
/**
* Remove inner padding and search cancel button in Safari and Chrome on OS X.
* Safari (but not Chrome) clips the cancel button when the search input has
* padding (and `textfield` appearance).
*/
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* Define consistent border, margin, and padding.
*/
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
/**
* 1. Correct `color` not being inherited in IE 8/9/10/11.
* 2. Remove padding so people aren't caught out if they zero out fieldsets.
*/
legend {
border: 0; /* 1 */
padding: 0; /* 2 */
}
/**
* Remove default vertical scrollbar in IE 8/9/10/11.
*/
textarea {
overflow: auto;
}
/**
* Don't inherit the `font-weight` (applied by a rule above).
* NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
*/
optgroup {
font-weight: bold;
}
/* Tables
========================================================================== */
/**
* Remove most spacing between table cells.
*/
table {
border-collapse: collapse;
border-spacing: 0;
}
td,
th {
padding: 0;
}
-234
View File
@@ -1,234 +0,0 @@
// Resume styles
// ---------------------------------------/
// util
.section-header {
@include section_border;
margin-bottom: 1.5rem;
h2 {
@include sans;
font-weight: 900;
font-size: 1.75rem;
letter-spacing: -.05rem;
margin: 0;
}
}
// page header styles
.page-header { padding: 1.5rem 0;
.avatar {
width: 95px;
max-width: 100%;
overflow: hidden;
@include border-radius(100px);
margin: 0 auto;
display: block;
}
.header-name {
@include sans;
font-size: 3rem;
font-weight: 900;
text-align: center;
letter-spacing: -.15rem;
line-height: 1;
margin: 1rem 0 0;
}
.header-contact-info {
@include sans;
text-align: center;
}
.header-contact-info p a {
color: inherit;
}
.title-bar {
@include section_border;
margin-top: 2rem;
padding: .4rem 0;
@include clearfix;
.header-title {
@include sans;
font-weight: 300;
font-size: 1.75rem;
line-height: 2rem;
letter-spacing: -.05rem;
text-align: center;
margin: 0 0 .5rem;
@include media_larger_than_mobile {
float: left;
margin: 0;
}
}
}
.executive-summary {
@include media_larger_than_mobile { font-size: 1.125rem; }
}
}
.icon-links {
margin: 0;
padding: 0;
list-style: none;
position: relative;
top: 4px;
text-align: center;
border-top: 2px solid #c7c7c7;
padding-top: .4rem;
@include media_larger_than_mobile {
float: right;
border-top: none;
padding-top: 0;
}
.icon-link-item {
display: inline-block;
margin-left: 5px;
}
.icon-link { display: inline-block;
&:hover .icon path {
fill: #333;
@include transition(all .2s ease);
}
}
.icon {
height: 28px;
}
}
.contact-button {
@include border-radius(3px);
background-color: #efefef;
text-decoration: none;
text-align: center;
display: block;
margin: 1.5rem auto;
width: 220px;
font-size: 1.375rem;
@include sans_light;
color: #333;
line-height: 55px;
@include transition(all .2s ease);
&:hover {
background-color: #333;
color: #fff;
}
&.not-looking { width: 400px; }
}
// resume content styles
.content-section {
margin: 0 0 3rem;
.resume-item { margin-bottom: 2rem; }
.resume-item-title {
@include sans_bold;
margin: 0 0 .75rem;
font-size: 1.5rem;
letter-spacing: -.05rem;
line-height: 1;
}
.resume-item-details {
font-size: 1.125rem;
margin: 0 0 .75rem;
line-height: 1;
&.award-title {
font-size: 1rem;
}
}
.resume-item-title,
.resume-item-details,
.resume-item-copy,
.resume-item-list {
a {
color: #333;
text-decoration: none;
border-bottom: 1px solid;
&:hover {
border-bottom: none;
color: #9c9c9c;
}
}
}
.resume-item-copy { margin: .75rem 0 0; }
}
// footer styles
.page-footer {
border-top: 4px solid #c7c7c7;
padding-top: 1.5rem;
text-align: center;
margin-bottom: 3rem;
.footer-line {
margin: 0;
font-size: .875rem;
color: #999;
> a {
font-weight: 700;
color: #333;
}
}
}
.print-only { display: none !important }
@media print {
.no-print { display: none !important }
.print-only { display: block !important }
body { font-size: 12px; line-height: 1rem; }
.page-header { padding: 1rem 0 .2rem;
.header-name {
@include sans_bold;
font-size: 2rem;
margin: 0 0 .2rem;
}
}
.section-header {
@include section_border_thin;
margin-bottom: 1rem;
h2 { font-size: 1.2rem; line-height: 1.5rem; }
}
.content-section {
margin: 0 0 1rem;
.resume-item { margin-bottom: 1rem; }
.resume-item-title {
font-size: 16px;
line-height: .7em;
}
.resume-item-details {
font-size: 12px;
margin: 0 0 .75rem;
line-height: .7em;
font-style:italic;
&.award-title {
font-size: 11px;
}
}
}
}
-21
View File
@@ -1,21 +0,0 @@
// Variables
// ---------------------------------------/
$container-width: 980px !default;
$grid-gutter: 10px !default;
// Colors
$white: #fff;
$black: #333;
// Styles
$text_color: $black;
// Font stack
$body-font: Helvetica, arial, nimbussansl, liberationsans, freesans, clean, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol" !default;
// The base body size
$body-font-size: 13px !default;
// Monospace font stack
$mono-font: Consolas, "Liberation Mono", Menlo, Courier, monospace !default;
-14
View File
@@ -1,14 +0,0 @@
---
---
// "Reset" styles and SCSS reusable components
@import "normalize";
@import "mixins";
@import "variables";
// Generic site base & layout styles
@import "base";
@import "layout";
// Layouts
@import "resume";
-2
View File
@@ -1,2 +0,0 @@
rm -rf /var/www/html
cp -rf _site /var/www/html
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 551 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

-3
View File
@@ -1,3 +0,0 @@
---
layout: resume
---
+1273
View File
File diff suppressed because it is too large Load Diff
+29
View File
@@ -0,0 +1,29 @@
{
"name": "pengzhan-dev",
"version": "1.0.0",
"description": "Dynamic web framework and personal site skeleton for pengzhan.dev",
"main": "server/index.js",
"scripts": {
"start": "node server/index.js",
"dev": "APP_ENV=staging node --watch --watch-path=server --watch-path=views --watch-path=public server/index.js",
"dev:persistent": "node --watch --watch-path=server --watch-path=views --watch-path=public server/index.js",
"lint": "node scripts/lint.js",
"test": "NODE_ENV=test node test/test.js"
},
"keywords": ["blog", "k8s", "ssr", "express", "markdown"],
"author": "Pengzhan Hao",
"license": "MIT",
"dependencies": {
"chokidar": "^4.0.3",
"ejs": "^3.1.10",
"express": "^4.21.2",
"feed": "^4.2.2",
"gray-matter": "^4.0.3",
"js-yaml": "^4.1.0",
"markdown-it": "^14.1.0",
"markdown-it-anchor": "^9.2.0",
"markdown-it-attrs": "^4.3.1",
"markdown-it-footnote": "^4.0.0",
"prismjs": "^1.29.0"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 KiB

+411
View File
@@ -0,0 +1,411 @@
:root {
--color-bg: #f9f9f9;
--color-surface: #ffffff;
--color-surface-soft: #f4f4f5;
--color-text-main: #111111;
--color-text-secondary: #4b5563;
--color-text-muted: #6b7280;
--color-border: #e5e7eb;
--color-border-hover: #d1d5db;
--color-accent: #111111;
--color-mark-bg: #fef08a;
--color-mark-text: #111111;
--font-serif: 'Cardo', 'Iowan Old Style', 'Palatino Linotype', 'Songti SC', 'STSong', Georgia, serif;
--font-mono: 'Maple Mono NF CN', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
--font-sans: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
--max-width: 1180px;
--content-width: 820px;
--header-height: 64px;
--radius-sm: 6px;
--radius-md: 12px;
--radius-lg: 20px;
--radius-full: 9999px;
--transition-fast: 0.15s ease;
--transition-normal: 0.25s ease;
}
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
font-size: 16px;
scroll-behavior: smooth;
scroll-padding-top: calc(var(--header-height) + 1.5rem);
-webkit-text-size-adjust: 100%;
}
/* Ensure hash navigation (#) and footnotes never hide under sticky header */
:target,
[id],
h1[id], h2[id], h3[id], h4[id], h5[id], h6[id],
.footnote-ref,
.footnote-item,
li[id^="fn"] {
scroll-margin-top: calc(var(--header-height) + 1.5rem);
}
/* Responsive media constraint - never wider than parent container */
img, video, iframe, embed, object {
max-width: 100%;
height: auto;
}
body {
font-family: var(--font-serif);
color: var(--color-text-main);
background-color: var(--color-bg);
line-height: 1.8;
letter-spacing: 0.01em;
min-height: 100vh;
display: flex;
flex-direction: column;
}
.container {
width: 100%;
max-width: var(--max-width);
margin: 0 auto;
padding: 0 2rem;
}
.content-container {
width: 100%;
max-width: var(--content-width);
margin: 0 auto;
}
/* Header */
.site-header {
position: sticky;
top: 0;
z-index: 100;
background: rgba(249, 249, 249, 0.95);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
height: var(--header-height);
display: flex;
align-items: center;
}
.header-inner {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
}
.brand {
text-decoration: none;
color: var(--color-text-main);
}
.brand-title {
font-size: 1.55rem;
font-family: var(--font-serif);
font-weight: 700;
letter-spacing: -0.01em;
}
.site-nav {
display: flex;
gap: 2rem;
align-items: center;
}
.nav-link {
text-decoration: none;
font-family: var(--font-serif);
font-size: 1.05rem;
color: var(--color-text-main);
transition: opacity var(--transition-fast);
padding: 0.25rem 0;
}
.nav-link:hover {
opacity: 0.65;
}
.nav-link.active {
text-decoration: underline;
text-underline-offset: 4px;
}
/* Mobile Hamburger Menu Toggle */
.mobile-menu-toggle {
display: none;
background: none;
border: none;
cursor: pointer;
padding: 0.5rem;
flex-direction: column;
justify-content: center;
gap: 5px;
width: 40px;
height: 40px;
}
.hamburger-line {
display: block;
width: 22px;
height: 2px;
background-color: var(--color-text-main);
border-radius: 2px;
transition: transform 0.25s ease, opacity 0.25s ease;
}
.mobile-menu-toggle.is-active .hamburger-line:nth-child(1) {
transform: translateY(7px) rotate(45deg);
}
.mobile-menu-toggle.is-active .hamburger-line:nth-child(2) {
opacity: 0;
}
.mobile-menu-toggle.is-active .hamburger-line:nth-child(3) {
transform: translateY(-7px) rotate(-45deg);
}
/* Mobile Slide-Down Navigation Drawer */
.mobile-nav-drawer {
display: none;
position: absolute;
top: 100%;
left: 0;
width: 100%;
background: var(--color-bg);
border-bottom: 1px solid var(--color-border);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
padding: 0.75rem 1.25rem 1.25rem;
z-index: 999;
}
.mobile-nav-drawer.is-open {
display: block;
animation: slideDown 0.25s ease;
}
@keyframes slideDown {
from { opacity: 0; transform: translateY(-8px); }
to { opacity: 1; transform: translateY(0); }
}
.mobile-nav-links {
display: flex;
flex-direction: column;
}
.mobile-nav-link {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.85rem 0.5rem;
text-decoration: none;
color: var(--color-text-main);
font-family: var(--font-serif);
font-size: 1.15rem;
border-bottom: 1px solid var(--color-border-subtle, rgba(0, 0, 0, 0.05));
transition: color var(--transition-fast), padding-left var(--transition-fast);
}
.mobile-nav-link:last-child {
border-bottom: none;
}
.mobile-nav-link:hover, .mobile-nav-link:active {
color: var(--color-accent);
padding-left: 0.75rem;
}
.mobile-nav-link.active {
font-weight: 600;
color: var(--color-accent);
}
.mobile-nav-arrow {
font-size: 1rem;
opacity: 0.4;
}
/* Main Content area */
.site-main {
flex: 1;
padding: 2.5rem 0 4rem;
}
/* Footer */
.site-footer {
border-top: 1px solid var(--color-border);
background-color: var(--color-surface);
padding: 2.5rem 0;
font-family: var(--font-sans);
font-size: 0.88rem;
color: var(--color-text-muted);
}
.footer-inner {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 1.5rem;
}
.social-links {
display: flex;
gap: 1.25rem;
align-items: center;
}
.social-link {
color: var(--color-text-secondary);
display: flex;
align-items: center;
justify-content: center;
transition: color var(--transition-fast), transform var(--transition-fast);
}
.social-link:hover {
color: var(--color-text-main);
transform: translateY(-2px);
}
.social-link svg {
width: 19px;
height: 19px;
fill: currentColor;
}
/* Headings & Typography */
h1, h2, h3, h4, h5, h6 {
color: var(--color-text-main);
line-height: 1.35;
margin-bottom: 1rem;
font-weight: 700;
}
h1 { font-size: 2.2rem; }
h2 { font-size: 1.65rem; margin-top: 2rem; }
h3 { font-size: 1.35rem; margin-top: 1.5rem; }
p {
margin-bottom: 1.25rem;
}
a {
color: var(--color-accent);
text-decoration: underline;
text-decoration-thickness: 1px;
text-underline-offset: 3px;
transition: color var(--transition-fast);
}
a:hover {
color: #000000;
}
mark {
background-color: var(--color-mark-bg);
color: var(--color-mark-text);
padding: 0.1em 0.25em;
border-radius: 3px;
font-weight: 500;
}
blockquote {
border-left: 3px solid var(--color-border-hover);
padding: 0.5rem 0 0.5rem 1.25rem;
margin: 1.5rem 0;
color: var(--color-text-secondary);
font-style: italic;
}
hr {
border: 0;
height: 1px;
background: var(--color-border);
margin: 2.5rem 0;
}
/* Responsive */
@media (max-width: 768px) {
:root {
--header-height: 64px;
--content-padding: 1.25rem;
}
.site-header {
position: sticky;
top: 0;
z-index: 990;
}
/* Hide inline desktop nav and show hamburger */
.site-nav.desktop-nav {
display: none;
}
.mobile-menu-toggle {
display: flex;
}
.brand-title {
font-size: 1.25rem;
}
.brand-tagline {
font-size: 0.68rem;
display: block;
margin-top: 0.1rem;
opacity: 0.85;
}
/* Centered, balanced editorial mobile footer */
.site-footer {
padding: 2.5rem 1.25rem 3rem;
}
.footer-inner {
flex-direction: column;
align-items: center;
text-align: center;
gap: 1.4rem;
}
.footer-left {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.45rem;
}
.footer-tagline {
font-family: var(--font-serif);
font-size: 1.05rem;
color: var(--color-text-main);
}
.footer-copyright {
font-size: 0.82rem;
color: var(--color-text-muted);
}
.social-links {
justify-content: center;
gap: 1.5rem;
}
.social-link {
display: flex;
align-items: center;
justify-content: center;
min-width: 44px;
min-height: 44px;
}
}
File diff suppressed because it is too large Load Diff
+24
View File
@@ -0,0 +1,24 @@
/* Fonts definitions with graceful local fallbacks */
@font-face {
font-family: 'XiaoheSimplifySerif-VF';
src: url('/fonts/XiaoheSimplifySerif-VF.otf') format('opentype');
font-weight: 100 900;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Maple Mono NF CN';
src: url('/fonts/MapleMono-NF-CN-Regular.ttf') format('truetype');
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Maple Mono NF CN';
src: url('/fonts/MapleMono-NF-CN-Bold.ttf') format('truetype');
font-weight: 700;
font-style: normal;
font-display: swap;
}
+118
View File
@@ -0,0 +1,118 @@
/**
* Okaidia PrismJS theme matching WordPress Prismatic plugin
*/
code[class*="language-"],
pre[class*="language-"] {
color: #f8f8f2;
background: none;
text-shadow: 0 1px rgba(0, 0, 0, 0.3);
font-family: 'Maple Mono NF CN', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 0.95em;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1.6;
tab-size: 2;
hyphens: none;
}
pre[class*="language-"] {
padding: 1.25rem 1.5rem;
margin: 1.75rem 0;
overflow: auto;
border-radius: 12px;
background: #272822;
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
}
:not(pre) > code[class*="language-"],
pre[class*="language-"] {
background: #272822;
}
/* Inline code outside pre */
:not(pre) > code {
padding: 0.2em 0.4em;
border-radius: 4px;
color: #e83e8c;
background-color: rgba(0, 0, 0, 0.05);
font-family: 'Maple Mono NF CN', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
font-size: 0.88em;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: #8292a2;
}
.token.punctuation {
color: #f8f8f2;
}
.token.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.constant,
.token.symbol,
.token.deleted {
color: #f92672;
}
.token.boolean,
.token.number {
color: #ae81ff;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #a6e22e;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string,
.token.variable {
color: #f8f8f2;
}
.token.atrule,
.token.attr-value,
.token.function,
.token.class-name {
color: #e6db74;
}
.token.keyword {
color: #66d9ef;
}
.token.regex,
.token.important {
color: #fd971f;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

+49
View File
@@ -0,0 +1,49 @@
/**
* Mobile Navigation Drawer Controller
*/
document.addEventListener('DOMContentLoaded', () => {
const toggleBtn = document.getElementById('mobile-menu-toggle');
const drawer = document.getElementById('mobile-nav-drawer');
if (toggleBtn && drawer) {
toggleBtn.addEventListener('click', () => {
const isOpen = drawer.classList.contains('is-open');
if (isOpen) {
drawer.classList.remove('is-open');
toggleBtn.classList.remove('is-active');
toggleBtn.setAttribute('aria-expanded', 'false');
} else {
drawer.classList.add('is-open');
toggleBtn.classList.add('is-active');
toggleBtn.setAttribute('aria-expanded', 'true');
}
});
// Close on navigation link click
drawer.querySelectorAll('.mobile-nav-link').forEach(link => {
link.addEventListener('click', () => {
drawer.classList.remove('is-open');
toggleBtn.classList.remove('is-active');
toggleBtn.setAttribute('aria-expanded', 'false');
});
});
// Close when clicking outside
document.addEventListener('click', (e) => {
if (drawer.classList.contains('is-open') && !drawer.contains(e.target) && !toggleBtn.contains(e.target)) {
drawer.classList.remove('is-open');
toggleBtn.classList.remove('is-active');
toggleBtn.setAttribute('aria-expanded', 'false');
}
});
// Close on Escape key
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && drawer.classList.contains('is-open')) {
drawer.classList.remove('is-open');
toggleBtn.classList.remove('is-active');
toggleBtn.setAttribute('aria-expanded', 'false');
}
});
}
});
+20
View File
@@ -0,0 +1,20 @@
document.addEventListener('DOMContentLoaded', () => {
const searchInput = document.getElementById('search-input');
const searchForm = document.getElementById('search-form');
const pillElements = document.querySelectorAll('.search-pill-btn');
if (searchInput && pillElements.length > 0) {
pillElements.forEach(pill => {
pill.addEventListener('click', (e) => {
e.preventDefault();
const query = pill.getAttribute('data-query');
if (query) {
searchInput.value = query;
if (searchForm) {
searchForm.submit();
}
}
});
});
}
});
+129
View File
@@ -0,0 +1,129 @@
/**
* Interactive Table of Contents Controller
* - Perfectly Centered Post Layout (No 70/30 split)
* - Margin-Docked Auto-Hide / Minimizable Desktop Rail (Dropbox Paper style)
* - Mobile / Tablet Floating Action Badge & Slide-Up Bottom Sheet
*/
document.addEventListener('DOMContentLoaded', () => {
const sideToc = document.getElementById('side-toc');
const minimizeBtn = document.getElementById('side-toc-minimize');
const floatingBadge = document.getElementById('floating-toc-badge');
const sheet = document.getElementById('mobile-toc-sheet');
const overlay = document.getElementById('mobile-toc-overlay');
const sheetCloseBtn = document.getElementById('mobile-sheet-close');
// Check user preference for minimized TOC in localStorage
let isMinimized = false;
try {
isMinimized = localStorage.getItem('pengzhan_toc_minimized') === 'true';
} catch (_) {}
function applyDesktopState() {
if (!sideToc) return;
if (window.innerWidth >= 1280) {
if (isMinimized) {
sideToc.classList.add('is-minimized');
if (floatingBadge) floatingBadge.classList.add('is-visible');
} else {
sideToc.classList.remove('is-minimized');
if (floatingBadge) floatingBadge.classList.remove('is-visible');
}
} else {
// On screens < 1280px, desktop side rail is hidden via CSS, floating badge is available
if (floatingBadge) floatingBadge.classList.add('is-visible');
}
}
if (minimizeBtn) {
minimizeBtn.addEventListener('click', () => {
isMinimized = true;
try { localStorage.setItem('pengzhan_toc_minimized', 'true'); } catch (_) {}
applyDesktopState();
});
}
// Floating badge click
if (floatingBadge) {
floatingBadge.addEventListener('click', () => {
if (window.innerWidth >= 1280) {
// Expand desktop side TOC
isMinimized = false;
try { localStorage.setItem('pengzhan_toc_minimized', 'false'); } catch (_) {}
applyDesktopState();
} else {
// Open mobile bottom sheet
openMobileSheet();
}
});
}
function openMobileSheet() {
if (sheet && overlay) {
sheet.classList.add('is-open');
overlay.classList.add('is-open');
document.body.style.overflow = 'hidden';
}
}
function closeMobileSheet() {
if (sheet && overlay) {
sheet.classList.remove('is-open');
overlay.classList.remove('is-open');
document.body.style.overflow = '';
}
}
if (sheetCloseBtn) sheetCloseBtn.addEventListener('click', closeMobileSheet);
if (overlay) overlay.addEventListener('click', closeMobileSheet);
// Close mobile sheet on anchor click
if (sheet) {
sheet.querySelectorAll('.mobile-sheet-link').forEach(link => {
link.addEventListener('click', () => {
closeMobileSheet();
});
});
}
window.addEventListener('resize', applyDesktopState, { passive: true });
applyDesktopState();
// Scroll Spy for Desktop Rail and Mobile List
const sideLinks = Array.from(document.querySelectorAll('.side-toc-link'));
if (sideLinks.length === 0) return;
const headings = sideLinks
.map(link => {
const id = link.getAttribute('data-id');
const target = document.getElementById(id);
return target ? { id, target, link } : null;
})
.filter(Boolean);
if (headings.length === 0) return;
function updateActiveHeading() {
const scrollPos = window.scrollY + 140;
let currentActive = headings[0];
for (let i = 0; i < headings.length; i++) {
const top = headings[i].target.getBoundingClientRect().top + window.scrollY;
if (top <= scrollPos) {
currentActive = headings[i];
} else {
break;
}
}
headings.forEach(item => {
if (item === currentActive) {
item.link.classList.add('toc-active');
} else {
item.link.classList.remove('toc-active');
}
});
}
window.addEventListener('scroll', updateActiveHeading, { passive: true });
updateActiveHeading();
});
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

+38
View File
@@ -0,0 +1,38 @@
site:
title: "Pengzhan"
tagline: "Was childish, Now foolish."
url: "https://pengzhan.dev"
author:
name: "Pengzhan Hao"
subtitle: "Software engineer on Cloud"
gravatar: "1f0283e19ce9680f2441b9c37211e0b890fd7ebf4b9ea5a5c413fe0379d7d3f9"
admin:
secret: "admin" # Admin dashboard password (or set via ADMIN_SECRET env var)
navigation:
- label: "Home"
path: "/"
- label: "Blogs"
path: "/blogs"
- label: "Search"
path: "/search"
- label: "Quick Links"
path: "/quick-links"
social:
- platform: github
url: "https://github.com/CoderSherlock"
- platform: instagram
url: "https://www.instagram.com/pengzhan.hao/"
- platform: linkedin
url: "https://www.linkedin.com/in/pengzhanhao/"
- platform: email
address: "[email protected]"
plugins:
google-analytics:
enabled: true
tracking_id: "G-MNP4QN1T9T"
reading-time:
enabled: true
+2
View File
@@ -0,0 +1,2 @@
/* Custom CSS overrides on Persistent Volume */
/* Any CSS written here will be loaded after base and components CSS */
File diff suppressed because it is too large Load Diff
+104
View File
@@ -0,0 +1,104 @@
---
title: "Pengzhan Hao"
layout: home
permalink: "/"
---
<div class="home-hero-section">
<h1 class="hero-name">Pengzhan Hao</h1>
<p class="hero-subtitle">Software engineer on Cloud</p>
<div class="hero-intro">
<p>Joined Google in 2022, working in GKE, and focusing on Node &amp; runtime area. Most projects landed on bringing ARM-based VMs to GKE, introduced more flexibilities in node customization and pod lifecycle improvement for preemptible VMs</p>
<p>Contributed to Kubernetes open source community and became a member in 2023</p>
<p>Now working on making GKE a more productive platform for AI/ML</p>
</div>
</div>
<section class="home-section">
<h2 class="section-title">Research and study life in Binghamton University</h2>
<div class="two-column-grid">
<div class="column-block">
<p>Wide interests in multiple research areas, and spend most of my time in Edge computing, distributed systems and networks, and mobile operating system research. Under supervision from Professor Yifan Zhang, I spent 7 years in these research areas and outcome publications</p>
</div>
<div class="column-block">
<p>I worked as a teaching assistant for Operating system and Distributed system courses for both graduate and undergraduate levels. Mostly, I helped lecturers finish academic goals and hosted Labs for students. Before and during my research career, I finished my Master’s level education with a full grade point average.</p>
</div>
</div>
</section>
<section class="home-section">
<h2 class="section-title">Publications</h2>
<div class="publications-list">
<div class="pub-item">
<a href="/documents/eddl-sec21.pdf" target="_blank" class="pub-title">EDDL: A Distributed Deep Learning System for Resource-limited Edge Computing Environment</a>
<p class="pub-meta">Pengzhan Hao, and Yifan Zhang &bull; The Sixth ACM/IEEE Symposium on Edge Computing (ACM/IEEE SEC) &bull; San Jose, CA, USA, 2021.</p>
</div>
<div class="pub-item">
<a href="/documents/eta-infocom18.pdf" target="_blank" class="pub-title">A Case for Web Service Bandwidth Reduction on Mobile Devices with Edge-hosted Personal Services</a>
<p class="pub-meta">Yongshu Bai, Pengzhan Hao, and Yifan Zhang &bull; The 37th IEEE International Conference on Computer Communications (IEEE INFOCOM) &bull; Honolulu, HI, USA, 2018.</p>
</div>
<div class="pub-item">
<a href="/documents/a7-hao.pdf" target="_blank" class="pub-title">EdgeCourier: An Edge-hosted Personal Service for Low-bandwidth Document Synchronization in Mobile Cloud Storage Services</a>
<p class="pub-meta">Pengzhan Hao, Yongshu Bai, Xin Zhang, and Yifan Zhang &bull; The 2nd ACM/IEEE Symposium on Edge Computing (ACM/IEEE SEC) &bull; San Jose, CA, USA, 2017.</p>
</div>
<div class="pub-item">
<a href="/documents/p169-zhang.pdf" target="_blank" class="pub-title">Poster: Securing Device Inputs for Smartphones Using Hypervisor Based Approach</a>
<p class="pub-meta">Xin Zhang, Yongshu Bai, Pengzhan Hao, and Yifan Zhang &bull; The 15th ACM International Conference on Mobile Systems, Applications, and Services (ACM MobiSys) &bull; Niagara Falls, NY, USA, 2017.</p>
</div>
<div class="pub-item">
<a href="/documents/p163-hao.pdf" target="_blank" class="pub-title">Poster: EPS – Edge-hosted Personal Services for Mobile Users</a>
<p class="pub-meta">Pengzhan Hao, Yongshu Bai, Xin Zhang, and Yifan Zhang &bull; The 15th ACM International Conference on Mobile Systems, Applications, and Services (ACM MobiSys) &bull; Niagara Falls, NY, USA, 2017.</p>
</div>
</div>
</section>
<section class="home-section">
<h2 class="section-title">Undergraduate life in Beihang University</h2>
<div class="two-column-grid">
<div class="column-block">
<p>From 2011 to 2015, I attended the Software Engineering program at Beihang University. Overall, I received 3.25 out of 4 when graduating. During most of the era, I was enthusiasm in participating Movie Association and Mountain Climbing Association, and also worked as student assistant for career program for the last year.</p>
</div>
<div class="column-block">
<p>Starting from my junior year, I studied and researched consecutively in two labs. Worked in Android Kernel and module localization and smart urban computing areas. In urban computing areas, led a small team to participate in a joint project together by the Advanced Computer application technology engineering research center(Beihang University) and Microsoft Research Asia(MSRA).</p>
</div>
</div>
</section>
<section class="home-section">
<h2 class="section-title">Projects</h2>
<div class="projects-list">
<div class="project-item">
<h3 class="project-name">EDDL &ndash; Edge distributed deep learning framework</h3>
<p>A lite-weight c++ written framework(based on Dlib) for distributed neural network training on embedded devices and mobile phones. With supporting of our implemented runtime profilers, training devices can interconnect in an optimized network topology under various network environments. In our last publication, we showed our framework can perform distributed training for diverse neural networks on resource-limited devices. The EDDL framework also showed well scalability in large amounts of devices. We are currently working on network optimization on per-batch communication time and overall convergence time.</p>
</div>
<div class="project-item">
<h3 class="project-name">Edge Courier</h3>
<p>A proxy-based protocol for incremental file synchronization and cooperative online document editing. This protocol supports file hosting services including Google Drive, Dropbox, and OneDrive as well as document collaboration services such as G Suite and Microsoft 365. Edge courier can significantly decrease bandwidth consumption for collaborative editing web apps, and also showed advancement in cloud file storage services.</p>
</div>
<div class="project-item">
<h3 class="project-name">RTRouting assistant</h3>
<p>A real time routing prototype which can guide vehicle to alternative routes with high-volume traffic prediction. The predicted traffic alert is generated by a pre-trained model learned from history monitored traffics, real time event warnings and a abnormal traffic prediction model. This project belongs to a urban computing project studied together by Advance computer application technology engineering research center(Beihang University) and Microsoft Research Asia(MSRA).</p>
</div>
</div>
</section>
<section class="home-section">
<h2 class="section-title">Recognition</h2>
<div class="recognition-list">
<div class="recog-item">
<strong>ACM/IEEE SEC student travel grant</strong>
<span class="recog-meta">IEEE &bull; 2017, 2021</span>
</div>
<div class="recog-item">
<strong>ACM MobiSys 2017 student travel grant</strong>
<span class="recog-meta">ACM &bull; 2017</span>
</div>
</div>
</section>
+20
View File
@@ -0,0 +1,20 @@
---
title: "Quick Links"
permalink: "/quick-links"
---
A curated collection of public resources, contact points, and references:
## Professional Profiles
- **GitHub**: [github.com/CoderSherlock](https://github.com/CoderSherlock) &mdash; open-source projects, personal tools, and experiments.
- **LinkedIn**: [linkedin.com/in/pengzhanhao](https://www.linkedin.com/in/pengzhanhao/) &mdash; professional background.
- **Instagram**: [@pengzhan.hao](https://www.instagram.com/pengzhan.hao/) &mdash; photography and outdoor life.
## Direct Contact
- **Email**: [[email protected]](mailto:[email protected])
## Documents
- **Curriculum Vitae**: [Download CV (PDF)](/documents/CV.pdf)
@@ -0,0 +1,17 @@
module.exports = {
injectHead({ pluginConfig }) {
const trackingId = pluginConfig.tracking_id;
if (!trackingId) return '';
return `
<!-- Google Analytics (GA4 via Plugin) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=${trackingId}"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${trackingId}');
</script>
`;
}
};
@@ -0,0 +1,5 @@
{
"name": "google-analytics",
"version": "1.0.0",
"description": "Injects Google Analytics 4 tracking script into head"
}
@@ -0,0 +1,18 @@
module.exports = {
onPostLoad({ post }) {
const wordCount = (post.rawContent || '').trim().split(/\s+/).length;
// Assume average 200 words per minute
const minutes = Math.max(1, Math.round(wordCount / 200));
post.readingTime = `${minutes} min read`;
post.wordCount = wordCount;
},
injectPostEnd({ post }) {
if (post && post.readingTime) {
return `<div style="font-family: var(--font-sans); font-size: 0.85rem; color: var(--color-text-muted); margin-top: 2rem; padding: 0.75rem 1rem; background: var(--color-surface-soft); border-radius: var(--radius-sm);">
Article statistics: ~${post.wordCount} words &bull; ${post.readingTime}
</div>`;
}
return '';
}
};
@@ -0,0 +1,5 @@
{
"name": "reading-time",
"version": "1.0.0",
"description": "Calculates reading time and word count for articles"
}
@@ -0,0 +1,15 @@
module.exports = {
onPostLoad({ post }) {
const raw = post.rawContent || '';
const internalMatches = raw.match(/\[[^\]]+\]\((\/[^)]+)\)/g) || [];
const externalMatches = raw.match(/\[[^\]]+\]\((https?:\/\/[^)]+)\)/g) || [];
post.seoMetrics = {
internalLinkCount: internalMatches.length,
externalLinkCount: externalMatches.length,
hasMetaDescription: Boolean(post.excerpt && post.excerpt.length > 30),
titleLength: post.title.length,
isTitleOptimal: post.title.length >= 25 && post.title.length <= 70
};
}
};
@@ -0,0 +1,5 @@
{
"name": "seo-analyzer",
"version": "1.0.0",
"description": "Dynamic SEO auditor and content health inspector"
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 MiB

@@ -0,0 +1,41 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 600" width="100%" height="100%">
<defs>
<linearGradient id="kidBg" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#fed7aa"/>
<stop offset="100%" stop-color="#fb923c"/>
</linearGradient>
<filter id="kidShadow" x="-10%" y="-10%" width="120%" height="120%">
<feDropShadow dx="0" dy="6" stdDeviation="10" flood-opacity="0.15"/>
</filter>
</defs>
<rect width="600" height="600" fill="url(#kidBg)" rx="32"/>
<!-- Creative Building Blocks / Robot Icon -->
<g transform="translate(300, 250)" filter="url(#kidShadow)">
<rect x="-90" y="-80" width="180" height="150" rx="24" fill="#ffffff"/>
<!-- Robot antenna -->
<line x1="0" y1="-80" x2="0" y2="-120" stroke="#ffffff" stroke-width="8" stroke-linecap="round"/>
<circle cx="0" cy="-125" r="14" fill="#ea580c"/>
<!-- Eyes -->
<circle cx="-40" cy="-15" r="16" fill="#0284c7"/>
<circle cx="40" cy="-15" r="16" fill="#0284c7"/>
<!-- Smile -->
<path d="M -40 30 Q 0 60 40 30" stroke="#ea580c" stroke-width="8" stroke-linecap="round" fill="none"/>
</g>
<!-- Educational Blocks -->
<g transform="translate(180, 420)" filter="url(#kidShadow)">
<rect x="0" y="0" width="60" height="60" rx="12" fill="#38bdf8"/>
<text x="30" y="42" text-anchor="middle" fill="#ffffff" font-family="-apple-system, sans-serif" font-size="32" font-weight="900">A</text>
</g>
<g transform="translate(260, 400)" filter="url(#kidShadow)">
<rect x="0" y="0" width="60" height="60" rx="12" fill="#ec4899"/>
<text x="30" y="42" text-anchor="middle" fill="#ffffff" font-family="-apple-system, sans-serif" font-size="32" font-weight="900">I</text>
</g>
<g transform="translate(340, 420)" filter="url(#kidShadow)">
<rect x="0" y="0" width="60" height="60" rx="12" fill="#10b981"/>
<text x="30" y="42" text-anchor="middle" fill="#ffffff" font-family="-apple-system, sans-serif" font-size="32" font-weight="900">&lt;&gt;</text>
</g>
<text x="300" y="530" text-anchor="middle" fill="#7c2d12" font-family="-apple-system, sans-serif" font-size="28" font-weight="800">Coding for Kids</text>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

@@ -0,0 +1,37 @@
---
title: "AI can change: Coding for kids"
date: 2026-03-02
author: "Pengzhan Hao"
category: "Non-tech"
tags: ["AI Era","Diary","Family","Gemini","Teaching"]
cover: "/posts-assets/ai-can-change-coding-for-kids/images/cover-en.png"
lang: "en"
excerpt: "Since the explosion of Large Language Models (LLMs), a wave of disruption has quietly swept through every industry. What does it mean for everyday life and parenting?"
---
Since the explosion of Large Language Models (LLMs), a wave of disruption has quietly swept through every industry. While it is impossible to list every sector currently in flux, the sheer volume of new demands from startups and the AI-driven overhauls of traditional businesses serve as constant reminders of the magnitude of this shift. However, unlike the macro-narrative of business-driven AI, the actual impact of LLMs on the average person remains somewhat elusive—even for programmers standing at the forefront of this change.
Two weeks ago, while taking care of my child alone, I noticed a letter-tracing worksheet she brought home from daycare. It hit me that she was starting to grasp the concept of "homework." In the United States, expecting a daycare to provide Chinese writing practice is almost a luxury. To help her build a foundational feel for writing Chinese characters, I decided to follow the pace of children growing up in China and create some practice sheets for her. After searching online, I found that most Chinese calligraphy worksheets are designed for older students or adults. For a child who can barely hold a pen steady, writing small characters is simply too challenging. I found a site called [E-Zitie](https://www.nqez.com/) that offered exactly what I needed: [large characters with stroke order guides](https://www.nqez.com/zitie-hanzibishunlizi.html). However, while the basic service is free, any customization requires a paid membership. Although the 38 RMB/year fee is negligible, the website’s UI felt like something from three generations ago, making me doubt its ability to meet my long-term needs.
Last week, a thought struck me: Why not build this tool myself? For a programmer with a background in infrastructure and backend development, building a web app is actually quite a daunting task. In my previous personal projects, the most time-consuming part was always the meticulous frontend tweaking. Even setting aside the frontend, architecting a backend that meets professional engineering standards is an exhausting endeavor. Whenever I thought about the sheer volume of work involved, my inner laziness would whisper: *Just pay for the membership.*
But this time, I started with an unusual level of confidence. My recent experiences using language models and practicing “[Vibe Coding](https://x.com/karpathy/status/1886192184321433842)” (as coined by Andrej Karpathy) taught me that AI is already incredibly capable of building prototypes. So, during the gaps in my parenting duties, I wrote a 300-word requirement and design doc. What followed was a highly efficient dialogue: about 10 minutes of AI generation and another 10 minutes of fine-tuning. [Gemini-CLI](https://github.com/google-gemini/gemini-cli), powered by the latest Gemini Pro model, helped me stand up a clean, functional website in no time.
![*Home interface of the calligraphy practice generator*](/posts-assets/ai-can-change-coding-for-kids/images/toolbox-index-1024x590.png "*Home interface of the calligraphy practice generator*")
![*AI-generated Chinese character tracing PDF with stroke order*](/posts-assets/ai-can-change-coding-for-kids/images/zitie-example.png "*AI-generated Chinese character tracing PDF with stroke order*")
**The project is currently hosted on my personal domain and can be accessed at [toolbox.pengzhan.dev](https://toolbox.pengzhan.dev).**
In less than 30 minutes, I was already printing out tracing sheets for my child to doodle on. This sense of instant gratification gave me a deeper realization of how AI will shape our future:
- **Software creation is becoming a task anyone can perform, regardless of coding ability.** This means that as long as you are willing to spend a few cents on LLM API calls, you can easily build what was traditionally considered a "program." The barrier to entry for programming has dropped significantly. More importantly, when everyone can be a Product Manager, the question of "who implements it" becomes secondary. Take education as an example: if you are a physics teacher explaining force analysis, an editable PPT might capture your logic, but it’s hard to make it look right. Existing educational software might demonstrate the concept, but it might not align perfectly with your teaching style. So, why not let AI build a bespoke, web-based teaching demo just for you?
- **Highly customized and truly innovative needs will define the human contribution to future software engineering.** Since basic needs are now easily met, where does human value lie? Clearly, AI still struggles with unique, highly specific requirements or completely innovative explorations. Even when AI can "stitch together" a solution, the process is often laborious and requires constant human guidance. For instance, because AI still lacks precision in visual positioning and coordinate handling, the generated PDF had a persistent offset bug that AI couldn't fix. Despite my lack of familiarity with the [gopdf](https://github.com/signintech/gopdf) library, I had to dive into the source code myself to identify the error in 2D coordinate calculations. This "last mile" limitation is the current bottleneck of AI. Similarly, for completely unknown technical problems—like a Unix Socket error I encountered last week—LLMs (at least the latest [Gemini-3.1-Pro](https://blog.google/technology/ai/google-gemini-3-1-pro-update/)) still cannot find a breakthrough using purely statistical logic. That human "spark"—an efficient activation of massive, compressed knowledge—remains far beyond the reach of current AI architectures.
- **As LLMs enter the next phase and supporting tools mature, the very form of education will undergo a massive transformation.** The power of LLMs is both transformative and "disruptive." My initial goal was just to satisfy a need for writing practice, but the success of this tool made me realize the possibilities are much broader. Recently, I noticed my child struggling with counting worksheets from school; she often didn't understand the instructions, leading her to color the wrong items or fail to fill in the counts correctly. So, I customized a new feature for her: I let the AI randomly generate similar counting exercises. This approach—completely on-demand and randomly generated—not only reinforces the skill but also prevents "false mastery" caused by repeating the same static problems. This is the true cornerstone of "student-centered" personalized education: it's no longer just a theoretical ideal, but a tangible infrastructure we can finally build.
![*Counting practice sheet from school; instructions are somewhat ambiguous*](/posts-assets/ai-can-change-coding-for-kids/images/in-school-counting-scaled-e1772402564249-859x1024.png "*Counting practice sheet from school; instructions are somewhat ambiguous*")
![*Customized counting exercise randomly generated by AI for the same skill set*](/posts-assets/ai-can-change-coding-for-kids/images/practice-ai-generated-counting-scaled-e1772402666560-782x1024.png "*Customized counting exercise randomly generated by AI for the same skill set*")
**We cannot predict exactly what comes next. But I stand in awe of this wave of change, and I am deeply excited to witness the next evolution of human society.**
@@ -0,0 +1,37 @@
---
title: "AI可以改变什么:为孩子编程"
date: 2026-03-02
author: "Pengzhan Hao"
category: "Non-tech"
tags: ["AI Era","Diary","Family","Gemini","Teaching"]
cover: "/posts-assets/ai-can-change-coding-for-kids/images/cover.png"
lang: "zh"
excerpt: "自从大语言模型风靡以来,一场席卷各行业的颠覆性变革悄然开启。以 LLM 为代表的 AI 模型对普通人的生活究竟意味着什么?"
---
自从大语言模型风靡以来,一场席卷各行业的颠覆性变革悄然开启。虽然我们无法枚举所有正在发生变化的行业,但仅从各类创业公司提出的新需求,以及大型科技公司对传统业务的 AI 化改进中,都能感受到这次变革影响之深。然而,与业务驱动的宏大叙事不同,以 LLM 为代表的 AI 模型对普通人的生活究竟意味着什么,即使是身处风口浪尖的程序员们,有时也难以言说。
两周前,独自带娃的我看着孩子从 daycare 带回来的字母练习纸,突然意识到她已开始接触“作业”这一概念。在美国,期望 daycare 提供中文书写练习几乎是一种奢求。为了帮助孩子建立基本的汉字书写感,我决定按照国内的节奏为她制作一些字帖。在网上寻觅一番后,我发现大多数中文字帖的受众是学龄童或成人,对于连握笔画画都不稳的孩子来说,写小字确实太具挑战性了。虽然 [E字帖](https://www.nqez.com/) 提供了符合我需求的[带笔顺范例的大字](https://www.nqez.com/zitie-hanzibishunlizi.html),但其定制功能需要充值会员。尽管 38 元一年的价格并不贵,但那个 UI 极具“年代感”的网站让我对其能否满足长期需求产生了质疑。
上周,我突发奇想:为什么不自己做一个工具呢?对于基础设施/后端背景的程序员来说,构建一个 Web App 其实是一项极具挑战的任务。在过往的个人项目中,耗时最多的往往是前端微调;而即便抛开前端,构建一个符合工程规范的后端架构也极其耗时耗力。每当想到这些繁杂的工作,心中的惰性总会劝我:还是花点小钱买会员吧。
但这一次,我开始得格外自信。近期使用语言模型和实践由 Andrej Karpathy 提出的 “[Vibe Coding](https://x.com/karpathy/status/1886192184321433842)” 的经历让我明白,如果只是构建原型产品,AI 已经做得足够出色。于是,我趁着带娃的间隙,写下了约 300 字的需求和设计稿。接下来的对话过程非常高效:大约 10 分钟的 AI 生成,加上 10 分钟的微调,[Gemini-CLI](https://github.com/google-gemini/gemini-cli) 配合最新的 Gemini Pro 模型就帮我搭建出了一个简洁易用的网站。
![*字帖生成工具的主页界面*](/posts-assets/ai-can-change-coding-for-kids/images/toolbox-index-1024x590.png "*字帖生成工具的主页界面*")
![*工具生成的带笔顺汉字练习 PDF*](/posts-assets/ai-can-change-coding-for-kids/images/zitie-example.png "*工具生成的带笔顺汉字练习 PDF*")
该项目目前托管在我的个人域名下,可以通过 [**toolbox.pengzhan.dev**](https://toolbox.pengzhan.dev) 访问。
用时不到 30 分钟,我就能打印出字帖供孩子涂鸦。这种即时反馈的惊喜,让我对 AI 影响未来的方式有了更深刻的认知:
- **人人皆可制作软件而无需编程。** 这意味着,只要愿意投入少量的 LLM API 成本,任何人都能轻易实现传统意义上的程序。编程技能的门槛大幅降低,或者说,当人人都能扮演产品经理(PM)的角色时,“由谁去实现”已不再是核心问题。以教育为例,如果你是一位物理老师在讲受力分析,易于编辑的 PPT 可以完全展现你的教学思路,但做好却不容易。有些现成的软件能演示受力分析,但它们未必能完美契合你的讲解逻辑。那么,为什么不让 AI 为你定制一个专属的动画 Web Demo 呢?
- **高度定制化与完全创新化的需求,将定义未来软件工程中人类的贡献。** 既然基础需求已能轻易满足,人的力量该如何体现?显然,面对独特且高度定制的需求,或是完全创新性的探索, AI 依然难以独立胜任。即使 AI 能缝合解决方案,过程往往也费时费力,需要人类持续引导。例如,由于 AI 在视觉定位和坐标处理上仍有欠缺,生成的 PDF 曾出现一个难以自动修复的偏移 Bug。我虽然不熟悉 [gopdf](https://github.com/signintech/gopdf) 的实现,但仍需深入研究其源码,最终发现了二维坐标计算的误差。这种“最后一公里”的局限,正是当前 AI 的短板。此外,面对完全未知的技术难题——比如我上周遇到的 Unix Socket 错误——LLM(至少是最近发布的 [Gemini-3.1-Pro](https://blog.google/technology/ai/google-gemini-3-1-pro-update/))依然无法通过统计逻辑找到突破口。人类那种基于知识压缩后的“灵光一闪”,依然是 AI 目前模型结构下,难望项背的高效激活方式。
- **随着 LLM 进入下一阶段及配套工具的成熟,教育形态将发生重大变革。** LLM 的力量既强大,又具有“破坏性”。我制作字帖初衷是为了满足写字需求,但它的成功让我意识到更多可能性。最近我观察到,孩子在面对学校的计数练习纸时,常因不理解指令而无法按要求涂色和填写个数。于是,我针对她的进度定制了一个新功能:让 AI 随机生成类似的练习内容。这种完全按需定制、随机生成的方式,不仅增强了技能锻炼,也避免了因题目重复导致的“虚假掌握”。这正是“以学生为中心”的个性化教育能够落地的基石,不再只是思想的发展和准备,而是更加强大的教育基础设施。
![*孩子在学校带回来的计数练习纸,指令较为模糊*](/posts-assets/ai-can-change-coding-for-kids/images/in-school-counting-scaled-e1772402564249-859x1024.png "*孩子在学校带回来的计数练习纸,指令较为模糊*")
![*针对同一技能定制开发的 AI 随机生成练习内容*](/posts-assets/ai-can-change-coding-for-kids/images/practice-ai-generated-counting-scaled-e1772402666560-782x1024.png "*针对同一技能定制开发的 AI 随机生成练习内容*")
**我们无法预知未来。但我对这种变革的浪潮既感到敬畏,也让我对人类社会形态的下一次进化充满期待。**
Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Some files were not shown because too many files have changed in this diff Show More