Contact EN 中文

Sixty Impact Report

An InDesign print report turned into a live site and a local multimodal RAG

At a glance

Problem
For its 60th anniversary the Tzu Chi Foundation published the Sixty Impact Report, a long Traditional-Chinese print document laid out in InDesign: about 64 spreads and 682 text stories.
What I built
Two consumers of one source file.
Outcome
Live at 60impact.tzuchi.org.tw (18 pages), plus a local Q&A tool that cites the report page behind each answer.
My role
Solo
Period
2026-05 → 2026-10

Abilities shown

Each links to the ability and its level on the Skills page.

The problem

For its 60th anniversary the Tzu Chi Foundation published the Sixty Impact Report, a long Traditional-Chinese print document laid out in InDesign: about 64 spreads and 682 text stories. The report needed a web version people could read on a phone. Its content was also worth querying: a question should get an answer that points back to the page it came from.

What I built

Two consumers of one source file. The first is the public website: 18 pages, built with Astro and deployed to Cloudflare Pages. The second is a RAG pipeline in Python that ingests the InDesign file (text, page renders and images) into a vector store on drust, my own BaaS. A small Flask app answers questions over it and cites each claim as (page N). The Q&A app runs locally only; it is not public.

How it works

  1. A parser reads the unzipped IDML directly: pages, exact text blocks with point sizes and positions, and placed images.
  2. A renderer rebuilds an approximate image of each page, places the real linked images, and tags every text block with an id.
  3. A local vision model looks at that render and returns only JSON: the reading order, role and heading level of each block id.
  4. Python assembles Markdown and chunks from the exact IDML text, embeds them with a 2,560-dimension model, and writes pages, chunks and images to drust.
  5. At query time: vector search on chunks, rerank, then expand strong hits to their whole page. Broad questions are answered from page summaries made at ingest.

Decisions

  • The vision model never re-types text. It only decides order and structure; the words come from the IDML. This gives zero OCR drift on a complex print layout.
  • Only local models. Chat, vision, embedding and rerank all run on self-hosted Qwen3 models behind an OpenAI-compatible proxy, so no external AI API is involved.
  • Migrate the site page by page. The first version was zero-dependency HTML. I wrapped it in Astro so old and new pages could ship side by side, and wrote a verify script, because Astro silently prefers a legacy file with the same name.
  • One content source. The repo JSON is the source of truth; drust is a mirror pushed by a script, with a local-JSON fallback if it fails.

Outcome

The site is live at 60impact.tzuchi.org.tw, with all 18 pages now migrated to Astro and deployed by CI on every push to main. The Q&A tool stays local, with cited answers. Built solo with Claude Code, using a spec → plan → implement → review loop.