Imported from liaoweihung/pteduimg (
AGENTS.md). Install upstream withnpx skills add liaoweihung/pteduimg. Copyright stays with the author.
pteduimg Codex Instructions
This repository is a static GitHub Pages site for pharmacy/public education image cards.
Project skill: for card additions, scheduled card releases, image conversion, and generated-page verification, also consult skills/pteduimg-card-manager/SKILL.md.
Card Data Model
cards.jsonis the main runtime data source.cards.manual.jsonis a human-maintained companion source. When adding a new card or changing card metadata, keep both files in sync unless the user explicitly says otherwise.- Card image paths live under each card's
stepsarray. - Card series should include useful search keywords or tags when available. If the user asks to add a new card/card series and does not provide keywords or tags, ask what search keywords or labels they want to use before finalizing the metadata.
- Public education cards use:
category:public_education- public page:
public.html - a non-empty
topicsarray so the card appears in one or more public-page topic filters. Withouttopics, the card appears only in全部圖卡.
- Use one or more of these public-card topic IDs in
topics:skin_woundeyesoral_healththroatinfectious_diseasesheat_illnessinsect_repellentgastrointestinalelimination_caregrowthchronic_conditionssleeppregnancytravel_medication
- When adding a public education card, add the same
topicsmetadata to bothcards.jsonandcards.manual.json. Use multiple topic IDs when the card genuinely belongs to more than one topic. - Pharmacist cards generally use:
category:pharmacist_generalorpharmacist_product- pharmacist page:
index.html - a non-empty
departmentsarray so the card appears in theindex.htmlhorizontal “依科別瀏覽” navigation. Use one or more applicable IDs:ophthalmologyentdermatologyplastic_surgerycolorectal_surgerydentistryobstetrics_gynecologypediatricschest_medicine_smoking_cessationcardiologychinese_medicinepharmacy_general
- When adding a pharmacist card, add the same
departmentsmetadata to bothcards.jsonandcards.manual.jsonwhen the card exists in both files. Use multiple IDs when the card genuinely belongs to more than one department. Withoutdepartments, the card appears only under全部圖卡.
Static Card Pages
This site uses Route B static card share pages.
Every image listed in a card's steps should have a generated page:
cards/{image_filename_without_extension}.html
Example:
img/oral_hygiene_01.webp
cards/oral_hygiene_01.html
Do not create these HTML files by hand. Run:
python build.py
build.py generates/updates:
cards/*.html- root
404.html cards/404.htmlsitemap.xmlall-cards.htmlseo.jsonrobots.txtsw.js
Generated card pages must keep static Open Graph tags in the HTML head. Do not rely on JavaScript to inject OG tags.
Static All Cards Index
build.py also generates all-cards.html, a plain static HTML index of every public card page. This page is part of the SEO crawl surface and must stay updated whenever cards are added, removed, hidden, published, or reordered. Do not edit all-cards.html by hand; run:
python build.py
After changes, verify that all-cards.html links to the expected generated cards/*.html pages and that sitemap.xml / sitemap-main.xml include all-cards.html.
Adding A New Image To An Existing Card
- Put the image in
img/. - Prefer
.webp. If the user gives PNG/JPG files, convert them to WebP first unless they ask not to. - Add the
img/...webppath to the correct card'sstepsarray incards.json. - If the same card exists in
cards.manual.json, update that file too. Some manual entries omitsteps; when in doubt, keep metadata consistent and ask only if needed. - Run:
python build.py
- Verify:
python -m json.tool cards.json
python -m json.tool cards.manual.json
- Check that the generated page exists, for example:
cards/wound_4_soln.html
- Check that
sitemap.xml,all-cards.html, andsw.jsmention the new generated page/image.
Adding A New Card Series
Add a new top-level key to cards.json, for example:
"bugspray": {
"title": "防蚊液",
"category": "public_education",
"order": 8,
"hidden": false,
"icon": "🦟",
"steps": [
"img/bugspray_1.webp",
"img/bugspray_2.webp",
"img/bugspray_3.webp"
]
}
Then mirror the appropriate metadata in cards.manual.json, run python build.py, and verify generated pages.
Scheduled Card Publishing
Prepared card series can be committed before they are visible on the site.
Use:
"hidden": true,
"publish_at": "2026-06-01T09:00:00+08:00"
Rules:
- Add
publish_atto bothcards.jsonandcards.manual.json. - Keep
hidden: trueuntil publication time. - Use an ISO 8601 date-time with timezone. Prefer Taiwan time with
+08:00. - The scheduled GitHub Actions workflow runs
scripts/publish_scheduled_cards.pyhourly. - While a card has both
hidden: trueandpublish_at,build.pytreats it as pending:- it is not included in generated
cards/*.html - it is not included in
sitemap.xml - its images are not added to the service worker pre-cache
- it is not included in generated
- When
publish_athas arrived, the workflow:- changes
hiddentofalsein both JSON files - updates
CHANGELOG.md - runs
python build.py - runs JSON checks and
python scripts/check_site.py - commits the updated data and generated static files
- changes
- If no scheduled cards are due, the workflow makes no commit.
Manual local test:
python scripts/publish_scheduled_cards.py --dry-run
python scripts/publish_scheduled_cards.py --now 2026-06-01T09:00:00+08:00 --dry-run
Image Conversion
Use Pillow when available:
from PIL import Image
with Image.open(src) as image:
image.save(dst, "WEBP", quality=90, method=6)
Keep filenames lowercase English when possible, with underscores and numeric suffixes:
img/bugspray_1.webp
img/bugspray_2.webp
img/bugspray_3.webp
Local Testing
After running build.py, test:
public.htmlfor public education cards.index.htmlfor pharmacist cards.- The generated card page under
cards/.
For card pages, verify:
- Image appears below the top toolbar.
- Previous/next arrows work within the same series.
返回首頁returns topublic.htmlforpublic_educationcards andindex.htmlfor other cards.- QR uses the
/cards/xxx.htmlURL. - Share uses the
/cards/xxx.htmlURL. - Favorite uses the existing
favImageslocalStorage format.
Regression Check
Offline Cache and Update Experience
The site intentionally uses a cache-first service-worker strategy for HTML, scripts, and data. Serve the cached page immediately for a fast, reliable offline-friendly experience, while fetching a newer version in the background. Do not change this to network-first or add cache-busting query parameters unless the user explicitly requests it.
Keep the existing update pill (#update-light) on both index.html and public.html. When a new service worker has installed while an older one controls the current page, it must appear; users choose when to apply the update by clicking it, which reloads the page. Do not force reloads or silently switch users to a new page version.
After changing homepage behavior, card page layout, service worker caching,
analytics, generated card pages, or build.py, run:
python scripts/check_site.py
This checks key site invariants, including:
cards.jsonandcards.manual.jsonare valid JSON.- Referenced images and generated
cards/*.htmlpages exist. - Homepage viewer keeps both transparent side-tap navigation and visible bottom navigation buttons.
- Homepage card data loading does not bypass cache.
- Service-worker fresh assets remain cache-first, with background updates.
- The update pill remains available for user-initiated version updates.
- Static card pages register the service worker.
- Static card pages keep side navigation and bottom page navigation.
- Static card page titles do not include redundant step-number suffixes.
- Local/private-network previews do not load GA unconditionally.
sw.jskeeps the stable runtime cache for previously viewed pages/images.
Git / Publishing
The user often publishes with GitHub Desktop.
After changes are verified, tell the user to commit/push these updated files:
- changed image files under
img/ - changed
cards.json - changed
cards.manual.jsonwhen applicable - generated
cards/*.html sitemap.xmlall-cards.htmlseo.jsonrobots.txtsw.js- any source/template files changed, especially
build.py
Do not include Python cache files. .gitignore already ignores:
__pycache__/
.codex_index_*
Important Rule
Uploading an image to img/ is not enough. A card appears on the site only after:
image exists -> cards.json references it -> python build.py has been run -> changes are committed/pushed