diff --git a/.gitignore b/.gitignore index e3a76bb..ec569e2 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ /watch/build /watch/src/c/i18n_tables.auto.c /watch/resources/img +/watch/resources/app_icon.png # Durable backup of the same real birth data, outside dist/ so a dist/ # wipe doesn't lose it — see the Makefile's `scripts` target. Never diff --git a/docs/input-output-format.md b/docs/input-output-format.md index 64e401d..4cd449a 100644 --- a/docs/input-output-format.md +++ b/docs/input-output-format.md @@ -171,7 +171,7 @@ cards rendered as an image: ### `--format json` -A single JSON object mirroring `DailyReading` exactly — `natal`, +A single JSON object mirroring `DailyReading` exactly — `date`, `natal`, `transits`, `spread` — using the **slug** accessors (`astro_body_slug()`, `astro_sign_slug()`, `astro_moon_phase_slug()`, `astro_aspect_slug()`, `tarot_card_slug()`, `tarot_position_slug()`) @@ -182,6 +182,7 @@ read. ```json { + "date": "2026-07-16", "natal": { "bodies": [ {"body": "sun", "sign": "taurus", "degree_in_sign": 23.4926, "ecliptic_longitude": 53.4926, "house": 3}, @@ -210,6 +211,13 @@ read. } ``` +- `date` is the UTC calendar date of `--date` (or the current system time + if `--date` was omitted) — the day this reading is *for*, not + necessarily the day it was generated on. `reading_generate()` stores + the full moment in `DailyReading.utc_moment`; only the date part is + serialized here since that's the only thing the rest of this format + (and the interpreter's own reports, which read this field back — see + "The interpreter" below) ever display. - `bodies` is always `NUM_BODIES` (10) entries, Sun..Pluto in `Body` enum order; `spread.positions` is always `TAROT_SPREAD_SIZE` (10) entries in `CelticCrossPosition` enum order (Present, Challenge, Crown, @@ -279,11 +287,16 @@ dist/run-interpreter.sh html de # override format/language for this run ### `--format text` A plain-text report, printed in a fixed order — - 1. the day's overall significance level (`Quiet`/`Notable`/ + 1. the day this reading is for (`Reading for: 2026-07-16`), from the + input JSON's top-level `date` field (see `--format json` above) — + not necessarily the day the report is actually being read on, if + `reading.json` was generated earlier and piped into + `interpreter-cli` later; + 2. the day's overall significance level (`Quiet`/`Notable`/ `Significant`/`Major`, plus its rank out of the 4 defined levels, e.g. `Notable (2/4)`), with a "worth a deeper Celtic Cross look" line on `Major` days only; - 2. up to 5 of today's aspects ranked by score, each naming the sign + 3. up to 5 of today's aspects ranked by score, each naming the sign and natal house the transiting planet currently occupies and the sign and house of the natal planet it's aspecting, followed by a short narrative sentence about what that transiting planet @@ -291,10 +304,10 @@ A plain-text report, printed in a fixed order — governs* (see `CLAUDE.md`'s `narrative.c` bullet) — omitted only if the source material has nothing to say for that planet/aspect combination; - 3. the full 10-position Celtic Cross spread, in Waite's own drawing + 4. the full 10-position Celtic Cross spread, in Waite's own drawing order, each with its card (and orientation), the position's own description, and that card's actual meaning; - 4. finally, a guidance paragraph tying the day's top transit to the + 5. finally, a guidance paragraph tying the day's top transit to the spread's Attitude/Outcome cards, with its opening sentence tailored to `day_level` (stronger wording on `Major`, softer on `Quiet`/`Notable`, falling back to a transit-free reading of the @@ -305,6 +318,8 @@ A plain-text report, printed in a fixed order — seen the full spread it references. ``` +Reading for: 2026-07-16 + Day significance: Major (4/4) (a major transit today - worth a deeper Celtic Cross look) @@ -354,6 +369,8 @@ The same report with `--lang de` (same input as the Major example above): ``` +Lesung für: 2026-07-16 + Bedeutung des Tages: Einschneidend (4/4) (ein einschneidender Transit heute - ein genauerer Blick auf das Keltische Kreuz lohnt sich) @@ -415,6 +432,7 @@ has the slugs to work with directly. ```json { + "date": "2026-07-16", "day_significance": { "level": "major", "rank": 4, @@ -448,6 +466,9 @@ has the slugs to work with directly. } ``` +- `date` is copied straight through from the input JSON's own top-level + `date` field (see `--format json` above) - always language-independent + regardless of `--lang`, same as every other slug in this document. - `significant_transits` has `interp.top_item_count` entries (0-5, same ranking as `--format text`); `celtic_cross` always has exactly 10, in `CelticCrossPosition` enum order (same order as `deck-engine`'s own diff --git a/engine/i18n/de.lang b/engine/i18n/de.lang index 7ad31c7..3eaafc4 100644 --- a/engine/i18n/de.lang +++ b/engine/i18n/de.lang @@ -162,6 +162,7 @@ card.world.upright=Gesicherter Erfolg, Belohnung, Reise, Weg, Auswanderung, Fluc card.world.reversed=Trägheit, Erstarrung, Stillstand, Beständigkeit. # --- UI-Bezeichnungen des Interpreters (interpreter/src/main.c) --- +interp.reading_date=Lesung für: interp.day_significance=Bedeutung des Tages: interp.day_level.quiet=Ruhig interp.day_level.notable=Bemerkenswert diff --git a/engine/i18n/en.lang b/engine/i18n/en.lang index 021ef0e..c05d82e 100644 --- a/engine/i18n/en.lang +++ b/engine/i18n/en.lang @@ -171,6 +171,7 @@ card.world.upright=Assured success, recompense, voyage, route, emigration, fligh card.world.reversed=Inertia, fixity, stagnation, permanence. # --- Interpreter UI labels (interpreter/src/main.c) --- +interp.reading_date=Reading for: interp.day_significance=Day significance: interp.day_level.quiet=Quiet interp.day_level.notable=Notable diff --git a/engine/src/reading.c b/engine/src/reading.c index 367742f..0df50a1 100644 --- a/engine/src/reading.c +++ b/engine/src/reading.c @@ -5,6 +5,7 @@ void reading_generate(const char *tarot_seed, const BirthData *birth, time_t utc_moment, DailyReading *out) { + out->utc_moment = utc_moment; astro_compute_natal_chart(birth, &out->natal); astro_compute_daily_transits(utc_moment, &out->natal, &out->transits); tarot_draw_celtic_cross(tarot_seed, &out->spread); @@ -160,6 +161,9 @@ static void print_body_position_json(FILE *out, const char *indent, Body body, void reading_print_json(const DailyReading *r, FILE *out) { fprintf(out, "{\n"); + struct tm *utc = gmtime(&r->utc_moment); + fprintf(out, " \"date\": \"%04d-%02d-%02d\",\n", utc->tm_year + 1900, utc->tm_mon + 1, utc->tm_mday); + fprintf(out, " \"natal\": {\n \"bodies\": [\n"); for (int b = 0; b < NUM_BODIES; b++) { print_body_position_json(out, " ", (Body)b, &r->natal.bodies[b]); diff --git a/engine/src/reading.h b/engine/src/reading.h index a125b58..dab604d 100644 --- a/engine/src/reading.h +++ b/engine/src/reading.h @@ -15,6 +15,12 @@ #include "tarot.h" typedef struct { + /* The moment passed to reading_generate() - the day this reading is + * for, not when it happened to be generated. Set by reading_generate() + * itself, so every caller (CLI, watch, reading_load_json() on the + * interpreter side) gets it for free without threading it through + * separately. */ + time_t utc_moment; NatalChart natal; DailyTransits transits; CelticCrossSpread spread; diff --git a/engine/tests/smoke_test.c b/engine/tests/smoke_test.c index d6da024..fcaf5ee 100644 --- a/engine/tests/smoke_test.c +++ b/engine/tests/smoke_test.c @@ -120,6 +120,7 @@ static void test_reading_print_json_shape(void) { * a stray astro_*_name()/tarot_*_name() call leaking translated text * into what must stay language-independent regardless of --lang). */ assert(buf[0] == '{'); + assert(strstr(buf, "\"date\": \"2025-07-02\"") != NULL); /* UTC date of the fixed 1751500000 seed */ assert(strstr(buf, "\"natal\"") != NULL); assert(strstr(buf, "\"transits\"") != NULL); assert(strstr(buf, "\"spread\"") != NULL); diff --git a/interpreter/src/main.c b/interpreter/src/main.c index e9b144c..d747e23 100644 --- a/interpreter/src/main.c +++ b/interpreter/src/main.c @@ -126,6 +126,15 @@ static const char *sign_display_name(ZodiacSign sign) { return ui(key, fallback[sign]); } +/* "YYYY-MM-DD" for reading->utc_moment - the day this reading is for + * (see reading_load_json()/parse_date() in reading_io.c), not today's + * real date if the two ever differ (e.g. reading.json was generated + * earlier and interpreted later). */ +static void format_date(time_t utc_moment, char *out, size_t out_size) { + struct tm *utc = gmtime(&utc_moment); + snprintf(out, out_size, "%04d-%02d-%02d", utc->tm_year + 1900, utc->tm_mon + 1, utc->tm_mday); +} + static const char *day_level_name(DaySignificance level) { switch (level) { case DAY_QUIET: return ui("interp.day_level.quiet", "Quiet"); @@ -179,6 +188,32 @@ static char *capture_guidance(const DailyInterpretation *interp, const CelticCro return buf; } +/* Prints one significant-transit item's full detail (title line with + * sign/house/orb/score, plus its narrative sentence) - shared between + * the "Significant Transits" list and the day's top item, which is + * repeated in full just above the guidance paragraph (see + * interp_print_text) so the reader has that same detail in view right + * next to the guidance text it informs. `index` is 1-based and prefixes + * the line with "N. "; pass 0 to omit it, for the repeated top item, + * which isn't part of the numbered list. */ +static void print_transit_item_text(FILE *out, const DailyReading *reading, + const SignificantItem *item, int index) { + const Aspect *a = &item->aspect; + if (index > 0) fprintf(out, " %d. ", index); + fprintf(out, "%s %s", ui("ui.transiting", "Transiting"), body_display_name(a->transiting_planet)); + print_body_in_sign_text(out, &reading->transits.bodies[a->transiting_planet]); + fprintf(out, " %s %s %s", aspect_display_name(a->type), ui("ui.natal", "natal"), + body_display_name(a->natal_planet)); + print_body_in_sign_text(out, &reading->natal.bodies[a->natal_planet]); + fprintf(out, " (%s %.1f\xc2\xb0, %s %.2f)\n", ui("ui.orb", "orb"), a->orb, + ui("interp.score", "score"), item->score); + + char narrative_buf[NARRATIVE_TEXT_MAX]; + narrative_write(narrative_buf, sizeof narrative_buf, " ", a, + reading->transits.bodies[a->transiting_planet].house); + fputs(narrative_buf, out); +} + static void print_top_transits_header(FILE *out, int count) { if (count == 1) { fprintf(out, "%s\n", ui("interp.top_transits.one", "Top significant transit:")); @@ -203,6 +238,10 @@ static void print_celtic_cross_text(FILE *out, const CelticCrossSpread *spread) } static void interp_print_text(FILE *out, const DailyReading *reading, const DailyInterpretation *interp) { + char date_buf[32]; + format_date(reading->utc_moment, date_buf, sizeof date_buf); + fprintf(out, "%s %s\n\n", ui("interp.reading_date", "Reading for:"), date_buf); + fprintf(out, "%s %s (%d/%d)\n", ui("interp.day_significance", "Day significance:"), day_level_name(interp->day_level), interp->day_level + 1, DAY_SIGNIFICANCE_COUNT); if (interpretation_deserves_framing(interp)) { @@ -211,41 +250,68 @@ static void interp_print_text(FILE *out, const DailyReading *reading, const Dail } fprintf(out, "\n"); + fprintf(out, "%s\n", ui("interp.heading_guidance", "Guidance")); + if (interp->top_item_count > 0) { + print_transit_item_text(out, reading, &interp->top_items[0], 0); + } + char guidance_buf[GUIDANCE_TEXT_MAX]; + guidance_write(guidance_buf, sizeof guidance_buf, "", interp, &reading->spread); + fputs(guidance_buf, out); + fprintf(out, "\n"); + if (interp->top_item_count == 0) { fprintf(out, "%s\n", ui("interp.no_notable_transits", "No notable transits today.")); } else { print_top_transits_header(out, interp->top_item_count); for (int i = 0; i < interp->top_item_count; i++) { - const SignificantItem *item = &interp->top_items[i]; - const Aspect *a = &item->aspect; - - fprintf(out, " %d. %s %s", i + 1, ui("ui.transiting", "Transiting"), - body_display_name(a->transiting_planet)); - print_body_in_sign_text(out, &reading->transits.bodies[a->transiting_planet]); - fprintf(out, " %s %s %s", aspect_display_name(a->type), ui("ui.natal", "natal"), - body_display_name(a->natal_planet)); - print_body_in_sign_text(out, &reading->natal.bodies[a->natal_planet]); - fprintf(out, " (%s %.1f\xc2\xb0, %s %.2f)\n", ui("ui.orb", "orb"), a->orb, - ui("interp.score", "score"), item->score); - - char narrative_buf[NARRATIVE_TEXT_MAX]; - narrative_write(narrative_buf, sizeof narrative_buf, " ", a, - reading->transits.bodies[a->transiting_planet].house); - fputs(narrative_buf, out); + print_transit_item_text(out, reading, &interp->top_items[i], i + 1); } } fprintf(out, "\n"); print_celtic_cross_text(out, &reading->spread); - fprintf(out, "\n"); - - char guidance_buf[GUIDANCE_TEXT_MAX]; - guidance_write(guidance_buf, sizeof guidance_buf, "", interp, &reading->spread); - fputs(guidance_buf, out); } /* ===== --format html ===== */ +/* HTML counterpart of print_transit_item_text() - same shared-between- + * the-repeated-top-item-and-the-full-list purpose, one table row (title + * + orb/score) plus a narrative row if non-empty. `index` is 1-based; + * pass 0 to omit the "N." cell, for the repeated top item. */ +static void print_transit_item_html(FILE *out, const DailyReading *reading, + const SignificantItem *item, int index) { + const Aspect *a = &item->aspect; + char index_buf[16] = ""; + if (index > 0) snprintf(index_buf, sizeof index_buf, "%d.", index); + char *text = capture_narrative(a, reading->transits.bodies[a->transiting_planet].house); + + fprintf(out, "%s%s %s %s %s %s" + "%s %.1f°, %s %.2f\n", + index_buf, ui("ui.transiting", "Transiting"), body_display_name(a->transiting_planet), + aspect_display_name(a->type), ui("ui.natal", "natal"), body_display_name(a->natal_planet), + ui("ui.orb", "orb"), a->orb, ui("interp.score", "score"), item->score); + if (text[0] != '\0') fprintf(out, "%s\n", text); + free(text); +} + +/* One Celtic Cross card as a ".card" div - shared between the + * Attitude/Outcome preview (right after guidance) and the full spread + * further down, so both render identically. */ +static void print_card_html(FILE *out, CelticCrossPosition position, const TarotDraw *draw) { + fprintf(out, + "
\n" + "
%s
\n" + " \"%s\"\n" + "
%s%s
\n" + "
%s
\n" + "
%s
\n" + "
\n", + tarot_position_name(position), draw->reversed ? "reversed" : "", + tarot_card_image_file(draw->card), tarot_card_name(draw->card), tarot_card_name(draw->card), + reversed_marker(draw->reversed), tarot_position_description(position), + tarot_card_meaning(draw->card, draw->reversed)); +} + static void interp_print_html(FILE *out, const DailyReading *reading, const DailyInterpretation *interp) { fprintf(out, "\n\n" @@ -267,6 +333,11 @@ static void interp_print_html(FILE *out, const DailyReading *reading, const Dail fprintf(out, "

