Home » Blog » May 2026 · What I've Been Up To [Switch Theme] [中文]

May 2026 · What I've Been Up To

Stats, emojis, SEO, comments, Markdown


Stats Page & Visitor Counter

  • ASCII Heatmap & World Map

At the end of April, I got the idea to build a stats page for the blog. Initially just an hourly distribution heatmap, but the requirements kept growing: daily bar charts, monthly trends, crawler sources, visitor loyalty...

ASCII world map. Used DB-IP's free city-level IP database (8 million rows), batch-parsed visitor IP coordinates through a single file scan, then overlaid density characters onto map.txt.

  • Visitor Counter GIF

Used Pillow to generate an 88×31 pixel LED seven-segment animated counter. 12-frame loop, scanline scrolling, glow effect, breathing light. Each homepage visit auto-updates the count and regenerates the image. Logic is isolated in scripts/counter.py.

  • Server Resource Monitoring

Added psutil for CPU, memory, disk, and uptime reading, with a ctypes fallback for systems without psutil.

Comprehensive SEO Optimization

  • HTML 4.01 Transitional DOCTYPE, <html lang="zh/en">
  • Per-page unique <title>, auto-generated <meta description> (extracted from first body paragraph), <meta keywords> (extracted from tags)
  • <link rel="canonical"> + <link rel="alternate" hreflang="..."> Chinese-English cross-linking
  • Open Graph + Twitter Card complete tags
  • Global <font size="6"> auto-wrapped with <h1>, <font size="5"> with <h2>
  • Breadcrumb navigation (Home > Blog > Article Title)
  • Sitemap rewrite: 14 → 47+ URLs, including changefreq / priority
  • security.txt + /.well-known/security.txt (RFC 9116)

IE 5.5 Compatibility Audit

In early May, I used AI to conduct a full-site compatibility audit, uncovering 30+ incompatibilities: rgba(), display:flex, box-sizing, overflow-x/y, placeholder, querySelector, etc. An audit document and blog post were produced. Core JS (guestbook, editor) already has IE 5.5 dual-path fallbacks.

