Compare commits

...
8 Commits
Author SHA1 Message Date
haopengzhan 053d19ce6a fix(analytics): prevent tracking on admin pages and local dev environments
Build and Push Docker Image / build (push) Successful in 19s
2026-09-16 23:41:33 -07:00
haopengzhan afc05deb91 chore(k8s): safeguard production secrets with example template and add Makefile k8s helpers
Build and Push Docker Image / build (push) Successful in 17s
2026-09-16 23:30:21 -07:00
haopengzhan 2502c2946e fix(k8s): add chmod 775 to init-content-perms container
Build and Push Docker Image / build (push) Successful in 39s
2026-09-16 23:18:27 -07:00
haopengzhan d865ee2d2b chore(k8s): add production kubernetes manifests for pengzhan-dev namespace
Build and Push Docker Image / build (push) Successful in 1m5s
2026-09-16 23:13:53 -07:00
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
201 changed files with 12416 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
+26 -3
View File
@@ -1,4 +1,27 @@
_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
# Kubernetes Production Secrets
k8s/01-secret.yaml
k8s/*secret.yaml
!k8s/*.example.yaml
-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.
+80
View File
@@ -0,0 +1,80 @@
.PHONY: help dev dev-persistent test lint clean docker-build docker-run k8s-status k8s-logs k8s-restart k8s-sync-content
# 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"
@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 " make k8s-status Show pods, service, ingress, and PVC in k8s"
@echo " make k8s-logs Stream live logs from production pod"
@echo " make k8s-restart Trigger zero-downtime rolling restart"
@echo " make k8s-sync-content Sync local markdown/media into live cluster PVC"
@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
k8s-status:
@echo "Checking Kubernetes status in namespace pengzhan-dev..."
kubectl get pods,svc,ingress,pvc -n pengzhan-dev -o wide
k8s-logs:
@echo "Streaming logs from pengzhan-web deployment..."
kubectl logs -f deployment/pengzhan-web -n pengzhan-dev
k8s-restart:
@echo "Triggering zero-downtime rolling restart of pengzhan-web..."
kubectl rollout restart deployment/pengzhan-web -n pengzhan-dev
kubectl rollout status deployment/pengzhan-web -n pengzhan-dev
k8s-sync-content:
@echo "Syncing local sample-content/ to Kubernetes /app/content/..."
@POD=$$(kubectl get pod -n pengzhan-dev -l app=pengzhan-web -o jsonpath='{.items[0].metadata.name}'); \
if [ -z "$$POD" ]; then \
echo "Error: No running pengzhan-web pod found."; exit 1; \
fi; \
echo "-> Target pod: $$POD"; \
kubectl cp sample-content/. pengzhan-dev/$$POD:/app/content/; \
kubectl exec -n pengzhan-dev $$POD -- chmod -R 775 /app/content; \
echo "-> Sync complete. Hot-reload watcher will index new content."
+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
---
+6
View File
@@ -0,0 +1,6 @@
apiVersion: v1
kind: Namespace
metadata:
name: pengzhan-dev
labels:
app.kubernetes.io/name: pengzhan-dev
+10
View File
@@ -0,0 +1,10 @@
# Template Kubernetes secret for pengzhan.dev
# Copy this file to k8s/01-secret.yaml (gitignored) and customize before applying.
apiVersion: v1
kind: Secret
metadata:
name: pengzhan-secrets
namespace: pengzhan-dev
type: Opaque
stringData:
ADMIN_SECRET: "CHANGEME_ADMIN_SECRET"
+12
View File
@@ -0,0 +1,12 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pengzhan-content-pvc
namespace: pengzhan-dev
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-path
resources:
requests:
storage: 5Gi
+88
View File
@@ -0,0 +1,88 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: pengzhan-web
namespace: pengzhan-dev
labels:
app: pengzhan-web
spec:
replicas: 1
revisionHistoryLimit: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: pengzhan-web
template:
metadata:
labels:
app: pengzhan-web
spec:
securityContext:
fsGroup: 1000
fsGroupChangePolicy: "OnRootMismatch"
initContainers:
- name: init-content-perms
image: busybox:1.36
imagePullPolicy: IfNotPresent
command: ["sh", "-c"]
args:
- |
mkdir -p /app/content/data
chown -R 1000:1000 /app/content || true
chmod -R 775 /app/content || true
volumeMounts:
- name: content-data
mountPath: /app/content
containers:
- name: pengzhan-web
image: git.pengzhan.dev/haopengzhan/pengzhan.dev:latest
imagePullPolicy: Always
ports:
- containerPort: 3000
name: http
env:
- name: NODE_ENV
value: "production"
- name: PORT
value: "3000"
- name: CONTENT_DIR
value: "/app/content"
- name: ADMIN_SECRET
valueFrom:
secretKeyRef:
name: pengzhan-secrets
key: ADMIN_SECRET
volumeMounts:
- name: content-data
mountPath: /app/content
livenessProbe:
httpGet:
path: /
port: 3000
initialDelaySeconds: 15
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /
port: 3000
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 2
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
volumes:
- name: content-data
persistentVolumeClaim:
claimName: pengzhan-content-pvc
+15
View File
@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: pengzhan-web
namespace: pengzhan-dev
labels:
app: pengzhan-web
spec:
type: ClusterIP
selector:
app: pengzhan-web
ports:
- name: http
port: 80
targetPort: 3000
+24
View File
@@ -0,0 +1,24 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: pengzhan-ingress
namespace: pengzhan-dev
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: traefik
rules:
- host: pengzhan.dev
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: pengzhan-web
port:
number: 80
tls:
- hosts:
- pengzhan.dev
secretName: pengzhan-dev-tls
+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,40 @@
module.exports = {
injectHead({ pluginConfig, pageType }) {
const trackingId = pluginConfig.tracking_id;
if (!trackingId) return '';
// 1. Never inject analytics on administrative moderation pages
if (pageType === 'admin') {
return '';
}
// 2. Do not inject in development, staging, or testing modes (unless explicitly enabled)
const isProd = process.env.NODE_ENV === 'production' && process.env.APP_ENV !== 'staging';
if (!isProd && !pluginConfig.enable_in_dev) {
return '';
}
return `
<!-- Google Analytics (GA4 via Plugin) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=${trackingId}"></script>
<script>
(function() {
// 3. Client-side domain safeguard: only track on production pengzhan.dev domains
var host = window.location.hostname;
if (host !== 'pengzhan.dev' && !host.endsWith('.pengzhan.dev')) {
window['ga-disable-${trackingId}'] = true;
window.dataLayer = window.dataLayer || [];
window.gtag = function(){};
return;
}
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
window.gtag = gtag;
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

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