Takazudo Modular Docs

Type to search...

to open search from anywhere

/CLAUDE.md

CLAUDE.md at /CLAUDE.md

Path: CLAUDE.md

CLAUDE.md

Astro 6 website for Takazudo Modular, a Japanese modular synthesizer shop. Content managed via MDX files and JS data modules. Deployed at https://takazudomodular.com/. Preview URLs use *.takazudomodular.pages.dev (Cloudflare Pages, post-Wave-8 cutover). During dual-deploy, Netlify preview URLs *--takazudomodular.netlify.app remain active.

Local Setup

After cloning, copy .env.example to .env, fill in values, then run:

pnpm setup-local

This creates local symlinks (imgs/ → Dropbox image storage, photos/ → Dropbox photo storage for the uploader pipeline, image-stash/ → capture repo) based on paths in .env. The imgs/ symlink is required for the image pipeline (pnpm convimgs:upload). The photos/ symlink is optional — it’s only useful for locally inspecting user-uploaded originals; the Photo Uploader app itself writes directly to R2. Build and dev server work without either symlink (production images are served from R2 CDN).

Generated files: src/data/generated/ contains capture-manifests.json and transcript-manifests.json (gitignored). These are auto-generated by pnpm run build:capture-manifest, which runs as part of _prepare (called by pnpm dev). If TypeScript complains about missing modules in src/data/generated/, run pnpm run build:capture-manifest to generate them.

Rules & Restrictions

  • rm -rf: NEVER use absolute paths. Always rm -rf ./relative/path
  • Git: No force push. No git commit --amend (unless user explicitly permits). No reusing branch names. Default merge strategy: regular merge (not squash) unless user requests squash
  • console.log: Prohibited in source code (CI fails). Use console.error for error handling. CI excludes test files and commented lines
  • Temp files: Always save to __inbox/ (gitignored). Never save temp files to repo root
  • Stdout warnings: Do NOT ignore warnings printed to stdout/stderr when running commands (pnpm dev, pnpm build, etc.). Read the output carefully. If you see warnings, either investigate and fix them, or ask the user about them. This is especially important for dev server startup messages, build warnings, and health checks

Git Worktree

There are two scenarios when working with worktrees:

Scenario A: Session launched from repo root, referencing worktrees

When the session starts at the repo root and you cd into /worktrees/ to read files for reference:

  • NEVER do git operations (commit, push, checkout) inside /worktrees/ directories. You may have cd’d there to read code, but git ops would affect that worktree’s branch, not the branch you’re working on in the main repo.
  • Worktrees are read-only references in this scenario. Read files, then navigate back to repo root before any git operations.
  • If your current path contains /worktrees/, navigate to repo root before any git operations.

Scenario B: Session launched from a worktree directory

When the user launches the CLI session directly from a /worktrees/<name>/ directory, that worktree IS your working directory. You should:

  • Make file changes, run commands, and develop normally in that worktree
  • Git operations (commit, branch, etc.) are fine here — the user intentionally launched from this worktree to work on its branch

General worktree rules

  • Setup: pnpm run init-worktree <name> (creates worktree + symlinks .env files)
  • Always pull the base branch before creating a worktree to include all merged changes
  • Working directory is where user launched CLI, not where previous sessions worked

WIP Article Base Branch

base/wip-articles is a long-lived accumulation branch for YouTube guide article drafts — not a feature branch. AI can draft articles fast, but humans finalize slowly, so drafts accumulate here before being shipped one episode at a time.

Workflow

  1. Draft — New article MDX files are ported into base/wip-articles (or a theme-scoped variant; see below).

  2. Finalize — When an episode is ready to publish, run:

    /l-youtube-guide-writer --finalize <mdx-path>

    See .claude/skills/l-youtube-guide-writer/SKILL.md for full usage.

  3. Ship — Each finalized episode gets its own small PR targeting main directly. Never merge base/wip-articlesmain wholesale.

Branch naming

BranchPurpose
base/wip-articlesDefault accumulation branch for all guide article drafts
base/wip-articles-<theme>Theme-scoped epic work (e.g., base/wip-articles-docs for documentation work)

Theme branches follow the same workflow: drafts accumulate there, finalized episodes get individual PRs to main.

Placement rules (series & base branch)