%s

\n", ui("interp.heading", "Daily Interpretation")); + char date_buf[32]; + format_date(reading->utc_moment, date_buf, sizeof date_buf); + fprintf(out, "

%s %s

\n", + ui("interp.reading_date", "Reading for:"), date_buf); + fprintf(out, "

%s

\n

%s %s (%d/%d)", ui("interp.heading_day_significance", "Day Significance"), ui("interp.day_significance", "Day significance:"), day_level_name(interp->day_level), @@ -277,48 +348,20 @@ static void interp_print_html(FILE *out, const DailyReading *reading, const Dail } fprintf(out, "

\n"); - fprintf(out, "

%s

\n", ui("interp.heading_transits", "Significant Transits")); - if (interp->top_item_count == 0) { - fprintf(out, "

%s

\n", ui("interp.no_notable_transits", "No notable transits today.")); - } else { - fprintf(out, "\n"); - for (int i = 0; i < interp->top_item_count; i++) { - const SignificantItem *item = &interp->top_items[i]; - const Aspect *a = &item->aspect; - char *text = capture_narrative(a, reading->transits.bodies[a->transiting_planet].house); + char *guidance = capture_guidance(interp, &reading->spread); + fprintf(out, "

%s

\n", ui("interp.heading_guidance", "Guidance")); - fprintf(out, "" - "\n", - i + 1, ui("ui.transiting", "Transiting"), body_display_name(a->transiting_planet), - aspect_display_name(a->type), ui("ui.natal", "natal"), - body_display_name(a->natal_planet), ui("ui.orb", "orb"), a->orb, - ui("interp.score", "score"), item->score); - if (text[0] != '\0') fprintf(out, "\n", text); - free(text); - } - fprintf(out, "
%d.%s %s %s %s %s%s %.1f°, %s %.2f
%s
\n"); - } - - fprintf(out, "

