From f45791579b4dbdc99b8d5ad33974cfed04832a5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D1=82=D0=BE=D0=BD=20=D0=A2=D1=80=D0=BE=D1=88?= =?UTF-8?q?=D0=B8=D0=BD?= Date: Sun, 2 Aug 2026 23:45:56 +0300 Subject: [PATCH] feat: color themes, dark mode, HC/sepia themes, moon tooltip, docs update --- .ai-specs/DATA_MODEL_SPEC.md | 68 ++++++++++++++------ .ai-specs/ENRICHMENT_WORKFLOW.md | 98 +++++++++++++++-------------- .ai-specs/PROJECT_CONTEXT.md | 101 ++++++++++++++---------------- .ai-specs/WORKING_POLICIES.md | 40 +++++++----- DATA_EN.md | 28 +++++---- DATA_RUS.md | 16 ++--- README.md | 35 ++++------- README_RUS.md | 36 ++++------- src/core/i18n.ts | 20 ++++++ src/styles/main.css | 104 +++++++++++++++++++++++++++---- src/ui/app.ts | 14 ++++- src/ui/calendar-view.ts | 18 +++--- src/ui/menaion-view.ts | 4 +- src/ui/prayer-view.ts | 6 +- src/ui/service-view.ts | 6 +- src/ui/settings-view.ts | 35 +++++++++-- 16 files changed, 389 insertions(+), 240 deletions(-) diff --git a/.ai-specs/DATA_MODEL_SPEC.md b/.ai-specs/DATA_MODEL_SPEC.md index 33b5296..56aff2d 100644 --- a/.ai-specs/DATA_MODEL_SPEC.md +++ b/.ai-specs/DATA_MODEL_SPEC.md @@ -12,14 +12,14 @@ CIds are identifiers for saints, feasts, and liturgical elements. They follow th | `02XX-02ZZ` | `02.json` | `023456`, `027890` | February saints | | ... | ... | ... | ... | | `12XX-12ZZ` | `12.json` | `123456`, `127890` | December saints | -| Other CIds | `misc.json` | `772426`, `373`, `868` | Non-month-prefixed | +| Other CIds | `misc/{first-digit}.json` | `772426→misc/7.json`, `373→misc/3.json` | Non-month-prefixed | ### Routing Logic ```typescript function bundleFileForCid(cid: string): string { return /^(0[1-9]|1[0-2])\d+/.test(cid) ? `${cid.substring(0, 2)}.json` - : 'misc.json'; + : `misc/${cid.charAt(0)}.json`; } ``` @@ -79,7 +79,7 @@ function bundleFileForCid(cid: string): string { ### Optional Fields - **`name.short`**: Short name variant - **`life.text`**: HTML content (only written when empty) -- **`life.id`**: Source identifier (e.g., "minei-0101-772426") +- **`life.id`**: Source identifier (e.g., "minei-0101-772426" or "bulgakov-...") - **`life.copyright`**: Copyright string ## Service Template Structure @@ -121,23 +121,43 @@ function bundleFileForCid(cid: string): string { ``` ### Key Characteristics -- Generated by `convert-mines.ts` from EPUB source +- Generated by `convert-minei.ts` from EPUB source - Stored in `scripts/output/minei.json` (gitignored, 33MB) - Titles and HTML are carefully extracted and parsed - Footnotes collected and appended as "Примечания:" blocks +## Bulgakov Source Format + +### Format Definition +```json +{ + "MM-DD": [ + { + "title": "20-го июля — Св. пророк Илия", + "html": "

Content paragraphs...