When starting a new guide draft, decide which series it belongs to and which base branch it sits on using this precedence — check from top to bottom, stop at the first match.

#RuleSeriesBase branch
1Existing series continuation — a guide series already exists for the target product (on main or any WIP branch)Add as next -ep{N+1} of that series. Update src/data/guide-series.mjsContinue the active themed sub-base if one exists, otherwise base/wip-articles directly
2User-specified series — user names a series in the invocation (e.g., --series=<slug>)Add to the named seriesSame rule as 1
3Tiny-module single article — one-off short video (e.g., 2-minute brand overview of a small utility), no existing seriesSingle article, no sub-series. File: {product}-guide.mdx (standalone, no ep-number)base/wip-articles directly (no themed sub-base)
4Multi-article epic / porting — bulk work such as ~5+ episodes, modernizing stale drafts, or porting from an old branchNew series per product as usualNew themed sub-base base/wip-articles-<theme> (epic PR targets base/wip-articles)

Rule of thumb: do not invent a new themed sub-base for a single tiny article. Sub-bases are for epic-scale work. A single short-video draft ships fastest when it lives directly on base/wip-articles and finalizes straight to main.

Canonical reference

The OXI Coral series EP.1–5 on main represents the canonical finalized shape for guide articles. Use it as a reference when checking formatting, frontmatter, and structure.

See also: doc/src/content/docs/writing/guide-article-writing.md for the full writing guide.

Port Mapping

Port/DomainServiceStart Command
zmod.localhost
Next.js Devpnpm dev
zmod.localhost
Next.js + API (local)pnpm dev:full
34434Next.js Prodpnpm serve
9999Netlify Functionspnpm functions:serve
localhost
zudo-doc Docscd doc && pnpm dev
localhost
zmdpreview (Mercari preview)cd sub-packages/zmdpreview && pnpm dev
localhost
photo-uploader (mobile photo upload)pnpm photo-uploader:dev
localhost
photo-uploader-worker (Cloudflare Worker, preview env)pnpm worker:dev
localhost
zmod-preorder-worker (Cloudflare Worker, preview env)pnpm --filter zmod-preorder-worker dev
localhost
image-stash-viewercd sub-packages/image-stash-viewer && pnpm tauri:dev
localhost
photo-manager (Tauri admin for Photo Uploader dataset)pnpm photo-manager:dev
localhost
wip-assets-viewercd sub-packages/wip-assets-viewer && pnpm tauri:dev

Sub-package ports: see each sub-package’s CLAUDE.md.

Preview Deploy URLs

Post-Wave-8 (Cloudflare Pages authoritative):

