Skip to main content

Life Blog Writing Workflow

This document is the long-term specification for "life-record blog posts" in this repo. It also records the branch rules that the entire blog/ section must follow when publishing new articles.

The goals are clear:

  • Make it repeatable: given a set of photo links and a few supporting details, consistently produce a publishable life blog post
  • Keep life blog posts and travel pages unified on "clickable enlargeable images" while distinguishing them in layout -- they should not blur into the same album page

If you continue writing about gatherings, meetups, casual walks, exhibitions, restaurant visits, or slices of daily life, default to this document.

If you write thinking essays, tech reflections, or AI tool experiments, still use this document to check file structure, frontmatter, blog overview, archive page, and build process -- but do not force the life-record writing style on the prose itself.

Current Blog Section Structure

The current blog section is composed of the Docusaurus classic blog plugin and two custom theme pages:

  • Articles live under blog/
  • Author info comes from blog/authors.yml
  • Blog plugin config is in docusaurus.config.js under presets.classic.blog, currently with showReadingTime: true and blogSidebarCount: 'ALL' enabled
  • The /blog overview page is controlled by src/theme/BlogListPage/index.js and src/theme/BlogListPage/styles.module.css
  • The /blog/archive page is controlled by src/theme/BlogArchivePage/index.js and src/theme/BlogArchivePage/styles.module.css
  • Image lightbox capability comes from src/components/TravelGallery

When writing a new blog post, first determine the article type:

TypeDefault FileDefault TagsImage RequirementsNotes
Life record with photos.mdx[life]Must set image; use TravelGallery layout="article" in bodyGatherings, meetups, daily moments, exhibitions, walks, etc.
Pure text essay.md[essay]No image needed; no TravelGalleryReflections, reviews, or personal thoughts without body images
Tech / AI tool experiment.md or .mdx[tech]Set image only if there are images; use TravelGallery layout="article" only if lightbox is neededAI tools, agents, model experiments, engineering practice notes
Motorsport / car interest.md or .mdx[motorsport]Set image only if there are images; use TravelGallery layout="article" only if lightbox is neededF1, sim racing, performance cars, car culture
Research / academic thinking.md or .mdx[research]Set image only if there are images; use TravelGallery layout="article" only if lightbox is neededResearch paradigms, academic training, research judgment

Therefore, .mdx is not the only correct format for the entire blog section. It is the format for articles that need JSX components or image lightboxes. Pure text articles correctly continue using .md.

How the Current Pages Read Data

When publishing a new article, understand what fields each page reads:

  • The /blog overview page reads title, date, description, tags, readingTime, and the cover image
  • The /blog overview page prefers the description from frontmatter for summaries -- do not rely on auto-truncated body text
  • The /blog overview page prefers the image from frontmatter for the cover
  • If no image exists, the /blog overview page falls back to POST_VISUALS in src/theme/BlogListPage/index.js
  • POST_VISUALS is a legacy compatibility fallback, not the default publishing flow for new articles
  • The /blog/archive page groups articles by date automatically, reading title, date, readingTime, and up to two tags
  • The /blog/archive page does not need per-article configuration and does not read description or image
  • Tag pages and the blog sidebar are auto-generated by Docusaurus from tags and blog metadata

In short, for a new article to display fully in the current blog section, the most critical factors are: file in the right place, correct date, correct title, correct authors, correct tags, standalone readable description; and if the article has images, image must be set.

Blog Tag System

The blog tag system uses a "few stable categories, add new ones when needed" strategy. Tags go in the article frontmatter tags field, using lowercase English. Display names and permalinks are maintained in blog/tags.yml.

Current fixed tags:

TagDisplay NameApplies To
lifeLifeDaily life, friends, dinners, exhibitions, walks, life moments
techTechAI, agents, tool experiments, engineering practice, tech trends
motorsportMotorsportF1, sim racing, performance cars, car culture
researchResearchResearch paradigms, academic training, research judgment
essayEssayPersonal values, self-reminders, literary or life reflections

Default to 1 primary tag per article. When an article genuinely spans two stable topics, 2 tags are acceptable. For example, AI-generated F1 driver posters can use [tech, motorsport]; sim racing plus a friend gathering can use [life, motorsport].

Do not use the old tags thoughts and life-log. thoughts is too broad and mixes tech, interests, and personal reflection; life-log has been consolidated into the shorter life.