%s

\n
\n", ui("ui.celtic_cross", "Celtic Cross")); - for (int i = 0; i < TAROT_SPREAD_SIZE; i++) { - const TarotDraw *draw = &reading->spread.positions[i]; - fprintf(out, - "
\n" - "
%s
\n" - " \"%s\"\n" - "
%s%s
\n" - "
%s
\n" - "
%s
\n" - "
\n", - tarot_position_name((CelticCrossPosition)i), draw->reversed ? "reversed" : "", - tarot_card_image_file(draw->card), tarot_card_name(draw->card), tarot_card_name(draw->card), - reversed_marker(draw->reversed), tarot_position_description((CelticCrossPosition)i), - tarot_card_meaning(draw->card, draw->reversed)); - } + fprintf(out, "
\n"); + print_card_html(out, POSITION_ATTITUDE, &reading->spread.positions[POSITION_ATTITUDE]); + print_card_html(out, POSITION_OUTCOME, &reading->spread.positions[POSITION_OUTCOME]); fprintf(out, "
\n"); - char *guidance = capture_guidance(interp, &reading->spread); - fprintf(out, "

%s

\n
\n", ui("interp.heading_guidance", "Guidance")); + fprintf(out, "
\n"); + if (interp->top_item_count > 0) { + fprintf(out, "\n"); + print_transit_item_html(out, reading, &interp->top_items[0], 0); + fprintf(out, "
\n"); + } const char *start = guidance; for (const char *p = guidance; ; p++) { if (*p == '\n' || *p == '\0') { @@ -330,6 +373,23 @@ static void interp_print_html(FILE *out, const DailyReading *reading, const Dail free(guidance); fprintf(out, "
\n"); + fprintf(out, "