BranchURL
previewhttps://preview.takazudomodular.pages.dev
expreview/*https://expreview-{name}.takazudomodular.pages.dev

Doc site uses a custom subdomain (CNAME required, user-action A17 in cutover checklist):

SiteURL
Dochttps://doc.takazudomodular.com
Styleguidehttps://zmod-styleguide.pages.dev (internal tool, no custom domain)

During dual-deploy (pre-cutover), Netlify preview URLs remain active:

BranchURL
previewhttps://preview--takazudomodular.netlify.app
expreview/*https://expreview-{name}--takazudomodular.netlify.app

Branch name length rule: Cloudflare Pages limits subdomain to 63 chars. With project name takazudomodular (15 chars) + . (1) = 16, branch slug must be ≤ 47 chars. For expreview/ branches: the prefix uses 10 chars (as expreview-), leaving up to 37 chars for the topic name.

URL to File Mapping

  • zmod.localhost:34434/notes/[slug] -> /src/mdx/notes/, /src/mdx/highlights/
  • zmod.localhost:34434/s/[slug] -> /src/mdx/s/ (standalone pages: about, discord)
  • zmod.localhost:34434/products -> /src/data/products.mjs
  • localhost:32342/docs/ -> /doc/src/content/docs/

Port 34434 is auto-killed before pnpm dev. For others: lsof -ti:[PORT] | xargs kill -9.

Image Pipeline

  • /imgs/ is a symlink to Dropbox (/Users/takazudo/Dropbox/takazudoModular/_web-img-storage), excluded from git
  • Processed images output to /static/images/p/[slug]/ (symlinked to /public/images/p/)
  • Formats: WebP (600w-2000w), mercari.png, metadata.json, and conditionally ogp.jpg (see OGP convention below)
  • Production images served from Cloudflare R2 via Netlify proxy redirects
  • NEVER put files directly into static/images/p/. Always use the pipeline: original → /imgs/convimgsr2:upload
  • Use pnpm convimgs:upload to process and upload in one step
  • Run pnpm build:metadata after pnpm convimgs or when creating new worktrees. Required before pnpm dev if images changed
  • Build does NOT require /imgs/ or local images — production uses R2 CDN URLs
  • Fresh clone: run pnpm r2:download to get processed images for local dev
  • Image health warnings during pnpm dev: The check:images step (runs automatically before dev server) warns when MDX frontmatter references image slugs not in metadata-db.json, or when source images in /imgs/ haven’t been processed. Look for WARNING: N slug(s) not found in metadata-db.json or WARNING: N source image(s) not processed in stdout. If you see these, ask the user whether to run pnpm r2:download or pnpm convimgs:upload. Do not push while these warnings are present — the same “slug not in metadata-db.json” condition surfaces in production as a black-square blurhash placeholder on home-page cards and an empty <ImgsGrid> on the detail page (this is exactly how the zudo-3u-to-1u regression manifested on 2026-05-02). Treat the warning as a release blocker, not a “fix later” note
  • Run pnpm r2:check-orphans to detect R2 images without originals in /imgs/
  • metadata-db.json is tracked in git (one-record-per-line, sorted by slug for merge-friendly diffs). Regenerate with pnpm build:metadata after image changes. Do not reformat this file
  • pnpm build:metadata is non-destructive by default. The committed metadata-db.json is loaded as a baseline, the local static/images/p/ tree is scanned, and the output is the union — locally-scanned slugs override the baseline, baseline-only slugs are preserved. This is what makes pnpm convimgs:upload && pnpm build:metadata safe to run inside a worktree (where static/images/p/ is empty by default) without nuking ~1900 production entries. Pass --prune (alias: --strict) only when you really do want the legacy “scan-only” behavior — i.e. you’ve truly retired slugs and your local static/images/p/ is intentionally complete (run pnpm r2:download first)
  • metadata-db.json regeneration diffs — additions look noisy, commit them anyway. Deletions of unrelated slugs — never commit.
    • Additions / refreshes: a single new product can produce a 100+ line +/- diff because (a) the new slug entries land mid-file in sort order, shifting nothing visually but registering as inserts, and (b) any other entries whose blurhashDataUri was deferred since the last regeneration also refresh in the same pass. The result looks like “many unrelated changes” and triggers an instinct to drop it from the commit. Do not. Ship the file as-is via /l-metadata-update.
    • Deletions of slugs unrelated to your current change: NEVER commit these. They mean your local static/images/p/ was sparse and an old (pre-merge-mode) pnpm build:metadata dropped baseline entries. With the merge-mode default this should not happen at all — if you see it, you almost certainly ran pnpm build:metadata --prune by mistake. Restore the file with git checkout metadata-db.json and re-run without --prune. Slugs missing from metadata-db.json ship to production as black-square blurhash placeholders / empty <ImgsGrid>s on real product pages (the zudo-3u-to-1u regression of 2026-05-02 hit exactly this path).

OGP Image Convention (__og / __ogonly suffixes)

The image pipeline uses __ (double underscore) as a delimiter for conversion rules. Three file types control OGP generation:

Source fileSlugGenerates
foobar.heicfoobarWebP + mercari + metadata (NO ogp.jpg)
foobar__og.heicfoobar__ogWebP + mercari + metadata + ogp.jpg
foobar__ogonly.heicfoobar__ogonlyogp.jpg ONLY
  • No suffix — regular image, OGP skipped for faster processing
  • __og — full processing plus OGP. Use for product thumbnails that also serve as social sharing images
  • __ogonly — OGP only, no WebP or mercari variants. Use for dedicated OGP images (e.g., brand OGP for site-wide default)
  • No slug stripping — the slug IS the filename without extension (e.g., foobar__og.heic → slug foobar__og)
  • The --force-ogp CLI flag can force OGP generation on regular files

When no ogp.jpg exists for a slug, no og:image meta tag is emitted.

MDX Frontmatter Image Fields

FieldPurposeNotes
imgThumbDisplay image slug (hero, thumbnails, cards)Preferred field name
heroImgUrlLegacy alias for imgThumbStill works, imgThumb takes precedence
imgOgpOptional OGP override slugWhen set, OGP uses this instead of imgThumb

When imgOgp is absent, OGP falls back to imgThumb/heroImgUrl (existing behavior). Use imgOgp when a page needs a different image for social sharing vs. display (e.g., standalone pages with __ogonly images).

Merge driver

metadata-db.json has a custom git merge driver (metadata-db-jsonl) that treats the file as a key-value map keyed by slug rather than a sequence of lines. Disjoint slug additions on two branches auto-merge without conflict.

Setup is automatic: pnpm setup-local registers the driver in the local git config. The .gitattributes line metadata-db.json merge=metadata-db-jsonl activates it for this file.

Limitation: GitHub’s server-side merge does NOT run client merge drivers. If the GitHub UI reports a conflict on metadata-db.json, resolve it by pulling locally (the driver re-merges cleanly), then pushing — GitHub will then merge trivially.

Fallback: a contributor who skipped pnpm setup-local falls back to git’s default text merger — the same manual-conflict experience as before the driver was introduced. No regression.

Photo Pipeline

Parallel to the product-image pipeline above, the Photo Uploader app (epic #1473, super-epic #1470) manages user-uploaded photos with their own storage layout and metadata DB. Keep the two pipelines conceptually and physically separate — they share only the R2 bucket.

R2 layout

The zmodmedia bucket carries three top-level prefixes:

PrefixPurpose
images/p/{slug}/...Product images (existing pipeline)
photos/originals/{YYYY}/{MM}/{slug}.{ext}User-uploaded originals (HEIC/JPEG/PNG), date-sharded
photos/variants/{slug}/{400w,800w,1600w}.webpGenerated multi-res WebP variants

Photos do not get mercari.png or ogp.jpg. Three widths only.

Canonical store: Cloudflare D1, owned by the Worker (epic #1592 cutover)

Photo metadata is canonical in Cloudflare D1 (table photos, schema in schema/photos.sql). Since the Workers cutover (epic #1592), the Cloudflare Worker at sub-packages/photo-uploader-worker/ owns all D1 access via its native D1 binding — there is no REST client and no Cloudflare API token involved at runtime. The Worker’s commit handler upserts into D1 directly, and the build pipeline reads from D1 indirectly by hitting the Worker’s GET /photos.json endpoint. The build script then regenerates photo-metadata-db.json from those rows ∪ filesystem variant scan. The historical photo-metadata-db.json file at the repo root is a build artifact — kept in git as a snapshot, a seed source for scripts/seed-d1-photos.mjs, and as the local-dev fallback target when no BUILD_AUTH_TOKEN is configured. Regenerate it with pnpm photos:build.

After the cutover, Netlify holds zero Cloudflare credentials — the deleted CLOUDFLARE_* env vars are no longer needed by any build- or runtime-path on the Netlify side. All Cloudflare auth lives either as Worker runtime secrets (wrangler secret put) or as GitHub Actions deploy-time secrets.

pnpm build runs pnpm photos:build between build:metadata and build:search-index, so every Netlify build picks up the latest captions / hashtags from D1 (via the Worker). The build script is defensive: a Worker outage logs the error and falls back to the filesystem snapshot rather than failing the build. Build logs print one of:

  • photos:build: reading from Cloudflare Worker /photos.json — Worker was reachable.
  • photos:build: filesystem fallback (no BUILD_AUTH_TOKEN) — local dev / no token.
  • photos:build: filesystem fallback (Worker unreachable) — Worker attempted, failed, fell back.

Operator setup, the secrets runbook (Worker runtime secrets via wrangler secret put + GitHub Actions deploy secrets), the D1 binding configuration, schema evolution policy, and the rollback procedure all live in sub-packages/photo-uploader-worker/CLAUDE.md. The Netlify side (netlify/functions/) is a thin proxy layer now — netlify/functions/photo-uploader-{login,sign,commit}.ts are tiny shim Functions that forward to the Worker. Client URLs stay stable. (The shim Functions exist instead of [[redirects]] because Netlify silently drops rewrite rules whose from starts with /.netlify/ — that prefix is reserved for the Functions / Edge Functions / Blobs platform.)

photo-metadata-db.json (build artifact / local-dev fallback)

Tracked in git at the repo root, one JSON object per line, sorted by slug ascending — same merge-friendly convention as metadata-db.json, but wrapped in [] (array) instead of {} (object-keyed-by-slug) because each record carries its own slug field.

Per-record shape:

{
  "slug": "20250101-120000-abc12345",
  "takenAt": "2025-01-01T12:00:00Z",
  "uploadedAt": "2025-01-15T09:30:00Z",
  "variants": {
    "400w": "/photos/variants/20250101-120000-abc12345/400w.webp",
    "800w": "/photos/variants/20250101-120000-abc12345/800w.webp",
    "1600w": "/photos/variants/20250101-120000-abc12345/1600w.webp"
  },
  "dimensions": { "w": 4000, "h": 3000 },
  "aspectRatio": 1.3333,
  "orientation": "landscape"
}
  • Slug format: YYYYMMDD-HHMMSS-{8-char hex} (derived from takenAt + random suffix)
  • orientation = landscape | portrait | square, derived from dimensions
  • aspectRatio = w / h, rounded to 4 decimals
  • takenAt / uploadedAt are ISO 8601 UTC (Z suffix)

Consumers import via lib/utils/photo-url.ts — it reads the virtual module virtual:photo-metadata-db under Astro/Vite and falls back to a direct fs.readFileSync in plain Node contexts (for scripts/).

Phase 1 status

Epic #1473 is multi-phase. Phase 1 (this commit) freezes the contract with a fixture photo-metadata-db.json and placeholder variant WebPs in static/photos/variants/ so Epic D can render against stable data. The uploader UI, HEIC conversion, R2 upload scripts, and auth all land in Phase 2.

Commands Reference

# Core
pnpm dev                    # Next.js dev server only (no API)
pnpm build                  # Production build (~3-4 min)
pnpm serve                  # Serve production build
pnpm clean                  # Clean Next.js cache

# Dev with API (3 environments)
pnpm dev:full               # Next.js + local functions (file-based blobs)
pnpm dev:full:preview       # Next.js + preview remote API
pnpm dev:full:prod          # Next.js + production remote API

# Code quality
pnpm check                  # Run all checks (typecheck + lint + format)
pnpm check:fix              # Fix all auto-fixable issues
pnpm typecheck              # TypeScript only
pnpm lint / pnpm lint:fix   # ESLint only
pnpm format / pnpm format:fix  # Prettier only

# Testing
pnpm test                   # Unit + critical e2e
pnpm test:all               # All tests including full e2e
pnpm test:unit              # Unit tests only

# Data & images
pnpm build:metadata         # Build image metadata DB (run after convimgs)
pnpm convimgs:upload        # Process images + auto-upload to R2
pnpm r2:upload              # Upload all images to R2 (incremental)
pnpm r2:download            # Download all images from R2
pnpm r2:check-orphans       # Detect orphaned images on R2

# Photos (epic #1473 — Photo Uploader)
pnpm photos:download        # Download user-uploaded originals from R2
pnpm photos:build           # Generate WebP variants + refresh photo-metadata-db.json
pnpm photos:upload-variants # Upload photo variants to R2 (incremental)
pnpm photos:check-orphans   # Report R2 photo objects with no DB entry

Pre-Push Checklist

Always run pnpm check before pushing. Fix issues with pnpm check:fix. For significant changes, also run pnpm test. For comprehensive pre-merge verification: ./scripts/pre-merge-check.sh.

Key Files

  • src/data/product-master-data.mjsMASTER DATA: product catalog (specs, images, metadata)
  • src/data/products.mjs — Processed products with utility functions (imports from master data)
  • src/data/brands.mjs — Brand information and SVG mappings
  • lib/data/taxonomy.ts — Tag and category label mappings
  • astro.config.ts — Astro configuration
  • src/astro/ — Astro pages, layouts, and components
  • sub-packages/design-system/ — Tailwind design tokens and theme

Commit Messages

Start with a scope prefix in brackets, then a short description:

  • [web] - main website (src/astro/, components/, lib/, src/, styles, config)
  • [content] - MDX content and translations (src/mdx/)
  • [data] - data files (src/data/, product-master-data, brands, guide-series)
  • [doc] - documentation site (doc/)
  • [zpreorder] - pre-order sub-package (sub-packages/zpreorder/)
  • [photo-uploader] - photo uploader sub-package (sub-packages/photo-uploader/) + its Netlify functions (netlify/functions/photo-uploader-*)
  • [products-viewer] - products viewer (sub-packages/products-viewer/)
  • [mercari-viewer] - Mercari viewer (sub-packages/mercari-viewer/)
  • [image-processor] - image pipeline tools (sub-packages/image-processor/, image-mixer/, product-photo-maker/)
  • [design-system] - design tokens (sub-packages/design-system/)
  • [claude] - Claude Code config (.claude/, CLAUDE.md)
  • [misc] - CI, dependencies, general config, other

Examples: [web] Add EN brand pages, [content] Translate power guide series, [misc] Update dependencies

Code Style

  • Prettier: 100-char line width, single quotes, trailing commas
  • File naming: Always kebab-case (e.g., brand-block.tsx). Never PascalCase for files
  • Imports: Absolute where possible, organized by type
  • Naming: camelCase for variables/functions, PascalCase for components/classes
  • Tailwind: No inline styles. Numeric Tailwind classes prohibited (e.g., p-2, m-4, gap-8). Use semantic tokens: p-vgap-*, p-hgap-*, gap-vgap-*. See components/CLAUDE.md for full styling guide
  • No CSS Modules: Do not use .module.css files. Use Tailwind utility classes for styling. For complex CSS that can’t be expressed as utilities (animations, clip-paths), use plain CSS files with unique class prefixes (e.g., hamster-animation.css with .hamster-* classes) imported as side effects
  • GitHub CLI: Use gh for PR/issue interaction (gh pr view, gh api repos/Takazudo/zmodular/pulls/<N>/comments)

Dependency Version Locks

  • cookie: 0.7.0 (security fix)
  • undici: >=7.24.0 (security fix, via pnpm override)

Never auto-merge Dependabot PRs. Run full test suite before updating dependencies.

Deployment

Post-cutover: hosted on Cloudflare Pages (zmod project) with automatic deployments. Search uses MiniSearch (built at build time, served via Pages Function functions/api/search.ts).

Documentation is auto-synced: pushes to main trigger sync-main-to-doc.yml which merges main into the doc branch. Post-cutover, doc-pages-deploy.yml then deploys to https://doc.takazudomodular.com. Direct pushes to doc also trigger doc-pages-deploy.yml.

Styleguide: pushes to styleguide branch trigger styleguide-pages-deploy.yml which deploys to https://zmod-styleguide.pages.dev.

Pre-cutover (dual-deploy): Netlify remains authoritative. Netlify deploy workflows (main-deploy.yml, preview-deploy.yml, preview-branch-deploy.yml) continue to fire. CF Pages workflows fire only after bigbang-move to zudolab/zzmod.

Dual-Deploy State — Cloudflare Pages Migration (zzmod epic #1798)

Status as of Wave 8.1 (sub-issue #1817): Repo-side cutover edits have landed. Awaiting operator §4.A items (CF project creation, D1 databases, secrets, service bindings) and the DNS flip.

Netlify remains the authoritative host until the DNS flip. After the flip, Cloudflare Pages is authoritative. The Netlify project must be kept intact (do not delete) as the rollback path through the soak window (Wave 9).

Files added in Waves 2–8:

  • wrangler.toml — CF Pages project config + service bindings (PHOTO_UPLOADER, PREORDER_WORKER)
  • .github/workflows/pages-deploy.yml — Build + deploy to CF Pages (dormant until bigbang-move: guards on github.repository == 'zudolab/zzmod')
  • _redirects-pages — CF Pages-format redirect rules. Wave 8 update: /.netlify/functions/* API entries removed; /api/* served by Pages Functions.
  • scripts/gen-cf-pages-redirects.mjs — Regenerates _redirects-pages. Wave 8 update: skips /.netlify/functions/* entries.
  • functions/api/notify-signup.ts, functions/api/reservation.ts, functions/api/admin/[[adminPath]].ts — Wave 8 preorder proxy Pages Functions.
  • functions/api/_shared/preorder-proxy.ts — Shared proxy helper for zmod-preorder-worker.

The pages-deploy.yml workflow copies _redirects-pages to dist/_redirects after pnpm build and before wrangler pages deploy. It does NOT touch static/_redirects, which remains for Netlify.

Wave 9 cleanup (post-soak): remove netlify/ directory, netlify.toml, @netlify/* deps, and Netlify workflow secrets.

Sub-Packages

26 sub-packages in sub-packages/. Most have their own CLAUDE.md with ports, architecture, and testing details.

Web apps (with dev servers)

  • zpreorder — Pre-order admin panel (Vite + React, connects to Netlify Functions)
  • photo-uploader — Mobile photo upload app for the Photo Uploader epic (Vite + React, localhost
    ). Calls the photo-uploader-worker via the Netlify rewrite at /.netlify/functions/photo-uploader-* for auth + presigned R2 PUT
  • mercari-viewer — View/edit Mercari CSV data (zmercari.localhost
    )
  • products-viewer — Edit product-master-data.mjs (zproducts.localhost
    )
  • zmdpreview — Mercari product description previewer (localhost
    )
  • keyword-viewer — Tag/keyword browser (localhost
    )
  • styleguide — Component styleguide for Preact components (localhost
    )
  • styleguide-v2 — Component styleguide, Astro-based (localhost
    )

Tauri apps

  • addac-order — ADDAC System price calculator, standalone Tauri v2 + React/Vite (localhost
    )
  • image-stash-viewer — Preview generated images in image-stash/ (localhost
    )
  • wip-assets-viewer — Browse WIP asset files (SVG diagrams etc.) for the Synth Diagram System; configurable watched dir, SVG/raster rendering, copy-path-to-clipboard (localhost
    )
  • photo-manager — Tauri admin for the Photo Uploader dataset (epic #1651). HTTP-only shell over the photo-uploader-worker /admin/* endpoints. Local-only, baked admin bearer token (no UI login). Vite dev: localhost
    . Use pnpm photo-manager:dev for the Vite-only browser dev loop, or pnpm photo-manager:tauri:dev for the full desktop window.
  • imgs-viewer — Browse, preview, and process product images (Tauri v2 with custom Rust backend, localhost
    )
  • tauri-doc — Tauri wrapper for doc dev server (localhost
    )
  • tauri-keyword-viewer — Tauri wrapper for keyword-viewer (localhost
    )
  • tauri-mercari-viewer — Tauri wrapper for mercari-viewer (localhost
    )
  • tauri-zmdpreview — Tauri wrapper for zmdpreview (localhost
    )
  • tauri-zpreorder-prev — Tauri wrapper for zpreorder, preview env (localhost
    )
  • tauri-zpreorder-prod — Tauri wrapper for zpreorder, prod env (localhost
    )

See sub-packages/TAURI-WRAPPERS.md for the common Tauri wrapper architecture pattern.

CLI tools

  • image-processor — Image pipeline: WebP conversion, OGP generation, metadata
  • image-mixer — Composite image layers for product photos
  • orange-calibrator — Normalize orange fabric background color across product photos
  • product-photo-maker — Product photo generation (bg removal, shadow, texture compositing)
  • product-md-sync — Sync product data to markdown files
  • synth-svg — SVG diagram generators for modular synth tutorial series
  • yt-tools — YouTube video processing tools for guide article creation

Libraries

  • design-system — Shared Tailwind CSS design tokens and theme
  • design-token-lint — Lint Tailwind class names against design system tokens
  • ogp-debug-panel — Configurable OGP debug panel for inspecting Open Graph meta tags

Cloudflare Workers

  • photo-uploader-worker — Cloudflare Worker backing the Photo Uploader app (epic #1592 cutover). Owns the four photo-uploader API routes (/photo-uploader-{login,sign,commit} + /photos.json), accesses D1 via native binding, signs R2 PUT URLs with aws4fetch. Local dev: pnpm worker:dev (port 8787). Deploys via pnpm worker:deploy:{preview,prod} or the deploy-photo-uploader-worker.yml GitHub Actions workflow. See its CLAUDE.md for the full secrets runbook and operator procedure.

Markdown/MDX formatting uses @takazudo/mdx-formatter (npm package, invoked via pnpm dlx).