New tags are allowed, but must meet these conditions:

  • The new tag represents a content direction that will be reused long-term, not a temporary keyword for a single article.
  • Multiple articles of the same kind already exist, or there is a clear intent to keep writing.
  • Do not use specific brand names, model names, people, or event names as tags -- for example, do not add gpt-image-2, bmw, verstappen.
  • New tags use lowercase English short words or hyphenated form, e.g., design, reading, product-notes.
  • When adding a tag, update blog/tags.yml with both label and permalink.

Understanding the Current Conventions

The current default technical implementation for life-record blog posts with photos is:

  1. Article lives under blog/
  2. File format defaults to .mdx, not plain .md
  3. Images reuse the lightbox capability from src/components/TravelGallery
  4. But the layout does not use the travel page waterfall/mosaic layout; it uses article mode layout="article"
  5. In article mode, images display at their original aspect ratio -- no cropping, no forced height limits, only responsive scaling within the content width
  6. Images are still clickable to enlarge

This means:

  • Life blog posts default to "photo-and-text articles"
  • They are not "travel albums"
  • They are not "pure text essays"
  • They are not "articles with a few plain Markdown images"

A ready-made reference example is:

  • blog/2026-04-18-friends-meet-firepot-bbq.mdx

Note: some legacy articles may still depend on POST_VISUALS in the /blog overview page for cover display. New articles going forward should not treat this fallback as default. If an article has images, set image directly in frontmatter.

Default Positioning of Life Blog Posts

The core of a life blog post is not a review, a guide, or a chronological account. It is capturing a moment worth preserving.

The default writing approach:

  • Prioritize scene and atmosphere, not ratings or recommendations
  • Focus on "me and my friends / how I felt at the time," not a complete event replay
  • Lead with real details -- do not force elevation, do not write purple prose
  • Aim for publishable content -- avoid overly private or identifying information

One sentence summary:

Life blog posts default to "warm documentary + a touch of feeling + a few useful details."

Default Writing Style

Unless otherwise specified, follow these defaults:

  • Tone: warm documentary
  • Point of view: first person
  • Privacy level: restrained and general, no friend names, no identifiable personal details
  • Article length: typically 800 to 1,600 words
  • Structure: 2 to 4 sections, not too scattered
  • Goal: record one evening, one meal, one meetup, one daily experience
  • Style boundary: emotions are fine, but do not pretend to be profound; details are fine, but do not turn it into a menu dump

What to avoid by default:

  • Do not turn the article into a restaurant review
  • No "recommendation score," "per-person cost," "ambiance stars" unless you explicitly ask for them
  • Do not fabricate dialogue, reactions, or events that did not happen
  • Do not sacrifice authenticity for literary flair

When to Write a Life Blog Post

These scenarios all fit this workflow:

  • Friend gatherings, dinners, BBQ, hotpot, late-night snacks
  • A memorable little shop
  • An ordinary but comfortable evening
  • A brief walk, exhibition, shopping trip, drinks, or conversation
  • A life moment you want to mark at a particular time

If the content clearly leans more toward "travel routes, city scenery, photo-heavy," route it to docs/Travel instead -- do not mix it into life blog posts.

Source Material Format for AI

If you want to continue using this workflow going forward, provide source material using this template.

Minimum required:

- Event:
- Date:
- Location / Restaurant:
- Photo links:
- Specific dishes / items / scenes:
- Specific drinks / beverages:
- Details I want to highlight:

Recommended additions:

- Desired tone: warm documentary / lighthearted / more literary
- Privacy requirements: only mention friends / more personal feelings OK
- Include knowledge content: no / a little / a separate small section
- Need to commit to git: yes / no

The most recommended complete input template:

- Event: BBQ gathering with friends
- Date: 2026-04-18
- Location / Restaurant: Chuanxi Huoyanshan Xichang Firepot BBQ (Xindu格林城市花园店)
- Photo links:
- https://...
- https://...
- Specific dishes:
- Free-range mini pork
- Spicy tender beef
- Grilled pork belly
- Specific drinks:
- Fenjiu
- Asahi beer
- Details to highlight:
- The dipping sauce had soybean powder
- First time trying soybean powder with BBQ -- incredible
- Desired tone: warm documentary
- Privacy requirements: restrained and general
- Include knowledge content: a separate small section

Default Rules for Showing Images to AI

If the task involves "look at photos first, then write," follow these defaults:

  • Do not read original images directly; generate compressed previews first for AI to read
  • Reading originals directly easily triggers conversation context size limits; the empirical risk threshold is around 20MB
  • Default first-tier preview uses a long edge of 1280px
  • If 1280px is still too small for text, menus, packaging, or key details, step up to a long edge of 1920px
  • For batches with many images, compress and read in batches -- do not feed the entire set of originals into the conversation at once
  • Originals are reserved for publishing, manual review, and detail re-checks when needed; they are not the default input for AI reading