Guestbook Improvements

  • Floor Number System: Each message has an absolute floor number #N, displayed in gray on the right
  • Reply Quoting: Clicking the reply button inserts @xxx[#N], build process auto-strips reference numbers
  • Banned Words: Defined in data/runtime/banned_words.json, auto-replaced with <em>*</em>
  • Changelog Escaping: HTML-escaped at build time, preventing <font> tag pollution

Article Comment System

Added comment sections to each blog post using a hybrid approach: CGI handles submission + build-time injection of existing comments. Supports threaded replies (unlimited nesting), emojis, IP toggle. Submission redirects with 302 back to the comment anchor, with background async build. Page-type (page-*) articles also have comment sections.

Email System Optimization

  • Background Async Sending: Previously, @reply emails were sent synchronously — users had to wait for the email to finish before seeing the page redirect. Now changed to an independent process for background sending (mailer.py).
  • Emoji Rendering in Emails: [em07] is converted to <img> tags pointing to GIFs on the server.
  • Email HTML Template Extraction: Email template separated into src/components/cgi/mailer-email.html, using {variable} placeholders.

Markdown Blog Support

All 50+ blog posts and standalone pages converted from HTML to Markdown. Created scripts/md2html.py converter — supports headings/bold/italic/code/tables/footnotes/quotes/images. Build process auto-detects .md extension and converts. Old HTML source files archived to src/archive-html/. MD and HTML can be mixed.

Web Server Optimization

  • HTTP/1.1 + keep-alive (single connection reuse)
  • ThreadingMixIn multi-threading (parallel requests, no queuing)
  • Static resource aggressive caching: Cache-Control: max-age=86400 + Expires: 2030
  • Startup parameters nolog and nocount
  • Route table refactored, /.well-known/ support added

Project Structure Cleanup

  • data/ layered: runtime/ (manually maintained), build/ (auto-generated), comments/, logs/
  • assets/images/backgroundsassets/images/bg
  • All scripts migrated to scripts/, .cmd entry points unified
  • changelog.txtdata/logs/changelog.log
  • External resources localized (mirror-external.py), removed preconnect external links
  • counter.py, banned_words.py and other new scripts split out independently

Other

  • Stats page excludes specified IP (180.154.121.226)
  • 404.html unified to HTML 4.01 standard
  • Editor path correction (pages/blogblog)
  • All images max-width:100%;height:auto; to prevent overflow
  • Blog post count rose to 50+, sitemap coverage updated accordingly
  • RSS changed to extract title + subtitle from compiled dist pages

Guestbook Emoji System

  • Pixel Art GIF Emojis

Added 20 pixel art emojis to the guestbook. Click » Emoticons below the input field to expand the folding panel, select from the grid, or manually type [em01] through [em20]. Stored as codes, automatically converted to images on both web pages and emails.

The emoji panel doesn't load images by default (JS lazy generation), saving 20 initial HTTP requests.

  • Compact Form Layout

Guestbook form changed from scattered label+input to HTML table layout. Name/Email/Content/Display IP/Send button all in a 125px-wide cell. Emoji panel saves space through folding, 1px CSS border replaced the original 2px default border.

Email System Optimization

  • Background Async Sending

Previously, @reply emails were sent synchronously — users had to wait for the email to finish before seeing the page redirect. Now changed to an independent background process (mailer.py), passing content via stdin, subprocess.Popen returns immediately after launch.

  • Emoji Rendering in Emails

[em07] in email body is now converted to <img> tags pointing to GIFs on the server, so recipients can also see the emoji animations.

Log System Refactoring

  • Unified Categorization

Previously, logs from various modules were scattered across different locations. Now all consolidated under data/logs/:


data/logs/
├── access/          ← Server access logs (daily .log, old logs .gz compressed)
├── guestbook.log    ← Guestbook operations (@mentions, SMTP status)
├── editor.log       ← Editor operations (create/edit/delete, auth failures)
├── search.log       ← Search keywords + result counts
├── toolbox.log      ← Toolbox usage records
├── build.log        ← Build records (page builds, full site rebuilds)
└── error.log        ← Cross-module errors

Created a unified logger.py module that all CGI scripts use to write to their respective categorized logs.

Performance Optimization

  • External badge images added loading="lazy", not blocking first render
  • 20 emoji GIFs also changed to loading="lazy", reducing first-screen requests by 20
  • Header added external domain preconnect hints
  • Sidebar comment area locked to 700px width, preventing long content from breaking layout

Other Small Improvements

  • Web server changed to whitelist routing, sensitive directories (data/, scripts/, src/) unified 403
  • Added <!DOCTYPE html> declaration site-wide, goodbye Quirks Mode
  • editor.py added operation auditing (who changed what article and when)
  • Build script incremental caching switched to MD5 content hash, component changes correctly trigger full site rebuild
  • Blog editor, search engine, and toolbox all connected to unified logging
  • README documentation updated with explanations of all new features

Reading Mode

In early May, I built a major feature: generating a reading mode version for every blog post and standalone page.

  • Dual Theme Toggle: The normal version uses the 90s neon dark theme; clicking [Reading Mode] on the right side of the breadcrumb switches to the light reading version. The reading version has a cream white background #f4f4ec, dark gray text, classic blue #3366cc links, and the font stack switches from SimSun to Georgia serif.
  • URL Directory Structure: Reading mode URLs use the /read/ directory prefix rather than the early filename-prefix approach. /read/blog-xxx, /en/read/blog-xxx — clean structure, switching modes won't jump out of the current language.
  • Layout Differences: Normal version is 980px three-column; reading mode drops the right sidebar and banner, content width expands from 700px to 840px. Blog/page keep the left TOC column (140px), while the homepage centers 840px content without an empty TOC column.
  • Reading-specific Components: reading-header.html, reading-footer.html, reading-sidebar-left.html and five other reading-specific components, completely independent from 90s components. Reading mode removes guestbook, changelog, comments, and 8831 friend links, leaving only article content and TOC.
  • Archive & Tags Pages Also Have Reading Mode: /read/archive, /read/tags, also with the light theme. generate-archive.ps1 auto-generates reading versions after generating the normal archives.
  • Unified Breadcrumb: Both normal and reading mode breadcrumbs use the table left-right layout — left side path Home » Blog » Title, right side [Reading Mode/Return to Normal] [English/中文] two buttons. Normal mode uses 90s neon colors, reading mode uses deep blue #3366cc.
  • Automatic Link Rewriting: During reading mode build, links in the body pointing to normal mode blog/page/archive/tags are automatically rewritten to /read/ versions, so users won't accidentally leave reading mode while clicking through.

IP Preference System

To make mode switching "remembered", I implemented server-side IP preference tracking:

  • No JS / No Cookie: Preferences stored in data/runtime/ip_prefs.txt, format IP|mode=read|lang=en. Visiting /read/<em> auto-records mode=read, visiting /en/</em> auto-records lang=en.
  • Inline Handling: ?set_mode=normal and ?set_lang=zh parameters are processed server-side for preferences, then the page is returned directly without issuing a redirect — avoiding IE 5.5 redirect freezes.
  • MSIE Skips 303: IE 5.5 needs to rebuild a TCP connection for every redirect, making it extremely slow. Server detects MSIE UA and skips all preference redirects; IE 5.5 users manually click [Reading Mode] to switch.
  • _pref_handled Flag: After preference processing, skips the .html → clean URL redirect, preventing an infinite loop of "click return to normal → 301 to clean URL → IP preference 303 back to reading mode". This bug wasted a lot of time to track down.

Font System Improvements

  • Reading Mode Body face Unification: During build, replace md2html.py's generated face="'宋体',SimSun" with face="Georgia, '宋体', SimSun", ensuring serif fonts throughout reading mode.
  • Code Monospace Fonts: Reading mode <code> tag font stack JetBrains Mono → Georgia → 宋体 → SimSun → monospace. Both CSS and face attribute written, modern browsers use CSS, IE 5.5 uses face attribute.
  • Refined Color Stripping: Previously, reading mode indiscriminately stripped all color attributes and color: style declarations, which also removed breadcrumb and footer gray colors. Now only strips 90s neon colors (#ff66cc, #ccffcc, #00ffff, #ffff00, khaki, etc.), while breadcrumb #999999 and footer #aaaaaa are left untouched.

Page Detail Optimizations

  • Banner Homepage Only: Blog and standalone pages remove the top 960×182 banner, content starts directly with the breadcrumb, making the reading area more compact.
  • Resources License Page: Created /resources-license, listing all fonts used on this site (SimSun, Georgia, JetBrains Mono, etc.), software (Pillow HPND, psutil BSD, Python PSF), friend link badge sources, audio statement, and full CC BY-NC-SA 4.0 license text. Footer added a link to it.
  • Language Switch Fix: English homepage archive link was hardcoded to /archive.html (Chinese archive), changed to /en/archive.html. Reading mode link rewrite rules fixed an ordering bug — the Chinese regex was executing first and incorrectly matching /en/ English paths.

Code Quality

  • Three Core Files Heavily Commented: web_server.py (~565 lines), build.ps1 (~930 lines), rebuild-all.ps1 (~230 lines) all received section-by-section comments, noting input/output, edge cases, and design decisions for each step. Makes future maintenance and refactoring easier.
  • PowerShell $? Auto-Variable Trap: "$var?param=value" causes ? to be parsed as the $? auto-variable, swallowing the rest of the string. Switched to string concatenation $var + "?param=value" to avoid this.
  • PowerShell $var.html Property Access Trap: "$originalPage.html" gets parsed as $($originalPage.html), accessing the .html property of the string, returning $null. Switched to ${originalPage}.html or direct concatenation.

Standalone Guestbook Page

  • Full Display: Created /guestbook.html standalone page, showing all messages (not limited to the sidebar's last 20). 980px centered layout, no left/right sidebars, message box style matching the homepage sidebar.
  • Emoji Panel Horizontal: Removed table layout, 20 emoji icons arranged horizontally, click to insert.
  • Simplified Navigation: Breadcrumb Home » Guestbook + [English], removed theme toggle, added return-to-homepage button at the bottom.
  • Full Reply Functionality: @username[#floor] format, emoji insertion, IP display toggle.

IE5.5 Incompatible Features List

Created standalone page /page-ie55-incompatible, honestly listing all 22 features incompatible with IE5.5 (11 CSS, 7 HTML, 4 JS), with a complete tech stack overview and layered degradation compatibility strategy.

Email Notification Fixes

  • mailer.py Path Error: data/guestbook.txtdata/runtime/guestbook.txt, fixed so find_email() correctly reads message data
  • extract_mentions Username Pollution: @DragonRSTER[#50] — the @(\S+) regex was capturing [#50] into the username, causing find_email_by_name('DragonRSTER[#50]') to fail. Fixed by auto-stripping trailing [#N] after matching
  • guestbook.py added target_email parameter directly passed to mailer, avoiding a second file lookup

Sidebar Improvements

  • Sticky Positioning: All 9 sidebars (including reading mode) added position: sticky; top: 10px, staying in view while scrolling. IE5.5 unsupported, degrades to normal scrolling
  • Back to Top Button: All 4 right sidebars added ↑ Back to Top link at the bottom, header added <a name="top"> anchor
  • Left Sidebar All Messages Entry: Right side of the emoji row added All Messages link pointing to the standalone guestbook page

English Homepage & Page Details

  • Created src/content/index/index-en.html, full English homepage translation
  • Fixed normal mode page breadcrumb "Page" missing hyperlink (pointing to /archive.html)
  • SEO metadata + Open Graph + canonical/hreflang auto-generated

—— Written on May 14, 2026



昵称
内容

« WD HC620 User Guide « Home My ThinkPad and I »