Skip to content

Latest commit

 

History

History
158 lines (132 loc) · 8.03 KB

File metadata and controls

158 lines (132 loc) · 8.03 KB

Architecture

A description of the app as it is shipped. To learn the astronomy and calendar maths from scratch, read the tutorial in guide/ — that is the primary teaching source. For a terse reference of the formulas and their provenance see METHODOLOGY.md (panchanga) and METHODOLOGY_JYOTISH.md (kundli).

What it is

A static, installable PWA — Svelte 5 (runes) + Vite + TypeScript. It computes a daily panchanga, a lunar month calendar, a festival list, a birth chart (Kundli) with guna-milan matching, a local Sky view, and an interactive Learn guide — entirely on-device, for any bundled city or GPS point, in English/हिन्दी with Latin or Devanagari numerals.

Two runtime dependencies only: astronomy-engine (ephemeris) and dexie (IndexedDB). No backend, no telemetry, no remote fonts. AGPL-3.0.

Principles

  • Pure compute, isolated I/O. The calculation layers are pure functions of (instant, location, options). Result caching and persistence live in the storage layer, never inside the math. (The compute layer does transparently memoize a few pure, hot sub-computations — sunrise/moonrise and new-moon instants — which is referentially invisible: identical inputs give identical outputs, distinct from the storage result cache.)
  • One swappable astronomy backend. Everything reads positions through lib/astro; swapping ephemeris.ts + ayanamsa.ts would change the engine without touching anything above.
  • Sidereal by default, sunrise-anchored civil days, ayanamsa applied once at the boundary — see Conventions.
  • Data-driven i18n, not branch-driven: a language is a table of strings, not a fork in the code.
  • Accuracy honesty: validate against Drik or label "preview"; never widen a tolerance to hide a disagreement (see Accuracy).

Layers

lib/astro/        julian · ephemeris (Sun/Moon/planets/nodes) · ayanamsa ·
                  sunrise · altaz · angle                     ← swappable backend
  ↓                                   ↓
lib/panchanga/                    lib/jyotish/
  tithi nakshatra yoga karana       grahas · lagna · chart (+ houses) ·
  vara masa samvat ritu ayana       dasha (Vimshottari) · divisional (navamsa) ·
  muhurta moon-phase tiebreakers    matching (ashtakoota) · sky-math · names
  festivals/
  ↓                                   ↓
routes/ + components/   Day Month Festivals Kundli Sky Learn Settings
lib/state/  (runes)     preferences · clock · jyotish-draft · sw-update
lib/storage/ (Dexie)    db · cache · birth-profiles · saved-locations · preferences
lib/i18n/ + lib/format/ en/hi + names + transliteration · numerals · time
lib/location/           cities (curated metros + diaspora) · GPS

lib/astro — the backend seam

julian.ts (instants, civil-midnight fixpoint that survives DST/extreme offsets), ephemeris.ts (the adapter over astronomy-engine: Sun, Moon, the five visible planets, true node, obliquity, sidereal time — EQJ vectors rotated to ecliptic-of-date), ayanamsa.ts (IAU-2006 precession; Lahiri default + Raman, anchored to Swiss-Ephemeris realizations — KP/Yukteshwar/True-Chitra were dropped as near-duplicates, see METHODOLOGY_JYOTISH §3.3), sunrise.ts, altaz.ts (horizontal coords, rise/set arcs and magnitudes for the Sky dome + the tonight observation table), angle.ts.

lib/panchanga — the almanac

compute.ts exposes the pure computePanchanga(); one file per anga, plus masa (with adhik-maas), samvat, ritu, ayana, muhurta, moon-phase. festivals/ holds the rule set (pan-india.ts + rules.ts); tiebreakers.ts (one level up, in panchanga/) is the vyapini-window / tie-break engine — the single place complexity concentrates and the highest-risk file. bisect.ts is the angular-crossing root-finder that locates anga end times.

lib/jyotish — the birth chart