Image Handling Rules

1. Life Photo-and-Text Blog Posts Default to MDX

The reason is simple: plain Markdown images do not easily reuse the existing click-to-enlarge capability.

So the default is:

  • blog/*.mdx

Not:

  • blog/*.md

This only applies to life-record photo-and-text articles. Pure text essays have no JSX component needs and correctly continue using blog/*.md. Essays with images that reuse TravelGallery use blog/*.mdx.

2. Default to TravelGallery, but Must Use Article Mode

The default image syntax for life blog posts:

import TravelGallery from '@site/src/components/TravelGallery';

Then use:

<TravelGallery images={photos} layout="article" />

Two rules to remember:

  • Life blog posts default to layout="article"
  • Do not use the travel section's waterfall/mosaic layout unless you explicitly request "make this one feel like an album"

3. Images Display at Original Aspect Ratio

Default behavior for images in life blog posts:

  • No cropping
  • No fixed-height display frame
  • No travel-album-style collage alignment
  • Display at original aspect ratio
  • Responsive scaling within content width only

This is a fixed default. Do not revert to waterfall logic unless you explicitly ask.

4. With Multiple Images, Prefer "Scene Groups"

Instead of cramming all images into one big album, split them into 2 to 4 groups by scene.

For example:

  • Group 1: tabletop, charcoal fire, skewers
  • Group 2: pouring drinks, small cups, dipping sauces
  • Group 3: main dishes, fish platter, side dishes

This reads more like an article than "a big pile of images stacked together."

5. Each Image Should Have alt and caption

Recommended syntax:

export const photos = [
{
src: 'https://picture.nevergpdzy.cn/xxx.jpg',
alt: 'Friend pouring drinks',
caption: 'Fenjiu poured into small cups -- the table already had the relaxed vibe of a friends gathering.',
},
];

Default requirements:

  • alt describes what is in the image
  • caption adds a short line of emotion or context for that image
  • Do not write caption as a long paragraph

6. Articles with Images Must Set image as the Blog Overview Cover

The current /blog overview page cover reading logic is in src/theme/BlogListPage/index.js:

  • First reads image from the article frontmatter
  • If no image, reads the hand-written POST_VISUALS in the overview component
  • If neither exists, the article shows no cover on the overview page -- the right side stays empty

So for any article with images, frontmatter must include:

image: https://picture.nevergpdzy.cn/xxx.jpg

This image should be the most representative main image from the article. Prioritize the self-hosted domain https://picture.nevergpdzy.cn/...; do not use the raw OSS domain.

Note: images in TravelGallery only handle body display and click-to-enlarge; they do not automatically become the /blog overview cover. The cover must be set separately via image. Do not add new POST_VISUALS entries for new articles unless maintaining legacy compatibility.

Default Article Structure Template

Unless otherwise specified, life blog posts default to this structure.

1. Opening: Set the Scene

The first paragraph handles:

  • Time
  • Place
  • What kind of gathering this was
  • Why it is worth recording

This paragraph typically goes before <!--truncate--> for the blog list summary display.

However, the blog overview card summary reads from the frontmatter description -- do not rely on auto-truncation of the opening paragraph. The opening can naturally lead into the scene; the summary is a standalone one-liner.

2. Middle: Describe the Scene and Food, but Not as an Order List

The middle section's focus is not listing every dish, but:

  • Capturing the most vivid scene
  • Capturing the most memorable way of eating
  • Capturing one or two details truly worth writing about

For example:

  • Charcoal slowly charring the meat to a smoky aroma
  • Someone tending the fire, someone flipping skewers, someone pouring drinks
  • A first attempt at something new that turned out surprisingly good

If you have already provided a full dish list, weave it naturally into the body -- do not list it stiffly.

3. Knowledge Content: Optional, but Must Be Restrained

If you explicitly ask to "add a bit of knowledge," the default approach:

  • Open a short standalone section
  • Only write knowledge directly related to this meal
  • Do not let it overshadow the life record itself

The most common examples:

  • The style, background, or flavor profile of a particular drink
  • A small fact about an ingredient or eating method

By default, do not produce:

  • Baidu Baike-style long explainer
  • Large blocks of brand history
  • A pile of sources crammed into the body

The knowledge section's role is "add a layer of depth," not steal the spotlight from the life record.

4. Ending: Return to "Why This Was Worth Recording"

The ending defaults to circling back to the people and the moment.

Good landing points:

  • What truly stayed with you from that evening
  • What made it memorable was not just the food, but the atmosphere
  • Why an ordinary night is still worth recording

By default, do not force a lesson at the ending, and do not turn it into motivational filler.

Content Judgment Priority

When generating articles, use information in this fixed order:

  1. Information you explicitly stated verbally
  2. Written supplementary details you provided
  3. Content that can be reliably identified from photos
  4. Careful inference last

In other words:

  • If you explicitly say the drink is Fenjiu, write it as Fenjiu
  • If the photo is unclear but you verbally confirm it, use your confirmation
  • If the photo only vaguely shows "looks like some kind of skewer" and you have not confirmed, do not commit to a specific description

File Naming and Frontmatter Convention

Default naming:

  • blog/YYYY-MM-DD-short-slug.mdx

For example:

  • blog/2026-04-18-friends-meet-firepot-bbq.mdx

The date must be based on the event date or source material date, not the current date at time of writing.

If you use a relative date, convert it to an absolute date first. For example, if the current date is 2026-04-26 and you say "the poster generated yesterday," the filename should use 2026-04-25-...mdx, and the body date should also say "April 25."

Default frontmatter:

---
title: A Short Title
authors: DingZhiyu
tags: [life]
description: A standalone summary of what this record actually covers.
image: https://picture.nevergpdzy.cn/xxx.jpg
---

Default rules:

  • title is a short Chinese title, not too long
  • authors is fixed as DingZhiyu
  • tags are selected from the current blog tag system based on the article topic; life records default to [life]
  • description must be written standalone -- do not copy the opening paragraph, do not write generic openers like "here is what happened"
  • description should be 35 to 70 Chinese characters, describing the scene, the memorable detail, and the article's value, for the /blog overview card display
  • If the article has images, image must be set for the /blog overview page and latest article card cover
  • image defaults to the most representative main image from the first image group in the article, and must use the self-hosted domain

If the content clearly leans toward personal reflection rather than life recording, switch to [essay]. If the main thread is AI, agents, tool experiments, or engineering practice, use [tech]. If the main thread is motorsport or car interests, use [motorsport].

Pure text essays can use this lighter frontmatter without image:

---
title: A Short Title
authors: DingZhiyu
tags: [essay]
description: A standalone summary of what this essay actually discusses.
---

If an essay has images and needs them to appear as the /blog overview cover, still use .mdx and add image.

Fixed Technical Template for Life Blog Posts

Going forward, default to this skeleton:

---
title: Title
authors: DingZhiyu
tags: [life]
description: A standalone summary of what this record actually covers.
image: https://picture.nevergpdzy.cn/xxx.jpg
---

import TravelGallery from '@site/src/components/TravelGallery';

export const scenePhotos = [
{
src: 'https://picture.nevergpdzy.cn/xxx.jpg',
alt: 'Image description',
caption: 'A short caption.',
},
];

# Title

Opening paragraph.

<!--truncate-->

Second paragraph.

<TravelGallery images={scenePhotos} layout="article" />

Subsequent body text.

If an article has multiple image groups, continue defining:

  • foodPhotos
  • drinkPhotos
  • detailPhotos

as variable names.

Blog Audio (TTS)

Every blog post has a TTS narration audio file. Audio is generated by the standalone project tts-blog-generator (located in the parent directory D:\Code\tts-blog-generator\) and hosted on Alibaba Cloud OSS.

Audio Player Component

src/components/BlogAudioPlayer/ is the audio player component, embedded directly in blog MDX/MD files:

import BlogAudioPlayer from '@site/src/components/BlogAudioPlayer';

<BlogAudioPlayer slug="your-post-slug" />

Slug rule: filename without date prefix and extension.

FilenameSlug
2026-04-27-bmw-m3-touring-24h-note.mdxbmw-m3-touring-24h-note
2024-6-9-The_method_of_spiritual_victory.mdThe_method_of_spiritual_victory

Player features:

  • Auto-detects current locale (zh-Hans / en) and loads the corresponding language audio
  • Play/pause, progress bar with drag-to-seek, speed control (0.75x / 1.0x / 1.25x / 1.5x / 2.0x)
  • Multi-chunk audio preloading with seamless transitions
  • Returns null when no audio is available, rendering no DOM

Generate Chinese Audio

cd ../tts-blog-generator
python generate.py

Generate English Audio

python generate.py --lang en --blog-dir "../Dev-Knowledge-Base/i18n/en/docusaurus-plugin-content-blog"

Copy the Manifest File to the Project

After generation, copy the manifest file to the project:

cp output/manifest.json ../Dev-Knowledge-Base/src/data/blogAudioManifest.json
cp output/manifest.json ../Dev-Knowledge-Base/static/audio/blog/manifest.json

The manifest file blogAudioManifest.json is the data source for the player component, imported via static import. Chinese entries use the slug as key (e.g., agent-harness); English entries use en/ + slug (e.g., en/agent-harness).

Audio Generation Technical Details

  • TTS API uses MiMo-V2.5-TTS; Chinese voice is "Molly" (茉莉), English voice is "Chloe"
  • API has a ~3500 character limit per request; long articles are automatically split at paragraph boundaries
  • Generated WAV files are converted to MP3 via ffmpeg
  • MP3 files are uploaded to OSS under Audio/blog/ (Chinese) and Audio/blog/en/ (English)
  • The generator is idempotent: existing MP3 files are skipped; use --force to force regeneration

OSS Anti-Hotlinking Configuration

Audio is hosted on picture.nevergpdzy.cn. The following domains must be whitelisted in the Alibaba Cloud OSS anti-hotlinking settings:

  • https://nevergpdzy.cn (production)
  • http://localhost:3000 (local development)

Also enable "Allow empty Referer."

Pre-Publish Checklist

After every article, check at least:

  • File is under blog/
  • Article type is correctly identified: life-record photo-and-text, pure text essay, essay with images -- do not mix rules
  • If life-record photo-and-text or essay with images, is it using .mdx?
  • If pure text essay, is .md confirmed sufficient without forcing image components?
  • If the body needs a lightbox, is TravelGallery correctly imported?
  • If using TravelGallery, is layout="article" explicitly used?
  • Does a photo article have frontmatter image?
  • Is image using the self-hosted domain and suitable as the /blog overview cover?
  • Are images displayed at original aspect ratio?
  • Are images clickable to enlarge?
  • Are title, authors, tags correct?
  • If a new tag was added, was blog/tags.yml updated?
  • Is description a standalone summary, not the first body sentence or a generic opener?
  • Is <!--truncate--> placed correctly?
  • Does the /blog overview page correctly show title, summary, tags, date, reading time, and cover or text-only card?
  • Does /blog/archive correctly list articles by year and date?
  • Do tag pages auto-collect articles via tags?
  • Do the filename date and body date match the event date you provided?
  • If you said "yesterday" or "the day before," was it converted to an absolute date based on the current date?
  • Are there any fabricated dish names, drink names, restaurant names, or dates?
  • Is any private information exposed that should not be?
  • If you requested knowledge content, is it within a reasonable length?
  • Has Chinese audio been generated via python generate.py?
  • Has English audio been generated via python generate.py --lang en ...?
  • Has the manifest file blogAudioManifest.json been copied to both src/data/ and static/audio/blog/?
  • Has BlogAudioPlayer been added to both the Chinese and English blog posts?
  • Is the slug correct (filename without date prefix and extension)?
  • Was npm run build executed at the end?

If You Request Version Bump and Git Commit

If the task includes not just writing the article but also a version bump and code commit, follow this:

  1. Read VERSIONING.md first
  2. Determine whether to bump patch, minor, or major
  3. Run the corresponding version command
  4. Confirm both package.json and package-lock.json are updated
  5. Run npm run build
  6. Then commit to git

Default understanding:

  • Fixing a typo only: PATCH
  • Adding a life blog post or a non-breaking visible feature: MINOR
  • Breaking routes or major structural changes: MAJOR

Default Conclusions for Future Tasks of This Type

If you give me a set of life photos and a few supporting details without many extra requirements, default to this:

  • Output to blog/
  • First determine article type: life-record photo-and-text defaults to .mdx, pure text essay can use .md, essay with images uses .mdx
  • Use TravelGallery only when a lightbox is needed
  • Force layout="article" when using TravelGallery
  • Images display at original aspect ratio
  • Write a standalone description in frontmatter for the blog overview card
  • Photo articles must set image in frontmatter for the /blog overview cover
  • image and body images default to the self-hosted domain https://picture.nevergpdzy.cn/...
  • Filename date uses the event or source material date, not the writing date; relative dates must be converted to absolute dates first
  • If image reading is needed, read compressed previews first; default long edge 1280px, step up to 1920px if needed; do not read originals directly
  • Article tone: warm documentary
  • Privacy style: restrained and general
  • Structure: opening scene -> middle details -> optional knowledge section -> ending closure
  • If there is a striking detail, make it the article's defining moment
  • If you ask to "add a bit of knowledge," open a short standalone section that does not overshadow the main content
  • Run npm run build at the end

The purpose of this document is not to rigidly define every article, but to make the default decisions clear. Most life blog posts going forward can follow this specification directly without re-establishing the process from scratch each time.