Dako

The content pipeline

2026-10-03 · technical note

Orange Lantern shows one small thing a day under a painting. This note follows the content from a JSON file to the bundled image. Unfinished steps are marked planned.

The library is one JSON file

Every small thing is an entry in actions.json. An entry holds an id, an energy tier (one, two or three bars), the sentence in Simplified Chinese, Traditional Chinese and English, an image name, a style id, the scene light and the after patch shown once the light comes on. Ids are never renumbered. The file has 85 entries numbered 1 to 91; a cut story leaves a gap, so old records stay valid.

Review

Text review runs on a private web page built from the same JSON. The reviewer marks each story keep, fix or drop and adds a note; choices save to the page's database per story. The October 2026 round dropped five stories and marked 38 for rewording.

Fonts

One font per language: Chill Round Gothic, Noto Sans CJK TC and Lora, all under the OFL. subset_fonts.py walks the library, collects every character each language uses, and cuts a subset with fontTools. It drops hinting, keeps kerning and locale forms, renames the family so no reserved name survives, then reopens the exported file and fails if any library character is missing from its cmap. A Swift test repeats the check: it loads each font with CoreText and confirms every library character is present and the PostScript name matches Theme.swift.

One row per painting

Each story has one composition row:

A v2 row with the chosen style, alignment notes and a palette is planned.

Styles and pairing

A style is a profile: palette, edge, material, shape, light, export rules, required visible evidence and forbidden traits. Eight profiles exist in this form so far. 106 styles were drawn as trials; 87 were kept as directions, none yet approved for production. Current paintings use four styles assigned by rotation. Pairing each story to one of the 87, at most two per style and no dark style as the main choice, is planned.

Prompts

A compiler turns the row, a short style description and a global contract (exact 2:3, quiet top and bottom bands, no text) into one prompt per painting and writes art_manifest.json. Production folders keep the prompts actually used. A new compiler that reads the style profiles, freezes each prompt and records its sha256 is planned.

From AI image tools to the bundle

Paintings come from AI image tools as a before frame and an after frame of the same scene; only the cat or snail changes. export_art.py rejects anything that is not exact 2:3 or smaller than 1024×1536, never upscales, caps output at 1440×2160 and saves HEIC. It diffs the frames, feathers the changed box and stores only that patch. The box must sit between 15% and 80% of the height; a change over 40% of the painting is refused. The script writes the patch position back to actions.json and clears the old lamp position so it has to be re-entered. If one master fails, nothing is written.

Tests close the loop

swift test runs LibraryCheck over the bundled JSON: three languages present, style known, light anchor in range, after frames inside the safe band. It then checks the Art folder holds exactly the files the library names, one file per name, under 300 KB per base and 120 KB per patch, 25 MB in all. Another test confirms every id has a production record. When a painting and the library disagree, swift test fails.