zpaper-draft

Type to search...

to open search from anywhere

l-convert

Convert a draft article to a blog-compatible MDX file. Use when: (1) User wants to publish a draft article to the blog, (2) User says 'convert to article', 'convert', 'publish article', or 'move to bl...

Convert Draft to Blog Article

Convert a doc site draft article from doc/src/content/docs/articles/ to a blog-compatible MDX file in blog/src/articles/.

Arguments

  • --auto: Automatically apply all suggested tags (including new ones) from /l-tag-attach-to-article without asking for confirmation. Still report what tags were applied.

Steps

1. Identify the draft article

Determine which draft to convert:

  • Check git diff --name-only for uncommitted changes in doc/src/content/docs/articles/
  • Check git log --oneline -10 --diff-filter=AM -- doc/src/content/docs/articles/ for recently added or modified articles
  • If the user specified an article name or path, use that
  • If multiple candidates exist or the target is unclear, ask the user which article to convert

2. Read the draft

Read the full content of the target Markdown file from doc/src/content/docs/articles/.

3. Determine the output filename

The output file goes to blog/src/articles/{slug}.mdx.

  • Strip date/time prefixes from draft filenames before using as slug. Draft files often have prefixes like 20260301-1500- — remove the YYYYMMDD-HHMM- portion. For example: 20260301-1500-claude-code-agent-teams-usageclaude-code-agent-teams-usage
  • If the remaining name is suitable as a slug (kebab-case, descriptive), use it
  • Otherwise, derive a slug from the article title (lowercase, hyphens, ASCII only, max 50 chars)
  • If a file with the same slug already exists, ask the user before overwriting

4. Build the frontmatter

The blog MDX requires this frontmatter format:

---
title: "記事のタイトル"
description: "記事の概要説明(1-2文)"
author: Takazudo
tags: []
createdAt: "YYYY-MM-DD"
---
  • title: Use the draft’s title frontmatter, or the first # heading if no frontmatter title exists. If neither exists, ask the user.
  • description: Use the draft’s description frontmatter if available. Otherwise, generate a concise summary (1-2 sentences) based on the article content.
  • author: Always Takazudo
  • tags: Leave as empty array [] for now (tags will be attached via /l-tag-attach-to-article in the next step)
  • createdAt: Today’s date in "YYYY-MM-DD" format

Do not include doc site-specific frontmatter fields (sidebar_position, sidebar_label, pagination_next, pagination_prev).

5. Convert doc site admonitions to MDX components

Convert doc site admonition blocks to the corresponding blog MDX components:

:::note<Note>

<!-- doc site input -->
:::note 注記

Content here.
Multiple lines supported.

:::
<!-- Blog MDX output -->
<Note>

Content here.
Multiple lines supported.

</Note>

:::info<Info>

<!-- doc site input -->
:::info 備考

Content here.

:::
<!-- Blog MDX output -->
<Info>

Content here.

</Info>

Custom titles

If the admonition has a custom title (other than the standard 注記/備考):

:::note カスタムタイトル

Content here.

:::
<Note title="カスタムタイトル">

Content here.

</Note>

The standard titles are:

  • :::note 注記<Note> (no title prop needed, defaults to 注記)
  • :::info 備考<Info> (no title prop needed, defaults to 備考)

Other admonition types

If :::tip, :::warning, :::danger, or :::caution are found (they shouldn’t be per writing rules, but just in case):

  • :::tip<Info>
  • :::warning<Note>
  • :::danger<Note>
  • :::caution<Note>

6. Handle other doc site-specific syntax

  • Internal doc links: Convert doc site relative links ([text](./other-doc.md)) that point to other docs to appropriate form. If the link target is another article, convert to the blog URL format (/articles/slug). If it’s a reference doc, remove the link and keep the text.
  • Import statements: Remove any doc site-specific import statements (e.g., import CategoryNav from ...)
  • doc site components: Remove doc site-specific JSX components (e.g., <CategoryNav />)
  • Image paths: See step 6a below.
  • Fix internal links in the doc draft: See step 6b below.

6a. Copy and rewrite image paths

