--- name: iching-study-app description: I Ching Study app architecture, deployment, and UI conventions — flashcard hexagram proportions, trigram detail rendering, and VPS deploy workflow. tags: [iching, flashcards, react, vite, pm2, deployment, ui] --- # I Ching Study App Project-specific guide for working on Alex's I Ching Study app. ## App Overview - Path: `/home/avalon/apps/iching-study` - URL: `https://iching-study.apps.poofc.com` - Stack: React 19 + Vite + TypeScript + Express - PM2 process: `iching-study` - Repo: `firemountain/iching-study` ## Build + Deploy Always use the app's build command, then restart PM2, then verify the built asset timestamps. ```bash cd /home/avalon/apps/iching-study npm run build pm2 restart iching-study pm2 save stat dist/assets/index-*.css dist/assets/index-*.js ``` Then visually verify the live site at `https://iching-study.apps.poofc.com`. ## Core UI Files - Main app UI: `src/App.tsx` - Styling: `src/index.css` - Hexagram data: `src/data/hexagrams.ts` - Divination helpers: `src/lib/divination.ts` - Human Design center quiz geometry: `src/lib/humanDesignCenters.ts` - Divination + quiz tests: `src/App.test.tsx`, `src/lib/divination.test.ts` ## Human Design Center Quiz The app now includes a quiz section for memorizing which Human Design gates/hexagrams belong to which centers, plus the gate's exact visual slot within each center. ### Product expectations - Sidebar includes a `Quiz section` entry separate from study/divination/admin. - Quiz home should act as a landing screen so more quizzes can be added later. - First quiz should be `Human Design centers`. - Prompt should show: - gate number - hexagram name - hexagram character - hexagram line figure - User must answer by tapping the exact numbered slot on the correct center card, not by picking only the center name. - This trains both center recall and positional recall so channel study can be layered on later. ### Reusable implementation pattern - Keep quiz routing lightweight inside `src/App.tsx` by extending `ViewMode` with `quiz` and using a small `activeQuizId` for sub-quiz selection. - Keep reusable Human Design center geometry in a dedicated module: `src/lib/humanDesignCenters.ts`. - Store per-center SVG path data, viewBox, label, fill color, and gate point coordinates in that module. - Derive `GATE_TO_CENTER`, `ALL_HUMAN_DESIGN_GATES`, and helper lookups from the shared center data so tests/UI stay in sync. - For prompts, reuse the existing `hexagrams.ts` dataset so gate = hexagram and the quiz can display the standard line glyph. ### Where the geometry came from The correct source of truth was the HD Prism app on the same server, not a hand-made recreation. Use these HD Prism sources: - center shapes / bodygraph SVG: `/home/avalon/apps/hd-prism/apps/hdprism/public/svg/empty-bodygraph.svg` - gate-to-center mapping: `/home/avalon/apps/hd-prism/apps/hdprism/lib/constants.ts` - placement logic reference: `/home/avalon/apps/hd-prism/apps/hdprism/components/BodygraphScreen.tsx` ### Practical extraction lesson The fastest reusable workflow was: 1. Read `constants.ts` for gate-to-center truth. 2. Read `BodygraphScreen.tsx` to understand how gate labels were placed on the SVG. 3. Extract center SVG paths and channel endpoints from `empty-bodygraph.svg`. 4. Build a compact local data file in the I Ching app (`src/lib/humanDesignCenters.ts`) rather than coupling directly to HD Prism runtime code. This is better than trying to import HD Prism components directly because the I Ching app only needs stable geometry + mappings, not the whole bodygraph renderer. ### UX lessons learned - The quiz works better when every center card shows both the silhouette and a tappable numbered answer grid below it. - When revealing the answer, highlight both the correct slot and the selected wrong slot so the user learns the positional correction instantly. - The quiz home page matters: it avoids painting the app into a corner when more quiz types (channels, harmonics, etc.) are added later. ### Test coverage When editing quizzes, cover at least: - sidebar opens and shows `Quiz section` - quiz home opens from the sidebar - human design center quiz launches - prompt exposes a gate number - selecting the correct numbered slot produces a success state and next-gate CTA ## Coin Divination Flow The app now includes a dedicated mobile-only divination mode launched from the sidebar. ### Entry point and screen behavior - Sidebar includes `Start divination` - Divination uses its own `view` mode in `src/App.tsx` - Keep the screen visually sparse: top-left menu button, coin stage, cast status, CTA, and bottom-stacked lines - After 6 casts, resolve the hexagram from bottom-up line order and open the existing hexagram details card automatically ### Coin semantics - Heads = Yang = value `3` - Tails = Yin = value `2` - Sum 3 coins per cast - Odd total => Yang line (`1`) - Even total => Yin line (`0`) - Lines stack bottom-up and match directly against `hexagram.lines` ### Implementation pattern Use pure helpers in `src/lib/divination.ts` for: - coin value mapping - line generation from 3 faces - completion detection - hexagram lookup from 6 lines Keep UI-specific animation/state in `src/App.tsx`. ### Animation and visual lessons learned Simple generic gold circles looked cheap and failed user expectations. The better pattern was: - ancient Chinese cash-coin styling with a square center hole - visible bronze/brass rim, inner ring, patina, and depth cues - explicit Yin/Yang labeling on the coins (`陰 Yin`, `陽 Yang`) - explicit role markers requested by user (`Woman`, `Man`, with ♀ / ♂) - randomized per-cast motion values so coins do NOT land in the same pile every time - higher toss arc, faster drop, and bounce/settle phases feel better than a soft float-up/float-down animation ### Practical motion pattern For each cast, generate per-coin values for: - horizontal drift - vertical drift - spin - tilt - animation delay - rise height - bounce amount - settle rotation - faux depth Then feed them into CSS custom properties and a single keyframe animation. This keeps animation expressive without adding a physics library. ### Visual QA heuristic If a visual review says the coins feel like a repeated tidy preset rather than tossed physical objects, increase: - toss height - drop speed contrast - per-coin randomization - spread/rotation variance If Yin/Yang is not obvious in a screenshot, do not rely on subtle engraved details alone — add explicit readable text labels. - Divination logic: `src/lib/divination.ts` ## Coin Divination Flow The app now includes a mobile-first coin divination mode launched from the sidebar. ### Entry point - Sidebar includes a `Start divination` action under its own `Divination` section. - This should switch the app into a dedicated `view === 'divination'` mode rather than layering over study/admin views. ### Intended mobile layout The divination screen should feel nearly blank and immersive: - Keep only the discreet top-left floating menu button visible outside the main interaction area. - Do NOT reuse the normal study top bar on this screen. - Center the coin throw interaction and keep the accumulated lines anchored near the bottom. - Initial state should be minimal: no big explanatory copy blocks, just the throw state and button. ### Throw behavior - Each throw uses exactly 3 coins. - Heads = yang = value `3` - Tails = yin = value `2` - Sum the three coin values. - Odd totals create a yang line (`1`) - Even totals create a yin line (`0`) - Build 6 lines from bottom to top. - After the 6th line, resolve the matching hexagram from `hexagrams.ts` and open that hexagram's existing detail card. ### Implementation pattern Use pure helper functions in `src/lib/divination.ts` for reusable logic: - `getCoinValue()` - `getLineFromCoins()` - `isDivinationComplete()` - `getHexagramFromThrownLines()` - `throwCoins()` Keep the UI state in `App.tsx`, but keep coin math and hexagram resolution out of the component. ### UX findings - Showing placeholder/default coins before the first throw makes the screen feel less blank than requested; start with no visible coins until the first throw. - A visually "discreet" menu button can still be too subtle; slightly increasing tap-target size improved mobile usability without breaking the minimal look. - Bottom-stacked lines looked too disconnected when the lower region was too tall; reduce the reserved bottom area so the built hexagram feels tied to the current throw. ### Test coverage When editing this flow, cover both pure logic and UI behavior: - `src/lib/divination.test.ts` for heads/tails values, odd/even line conversion, completion, and hexagram lookup. - `src/App.test.tsx` for sidebar entry, divination screen launch, first throw result, and six-throw completion opening the resolved hexagram. ## Hexagram Rendering Conventions ### Main card hexagram proportions The front-of-card hexagram should look like a normal I Ching hexagram — relatively narrow/tall, not stretched wide. The glyph is rendered by `LineGlyph` in `src/App.tsx` and styled in `src/index.css`. Important styling constraints: - Keep `.line-glyph` width constrained (`~11rem max`) so the six-line stack does not look flattened. - Keep vertical line spacing balanced with width. - Avoid giving the glyph full-card width. - Rounded modern line styling is okay, but the width-to-height ratio must still read as a traditional hexagram. ### Compact trigram glyphs For compact trigram cards on the back/details side: - Use a smaller fixed width (`~4.2rem`) - Keep the compact line gap tighter than the full hexagram - Slightly thicker compact lines are okay if the overall trigram remains legible ## Trigram Detail Rendering A text-only trigram section can look broken or "missing" even if the data is present. For each trigram card, show ALL of the following together: 1. Compact line glyph (`LineGlyph ... compact`) 2. Trigram symbol character (`☰`, etc.) 3. Human-readable image label (`heaven`, `earth`, etc.) 4. Name and attribute text ### Proven working pattern Wrap the trigram visuals in a dedicated visual block: - `.trigram-visual` - `.trigram-symbol-block` - `.trigram-symbol` - `.trigram-image-label` This makes the trigram instantly readable and prevents the symbol area from looking empty in browser screenshots or on mobile. ## UX Lesson Learned If a user says a visual element is "not showing," don't assume the data binding is broken. In this app, the trigram data existed and `LineGlyph` rendered, but the presentation was too subtle. The correct fix was to improve the visual affordance, not to debug API/data flow. ## Coin Divination Flow The app now includes a dedicated mobile-only divination mode launched from the sidebar. ### Product expectations - Sidebar must include `Start divination` - Divination is a fullscreen-feeling mode with only: - discreet top-left menu button - central coin-casting hero area - bottom-stacked line results - User performs 6 casts total - Each cast throws 3 coins - Heads / Yang = 3 - Tails / Yin = 2 - Sum the 3 coins: - odd total => yang line (`1`) - even total => yin line (`0`) - Stack lines bottom-up - After 6 lines, resolve the matching hexagram and open that hexagram directly in study view with the back/details side visible ### Implementation pattern - Keep coin math in a pure helper module: `src/lib/divination.ts` - Keep UI state in `src/App.tsx` - Use deterministic helper functions for: - coin face value mapping - line creation from 3 coins - completion detection - lookup from 6 thrown lines to `hexagrams` - Add dedicated tests in `src/lib/divination.test.ts` - Add UI coverage in `src/App.test.tsx` ### Premium coin visual direction If Alex asks for the coins to feel "cool," "ancient Chinese," or more premium, the correct direction is: - Ancient Chinese cash coin look with square hole in the center - Coins should feel like aged bronze/brass artifacts, not flat yellow UI circles - Use: - rim + inner ring - patina / oxidation variation - darker recesses around square hole - engraved character treatment - stronger contact shadows - visible thickness / perspective - Larger coins read better on mobile; do not keep them too small - Remove tiny visual labels around the coins if they clutter the hero composition; use screen-reader-only text instead ### Motion / physics direction Cheap coin motion feels fake immediately. Better results come from: - per-coin drift and staggered timing - larger toss arc - stronger tumbling / rotation variation per coin - settle state with convincing shadow placement - a more ceremonial, sparse composition rather than crowding the screen ### UX lesson learned For this app, minimalist is good, but "empty + generic" is not. The divination screen only feels premium when the coins themselves become the hero object. If the coins are too small, too flat, or too labeled, the whole experience reads like placeholder UI instead of ritual interaction. ## Wilhelm Translation Import Workflow The app now supports a full Wilhelm translation panel on the hexagram details side. ### Preferred extraction strategy Do NOT rely on the PDF text layer for Wilhelm extraction. In this PDF, direct text extraction is garbage due to font encoding. Use this pipeline instead: 1. Use PDF bookmarks / TOC to map each hexagram to page ranges. 2. Render those pages to PNGs. 3. Use a vision-capable model to transcribe the PNGs into structured JSON. 4. Preserve source page images in the repo for fidelity and future spot-checks. 5. Merge the generated Wilhelm data into `src/data/hexagrams.ts`. ### Files involved - Vision extractor: `scripts/extract-wilhelm-vision.ts` - Section map: `data/wilhelm-section-map.json` - Generated full dataset: `src/data/wilhelm-extracted.generated.json` - Source page images: `public/wilhelm//page-XXX.png` - Hexagram data merge: `src/data/hexagrams.ts` - UI rendering: `src/App.tsx` - Styling: `src/index.css` - UI test: `src/App.test.tsx` ### Extraction lessons learned - Vision-first was materially better than OCR-first for this document. - TOC/bookmarks are reliable for segmentation. - Vision handled headings, `The Judgement`, `The Image`, and `The Lines` far better than Tesseract cleanup. - Prompt wording matters a lot. The better prompt explicitly says: - do not paraphrase or modernize - preserve old-fashioned wording - preserve visible line breaks when they are part of the poetic/book layout - format Above/Below as `Romanized / English name, Element` - Page 133 is afterword material and should NOT be included in hexagram 64. The TOC-derived page range fixes this. ### Integration pattern Extend `Hexagram` with: - `wilhelm?: { heading, pageStart, pageEnd, sourceImages, above, below, judgement, image, lines, notes, fullText }` Render a dedicated `WilhelmPanel` on the details/back side with: - heading - page range - above / below cards - Judgement block - Image block - line cards for `The Lines` Use `white-space: pre-wrap` for Wilhelm text so line breaks survive. ### Practical repo workflow - Run sample first: - `npx tsx scripts/extract-wilhelm-vision.ts --sample` - Run full extraction: - `npx tsx scripts/extract-wilhelm-vision.ts` - Copy the full report JSON into app data if needed: - `cp docs/reports/wilhelm-vision-full.json src/data/wilhelm-extracted.generated.json` ### Testing lesson Once Wilhelm is in the UI, existing tests may start failing from ambiguous matches because the same phrases appear in both the modern summary copy and the Wilhelm panel. Fix tests by scoping assertions with `within(panel)` and using heading levels when needed. ## Verification Checklist After changing card/trigram visuals, divination visuals, or Wilhelm content: 1. Build succeeds 2. PM2 process restarts cleanly 3. `dist/` asset timestamps are fresh 4. Front card hexagram looks narrower and closer to traditional proportions 5. Back/details shows clearly visible upper + lower trigram visuals 6. Divination sidebar entry exists and opens correctly 7. Divination screen shows 3 visible premium coins before the first cast 8. Casting animation completes and line progress updates correctly 9. Six casts resolve to a matching hexagram and open its details view 10. Wilhelm panel appears on the hexagram details side with heading, sections, and lines 11. Live browser check shows no JS errors 12. Commit and push changes to GitHub ## Git Workflow Always commit and push after changes: ```bash git add src/App.tsx src/index.css git commit -m "Fix hexagram proportions and trigram visuals" git push origin main ``` ## Pitfalls - A glyph can be technically present but visually read as missing if the symbol area is too subtle. - Full-width flex rendering makes hexagrams look too wide; constrain width explicitly. - Always verify with a visual browser check after deploy, not just local code inspection.