%s

\n", ui("interp.heading_transits", "Significant Transits")); + if (interp->top_item_count == 0) { + fprintf(out, "

%s

\n", ui("interp.no_notable_transits", "No notable transits today.")); + } else { + fprintf(out, "\n"); + for (int i = 0; i < interp->top_item_count; i++) { + print_transit_item_html(out, reading, &interp->top_items[i], i + 1); + } + fprintf(out, "
\n"); + } + + fprintf(out, "

%s

\n
\n", ui("ui.celtic_cross", "Celtic Cross")); + for (int i = 0; i < TAROT_SPREAD_SIZE; i++) { + print_card_html(out, (CelticCrossPosition)i, &reading->spread.positions[i]); + } + fprintf(out, "
\n"); + fprintf(out, "\n"); } @@ -356,6 +416,10 @@ static void json_string(FILE *out, const char *s) { static void interp_print_json(FILE *out, const DailyReading *reading, const DailyInterpretation *interp) { fprintf(out, "{\n"); + char date_buf[32]; + format_date(reading->utc_moment, date_buf, sizeof date_buf); + fprintf(out, " \"date\": \"%s\",\n", date_buf); + fprintf(out, " \"day_significance\": {\n"); fprintf(out, " \"level\": \"%s\",\n", k_day_level_slug[interp->day_level]); fprintf(out, " \"rank\": %d,\n", interp->day_level + 1); diff --git a/interpreter/src/reading_io.c b/interpreter/src/reading_io.c index fbe395f..ec085d0 100644 --- a/interpreter/src/reading_io.c +++ b/interpreter/src/reading_io.c @@ -1,6 +1,9 @@ +#define _DEFAULT_SOURCE /* for timegm, parsing "date" back into utc_moment */ + #include "reading_io.h" #include "json.h" +#include #include /* Mirrors astro.c's own k_body_slug/k_aspect_slug (its i18n lookup-key @@ -133,6 +136,25 @@ static void parse_spread(const JsonValue *positions, CelticCrossSpread *out) { } } +/* Parses the top-level "date" field ("YYYY-MM-DD", see reading_print_json + * in engine/src/reading.c) back into out->utc_moment, as midnight UTC of + * that date - reading_print_json only ever wrote the calendar date, not a + * time of day, so that's the only value that round-trips through + * gmtime() to the same date. Lenient like parse_bodies()/parse_spread(): + * a missing or malformed "date" just leaves out->utc_moment at 0 (from + * reading_load_json's own memset), not a load failure - nothing here + * depends on it for scoring, only for display. */ +static void parse_date(const JsonValue *date, DailyReading *out) { + const char *date_str = date ? json_as_string(date) : NULL; + if (!date_str) return; + + struct tm tm_date = {0}; + if (sscanf(date_str, "%d-%d-%d", &tm_date.tm_year, &tm_date.tm_mon, &tm_date.tm_mday) != 3) return; + tm_date.tm_year -= 1900; + tm_date.tm_mon -= 1; + out->utc_moment = timegm(&tm_date); +} + bool reading_load_json(const char *text, size_t length, DailyReading *out) { memset(out, 0, sizeof(*out)); @@ -174,6 +196,8 @@ bool reading_load_json(const char *text, size_t length, DailyReading *out) { } out->transits.aspect_count = filled; + parse_date(json_object_get(root, "date"), out); + const JsonValue *natal = json_object_get(root, "natal"); parse_bodies(natal ? json_object_get(natal, "bodies") : NULL, out->natal.bodies); parse_bodies(json_object_get(transits, "bodies"), out->transits.bodies); diff --git a/interpreter/src/reading_io.h b/interpreter/src/reading_io.h index 8ec2192..541d82d 100644 --- a/interpreter/src/reading_io.h +++ b/interpreter/src/reading_io.h @@ -14,10 +14,12 @@ * around each significant event, and for guidance_print()'s tarot * framing: out->transits.aspects[]/aspect_count (used for scoring - see * significance.c), out->natal.bodies[]/out->transits.bodies[] (sign + - * house per body, used only for display), and out->spread.positions[] + * house per body, used only for display), out->spread.positions[] * (card + reversed per Celtic Cross position, used only by - * guidance.c). out->natal.ascendant_longitude/houses[] are left zeroed - - * nothing reads them yet. + * guidance.c), and out->utc_moment (the top-level "date" field, parsed + * back to midnight UTC of that date - used only for display, e.g. main.c + * printing which day a reading is for). out->natal.ascendant_longitude/ + * houses[] are left zeroed - nothing reads them yet. * * Returns false on malformed JSON, or if "transits"."aspects" isn't * present as an array (an empty array is fine - that's a real "no diff --git a/interpreter/tests/json_test.c b/interpreter/tests/json_test.c index 9b324be..345b644 100644 --- a/interpreter/tests/json_test.c +++ b/interpreter/tests/json_test.c @@ -1,6 +1,7 @@ #include #include #include +#include #include "../src/json.h" #include "../src/reading_io.h" @@ -48,6 +49,7 @@ static void test_json_parse_rejects_malformed(void) { * absent entirely, to prove partial spread data doesn't fail the load. */ static const char *k_fixture = "{" + " \"date\": \"2026-07-16\"," " \"natal\": {\"bodies\": [{\"body\": \"sun\", \"sign\": \"taurus\", \"house\": 3}], \"houses\": []}," " \"transits\": {" " \"bodies\": [{\"body\": \"sun\", \"sign\": \"cancer\", \"house\": 5}]," @@ -80,6 +82,30 @@ static void test_reading_load_json_extracts_aspects(void) { printf("PASS test_reading_load_json_extracts_aspects\n"); } +static void test_reading_load_json_extracts_date(void) { + DailyReading reading; + bool ok = reading_load_json(k_fixture, strlen(k_fixture), &reading); + + assert(ok); + struct tm *utc = gmtime(&reading.utc_moment); + assert(utc->tm_year + 1900 == 2026); + assert(utc->tm_mon + 1 == 7); + assert(utc->tm_mday == 16); + + printf("PASS test_reading_load_json_extracts_date\n"); +} + +static void test_reading_load_json_missing_date_defaults_to_zero(void) { + const char *text = "{\"transits\": {\"aspects\": []}}"; + DailyReading reading; + bool ok = reading_load_json(text, strlen(text), &reading); + + assert(ok); + assert(reading.utc_moment == 0); + + printf("PASS test_reading_load_json_missing_date_defaults_to_zero\n"); +} + static void test_reading_load_json_extracts_body_sign_and_house(void) { DailyReading reading; bool ok = reading_load_json(k_fixture, strlen(k_fixture), &reading); @@ -178,6 +204,8 @@ int main(void) { test_json_parse_basic_shapes(); test_json_parse_rejects_malformed(); test_reading_load_json_extracts_aspects(); + test_reading_load_json_extracts_date(); + test_reading_load_json_missing_date_defaults_to_zero(); test_reading_load_json_extracts_body_sign_and_house(); test_reading_load_json_extracts_spread_positions(); test_reading_load_json_missing_spread_is_not_fatal(); diff --git a/watch/package.json b/watch/package.json index 8a7325a..a9f86a9 100644 --- a/watch/package.json +++ b/watch/package.json @@ -42,6 +42,12 @@ ], "resources": { "media": [ + { + "type": "bitmap", + "name": "IMAGE_APP_ICON", + "file": "app_icon.png", + "menuIcon": true + }, { "type": "bitmap", "name": "IMG_CARD_FOOL", diff --git a/watch/scripts/gen_app_icon.py b/watch/scripts/gen_app_icon.py new file mode 100644 index 0000000..38693c1 --- /dev/null +++ b/watch/scripts/gen_app_icon.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +"""Downsizes res/app_icon_50x50_64_color.png into watch/resources/app_icon.png. + +Pebble's own build tooling (process_sdk_resources.py) hard-fails any +"menuIcon": true resource larger than 25x25px - the fixed size Pebble OS +renders app icons at in the watch's own app launcher list (and, synced +from the watch, in the mobile app's installed-apps list too). The source +art in res/ is 50x50 (square, so it downsamples to exactly 25x25 with no +letterboxing) - the color variant is used, per the same "prefer color" +choice as the rest of this app's card art, over the companion +*_gray.png fallback also present in res/. + +Generated output, like i18n_tables.auto.c and resources/img/*.png - not +committed (see .gitignore), regenerate by running this script directly +or via `pebble build` (wired into wscript alongside gen_card_images.py). +Requires Pillow (`pip install Pillow`), not part of this repo's own +desktop build/test toolchain. +""" +import os +import sys + +from PIL import Image + +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) +SRC_PATH = os.path.normpath( + os.path.join(SCRIPT_DIR, "..", "..", "res", "app_icon_50x50_64_color.png") +) +OUT_PATH = os.path.normpath(os.path.join(SCRIPT_DIR, "..", "resources", "app_icon.png")) + +MAX_ICON_SIZE = 25 # Pebble's own hard cap for "menuIcon": true resources. + + +def main(): + if os.path.exists(OUT_PATH) and os.path.getmtime(OUT_PATH) >= os.path.getmtime(SRC_PATH): + print(f"{OUT_PATH} already up to date", file=sys.stderr) + return + + os.makedirs(os.path.dirname(OUT_PATH), exist_ok=True) + with Image.open(SRC_PATH) as img: + ratio = MAX_ICON_SIZE / max(img.width, img.height) + size = (round(img.width * ratio), round(img.height * ratio)) + resized = img.convert("RGBA").resize(size, Image.LANCZOS) + resized.save(OUT_PATH, "PNG", optimize=True) + + print(f"wrote {OUT_PATH} ({size[0]}x{size[1]})", file=sys.stderr) + + +if __name__ == "__main__": + main() diff --git a/watch/src/c/ui_report_window.c b/watch/src/c/ui_report_window.c index 1deb4e7..49f310f 100644 --- a/watch/src/c/ui_report_window.c +++ b/watch/src/c/ui_report_window.c @@ -31,6 +31,7 @@ static GFont s_font_body; * Static rather than malloc'd - fixed, known-in-advance sizes, and it * keeps report_window_reload() from having to reason about freeing the * previous run's buffers before reusing them. */ +static char s_reading_date[48]; static char s_day_title[64]; static char s_transit_titles[MAX_SIGNIFICANT_ITEMS][96]; static char s_narratives[MAX_SIGNIFICANT_ITEMS][NARRATIVE_TEXT_MAX]; @@ -53,6 +54,14 @@ typedef struct { static ImageSlot s_slots[TAROT_SPREAD_SIZE]; +/* Attitude (0) and Outcome (1) preview slots, drawn at half screen + * width each so the two sit side by side - the BitmapLayer's own bounds + * (narrower than IMAGE_WIDTH on 144px-wide platforms) clip the centered + * image automatically, same as GAlignCenter already does for the + * full-width slots above, no separate scaling logic needed. */ +#define REPEAT_SLOT_COUNT 2 +static ImageSlot s_repeat_slots[REPEAT_SLOT_COUNT]; + #define MAX_TEXT_LAYERS 50 static TextLayer *s_text_layers[MAX_TEXT_LAYERS]; static int s_text_layer_count; @@ -75,8 +84,8 @@ static const char *reversed_marker(bool reversed) { return reversed ? i18n_get("ui.reversed", "(Reversed)") : ""; } -static int16_t add_text(int16_t width, int16_t y, const char *text, GFont font) { - TextLayer *tl = text_layer_create(GRect(0, y, width, 4000)); +static int16_t add_text_at(int16_t x, int16_t width, int16_t y, const char *text, GFont font) { + TextLayer *tl = text_layer_create(GRect(x, y, width, 4000)); text_layer_set_font(tl, font); text_layer_set_text(tl, text); text_layer_set_overflow_mode(tl, GTextOverflowModeWordWrap); @@ -91,6 +100,10 @@ static int16_t add_text(int16_t width, int16_t y, const char *text, GFont font) return y + size.h + MARGIN; } +static int16_t add_text(int16_t width, int16_t y, const char *text, GFont font) { + return add_text_at(0, width, y, text, font); +} + static void teardown_content(void) { for (int i = 0; i < s_text_layer_count; i++) { text_layer_destroy(s_text_layers[i]); @@ -107,6 +120,30 @@ static void teardown_content(void) { s_slots[i].bitmap_layer = NULL; } } + + for (int i = 0; i < REPEAT_SLOT_COUNT; i++) { + if (s_repeat_slots[i].bitmap) { + gbitmap_destroy(s_repeat_slots[i].bitmap); + s_repeat_slots[i].bitmap = NULL; + } + if (s_repeat_slots[i].bitmap_layer) { + bitmap_layer_destroy(s_repeat_slots[i].bitmap_layer); + s_repeat_slots[i].bitmap_layer = NULL; + } + } +} + +static void update_slot_visibility(ImageSlot *slot, int16_t visible_top, int16_t visible_bottom) { + bool visible = slot->y < visible_bottom && slot->y + slot->height > visible_top; + + if (visible && !slot->bitmap) { + slot->bitmap = gbitmap_create_with_resource(card_resource_id(slot->card)); + bitmap_layer_set_bitmap(slot->bitmap_layer, slot->bitmap); + } else if (!visible && slot->bitmap) { + bitmap_layer_set_bitmap(slot->bitmap_layer, NULL); + gbitmap_destroy(slot->bitmap); + slot->bitmap = NULL; + } } static void update_visible_images(void) { @@ -115,17 +152,10 @@ static void update_visible_images(void) { int16_t visible_bottom = visible_top + s_screen_height; for (int i = 0; i < TAROT_SPREAD_SIZE; i++) { - ImageSlot *slot = &s_slots[i]; - bool visible = slot->y < visible_bottom && slot->y + slot->height > visible_top; - - if (visible && !slot->bitmap) { - slot->bitmap = gbitmap_create_with_resource(card_resource_id(slot->card)); - bitmap_layer_set_bitmap(slot->bitmap_layer, slot->bitmap); - } else if (!visible && slot->bitmap) { - bitmap_layer_set_bitmap(slot->bitmap_layer, NULL); - gbitmap_destroy(slot->bitmap); - slot->bitmap = NULL; - } + update_slot_visibility(&s_slots[i], visible_top, visible_bottom); + } + for (int i = 0; i < REPEAT_SLOT_COUNT; i++) { + update_slot_visibility(&s_repeat_slots[i], visible_top, visible_bottom); } } @@ -139,25 +169,87 @@ static void build_content(int16_t width) { int16_t y = MARGIN; + /* The day this reading is for (reading->utc_moment, set by + * reading_generate() - see reading_state_recompute()), not necessarily + * "today" if the report is viewed right at a UTC-midnight boundary + * before the next recompute. Plain gmtime(), same justification as + * reading_state.c's own use of it. */ + struct tm *utc = gmtime(&reading->utc_moment); + snprintf(s_reading_date, sizeof s_reading_date, "%s %04d-%02d-%02d", + i18n_get("interp.reading_date", "Reading for:"), utc->tm_year + 1900, + utc->tm_mon + 1, utc->tm_mday); + y = add_text(width, y, s_reading_date, s_font_body); + y = add_text(width, y, i18n_get("interp.heading_day_significance", "Day Significance"), s_font_section); snprintf(s_day_title, sizeof s_day_title, "%s (%d/%d)", day_level_name(interp->day_level), interp->day_level + 1, DAY_SIGNIFICANCE_COUNT); y = add_text(width, y, s_day_title, s_font_body); - y = add_text(width, y, i18n_get("interp.heading_transits", "Significant Transits"), s_font_section); - if (interp->top_item_count == 0) { - y = add_text(width, y, i18n_get("interp.no_notable_transits", "No notable transits today."), s_font_body); - } else { + /* Build every transit's title + narrative up front (used both for the + * repeated top-item line below and the full list further down), so + * neither buffer is computed twice for item 0. */ + if (interp->top_item_count > 0) { for (int i = 0; i < interp->top_item_count; i++) { const Aspect *a = &interp->top_items[i].aspect; snprintf(s_transit_titles[i], sizeof s_transit_titles[i], "%s %s %s %s %s", i18n_get("ui.transiting", "Transiting"), astro_body_name(a->transiting_planet), astro_aspect_name(a->type), i18n_get("ui.natal", "natal"), astro_body_name(a->natal_planet)); - y = add_text(width, y, s_transit_titles[i], s_font_subhead); - narrative_write(s_narratives[i], sizeof s_narratives[i], "", a, reading->transits.bodies[a->transiting_planet].house); + } + } + + y = add_text(width, y, i18n_get("interp.heading_guidance", "Guidance"), s_font_section); + + /* Attitude/Outcome preview, side by side, image only - no per-card + * caption text. A "Position: Card Name (Reversed)" label doesn't fit + * a half-width column without wrapping into 3+ short lines, and with + * both columns wrapping independently the reader ends up reading + * fragments across columns rather than down one column at a time + * (confirmed on-device: it reads as one garbled interleaved sentence, + * not two). Captions are unnecessary anyway - the guidance text + * printed immediately below already names and quotes both cards in + * full ("Deine Haltungskarte ist ..." / "Das wahrscheinliche Ergebnis + * ... ist ..."), and both reappear with their full position/name/ + * meaning again in the Celtic Cross walkthrough further down. */ + { + int16_t half_w = width / 2; + const TarotDraw *attitude = &reading->spread.positions[POSITION_ATTITUDE]; + const TarotDraw *outcome = &reading->spread.positions[POSITION_OUTCOME]; + int16_t img_height = card_image_height_for_width(IMAGE_WIDTH); + + s_repeat_slots[0].card = attitude->card; + s_repeat_slots[0].y = y; + s_repeat_slots[0].height = img_height; + s_repeat_slots[0].bitmap_layer = bitmap_layer_create(GRect(0, y, half_w, img_height)); + bitmap_layer_set_alignment(s_repeat_slots[0].bitmap_layer, GAlignCenter); + scroll_layer_add_child(s_scroll_layer, bitmap_layer_get_layer(s_repeat_slots[0].bitmap_layer)); + + s_repeat_slots[1].card = outcome->card; + s_repeat_slots[1].y = y; + s_repeat_slots[1].height = img_height; + s_repeat_slots[1].bitmap_layer = + bitmap_layer_create(GRect(half_w, y, width - half_w, img_height)); + bitmap_layer_set_alignment(s_repeat_slots[1].bitmap_layer, GAlignCenter); + scroll_layer_add_child(s_scroll_layer, bitmap_layer_get_layer(s_repeat_slots[1].bitmap_layer)); + + y += img_height + MARGIN; + } + + if (interp->top_item_count > 0) { + y = add_text(width, y, s_transit_titles[0], s_font_subhead); + y = add_text(width, y, s_narratives[0], s_font_body); + } + guidance_write(s_guidance, sizeof s_guidance, "", interp, &reading->spread); + y = add_text(width, y, s_guidance, s_font_body); + + y = add_text(width, y, i18n_get("interp.heading_transits", "Significant Transits"), s_font_section); + if (interp->top_item_count == 0) { + y = add_text(width, y, i18n_get("interp.no_notable_transits", "No notable transits today."), s_font_body); + } else { + for (int i = 0; i < interp->top_item_count; i++) { + y = add_text(width, y, s_transit_titles[i], s_font_subhead); y = add_text(width, y, s_narratives[i], s_font_body); } } @@ -184,10 +276,6 @@ static void build_content(int16_t width) { y = add_text(width, y, tarot_card_meaning(draw->card, draw->reversed), s_font_body); } - y = add_text(width, y, i18n_get("interp.heading_guidance", "Guidance"), s_font_section); - guidance_write(s_guidance, sizeof s_guidance, "", interp, &reading->spread); - y = add_text(width, y, s_guidance, s_font_body); - scroll_layer_set_content_size(s_scroll_layer, GSize(width, y)); } diff --git a/watch/src/pkjs/index.js b/watch/src/pkjs/index.js index 18c5b7b..c7a9e5c 100644 --- a/watch/src/pkjs/index.js +++ b/watch/src/pkjs/index.js @@ -21,6 +21,14 @@ var LANGUAGES = [ { code: 'de', label: 'Deutsch' } ]; +// 120x120 PNG of res/logo-small.png, inlined as base64 - this page is a +// bare `data:` URI with no server, so there's nowhere else to load an +// image from. Shown in place of a plain text title, matching the app's +// own branding on the one surface this project draws on the phone +// itself (the watch's own launcher icon is a separate, unrelated image - +// see package.json's IMAGE_APP_ICON). +var LOGO_DATA_URI = 'data:image/png;base64,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'; + function loadSavedConfig() { try { var raw = window.localStorage.getItem(STORAGE_KEY); @@ -133,7 +141,10 @@ function buildConfigHtml(saved) { 'color:#fff;border:none;border-radius:4px;}' + '.error{color:#c00;font-size:13px;margin-top:8px;display:none;}' + '' + - '