doc site serves static files from doc/public/, so images in drafts use paths like /img/articles/.... The blog (Next.js) serves static files from blog/public/ with basePath: '/pj/zpaper', so images must be both copied and path-rewritten.

For each image reference found in the article (markdown ![alt](/img/...) or JSX <Img src="/img/..." />):

  1. Copy the image file from doc/public/{path} to blog/public/{path}
  • Example: doc/public/img/articles/20260217-foo/bar.pngblog/public/img/articles/20260217-foo/bar.png
  • Create intermediate directories as needed (mkdir -p)
  1. Rewrite the path in the MDX to include the blog basePath prefix /pj/zpaper/
  • Example: /img/articles/20260217-foo/bar.png/pj/zpaper/img/articles/20260217-foo/bar.png

This is necessary because:

  • The blog uses basePath: '/pj/zpaper' in next.config.mjs
  • Raw <img> tags (from markdown images) are NOT automatically prefixed by Next.js — only next/image gets basePath prefixing
  • The blog’s Img component renders a plain <img> tag without basePath handling

If the draft article has no images, skip this step.

The doc draft (doc/src/content/docs/articles/) and the blog output (blog/src/articles/) use different link formats for internal article references. A link that works in the blog may break the doc site build, and vice versa. This step ensures both files have correct links.

Scan all markdown links in the article and identify internal links — links pointing to other articles or pages within the site (not external URLs like https://...).

Fix the doc draft source file (edit in place):

  • Links to other articles must use doc site-relative paths: ./other-article-slug (no /articles/ prefix, no .md extension needed)
  • Blog-style absolute paths like /articles/slug will cause doc site broken link error — convert them to ./slug
  • Verify the link target actually exists in doc/src/content/docs/articles/ by checking for a matching .md file

Fix the blog output:

  • Links to other articles should use the blog URL format: /articles/slug
  • doc site-relative paths like ./other-article-slug should be converted to /articles/other-article-slug
  • Verify the link target actually exists in blog/src/articles/ by checking for a matching .mdx file

If a link target doesn’t exist in either location, warn the user and ask how to handle it (remove the link, keep as-is, or fix the path).

7. Write the output file

Write the converted MDX to blog/src/articles/{slug}.mdx.

8. Verify the output

After writing, read the output file and verify:

  • Frontmatter has all required fields (title, description, author, tags, createdAt)
  • No doc site-specific syntax remains (:::, doc site imports, doc site components)
  • Admonitions are properly converted to <Note> / <Info> components
  • The markdown structure is intact

9. Generate thumbnail and OGP image

Generate the kumiko-pattern thumbnail SVG and OGP PNG for the article:

pnpm kumiko-gen {slug}
pnpm svg-to-png {slug}

The root script already includes --color-scheme random --zoom 5, so no extra flags are needed.

This creates:

  • blog/public/thumbnails/{slug}.svg — Thumbnail displayed in article header
  • blog/public/thumbnails/{slug}.png — OGP image for social media (1200x630px)

No frontmatter changes are needed — the blog resolves thumbnails automatically by slug.

10. Tag the article

After successful conversion, run the tag attachment workflow:

  • Use /l-tag-attach-to-article to suggest and attach tags
  • Use /l-tag-update-list if new tags are needed

If --auto was passed: Automatically apply all suggested tags (including new ones) without asking for confirmation. Still report what tags were applied.

11. Report

Tell the user:

  • The output file path
  • A summary of conversions performed (number of admonitions converted, etc.)
  • Remind them to review the article and commit when ready

Available Blog MDX Components

For reference, these are the custom MDX components available in the blog:

ComponentPurposeProps
<Note>Supplementary note (注記)title?: string (default: “注記”)
<Info>Additional info/aside (備考)title?: string (default: “備考”)
<InfoBox>Highlighted info box(children only)
<Column>Bordered column block(children only)
<Outro>End-of-article section(children only)
<Youtube>YouTube embed(see component)
<Img>Image with caption(see component)
<ExImg>Extended image(see component)
<ImgsGrid>Image grid layout(see component)
<FlowRoot>Float clearing container(children only)
<LayoutDivide>Multi-column layout(children only)
<LayoutDivideItem>Column item(children only)