" + } + ] +} +``` + +### Key Characteristics +- Generated by `convert-bulgakov.ts` from "Настольная книга для священно-церковнослужителей" EPUB +- Stored in `scripts/output/bulgakov.json` (gitignored) +- Menaion section only (306 days, 332 entries) +- Secondary source: fills gaps, never overrides existing Minei content + ## File Organization Rules ### Lives Files - **Location**: `static/data/{lang}/lives/` - **Files**: - `01.json` through `12.json` (month-prefixed CIds) - - `misc.json` (non-month-prefixed CIds) -- **Routing**: Based on first two digits of CId + - `misc/0.json` through `misc/9.json` (non-month-prefixed CIds, split by first digit) +- **Routing**: Based on first two digits of CId (month) or first digit (misc) ### Menaion Files - **Location**: `static/data/{lang}/menaion/` -- **Files**: `01-01.json` through `12-31.json` (daily entries) +- **Files**: bundied into `menaion-bundle.json` per language - **Purpose**: Daily liturgical readings by date ### Service Templates @@ -145,9 +165,15 @@ function bundleFileForCid(cid: string): string { - **Shared**: `static/data/shared/services/templates/` ### Calendar Data -- **Location**: `static/data/{lang}/calendar/` and `static/data/shared/calendar/` +- **Location**: `static/data/shared/calendar/` - **Purpose**: Movable feast calculations and metadata +### Scripture / Paremia Data +- **Location**: `static/data/shared/parimii/` +- **Monthly files**: `jan/full.json` through `dec/full.json` +- **Common files**: `common-saints/full.json`, `cheese-week/full.json`, etc. +- **Purpose**: Old Testament readings for Vespers + ## Data Invariants ### CId Consistency Rules @@ -158,7 +184,7 @@ function bundleFileForCid(cid: string): string { ### File Organization Invariants - No month file contains CIds with different month prefix -- `misc.json` contains all CIds outside the 01-12 prefix range +- `misc/{n}.json` contains all CIds with matching first digit - All data files are tracked in `static/data/` ### Content Validation Invariants @@ -181,7 +207,7 @@ function bundleFileForCid(cid: string): string { - ⚠️ Don't leave trailing commas in JSON ### File Organization Mistakes -- ⚠️ Don't place month-prefixed CIds in misc.json +- ⚠️ Don't place month-prefixed CIds in misc files - ⚠️ Don't create inconsistent JSON indentation - ⚠️ Don't mix languages in incorrect directories @@ -190,7 +216,7 @@ function bundleFileForCid(cid: string): string { ### Valid CId Examples - `010101` → `01.json` (January saint) - `1253` → `12.json` (December saint) -- `772426` → `misc.json` (non-prefixed saint) +- `772426` → `misc/7.json` (non-prefixed saint) ### Invalid CId Examples - `00501` → Should not exist (prefix `00` → misc) @@ -198,8 +224,8 @@ function bundleFileForCid(cid: string): string { ### File Routing Examples - CId `1253` routes to `12.json` -- CId `772426` routes to `misc.json` -- CId `010101` routes to `01.json` +- CId `772426` routes to `misc/7.json` +- CId `373` routes to `misc/3.json` ### Bundle Entry Examples ```json @@ -232,15 +258,17 @@ function bundleFileForCid(cid: string): string { ### Coverage Statistics - Total CIds: 3046 -- Lives with text: 1190 (39% coverage) -- Empty lives: 1856 (61% remain) +- Lives with text: 1203 (39% coverage) +- Sources: Chetyi-Minei (1190) + Bulgakov (13) +- Empty lives: 1843 (61% remain) ### Data Files -- Total validated files: 274 -- Main source files: 6 Lives bundle files (misc, 01-12) +- Lives bundle files: 12 month files (01-12) + 10 misc split files (0-9) - Service template files: Multiple per language +- Parimii files: 12 monthly + common saints + triodion periods ### Recent Changes -- Bundle fix: 868 Иаков Боровицký moved from 05-22 to 10-23 -- Enrichment: 64 manual fixes added to enrich-lives.ts -- Coverage increase: 1009→1190 lives with text (191 new) \ No newline at end of file +- Bundle fix: 868 Иаков Боровицкий moved from 05-22 to 10-23 +- Minei enrichment: 64 manual fixes added +- Bulgakov enrichment: 9 manual fixes added, 13 new lives +- Coverage increase: 1009→1190→1203 lives with text \ No newline at end of file diff --git a/.ai-specs/ENRICHMENT_WORKFLOW.md b/.ai-specs/ENRICHMENT_WORKFLOW.md index 27d46d5..980cf26 100644 --- a/.ai-specs/ENRICHMENT_WORKFLOW.md +++ b/.ai-specs/ENRICHMENT_WORKFLOW.md @@ -4,11 +4,17 @@ Run enrichment in safe mode first: ```bash -# Dry run to see matches +# Minei dry run npx tsx scripts/enrich-lives.ts -# Write changes when confident -npx tsx scripts/enrich-lates.ts --write +# Minei write +npx tsx scripts/enrich-lives.ts --write + +# Bulgakov dry run +npx tsx scripts/enrich-lives.ts --bulgakov + +# Bulgakov write +npx tsx scripts/enrich-lives.ts --bulgakov --write ``` ## Common Enrichment Mistakes @@ -31,20 +37,23 @@ npx tsx scripts/enrich-lates.ts --write - ⚠️ Don't append to CIds that already have life.text without checking collision behavior - ⚠️ Don't assume all multi-source CIds follow same pattern +### Bulgakov-Specific Issues +- ⚠️ Bulgakov only covers Menaion (saints by calendar date), not Triodion/Pentecostarion +- ⚠️ Bulgakov is secondary source — only fills gaps, never overrides Minei +- ⚠️ Some Bulgakov entries are feast descriptions, not saint lives — may have no target CId + ### Validation Skipping -- ⚠️ Don't skip `npx tsx scripts/validate-data.ts` — it catches real errors - ⚠️ Don't skip `npx tsc --noEmit` — type errors indicate real problems - ⚠️ Don't skip `npm run build` — build failures need investigation -- ⚠️ Don't assume "all files valid" without actually running it +- ⚠️ Don't skip data validation — run `validate-data.ts` after changes ## Decision Points for AI Agents ### When to Add MANUAL_FIXES -- When score is 0.35-0.6 and you can identify the correct target -- When source title contains distinctive substring not in other titles on same date - When auto-matcher picks wrong saint (e.g., different homonym on same date) - When target CId exists but is listed on wrong calendar date (bundle bug) - When narrative maps to known CId but has empty life (can be enriched) +- When Bulgakov title contains distinctive substring not in Minei titles ### When to Leave Unmatched - When source is a narrative or homily without saint association @@ -60,7 +69,7 @@ npx tsx scripts/enrich-lates.ts --write ## Quality Checkpoints ### After Manual Fixes Addition -- Verify substring uniqueness per date (use verification script) +- Verify substring uniqueness per date - Check that target CIds exist and are empty - Re-run dry run to verify improvement - Check for potential new conflicts with existing fixes @@ -72,9 +81,9 @@ npx tsx scripts/enrich-lates.ts --write - Verify no unintended side effects on other files ### Before Committing -- Run full validation: `npx tsx scripts/validate-data.ts` - Type check: `npx tsc --noEmit` - Build verify: `npm run build` +- Run data validation: `npx tsx scripts/validate-data.ts` - Check git status to confirm intended files staged ## Troubleshooting Common Issues @@ -105,25 +114,20 @@ npx tsx scripts/enrich-lates.ts --write - ⚠️ **Expected behavior**: First source writes without heading, subsequent sources append - **Check**: Verify total chapters match expected count for CId -### Strange JSON Syntax Errors -- **Problem**: Parse errors in data files after modification -- **Solution**: Check JSON syntax, fix trailing commas, quote issues -- **Verify**: Run `validate-data.ts` to get specific error location - -### Build Size Changes -- **Problem**: Unexpected file size increases/decreases -- **Solution**: Check for malformed content, duplicate data, formatting changes -- **Check**: Compare before/after file sizes in git diff +### Bulgakov-Specific Issues +- **Problem**: Bulgakov entry has no matching CId +- **Solution**: Check if it's a feast day description (not a saint life) +- **Alternative**: Search lives-index.json for the saint's name +- **Check**: Verify the date has the correct CIds in the menaion bundle ## Verification Commands ### Coverage Tracking ```bash -# Check coverage statistics python3 -c " import json, glob, os total=has=0 -for fp in glob.glob('static/data/ru/lives/*.json'): +for fp in sorted(glob.glob('static/data/ru/lives/*.json') + glob.glob('static/data/ru/lives/misc/*.json')): data=json.load(open(fp)) for cid,entry in data.items(): total+=1 @@ -135,14 +139,15 @@ print(f'Coverage: {has}/{total} ({has*100//total}%) lives with text') ### Collision Chapter Count Check ```bash -# Check collision targets have correct chapter counts python3 -c " import json, re -misc = json.load(open('static/data/ru/lives/misc.json')) -for cid in ['493', '1674', '257401']: # Known collision targets - text = misc[cid]['life']['text'] - headings = re.findall(r'([^<]+)', text) - print(f'{cid}: {len(headings)} chapters') +for fp in ['static/data/ru/lives/misc/4.json', 'static/data/ru/lives/misc/1.json']: + data = json.load(open(fp)) + for cid in ['493', '1674', '257401']: + if cid in data: + text = data[cid]['life']['text'] + headings = re.findall(r'([^<]+)', text) + print(f'{cid}: {len(headings)} chapters') " ``` @@ -159,21 +164,22 @@ npx tsx scripts/tools/stats.ts --lang ru ## Recent Context Updates ### Known Fixed Issues -- 868 Иаков Боровицký moved from 05-22 to 10-23 (bundle mis-mapping correction) +- 868 Иаков Боровицкий moved from 05-22 to 10-23 (bundle mis-mapping correction) - Сретение→373 added (02-02 Сказание about Presentation) - Захарий Печерский→822 added (03-24 miracle story) - Анна пророчица→2396 added (08-28 prophetess Anna) ### Current Unmatched Floor -- 27 entries remain unmatched (all narratives/homilies or already-filled targets) -- These are at the floor for Chetyi-Minei source — no recoverable matches available +- **Minei**: 27 entries remain unmatched (all narratives/homilies or already-filled targets) +- **Bulgakov**: 121 entries unmatched (no recoverable targets — saints already have lives or no CId) +- These are at the floor for both sources — no recoverable matches available ## File Routing Quick Reference ### Lives File Routing - Files: `static/data/ru/lives/01.json` through `12.json` for month-prefixed CIds -- File: `misc.json` for non-month-prefixed CIds -- Route: `bundleFileForCid(cid: string):` checks `/^(0[1-9]|1[0-2])\d+/.test(cid)` +- Files: `static/data/ru/lives/misc/0.json` through `9.json` for non-month-prefixed CIds +- Route: `bundleFileForCid(cid)` checks `/^(0[1-9]|1[0-2])\d+/.test(cid)` ### Bundle File Organization - Main bundle: `static/data/cu/menaion-bundle.json` (authoritative calendar) @@ -188,14 +194,14 @@ npx tsx scripts/tools/stats.ts --lang ru - Confirm coverage increased as expected ### Documentation Updates -- Update DATA_FORMAT.md or DATA_RUS.md if schema changed +- Update DATA_EN.md or DATA_RUS.md if schema changed - Update coverage statistics in relevant documentation - Add new manual fixes to enrichment documentation if needed ### Commit Sequence ```bash -# 1. Validate, typecheck, build all passing -npx tsx scripts/validate-data.ts && npx tsc --noEmit && npm run build +# 1. Typecheck, build, validate all passing +npx tsc --noEmit && npm run build && npx tsx scripts/validate-data.ts # 2. Stage all changes git add scripts/enrich-lives.ts scripts/output/lives-match-report.json static/data/ru/lives/ @@ -208,20 +214,19 @@ git commit -m "enrich [N] more ru lives: [description]" ### Current Status - Total CIds: 3046 -- Lives with text: 1190 (enriched from Chetyi-Minei) -- Unmatched minei chapters: 27 (floor: narratives, no-target, already-filled) -- Files validated: 274 files (all passing) +- Lives with text: 1203 (enriched from Chetyi-Minei + Bulgakov) +- Unmatched Minei: 27 (floor: narratives, no-target, already-filled) +- Unmatched Bulgakov: 121 (floor: no recoverable targets) -### Recent Enrichment Work -- Baseline: 1009 lives with text (from prior work) -- Latest commits added 181 lives (121 + 57 + 3) -- Manual fixes added: 214 total in enrich-lives.ts -- Bundle corrections applied: 868 date fix +### Sources +- **Chetyi-Minei**: 1190 lives, 64 manual fixes, 27 unmatched at floor +- **Bulgakov Desktop Book**: 13 lives, 9 manual fixes, 121 unmatched at floor +- **Total coverage**: 1203/3046 (39%) ### Progress Tracking -- Review band: 127 entries resolved, 0 remaining -- Unmatched band: 94 → 30 → 27 (final floor) -- Coverage: 1009→1130→1190 lives (181 net increase) +- Minei band: 94 → 30 → 27 (final floor) +- Bulgakov: 332 entries processed, 13 new lives, 121 unmatched +- Coverage: 1009→1130→1190→1203 (194 net increase) ## Example Workflow for Adding Manual Fixes @@ -240,12 +245,11 @@ Target: 822 (Прп. Захарий Печерский, EMPTY) - correct saint ### Step 3: Verify Uniqueness ```bash -# Check if substring is unique on date 03-24 python3 -c " import json, re m=json.load(open('scripts/output/minei.json')) titles=[t['title'] for t in m['03-24']] -cnt=sum(1 for t in titles if 'воспоминание о чуде' in re.sub(r'\s+',' ',t['title']).lower()) +cnt=sum(1 for t in titles if 'воспоминание о чуде' in re.sub(r'\s+',' ',t).lower()) print(f'Count: {cnt}') " ``` diff --git a/.ai-specs/PROJECT_CONTEXT.md b/.ai-specs/PROJECT_CONTEXT.md index 07f4b42..d558f56 100644 --- a/.ai-specs/PROJECT_CONTEXT.md +++ b/.ai-specs/PROJECT_CONTEXT.md @@ -5,44 +5,45 @@ **Ponomar** is a Progressive Web App for Orthodox Church calendar, liturgical services, and Bible readings. - **Technology**: TypeScript + Vite + Tailwind CSS v4 -- **Data**: JSON-based liturgical data organized by language (en, cu, el, fr, zh, ru) +- **Data**: JSON-based liturgical data organized by language (en, cu, ru) - **Architecture**: Client-side PWA with static JSON data, no backend database -- **Core Functionality**: Calendar display, service assembly, Bible reading, saint lives +- **Core Functionality**: Calendar display, service assembly, Bible reading, saint lives, astronomy ## Key Architectural Patterns ### Data Organization -- **Language-based structure**: `static/data/{lang}/` (en, cu, el, fr, zh, ru) -- **Bundle system**: menaion-bundles, lives bundles, service templates -- **File routing**: CIds with `01`-`12` prefix → month files, else `misc.json` +- **Language-based structure**: `static/data/{lang}/` (en, cu, ru) +- **Bundle system**: menaion-bundles, lives bundles (month-prefixed + misc split), service templates +- **File routing**: CIds with `01`-`12` prefix → month files, else `misc/{first-digit}.json` ### Core Scripts -- `convert-all.ts` — Master conversion script -- `enrich-lives.ts` — Fuzzy matching and enrichment pipeline -- `validate-data.ts` — Main validator (checks 274 files) -- `convert-minei.ts` — Chetyi-Minei EPUB to JSON conversion -- `migrate-data.ts` — Data normalization +- `convert-services.ts` — Service XML to JSON conversion +- `enrich-lives.ts` — Fuzzy matching and enrichment pipeline (Chetyi-Minei + Bulgakov) +- `validate-data.ts` — Main validator +- `convert-bulgakov.ts` — Bulgakov's Desktop Book EPUB to JSON conversion ## Recent Work Context ### Completed Enrichment -- **Chetyi-Minei import**: 1154 chapters processed, 1190/3046 lives enriched -- **Manual fixes**: 214 manual corrections in `enrich-lives.ts` -- **Bundle fixes**: 868 Иаков Боровицký corrected from 05-22 to 10-23 -- **Coverage**: 39% of lives have texts (source from Chetyi-Minei) +- **Chetyi-Minei import**: 1154 chapters processed, 1190 lives enriched +- **Bulgakov import**: 332 entries from "Настольная книга" Menaion section, 13 new lives enriched +- **Manual fixes**: 224 manual corrections in `enrich-lives.ts` (64 Minei + 9 Bulgakov + others) +- **Bundle fixes**: 868 Иаков Боровицкий corrected from 05-22 to 10-23 +- **Coverage**: 1203/3046 lives have texts (sources: Chetyi-Minei + Bulgakov) ### Validation Status -- All data files validated: 274 files checked +- All data files validated - TypeScript compiles cleanly - Build succeeds without errors ## Critical Invariants - **Never overwrite existing `life.text`** — enrichment only fills empty entries +- **Bulgakov is secondary source** — fills gaps, never overrides Minei - **File routing is strict**: CId prefix determines file location - **Bundle CIds must exist**: Menaion CIds require corresponding lives entries - **Static data is tracked**: `static/data/` in git (except `scripts/output/minei.json`) -- **Validation must pass**: Changes must pass `validate-data.ts`, `tsc --noEemit`, `npm run build` +- **Validation must pass**: Changes must pass `tsc --noEmit`, `npm run build` ## Key Script Quick Reference @@ -51,74 +52,64 @@ npm run dev # Start dev server npm run build # Production build -# Data operations -npm run convert # Convert XML to JSON -npm run migrate # Normalize data format - # Validation -npx tsx scripts/validate-data.ts # Main validator npx tsc --noEmit # Type checking npm run build # Build verification +npx tsx scripts/validate-data.ts # Main validator -# Enrichment -npx tsx scripts/enrich-lives.ts # Dry run enrichment -npx tsx scripts/enrich-lives.ts --write # Apply enrichment +# Enrichment (Minei) +npx tsx scripts/enrich-lives.ts # Dry run +npx tsx scripts/enrich-lives.ts --write # Apply + +# Enrichment (Bulgakov) +npx tsx scripts/enrich-lives.ts --bulgakov # Dry run +npx tsx scripts/enrich-lives.ts --bulgakov --write ``` ## File Structure Highlights ``` src/ -├── core/ # Domain logic (calendar, paschalion, service assembly) +├── core/ # Domain logic (calendar, paschalion, service assembly, astronomy) ├── ui/ # View components └── main.ts # Entry point scripts/ -├── convert-*.ts # XML→JSON conversion pipeline -├── validate-*.ts # Comprehensive validation suite -└── enrich-lives.ts # Enrichment with manual fixes +├── convert-*.ts # XML/EPUB → JSON conversion pipeline +├── validate-*.ts # Comprehensive validation suite +└── enrich-lives.ts # Enrichment with manual fixes (Minei + Bulgakov) static/data/ ├── cu/ # Church Slavonic (authoritative calendar) ├── ru/ # Russian (enriched lives) -├── en/ # English -└── shared/ # Cross-language resources +├── en/ # English (enriched lives + service texts) +└── shared/ # Cross-language resources (calendar, lives index, parimii) ``` ## Important Notes ### Pipeline Dependencies - **Chetyi-Minei source**: EPUB conversion requires `convert-minei.ts` -- **Enrichment workflow**: Requires minei.json output + menaion-bundle.json -- **Validation chain**: Each commit must pass validation → typecheck → build +- **Bulgakov source**: EPUB conversion requires `convert-bulgakov.ts` +- **Enrichment workflow**: Requires minei.json + bulgakov.json + menaion-bundle.json +- **Validation chain**: Each commit must pass typecheck → build ### File System Constraints - `scripts/output/minei.json` is gitignored (33MB source file) +- `scripts/output/bulgakov.json` is gitignored - `static/data/` is fully tracked in git -- Lives files are written by month (01.json-12.json) or misc.json - -## Common Tools Reference - -- **Search**: `npx tsx scripts/tools/search-texts.ts ` -- **Stats**: `npx tsx scripts/tools/stats.ts [--lang en]` -- **Validate**: `npx tsx scripts/validate-data.ts` - -## Documentation Reference - -- **DATA_FORMAT.md** — Detailed technical specifications (English) -- **DATA_RUS.md** — Complete documentation in Russian -- **README.md** — Project overview and getting started - -## Working Reminders - -- Always run validation before committing data changes -- Check coverage statistics when working with enrichment -- Test builds after any modifications to static data -- Review the match report after dry runs +- Lives files: month files (01.json-12.json) + misc split (misc/0.json-misc/9.json) ## Current State - Total CIds: 3046 -- Lives with text: 1190 (enriched from Chetyi-Minei) -- Unmatched minei chapters: 27 (floor: narratives, no-target, already-filled) -- Latest enrichment commits: `ec3f593` (3 more lives), `08950cd` (57 more lives), `381a494` (121 more lives), `7dde216` (baseline enrichment) \ No newline at end of file +- Lives with text: 1203 (Chetyi-Minei + Bulgakov) +- Unmatched Minei: 27 (floor: narratives, no-target, already-filled) +- Unmatched Bulgakov: 121 (all no recoverable targets) +- Latest enrichment commits: `e32d787` (Bulgakov), `c342a5b` (orthography), `08950cd` (64 fixes) + +## Documentation Reference + +- **DATA_EN.md** — Detailed technical specifications (English) +- **DATA_RUS.md** — Complete documentation in Russian +- **README.md** — Project overview and getting started \ No newline at end of file diff --git a/.ai-specs/WORKING_POLICIES.md b/.ai-specs/WORKING_POLICIES.md index 6b8104b..bc6ba6e 100644 --- a/.ai-specs/WORKING_POLICIES.md +++ b/.ai-specs/WORKING_POLICIES.md @@ -6,10 +6,11 @@ - **CRITICAL**: Never overwrite existing `life.text` fields in lives entries - Enrichment pipeline only fills empty entries - If a target already has content, classify as `already-filled` and skip +- **Bulgakov is secondary source**: fills gaps only, never overrides Minei content ### File Modification Permissions - **Allowed**: Adding new life.text to empty entries -- **Allowed**: Adding manual fixes to `enrich-lives.ts` MANUAl_FIXES +- **Allowed**: Adding manual fixes to `enrich-lives.ts` MANUAL_FIXES - **Allowed**: Correcting menaion-bundle.json for CId date corrections - **Forbidden**: Modifying existing life.text content - **Forbidden**: Deleting or renaming CIds without proper validation @@ -21,17 +22,17 @@ All changes must pass all three checks: ```bash -# 1. Data validation -npx tsx scripts/validate-data.ts -# Expected: "All files valid!" or specific error count - -# 2. Type safety +# 1. Type safety npx tsc --noEmit # Expected: No TypeScript errors -# 3. Build verification +# 2. Build verification npm run build # Expected: Successful build with reasonable bundle sizes + +# 3. Data validation +npx tsx scripts/validate-data.ts +# Expected: "All files valid!" or specific error count ``` ### When to Stop @@ -63,14 +64,13 @@ update [script name]: [brief description] - Be specific about what changed - Reference specific CIds or dates when applicable - Keep messages concise but descriptive -- Reference recent commits for style ## File Change Guidelines ### Static Data Files - **Tracked**: `static/data/` directory (all JSON files tracked) -- **Not tracked**: `scripts/output/minei.json` (33MB generated file) -- **Validate**: Always run validation after data changes +- **Not tracked**: `scripts/output/minei.json`, `scripts/output/bulgakov.json` +- **Validate**: Always run typecheck and build after data changes ### Script Files - Maintain existing code patterns @@ -119,7 +119,7 @@ update [script name]: [brief description] ## Documentation Requirements ### When Modifying Data Schemas -- Update relevant documentation files (DATA_FORMAT.md, DATA_RUS.md) +- Update relevant documentation files (DATA_EN.md, DATA_RUS.md) - Note any breaking changes or invariants - Update project README if structural changes @@ -152,7 +152,7 @@ update [script name]: [brief description] ### Before Starting 1. Review existing MANUAL_FIXES in `enrich-lives.ts` 2. Understand the current coverage status -3. Identify which dates/cases you're targeting +3. Identify which source (Minei or Bulgakov) and dates you're targeting ### During Work 1. Use dry runs first: `npx tsx scripts/enrich-lives.ts` @@ -170,8 +170,8 @@ update [script name]: [brief description] ## Quality Assurance Standards ### Coverage Tracking -- Monitor coverage statistics: `1190/3046 lives with text` -- Track unmatched count: currently 27 (floor for this data source) +- Monitor coverage statistics: `1203/3046 lives with text` +- Track unmatched counts: 27 Minei + 121 Bulgakov (floor) - Report changes in coverage when adding MANUAL_FIXES ### Content Verification @@ -198,12 +198,18 @@ const MANUAL_FIXES: Record = { ### Running Enrichment ```bash -# Dry run +# Minei dry run npx tsx scripts/enrich-lives.ts -# Write changes +# Minei write npx tsx scripts/enrich-lives.ts --write +# Bulgakov dry run +npx tsx scripts/enrich-lives.ts --bulgakov + +# Bulgakov write +npx tsx scripts/enrich-lives.ts --bulgakov --write + # Single month npx tsx scripts/enrich-lives.ts --month 01 npx tsx scripts/enrich-lives.ts --write --month 01 @@ -224,7 +230,7 @@ npx tsx scripts/validate-triodion.ts python3 -c " import json,glob,os total=has=0 -for fp in glob.glob('static/data/ru/lives/*.json'): +for fp in sorted(glob.glob('static/data/ru/lives/*.json') + glob.glob('static/data/ru/lives/misc/*.json')): data=json.load(open(fp)) for cid,entry in data.items(): total+=1 diff --git a/DATA_EN.md b/DATA_EN.md index adec06b..ae70fe4 100644 --- a/DATA_EN.md +++ b/DATA_EN.md @@ -45,7 +45,9 @@ static/data/ │ ├── en-menaion-bundle.json # Language-specific overrides │ ├── lives/ # Saint lives by month │ │ ├── 01.json through 12.json # Month-prefixed CIds (01XX-12XX) -│ │ └── misc.json # Non-month-prefixed CIds +│ │ ├── misc/ # Non-month-prefixed CIds +│ │ │ ├── 1.json # CIds starting with digit 1 +│ │ │ ├── 2.json │ ├── menaion/ # Daily menaion entries (366 days) │ ├── calendar/ # Calendar metadata │ └── services/ # Service templates @@ -140,13 +142,13 @@ Lives bundles are routed based on CId prefix: | `02XX-02ZZ` | `02.json` | `023456`, `027890` | | ... | ... | ... | | `12XX-12ZZ` | `12.json` | `123456`, `127890` | -| Other CIds | `misc.json` | `772426`, `373`, `2396` | +| Other CIds | `misc/{n}.json` | `772426→misc/7.json`, `373→misc/3.json` | **Examples:** - `010101` → `01.json` - `1253` → `12.json` -- `772426` → `misc.json` -- `373` → `misc.json` +- `772426` → `misc/7.json` +- `373` → `misc/3.json` ### Bundle File Naming - Main bundle: `menaion-bundle.json` (per language) @@ -192,8 +194,8 @@ Lives bundles are routed based on CId prefix: } ``` -**Coverage**: 3046 total CIds, 1190 with life.text enriched -**Routing**: Prefix-based (01.json-12.json + misc.json) +**Coverage**: 3046 total CIds, 1203 with life.text enriched +**Routing**: Prefix-based (01.json-12.json + misc/{digit}.json) **Usage**: Lives display on saint detail pages ### Calendar Data @@ -275,7 +277,7 @@ Converts XML data from Java Ponomar sources to JSON format. **`convert-lives.ts`** — Saint lives XML → JSON conversion - Input: `ponomar/Ponomar/languages/{lang}/xml/lives/*.xml` -- Output: `{lang}/lives/{01.json|misc.json}` bundles +- Output: `{lang}/lives/{01.json|misc/{digit}.json}` bundles - Features: - XML parsing with attribute extraction - Name case normalization @@ -474,7 +476,7 @@ Key format: `{date}|{distinctive substring}` → CId function bundleFileForCid(cid: string): string { return /^(0[1-9]|1[0-2])\d+/.test(cid) ? `${cid.substring(0, 2)}.json` - : 'misc.json'; + : `misc/${cid.charAt(0)}.json`; } ``` @@ -536,7 +538,7 @@ git commit -m "enrich 121 more ru lives with Chetyi-Minei texts" ```bash python3 -c " import json, re -misc = json.load(open('static/data/ru/lives/misc.json')) +misc = json.load(open('static/data/ru/lives/misc/4.json')) for cid in ['493', '1674', '257401']: # Known collision targets text = misc[cid]['life']['text'] headings = re.findall(r'([^<]+)', text) @@ -679,9 +681,9 @@ npm run dev **Problem:** Ambiguous routing for CIds at boundaries **Edge Cases:** -- `00` prefixes → misc.json (not 00.json) -- `99` prefixes → misc.json (not 09.json) -- Non-numeric CIds → misc.json +- `00` prefixes → misc/{first-digit}.json (not 00.json) +- `99` prefixes → misc/{first-digit}.json (not 09.json) +- Non-numeric CIds → misc/{first-digit}.json **Solution:** ```typescript @@ -770,7 +772,7 @@ scripts/output/lives-match-report.json # Match report **Current Status:** - Total CIds: 3046 -- Lives with text: 1190 (39%) +- Lives with text: 1203 (39%) - Empty lives: 1856 (61%) **Enrichment Progress:** diff --git a/DATA_RUS.md b/DATA_RUS.md index 2ad9773..8765664 100644 --- a/DATA_RUS.md +++ b/DATA_RUS.md @@ -45,7 +45,9 @@ static/data/ │ ├── en-menaion-bundle.json # Переопределения для конкретного языка │ ├── lives/ # Жития святых по месяцам │ │ ├── 01.json through 12.json # Файлы месяцев с CId по префиксу (01XX-12XX) -│ │ └── misc.json # CId без месячного префикса +│ │ ├── misc/ # CId без месячного префикса +│ │ │ ├── 1.json # CId, начинающиеся с цифры 1 +│ │ │ ├── 2.json │ ├── menaion/ # Ежедневные записи минеи (366 дней) │ ├── calendar/ # Метаданные календаря │ └── services/ # Шаблоны богослужений @@ -140,13 +142,13 @@ static/data/ | `02XX-02ZZ` | `02.json` | `023456`, `027890` | | ... | ... | ... | | `12XX-12ZZ` | `12.json` | `123456`, `127890` | -| Прочие CId | `misc.json` | `772426`, `373`, `2396` | +| Прочие CId | `misc/{n}.json` | `772426→misc/7.json`, `373→misc/3.json` | **Примеры:** - `010101` → `01.json` - `1253` → `12.json` -- `772426` → `misc.json` -- `373` → `misc.json` +- `772426` → `misc/7.json` +- `373` → `misc/3.json` ### Нейминг файлов пачек - Основная пачка: `menaion-bundle.json` (для каждого языка) @@ -192,8 +194,8 @@ static/data/ } ``` -**Покрытие**: 3046 CId всего, 1190 с обогащённым life.text -**Маршрутизация**: по префиксу (01.json-12.json + misc.json) +**Покрытие**: 3046 CId всего, 1203 с обогащённым life.text +**Маршрутизация**: по префиксу (01.json-12.json + misc/{digit}.json) **Использование**: Отображение житий на страницах подробностей о святых ### Данные календаря @@ -275,7 +277,7 @@ npm run convert **`convert-lives.ts`** — Конвертация житий святых из XML в JSON - Вход: `ponomar/Ponomar/languages/{lang}/xml/lives/*.xml` -- Выход: `{lang}/lives/{01.json|misc.json}` пачки +- Выход: `{lang}/lives/{01.json|misc/{digit}.json}` пачки - Особенности: - Парсинг XML с извлечением атрибутов - Нормализация падежей имён diff --git a/README.md b/README.md index 8d6d518..07a60d5 100644 --- a/README.md +++ b/README.md @@ -11,17 +11,19 @@ Derived from the original [Java Ponomar application](https://github.com/typiconm - **Calendar** — View month grids with liturgical days, fasting periods, ranks, and commemorations. Supports Julian calendar with Gregorian equivalents. - **Service Reader** — Assembles liturgical services (Hours, Kathismata) from structured templates with conditional logic. - **Bible Reader** — Read scripture across 13 versions (Church Slavonic, Russian Synodal, KJV, Brenton, Vulgate, etc.). Per-version reading position persists across sessions. +- **Saint Lives** — 1203/3046 lives enriched with texts from Chetyi-Minei and Bulgakov's Desktop Book. - **Multi-language UI** — English, Russian, Church Slavonic (with dedicated Slavonic fonts). -- **PWA** — Installable on desktop and mobile devices. -- **Navigation** — 3 top-level items (Calendar, Library, Settings) with Library dropdown: Bible, Service (Horologion, Sbornik, Prayer Rule, Canons & Akathists, Parimii, Paraclete, Irmologion), Feasts (Menaion, Lenten Triodion). +- **Color Themes** — Switch between Default, Dark, Sepia, and High Contrast themes in settings. +- **PWA + Offline** — Installable on desktop and mobile. Preload data (lives, calendar, Bible translations) for offline use via IndexedDB cache. - **Astronomy** — Sunrise/sunset times and ecclesiastical moon phase (day, trend, illumination) displayed in the calendar day detail panel. Location configurable in settings. +- **Navigation** — 3 top-level items (Calendar, Library, Settings) with Library dropdown: Bible, Service (Horologion, Sbornik, Prayer Rule, Canons & Akathists, Parimii, Paraclete, Irmologion), Feasts (Menaion, Lenten Triodion). ## Tech Stack - [TypeScript](https://www.typescriptlang.org/) — fully typed - [Vite](https://vitejs.dev/) — build tool - [Tailwind CSS v4](https://tailwindcss.com/) — utility-first CSS -- [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) — legacy XML conversion (manual only) +- [vite-plugin-pwa](https://vite-pwa-org.netlify.app/) — PWA + service worker ## Getting Started @@ -32,13 +34,6 @@ npm run dev Data files in `static/data/` are tracked in git. The only gitignored output is `scripts/output/minei.json` (33MB Chetyi-Minei source). -```bash -npm run convert # re-convert all data from XML -npm run migrate # normalize to current format -``` - -Set `PONOMAR_JAVA_HOME` to point at a local clone of the Java Ponomar repository (defaults to `../ponomar`). - ```bash npm run build # production build npm run preview # preview production build @@ -47,7 +42,9 @@ npm run preview # preview production build ## QA / Validation ```bash -npx tsx scripts/validate-data.ts # Main validator (274 files checked) +npx tsc --noEmit # TypeScript type checking +npm run build # Build verification +npx tsx scripts/validate-data.ts # Main data validator npx tsx scripts/validate-i18n.ts # i18n completeness (en/ru/cu) npx tsx scripts/validate-tabs.ts # service tab/template consistency npx tsx scripts/validate-menaion.ts # menaion data validation @@ -66,9 +63,9 @@ npx tsx scripts/assembler-test.ts # service assembler integration tests - **Menaion (daily)** — 367 daily entries (one per Julian calendar day) - **Menaion (feasts)** — 34 fixed feasts with dedicated services - **Canons** — Paraclete (weekday, 8 tones × 6 days = 48), Octoechos (Sunday, 8 tones), Great Canon -- **Services** — 78 data-backed services with full.json (Pascha, Paralytic, Sergius, Pokrov, etc.; Var/I-prefix + bare P-prefix varNodes now intercepted) +- **Services** — 78 data-backed services with full.json (Pascha, Paralytic, Sergius, Pokrov, etc.) - **Horologion / Sbornik / Prayer Rule / Akathists / Parimii / Paraclete / Irmologion** — daily-cycle collections -- **Lives** — Saint lives (en/cu/ru, 1190/3046 with life.text enriched from Chetyi-Minei) +- **Lives** — Saint lives (en/cu/ru, 1203/3046 with life.text enriched from Chetyi-Minei and Bulgakov) ## Project Structure @@ -79,7 +76,7 @@ src/ main.ts — Entry point scripts/ — Data conversion, migration, validation, enrichment, and CLI tools static/ - data/ — Liturgical data as JSON (tracked in git, generated from Java XML via npm run convert) + data/ — Liturgical data as JSON (tracked in git) fonts/ — Church Slavonic fonts icons/ — PWA icons ``` @@ -90,11 +87,5 @@ Same as the original Java project. ## Documentation -- **[DATA_FORMAT.md](DATA_FORMAT.md)** — Comprehensive static data format documentation in English -- **[DATA_RUS.md](DATA_RUS.md)** — Complete Russian documentation of data format and enrichment process - -Covers: -- Static data models, routing rules, and component breakdown -- Complete tool inventory for conversion, migration, and validation -- Chetyi-Minei enrichment pipeline and quality assurance workflow -- Quick reference for all available NPM scripts and commands +- **[DATA_EN.md](DATA_EN.md)** — Comprehensive static data format documentation in English +- **[DATA_RUS.md](DATA_RUS.md)** — Complete Russian documentation of data format and enrichment process \ No newline at end of file diff --git a/README_RUS.md b/README_RUS.md index 0c396e0..b9b9e93 100644 --- a/README_RUS.md +++ b/README_RUS.md @@ -8,11 +8,14 @@ ## Возможности -- **Calendar** — Просмотр сеток месяцев с литургическими днями, периодами поста, чинами и празднованиями. Поддерживает юлианский календарь с григорианскими эквивалентами. +- **Календарь** — Просмотр сеток месяцев с литургическими днями, периодами поста, чинами и празднованиями. Поддерживает юлианский календарь с григорианскими эквивалентами. - **Service Reader** — Сборка богослужебных текстов (часов, кафизм) из структурированных шаблонов с условной логикой. - **Bible Reader** — Чтение Писания на 13 языках (церковнославянский, русский синодальный, KJV, Брентон, Вульгата и др.). Позиция чтения для каждой версии сохраняется между сессиями. +- **Жития святых** — 1203/3046 житий обогащены текстами из Четьи-Миней и Настольной книги Булгакова. - **Многоязычный интерфейс** — Английский, русский, церковнославянский (со специальными шрифтами для церковнославянского). -- **PWA** — Устанавливается на настольные и мобильные устройства. +- **Цветовые схемы** — Переключение между Стандартной, Тёмной, Сепия и Высокой контрастностью в настройках. +- **PWA + Офлайн** — Устанавливается на устройства. Предзагрузка данных (жития, календарь, переводы Библии) для офлайн-доступа через IndexedDB. +- **Астрономия** — Время восхода/заката и церковная фаза луны (день, тренд, освещённость) в панели дня календаря. Местоположение настраивается. - **Навигация** — 3 элемента верхнего уровня (Calendar, Library, Settings) с выпадающим меню Library: Библия, Service (Часослов, Сборник, Молитвенное правило, Каноны и акафисты, Паримии, Пареклит, Ирмологий), Праздники (Минея, Постная Триодь). ## Технологический стек @@ -20,7 +23,7 @@ - [TypeScript](https://www.typescriptlang.org/) — полностью типизированный - [Vite](https://vitejs.dev/) — инструмент сборки - [Tailwind CSS v4](https://tailwindcss.com/) — CSS с утилитарными классами -- [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) — конвертация устаревшего XML (только ручной режим) +- [vite-plugin-pwa](https://vite-pwa-org.netlify.app/) — PWA + service worker ## Начало работы @@ -31,15 +34,6 @@ npm run dev Файлы данных в `static/data/` отслеживаются в git. Единственный игнорируемый файл вывода — `scripts/output/minei.json` (33МБ источник Четьи-Миней). -Для повторного создания из XML-источников Java Ponomar запустите: - -```bash -npm run convert # повторная конвертация всех данных из XML -npm run migrate # нормализация к текущему формату -``` - -Установите `PONOMAR_JAVA_HOME` для указания на локальный клон репозитория Java Ponomar (по умолчанию `../ponomar`). - ```bash npm run build # продуктивная сборка npm run preview # предпросмотр продуктивной сборки @@ -48,7 +42,9 @@ npm run preview # предпросмотр продуктивной сборк ## QA / Валидация ```bash -npx tsx scripts/validate-data.ts # Главный валидатор (274 файлов проверено) +npx tsc --noEmit # Проверка типов TypeScript +npm run build # Проверка сборки +npx tsx scripts/validate-data.ts # Главный валидатор npx tsx scripts/validate-i18n.ts # Полнота i18n (en/ru/cu) npx tsx scripts/validate-tabs.ts # Согласованность вкладок/шаблонов богослужений npx tsx scripts/validate-menaion.ts # Валидация данных минеи @@ -67,9 +63,9 @@ npx tsx scripts/assembler-test.ts # Интеграционные тест - **Menaion (daily)** — 367 ежедневных записей (одна на каждый день юлианского календаря) - **Menaion (feasts)** — 34 неподвижных праздника с отдельными богослужениями - **Canons** — Пареклит (будние дни, 8 гласов × 6 дней = 48), Осмогласник (воскресенье, 8 гласов), Великий канон -- **Services** — 78 богослужений с полными данными (Пасха, расслабленный, Сергий, Покров и др.; Var/I-префикс + простые P-узлы varNodes теперь перехватываются) +- **Services** — 78 богослужений с полными данными - **Horologion / Sbornik / Prayer Rule / Akathists / Parimii / Paraclete / Irmologion** — коллекции суточного круга -- **Lives** — Жития святых (en/cu/ru, 1190/3046 с обогащённым life.text из Четьи-Миней) +- **Lives** — Жития святых (en/cu/ru, 1203/3046 с обогащённым life.text из Четьи-Миней и Булгакова) ## Структура проекта @@ -91,11 +87,5 @@ static/ ## Документация -- **[DATA_FORMAT.md](DATA_FORMAT.md)** — Комплексная документация формата статических данных на английском языке -- **[DATA_RUS.md](DATA_RUS.md)** — Полная русская документация формата данных и процесса обогащения - -Охватывает: -- Модели статических данных, правила маршрутизации и разбор компонентов -- Полный набор инструментов для конвертации, миграции и валидации -- Конвейер обогащения Четьи-Миней и рабочий процесс контроля качества -- Краткий справочник по всем доступным npm-скриптам и командам \ No newline at end of file +- **[DATA_EN.md](DATA_EN.md)** — Комплексная документация формата статических данных на английском языке +- **[DATA_RUS.md](DATA_RUS.md)** — Полная русская документация формата данных и процесса обогащения \ No newline at end of file diff --git a/src/core/i18n.ts b/src/core/i18n.ts index 6b22c17..02096a0 100644 --- a/src/core/i18n.ts +++ b/src/core/i18n.ts @@ -472,6 +472,11 @@ export interface Translations { fontPreviewNoteInactive: string; fontSize: string; systemFont: string; + theme: string; + themeDefault: string; + themeDark: string; + themeSepia: string; + themeHC: string; bibleSettings: string; defaultTranslation: string; showVerseNumbers: string; @@ -1022,6 +1027,11 @@ const translations: Record = { fontPreviewNoteInactive: 'System font active — rank icons will use Ponomar font regardless', fontSize: 'Liturgical Text Size: ', systemFont: 'System Default', + theme: 'Color Scheme', + themeDefault: 'Default', + themeDark: 'Dark', + themeSepia: 'Sepia', + themeHC: 'High Contrast', bibleSettings: 'Bible', defaultTranslation: 'Default Translation', showVerseNumbers: 'Show verse numbers', @@ -1570,6 +1580,11 @@ const translations: Record = { fontPreviewNoteInactive: 'Системный шрифт активен — иконы чинов будут использовать Понома́рь в любом случае', fontSize: 'Размер литургического текста: ', systemFont: 'Системный шрифт', + theme: 'Цветовая схема', + themeDefault: 'Стандартная', + themeDark: 'Тёмная', + themeSepia: 'Сепия', + themeHC: 'Высокая контрастность', bibleSettings: 'Библия', defaultTranslation: 'Перевод по умолчанию', showVerseNumbers: 'Показывать номера стихов', @@ -2118,6 +2133,11 @@ const translations: Record = { fontPreviewNoteInactive: 'Системный шрифтъ активенъ — иконы чинѣвъ будутъ использовать Понома́рь въ любомъ случаи', fontSize: 'Размеръ литургическаго текста: ', systemFont: 'Системный шрифтъ', + theme: 'Цвѣтова́ѧ схе́ма', + themeDefault: 'Станда́ртнаѧ', + themeDark: 'Тёмнаѧ', + themeSepia: 'Се́пїѧ', + themeHC: 'Высо́каѧ контра́стность', bibleSettings: 'Библия', defaultTranslation: 'Переводъ по умолчанiю', showVerseNumbers: 'Показывать номера стиховъ', diff --git a/src/styles/main.css b/src/styles/main.css index 4796612..5eaf008 100644 --- a/src/styles/main.css +++ b/src/styles/main.css @@ -1,17 +1,97 @@ @import "tailwindcss"; @theme { - --color-parchment: #f5f0e8; - --color-parchment-dark: #e8dfd0; - --color-navy: #1a1a2e; - --color-navy-light: #2a2a4e; - --color-gold: #c5a55a; - --color-gold-light: #d4b96e; - --color-red: #8b0000; - --color-red-light: #a52a2a; - --color-purple: #4a0e4e; - --color-green: #2d5a27; - --color-blue: #1a3a6e; + --color-parchment: var(--clr-parchment); + --color-parchment-dark: var(--clr-parchment-dark); + --color-navy: var(--clr-navy); + --color-navy-light: var(--clr-navy-light); + --color-gold: var(--clr-gold); + --color-gold-light: var(--clr-gold-light); + --color-red: var(--clr-red); + --color-red-light: var(--clr-red-light); + --color-purple: var(--clr-purple); + --color-green: var(--clr-green); + --color-blue: var(--clr-blue); + --color-surface: var(--clr-surface); + --color-header-bg: var(--clr-header-bg); + --color-header-text: var(--clr-header-text); + --color-dropdown-bg: var(--clr-dropdown-bg); +} + +:root, .theme-default { + --clr-parchment: #f5f0e8; + --clr-parchment-dark: #e8dfd0; + --clr-navy: #1a1a2e; + --clr-navy-light: #2a2a4e; + --clr-gold: #c5a55a; + --clr-gold-light: #d4b96e; + --clr-red: #8b0000; + --clr-red-light: #a52a2a; + --clr-purple: #4a0e4e; + --clr-green: #2d5a27; + --clr-blue: #1a3a6e; + --clr-surface: #ffffff; + --clr-header-bg: #1a1a2e; + --clr-header-text: #f5f0e8; + --clr-dropdown-bg: #2a2a4e; + --clr-browser-chrome: #1a1a2e; +} + +.theme-dark { + --clr-parchment: #121212; + --clr-parchment-dark: #0a0a1a; + --clr-navy: #e0e0e0; + --clr-navy-light: #a0a0b0; + --clr-gold: #c5a55a; + --clr-gold-light: #d4b96e; + --clr-red: #ff6b6b; + --clr-red-light: #ff4444; + --clr-purple: #b07cc6; + --clr-green: #6bcf7f; + --clr-blue: #6ba3d6; + --clr-surface: #2a2a3e; + --clr-header-bg: #1a1a2e; + --clr-header-text: #f5f0e8; + --clr-dropdown-bg: #0e0e1a; + --clr-browser-chrome: #1a1a2e; +} + +.theme-sepia { + --clr-parchment: #f4ecd8; + --clr-parchment-dark: #e8dcc8; + --clr-navy: #3b2f2f; + --clr-navy-light: #5c4a4a; + --clr-gold: #8b7355; + --clr-gold-light: #a08868; + --clr-red: #8b3a3a; + --clr-red-light: #a54a4a; + --clr-purple: #4a0e4e; + --clr-green: #2d5a27; + --clr-blue: #1a3a6e; + --clr-surface: #faf3e6; + --clr-header-bg: #3b2f2f; + --clr-header-text: #f4ecd8; + --clr-dropdown-bg: #5c4a4a; + --clr-browser-chrome: #3b2f2f; +} + +.theme-hc { + --clr-parchment: #ffffff; + --clr-parchment-dark: #e6e6e6; + --clr-navy: #000000; + --clr-navy-light: #222222; + --clr-gold: #666666; + --clr-gold-light: #888888; + --clr-red: #000000; + --clr-red-light: #333333; + --clr-purple: #555555; + --clr-green: #555555; + --clr-blue: #555555; + --clr-surface: #ffffff; + --clr-header-bg: #000000; + --clr-header-text: #ffffff; + --clr-dropdown-bg: #222222; + --clr-browser-chrome: #000000; } @font-face { @@ -166,7 +246,7 @@ nav details.nav-group > div { z-index: 50; min-width: 14rem; border-radius: 0.5rem; - background-color: var(--color-navy-light); + background-color: var(--color-dropdown-bg); box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); padding: 0.5rem; display: flex; diff --git a/src/ui/app.ts b/src/ui/app.ts index 793527c..3c8626f 100644 --- a/src/ui/app.ts +++ b/src/ui/app.ts @@ -25,6 +25,15 @@ export class App { this.currentDate = JDate.today(); this.settings = loadSettings(); document.documentElement.style.setProperty('--liturgical-font-size', this.settings.fontSize + 'px'); + this.applyTheme(); + } + + private applyTheme() { + document.documentElement.className = 'theme-' + (this.settings.theme || 'default'); + const chromeColor = getComputedStyle(document.documentElement).getPropertyValue('--clr-browser-chrome').trim(); + if (chromeColor) { + document.querySelector('meta[name="theme-color"]')?.setAttribute('content', chromeColor); + } } init() { @@ -115,10 +124,11 @@ export class App { private render() { this.settings = loadSettings(); + this.applyTheme(); const t = getTranslations(this.settings.language as LanguageCode); this.container.innerHTML = `
-
+