Deck in a Dash

' + + '
' + + 'Deck in a Dash' + + '

Deck in a Dash

' + + '
' + '
' + '

Birth Details

' + '' + diff --git a/watch/wscript b/watch/wscript index 6961a3b..45dbaf2 100644 --- a/watch/wscript +++ b/watch/wscript @@ -63,15 +63,18 @@ def configure(ctx): def build(ctx): ctx.load('pebble_sdk') - # Regenerate src/c/i18n_tables.auto.c from engine/i18n/*.lang, and - # resources/img/*.png from res/img/*.jpeg, before compiling/bundling - # anything - see each script's own doc comment. Both run eagerly (not - # as waf Tasks) since the bundle step below depends on their output - # existing; gen_card_images.py skips any card whose PNG is already - # up to date, so this stays cheap on repeat builds. Requires Pillow - # (`pip install Pillow`) - see watch/README.md. + # Regenerate src/c/i18n_tables.auto.c from engine/i18n/*.lang, + # resources/img/*.png from res/img/*.jpeg, and resources/app_icon.png + # from res/app_icon_50x50_64_color.png, before compiling/bundling + # anything - see each script's own doc comment. All three run eagerly + # (not as waf Tasks) since the bundle step below depends on their + # output existing; gen_card_images.py/gen_app_icon.py both skip + # regenerating anything already up to date, so this stays cheap on + # repeat builds. Requires Pillow (`pip install Pillow`) - see + # watch/README.md. subprocess.check_call([sys.executable, 'scripts/gen_i18n_tables.py']) subprocess.check_call([sys.executable, 'scripts/gen_card_images.py']) + subprocess.check_call([sys.executable, 'scripts/gen_app_icon.py']) engine_dir = ctx.path.parent.find_dir('engine/src') engine_nodes = [engine_dir.find_node(name) for name in ENGINE_SRCS]