Pure arithmetic on top of the same astro backend: grahas.ts (sidereal graha longitudes), lagna.ts (ascendant from sidereal time), chart.ts (whole-sign houses), dasha.ts (Vimshottari from Moon nakshatra), divisional.ts (navamsa), matching.ts (ashtakoota guna-milan from two charts), names.ts, glyphs.ts, rashi-art.ts. sky-math.ts (+ the lib/sky/ data & geometry module) back the Sky and Learn views.

Presentation

Hash-routed pages in routes/ (no router dependency) render components in components/ (DayCard, MonthGrid, KundliChart, MoonPhase, BodyIcon, LocationPicker, …). Kundli/Match/Sky/Learn are lazy-loaded. The Sky and Learn views share components/sky/ (the all-sky dome, ecliptic wheel, time clock, observation table, concept primer) and lib/sky/ (their bilingual data tables + SVG geometry). festivals.worker.ts computes the year's festival list off the main thread (postMessage requires $state.snapshot() of the location — $state proxies don't structured-clone).

State & storage

lib/state/*.svelte.ts are runes stores — preferences (language, numerals, ayanamsa, node type, month system, week start, location, theme), clock, jyotish-draft, sw-update. lib/storage is Dexie (versioned schema, currently v3) with cachedPanchangas, cachedFestivals, birthProfiles, and saved-locations/preferences mirrors.

i18n & format

lib/i18n is two string tables (en.ts/hi.ts) plus names.ts/names-hi.ts for the Sanskrit name sets, driven by a per-language META table. A transliteration preference swaps grahas/rashis between Sanskrit (Mesha, Maṅgala) and Western (Aries, Mars); tithi/nakshatra/yoga/festival names stay Sanskrit in both. lib/format handles numerals (Devanagari/Latin) and time.

Routes (hash-based)

Hash Page
#/ or #/today Today (rendered by Day.svelte with today's date)
#/day/YYYY-MM-DD Day detail (same component)
#/month/YYYY-MM Month calendar
#/festivals/YYYY Festival list
#/kundli Birth chart (+ the Milan matching section)
#/sky Sky view (local-sky dome + tonight's rise/set table)
#/learn Learn guide (how the panchanga works, interactive)
#/settings Settings

Data flow

preferences (runes) → a route reads the instant + location + options → calls a pure compute*() → the storage layer memoizes the result → the component renders. Changing a preference re-runs the affected deriveds; nothing writes preferences during render. The Sky and Learn views share a time widget (SkyClock) that runs a self-throttled rAF (idle 1 Hz at rest, ~30 fps while playing) and tears it down on unmount.

Conventions

  • Sidereal longitudes everywhere; the ayanamsa is subtracted once at the boundary, never baked into the ephemeris.
  • Civil days are sunrise-anchored (vara, and the day a tithi "belongs to"); festival walks re-anchor per civil day rather than adding 86.4 M ms.
  • Defaults (storage/db.ts): purnimanta month system, true node (Rahu/Ketu), transliteration on, Lahiri ayanamsa.
  • End times come from bisect.ts angular bisection, not linear interpolation.

Build, test, deploy

  • pnpm dev (Vite, port 5173) · pnpm build (static bundle + vite-plugin-pwa service worker) · pnpm test (Vitest) · pnpm run lint — the one gate: tsc --noEmit + svelte-check (types + a11y) + ESLint (typescript-eslint + svelte).
  • Output is fully static and offline-capable; deploy anywhere that serves files.
  • Node pinned via .nvmrc and package.json (engines/volta).

Accuracy & testing

Formulas are sourced and fixture-tested (sunrise/sunset, anga end-times, a multi-year New Delhi festival audit, a multi-city smoke, polar/DST regressions). Known gaps are named, not hidden (e.g. strict moonrise/moonset coverage). The rule: anything not proven against a reference is labelled a preview, and tolerances are never loosened to manufacture agreement.


Historical note: the original 800-line pre-implementation build spec is in git history (before this rewrite) if the early design rationale is ever needed.