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.jsunderpresets.classic.blog, currently withshowReadingTime: trueandblogSidebarCount: 'ALL'enabled - The
/blogoverview page is controlled bysrc/theme/BlogListPage/index.jsandsrc/theme/BlogListPage/styles.module.css - The
/blog/archivepage is controlled bysrc/theme/BlogArchivePage/index.jsandsrc/theme/BlogArchivePage/styles.module.css - Image lightbox capability comes from
src/components/TravelGallery
When writing a new blog post, first determine the article type:
| Type | Default File | Default Tags | Image Requirements | Notes |
|---|---|---|---|---|
| Life record with photos | .mdx | [life] | Must set image; use TravelGallery layout="article" in body | Gatherings, meetups, daily moments, exhibitions, walks, etc. |
| Pure text essay | .md | [essay] | No image needed; no TravelGallery | Reflections, 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 needed | AI 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 needed | F1, 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 needed | Research 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
/blogoverview page readstitle,date,description,tags,readingTime, and the cover image - The
/blogoverview page prefers thedescriptionfrom frontmatter for summaries -- do not rely on auto-truncated body text - The
/blogoverview page prefers theimagefrom frontmatter for the cover - If no
imageexists, the/blogoverview page falls back toPOST_VISUALSinsrc/theme/BlogListPage/index.js POST_VISUALSis a legacy compatibility fallback, not the default publishing flow for new articles- The
/blog/archivepage groups articles by date automatically, readingtitle,date,readingTime, and up to twotags - The
/blog/archivepage does not need per-article configuration and does not readdescriptionorimage - Tag pages and the blog sidebar are auto-generated by Docusaurus from
tagsand 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:
| Tag | Display Name | Applies To |
|---|---|---|
life | Life | Daily life, friends, dinners, exhibitions, walks, life moments |
tech | Tech | AI, agents, tool experiments, engineering practice, tech trends |
motorsport | Motorsport | F1, sim racing, performance cars, car culture |
research | Research | Research paradigms, academic training, research judgment |
essay | Essay | Personal 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.ymlwith bothlabelandpermalink.
Understanding the Current Conventions
The current default technical implementation for life-record blog posts with photos is:
- Article lives under
blog/ - File format defaults to
.mdx, not plain.md - Images reuse the lightbox capability from
src/components/TravelGallery - But the layout does not use the travel page waterfall/mosaic layout; it uses article mode
layout="article" - In article mode, images display at their original aspect ratio -- no cropping, no forced height limits, only responsive scaling within the content width
- 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
1280pxis still too small for text, menus, packaging, or key details, step up to a long edge of1920px - 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:
altdescribes what is in the imagecaptionadds a short line of emotion or context for that image- Do not write
captionas 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
imagefrom the article frontmatter - If no
image, reads the hand-writtenPOST_VISUALSin 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:
- Information you explicitly stated verbally
- Written supplementary details you provided
- Content that can be reliably identified from photos
- 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:
titleis a short Chinese title, not too longauthorsis fixed asDingZhiyutagsare selected from the current blog tag system based on the article topic; life records default to[life]descriptionmust be written standalone -- do not copy the opening paragraph, do not write generic openers like "here is what happened"descriptionshould be 35 to 70 Chinese characters, describing the scene, the memorable detail, and the article's value, for the/blogoverview card display- If the article has images,
imagemust be set for the/blogoverview page and latest article card cover imagedefaults 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:
foodPhotosdrinkPhotosdetailPhotos
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.
| Filename | Slug |
|---|---|
2026-04-27-bmw-m3-touring-24h-note.mdx | bmw-m3-touring-24h-note |
2024-6-9-The_method_of_spiritual_victory.md | The_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
nullwhen 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) andAudio/blog/en/(English) - The generator is idempotent: existing MP3 files are skipped; use
--forceto 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
.mdconfirmed sufficient without forcing image components? - If the body needs a lightbox, is
TravelGallerycorrectly imported? - If using
TravelGallery, islayout="article"explicitly used? - Does a photo article have frontmatter
image? - Is
imageusing the self-hosted domain and suitable as the/blogoverview cover? - Are images displayed at original aspect ratio?
- Are images clickable to enlarge?
- Are
title,authors,tagscorrect? - If a new tag was added, was
blog/tags.ymlupdated? - Is
descriptiona standalone summary, not the first body sentence or a generic opener? - Is
<!--truncate-->placed correctly? - Does the
/blogoverview page correctly show title, summary, tags, date, reading time, and cover or text-only card? - Does
/blog/archivecorrectly 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.jsonbeen copied to bothsrc/data/andstatic/audio/blog/? - Has
BlogAudioPlayerbeen added to both the Chinese and English blog posts? - Is the slug correct (filename without date prefix and extension)?
- Was
npm run buildexecuted 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:
- Read
VERSIONING.mdfirst - Determine whether to bump
patch,minor, ormajor - Run the corresponding version command
- Confirm both
package.jsonandpackage-lock.jsonare updated - Run
npm run build - 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
TravelGalleryonly when a lightbox is needed - Force
layout="article"when usingTravelGallery - Images display at original aspect ratio
- Write a standalone
descriptionin frontmatter for the blog overview card - Photo articles must set
imagein frontmatter for the/blogoverview cover imageand body images default to the self-hosted domainhttps://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 to1920pxif 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 buildat 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.